Skip to content

Commit

Permalink
feat: add indexOf methods to Array & Vector views
Browse files Browse the repository at this point in the history
  • Loading branch information
zandaqo committed Nov 4, 2021
1 parent d40d2de commit 3bef86a
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 26 deletions.
30 changes: 30 additions & 0 deletions array-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ export class ArrayView<T> extends DataView implements ContainerView<T> {
return (length / this.itemLength) | 0;
}

static indexOf<T>(
value: T,
view: DataView,
startIndex = 0,
startOffset = 0,
length = view.byteLength,
): number {
const size = this.getSize(length);
const valueView = this.View.from(value);
outer:
for (let i = startIndex; i < size; i++) {
const offset = startOffset + this.getOffset(i);
for (let j = 0; j < valueView.byteLength; j++) {
if (valueView.getUint8(j) !== view.getUint8(offset + j)) continue outer;
}
return i;
}
return -1;
}

*[Symbol.iterator](): Generator<ViewInstance<T>> {
const { size } = this;
for (let i = 0; i < size; i++) {
Expand Down Expand Up @@ -103,6 +123,16 @@ export class ArrayView<T> extends DataView implements ContainerView<T> {
);
}

indexOf(value: T, start = 0): number {
return (this.constructor as typeof ArrayView).indexOf(
value,
this,
start,
0,
this.byteLength,
);
}

set(index: number, value: T): void {
const constructor = this.constructor as typeof ArrayView;
const View = constructor.View as ViewConstructor<T>;
Expand Down
10 changes: 10 additions & 0 deletions tests/array-view_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ test("[ArrayView#getView] returns an item view at a given index", () => {
assertEquals(primitiveView.buffer, arrayView.buffer);
});

test("[ArrayView#indexOf] searches for a value in the array returning its index", () => {
const arrayView = Uint32ArrayView.from([30, 40, 40, 50]);
assertEquals(arrayView.indexOf(1), -1);
assertEquals(arrayView.indexOf(30), 0);
assertEquals(arrayView.indexOf(40), 1);
assertEquals(arrayView.indexOf(40, 2), 2);
assertEquals(arrayView.indexOf(50), 3);
assertEquals(arrayView.indexOf(150), -1);
});

test("[ArrayView#set] sets an item at a given index", () => {
const arrayView = Uint32ArrayView.from([30, 40, 50]);
const expected = 60;
Expand Down
10 changes: 10 additions & 0 deletions tests/vector-view_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ test("[VectorView#getView] returns a view at a given index", () => {
assertEquals(actual.byteLength, 2);
});

test("[VectorView#indexOf] returns a view at a given index", () => {
const vector = StringVector.from(["a", "b", "b", undefined, "cd"]);
assertEquals(vector.indexOf(""), -1);
assertEquals(vector.indexOf("a"), 0);
assertEquals(vector.indexOf("b"), 1);
assertEquals(vector.indexOf("b", 2), 2);
assertEquals(vector.indexOf("z"), -1);
assertEquals(vector.indexOf("cd"), 4);
});

test("[VectorView#getView] returns undefined for absent index", () => {
assertEquals(
StringVector.from(["a", "b", undefined, "cd"]).getView(2),
Expand Down
88 changes: 62 additions & 26 deletions vector-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ export class VectorView<T> extends DataView implements ContainerView<T> {
const size = this.getSize(view, start);
const array = new Array(size) as Array<T | undefined>;
for (let i = 0; i < size; i++) {
const offset = (i + 1) << 2;
const startOffset = view.getUint32(start + offset, true);
const end = view.getUint32(start + offset + 4, true);
array[i] = startOffset !== end
? View.decode(view, start + startOffset, end - startOffset)
const offset = this.getOffset(i, view, start);
array[i] = offset
? View.decode(view, start + offset[0], offset[1])
: undefined;
}
return array;
Expand Down Expand Up @@ -92,10 +90,44 @@ export class VectorView<T> extends DataView implements ContainerView<T> {
return length;
}

static getOffset(
index: number,
view: DataView,
start = 0,
): [start: number, length: number] | undefined {
const offset = start + ((index + 1) << 2);
const begin = view.getUint32(offset, true);
const end = view.getUint32(offset + 4, true);
if (begin === end) return undefined;
return [begin, end - begin];
}

static getSize(view: DataView, start = 0): number {
return view.getUint32(start, true);
}

static indexOf<T>(
value: T,
view: DataView,
startIndex = 0,
startOffset = 0,
) {
const size = this.getSize(view, startOffset);
const valueView = this.View.from(value);
outer:
for (let i = startIndex; i < size; i++) {
const offset = this.getOffset(i, view, startOffset);
if (!offset || offset[1] !== valueView.byteLength) continue;
for (let j = 0; j < valueView.byteLength; j++) {
if (valueView.getUint8(j) !== view.getUint8(offset[0] + j)) {
continue outer;
}
}
return i;
}
return -1;
}

*[Symbol.iterator](): Generator<ViewInstance<T> | undefined> {
const { size } = this;
for (let i = 0; i < size; i++) {
Expand All @@ -111,47 +143,51 @@ export class VectorView<T> extends DataView implements ContainerView<T> {
get(index: number): T | undefined {
const View = (this.constructor as typeof VectorView)
.View as ViewConstructor<T>;
const layout = this.getLayout(index);
if (!layout) return undefined;
return View.decode(this, layout[0], layout[1]);
const offset = this.getOffset(index);
if (!offset) return undefined;
return View.decode(this, offset[0], offset[1]);
}

getLength(index: number): number {
const layout = this.getLayout(index);
if (!layout) return 0;
return layout[1];
const offset = this.getOffset(index);
return offset ? offset[1] : 0;
}

getLayout(index: number): [number, number] | undefined {
getOffset(index: number): [number, number] | undefined {
const length = this.getUint32(0, true);
if (index >= length) return undefined;
const startOffset = (index + 1) << 2;
const start = this.getUint32(startOffset, true);
const end = this.getUint32(startOffset + 4, true);
if (start === end) return undefined;
return [start, end - start];
return (this.constructor as typeof VectorView).getOffset(index, this, 0);
}

getView(index: number): ViewInstance<T> | undefined {
const View = (this.constructor as typeof VectorView)
.View as ViewConstructor<T>;
const layout = this.getLayout(index);
if (!layout) return undefined;
return new View(this.buffer, this.byteOffset + layout[0], layout[1]);
const offset = this.getOffset(index);
if (!offset) return undefined;
return new View(this.buffer, this.byteOffset + offset[0], offset[1]);
}

indexOf(value: T, start = 0): number {
return (this.constructor as typeof VectorView).indexOf(
value,
this,
start,
0,
);
}

set(index: number, value: T): void {
const View = (this.constructor as typeof VectorView)
.View as ViewConstructor<T>;
const layout = this.getLayout(index);
if (!layout) return undefined;
View.encode(value, this, this.byteOffset + layout[0], layout[1]);
const offset = this.getOffset(index);
if (!offset) return undefined;
View.encode(value, this, this.byteOffset + offset[0], offset[1]);
}

setView(index: number, value: DataView): void {
const layout = this.getLayout(index);
if (!layout) return undefined;
new Uint8Array(this.buffer, this.byteOffset + layout[0], layout[1]).set(
const offset = this.getOffset(index);
if (!offset) return undefined;
new Uint8Array(this.buffer, this.byteOffset + offset[0], offset[1]).set(
new Uint8Array(value.buffer, value.byteOffset, value.byteLength),
);
}
Expand Down

0 comments on commit 3bef86a

Please sign in to comment.