Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept byteOffset when creating an index from a buffer #55

Merged
merged 9 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ export default class Flatbush {
/**
* Recreate a Flatbush index from raw `ArrayBuffer` or `SharedArrayBuffer` data.
* @param {ArrayBuffer | SharedArrayBuffer} data
* @param {number} [byteOffset=0] byte offset to the start of the Flatbush buffer in the referenced ArrayBuffer.
* @returns {Flatbush} index
*/
static from(data) {
static from(data, byteOffset = 0) {
if (byteOffset % 8 !== 0) {
throw new Error('byteOffset must be 8-byte aligned.');
}

// @ts-expect-error duck typing array buffers
if (!data || data.byteLength === undefined || data.buffer) {
throw new Error('Data must be an instance of ArrayBuffer or SharedArrayBuffer.');
}
const [magic, versionAndType] = new Uint8Array(data, 0, 2);

const [magic, versionAndType] = new Uint8Array(data, byteOffset + 0, 2);
if (magic !== 0xfb) {
throw new Error('Data does not appear to be in a Flatbush format.');
}
Expand All @@ -29,10 +35,10 @@ export default class Flatbush {
if (!ArrayType) {
throw new Error('Unrecognized array type.');
}
const [nodeSize] = new Uint16Array(data, 2, 1);
const [numItems] = new Uint32Array(data, 4, 1);
const [nodeSize] = new Uint16Array(data, byteOffset + 2, 1);
const [numItems] = new Uint32Array(data, byteOffset + 4, 1);

return new Flatbush(numItems, nodeSize, ArrayType, undefined, data);
return new Flatbush(numItems, nodeSize, ArrayType, undefined, data, byteOffset);
}

/**
Expand All @@ -42,13 +48,15 @@ export default class Flatbush {
* @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
* @param {ArrayBufferConstructor | SharedArrayBufferConstructor} [ArrayBufferType=ArrayBuffer] The array buffer type used to store data (`ArrayBuffer` by default).
* @param {ArrayBuffer | SharedArrayBuffer} [data] (Only used internally)
* @param {number} [byteOffset=0] (Only used internally)
*/
constructor(numItems, nodeSize = 16, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data) {
constructor(numItems, nodeSize = 16, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data, byteOffset = 0) {
if (numItems === undefined) throw new Error('Missing required argument: numItems.');
if (isNaN(numItems) || numItems <= 0) throw new Error(`Unexpected numItems value: ${numItems}.`);

this.numItems = +numItems;
this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
this.byteOffset = byteOffset;

// calculate the total number of nodes in the R-tree to allocate space for
// and the index of each tree level (used in search later)
Expand All @@ -74,8 +82,8 @@ export default class Flatbush {
// @ts-expect-error duck typing array buffers
if (data && data.byteLength !== undefined && !data.buffer) {
this.data = data;
this._boxes = new this.ArrayType(this.data, 8, numNodes * 4);
this._indices = new this.IndexArrayType(this.data, 8 + nodesByteSize, numNodes);
this._boxes = new this.ArrayType(this.data, byteOffset + 8, numNodes * 4);
this._indices = new this.IndexArrayType(this.data, byteOffset + 8 + nodesByteSize, numNodes);

this._pos = numNodes * 4;
this.minX = this._boxes[this._pos - 4];
Expand Down
29 changes: 29 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,35 @@ test('reconstructs an index from array buffer', () => {
assert.deepEqual(index, index2);
});

test('throws an error when reconstructing an index from array buffer if not 8-byte aligned', () => {
const index = createIndex();
const byteOffset = 12;
const newArrayBuffer = new ArrayBuffer(index.data.byteLength + byteOffset);
const newView = new Uint8Array(newArrayBuffer, byteOffset);
newView.set(new Uint8Array(index.data));

assert.throws(() => {
Flatbush.from(newArrayBuffer, byteOffset);
});
});

test('reconstructs an index from a Uint8Array', () => {
const index = createIndex();
const byteOffset = 16;
const newArrayBuffer = new ArrayBuffer(index.data.byteLength + byteOffset);
const newView = new Uint8Array(newArrayBuffer, byteOffset);
newView.set(new Uint8Array(index.data));

const index2 = Flatbush.from(newArrayBuffer, byteOffset);

assert.deepEqual(index._boxes, index2._boxes);
assert.deepEqual(index._indices, index2._indices);
assert.equal(index.numItems, index2.numItems);
assert.equal(index.nodeSize, index2.nodeSize);
assert.deepEqual(index._levelBounds, index2._levelBounds);
assert.notEqual(index.byteOffset, index2.byteOffset);
});

test('throws an error if added less items than the index size', () => {
assert.throws(() => {
const index = new Flatbush(data.length / 4);
Expand Down
Loading