@osmix/core 0.1.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -72
  4. package/dist/contracts.d.ts +27 -0
  5. package/dist/contracts.d.ts.map +1 -0
  6. package/dist/contracts.js +1 -0
  7. package/dist/contracts.js.map +1 -0
  8. package/dist/entities.d.ts +22 -2
  9. package/dist/entities.d.ts.map +1 -1
  10. package/dist/entities.js +54 -10
  11. package/dist/entities.js.map +1 -1
  12. package/dist/ids.d.ts +20 -5
  13. package/dist/ids.d.ts.map +1 -1
  14. package/dist/ids.js +87 -29
  15. package/dist/ids.js.map +1 -1
  16. package/dist/index.d.ts +7 -4
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +6 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/indirect-kd-index.d.ts +28 -0
  21. package/dist/indirect-kd-index.d.ts.map +1 -0
  22. package/dist/indirect-kd-index.js +163 -0
  23. package/dist/indirect-kd-index.js.map +1 -0
  24. package/dist/limits.d.ts +30 -0
  25. package/dist/limits.d.ts.map +1 -0
  26. package/dist/limits.js +51 -0
  27. package/dist/limits.js.map +1 -0
  28. package/dist/mocks.d.ts.map +1 -1
  29. package/dist/mocks.js.map +1 -1
  30. package/dist/nodes.d.ts +36 -15
  31. package/dist/nodes.d.ts.map +1 -1
  32. package/dist/nodes.js +185 -62
  33. package/dist/nodes.js.map +1 -1
  34. package/dist/osm.d.ts +60 -4
  35. package/dist/osm.d.ts.map +1 -1
  36. package/dist/osm.js +32 -6
  37. package/dist/osm.js.map +1 -1
  38. package/dist/relations.d.ts +11 -4
  39. package/dist/relations.d.ts.map +1 -1
  40. package/dist/relations.js +73 -29
  41. package/dist/relations.js.map +1 -1
  42. package/dist/renumber.d.ts +23 -0
  43. package/dist/renumber.d.ts.map +1 -0
  44. package/dist/renumber.js +64 -0
  45. package/dist/renumber.js.map +1 -0
  46. package/dist/stringtable.d.ts +2 -1
  47. package/dist/stringtable.d.ts.map +1 -1
  48. package/dist/stringtable.js +9 -14
  49. package/dist/stringtable.js.map +1 -1
  50. package/dist/tags.d.ts +45 -12
  51. package/dist/tags.d.ts.map +1 -1
  52. package/dist/tags.js +153 -27
  53. package/dist/tags.js.map +1 -1
  54. package/dist/typed-arrays.d.ts +63 -48
  55. package/dist/typed-arrays.d.ts.map +1 -1
  56. package/dist/typed-arrays.js +184 -107
  57. package/dist/typed-arrays.js.map +1 -1
  58. package/dist/ways.d.ts +52 -3
  59. package/dist/ways.d.ts.map +1 -1
  60. package/dist/ways.js +223 -46
  61. package/dist/ways.js.map +1 -1
  62. package/package.json +45 -31
  63. package/src/contracts.ts +29 -0
  64. package/src/entities.ts +242 -194
  65. package/src/ids.ts +278 -235
  66. package/src/index.ts +22 -13
  67. package/src/indirect-kd-index.ts +212 -0
  68. package/src/limits.ts +73 -0
  69. package/src/mocks.ts +114 -113
  70. package/src/nodes.ts +538 -397
  71. package/src/osm.ts +282 -207
  72. package/src/relations.ts +580 -558
  73. package/src/renumber.ts +80 -0
  74. package/src/stringtable.ts +189 -184
  75. package/src/tags.ts +406 -272
  76. package/src/typed-arrays.ts +307 -205
  77. package/src/types/geoflatbush.d.ts +28 -33
  78. package/src/ways.ts +577 -406
  79. package/test/entity-index-errors.test.ts +41 -0
  80. package/test/ids.bench.ts +33 -0
  81. package/test/ids.test.ts +122 -0
  82. package/test/limits.test.ts +83 -0
  83. package/test/memory.test.ts +29 -30
  84. package/test/no-shared-array-buffer.test.ts +41 -0
  85. package/test/nodes-spatial-index.test.ts +261 -0
  86. package/test/osm-bbox.test.ts +30 -0
  87. package/test/relations.bench.ts +32 -0
  88. package/test/relations.test.ts +124 -0
  89. package/test/renumber.test.ts +54 -0
  90. package/test/spatial-index-rebuild.test.ts +33 -0
  91. package/test/storage-compaction.test.ts +269 -0
  92. package/test/tags-search.test.ts +89 -56
  93. package/test/typed-arrays.test.ts +252 -132
  94. package/tsconfig.build.json +6 -6
  95. package/tsconfig.json +7 -7
  96. package/src/types/geokdbush.d.ts +0 -37
  97. package/src/types/kdbush.d.ts +0 -47
