From 71a4f800b1d7d214fa223705d70440a7edccaebf Mon Sep 17 00:00:00 2001 From: Lukas Renggli Date: Sun, 3 Sep 2023 18:07:08 +0200 Subject: [PATCH] When growing a BitList make sure all newly exposed values are properly initialized. --- lib/src/collection/bitlist.dart | 48 +++++++++++++++++++-------------- test/collection_test.dart | 17 ++++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/lib/src/collection/bitlist.dart b/lib/src/collection/bitlist.dart index 27f71c0..e720642 100644 --- a/lib/src/collection/bitlist.dart +++ b/lib/src/collection/bitlist.dart @@ -107,26 +107,7 @@ abstract class BitList extends ListBase { @override void fillRange(int start, int end, [bool? fill]) { RangeError.checkValidRange(start, end, length); - if (start == end) return; - final startIndex = start >> bitShift, startBit = start & bitOffset; - final endIndex = (end - 1) >> bitShift, endBit = (end - 1) & bitOffset; - if (startIndex == endIndex) { - if (fill == true) { - buffer[startIndex] |= ((1 << (endBit - startBit + 1)) - 1) << startBit; - } else { - buffer[startIndex] &= ((1 << startBit) - 1) | (bitMask << (endBit + 1)); - } - } else { - if (fill == true) { - buffer[startIndex] |= bitMask << startBit; - buffer.fillRange(startIndex + 1, endIndex, bitMask); - buffer[endIndex] |= (1 << (endBit + 1)) - 1; - } else { - buffer[startIndex] &= (1 << startBit) - 1; - buffer.fillRange(startIndex + 1, endIndex, 0); - buffer[endIndex] &= bitMask << (endBit + 1); - } - } + _fillRange(buffer, start, end, fill ?? false); } /// Sets the bit at the specified [index] to the complement of its current @@ -332,6 +313,10 @@ class GrowableBitList extends BitList { newBuffer.setRange(0, newBuffer.length, buffer); buffer = newBuffer; } + if (_length < length) { + // When growing, make sure we always have predictable state. + _fillRange(buffer, _length, length, false); + } _length = length; } } @@ -425,3 +410,26 @@ const List bitClearMask = [ -1073741825, -2147483649 ]; + +void _fillRange(Uint32List buffer, int start, int end, bool? fill) { + if (start == end) return; + final startIndex = start >> bitShift, startBit = start & bitOffset; + final endIndex = (end - 1) >> bitShift, endBit = (end - 1) & bitOffset; + if (startIndex == endIndex) { + if (fill == true) { + buffer[startIndex] |= ((1 << (endBit - startBit + 1)) - 1) << startBit; + } else { + buffer[startIndex] &= ((1 << startBit) - 1) | (bitMask << (endBit + 1)); + } + } else { + if (fill == true) { + buffer[startIndex] |= bitMask << startBit; + buffer.fillRange(startIndex + 1, endIndex, bitMask); + buffer[endIndex] |= (1 << (endBit + 1)) - 1; + } else { + buffer[startIndex] &= (1 << startBit) - 1; + buffer.fillRange(startIndex + 1, endIndex, 0); + buffer[endIndex] &= bitMask << (endBit + 1); + } + } +} diff --git a/test/collection_test.dart b/test/collection_test.dart index f422655..e512e0f 100644 --- a/test/collection_test.dart +++ b/test/collection_test.dart @@ -607,6 +607,23 @@ void main() { target.length -= 2; expect(target.buffer, same(buffer)); }); + test('length (cleared)', () { + final generator = Random(584); + for (var i = 0; i < 0xff; i++) { + final original = 1 + generator.nextInt(0xff); + final smaller = generator.nextInt(original); + final larger = original + generator.nextInt(0xff); + final target = BitList.filled(original, true, growable: growable); + target.length = smaller; + target.length = larger; + for (var i = 0; i < smaller; i++) { + expect(target[i], isTrue); + } + for (var i = smaller; i < larger; i++) { + expect(target[i], isFalse); + } + } + }); test('removeLast', () { final source = randomBooleans(453, 500); final target = BitList.of(source, growable: growable);