From c27f884ec1d18d9cff0d19d6ba8de1dd54d991c4 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 9 Dec 2022 08:57:36 +0100
Subject: Allow OR operations in bit fields at a given position.

---
 plugins/pychrysalide/common/bits.c |  92 +++++++++++++++++++++++++
 src/common/bits.c                  | 138 ++++++++++++++++++++++++++++++++++++-
 src/common/bits.h                  |   6 ++
 tests/common/bitfield.py           |  37 ++++++++++
 4 files changed, 272 insertions(+), 1 deletion(-)

diff --git a/plugins/pychrysalide/common/bits.c b/plugins/pychrysalide/common/bits.c
index e73ce7a..9275996 100644
--- a/plugins/pychrysalide/common/bits.c
+++ b/plugins/pychrysalide/common/bits.c
@@ -65,6 +65,9 @@ static PyObject *py_bitfield_richcompare(PyObject *, PyObject *, int);
 /* Crée une copie d'un champ de bits classique. */
 static PyObject *py_bitfield_dup(PyObject *, PyObject *);
 
+/* Redimensionne un champ de bits. */
+static PyObject *py_bitfield_resize(PyObject *, PyObject *);
+
 /* Bascule à 0 un champ de bits dans son intégralité. */
 static PyObject *py_bitfield_reset_all(PyObject *, PyObject *);
 
@@ -77,6 +80,9 @@ static PyObject *py_bitfield_reset(PyObject *, PyObject *);
 /* Bascule à 1 une partie d'un champ de bits. */
 static PyObject *py_bitfield_set(PyObject *, PyObject *);
 
+/* Réalise une opération OU logique entre deux champs de bits. */
+static PyObject *py_bitfield_or_at(PyObject *, PyObject *);
+
 /* Détermine si un bit est à 1 dans un champ de bits. */
 static PyObject *py_bitfield_test(PyObject *, PyObject *);
 
