blob: ef2091d3eca85ae19b6c1fcad27bb5a320729f55 [file] [log] [blame]
// Copyright (c) 2022, 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';
import 'dart:typed_data';
// The default set implementation is based on a Uint32List+List where both are
// linear in the number of entries. That means we consume on 64-bit VMs at
// least 12 bytes per entry.
//
// We should consider making a more memory efficient hash set implementation
// that uses Int32List and utilizing the fact that we never store negative
// numbers in it.
typedef IntSet = Set<int>;
/// Set of ints that can hold ints between 0 and up to (but not including)
/// [maxLength].
///
/// The memory usage of this is fixed and depend on the [maxLength], and is
/// something like ~125 kb per mio. as specified by [maxLength], e.g. a max
/// length of 50 mio should take ~6 mb.
class SpecializedIntSet extends SetBase<int> {
final Uint32List _data;
final int maxLength;
int _length = 0;
SpecializedIntSet(this.maxLength)
: _data = Uint32List((maxLength + 31) ~/ 32);
@override
Iterator<int> get iterator => _SpecializedIntSetIterator(this);
@override
int get length => _length;
@override
bool add(int value) {
if (value < 0 || value >= maxLength) {
throw RangeError.range(value, 0, maxLength - 1);
}
final int index = value >> 5;
final int offset = value & 31;
final int mask = 1 << (31 - offset);
if (_data[index] & mask != 0) return false;
_data[index] |= mask;
_length++;
return true;
}
@override
bool contains(Object? element) {
if (element is! int) return false;
if (element < 0) return false;
if (element >= maxLength) return false;
final int index = element >> 5;
final int offset = element & 31;
final int mask = 1 << (31 - offset);
return _data[index] & mask != 0;
}
String getDumpData() {
final StringBuffer sb = StringBuffer();
for (int value in _data) {
if (sb.isNotEmpty) sb.write(" ");
sb.write(value.toRadixString(2).padLeft(32, "0"));
}
return sb.toString();
}
@override
int? lookup(Object? element) {
if (contains(element)) return element as int;
return null;
}
@override
bool remove(Object? value) {
if (value is! int) return false;
if (value < 0) return false;
if (value >= maxLength) return false;
final int index = value >> 5;
final int offset = value & 31;
final int mask = 1 << (31 - offset);
if (_data[index] & mask != 0) {
_data[index] &= ~mask;
_length--;
return true;
}
return false;
}
@override
Set<int> toSet() {
final SpecializedIntSet result = SpecializedIntSet(maxLength);
result._length = _length;
result._data.setRange(0, _data.length, _data);
return result;
}
}
class _SpecializedIntSetIterator implements Iterator<int> {
int? _current;
int _nextToCheck = 0;
int _returns = 0;
final SpecializedIntSet _set;
_SpecializedIntSetIterator(this._set);
@override
int get current => _current ?? (throw StateError('No element'));
@override
bool moveNext() {
if (_returns >= _set.length) return false;
while (_nextToCheck < _set.maxLength) {
final int index = _nextToCheck >> 5;
final int dataAtIndex = _set._data[index];
if (dataAtIndex == 0) {
// Nothing in this byte.
_nextToCheck = (index + 1) << 5;
continue;
}
final int restBytes;
final int offset = _nextToCheck & 31;
{
final int maskRest = (1 << (32 - offset)) - 1;
restBytes = dataAtIndex & maskRest;
}
if (restBytes == 0) {
// No more in this byte.
_nextToCheck = (index + 1) << 5;
continue;
}
int mask = (1 << (31 - offset));
while (restBytes & mask == 0) {
_nextToCheck++;
mask >>= 1;
}
_current = _nextToCheck;
_nextToCheck++;
_returns++;
return true;
}
return false;
}
}