blob: 7b4912bdd1168c6b304e9ff5704166bb776e5396 [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.
// part of "core_patch.dart";
@pragma("wasm:entry-point")
class _GrowableList<E> extends _ModifiableList<E> {
void insert(int index, E element) {
if ((index < 0) || (index > length)) {
throw new RangeError.range(index, 0, length);
}
int oldLength = this.length;
add(element);
if (index == oldLength) {
return;
}
Lists.copy(this, index, this, index + 1, oldLength - index);
this[index] = element;
}
E removeAt(int index) {
var result = this[index];
int newLength = this.length - 1;
if (index < newLength) {
Lists.copy(this, index + 1, this, index, newLength - index);
}
this.length = newLength;
return result;
}
bool remove(Object? element) {
for (int i = 0; i < this.length; i++) {
if (this[i] == element) {
removeAt(i);
return true;
}
}
return false;
}
void insertAll(int index, Iterable<E> iterable) {
if (index < 0 || index > length) {
throw new RangeError.range(index, 0, length);
}
if (iterable is! _ListBase) {
// Read out all elements before making room to ensure consistency of the
// modified list in case the iterator throws.
iterable = _List.of(iterable);
}
int insertionLength = iterable.length;
int capacity = _capacity;
int newLength = length + insertionLength;
if (newLength > capacity) {
do {
capacity = _nextCapacity(capacity);
} while (newLength > capacity);
_grow(capacity);
}
_setLength(newLength);
setRange(index + insertionLength, this.length, this, index);
setAll(index, iterable);
}
void removeRange(int start, int end) {
RangeError.checkValidRange(start, end, this.length);
Lists.copy(this, end, this, start, this.length - end);
this.length = this.length - (end - start);
}
_GrowableList._(int length, int capacity) : super(length, capacity);
factory _GrowableList(int length) {
return _GrowableList<E>._(length, length);
}
factory _GrowableList.withCapacity(int capacity) {
return _GrowableList<E>._(0, capacity);
}
// Specialization of List.empty constructor for growable == true.
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
factory _GrowableList.empty() => _GrowableList(0);
// Specialization of List.filled constructor for growable == true.
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
factory _GrowableList.filled(int length, E fill) {
final result = _GrowableList<E>(length);
if (fill != null) {
for (int i = 0; i < result.length; i++) {
result[i] = fill;
}
}
return result;
}
// Specialization of List.generate constructor for growable == true.
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
factory _GrowableList.generate(int length, E generator(int index)) {
final result = _GrowableList<E>(length);
for (int i = 0; i < result.length; ++i) {
result[i] = generator(i);
}
return result;
}
// Specialization of List.of constructor for growable == true.
factory _GrowableList.of(Iterable<E> elements) {
if (elements is _ListBase) {
return _GrowableList._ofListBase(unsafeCast(elements));
}
if (elements is EfficientLengthIterable) {
return _GrowableList._ofEfficientLengthIterable(unsafeCast(elements));
}
return _GrowableList._ofOther(elements);
}
factory _GrowableList._ofListBase(_ListBase<E> elements) {
final int length = elements.length;
final list = _GrowableList<E>(length);
for (int i = 0; i < length; i++) {
list[i] = elements[i];
}
return list;
}
factory _GrowableList._ofEfficientLengthIterable(
EfficientLengthIterable<E> elements) {
final int length = elements.length;
final list = _GrowableList<E>(length);
if (length > 0) {
int i = 0;
for (var element in elements) {
list[i++] = element;
}
if (i != length) throw ConcurrentModificationError(elements);
}
return list;
}
factory _GrowableList._ofOther(Iterable<E> elements) {
final list = _GrowableList<E>(0);
for (var elements in elements) {
list.add(elements);
}
return list;
}
_GrowableList._withData(WasmObjectArray<Object?> data)
: super._withData(data.length, data);
int get _capacity => _data.length;
void set length(int new_length) {
if (new_length > length) {
// Verify that element type is nullable.
null as E;
if (new_length > _capacity) {
_grow(new_length);
}
_setLength(new_length);
return;
}
final int new_capacity = new_length;
// We are shrinking. Pick the method which has fewer writes.
// In the shrink-to-fit path, we write |new_capacity + new_length| words
// (null init + copy).
// In the non-shrink-to-fit path, we write |length - new_length| words
// (null overwrite).
final bool shouldShrinkToFit =
(new_capacity + new_length) < (length - new_length);
if (shouldShrinkToFit) {
_shrink(new_capacity, new_length);
} else {
for (int i = new_length; i < length; i++) {
_data.write(i, null);
}
}
_setLength(new_length);
}
void _setLength(int new_length) {
_length = new_length;
}
void add(E value) {
var len = length;
if (len == _capacity) {
_growToNextCapacity();
}
_setLength(len + 1);
this[len] = value;
}
void addAll(Iterable<E> iterable) {
var len = length;
if (iterable is EfficientLengthIterable) {
if (identical(iterable, this)) {
throw new ConcurrentModificationError(this);
}
var cap = _capacity;
// Pregrow if we know iterable.length.
var iterLen = iterable.length;
if (iterLen == 0) {
return;
}
var newLen = len + iterLen;
if (newLen > cap) {
do {
cap = _nextCapacity(cap);
} while (newLen > cap);
_grow(cap);
}
}
Iterator it = iterable.iterator;
if (!it.moveNext()) return;
do {
while (len < _capacity) {
int newLen = len + 1;
this._setLength(newLen);
this[len] = it.current;
if (!it.moveNext()) return;
if (this.length != newLen) throw new ConcurrentModificationError(this);
len = newLen;
}
_growToNextCapacity();
} while (true);
}
E removeLast() {
var len = length - 1;
var elem = this[len];
this.length = len;
return elem;
}
// Shared array used as backing for new empty growable lists.
static final WasmObjectArray<Object?> _emptyData =
WasmObjectArray<Object?>(0);
static WasmObjectArray<Object?> _allocateData(int capacity) {
if (capacity == 0) {
// Use shared empty list as backing.
return _emptyData;
}
return new WasmObjectArray<Object?>(capacity);
}
// Grow from 0 to 3, and then double + 1.
int _nextCapacity(int old_capacity) => (old_capacity * 2) | 3;
void _grow(int new_capacity) {
var newData = WasmObjectArray<Object?>(new_capacity);
for (int i = 0; i < length; i++) {
newData.write(i, this[i]);
}
_data = newData;
}
void _growToNextCapacity() {
_grow(_nextCapacity(_capacity));
}
void _shrink(int new_capacity, int new_length) {
var newData = _allocateData(new_capacity);
for (int i = 0; i < new_length; i++) {
newData.write(i, this[i]);
}
_data = newData;
}
Iterator<E> get iterator {
return new _GrowableListIterator<E>(this);
}
}
// Iterator for growable lists.
class _GrowableListIterator<E> implements Iterator<E> {
final _GrowableList<E> _list;
final int _length; // Cache list length for modification check.
int _index;
E? _current;
_GrowableListIterator(_GrowableList<E> list)
: _list = list,
_length = list.length,
_index = 0 {
assert(list is _List<E> || list is _ImmutableList<E>);
}
E get current => _current as E;
bool moveNext() {
if (_list.length != _length) {
throw ConcurrentModificationError(_list);
}
if (_index >= _length) {
_current = null;
return false;
}
_current = unsafeCast(_list._data.read(_index));
_index++;
return true;
}
}