-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add BoolList class. (dart-archive/collection#120)
Adds `BoolList` class which implements a list of booleans with one bit of storage per value.
- Loading branch information
1 parent
6ab6093
commit 15a7681
Showing
3 changed files
with
421 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:collection' show ListMixin; | ||
import 'dart:typed_data' show Uint32List; | ||
|
||
import 'unmodifiable_wrappers.dart' show NonGrowableListMixin; | ||
|
||
/// A space-efficient list of boolean values. | ||
/// | ||
/// Uses list of integers as internal storage to reduce memory usage. | ||
abstract class BoolList with ListMixin<bool> { | ||
static const int _entryShift = 5; | ||
|
||
static const int _bitsPerEntry = 32; | ||
|
||
static const int _entrySignBitIndex = 31; | ||
|
||
int _length; | ||
|
||
Uint32List _data; | ||
|
||
BoolList._(this._data, this._length); | ||
|
||
factory BoolList._selectType(int length, bool growable) { | ||
if (growable) { | ||
return _GrowableBoolList(length); | ||
} else { | ||
return _NonGrowableBoolList(length); | ||
} | ||
} | ||
|
||
/// Creates a list of booleans with the provided length. | ||
/// | ||
/// The list is initially filled with the [fill] value, and | ||
/// the list is growable if [growable] is true. | ||
factory BoolList(int length, {bool fill = false, bool growable = false}) { | ||
RangeError.checkNotNegative(length, 'length'); | ||
|
||
BoolList boolist; | ||
if (growable) { | ||
boolist = _GrowableBoolList(length); | ||
} else { | ||
boolist = _NonGrowableBoolList(length); | ||
} | ||
|
||
if (fill) { | ||
boolist.fillRange(0, length, true); | ||
} | ||
|
||
return boolist; | ||
} | ||
|
||
/// Creates an empty list of booleans. | ||
/// | ||
/// The list defaults to being growable unless [growable] is `false`. | ||
/// If [capacity] is provided, and [growable] is not `false`, | ||
/// the implementation will attempt to make space for that | ||
/// many elements before needing to grow its internal storage. | ||
factory BoolList.empty({bool growable = true, int capacity = 0}) { | ||
RangeError.checkNotNegative(capacity, 'length'); | ||
|
||
if (growable) { | ||
return _GrowableBoolList._withCapacity(0, capacity); | ||
} else { | ||
return _NonGrowableBoolList._withCapacity(0, capacity); | ||
} | ||
} | ||
|
||
/// Generates a [BoolList] of values. | ||
/// | ||
/// Creates a [BoolList] with [length] positions and fills it with values created by | ||
/// calling [generator] for each index in the range `0` .. `length - 1` in increasing order. | ||
/// | ||
/// The created list is fixed-length unless [growable] is true. | ||
factory BoolList.generate( | ||
int length, | ||
bool Function(int) generator, { | ||
bool growable = true, | ||
}) { | ||
RangeError.checkNotNegative(length, 'length'); | ||
|
||
var instance = BoolList._selectType(length, growable); | ||
for (var i = 0; i < length; i++) { | ||
instance._setBit(i, generator(i)); | ||
} | ||
return instance; | ||
} | ||
|
||
/// Creates a list containing all [elements]. | ||
/// | ||
/// The [Iterator] of [elements] provides the order of the elements. | ||
/// | ||
/// This constructor creates a growable [BoolList] when [growable] is true; | ||
/// otherwise, it returns a fixed-length list. | ||
factory BoolList.from(Iterable<bool> elements, {bool growable = false}) { | ||
return BoolList._selectType(elements.length, growable)..setAll(0, elements); | ||
} | ||
|
||
@override | ||
int get length => _length; | ||
|
||
@override | ||
bool operator [](int index) { | ||
RangeError.checkValidIndex(index, this, 'index', _length); | ||
return (_data[index >> _entryShift] & | ||
(1 << (index & _entrySignBitIndex))) != | ||
0; | ||
} | ||
|
||
@override | ||
void operator []=(int index, bool val) { | ||
RangeError.checkValidIndex(index, this, 'index', _length); | ||
_setBit(index, val); | ||
} | ||
|
||
@override | ||
void fillRange(int start, int end, [bool? fill]) { | ||
RangeError.checkValidRange(start, end, _length); | ||
fill ??= false; | ||
|
||
var startWord = start >> _entryShift; | ||
var endWord = (end - 1) >> _entryShift; | ||
|
||
var startBit = start & _entrySignBitIndex; | ||
var endBit = (end - 1) & _entrySignBitIndex; | ||
|
||
if (startWord < endWord) { | ||
if (fill) { | ||
_data[startWord] |= -1 << startBit; | ||
_data.fillRange(startWord + 1, endWord, -1); | ||
_data[endWord] |= (1 << (endBit + 1)) - 1; | ||
} else { | ||
_data[startWord] &= (1 << startBit) - 1; | ||
_data.fillRange(startWord + 1, endWord, 0); | ||
_data[endWord] &= -1 << (endBit + 1); | ||
} | ||
} else { | ||
if (fill) { | ||
_data[startWord] |= ((1 << (endBit - startBit + 1)) - 1) << startBit; | ||
} else { | ||
_data[startWord] &= ((1 << startBit) - 1) | (-1 << (endBit + 1)); | ||
} | ||
} | ||
} | ||
|
||
/// Returns custom iterator for [BoolList]. | ||
/// | ||
/// To provide null safety [Iterator.current] getter of returned iterator | ||
/// returns `false` before and after iteration process. | ||
@override | ||
Iterator<bool> get iterator => _BoolListIterator(this); | ||
|
||
void _setBit(int index, bool val) { | ||
if (val) { | ||
_data[index >> _entryShift] |= 1 << (index & _entrySignBitIndex); | ||
} else { | ||
_data[index >> _entryShift] &= ~(1 << (index & _entrySignBitIndex)); | ||
} | ||
} | ||
|
||
static int _lengthInWords(int bitsLength) { | ||
return (bitsLength + (_bitsPerEntry - 1)) >> _entryShift; | ||
} | ||
} | ||
|
||
class _GrowableBoolList extends BoolList { | ||
static const int _growthFactor = 2; | ||
|
||
_GrowableBoolList._withCapacity(int length, int capacity) | ||
: super._( | ||
Uint32List(BoolList._lengthInWords(capacity)), | ||
length, | ||
); | ||
|
||
_GrowableBoolList(int length) | ||
: super._( | ||
Uint32List(BoolList._lengthInWords(length * _growthFactor)), | ||
length, | ||
); | ||
|
||
@override | ||
set length(int length) { | ||
RangeError.checkNotNegative(length, 'length'); | ||
if (length > _length) { | ||
_expand(length); | ||
} else if (length < _length) { | ||
_shrink(length); | ||
} | ||
} | ||
|
||
void _expand(int length) { | ||
if (length > _data.length * BoolList._bitsPerEntry) { | ||
_data = Uint32List( | ||
BoolList._lengthInWords(length * _growthFactor), | ||
)..setAll(0, _data); | ||
} | ||
_length = length; | ||
} | ||
|
||
void _shrink(int length) { | ||
if (length < _length ~/ _growthFactor) { | ||
var newDataLength = BoolList._lengthInWords(length); | ||
_data = Uint32List(newDataLength)..setRange(0, newDataLength, _data); | ||
} | ||
|
||
for (var i = length; i < _data.length * BoolList._bitsPerEntry; i++) { | ||
_setBit(i, false); | ||
} | ||
|
||
_length = length; | ||
} | ||
} | ||
|
||
class _NonGrowableBoolList extends BoolList with NonGrowableListMixin<bool> { | ||
_NonGrowableBoolList._withCapacity(int length, int capacity) | ||
: super._( | ||
Uint32List(BoolList._lengthInWords(capacity)), | ||
length, | ||
); | ||
|
||
_NonGrowableBoolList(int length) | ||
: super._( | ||
Uint32List(BoolList._lengthInWords(length)), | ||
length, | ||
); | ||
} | ||
|
||
class _BoolListIterator implements Iterator<bool> { | ||
bool _current = false; | ||
int _pos = 0; | ||
final int _length; | ||
|
||
final BoolList _boolList; | ||
|
||
_BoolListIterator(this._boolList) : _length = _boolList._length; | ||
|
||
@override | ||
bool get current => _current; | ||
|
||
@override | ||
bool moveNext() { | ||
if (_boolList._length != _length) { | ||
throw ConcurrentModificationError(_boolList); | ||
} | ||
|
||
if (_pos < _boolList.length) { | ||
var pos = _pos++; | ||
_current = _boolList._data[pos >> BoolList._entryShift] & | ||
(1 << (pos & BoolList._entrySignBitIndex)) != | ||
0; | ||
return true; | ||
} | ||
_current = false; | ||
return false; | ||
} | ||
} |
Oops, something went wrong.