@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.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +136 -72
- package/dist/contracts.d.ts +27 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +1 -0
- package/dist/contracts.js.map +1 -0
- package/dist/entities.d.ts +22 -2
- package/dist/entities.d.ts.map +1 -1
- package/dist/entities.js +54 -10
- package/dist/entities.js.map +1 -1
- package/dist/ids.d.ts +20 -5
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +87 -29
- package/dist/ids.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/indirect-kd-index.d.ts +28 -0
- package/dist/indirect-kd-index.d.ts.map +1 -0
- package/dist/indirect-kd-index.js +163 -0
- package/dist/indirect-kd-index.js.map +1 -0
- package/dist/limits.d.ts +30 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +51 -0
- package/dist/limits.js.map +1 -0
- package/dist/mocks.d.ts.map +1 -1
- package/dist/mocks.js.map +1 -1
- package/dist/nodes.d.ts +36 -15
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +185 -62
- package/dist/nodes.js.map +1 -1
- package/dist/osm.d.ts +60 -4
- package/dist/osm.d.ts.map +1 -1
- package/dist/osm.js +32 -6
- package/dist/osm.js.map +1 -1
- package/dist/relations.d.ts +11 -4
- package/dist/relations.d.ts.map +1 -1
- package/dist/relations.js +73 -29
- package/dist/relations.js.map +1 -1
- package/dist/renumber.d.ts +23 -0
- package/dist/renumber.d.ts.map +1 -0
- package/dist/renumber.js +64 -0
- package/dist/renumber.js.map +1 -0
- package/dist/stringtable.d.ts +2 -1
- package/dist/stringtable.d.ts.map +1 -1
- package/dist/stringtable.js +9 -14
- package/dist/stringtable.js.map +1 -1
- package/dist/tags.d.ts +45 -12
- package/dist/tags.d.ts.map +1 -1
- package/dist/tags.js +153 -27
- package/dist/tags.js.map +1 -1
- package/dist/typed-arrays.d.ts +63 -48
- package/dist/typed-arrays.d.ts.map +1 -1
- package/dist/typed-arrays.js +184 -107
- package/dist/typed-arrays.js.map +1 -1
- package/dist/ways.d.ts +52 -3
- package/dist/ways.d.ts.map +1 -1
- package/dist/ways.js +223 -46
- package/dist/ways.js.map +1 -1
- package/package.json +45 -31
- package/src/contracts.ts +29 -0
- package/src/entities.ts +242 -194
- package/src/ids.ts +278 -235
- package/src/index.ts +22 -13
- package/src/indirect-kd-index.ts +212 -0
- package/src/limits.ts +73 -0
- package/src/mocks.ts +114 -113
- package/src/nodes.ts +538 -397
- package/src/osm.ts +282 -207
- package/src/relations.ts +580 -558
- package/src/renumber.ts +80 -0
- package/src/stringtable.ts +189 -184
- package/src/tags.ts +406 -272
- package/src/typed-arrays.ts +307 -205
- package/src/types/geoflatbush.d.ts +28 -33
- package/src/ways.ts +577 -406
- package/test/entity-index-errors.test.ts +41 -0
- package/test/ids.bench.ts +33 -0
- package/test/ids.test.ts +122 -0
- package/test/limits.test.ts +83 -0
- package/test/memory.test.ts +29 -30
- package/test/no-shared-array-buffer.test.ts +41 -0
- package/test/nodes-spatial-index.test.ts +261 -0
- package/test/osm-bbox.test.ts +30 -0
- package/test/relations.bench.ts +32 -0
- package/test/relations.test.ts +124 -0
- package/test/renumber.test.ts +54 -0
- package/test/spatial-index-rebuild.test.ts +33 -0
- package/test/storage-compaction.test.ts +269 -0
- package/test/tags-search.test.ts +89 -56
- package/test/typed-arrays.test.ts +252 -132
- package/tsconfig.build.json +6 -6
- package/tsconfig.json +7 -7
- package/src/types/geokdbush.d.ts +0 -37
- package/src/types/kdbush.d.ts +0 -47
package/src/typed-arrays.ts
CHANGED
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
*
|
|
66
|
-
*
|
|
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
|
-
|
|
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()`
|
|
74
|
-
*
|
|
75
|
-
* -
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
2
|
+
import type Flatbush from "flatbush";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
}
|