From c27f884ec1d18d9cff0d19d6ba8de1dd54d991c4 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard 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