@@ -405,6 +411,47 @@ static PyObject *py_bitfield_dup(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : self = champ de bits à dupliquer.                            *
+*                args = non utilisé ici.                                      *
+*                                                                             *
+*  Description : Redimensionne un champ de bits.                              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bitfield_resize(PyObject *self, PyObject *args)
+{
+    unsigned long length;                   /* Nouvelle taille à respecter */
+    int ret;                                /* Bilan de lecture des args.  */
+    py_bitfield_t *bf;                      /* Instance à manipuler        */
+
+#define BITFIELD_RESIZE_METHOD PYTHON_METHOD_DEF                        \
+(                                                                       \
+    resize, "$self, length, /",                                         \
+    METH_VARARGS, py_bitfield,                                          \
+    "Resize a bitfield and fix its new size to *length*.\n"             \
+    "\n"                                                                \
+    "The new bits get initialized to the same state used at the"        \
+    " bitfield creation."                                               \
+)
+
+    ret = PyArg_ParseTuple(args, "k", &length);
+    if (!ret) return NULL;
+
+    bf = (py_bitfield_t *)self;
+
+    resize_bit_field(&bf->native, length);
+
+    Py_RETURN_NONE;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = champ de bits à modifier.                             *
 *                args = non utilisé ici.                                      *
 *                                                                             *
@@ -558,6 +605,49 @@ static PyObject *py_bitfield_set(PyObject *self, PyObject *args)
 *  Paramètres  : self = champ de bits à consulter.                            *
 *                args = arguments fournis pour la conduite de l'opération.    *
 *                                                                             *
+*  Description : Réalise une opération OU logique entre deux champs de bits.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bitfield_or_at(PyObject *self, PyObject *args)
+{
+    bitfield_t *src;                        /* Seconde champ de bits       */
+    unsigned long first;                    /* Indice du premier bit testé */
+    int ret;                                /* Bilan de lecture des args.  */
+    py_bitfield_t *bf;                      /* Instance à manipuler        */
+
+#define BITFIELD_OR_AT_METHOD PYTHON_METHOD_DEF             \
+(                                                           \
+    or_at, "$self, src, first, /",                          \
+    METH_VARARGS, py_bitfield,                              \
+    "Perform an OR operation with another bitfield.\n"      \
+    "\n"                                                    \
+    "The *src* argument is expected to be another"          \
+    " pychrysalide.common.BitField instance. The area to"   \
+    " process starts at bit *first* from *src*."            \
+)
+
+    ret = PyArg_ParseTuple(args, "O&k", convert_to_bitfield, &src, &first);
+    if (!ret) return NULL;
+
+    bf = (py_bitfield_t *)self;
+
+    or_bit_field_at(bf->native, src, first);
+
+    Py_RETURN_NONE;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = champ de bits à consulter.                            *
+*                args = arguments fournis pour la conduite de l'opération.    *
+*                                                                             *
 *  Description : Détermine si un bit est à 1 dans un champ de bits.           *
 *                                                                             *
 *  Retour      : true si le bit correspondant est à l'état haut.              *
@@ -897,10 +987,12 @@ PyTypeObject *get_python_bitfield_type(void)
 
     static PyMethodDef py_bitfield_methods[] = {
         BITFIELD_DUP_METHOD,
+        BITFIELD_RESIZE_METHOD,
         BITFIELD_RESET_ALL_METHOD,
         BITFIELD_SET_ALL_METHOD,
         BITFIELD_RESET_METHOD,
         BITFIELD_SET_METHOD,
+        BITFIELD_OR_AT_METHOD,
         BITFIELD_TEST_METHOD,
         BITFIELD_TEST_NONE_METHOD,
         BITFIELD_TEST_ALL_METHOD,
diff --git a/src/common/bits.c b/src/common/bits.c
index d3b45bb..590c318 100644
--- a/src/common/bits.c
+++ b/src/common/bits.c
@@ -40,6 +40,8 @@ struct _bitfield_t
     size_t length;                          /* Nombre de bits représentés  */
     size_t requested;                       /* Nombre de mots alloués      */
 
+    bool default_state;                     /* Etat d'initialisation       */
+
     unsigned long bits[0];                  /* Mémoire d'accès associée    */
 
 };
@@ -108,6 +110,8 @@ bitfield_t *create_bit_field(size_t length, bool state)
 
     result = _create_bit_field(length);
 
+    result->default_state = state;
+
     if (state)
         set_all_in_bit_field(result);
     else
@@ -186,6 +190,83 @@ void copy_bit_field(bitfield_t *dest, const bitfield_t *src)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : field  = champ de bits à modifier. [OUT]                     *
+*                length = nouveau nombre de bits du champ à représenter.      *
+*                                                                             *
+*  Description : Redimensionne un champ de bits.                              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void resize_bit_field(bitfield_t **field, size_t length)
+{
+    bitfield_t *_field;                     /* Commodité d'accès           */
+    size_t requested;                       /* Nombre de mots à allouer    */
+    size_t base;                            /* Allocation de base en octets*/
+    size_t remaining;                       /* Nombre de derniers bits     */
+    size_t last;                            /* Dernier mot utilisé         */
+    unsigned long mask;                     /* Masque d'initialisation     */
+    size_t i;                               /* Boucle de parcours          */
+
+    _field = *field;
+
+    if (_field->length != length)
+    {
+        /* Redimensionnement */
+
+        requested = length / (sizeof(unsigned long) * 8);
+        if (length % (sizeof(unsigned long) * 8) != 0) requested++;
+
+        base = sizeof(bitfield_t) + requested * sizeof(unsigned long);
+
+        *field = realloc(_field, base);
+        _field = *field;
+
+        /* Initialisation, si nécessaire */
+
+        if (_field->length < length)
+        {
+            last = _field->length / (sizeof(unsigned long) * 8);
+            remaining = _field->length % (sizeof(unsigned long) * 8);
+
+            if (remaining != 0)
+            {
+                mask = (1ul << remaining) - 1;
+
+                if (_field->default_state)
+                    _field->bits[last] |= ~mask;
+                else
+                    _field->bits[last] &= mask;
+
+                last++;
+
+            }
+
+            for (i = last; i < requested; i++)
+            {
+                if (_field->default_state)
+                    _field->bits[i] = ~0ul;
+                else
+                    _field->bits[i] = 0ul;
+            }
+
+        }
+
+        /* Actualisation des tailles */
+
+        _field->length = length;
+        _field->requested = requested;
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : field = champ de bits à consulter.                           *
 *                                                                             *
 *  Description : Indique la taille d'un champ de bits donné.                  *
@@ -435,6 +516,61 @@ void or_bit_field(bitfield_t *dest, const bitfield_t *src)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : dest  = champ de bits à modifier.                            *
+*                src   = champ de bits à utiliser pour l'opération.           *
+*                first = point de départ pour l'opération à réaliser.         *
+*                                                                             *
+*  Description : Réalise une opération OU logique entre deux champs de bits.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void or_bit_field_at(bitfield_t *dest, const bitfield_t *src, size_t first)
+{
+    size_t start;                           /* Mot de départ dans le champ */
+    size_t offset;                          /* Décalage des mots à basculer*/
+    size_t remaining;                       /* Taille du dernier tronçon   */
+    unsigned long finalcut;                 /* Limitation du mot final     */
+    size_t i;                               /* Boucle de parcours          */
+    unsigned long word;                     /* Mot reconstituté à tester   */
+
+    assert(dest->length <= (first + src->length));
+
+    start = first / (sizeof(unsigned long) * 8);
+    offset = first % (sizeof(unsigned long) * 8);
+
+    remaining = (first + src->length) % (sizeof(unsigned long) * 8);
+
+    if (remaining == 0)
+        finalcut = ~0ul;
+    else
+        finalcut = (1ul << remaining) - 1;
+
+    for (i = 0; i < (src->requested + 1); i++)
+    {
+        if (i < src->requested)
+            word = src->bits[i] << offset;
+        else
+            word = 0;
+
+        if (i > 0)
+            word |= src->bits[i - 1] >> (sizeof(unsigned long) * 8 - offset);
+
+        if (i == src->requested)
+            word &= finalcut;
+
+        dest->bits[start + i] |= word;
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : field = champ de bits à consulter.                           *
 *                n     = indice du bit à traiter.                             *
 *                                                                             *
@@ -610,7 +746,7 @@ static bool test_state_within_bit_field(const bitfield_t *field, size_t first, c
         {
             word = field->bits[windex] >> offset;
             if ((windex + 1) < field->requested)
-                word |= field->bits[start + i + 1] << (sizeof(unsigned long) * 8 - offset);
+                word |= field->bits[windex + 1] << (sizeof(unsigned long) * 8 - offset);
         }
 
         bitmask = mask->bits[i];
diff --git a/src/common/bits.h b/src/common/bits.h
index fb231d5..58fa0fe 100644
--- a/src/common/bits.h
+++ b/src/common/bits.h
@@ -45,6 +45,9 @@ void delete_bit_field(bitfield_t *);
 /* Copie un champ de bits dans un autre. */
 void copy_bit_field(bitfield_t *, const bitfield_t *);
 
+/* Redimensionne un champ de bits. */
+void resize_bit_field(bitfield_t **, size_t);
+
 /* Indique la taille d'un champ de bits donné. */
 size_t get_bit_field_size(const bitfield_t *);
 
@@ -69,6 +72,9 @@ void and_bit_field(bitfield_t *, const bitfield_t *);
 /* Réalise une opération OU logique entre deux champs de bits. */
 void or_bit_field(bitfield_t *, const bitfield_t *);
 
+/* Réalise une opération OU logique entre deux champs de bits. */
+void or_bit_field_at(bitfield_t *, const bitfield_t *, size_t);
+
 /* Détermine si un bit est à 1 dans un champ de bits. */
 bool test_in_bit_field(const bitfield_t *, size_t);
 
diff --git a/tests/common/bitfield.py b/tests/common/bitfield.py
index 7359a8a..8c4a470 100644
--- a/tests/common/bitfield.py
+++ b/tests/common/bitfield.py
@@ -19,6 +19,23 @@ class TestBitFields(ChrysalideTestCase):
 
         self.assertEqual(bf.popcount, bf2.popcount)
 
+    def testResizeBitField(self):
+        """Resize bitfields."""
+
+        bf_a = BitField(10, 0)
+
+        bf_b = BitField(6, 0)
+        bf_b.resize(10)
+
+        self.assertEqual(bf_a, bf_b)
+
+        bf_a = BitField(133, 1)
+
+        bf_b = BitField(64, 1)
+        bf_b.resize(133)
+
+        self.assertEqual(bf_a, bf_b)
+
 
     def testBitFieldValues(self):
         """Evaluate bitfields basic values."""
@@ -70,6 +87,26 @@ class TestBitFields(ChrysalideTestCase):
         self.assertEqual(bf_f.popcount, bf_a.popcount)
 
 
+    def testBitFieldLogicalOperationsAt(self):
+        """Perform logical operations on bitfields at a given position."""
+
+        bf_a = BitField(75, 0)
+
+        bf_b = BitField(4, 1)
+        bf_b.reset(2, 1)
+
+        bf_a.or_at(bf_b, 63)
+
+        self.assertFalse(bf_a.test(62))
+
+        self.assertTrue(bf_a.test(63))
+        self.assertTrue(bf_a.test(64))
+        self.assertFalse(bf_a.test(65))
+        self.assertTrue(bf_a.test(66))
+
+        self.assertFalse(bf_a.test(67))
+
+
     def testBitFieldSwitch(self):
         """Switch various bits in bitfields."""
 
-- 
cgit v0.11.2-87-g4458