@@ -7,41 +7,89 @@
7
7
  * @module
8
8
  */
9
9
 
10
+ import { isSharedArrayBuffer } from "@osmix/shared/backing-buffers";
11
+
10
12
  /**
11
13
  * Use SharedArrayBuffer if the runtime supports it, otherwise fall back to ArrayBuffer.
12
14
  * SharedArrayBuffer enables zero-copy transfer between workers.
13
15
  */
14
16
  export const BufferConstructor =
15
- typeof SharedArrayBuffer !== "undefined"
16
- ? (SharedArrayBuffer as SharedArrayBufferConstructor)
17
- : (ArrayBuffer as ArrayBufferConstructor)
17
+ typeof SharedArrayBuffer !== "undefined"
18
+ ? (SharedArrayBuffer as SharedArrayBufferConstructor)
19
+ : (ArrayBuffer as ArrayBufferConstructor);
18
20
 
19
21
  /** The buffer type used by this runtime (SharedArrayBuffer or ArrayBuffer). */
20
- export type BufferType = InstanceType<typeof BufferConstructor>
22
+ export type BufferType = InstanceType<typeof BufferConstructor>;
21
23
 
22
24
  /**
23
25
  * Union of all standard typed array types.
24
26
  * Generic over buffer type to preserve SharedArrayBuffer compatibility.
25
27
  */
26
28
  export type TypedArray<B extends BufferType = BufferType> =
27
- | Int8Array<B>
28
- | Uint8Array<B>
29
- | Uint8ClampedArray<B>
30
- | Int16Array<B>
31
- | Uint16Array<B>
32
- | Int32Array<B>
33
- | Uint32Array<B>
34
- | Float32Array<B>
35
- | Float64Array<B>
29
+ | Int8Array<B>
30
+ | Uint8Array<B>
31
+ | Uint8ClampedArray<B>
32
+ | Int16Array<B>
33
+ | Uint16Array<B>
34
+ | Int32Array<B>
35
+ | Uint32Array<B>
36
+ | Float32Array<B>
37
+ | Float64Array<B>;
36
38
 
37
39
  /**
38
40
  * Constructor interface for typed arrays.
39
41
  */
