From 7b739f256cb88cb627954d4a1ff04bdb4fc49515 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 3 Jan 2023 15:37:09 +0100
Subject: Fix some mistakes in bitfield operations.

---
 src/common/bits.c        | 21 ++++++++-------
 tests/common/bitfield.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 9 deletions(-)

diff --git a/src/common/bits.c b/src/common/bits.c
index 590c318..a037078 100644
--- a/src/common/bits.c
+++ b/src/common/bits.c
@@ -533,34 +533,35 @@ 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 last_iter;                       /* Dernière itération à mener  */
     size_t i;                               /* Boucle de parcours          */
     unsigned long word;                     /* Mot reconstituté à tester   */
 
-    assert(dest->length <= (first + src->length));
+    assert((first + src->length) <= dest->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;
+    if ((first + src->length) % (sizeof(unsigned long) * 8) > 0)
+        last_iter = src->requested;
     else
-        finalcut = (1ul << remaining) - 1;
+        last_iter = src->requested - 1;
 
-    for (i = 0; i < (src->requested + 1); i++)
+
+    for (i = 0; i <= last_iter; i++)
     {
         if (i < src->requested)
             word = src->bits[i] << offset;
         else
             word = 0;
 
-        if (i > 0)
+        if (i > 0 && offset > 0)
             word |= src->bits[i - 1] >> (sizeof(unsigned long) * 8 - offset);
 
-        if (i == src->requested)
-            word &= finalcut;
+        if (i == last_iter && remaining > 0)
+            word &= (1ul << remaining) - 1;
 
         dest->bits[start + i] |= word;
 
@@ -753,6 +754,8 @@ static bool test_state_within_bit_field(const bitfield_t *field, size_t first, c
 
         test = word ^ bitmask;
 
+        test &= bitmask;
+
         if ((i + 1) == mask->requested)
         {
             bitmask &= finalcut;
diff --git a/tests/common/bitfield.py b/tests/common/bitfield.py
index 8c4a470..aff5de6 100644
--- a/tests/common/bitfield.py
+++ b/tests/common/bitfield.py
@@ -106,6 +106,19 @@ class TestBitFields(ChrysalideTestCase):
 
         self.assertFalse(bf_a.test(67))
 
+        bf_a = BitField(75, 0)
+
+        bf_a.or_at(bf_b, 60)
+
+        self.assertFalse(bf_a.test(59))
+
+        self.assertTrue(bf_a.test(60))
+        self.assertTrue(bf_a.test(61))
+        self.assertFalse(bf_a.test(62))
+        self.assertTrue(bf_a.test(63))
+
+        self.assertFalse(bf_a.test(64))
+
 
     def testBitFieldSwitch(self):
         """Switch various bits in bitfields."""
@@ -216,3 +229,57 @@ class TestBitFields(ChrysalideTestCase):
         bf_b = BitField(9, 1)
 
         self.assertNotEqual(bf_a, bf_b)
+
+
+    def testRealCase00(self):
+        """Test bits in bitfields against other bitfields in a real case (#02)."""
+
+        bf = BitField(128, 0)
+
+        for b in [ 0, 50, 54, 58, 66, 70, 98 ]:
+            bf.set(b, 1)
+
+        mask = BitField(128, 0)
+
+        for b in [ 0, 51 ]:
+            mask.set(b, 1)
+
+        self.assertFalse(bf.test_zeros_with(0, mask))
+
+        self.assertTrue(bf.test_zeros_with(1, mask))
+
+        bf = BitField(32, 0)
+
+        mask = BitField(32, 0)
+
+        self.assertTrue(bf.test_zeros_with(0, mask))
+
+        for b in [ 0, 8, 9, 10 ]:
+            mask.set(b, 1)
+
+        self.assertTrue(bf.test_zeros_with(0, mask))
+
+        bf = BitField(32, 1)
+
+        self.assertFalse(bf.test_zeros_with(0, mask))
+
+        self.assertTrue(bf.test_ones_with(0, mask))
+
+
+    def testRealCase01(self):
+        """Test bits in bitfields against other bitfields in a real case (#01)."""
+
+        bf = BitField(128, 0)
+
+        mask = BitField(128, 0)
+
+        bits = [ 0, 50, 54, 58, 66, 70, 98 ]
+
+        for b in bits:
+            mask.set(b, 1)
+
+        bf.or_at(mask, 0)
+
+        self.assertEqual(mask.popcount, len(bits))
+
+        self.assertEqual(mask.popcount, bf.popcount)
-- 
cgit v0.11.2-87-g4458