mirror of
https://github.com/Instadapp/Swap-Aggregator-Subgraph.git
synced 2024-07-29 21:57:12 +00:00
549 lines
19 KiB
TypeScript
549 lines
19 KiB
TypeScript
/// <reference path="./rt/index.d.ts" />
|
|
|
|
import { BLOCK_MAXSIZE } from "./rt/common";
|
|
import { COMPARATOR, SORT } from "./util/sort";
|
|
import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, joinReferenceArray } from "./util/string";
|
|
import { idof, isArray as builtin_isArray } from "./builtins";
|
|
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_ILLEGALGENTYPE, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";
|
|
|
|
// @ts-ignore: decorator
|
|
@inline @lazy const MIN_SIZE: usize = 8;
|
|
|
|
/** Ensures that the given array has _at least_ the specified backing size. */
|
|
function ensureCapacity(array: usize, newSize: usize, alignLog2: u32, canGrow: bool = true): void {
|
|
// Depends on the fact that Arrays mimic ArrayBufferView
|
|
var oldCapacity = <usize>changetype<ArrayBufferView>(array).byteLength;
|
|
if (newSize > oldCapacity >>> alignLog2) {
|
|
if (newSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
|
|
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).buffer);
|
|
// Grows old capacity by factor of two.
|
|
// Make sure we don't reach BLOCK_MAXSIZE for new growed capacity.
|
|
let newCapacity = max(newSize, MIN_SIZE) << alignLog2;
|
|
if (canGrow) newCapacity = max(min(oldCapacity << 1, BLOCK_MAXSIZE), newCapacity);
|
|
let newData = __renew(oldData, newCapacity);
|
|
memory.fill(newData + oldCapacity, 0, newCapacity - oldCapacity);
|
|
if (newData !== oldData) { // oldData has been free'd
|
|
store<usize>(array, newData, offsetof<ArrayBufferView>("buffer"));
|
|
store<usize>(array, newData, offsetof<ArrayBufferView>("dataStart"));
|
|
__link(array, changetype<usize>(newData), false);
|
|
}
|
|
store<u32>(array, <u32>newCapacity, offsetof<ArrayBufferView>("byteLength"));
|
|
}
|
|
}
|
|
|
|
export class Array<T> {
|
|
[key: number]: T;
|
|
|
|
// Mimicking ArrayBufferView isn't strictly necessary here but is done to allow glue code
|
|
// to work with typed and normal arrays interchangeably. Technically, normal arrays do not need
|
|
// `dataStart` (equals `buffer`) and `byteLength` (equals computed `buffer.byteLength`), but the
|
|
// block is 16 bytes anyway so it's fine to have a couple extra fields in there.
|
|
|
|
private buffer: ArrayBuffer;
|
|
@unsafe readonly dataStart: usize;
|
|
private byteLength: i32; // Uses here as capacity
|
|
|
|
// Also note that Array<T> with non-nullable T must guard against uninitialized null values
|
|
// whenever an element is accessed. Otherwise, the compiler wouldn't be able to guarantee
|
|
// type-safety anymore. For lack of a better word, such an array is "holey".
|
|
|
|
private length_: i32;
|
|
|
|
static isArray<U>(value: U): bool {
|
|
return isReference<U>() ? builtin_isArray(value) && value !== null : false;
|
|
}
|
|
|
|
static create<T>(capacity: i32 = 0): Array<T> {
|
|
WARNING("'Array.create' is deprecated. Use 'new Array' instead, making sure initial elements are initialized.");
|
|
var array = new Array<T>(capacity);
|
|
array.length = 0;
|
|
return array;
|
|
}
|
|
|
|
constructor(length: i32 = 0) {
|
|
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
|
|
// reserve capacity for at least MIN_SIZE elements
|
|
var bufferSize = max(<usize>length, MIN_SIZE) << alignof<T>();
|
|
var buffer = changetype<ArrayBuffer>(__new(bufferSize, idof<ArrayBuffer>()));
|
|
memory.fill(changetype<usize>(buffer), 0, bufferSize);
|
|
this.buffer = buffer; // links
|
|
this.dataStart = changetype<usize>(buffer);
|
|
this.byteLength = <i32>bufferSize;
|
|
this.length_ = length;
|
|
}
|
|
|
|
get length(): i32 {
|
|
return this.length_;
|
|
}
|
|
|
|
set length(newLength: i32) {
|
|
ensureCapacity(changetype<usize>(this), newLength, alignof<T>(), false);
|
|
this.length_ = newLength;
|
|
}
|
|
|
|
every(fn: (value: T, index: i32, array: Array<T>) => bool): bool {
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
if (!fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
findIndex(fn: (value: T, index: i32, array: Array<T>) => bool): i32 {
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
if (fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this)) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
findLastIndex(fn: (value: T, index: i32, array: Array<T>) => bool): i32 {
|
|
for (let i = this.length_ - 1; i >= 0; --i) {
|
|
if (fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this)) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
@operator("[]") private __get(index: i32): T {
|
|
if (<u32>index >= <u32>this.length_) throw new RangeError(E_INDEXOUTOFRANGE);
|
|
var value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
|
if (isReference<T>()) {
|
|
if (!isNullable<T>()) {
|
|
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
@unsafe @operator("{}") private __uget(index: i32): T {
|
|
return load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
|
}
|
|
|
|
@operator("[]=") private __set(index: i32, value: T): void {
|
|
if (<u32>index >= <u32>this.length_) {
|
|
if (index < 0) throw new RangeError(E_INDEXOUTOFRANGE);
|
|
ensureCapacity(changetype<usize>(this), index + 1, alignof<T>());
|
|
this.length_ = index + 1;
|
|
}
|
|
this.__uset(index, value);
|
|
}
|
|
|
|
@unsafe @operator("{}=") private __uset(index: i32, value: T): void {
|
|
store<T>(this.dataStart + (<usize>index << alignof<T>()), value);
|
|
if (isManaged<T>()) {
|
|
__link(changetype<usize>(this), changetype<usize>(value), true);
|
|
}
|
|
}
|
|
|
|
at(index: i32): T {
|
|
var len = this.length_;
|
|
index += select(0, len, index >= 0);
|
|
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
|
|
var value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
|
if (isReference<T>()) {
|
|
if (!isNullable<T>()) {
|
|
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
fill(value: T, start: i32 = 0, end: i32 = i32.MAX_VALUE): this {
|
|
var ptr = this.dataStart;
|
|
var len = this.length_;
|
|
start = start < 0 ? max(len + start, 0) : min(start, len);
|
|
end = end < 0 ? max(len + end, 0) : min(end, len);
|
|
if (isManaged<T>()) {
|
|
for (; start < end; ++start) {
|
|
store<usize>(ptr + (<usize>start << alignof<T>()), changetype<usize>(value));
|
|
__link(changetype<usize>(this), changetype<usize>(value), true);
|
|
}
|
|
} else if (sizeof<T>() == 1) {
|
|
if (start < end) {
|
|
memory.fill(
|
|
ptr + <usize>start,
|
|
u8(value),
|
|
<usize>(end - start)
|
|
);
|
|
}
|
|
} else {
|
|
for (; start < end; ++start) {
|
|
store<T>(ptr + (<usize>start << alignof<T>()), value);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
includes(value: T, fromIndex: i32 = 0): bool {
|
|
if (isFloat<T>()) {
|
|
let len = this.length_;
|
|
if (len == 0 || fromIndex >= len) return false;
|
|
if (fromIndex < 0) fromIndex = max(len + fromIndex, 0);
|
|
let ptr = this.dataStart;
|
|
while (fromIndex < len) {
|
|
let elem = load<T>(ptr + (<usize>fromIndex << alignof<T>()));
|
|
// @ts-ignore
|
|
if (elem == value || isNaN(elem) & isNaN(value)) return true;
|
|
++fromIndex;
|
|
}
|
|
return false;
|
|
} else {
|
|
return this.indexOf(value, fromIndex) >= 0;
|
|
}
|
|
}
|
|
|
|
indexOf(value: T, fromIndex: i32 = 0): i32 {
|
|
var len = this.length_;
|
|
if (len == 0 || fromIndex >= len) return -1;
|
|
if (fromIndex < 0) fromIndex = max(len + fromIndex, 0);
|
|
var ptr = this.dataStart;
|
|
while (fromIndex < len) {
|
|
if (load<T>(ptr + (<usize>fromIndex << alignof<T>())) == value) return fromIndex;
|
|
++fromIndex;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
lastIndexOf(value: T, fromIndex: i32 = this.length_): i32 {
|
|
var len = this.length_;
|
|
if (len == 0) return -1;
|
|
if (fromIndex < 0) fromIndex = len + fromIndex;
|
|
else if (fromIndex >= len) fromIndex = len - 1;
|
|
var ptr = this.dataStart;
|
|
while (fromIndex >= 0) {
|
|
if (load<T>(ptr + (<usize>fromIndex << alignof<T>())) == value) return fromIndex;
|
|
--fromIndex;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
push(value: T): i32 {
|
|
var oldLen = this.length_;
|
|
var len = oldLen + 1;
|
|
ensureCapacity(changetype<usize>(this), len, alignof<T>());
|
|
if (isManaged<T>()) {
|
|
store<usize>(this.dataStart + (<usize>oldLen << alignof<T>()), changetype<usize>(value));
|
|
__link(changetype<usize>(this), changetype<usize>(value), true);
|
|
} else {
|
|
store<T>(this.dataStart + (<usize>oldLen << alignof<T>()), value);
|
|
}
|
|
this.length_ = len;
|
|
return len;
|
|
}
|
|
|
|
concat(other: Array<T>): Array<T> {
|
|
var thisLen = this.length_;
|
|
var otherLen = select(0, other.length_, other === null);
|
|
var outLen = thisLen + otherLen;
|
|
if (<u32>outLen > <u32>BLOCK_MAXSIZE >>> alignof<T>()) throw new Error(E_INVALIDLENGTH);
|
|
var out = changetype<Array<T>>(__newArray(outLen, alignof<T>(), idof<Array<T>>()));
|
|
var outStart = out.dataStart;
|
|
var thisSize = <usize>thisLen << alignof<T>();
|
|
if (isManaged<T>()) {
|
|
let thisStart = this.dataStart;
|
|
for (let offset: usize = 0; offset < thisSize; offset += sizeof<T>()) {
|
|
let ref = load<usize>(thisStart + offset);
|
|
store<usize>(outStart + offset, ref);
|
|
__link(changetype<usize>(out), ref, true);
|
|
}
|
|
outStart += thisSize;
|
|
let otherStart = other.dataStart;
|
|
let otherSize = <usize>otherLen << alignof<T>();
|
|
for (let offset: usize = 0; offset < otherSize; offset += sizeof<T>()) {
|
|
let ref = load<usize>(otherStart + offset);
|
|
store<usize>(outStart + offset, ref);
|
|
__link(changetype<usize>(out), ref, true);
|
|
}
|
|
} else {
|
|
memory.copy(outStart, this.dataStart, thisSize);
|
|
memory.copy(outStart + thisSize, other.dataStart, <usize>otherLen << alignof<T>());
|
|
}
|
|
return out;
|
|
}
|
|
|
|
copyWithin(target: i32, start: i32, end: i32 = i32.MAX_VALUE): this {
|
|
var ptr = this.dataStart;
|
|
var len = this.length_;
|
|
|
|
end = min<i32>(end, len);
|
|
|
|
var to = target < 0 ? max(len + target, 0) : min(target, len);
|
|
var from = start < 0 ? max(len + start, 0) : min(start, len);
|
|
var last = end < 0 ? max(len + end, 0) : min(end, len);
|
|
var count = min(last - from, len - to);
|
|
|
|
memory.copy( // is memmove
|
|
ptr + (<usize>to << alignof<T>()),
|
|
ptr + (<usize>from << alignof<T>()),
|
|
<usize>count << alignof<T>()
|
|
);
|
|
return this;
|
|
}
|
|
|
|
pop(): T {
|
|
var len = this.length_;
|
|
if (len < 1) throw new RangeError(E_EMPTYARRAY);
|
|
var val = load<T>(this.dataStart + (<usize>(--len) << alignof<T>()));
|
|
this.length_ = len;
|
|
return val;
|
|
}
|
|
|
|
forEach(fn: (value: T, index: i32, array: Array<T>) => void): void {
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this);
|
|
}
|
|
}
|
|
|
|
map<U>(fn: (value: T, index: i32, array: Array<T>) => U): Array<U> {
|
|
var len = this.length_;
|
|
var out = changetype<Array<U>>(__newArray(len, alignof<U>(), idof<Array<U>>()));
|
|
var outStart = out.dataStart;
|
|
for (let i = 0; i < min(len, this.length_); ++i) {
|
|
let result = fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this);
|
|
store<U>(outStart + (<usize>i << alignof<U>()), result);
|
|
if (isManaged<U>()) {
|
|
__link(changetype<usize>(out), changetype<usize>(result), true);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
filter(fn: (value: T, index: i32, array: Array<T>) => bool): Array<T> {
|
|
var result = changetype<Array<T>>(__newArray(0, alignof<T>(), idof<Array<T>>()));
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
let value = load<T>(this.dataStart + (<usize>i << alignof<T>()));
|
|
if (fn(value, i, this)) result.push(value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
reduce<U>(
|
|
fn: (previousValue: U, currentValue: T, currentIndex: i32, array: Array<T>) => U,
|
|
initialValue: U
|
|
): U {
|
|
var acc = initialValue;
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
acc = fn(acc, load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
reduceRight<U>(
|
|
fn: (previousValue: U, currentValue: T, currentIndex: i32, array: Array<T>) => U,
|
|
initialValue: U
|
|
): U {
|
|
var acc = initialValue;
|
|
for (let i = this.length_ - 1; i >= 0; --i) {
|
|
acc = fn(acc, load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
shift(): T {
|
|
var len = this.length_;
|
|
if (len < 1) throw new RangeError(E_EMPTYARRAY);
|
|
var base = this.dataStart;
|
|
var element = load<T>(base);
|
|
var lastIndex = len - 1;
|
|
memory.copy(
|
|
base,
|
|
base + sizeof<T>(),
|
|
<usize>lastIndex << alignof<T>()
|
|
);
|
|
if (isReference<T>()) {
|
|
store<usize>(base + (<usize>lastIndex << alignof<T>()), 0);
|
|
} else {
|
|
// @ts-ignore
|
|
store<T>(base + (<usize>lastIndex << alignof<T>()), <T>0);
|
|
}
|
|
this.length_ = lastIndex;
|
|
return element;
|
|
}
|
|
|
|
some(fn: (value: T, index: i32, array: Array<T>) => bool): bool {
|
|
for (let i = 0, len = this.length_; i < min(len, this.length_); ++i) {
|
|
if (fn(load<T>(this.dataStart + (<usize>i << alignof<T>())), i, this)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unshift(value: T): i32 {
|
|
var len = this.length_ + 1;
|
|
ensureCapacity(changetype<usize>(this), len, alignof<T>());
|
|
var ptr = this.dataStart;
|
|
memory.copy(
|
|
ptr + sizeof<T>(),
|
|
ptr,
|
|
<usize>(len - 1) << alignof<T>()
|
|
);
|
|
store<T>(ptr, value);
|
|
if (isManaged<T>()) {
|
|
__link(changetype<usize>(this), changetype<usize>(value), true);
|
|
}
|
|
this.length_ = len;
|
|
return len;
|
|
}
|
|
|
|
slice(start: i32 = 0, end: i32 = i32.MAX_VALUE): Array<T> {
|
|
var len = this.length_;
|
|
start = start < 0 ? max(start + len, 0) : min(start, len);
|
|
end = end < 0 ? max(end + len, 0) : min(end , len);
|
|
len = max(end - start, 0);
|
|
var slice = changetype<Array<T>>(__newArray(len, alignof<T>(), idof<Array<T>>()));
|
|
var sliceBase = slice.dataStart;
|
|
var thisBase = this.dataStart + (<usize>start << alignof<T>());
|
|
if (isManaged<T>()) {
|
|
let off = <usize>0;
|
|
let end = <usize>len << alignof<usize>();
|
|
while (off < end) {
|
|
let ref = load<usize>(thisBase + off);
|
|
store<usize>(sliceBase + off, ref);
|
|
__link(changetype<usize>(slice), ref, true);
|
|
off += sizeof<usize>();
|
|
}
|
|
} else {
|
|
memory.copy(sliceBase, thisBase, len << alignof<T>());
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
splice(start: i32, deleteCount: i32 = i32.MAX_VALUE): Array<T> {
|
|
var len = this.length_;
|
|
start = start < 0 ? max<i32>(len + start, 0) : min<i32>(start, len);
|
|
deleteCount = max<i32>(min<i32>(deleteCount, len - start), 0);
|
|
var result = changetype<Array<T>>(__newArray(deleteCount, alignof<T>(), idof<Array<T>>()));
|
|
var resultStart = result.dataStart;
|
|
var thisStart = this.dataStart;
|
|
var thisBase = thisStart + (<usize>start << alignof<T>());
|
|
memory.copy(
|
|
resultStart,
|
|
thisBase,
|
|
<usize>deleteCount << alignof<T>()
|
|
);
|
|
var offset = start + deleteCount;
|
|
if (len != offset) {
|
|
memory.copy(
|
|
thisBase,
|
|
thisStart + (<usize>offset << alignof<T>()),
|
|
<usize>(len - offset) << alignof<T>()
|
|
);
|
|
}
|
|
this.length_ = len - deleteCount;
|
|
return result;
|
|
}
|
|
|
|
reverse(): Array<T> {
|
|
var len = this.length_;
|
|
if (len) {
|
|
let front = this.dataStart;
|
|
let back = this.dataStart + (<usize>(len - 1) << alignof<T>());
|
|
while (front < back) {
|
|
let temp = load<T>(front);
|
|
store<T>(front, load<T>(back));
|
|
store<T>(back, temp);
|
|
front += sizeof<T>();
|
|
back -= sizeof<T>();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
sort(comparator: (a: T, b: T) => i32 = COMPARATOR<T>()): this {
|
|
SORT<T>(this.dataStart, this.length_, comparator);
|
|
return this;
|
|
}
|
|
|
|
join(separator: string = ","): string {
|
|
var ptr = this.dataStart;
|
|
var len = this.length_;
|
|
if (isBoolean<T>()) return joinBooleanArray(ptr, len, separator);
|
|
if (isInteger<T>()) return joinIntegerArray<T>(ptr, len, separator);
|
|
if (isFloat<T>()) return joinFloatArray<T>(ptr, len, separator);
|
|
|
|
if (ASC_SHRINK_LEVEL < 1) {
|
|
if (isString<T>()) return joinStringArray(ptr, len, separator);
|
|
}
|
|
// For rest objects and arrays use general join routine
|
|
if (isReference<T>()) return joinReferenceArray<T>(ptr, len, separator);
|
|
ERROR("unspported element type");
|
|
return <string>unreachable();
|
|
}
|
|
|
|
flat(): T {
|
|
if (!isArray<T>()) {
|
|
throw new TypeError(E_ILLEGALGENTYPE);
|
|
}
|
|
// Get the length and data start values
|
|
var ptr = this.dataStart;
|
|
var len = this.length_;
|
|
|
|
// calculate the end size with an initial pass
|
|
var size = 0;
|
|
for (let i = 0; i < len; ++i) {
|
|
let child = load<usize>(ptr + (i << alignof<T>()));
|
|
size += child == 0 ? 0 : load<i32>(child, offsetof<T>("length_"));
|
|
}
|
|
|
|
// calculate the byteLength of the resulting backing ArrayBuffer
|
|
var byteLength = <usize>size << usize(alignof<valueof<T>>());
|
|
var outBuffer = changetype<ArrayBuffer>(__new(byteLength, idof<ArrayBuffer>()));
|
|
|
|
// create the return value and initialize it
|
|
var outArray = changetype<T>(__new(offsetof<T>(), idof<T>()));
|
|
store<i32>(changetype<usize>(outArray), size, offsetof<T>("length_"));
|
|
|
|
// byteLength, dataStart, and buffer are all readonly
|
|
store<i32>(changetype<usize>(outArray), byteLength, offsetof<T>("byteLength"));
|
|
store<usize>(changetype<usize>(outArray), changetype<usize>(outBuffer), offsetof<T>("dataStart"));
|
|
store<usize>(changetype<usize>(outArray), changetype<usize>(outBuffer), offsetof<T>("buffer"));
|
|
__link(changetype<usize>(outArray), changetype<usize>(outBuffer), false);
|
|
|
|
// set the elements
|
|
var resultOffset: usize = 0;
|
|
for (let i = 0; i < len; ++i) { // for each child
|
|
let child = load<usize>(ptr + (<usize>i << alignof<T>()));
|
|
|
|
// ignore null arrays
|
|
if (child == 0) continue;
|
|
|
|
// copy the underlying buffer data to the result buffer
|
|
let childDataLength = load<i32>(child, offsetof<T>("byteLength"));
|
|
memory.copy(
|
|
changetype<usize>(outBuffer) + resultOffset,
|
|
load<usize>(child, offsetof<T>("dataStart")),
|
|
<usize>childDataLength
|
|
);
|
|
|
|
// advance the result length
|
|
resultOffset += childDataLength;
|
|
}
|
|
|
|
// if the `valueof<T>` type is managed, we must link each reference
|
|
if (isManaged<valueof<T>>()) {
|
|
for (let i = 0; i < size; ++i) {
|
|
let ref = load<usize>(changetype<usize>(outBuffer) + (<usize>i << usize(alignof<valueof<T>>())));
|
|
__link(changetype<usize>(outBuffer), ref, true);
|
|
}
|
|
}
|
|
|
|
return outArray;
|
|
}
|
|
|
|
toString(): string {
|
|
return this.join();
|
|
}
|
|
|
|
// RT integration
|
|
|
|
@unsafe private __visit(cookie: u32): void {
|
|
if (isManaged<T>()) {
|
|
let cur = this.dataStart;
|
|
let end = cur + (<usize>this.length_ << alignof<T>());
|
|
while (cur < end) {
|
|
let val = load<usize>(cur);
|
|
if (val) __visit(val, cookie);
|
|
cur += sizeof<usize>();
|
|
}
|
|
}
|
|
__visit(changetype<usize>(this.buffer), cookie);
|
|
}
|
|
}
|