From 6dea5e4fed979cb57f3dbc0c9144f1ff1854b800 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Wed, 7 Dec 2022 03:52:51 +0100 Subject: Provide functions to test bit fields against bit fields. --- plugins/pychrysalide/common/bits.c | 153 +++++++++++++++++++++++++++++++++++++ plugins/pychrysalide/common/bits.h | 3 + src/common/bits.c | 128 +++++++++++++++++++++++++++++++ src/common/bits.h | 6 ++ tests/common/bitfield.py | 41 ++++++++++ 5 files changed, 331 insertions(+) diff --git a/plugins/pychrysalide/common/bits.c b/plugins/pychrysalide/common/bits.c index 73fd55b..e73ce7a 100644 --- a/plugins/pychrysalide/common/bits.c +++ b/plugins/pychrysalide/common/bits.c @@ -86,6 +86,12 @@ static PyObject *py_bitfield_test_none(PyObject *, PyObject *); /* Détermine si un ensemble de bits est à 1 dans un champ. */ static PyObject *py_bitfield_test_all(PyObject *, PyObject *); +/* Teste l'état à 0 de bits selon un masque de bits. */ +static PyObject *py_bitfield_test_zeros_with(PyObject *, PyObject *); + +/* Teste l'état à 1 de bits selon un masque de bits. */ +static PyObject *py_bitfield_test_ones_with(PyObject *, PyObject *); + /* Indique la taille d'un champ de bits donné. */ static PyObject *py_bitfield_get_size(PyObject *, void *); @@ -695,6 +701,106 @@ static PyObject *py_bitfield_test_all(PyObject *self, PyObject *args) /****************************************************************************** * * +* Paramètres : self = champ de bits à consulter. * +* args = arguments fournis pour la conduite de l'opération. * +* * +* Description : Teste l'état à 0 de bits selon un masque de bits. * +* * +* Retour : true si les bits visés sont à l'état bas. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_bitfield_test_zeros_with(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à faire remonter */ + unsigned long first; /* Indice du premier bit testé */ + bitfield_t *mask; /* Champ de bits natif */ + int ret; /* Bilan de lecture des args. */ + py_bitfield_t *bf; /* Instance à manipuler */ + bool status; /* Bilan d'analyse */ + +#define BITFIELD_TEST_ZEROS_WITH_METHOD PYTHON_METHOD_DEF \ +( \ + test_zeros_with, "$self, first, mask, /", \ + METH_VARARGS, py_bitfield, \ + "Test a range of bits against another bit field.\n" \ + "\n" \ + "The area to process starts at bit *first* and the" \ + " test relies on bits set within the *mask* object.\n" \ + "\n" \ + "The result is a boolean value: True if all tested" \ + " bits are unset, False otherwise." \ +) + + ret = PyArg_ParseTuple(args, "kO&", &first, convert_to_bitfield, &mask); + if (!ret) return NULL; + + bf = (py_bitfield_t *)self; + + status = test_zeros_within_bit_field(bf->native, first, mask); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = champ de bits à consulter. * +* args = arguments fournis pour la conduite de l'opération. * +* * +* Description : Teste l'état à 1 de bits selon un masque de bits. * +* * +* Retour : true si les bits visés sont à l'état haut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_bitfield_test_ones_with(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à faire remonter */ + unsigned long first; /* Indice du premier bit testé */ + bitfield_t *mask; /* Champ de bits natif */ + int ret; /* Bilan de lecture des args. */ + py_bitfield_t *bf; /* Instance à manipuler */ + bool status; /* Bilan d'analyse */ + +#define BITFIELD_TEST_ONES_WITH_METHOD PYTHON_METHOD_DEF \ +( \ + test_ones_with, "$self, first, mask, /", \ + METH_VARARGS, py_bitfield, \ + "Test a range of bits against another bit field.\n" \ + "\n" \ + "The area to process starts at bit *first* and the" \ + " test relies on bits set within the *mask* object.\n" \ + "\n" \ + "The result is a boolean value: True if all tested" \ + " bits are set, False otherwise." \ +) + + ret = PyArg_ParseTuple(args, "kO&", &first, convert_to_bitfield, &mask); + if (!ret) return NULL; + + bf = (py_bitfield_t *)self; + + status = test_ones_within_bit_field(bf->native, first, mask); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : self = classe représentant une instruction. * * closure = adresse non utilisée ici. * * * @@ -798,6 +904,8 @@ PyTypeObject *get_python_bitfield_type(void) BITFIELD_TEST_METHOD, BITFIELD_TEST_NONE_METHOD, BITFIELD_TEST_ALL_METHOD, + BITFIELD_TEST_ZEROS_WITH_METHOD, + BITFIELD_TEST_ONES_WITH_METHOD, { NULL } }; @@ -876,6 +984,51 @@ bool ensure_python_bitfield_is_registered(void) /****************************************************************************** * * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en champ de bits. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_bitfield(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_bitfield_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to bit field"); + break; + + case 1: + *((bitfield_t **)dst) = ((py_bitfield_t *)arg)->native; + break; + + default: + assert(false); + break; + + } + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : field = structure interne à copier en objet Python. * * * * Description : Convertit une structure de type 'bitfield_t' en objet Python.* diff --git a/plugins/pychrysalide/common/bits.h b/plugins/pychrysalide/common/bits.h index 6ddaaa5..804c3b5 100644 --- a/plugins/pychrysalide/common/bits.h +++ b/plugins/pychrysalide/common/bits.h @@ -40,6 +40,9 @@ PyTypeObject *get_python_bitfield_type(void); /* Prend en charge l'objet 'pychrysalide.common.BitField'. */ bool ensure_python_bitfield_is_registered(void); +/* Tente de convertir en champ de bits. */ +int convert_to_bitfield(PyObject *, void *); + /* Convertit une structure de type 'bitfield_t' en objet Python. */ PyObject *build_from_internal_bitfield(const bitfield_t *); diff --git a/src/common/bits.c b/src/common/bits.c index a450bb2..d3b45bb 100644 --- a/src/common/bits.c +++ b/src/common/bits.c @@ -51,6 +51,9 @@ static bitfield_t *_create_bit_field(size_t); /* Détermine si un ensemble de bits est homogène dans un champ. */ static bool test_state_in_bit_field(const bitfield_t *, size_t, size_t, bool); +/* Teste l'état de bits selon un masque de bits. */ +static bool test_state_within_bit_field(const bitfield_t *, size_t, const bitfield_t *, bool); + /****************************************************************************** @@ -556,6 +559,131 @@ bool test_all_in_bit_field(const bitfield_t *field, size_t first, size_t count) /****************************************************************************** * * +* Paramètres : field = champ de bits à modifier. * +* first = indice du premier bit à traiter. * +* mask = second champ de bits à tester logiquement. * +* state = état global à retrouver idéalement. * +* * +* Description : Teste l'état de bits selon un masque de bits. * +* * +* Retour : true si les bits visés sont tous à l'état indiqué. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool test_state_within_bit_field(const bitfield_t *field, size_t first, const bitfield_t *mask, bool state) +{ + bool result; /* Bilan à retourner */ + size_t start; /* Mot de départ dans le champ */ + size_t offset; /* Décalage des mots à testter */ + size_t remaining; /* Taille du dernier tronçon */ + unsigned long finalcut; /* Limitation du mot final */ + size_t i; /* Boucle de parcours */ + size_t windex; /* Indice du mot courant */ + unsigned long word; /* Mot reconstituté à tester */ + unsigned long bitmask; /* Masque à appliquer */ + unsigned long test; /* Valeur résultante du test */ + + result = true; + + assert((first + mask->length) <= field->length); + + start = first / (sizeof(unsigned long) * 8); + offset = first % (sizeof(unsigned long) * 8); + + remaining = mask->length % (sizeof(unsigned long) * 8); + + if (remaining == 0) + finalcut = ~0lu; + else + finalcut = (1lu << remaining) - 1; + + for (i = 0; i < mask->requested && result; i++) + { + windex = start + i; + + if (offset == 0) + word = field->bits[windex]; + + else + { + word = field->bits[windex] >> offset; + if ((windex + 1) < field->requested) + word |= field->bits[start + i + 1] << (sizeof(unsigned long) * 8 - offset); + } + + bitmask = mask->bits[i]; + + test = word ^ bitmask; + + if ((i + 1) == mask->requested) + { + bitmask &= finalcut; + test &= finalcut; + } + + result = (state ? test == 0 : test == bitmask); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : field = champ de bits à modifier. * +* first = indice du premier bit à traiter. * +* mask = second champ de bits à tester logiquement. * +* * +* Description : Teste l'état à 0 de bits selon un masque de bits. * +* * +* Retour : true si les bits visés sont à l'état bas. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool test_zeros_within_bit_field(const bitfield_t *field, size_t first, const bitfield_t *mask) +{ + bool result; /* Valeur retrouvée à renvoyer */ + + result = test_state_within_bit_field(field, first, mask, false); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : field = champ de bits à modifier. * +* first = indice du premier bit à traiter. * +* mask = second champ de bits à tester logiquement. * +* * +* Description : Teste l'état à 1 de bits selon un masque de bits. * +* * +* Retour : true si les bits visés sont à l'état haut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool test_ones_within_bit_field(const bitfield_t *field, size_t first, const bitfield_t *mask) +{ + bool result; /* Valeur retrouvée à renvoyer */ + + result = test_state_within_bit_field(field, first, mask, true); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : field = champ de bits à consulter. * * * * Description : Détermine le nombre de bits à 1 dans un champ. * diff --git a/src/common/bits.h b/src/common/bits.h index 96ea06a..fb231d5 100644 --- a/src/common/bits.h +++ b/src/common/bits.h @@ -78,6 +78,12 @@ bool test_none_in_bit_field(const bitfield_t *, size_t, size_t); /* Détermine si un ensemble de bits est à 1 dans un champ. */ bool test_all_in_bit_field(const bitfield_t *, size_t, size_t); +/* Teste l'état à 0 de bits selon un masque de bits. */ +bool test_zeros_within_bit_field(const bitfield_t *, size_t, const bitfield_t *); + +/* Teste l'état à 1 de bits selon un masque de bits. */ +bool test_ones_within_bit_field(const bitfield_t *, size_t, const bitfield_t *); + /* Détermine le nombre de bits à 1 dans un champ. */ size_t popcount_for_bit_field(const bitfield_t *); diff --git a/tests/common/bitfield.py b/tests/common/bitfield.py index e014111..7359a8a 100644 --- a/tests/common/bitfield.py +++ b/tests/common/bitfield.py @@ -118,6 +118,47 @@ class TestBitFields(ChrysalideTestCase): self.assertTrue(bf.test_none(0, 54)) + def testBitFieldWithBitField(self): + """Test bits in bitfields against other bitfields.""" + + bf = BitField(32, 0) + bf.set(8, 16) + + mask = BitField(8, 1) + + self.assertTrue(bf.test_ones_with(8, mask)) + self.assertTrue(bf.test_ones_with(16, mask)) + self.assertFalse(bf.test_ones_with(17, mask)) + self.assertTrue(bf.test_zeros_with(24, mask)) + + bf = BitField(256, 0) + bf.set(60, 8) + bf.set(126, 10) + + mask = BitField(4, 1) + + self.assertTrue(bf.test_zeros_with(8, mask)) + self.assertTrue(bf.test_zeros_with(122, mask)) + + self.assertFalse(bf.test_zeros_with(58, mask)) + self.assertFalse(bf.test_ones_with(58, mask)) + self.assertTrue(bf.test_ones_with(60, mask)) + self.assertFalse(bf.test_zeros_with(63, mask)) + self.assertTrue(bf.test_ones_with(64, mask)) + self.assertFalse(bf.test_zeros_with(65, mask)) + self.assertFalse(bf.test_ones_with(65, mask)) + + self.assertFalse(bf.test_zeros_with(125, mask)) + self.assertFalse(bf.test_ones_with(125, mask)) + self.assertTrue(bf.test_ones_with(128, mask)) + self.assertFalse(bf.test_zeros_with(129, mask)) + self.assertTrue(bf.test_ones_with(132, mask)) + self.assertFalse(bf.test_zeros_with(133, mask)) + self.assertFalse(bf.test_ones_with(133, mask)) + + self.assertTrue(bf.test_zeros_with(136, mask)) + + def testPopCountForBitField(self): """Count bits set to 1 in bitfield.""" -- cgit v0.11.2-87-g4458