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

Add SharedArrayBuffer support #46

Merged
merged 10 commits into from
Apr 10, 2023
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,14 @@ Alternatively, there's a browser bundle with a `Flatbush` global variable:

## API

#### `new Flatbush(numItems[, nodeSize, ArrayType])`
#### `new Flatbush(numItems[, nodeSize, ArrayType, useSharedArrayBuffer])`

Creates a Flatbush index that will hold a given number of items (`numItems`). Additionally accepts:

- `nodeSize`: size of the tree node (`16` by default); experiment with different values for best performance (increasing this value makes indexing faster and queries slower, and vise versa).
- `ArrayType`: the array type used for coordinates storage (`Float64Array` by default);
other types may be faster in certain cases (e.g. `Int32Array` when your data is integer).
- `useSharedArrayBuffer`: set `true` if you prefer to use a `SharedArrayBuffer` in place of `ArrayBuffer` (`false` by default). This kind of data structure can be shared between threads.

#### `index.add(minX, minY, maxX, maxY)`

Expand Down Expand Up @@ -114,9 +115,9 @@ Also accepts a `filterFn` similar to `index.search`.

#### `Flatbush.from(data)`

Recreates a Flatbush index from raw `ArrayBuffer` data
Recreates a Flatbush index from raw `ArrayBuffer` or `SharedArrayBuffer` data
(that's exposed as `index.data` on a previously indexed Flatbush instance).
Very useful for transferring indices between threads or storing them in a file.
Very useful for transferring or sharing indices between threads or storing them in a file.

### Properties

Expand Down
18 changes: 10 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

/* global SharedArrayBuffer */
import FlatQueue from 'flatqueue';

const ARRAY_TYPES = [
Expand All @@ -11,8 +11,8 @@ const VERSION = 3; // serialized format version
export default class Flatbush {

static from(data) {
if (!(data instanceof ArrayBuffer)) {
throw new Error('Data must be an instance of ArrayBuffer.');
if (!(data instanceof ArrayBuffer) && SharedArrayBuffer && !(data instanceof SharedArrayBuffer)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues here, if I'm following this right:

  • In case SharedArrayBuffer isn't available, just referencing it will throw an error, so this won't work as a check. We need to use typeof.
  • If we fix the reference, the whole condition will still evaluate to false in case of a wrong data type, so the error won't be thrown.

So this would need to look something like this:

Suggested change
if (!(data instanceof ArrayBuffer) && SharedArrayBuffer && !(data instanceof SharedArrayBuffer)) {
if (!(data instanceof ArrayBuffer) && (typeof SharedArrayBuffer === 'undefined' || !(data instanceof SharedArrayBuffer))) {

throw new Error('Data must be an instance of ArrayBuffer or SharedArrayBuffer.');
}
const [magic, versionAndType] = new Uint8Array(data, 0, 2);
if (magic !== 0xfb) {
Expand All @@ -24,12 +24,13 @@ export default class Flatbush {
const [nodeSize] = new Uint16Array(data, 2, 1);
const [numItems] = new Uint32Array(data, 4, 1);

return new Flatbush(numItems, nodeSize, ARRAY_TYPES[versionAndType & 0x0f], data);
return new Flatbush(numItems, nodeSize, ARRAY_TYPES[versionAndType & 0x0f], undefined, data);
}

constructor(numItems, nodeSize = 16, ArrayType = Float64Array, data) {
constructor(numItems, nodeSize = 16, ArrayType = Float64Array, useSharedArrayBuffer = false, data) {
if (numItems === undefined) throw new Error('Missing required argument: numItems.');
if (isNaN(numItems) || numItems <= 0) throw new Error(`Unpexpected numItems value: ${numItems}.`);
if (isNaN(numItems) || numItems <= 0) throw new Error(`Unexpected numItems value: ${numItems}.`);
if (useSharedArrayBuffer && !SharedArrayBuffer) throw new Error('SharedArrayBuffer not available in your runtime.');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue, typeof


this.numItems = +numItems;
this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
Expand All @@ -46,6 +47,7 @@ export default class Flatbush {
} while (n !== 1);

this.ArrayType = ArrayType || Float64Array;
this.ArrayBufferType = useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
this.IndexArrayType = numNodes < 16384 ? Uint16Array : Uint32Array;

const arrayTypeIndex = ARRAY_TYPES.indexOf(this.ArrayType);
Expand All @@ -55,7 +57,7 @@ export default class Flatbush {
throw new Error(`Unexpected typed array class: ${ArrayType}.`);
}

if (data && (data instanceof ArrayBuffer)) {
if (data && ((data instanceof ArrayBuffer) || (SharedArrayBuffer && data instanceof SharedArrayBuffer))) {
this.data = data;
this._boxes = new this.ArrayType(this.data, 8, numNodes * 4);
this._indices = new this.IndexArrayType(this.data, 8 + nodesByteSize, numNodes);
Expand All @@ -67,7 +69,7 @@ export default class Flatbush {
this.maxY = this._boxes[this._pos - 1];

} else {
this.data = new ArrayBuffer(8 + nodesByteSize + numNodes * this.IndexArrayType.BYTES_PER_ELEMENT);
this.data = new this.ArrayBufferType(8 + nodesByteSize + numNodes * this.IndexArrayType.BYTES_PER_ELEMENT);
this._boxes = new this.ArrayType(this.data, 8, numNodes * 4);
this._indices = new this.IndexArrayType(this.data, 8 + nodesByteSize, numNodes);
this._pos = 0;
Expand Down