40
- export interface TypedArrayConstructor<
41
- T extends TypedArray<BufferType> = TypedArray<BufferType>,
42
- > {
43
- new (buffer: BufferType): T
44
- readonly BYTES_PER_ELEMENT: number
42
+ export interface TypedArrayConstructor<T extends TypedArray<BufferType> = TypedArray<BufferType>> {
43
+ new (buffer: BufferType, byteOffset?: number, length?: number): T;
44
+ readonly BYTES_PER_ELEMENT: number;
45
+ readonly name: string;
46
+ }
47
+
48
+ export type TypedBufferAllocationOperation = "create" | "grow" | "compact";
49
+ export type TypedBufferType = "array-buffer" | "shared-array-buffer";
50
+
51
+ /** Structured allocation failure suitable for transport across worker boundaries. */
52
+ export class TypedBufferAllocationError extends Error {
53
+ readonly code = "TYPED_BUFFER_ALLOCATION_FAILED";
54
+ readonly stage = "typed-buffer-allocation";
55
+ readonly operation: TypedBufferAllocationOperation;
56
+ readonly typedArray: string;
57
+ readonly bufferType: TypedBufferType;
58
+ readonly elementCount: number;
59
+ readonly bytesPerElement: number;
60
+ readonly requiredBytes: number;
61
+
62
+ constructor(
63
+ args: {
64
+ operation: TypedBufferAllocationOperation;
65
+ typedArray: string;
66
+ bufferType: TypedBufferType;
67
+ elementCount: number;
68
+ bytesPerElement: number;
69
+ requiredBytes: number;
70
+ },
71
+ cause: unknown,
72
+ ) {
73
+ super(
74
+ `${args.typedArray} ${args.operation} requires ${args.requiredBytes.toLocaleString()} bytes in a single ${args.bufferType === "shared-array-buffer" ? "SharedArrayBuffer" : "ArrayBuffer"}.`,
75
+ { cause },
76
+ );
77
+ this.name = "TypedBufferAllocationError";
78
+ this.operation = args.operation;
79
+ this.typedArray = args.typedArray;
80
+ this.bufferType = args.bufferType;
81
+ this.elementCount = args.elementCount;
82
+ this.bytesPerElement = args.bytesPerElement;
83
+ this.requiredBytes = args.requiredBytes;
84
+ }
85
+ }
86
+
87
+ type BufferConstructorType = SharedArrayBufferConstructor | ArrayBufferConstructor;
88
+
89
+ function getBufferType(BC: BufferConstructorType): TypedBufferType {
90
+ return typeof SharedArrayBuffer !== "undefined" && BC === SharedArrayBuffer
91
+ ? "shared-array-buffer"
92
+ : "array-buffer";
45
93
  }
46
94
 
47
95
  /**
@@ -51,201 +99,255 @@ export interface TypedArrayConstructor<
51
99
  * can exactly represent integers up to 2^53, which covers all current OSM IDs.
52
100
  * Float64Array allows typed array operations while maintaining precision.
53
101
  */
54
- export const IdArrayType = Float64Array
102
+ export const IdArrayType = Float64Array;
55
103
 
56
- /**
57
- * Uint32Array for storing array indices.
58
- *
59
- * Internal indices into typed arrays never exceed 2^32 elements,
60
- * so Uint32Array provides the best balance of range and memory efficiency.
61
- */
62
- export const IndexArrayType = Uint32Array
104
+ const MiB = 2 ** 20;
105
+
106
+ /** Starting capacity of a new column. Growth is in place, so small columns stay small. */
107
+ const INITIAL_BYTES = 64 * 1024;
63
108
 
64
109
  /**
65
- * Initial buffer size for ResizeableTypedArray.
66
- * 1 MiB provides reasonable initial capacity while avoiding excessive memory allocation.
110
+ * Address space a growing column reserves so that it grows in place, without copying.
111
+ * Reserved pages hold no memory until written. Chromium's V8 sandbox has room for about 250
112
+ * reservations of this size, and a load builds about 35 columns at once. A reservation that
113
+ * fails falls back to growing by reallocation.
67
114
  */
68
- export const DEFAULT_BUFFER_SIZE = 2 ** 20 // 1 MiB
115
+ const GROWTH_RESERVE_BYTES = 4 * 2 ** 30;
116
+
117
+ /** Above this size a column grows by 1.5× instead of 2×, so it overshoots less. */
118
+ const DOUBLING_LIMIT_BYTES = 256 * MiB;
69
119
 
70
120
  /**
71
121
  * Auto-expanding typed array wrapper.
72
122
  *
73
- * - `push()` appends elements, doubling buffer size as needed.
74
- * - `compact()` shrinks buffer to fit data.
75
- * - Supports growable SharedArrayBuffer and resizable ArrayBuffer.
123
+ * - `push()` / `pushMany()` append, growing the buffer in place when the runtime supports
124
+ * growable buffers, otherwise by reallocating.
125
+ * - `array` is a view of the stored items only; `compact()` shrinks the buffer to fit them.
126
+ * - `from()` wraps a finished (transferred) buffer, which can still grow.
76
127
  */
77
128
  export class ResizeableTypedArray<TA extends TypedArray> {
78
- /** The typed array constructor for this instance */
79
- ArrayType: TypedArrayConstructor<TA>
80
- /** The current typed array view into the buffer */
81
- array: TA
82
- /** Number of items actually stored (may be less than array.length) */
83
- items = 0
84
-
85
- /** The underlying ArrayBuffer or SharedArrayBuffer */
86
- buffer: BufferType
87
- /** Current buffer size in bytes */
88
- bufferSize: number
89
- /** Maximum byte length for growable buffers */
90
- maxByteLength: number
91
-
92
- /** Buffer constructor (SharedArrayBuffer or ArrayBuffer) */
93
- BC: SharedArrayBufferConstructor | ArrayBufferConstructor
94
-
95
- /**
96
- * Reconstruct a ResizeableTypedArray from an existing buffer.
97
- *
98
- * Used after transferring buffers between workers. The resulting array
99
- * is considered "compacted" with items = array.length.
100
- *
101
- * @param ArrayType - The typed array constructor.
102
- * @param buffer - The existing buffer to wrap.
103
- * @returns A new ResizeableTypedArray wrapping the buffer.
104
- */
105
- static from<TA extends TypedArray>(
106
- ArrayType: TypedArrayConstructor<TA>,
107
- buffer: BufferType,
108
- ) {
109
- const rta = new ResizeableTypedArray<TA>(
110
- ArrayType,
111
- buffer instanceof SharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer,
112
- )
113
- rta.buffer = buffer
114
- rta.array = new ArrayType(buffer)
115
- rta.items = rta.array.length
116
- return rta
117
- }
118
-
119
- /**
120
- * Create a new ResizeableTypedArray with an empty buffer.
121
- *
122
- * @param ArrayType - The typed array constructor (e.g., Float64Array).
123
- * @param BC - Buffer constructor to use (defaults to SharedArrayBuffer if available).
124
- */
125
- constructor(
126
- ArrayType: TypedArrayConstructor<TA>,
127
- BC:
128
- | SharedArrayBufferConstructor
129
- | ArrayBufferConstructor = BufferConstructor,
130
- ) {
131
- this.ArrayType = ArrayType
132
- this.bufferSize = DEFAULT_BUFFER_SIZE
133
- this.maxByteLength = DEFAULT_BUFFER_SIZE * 2
134
- this.BC = BC
135
- this.buffer = new BC(this.bufferSize, {
136
- maxByteLength: this.maxByteLength,
137
- })
138
- this.array = new this.ArrayType(this.buffer)
139
- }
140
-
141
- /**
142
- * Iterate over the array values.
143
- */
144
- [Symbol.iterator](): ArrayIterator<number> {
145
- return this.array[Symbol.iterator]()
146
- }
147
-
148
- /**
149
- * Double the buffer capacity.
150
- * Uses in-place grow/resize if supported, otherwise allocates new buffer and copies.
151
- */
152
- expandArray() {
153
- this.bufferSize *= 2
154
- if (this.bufferSize > this.buffer.maxByteLength) {
155
- // Need a completely new buffer with larger maxByteLength
156
- this.maxByteLength *= 2
157
- const newBuffer = new this.BC(this.bufferSize, {
158
- maxByteLength: this.maxByteLength,
159
- })
160
- const newArray = new this.ArrayType(newBuffer)
161
- newArray.set(this.array)
162
- this.buffer = newBuffer
163
- this.array = newArray
164
- } else {
165
- // Can grow/resize the existing buffer in place
166
- if (this.buffer instanceof SharedArrayBuffer && this.buffer.growable) {
167
- this.buffer.grow(this.bufferSize)
168
- } else if (this.buffer instanceof ArrayBuffer && this.buffer.resizable) {
169
- this.buffer.resize(this.bufferSize)
170
- } else {
171
- throw Error("Buffer is not growable or resizable")
172
- }
173
- }
174
- }
175
-
176
- /**
177
- * Get the value at an index. Handles negative indices.
178
- */
179
- at(index: number): number {
180
- if (index < -this.length || index >= this.length)
181
- throw Error(`Index out of bounds: ${index}. Length: ${this.length}`)
182
- if (index < 0) return this.at(this.length + index)
183
- const result = this.array[index]
184
- if (result === undefined) throw Error(`No value at index: ${index}`)
185
- return result
186
- }
187
-
188
- /**
189
- * Get a slice of the array.
190
- */
191
- slice(start: number, end: number) {
192
- return this.array.slice(start, end)
193
- }
194
-
195
- get length() {
196
- return this.items
197
- }
198
-
199
- /**
200
- * Push a value to the end of the array.
201
- */
202
- push(value: number): number {
203
- if (this.length >= this.array.length) {
204
- this.expandArray()
205
- }
206
- this.array[this.items++] = value
207
- return this.length - 1
208
- }
209
-
210
- /**
211
- * Set a value at a specific index. Expands array if needed.
212
- */
213
- set(index: number, value: number) {
214
- if (index < 0) throw Error("Index out of bounds")
215
- if (index >= this.length) {
216
- while (index >= this.array.length) this.expandArray()
217
- this.items = index + 1
218
- }
219
- this.array[index] = value
220
- }
221
-
222
- /**
223
- * Push multiple values to the end of the array.
224
- */
225
- pushMany(values: number[] | TypedArray) {
226
- while (this.length + values.length > this.array.length) this.expandArray()
227
- this.array.set(values, this.length)
228
- this.items += values.length
229
- }
230
-
231
- /**
232
- * Shrink the buffer to exactly fit stored items.
233
- * Buffer becomes fixed-length after compacting.
234
- */
235
- compact() {
236
- if (this.buffer instanceof SharedArrayBuffer) {
237
- // SharedArrayBuffer uses slice() to create a new fixed-size buffer
238
- this.buffer = this.buffer.slice(
239
- 0,
240
- this.length * this.ArrayType.BYTES_PER_ELEMENT,
241
- )
242
- } else {
243
- // ArrayBuffer uses transferToFixedLength() to detach and resize
244
- this.buffer = this.buffer.transferToFixedLength(
245
- this.length * this.ArrayType.BYTES_PER_ELEMENT,
246
- )
247
- }
248
- this.array = new this.ArrayType(this.buffer)
249
- return this.array
250
- }
129
+ /** The typed array constructor for this instance */
130
+ readonly ArrayType: TypedArrayConstructor<TA>;
131
+ /** Buffer constructor (SharedArrayBuffer or ArrayBuffer) */
132
+ private readonly BC: BufferConstructorType;
133
+ /** View of the whole buffer: stored items, then spare capacity. */
134
+ private view: TA;
135
+ /** Number of items actually stored (may be less than the capacity). */
136
+ private items = 0;
137
+
138
+ /**
139
+ * The buffer size a new column reaches when it is filled one `push` at a time to
140
+ * `requiredBytes`, for projecting peak memory before a load.
141
+ */
142
+ static capacityBytesFor(requiredBytes: number, bytesPerElement: number): number {
143
+ let bytes = INITIAL_BYTES;
144
+ while (bytes < requiredBytes) {
145
+ bytes = nextByteLength(bytes, bytes + bytesPerElement, bytesPerElement);
146
+ }
147
+ return bytes;
148
+ }
149
+
150
+ /**
151
+ * Wrap an existing buffer, as after transferring it between workers. Every element of the
152
+ * buffer is a stored item. Nothing is allocated until the array grows.
153
+ */
154
+ static from<TA extends TypedArray>(ArrayType: TypedArrayConstructor<TA>, buffer: BufferType) {
155
+ const BC = isSharedArrayBuffer(buffer) ? SharedArrayBuffer : ArrayBuffer;
156
+ const rta = new ResizeableTypedArray<TA>(ArrayType, BC, new ArrayType(buffer));
157
+ rta.items = rta.view.length;
158
+ return rta;
159
+ }
160
+
161
+ /**
162
+ * Create a new ResizeableTypedArray with an empty buffer.
163
+ *
164
+ * @param ArrayType - The typed array constructor (e.g., Float64Array).
165
+ * @param BC - Buffer constructor to use (defaults to SharedArrayBuffer if available).
166
+ */
167
+ constructor(
168
+ ArrayType: TypedArrayConstructor<TA>,
169
+ BC: BufferConstructorType = BufferConstructor,
170
+ /** @internal A view to wrap instead of allocating; used by `from()`. */
171
+ view?: TA,
172
+ ) {
173
+ this.ArrayType = ArrayType;
174
+ this.BC = BC;
175
+ this.view = view ?? new ArrayType(this.allocate("create", INITIAL_BYTES, GROWTH_RESERVE_BYTES));
176
+ }
177
+
178
+ /** The stored items. Before `compact()`, a view that excludes the spare capacity. */
179
+ get array(): TA {
180
+ return this.items === this.view.length ? this.view : (this.view.subarray(0, this.items) as TA);
181
+ }
182
+
183
+ /** The underlying buffer, including any spare capacity. */
184
+ get buffer(): BufferType {
185
+ return this.view.buffer as BufferType;
186
+ }
187
+
188
+ get length() {
189
+ return this.items;
190
+ }
191
+
192
+ /** Number of items the current buffer can hold before it must grow. */
193
+ get capacity() {
194
+ return this.view.length;
195
+ }
196
+
197
+ /** Iterate over the stored values. */
198
+ [Symbol.iterator](): ArrayIterator<number> {
199
+ return this.array[Symbol.iterator]();
200
+ }
201
+
202
+ /**
203
+ * Get the value at an index. Handles negative indices.
204
+ */
205
+ at(index: number): number {
206
+ if (index >= 0 && index < this.items) return this.view[index]!;
207
+ if (index < 0 && index >= -this.items) return this.view[this.items + index]!;
208
+ throw Error(`Index out of bounds: ${index}. Length: ${this.items}`);
209
+ }
210
+
211
+ /**
212
+ * Get a copy of a range of the stored items.
213
+ */
214
+ slice(start: number, end: number) {
215
+ return this.array.slice(start, end);
216
+ }
217
+
218
+ /**
219
+ * Push a value to the end of the array.
220
+ */
221
+ push(value: number): number {
222
+ if (this.items === this.view.length) this.ensureCapacity(this.items + 1);
223
+ this.view[this.items] = value;
224
+ return this.items++;
225
+ }
226
+
227
+ /**
228
+ * Set a value at a specific index. Expands array if needed.
229
+ */
230
+ set(index: number, value: number) {
231
+ if (index < 0) throw Error("Index out of bounds");
232
+ if (index >= this.items) {
233
+ this.ensureCapacity(index + 1);
234
+ this.items = index + 1;
235
+ }
236
+ this.view[index] = value;
237
+ }
238
+
239
+ /**
240
+ * Push multiple values to the end of the array.
241
+ */
242
+ pushMany(values: ArrayLike<number>) {
243
+ this.ensureCapacity(this.items + values.length);
244
+ this.view.set(values, this.items);
245
+ this.items += values.length;
246
+ }
247
+
248
+ /**
249
+ * Shrink the buffer to exactly fit stored items.
250
+ * Buffer becomes fixed-length after compacting.
251
+ */
252
+ compact() {
253
+ const requiredBytes = this.items * this.ArrayType.BYTES_PER_ELEMENT;
254
+ const buffer = this.buffer;
255
+ let compacted: BufferType;
256
+ try {
257
+ compacted = isSharedArrayBuffer(buffer)
258
+ ? // A SharedArrayBuffer cannot shrink: copy the items into a fixed-size buffer.
259
+ buffer.slice(0, requiredBytes)
260
+ : buffer.transferToFixedLength(requiredBytes);
261
+ } catch (cause) {
262
+ throw this.allocationError("compact", requiredBytes, cause);
263
+ }
264
+ this.view = new this.ArrayType(compacted);
265
+ return this.view;
266
+ }
267
+
268
+ /** Make room for `count` items: grow in place if the buffer can, otherwise reallocate. */
269
+ private ensureCapacity(count: number) {
270
+ if (count <= this.view.length) return;
271
+ const bytesPerElement = this.ArrayType.BYTES_PER_ELEMENT;
272
+ const buffer = this.buffer;
273
+ const byteLength = nextByteLength(buffer.byteLength, count * bytesPerElement, bytesPerElement);
274
+ if (buffer.maxByteLength >= byteLength && growInPlace(buffer, byteLength)) {
275
+ // A resized buffer keeps a length-tracking view: take a fresh fixed-length one.
276
+ this.view = new this.ArrayType(buffer, 0, byteLength / bytesPerElement);
277
+ return;
278
+ }
279
+ const next = this.allocate("grow", byteLength, GROWTH_RESERVE_BYTES);
280
+ const view = new this.ArrayType(next, 0, byteLength / bytesPerElement);
281
+ view.set(this.array);
282
+ this.view = view;
283
+ }
284
+
285
+ /**
286
+ * Allocate `byteLength` bytes that can grow in place up to `reserveBytes` when the runtime
287
+ * allows. A failed reservation falls back to a buffer that doubles once, then to fixed size.
288
+ */
289
+ private allocate(
290
+ operation: TypedBufferAllocationOperation,
291
+ byteLength: number,
292
+ reserveBytes: number,
293
+ ): BufferType {
294
+ for (const maxByteLength of [Math.max(reserveBytes, byteLength), byteLength * 2]) {
295
+ try {
296
+ return new this.BC(byteLength, { maxByteLength });
297
+ } catch {
298
+ // The runtime refused the reservation: try a smaller one, then a fixed buffer.
299
+ }
300
+ }
301
+ try {
302
+ return new this.BC(byteLength);
303
+ } catch (cause) {
304
+ throw this.allocationError(operation, byteLength, cause);
305
+ }
306
+ }
307
+
308
+ private allocationError(
309
+ operation: TypedBufferAllocationOperation,
310
+ requiredBytes: number,
311
+ cause: unknown,
312
+ ): TypedBufferAllocationError {
313
+ return new TypedBufferAllocationError(
314
+ {
315
+ operation,
316
+ typedArray: this.ArrayType.name,
317
+ bufferType: getBufferType(this.BC),
318
+ elementCount: Math.ceil(requiredBytes / this.ArrayType.BYTES_PER_ELEMENT),
319
+ bytesPerElement: this.ArrayType.BYTES_PER_ELEMENT,
320
+ requiredBytes,
321
+ },
322
+ cause,
323
+ );
324
+ }
325
+ }
326
+
327
+ /**
328
+ * The next buffer size for a column that needs `requiredBytes`: double while small, then grow
329
+ * by 1.5×, and never less than required. Rounded up to a multiple of 8 bytes so every element
330
+ * type divides it.
331
+ */
332
+ function nextByteLength(currentBytes: number, requiredBytes: number, bytesPerElement: number) {
333
+ const current = Math.max(currentBytes, bytesPerElement);
334
+ const grown = current < DOUBLING_LIMIT_BYTES ? current * 2 : Math.ceil(current * 1.5);
335
+ return Math.ceil(Math.max(grown, requiredBytes) / 8) * 8;
336
+ }
337
+
338
+ /** Grow a growable or resizable buffer to `byteLength`. False if it cannot grow in place. */
339
+ function growInPlace(buffer: BufferType, byteLength: number): boolean {
340
+ try {
341
+ if (isSharedArrayBuffer(buffer)) {
342
+ if (!buffer.growable) return false;
343
+ buffer.grow(byteLength);
344
+ return true;
345
+ }
346
+ if (!buffer.resizable) return false;
347
+ buffer.resize(byteLength);
348
+ return true;
349
+ } catch {
350
+ // Committing the pages failed: fall back to reallocating, which reports its own error.
351
+ return false;
352
+ }
251
353
  }
@@ -1,37 +1,32 @@
1
1
  declare module "geoflatbush" {
2
- import type Flatbush from "flatbush"
2
+ import type Flatbush from "flatbush";
3
3
 
4
- /**
5
- * Returns an array of the closest items from a given location in order of increasing distance.
6
- * @param index - A Flatbush index with geographic bounding boxes in degrees (minLng, minLat, maxLng, maxLat).
7
- * @param lng - Query point longitude in degrees.
8
- * @param lat - Query point latitude in degrees.
9
- * @param maxResults - Maximum number of items to return (default: Infinity).
10
- * @param maxDistance - Maximum distance in kilometers (default: Infinity).
11
- * @param filterFn - Optional filter function called with index of each item.
12
- * @returns Array of item indices from the index, sorted by distance.
13
- */
14
- export function around(
15
- index: Flatbush,
16
- lng: number,
17
- lat: number,
18
- maxResults?: number,
19
- maxDistance?: number,
20
- filterFn?: (index: number) => boolean,
21
- ): number[]
4
+ /**
5
+ * Returns an array of the closest items from a given location in order of increasing distance.
6
+ * @param index - A Flatbush index with geographic bounding boxes in degrees (minLng, minLat, maxLng, maxLat).
7
+ * @param lng - Query point longitude in degrees.
8
+ * @param lat - Query point latitude in degrees.
9
+ * @param maxResults - Maximum number of items to return (default: Infinity).
10
+ * @param maxDistance - Maximum distance in kilometers (default: Infinity).
11
+ * @param filterFn - Optional filter function called with index of each item.
12
+ * @returns Array of item indices from the index, sorted by distance.
13
+ */
14
+ export function around(
15
+ index: Flatbush,
16
+ lng: number,
17
+ lat: number,
18
+ maxResults?: number,
19
+ maxDistance?: number,
20
+ filterFn?: (index: number) => boolean,
21
+ ): number[];
22
22
 
23
- /**
24
- * Returns the great circle distance between two locations in kilometers.
25
- * @param lng1 - First point longitude in degrees.
26
- * @param lat1 - First point latitude in degrees.
27
- * @param lng2 - Second point longitude in degrees.
28
- * @param lat2 - Second point latitude in degrees.
29
- * @returns Distance in kilometers.
30
- */
31
- export function distance(
32
- lng: number,
33
- lat: number,
34
- lng2: number,
35
- lat2: number,
36
- ): number
23
+ /**
24
+ * Returns the great circle distance between two locations in kilometers.
25
+ * @param lng1 - First point longitude in degrees.
26
+ * @param lat1 - First point latitude in degrees.
27
+ * @param lng2 - Second point longitude in degrees.
28
+ * @param lat2 - Second point latitude in degrees.
29
+ * @returns Distance in kilometers.
30
+ */
31
+ export function distance(lng: number, lat: number, lng2: number, lat2: number): number;
37
32
  }