BitVector sealed
sealed class BitVector implements Comparable<BitVector>An immutable, indexable sequence of bits with rich operations for binary data manipulation.
BitVector supports bitwise operations (and, or, xor, not), shifting (shiftLeft, shiftRight, rotateLeft, rotateRight), slicing (take, drop, slice), and conversion to and from various base encodings (binary, hex, base32, base58, base64).
Internally, a BitVector is represented as a persistent tree of byte chunks. This enables efficient concatenation and lazy evaluation via unfold and bufferBy while maintaining immutability.
final bv = BitVector.fromValidHex('deadbeef');
print(bv.size); // 32
print(bv.toInt()); // 0xdeadbeef (signed)See also:
- ByteVector, which operates at byte granularity.
- Bytes, the leaf node that backs most
BitVectorinstances.
Implemented types
- Comparable<T>
Implementers
Constructors
BitVector.bit() factory
factory BitVector.bit(bool high)Creates a single-bit vector that is high if high is true, low otherwise.
Implementation
factory BitVector.bit(bool high) => high ? one : zero;BitVector.bits() factory
factory BitVector.bits(Iterable<bool> b)Creates a bit vector from an iterable of boolean values.
Each true value corresponds to a high bit and each false value corresponds to a low bit.
Implementation
factory BitVector.bits(Iterable<bool> b) => IList.fromDart(
b,
).zipWithIndex().foldLeft(BitVector.low(b.length), (acc, b) => acc.update(b.$2, b.$1));BitVector.byte() factory
factory BitVector.byte(int byte)Creates an 8-bit vector from the given byte value.
Implementation
factory BitVector.byte(int byte) => _toBytes(ByteVector([byte]), 8);BitVector.concat() factory
factory BitVector.concat(RIterableOnce<BitVector> bvs)Creates a bit vector by concatenating all vectors in bvs.
Implementation
factory BitVector.concat(RIterableOnce<BitVector> bvs) =>
bvs.iterator.foldLeft(BitVector.empty, (a, b) => a.concat(b));BitVector.concatDart() factory
factory BitVector.concatDart(Iterable<BitVector> bvs)Creates a bit vector by concatenating all vectors in bvs.
Variant of BitVector.concat that accepts a Dart Iterable.
Implementation
factory BitVector.concatDart(Iterable<BitVector> bvs) =>
RIterator.fromDart(bvs.iterator).foldLeft(BitVector.empty, (a, b) => a.concat(b));BitVector.fill() factory
factory BitVector.fill(int n, bool high)Creates an n-bit vector with every bit set to high.
Implementation
factory BitVector.fill(int n, bool high) {
final needed = _bytesNeededForBits(n);
final bs = ByteVector.fill(needed, high ? -1 : 0);
return _toBytes(bs, n);
}BitVector.from() factory
factory BitVector.from(RIterableOnce<int> bs)Creates a bit vector from a sequence of bytes.
The resulting vector's size is bs.size * 8.
Implementation
factory BitVector.from(RIterableOnce<int> bs) => _toBytes(ByteVector.from(bs), bs.size * 8);BitVector.fromBigInt() factory
factory BitVector.fromBigInt(
BigInt value, {
Option<int> size = const None(),
Endian ordering = Endian.big,
})Creates a bit vector from a BigInt value.
If size is provided, the result is exactly that many bits. Otherwise the size is value.bitLength + 1 (to include a sign bit). Use ordering to specify byte order (Endian.big by default).
Implementation
factory BitVector.fromBigInt(
BigInt value, {
Option<int> size = const None(),
Endian ordering = Endian.big,
}) {
final actualSize = size.getOrElse(() => value.bitLength + 1);
final bits = BitVector.fromValidBin(value.toRadixString(2));
late BitVector relevantBits;
if (bits.size < actualSize) {
relevantBits = BitVector.fill(actualSize - bits.size, false).concat(bits);
} else {
relevantBits = bits.takeRight(actualSize);
}
return ordering == Endian.big ? relevantBits : relevantBits.reverseByteOrder();
}BitVector.fromByteVector() factory
factory BitVector.fromByteVector(ByteVector bs)Creates a bit vector from the contents of bs.
The resulting vector's size is bs.size * 8.
Implementation
factory BitVector.fromByteVector(ByteVector bs) => _toBytes(bs, bs.size * 8);BitVector.fromDart() factory
factory BitVector.fromDart(Iterable<int> bs)Creates a bit vector from a Dart iterable of bytes.
The resulting vector's size is bs.length * 8.
Implementation
factory BitVector.fromDart(Iterable<int> bs) => _toBytes(ByteVector.fromDart(bs), bs.length * 8);BitVector.fromInt() factory
factory BitVector.fromInt(int i, {int? size, Endian ordering = Endian.big})Creates a bit vector from an integer value.
The resulting vector has size bits (defaults to Integer.size). Use ordering to specify byte order (Endian.big by default).
Implementation
factory BitVector.fromInt(
int i, {
int? size,
Endian ordering = Endian.big,
}) {
final nBits = size ?? Integer.size;
final Uint8List bytes;
if (!_kIsWeb) {
bytes = Uint8List(8)..buffer.asByteData().setInt64(0, i);
} else {
bytes = Uint8List(8);
final view = bytes.buffer.asByteData();
view.setInt32(0, (i / 4294967296).floor());
view.setInt32(4, i & 0xFFFFFFFF);
}
final relevantBits = ByteVector(bytes).bits.shiftLeft(64 - nBits).take(nBits);
return ordering == Endian.big ? relevantBits : relevantBits.reverseByteOrder();
}BitVector.high() factory
factory BitVector.high(int size)Creates an size-bit vector with all bits high.
Implementation
factory BitVector.high(int size) => BitVector.fill(size, true);BitVector.low() factory
factory BitVector.low(int size)Creates an size-bit vector with all bits low.
Implementation
factory BitVector.low(int size) => BitVector.fill(size, false);BitVector.view() factory
factory BitVector.view(Uint8List bs, {int? sizeInBits})Creates a bit vector backed by the given Uint8List without copying.
If sizeInBits is provided, only that many bits are considered valid; otherwise the full byte array length in bits is used.
Implementation
factory BitVector.view(Uint8List bs, {int? sizeInBits}) =>
_toBytes(ByteVector.view(bs), sizeInBits ?? bs.length * 8);Properties
bytes no setter
ByteVector get bytesReturns the underlying bytes of this bit vector.
Implementation
ByteVector get bytes => toByteVector();hashCode no setter override
int get hashCodeThe hash code for this object.
A hash code is a single integer which represents the state of the object that affects operator == comparisons.
All objects have hash codes. The default hash code implemented by Object represents only the identity of the object, the same way as the default operator == implementation only considers objects equal if they are identical (see identityHashCode).
If operator == is overridden to use the object state instead, the hash code must also be changed to represent that state, otherwise the object cannot be used in hash based data structures like the default Set and Map implementations.
Hash codes must be the same for objects that are equal to each other according to operator ==. The hash code of an object should only change if the object changes in a way that affects equality. There are no further requirements for the hash codes. They need not be consistent between executions of the same program and there are no distribution guarantees.
Objects that are not equal are allowed to have the same hash code. It is even technically allowed that all instances have the same hash code, but if clashes happen too often, it may reduce the efficiency of hash-based data structures like HashSet or HashMap.
If a subclass overrides hashCode, it should override the operator == operator as well to maintain consistency.
Implementation
@override
int get hashCode => Object.hash(size, bytes);head no setter
bool get headReturns the first bit of this vector or throws if vector is emtpy.
Implementation
bool get head => get(0);headOption no setter
Option<bool> get headOptionReturns the first bit of this vector or None if vector is emtpy.
Implementation
Option<bool> get headOption => lift(0);init no setter
BitVector get initReturns a vector of all bits in this vector except the last bit.
Implementation
BitVector get init => dropRight(1);isEmpty no setter
bool get isEmptyReturns true if this vector has no bits.
Implementation
bool get isEmpty => sizeLessThan(1);last no setter
bool get lastReturns the last bit in this vector or throws if vector is empty.
Implementation
bool get last => get(size - 1);lastOption no setter
Option<bool> get lastOptionReturns the last bit in this vector or returns None if vector is empty.
Implementation
Option<bool> get lastOption => lift(size - 1);length no setter
int get lengthAlias for size.
Implementation
int get length => size;nonEmpty no setter
bool get nonEmptyReturns true if this vector has a non-zero number of bits.
Implementation
bool get nonEmpty => !isEmpty;not no setter
BitVector get notReturns a bitwise complement of this BitVector.
Implementation
BitVector get not => _mapBytes((b) => b.not);reverse no setter
BitVector get reverseReturns a bit vector with the bits in reverse order.
Implementation
BitVector get reverse => BitVector.fromByteVector(
compact().underlying.reverse.map(_reverseBitsInByte),
).drop(8 - _validBitsInLastByte(size));runtimeType no setter inherited
Type get runtimeTypeA representation of the runtime type of the object.
Inherited from Object.
Implementation
external Type get runtimeType;size no setter
int get sizeReturns number of bits in this vector.
Implementation
int get size;tail no setter
BitVector get tailReturns a vector of all bits in this vector except the first bit.
Implementation
BitVector get tail => drop(1);Methods
acquire()
Returns a vector whose contents are the results of taking the first n bits of this vector.
If this vector does not contain at least n bits, an error message is returned.
Implementation
Either<String, BitVector> acquire(int n) => Either.cond(
() => sizeGreaterThanOrEqual(n),
() => take(n),
() => 'cannot acquire $n bits from a vector that contains $size bits',
);acquireThen()
R acquireThen<R>(int n, R Function(String) err, R Function(BitVector) f)Like aquire, but immediately consumes the Either via the pair of functions err and f.
Implementation
R acquireThen<R>(
int n,
Function1<String, R> err,
Function1<BitVector, R> f,
) =>
sizeGreaterThanOrEqual(n)
? f(take(n))
: err('cannot acquire $n bits from a vector that contains $size bits');align()
Bytes align()Produce a single flat Bytes by interpreting any non-byte-aligned appends or drops. Unlike compact, the underlying ByteVector is not necessarily copied.
Implementation
Bytes align();and()
Returns a bitwise AND of this BitVector with the specified BitVector.
Implementation
BitVector and(BitVector other) => _zipBytesWith(other, (a, b) => a & b);append()
BitVector append(bool b)Returns a new vector with the specified bit appended.
Implementation
BitVector append(bool b) => concat(BitVector.bit(b));bufferBy()
BitVector bufferBy([int chunkSizeInBits = 8192])Returns a buffered version of this bit vector that amortizes appends by collecting them into chunks of chunkSizeInBits bits.
Implementation
BitVector bufferBy([int chunkSizeInBits = 8192]) {
switch (this) {
case final _Buffer b:
if (b.lastChunk.length * 8 >= chunkSizeInBits) {
return b;
} else {
return b.unbuffer().bufferBy(chunkSizeInBits);
}
default:
return _Buffer(this, Uint8List((chunkSizeInBits + 7) ~/ 8), 0, _BufferState(0));
}
}call()
bool call(int n)Alias for get.
Implementation
bool call(int n) => get(n);clear()
BitVector clear(int n)Returns a new bit vector with the nth bit low (and all other bits unmodified).
Implementation
BitVector clear(int n) => update(n, false);compact()
Bytes compact()Compacts this bit vector into a single contiguous Bytes node.
If this vector already consists of a single chunk, the underlying byte vector may be returned without copying. Use copy when a guaranteed fresh copy is required.
Implementation
Bytes compact() {
if (_bytesNeededForBits(size) > Integer.maxValue) {
throw ArgumentError('cannot compact bit vector of size ${size.toDouble() / 8 / 1e9} GB');
}
IVector<Bytes> go(IList<BitVector> b, IVector<Bytes> acc) {
var currentB = b;
var currentAcc = acc;
while (currentB.nonEmpty) {
final head = currentB.head;
final rem = currentB.tail;
switch (head) {
case final _Suspend s:
currentB = rem.prepended(s.underlying);
case final Bytes bytesNode:
currentB = rem;
currentAcc = currentAcc.appended(bytesNode);
case final _Drop d:
currentB = rem;
currentAcc = currentAcc.appended(d.interpretDrop());
case _Append(left: final l, right: final r):
currentB = rem.prepended(r).prepended(l);
case final _Chunks c:
currentB = rem.prepended(c.chunks.right).prepended(c.chunks.left);
case final _Buffer b:
currentB = rem;
currentAcc = currentAcc.appended(b.unbuffer().align());
}
}
return currentAcc;
}
switch (this) {
case final Bytes bs:
final b2 = bs.underlying.compact();
return b2 == bs.underlying ? bs : Bytes(b2, bs.size);
case final _Drop d:
final bs = d.interpretDrop();
final b2 = bs.underlying.compact();
return b2 == bs.underlying ? bs : Bytes(b2, bs.size);
default:
final balanced = _reduceBalanced(
go(ilist([this]), IVector.empty()),
(bv) => bv.size,
(x, y) => x.combine(y),
);
return Bytes(balanced.underlying.compact(), balanced.size);
}
}compareTo() override
int compareTo(BitVector that)Compares this object to another object.
Returns a value like a Comparator when comparing this to other. That is, it returns a negative integer if this is ordered before other, a positive integer if this is ordered after other, and zero if this and other are ordered together.
The other argument must be a value that is comparable to this object.
Implementation
@override
int compareTo(BitVector that) {
if (this == that) {
return 0;
} else {
final thisLength = length;
final thatLength = that.length;
final commonLength = min(thisLength, thatLength);
var i = 0;
while (i < commonLength) {
final thisI = get(i);
final cmp =
thisI == that.get(i)
? 0
: thisI
? 1
: -1;
if (cmp != 0) return cmp;
i = i + 1;
}
if (thisLength < thatLength) {
return -1;
} else if (thisLength > thatLength) {
return 1;
} else {
return 0;
}
}
}concat()
Returns a new bit vector representing this vector's contents followed by the specified vector's contents.
Implementation
BitVector concat(BitVector b2) {
return isEmpty ? b2 : _Chunks(_Append(this, b2));
}consume()
Consumes the first n bits of this vector and decodes them with the specified function, resulting in a vector of the remaining bits and the decoded value. If this vector does not have n bits or an error occurs while decoding, an error is returned instead.
Implementation
Either<String, (BitVector, A)> consume<A>(
int n,
Function1<BitVector, Either<String, A>> decode,
) => acquire(n).flatMap((toDecode) => decode(toDecode).map((decoded) => (drop(n), decoded)));consumeThen()
If this vector has at least n bits, returns f(take(n),drop(n)), otherwise calls err with a meaningful error message. This function can be used to avoid intermediate allocations of Either objects when using acquire or consume directly.
Implementation
R consumeThen<R>(
int n,
Function1<String, R> err,
Function2<BitVector, BitVector, R> f,
) {
if (sizeGreaterThanOrEqual(n)) {
return f(take(n), drop(n)); // todo unsafeTake, unsafeDrop
} else {
return err("cannot acquire $n bits from a vector that contains $size bits");
}
}containsSlice()
bool containsSlice(BitVector slice)Determines if the specified slice is in this vector.
Implementation
bool containsSlice(BitVector slice) => indexOfSlice(slice) >= 0;copy()
Bytes copy()Return a BitVector with the same contents as this, but based off a single flat ByteVector. This function is guaranteed to copy all the bytes in this BitVector, unlike compact, which may no-op if this BitVector already consists of a single ByteVector chunk.
Implementation
Bytes copy() => switch (this) {
final Bytes b => Bytes(b.underlying.copy(), b.size),
_ => compact(),
};drop()
BitVector drop(int n)Returns a vector of all bits in this vector except the first n bits.
The resulting vector's size is 0 max (size - n).
Implementation
BitVector drop(int n);dropRight()
BitVector dropRight(int n)Returns a vector of all bits in this vector except the last n bits.
The resulting vector's size is 0 max (size - n).
Implementation
BitVector dropRight(int n) {
if (n <= 0) {
return this;
} else if (n >= size) {
return BitVector.empty;
} else {
return take(size - n);
}
}dropWhile()
BitVector dropWhile(bool Function(bool) f)Drops the longest prefix of bits that satisfy f.
Implementation
BitVector dropWhile(Function1<bool, bool> f) {
var toDrop = 0;
while (toDrop < size && f(get(toDrop))) {
toDrop += 1;
}
return drop(toDrop);
}endsWith()
bool endsWith(BitVector b)Returns true if this bit vector ends with the specified vector.
Implementation
bool endsWith(BitVector b) => takeRight(b.size) == b;force()
BitVector force()Forces any Suspend nodes in this BitVector and ensures the tree is balanced.
Implementation
BitVector force() {
BitVector go(IVector<BitVector> cont) {
var currentCont = cont;
while (currentCont.nonEmpty) {
final cur = currentCont.head;
final tail = currentCont.tail;
switch (cur) {
case final Bytes b:
return tail.foldLeft(b, (a, b) => a.concat(b));
case _Append(left: final l, right: final r):
currentCont = tail.prepended(r).prepended(l);
case final _Drop d:
return tail.foldLeft(d, (a, b) => a.concat(b));
case final _Suspend s:
currentCont = tail.prepended(s.underlying);
case final _Chunks c:
currentCont = tail.prepended(c.chunks);
case final _Buffer b:
currentCont = tail.prepended(b.unbuffer());
}
}
return currentCont.foldLeft(BitVector.empty, (a, b) => a.concat(b));
}
return go(ivec([this]));
}get()
bool get(int n)Returns true if the nth bit is high, false otherwise.
Implementation
bool get(int n);getByte()
int getByte(int n)Returns the nth byte, 0-indexed.
Implementation
int getByte(int n);indexOfSlice()
int indexOfSlice(BitVector slice, [int from = 0])Finds the first index after from of the specified bit pattern in this vector.
Implementation
int indexOfSlice(BitVector slice, [int from = 0]) {
int go(BitVector b, int idx) {
var b2 = b;
var idx2 = idx;
while (true) {
if (b2.startsWith(slice)) {
return idx2;
} else if (b2.isEmpty) {
return -1;
} else {
b2 = b2.tail;
idx2 += 1;
}
}
}
return go(drop(from), from);
}insert()
BitVector insert(int idx, bool b)Returns a vector with the specified bit inserted at the specified index.
Implementation
BitVector insert(int idx, bool b) => take(idx).append(b).concat(drop(idx));invertReverseByteOrder()
BitVector invertReverseByteOrder()Inverse of reverseByteOrder.
Implementation
BitVector invertReverseByteOrder() {
if (size % 8 == 0) {
return reverseByteOrder();
} else {
final validFinalBits = _validBitsInLastByte(size);
final (init, last) = splitAt(size - validFinalBits);
return last.concat(init.bytes.reverse.bits);
}
}lift()
Option<bool> lift(int n)Returns Some(true) if the nth bit is high, Some(false) if low, and None if n >= size.
Implementation
Option<bool> lift(int n) => Option.when(() => sizeGreaterThan(n), () => get(n));noSuchMethod() inherited
dynamic noSuchMethod(Invocation invocation)Invoked when a nonexistent method or property is accessed.
A dynamic member invocation can attempt to call a member which doesn't exist on the receiving object. Example:
dynamic object = 1;
object.add(42); // Statically allowed, run-time errorThis invalid code will invoke the noSuchMethod method of the integer 1 with an Invocation representing the .add(42) call and arguments (which then throws).
Classes can override noSuchMethod to provide custom behavior for such invalid dynamic invocations.
A class with a non-default noSuchMethod invocation can also omit implementations for members of its interface. Example:
class MockList<T> implements List<T> {
noSuchMethod(Invocation invocation) {
log(invocation);
super.noSuchMethod(invocation); // Will throw.
}
}
void main() {
MockList().add(42);
}This code has no compile-time warnings or errors even though the MockList class has no concrete implementation of any of the List interface methods. Calls to List methods are forwarded to noSuchMethod, so this code will log an invocation similar to Invocation.method(#add, [42]) and then throw.
If a value is returned from noSuchMethod, it becomes the result of the original invocation. If the value is not of a type that can be returned by the original invocation, a type error occurs at the invocation.
The default behavior is to throw a NoSuchMethodError.
Inherited from Object.
Implementation
@pragma("vm:entry-point")
@pragma("wasm:entry-point")
external dynamic noSuchMethod(Invocation invocation);or()
Returns a bitwise OR of this BitVector with the specified BitVector.
Implementation
BitVector or(BitVector other) => _zipBytesWith(other, (a, b) => a | b);padLeft()
BitVector padLeft(int n)Returns an n-bit vector whose contents are 0 or more low bits followed by this vector's contents.
Implementation
BitVector padLeft(int n) => size < n ? BitVector.low(n - size).concat(this) : this;padRight()
BitVector padRight(int n)Returns an n-bit vector whose contents are 0 or more low bits followed by this vector's contents.
Implementation
BitVector padRight(int n) => size < n ? concat(BitVector.low(n - size)) : this;padTo()
BitVector padTo(int n)Alias for padRight.
Implementation
BitVector padTo(int n) => padRight(n);patch()
Returns a vector with the specified bit vector replacing bits [idx, idx + b.size].
Implementation
BitVector patch(int idx, BitVector b) => take(idx).concat(b).concat(drop(idx + b.size));populationCount()
int populationCount()Returns the number of bits that are high.
Implementation
int populationCount() {
var count = 0;
var ix = 0;
while (ix < size) {
if (get(ix)) count++;
ix++;
}
return count;
}prepend()
BitVector prepend(bool b)Returns a new vector with the specified bit prepended.
Implementation
BitVector prepend(bool b) => BitVector.bit(b).concat(this);printHexDump()
void printHexDump()Prints a colorized hex dump of this bit vector to stdout.
Implementation
void printHexDump() => HexDumpFormat.defaultFormat.printBits(this);reverseBitOrder()
BitVector reverseBitOrder()Returns a new vector of the same size with the bit order reversed.
Implementation
BitVector reverseBitOrder() {
final reversed = compact().underlying.map(_reverseBitsInByte);
if (size % 8 == 0) {
return BitVector.fromByteVector(reversed);
} else {
final lastIdx = reversed.size - 1;
final toDrop = 8 - _validBitsInLastByte(size);
return BitVector.fromByteVector(
reversed.update(lastIdx, (reversed.get(lastIdx) << toDrop) & 0xff),
).dropRight(toDrop);
}
}reverseByteOrder()
BitVector reverseByteOrder()Returns a new vector of the same size with the byte order reversed.
Note that reverseByteOrder.reverseByteOrder == identity only when size is evenly divisble by 8. To invert reverseByteOrder for an arbitrary size, use invertReverseByteOrder.
Implementation
BitVector reverseByteOrder() {
if (size % 8 == 0) {
return _toBytes(compact().underlying.reverse, size);
} else {
final validFinalBits = _validBitsInLastByte(size);
final last = take(validFinalBits).compact();
final b = drop(validFinalBits).bytes.reverse;
final init = _toBytes(b, size - last.size);
return init.concat(last);
}
}rotateLeft()
BitVector rotateLeft(int n)Returns a BitVector of the same size with each bit circularly shifted to the left n bits.
Implementation
BitVector rotateLeft(int n) {
if (n <= 0 || isEmpty) {
return this;
} else {
final n0 = n % size;
return n0 == 0 ? this : drop(n0).concat(take(n0));
}
}rotateRight()
BitVector rotateRight(int n)Returns a BitVector of the same size with each bit circularly shifted to the right n bits.
Implementation
BitVector rotateRight(int n) {
if (n <= 0 || isEmpty) {
return this;
} else {
final n0 = n % size;
return n0 == 0 ? this : takeRight(n0).concat(dropRight(n0));
}
}set()
BitVector set(int n)Returns a new bit vector with the nth bit high (and all other bits unmodified).
Implementation
BitVector set(int n) => update(n, true);shiftLeft()
BitVector shiftLeft(int n)Returns a BitVector of the same size with each bit shifted to the left n bits.
Implementation
BitVector shiftLeft(int n) {
if (n <= 0) {
return this;
} else if (n >= size) {
return BitVector.low(size);
} else {
return drop(n).concat(BitVector.low(n));
}
}shiftRight()
BitVector shiftRight(int n, bool signExtension)Returns a BitVector of the same size with each bit shifted to the right n bits.
Implementation
BitVector shiftRight(int n, bool signExtension) {
if (isEmpty || n <= 0) {
return this;
} else {
final extensionHigh = signExtension && get(0);
if (n >= size) {
return extensionHigh ? BitVector.high(size) : BitVector.low(size);
} else {
return (extensionHigh ? BitVector.high(n) : BitVector.low(n)).concat(dropRight(n));
}
}
}sizeGreaterThan()
bool sizeGreaterThan(int n)Returns true if the size of this BitVector is greater than n. Unlike size, this forces this BitVector from left to right, halting as soon as it has a definite answer.
Implementation
bool sizeGreaterThan(int n) => n < 0 || !sizeLessThanOrEqual(n);sizeGreaterThanOrEqual()
bool sizeGreaterThanOrEqual(int n)Returns true if the size of this BitVector is greater than or equal to n. Unlike size, this forces this BitVector from left to right, halting as soon as it has a definite answer.
Implementation
bool sizeGreaterThanOrEqual(int n) => n < 0 || !sizeLessThanOrEqual(n - 1);sizeLessThan()
bool sizeLessThan(int n)Returns true if the size of this BitVector is less than n. Unlike size, this forces this BitVector from left to right, halting as soon as it has a definite answer.
Implementation
bool sizeLessThan(int n);sizeLessThanOrEqual()
bool sizeLessThanOrEqual(int n)Returns true if the size of this BitVector is less than or equal to n. Unlike size, this forces this BitVector from left to right, halting as soon as it has a definite answer.
Implementation
bool sizeLessThanOrEqual(int n) => n == Integer.maxValue || sizeLessThan(n + 1);slice()
BitVector slice(int from, int until)Returns a vector made up of the bits starting at index from up to index until, not including the index until.
Implementation
BitVector slice(int from, int until) => drop(from).take(until - max(from, 0));sliding()
Returns an iterator of n-bit sliding windows over this vector, advancing step bits between consecutive windows.
Implementation
RIterator<BitVector> sliding(int n, [int step = 1]) {
assert(n > 0 && step > 0, 'both n and step must be positive');
RIterator<int> limit(RIterator<int> itr) =>
(step < n) ? itr.take((size - n) + 1) : itr.takeWhile((i) => i < size);
return limit(RIterator.iterate(0, (x) => x + step)).map((idx) => slice(idx, idx + n));
}splice()
Returns a vector with the specified bit vector inserted at the specified index.
Implementation
BitVector splice(int idx, BitVector b) => take(idx).concat(b).concat(drop(idx));splitAt()
Record splitAt(int n)Returns a pair of vectors that is equal to (take(n), drop(n)).
Implementation
(BitVector, BitVector) splitAt(int n) => (take(n), drop(n));startsWith()
bool startsWith(BitVector b)Returns true if this bit vector starts with the specified vector.
Implementation
bool startsWith(BitVector b) => take(b.size) == b;take()
BitVector take(int n)Returns a vector of the first n bits of this vector.
The resulting vector's size is n min size.
Note: if an n-bit vector is required, use the acquire method instead.
Implementation
BitVector take(int n);takeRight()
BitVector takeRight(int n)Returns a vector of the last n bits of this vector.
The resulting vector's size is n min size.
Implementation
BitVector takeRight(int n) {
if (n < 0) {
throw ArgumentError('takeRight($n)');
} else if (n >= size) {
return this;
} else {
return drop(size - n);
}
}toBase16()
String toBase16([HexAlphabet alphabet = Alphabets.hexLower])Alias for toHex.
Implementation
String toBase16([HexAlphabet alphabet = Alphabets.hexLower]) => toHex(alphabet);toBase32()
String toBase32([Base32Alphabet alphabet = Alphabets.base32])Encodes this bit vector as a base-32 string.
Implementation
String toBase32([Base32Alphabet alphabet = Alphabets.base32]) => bytes.toBase32(alphabet);toBase64()
String toBase64([Base64Alphabet alphabet = Alphabets.base64])Encodes this bit vector as a base-64 string.
Implementation
String toBase64([Base64Alphabet alphabet = Alphabets.base64]) => bytes.toBase64(alphabet);toBase64NoPad()
String toBase64NoPad()Encodes as base-64 without padding characters.
Implementation
String toBase64NoPad() => toBase64(Alphabets.base64NoPad);toBase64Url()
String toBase64Url()Encodes as URL-safe base-64.
Implementation
String toBase64Url() => toBase64(Alphabets.base64Url);toBase64UrlNoPad()
String toBase64UrlNoPad()Encodes as URL-safe base-64 without padding characters.
Implementation
String toBase64UrlNoPad() => toBase64(Alphabets.base64UrlNoPad);toBigInt()
BigInt toBigInt({bool signed = true, Endian ordering = Endian.big})Converts this bit vector to a BigInt.
If signed is true (the default), the most significant bit is treated as a sign bit. Use ordering to specify byte order.
Implementation
BigInt toBigInt({bool signed = true, Endian ordering = Endian.big}) =>
ordering == Endian.little
? invertReverseByteOrder().toBigInt(signed: signed)
: _getBigEndianBigInt(0, size, signed);toBin()
String toBin([BinaryAlphabet alphabet = Alphabets.binary])Encodes this bit vector as a binary string using the given alphabet.
Implementation
String toBin([BinaryAlphabet alphabet = Alphabets.binary]) =>
bytes.toBin(alphabet).substring(0, size);toByteArray()
Uint8List toByteArray()Returns the contents of this bit vector as a Uint8List.
Implementation
Uint8List toByteArray() => bytes.toByteArray();toByteVector()
ByteVector toByteVector()Converts this bit vector to a ByteVector, clearing any trailing padding bits in the last byte.
Implementation
ByteVector toByteVector() => _clearUnneededBits(size, compact().underlying);toHex()
String toHex([HexAlphabet alphabet = Alphabets.hexLower])Encodes this bit vector as a hexadecimal string using the given alphabet.
Implementation
String toHex([HexAlphabet alphabet = Alphabets.hexLower]) {
final full = bytes.toHex(alphabet);
if (size % 8 == 0) {
return full;
} else if (size % 8 <= 4) {
return full.init;
} else {
return full;
}
}toHexDump()
String toHexDump()Returns a plain-text hex dump of this bit vector (no ANSI colors).
Implementation
String toHexDump() => HexDumpFormat.noAnsi.renderBits(this);toHexDumpColorized()
String toHexDumpColorized()Returns a colorized hex dump of this bit vector.
Implementation
String toHexDumpColorized() => HexDumpFormat.defaultFormat.renderBits(this);toIList()
IList<bool> toIList()Returns an IList of booleans, one per bit.
Implementation
IList<bool> toIList() => IList.tabulate(size, (ix) => get(ix));toInt()
int toInt({bool signed = true, Endian ordering = Endian.big})Converts this bit vector to a Dart int.
If signed is true (the default), the result is sign-extended. Use ordering to specify byte order (Endian.big by default).
Implementation
int toInt({bool signed = true, Endian ordering = Endian.big}) {
return switch (this) {
final Bytes bytes => switch (size) {
32 when signed => ByteData.sublistView(
bytes.underlying.toByteArray(),
).getInt32(0, ordering),
32 when !signed => ByteData.sublistView(
bytes.underlying.toByteArray(),
).getUint32(0, ordering),
16 when signed => ByteData.sublistView(
bytes.underlying.toByteArray(),
).getInt16(0, ordering),
16 when !signed => ByteData.sublistView(
bytes.underlying.toByteArray(),
).getUint16(0, ordering),
8 when signed => ByteData.sublistView(bytes.underlying.toByteArray()).getInt8(0),
8 when !signed => ByteData.sublistView(bytes.underlying.toByteArray()).getUint8(0),
_ =>
ordering == Endian.little
? invertReverseByteOrder().toInt(signed: signed)
: _getBigEndianInt(0, size, signed),
},
_ =>
ordering == Endian.little
? invertReverseByteOrder().toInt(signed: signed)
: _getBigEndianInt(0, size, signed),
};
}toString() override
String toString()A string representation of this object.
Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string representation.
Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.
Implementation
@override
String toString() {
if (isEmpty) {
return 'BitVector.empty';
} else if (sizeLessThan(513)) {
return 'BitVector(${toHex()})';
} else {
return 'BitVector($size, $hashCode)';
}
}unbuffer()
BitVector unbuffer()Materializes any buffered appends, returning an unbuffered bit vector.
Implementation
BitVector unbuffer() => this;update()
BitVector update(int n, bool high)Returns a new bit vector with the nth bit high if high is true or low if high is false.
Implementation
BitVector update(int n, bool high);xor()
Returns a bitwise XOR of this BitVector with the specified BitVector.
Implementation
BitVector xor(BitVector other) => _zipBytesWith(other, (a, b) => a ^ b);Operators
operator <<()
BitVector operator <<(int n)Left shift by n bits.
Implementation
BitVector operator <<(int n) => shiftLeft(n);operator ==() override
bool operator ==(Object other)The equality operator.
The default behavior for all Objects is to return true if and only if this object and other are the same object.
Override this method to specify a different equality relation on a class. The overriding method must still be an equivalence relation. That is, it must be:
Total: It must return a boolean for all arguments. It should never throw.
Reflexive: For all objects
o,o == omust be true.Symmetric: For all objects
o1ando2,o1 == o2ando2 == o1must either both be true, or both be false.Transitive: For all objects
o1,o2, ando3, ifo1 == o2ando2 == o3are true, theno1 == o3must be true.
The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.
If a subclass overrides the equality operator, it should override the hashCode method as well to maintain consistency.
Implementation
@override
bool operator ==(Object other) {
if (other is! BitVector) {
return false;
} else if (other.size != size) {
return false;
} else {
const chunkSize = 8 * 1024 * 64;
var x = this;
var y = other;
while (true) {
if (x.isEmpty) {
return y.isEmpty;
} else {
final chunkX = x.take(chunkSize);
final chunkY = y.take(chunkSize);
if (chunkX.bytes != chunkY.bytes) {
return false;
} else {
x = x.drop(chunkSize);
y = y.drop(chunkSize);
}
}
}
}
}operator >>()
BitVector operator >>(int n)Arithmetic right shift by n bits (sign-extending).
Implementation
BitVector operator >>(int n) => shiftRight(n, true);operator >>>()
BitVector operator >>>(int n)Logical right shift by n bits (zero-filling).
Implementation
BitVector operator >>>(int n) => shiftRight(n, false);operator ^()
Bitwise XOR of this vector and other.
Implementation
BitVector operator ^(BitVector other) => xor(other);operator |()
Bitwise OR of this vector and other.
Implementation
BitVector operator |(BitVector other) => or(other);operator ~()
BitVector operator ~()Bitwise NOT. Returns the complement of this vector.
Implementation
BitVector operator ~() => not;Static Properties
empty final
final BitVector emptyAn empty bit vector containing zero bits.
Implementation
static final BitVector empty = _toBytes(ByteVector.empty, 0);highByte final
final BitVector highByteAn 8-bit vector with all bits high (0xFF).
Implementation
static final highByte = BitVector.high(8);lowByte final
final BitVector lowByteAn 8-bit vector with all bits low (0x00).
Implementation
static final lowByte = BitVector.low(8);one final
final Bytes oneA single-bit vector whose bit is high (1).
Implementation
static final one = _toBytes(ByteVector.of(0xff), 1);zero final
final Bytes zeroA single-bit vector whose bit is low (0).
Implementation
static final zero = _toBytes(ByteVector.of(0x00), 1);Static Methods
fromBase32()
Option<BitVector> fromBase32(
String s, [
Base32Alphabet alphabet = Alphabets.base32,
])Decodes a base-32 string into a BitVector, returning None on failure.
Implementation
static Option<BitVector> fromBase32(
String s, [
Base32Alphabet alphabet = Alphabets.base32,
]) => fromBase32Descriptive(s, alphabet).toOption();fromBase32Descriptive()
Either<String, BitVector> fromBase32Descriptive(
String str, [
Base32Alphabet alphabet = Alphabets.base32,
])Decodes a base-32 string into a BitVector, returning a descriptive error message on the left on failure.
Implementation
static Either<String, BitVector> fromBase32Descriptive(
String str, [
Base32Alphabet alphabet = Alphabets.base32,
]) => fromBase32Internal(str, alphabet).map((a) => a.$1.bits);fromBase64()
Option<BitVector> fromBase64(
String s, [
Base64Alphabet alphabet = Alphabets.base64,
])Decodes a base-64 string into a BitVector, returning None on failure.
Implementation
static Option<BitVector> fromBase64(
String s, [
Base64Alphabet alphabet = Alphabets.base64,
]) => fromBase64Descriptive(s, alphabet).toOption();fromBase64Descriptive()
Either<String, BitVector> fromBase64Descriptive(
String str, [
Base64Alphabet alphabet = Alphabets.base64,
])Decodes a base-64 string into a BitVector, returning a descriptive error message on the left on failure.
Implementation
static Either<String, BitVector> fromBase64Descriptive(
String str, [
Base64Alphabet alphabet = Alphabets.base64,
]) => fromBase64Internal(str, alphabet).map((a) => a.$1.bits);fromBin()
Option<BitVector> fromBin(
String s, [
BinaryAlphabet alphabet = Alphabets.binary,
])Decodes a binary string into a BitVector, returning None on failure.
Implementation
static Option<BitVector> fromBin(
String s, [
BinaryAlphabet alphabet = Alphabets.binary,
]) => fromBinDescriptive(s, alphabet).toOption();fromBinDescriptive()
Either<String, BitVector> fromBinDescriptive(
String s, [
BinaryAlphabet alphabet = Alphabets.binary,
])Decodes a binary string into a BitVector, returning a descriptive error message on the left on failure.
Implementation
static Either<String, BitVector> fromBinDescriptive(
String s, [
BinaryAlphabet alphabet = Alphabets.binary,
]) {
return fromBinInternal(s, alphabet).mapN((bytes, size) {
final toDrop = switch (size) {
0 => 0,
_ when size % 8 == 0 => 0,
_ => 8 - (size % 8),
};
return bytes.bits.drop(toDrop);
});
}fromHex()
Option<BitVector> fromHex(String s, [HexAlphabet alphabet = Alphabets.hexLower])Decodes a hexadecimal string into a BitVector, returning None on failure.
Implementation
static Option<BitVector> fromHex(
String s, [
HexAlphabet alphabet = Alphabets.hexLower,
]) => fromHexDescriptive(s, alphabet).toOption();fromHexDescriptive()
Either<String, BitVector> fromHexDescriptive(
String s, [
HexAlphabet alphabet = Alphabets.hexLower,
])Decodes a hexadecimal string into a BitVector, returning a descriptive error message on the left on failure.
Implementation
static Either<String, BitVector> fromHexDescriptive(
String s, [
HexAlphabet alphabet = Alphabets.hexLower,
]) => fromHexInternal(s, alphabet).mapN((bytes, count) => bytes.bits.drop(count.isEven ? 0 : 4));fromValidBase32()
BitVector fromValidBase32(
String s, [
Base32Alphabet alphabet = Alphabets.base32,
])Decodes a base-32 string into a BitVector, throwing on failure.
Implementation
static BitVector fromValidBase32(
String s, [
Base32Alphabet alphabet = Alphabets.base32,
]) => fromBase32Descriptive(s, alphabet).fold((err) => throw ArgumentError(err), identity);fromValidBase64()
BitVector fromValidBase64(
String s, [
Base64Alphabet alphabet = Alphabets.base64,
])Decodes a base-64 string into a BitVector, throwing on failure.
Implementation
static BitVector fromValidBase64(
String s, [
Base64Alphabet alphabet = Alphabets.base64,
]) => fromBase64Descriptive(s, alphabet).fold((err) => throw ArgumentError(err), identity);fromValidBin()
BitVector fromValidBin(String s, [BinaryAlphabet alphabet = Alphabets.binary])Decodes a binary string into a BitVector, throwing on failure.
Implementation
static BitVector fromValidBin(
String s, [
BinaryAlphabet alphabet = Alphabets.binary,
]) => fromBinDescriptive(s, alphabet).fold((err) => throw ArgumentError(err), identity);fromValidHex()
BitVector fromValidHex(String s, [HexAlphabet alphabet = Alphabets.hexLower])Decodes a hexadecimal string into a BitVector, throwing on failure.
Implementation
static BitVector fromValidHex(
String s, [
HexAlphabet alphabet = Alphabets.hexLower,
]) => fromHexDescriptive(s, alphabet).fold((err) => throw ArgumentError(err), identity);unfold()
Lazily builds a bit vector by repeatedly applying f to a state s.
f returns Some((chunk, nextState)) to emit a chunk and continue, or None() to terminate. The resulting bit vector is evaluated lazily: chunks are not materialized until accessed.
Implementation
static BitVector unfold<S>(S s, Function1<S, Option<(BitVector, S)>> f) {
return _Suspend(() {
return f(s)
.map<BitVector>((tuple) {
final (h, t) = tuple;
return _Append(h, unfold(t, f));
})
.getOrElse(() => BitVector.empty);
});
}