ordered-binary 1.6.0 → 1.6.2

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.
@@ -0,0 +1,450 @@
1
+ /*
2
+ control character types:
3
+ 1 - metadata
4
+ 2 - symbols
5
+ 6 - false
6
+ 7 - true
7
+ 8- 16 - negative doubles
8
+ 16-24 positive doubles
9
+ 27 - String starts with a character 27 or less or is an empty string
10
+ 0 - multipart separator
11
+ > 27 normal string characters
12
+ */
13
+ /*
14
+ * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
15
+ */
16
+
17
+ const float64Array = new Float64Array(2)
18
+ const int32Array = new Int32Array(float64Array.buffer, 0, 4)
19
+ const {lowIdx, highIdx} = (() => {
20
+ if (new Uint8Array(new Uint32Array([0xFFEE1100]).buffer)[0] === 0xFF) {
21
+ return { lowIdx: 1, highIdx: 0 };
22
+ }
23
+ return { lowIdx: 0, highIdx: 1 };
24
+ })();
25
+ let nullTerminate = false
26
+ let textEncoder
27
+ try {
28
+ textEncoder = new TextEncoder()
29
+ } catch (error) {}
30
+
31
+ /*
32
+ * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
33
+ */
34
+ export function writeKey(key, target, position, inSequence) {
35
+ let targetView = target.dataView
36
+ if (!targetView)
37
+ targetView = target.dataView = new DataView(target.buffer, target.byteOffset, ((target.byteLength + 3) >> 2) << 2)
38
+ switch (typeof key) {
39
+ case 'string':
40
+ let strLength = key.length
41
+ let c1 = key.charCodeAt(0)
42
+ if (!(c1 >= 28)) // escape character
43
+ target[position++] = 27
44
+ if (strLength < 0x40) {
45
+ let i, c2
46
+ for (i = 0; i < strLength; i++) {
47
+ c1 = key.charCodeAt(i)
48
+ if (c1 <= 4) {
49
+ target[position++] = 4
50
+ target[position++] = c1
51
+ } else if (c1 < 0x80) {
52
+ target[position++] = c1
53
+ } else if (c1 < 0x800) {
54
+ target[position++] = c1 >> 6 | 0xc0
55
+ target[position++] = c1 & 0x3f | 0x80
56
+ } else if (
57
+ (c1 & 0xfc00) === 0xd800 &&
58
+ ((c2 = key.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
59
+ ) {
60
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
61
+ i++
62
+ target[position++] = c1 >> 18 | 0xf0
63
+ target[position++] = c1 >> 12 & 0x3f | 0x80
64
+ target[position++] = c1 >> 6 & 0x3f | 0x80
65
+ target[position++] = c1 & 0x3f | 0x80
66
+ } else {
67
+ target[position++] = c1 >> 12 | 0xe0
68
+ target[position++] = c1 >> 6 & 0x3f | 0x80
69
+ target[position++] = c1 & 0x3f | 0x80
70
+ }
71
+ }
72
+ } else {
73
+ if (target.utf8Write)
74
+ position += target.utf8Write(key, position, target.byteLength - position)
75
+ else
76
+ position += textEncoder.encodeInto(key, target.subarray(position)).written
77
+ if (position > target.length - 4)
78
+ throw new RangeError('String does not fit in target buffer')
79
+ }
80
+ break
81
+ case 'number':
82
+ float64Array[0] = key
83
+ let lowInt = int32Array[lowIdx]
84
+ let highInt = int32Array[highIdx]
85
+ let length
86
+ if (key < 0) {
87
+ targetView.setInt32(position + 4, ~((lowInt >>> 4) | (highInt << 28)))
88
+ targetView.setInt32(position + 0, (highInt ^ 0x7fffffff) >>> 4)
89
+ targetView.setInt32(position + 8, ((lowInt & 0xf) ^ 0xf) << 4, true) // just always do the null termination here
90
+ return position + 9
91
+ } else if ((lowInt & 0xf) || inSequence) {
92
+ length = 9
93
+ } else if (lowInt & 0xfffff)
94
+ length = 8
95
+ else if (lowInt || (highInt & 0xf))
96
+ length = 6
97
+ else
98
+ length = 4
99
+ // switching order to go to little endian
100
+ targetView.setInt32(position + 0, (highInt >>> 4) | 0x10000000)
101
+ targetView.setInt32(position + 4, (lowInt >>> 4) | (highInt << 28))
102
+ // if (length == 9 || nullTerminate)
103
+ targetView.setInt32(position + 8, (lowInt & 0xf) << 4, true)
104
+ return position + length;
105
+ case 'object':
106
+ if (key) {
107
+ if (Array.isArray(key)) {
108
+ for (let i = 0, l = key.length; i < l; i++) {
109
+ if (i > 0)
110
+ target[position++] = 0
111
+ position = writeKey(key[i], target, position, true)
112
+ }
113
+ break
114
+ } else if (key instanceof Uint8Array) {
115
+ target.set(key, position)
116
+ position += key.length
117
+ break
118
+ } else {
119
+ throw new Error('Unable to serialize object as a key: ' + JSON.stringify(key))
120
+ }
121
+ } else // null
122
+ target[position++] = 0
123
+ break
124
+ case 'boolean':
125
+ targetView.setUint32(position++, key ? 7 : 6, true)
126
+ return position
127
+ case 'bigint':
128
+ let asFloat = Number(key)
129
+ if (BigInt(asFloat) > key) {
130
+ float64Array[0] = asFloat;
131
+ if (asFloat > 0) {
132
+ if (int32Array[lowIdx])
133
+ int32Array[lowIdx]--;
134
+ else {
135
+ int32Array[highIdx]--;
136
+ int32Array[lowIdx] = 0xffffffff;
137
+ }
138
+ } else {
139
+ if (int32Array[lowIdx] < 0xffffffff)
140
+ int32Array[lowIdx]++;
141
+ else {
142
+ int32Array[highIdx]++;
143
+ int32Array[lowIdx] = 0;
144
+ }
145
+ }
146
+ asFloat = float64Array[0];
147
+ }
148
+ let difference = key - BigInt(asFloat);
149
+ if (difference === 0n)
150
+ return writeKey(asFloat, target, position, inSequence)
151
+ writeKey(asFloat, target, position, inSequence)
152
+ position += 9; // always increment by 9 if we are adding fractional bits
153
+ let exponent = BigInt((int32Array[highIdx] >> 20 & 0x7ff) - 1079);
154
+ let nextByte = difference >> exponent;
155
+ target[position - 1] |= Number(nextByte);
156
+ difference -= nextByte << exponent;
157
+ let first = true;
158
+ while (difference || first) {
159
+ first = false;
160
+ exponent -= 7n;
161
+ let nextByte = difference >> exponent;
162
+ target[position++] = Number(nextByte) | 0x80;
163
+ difference -= nextByte << exponent;
164
+ }
165
+ return position;
166
+ case 'undefined':
167
+ return position
168
+ // undefined is interpreted as the absence of a key, signified by zero length
169
+ case 'symbol':
170
+ target[position++] = 2
171
+ return writeKey(key.description, target, position, inSequence)
172
+ default:
173
+ throw new Error('Can not serialize key of type ' + typeof key)
174
+ }
175
+ if (nullTerminate && !inSequence)
176
+ targetView.setUint32(position, 0)
177
+ return position
178
+ }
179
+
180
+ let position
181
+ export function readKey(buffer, start, end, inSequence) {
182
+ position = start
183
+ let controlByte = buffer[position]
184
+ let value
185
+ if (controlByte < 24) {
186
+ if (controlByte < 8) {
187
+ position++
188
+ if (controlByte == 6) {
189
+ value = false
190
+ } else if (controlByte == 7) {
191
+ value = true
192
+ } else if (controlByte == 0) {
193
+ value = null
194
+ } else if (controlByte == 2) {
195
+ value = Symbol.for(readStringSafely(buffer, end))
196
+ } else
197
+ return Uint8Array.prototype.slice.call(buffer, start, end)
198
+ } else {
199
+ let dataView;
200
+ try {
201
+ dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2))
202
+ } catch(error) {
203
+ // if it is write at the end of the ArrayBuffer, we may need to retry with the exact remaining bytes
204
+ dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.buffer.byteLength - buffer.byteOffset))
205
+ }
206
+
207
+ let highInt = dataView.getInt32(position) << 4
208
+ let size = end - position
209
+ let lowInt
210
+ if (size > 4) {
211
+ lowInt = position + 8 <= buffer.length ? dataView.getInt32(position + 4) : (
212
+ buffer[position + 4] << 24 |
213
+ buffer[position + 5] << 16 |
214
+ buffer[position + 6] << 8 |
215
+ buffer[position + 7]
216
+ )
217
+ highInt |= lowInt >>> 28
218
+ if (size <= 6) { // clear the last bits
219
+ lowInt &= -0x10000
220
+ }
221
+ lowInt = lowInt << 4
222
+ if (size > 8) {
223
+ lowInt = lowInt | buffer[position + 8] >> 4
224
+ }
225
+ } else
226
+ lowInt = 0
227
+ if (controlByte < 16) {
228
+ // negative gets negated
229
+ highInt = highInt ^ 0x7fffffff
230
+ lowInt = ~lowInt
231
+ }
232
+ int32Array[highIdx] = highInt
233
+ int32Array[lowIdx] = lowInt
234
+ value = float64Array[0]
235
+ position += 9
236
+ if (size > 9 && buffer[position] > 0) {
237
+ // convert the float to bigint, and then we will add precision as we enumerate through the
238
+ // extra bytes
239
+ value = BigInt(value);
240
+ let exponent = highInt >> 20 & 0x7ff;
241
+ let next_byte = buffer[position - 1] & 0xf;
242
+ value += BigInt(next_byte) << BigInt(exponent - 1079);
243
+ while ((next_byte = buffer[position]) > 0 && position++ < end) {
244
+ value += BigInt(next_byte & 0x7f) << BigInt((start - position) * 7 + exponent - 1016);
245
+ }
246
+ }
247
+ }
248
+ } else {
249
+ if (controlByte == 27) {
250
+ position++
251
+ }
252
+ value = readStringSafely(buffer, end)
253
+ if (position < end) position-- // if have a null terminator for the string, count that as the array separator
254
+ }
255
+ while (position < end) {
256
+ if (buffer[position] === 0)
257
+ position++
258
+ if (inSequence) {
259
+ encoder.position = position
260
+ return value
261
+ }
262
+ let nextValue = readKey(buffer, position, end, true)
263
+ if (value instanceof Array) {
264
+ value.push(nextValue)
265
+ } else
266
+ value = [ value, nextValue ]
267
+ }
268
+ return value
269
+ }
270
+ export const enableNullTermination = () => nullTerminate = true
271
+
272
+ export const encoder = {
273
+ writeKey,
274
+ readKey,
275
+ enableNullTermination,
276
+ }
277
+ let targetBuffer = []
278
+ let targetPosition = 0
279
+ const hasNodeBuffer = typeof Buffer !== 'undefined'
280
+ const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
281
+ export const toBufferKey = (key) => {
282
+ let newBuffer
283
+ if (targetPosition + 100 > targetBuffer.length) {
284
+ targetBuffer = new ByteArrayAllocate(8192)
285
+ targetPosition = 0
286
+ newBuffer = true
287
+ }
288
+ try {
289
+ let result = targetBuffer.slice(targetPosition, targetPosition = writeKey(key, targetBuffer, targetPosition))
290
+ if (targetPosition > targetBuffer.length) {
291
+ if (newBuffer)
292
+ throw new Error('Key is too large')
293
+ return toBufferKey(key)
294
+ }
295
+ return result
296
+ } catch(error) {
297
+ if (newBuffer)
298
+ throw error
299
+ targetPosition = targetBuffer.length
300
+ return toBufferKey(key)
301
+ }
302
+ }
303
+ export const fromBufferKey = (sourceBuffer) => {
304
+ return readKey(sourceBuffer, 0, sourceBuffer.length)
305
+ }
306
+ const fromCharCode = String.fromCharCode
307
+ function makeStringBuilder() {
308
+ let stringBuildCode = '(source) => {'
309
+ let previous = []
310
+ for (let i = 0; i < 0x30; i++) {
311
+ let v = fromCharCode((i & 0xf) + 97) + fromCharCode((i >> 4) + 97)
312
+ stringBuildCode += `
313
+ let ${v} = source[position++]
314
+ if (${v} > 4) {
315
+ if (${v} >= 0x80) ${v} = finishUtf8(${v}, source)
316
+ } else {
317
+ if (${v} === 4)
318
+ ${v} = source[position++]
319
+ else
320
+ return fromCharCode(${previous})
321
+ }
322
+ `
323
+ previous.push(v)
324
+ if (i == 1000000) // this just exists to prevent rollup from doing dead code elimination on finishUtf8
325
+ finishUtf8()
326
+ }
327
+ stringBuildCode += `return fromCharCode(${previous}) + readString(source)}`
328
+ return stringBuildCode
329
+ }
330
+
331
+ let pendingSurrogate
332
+ function finishUtf8(byte1, src) {
333
+ if ((byte1 & 0xe0) === 0xc0) {
334
+ // 2 bytes
335
+ const byte2 = src[position++] & 0x3f
336
+ return ((byte1 & 0x1f) << 6) | byte2
337
+ } else if ((byte1 & 0xf0) === 0xe0) {
338
+ // 3 bytes
339
+ const byte2 = src[position++] & 0x3f
340
+ const byte3 = src[position++] & 0x3f
341
+ return ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3
342
+ } else if ((byte1 & 0xf8) === 0xf0) {
343
+ // 4 bytes
344
+ if (pendingSurrogate) {
345
+ byte1 = pendingSurrogate
346
+ pendingSurrogate = null
347
+ position += 3
348
+ return byte1
349
+ }
350
+ const byte2 = src[position++] & 0x3f
351
+ const byte3 = src[position++] & 0x3f
352
+ const byte4 = src[position++] & 0x3f
353
+ let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4
354
+ if (unit > 0xffff) {
355
+ pendingSurrogate = 0xdc00 | (unit & 0x3ff)
356
+ unit = (((unit - 0x10000) >>> 10) & 0x3ff) | 0xd800
357
+ position -= 4 // reset so we can return the next part of the surrogate pair
358
+ }
359
+ return unit
360
+ } else {
361
+ return byte1
362
+ }
363
+ }
364
+
365
+ const readString =
366
+ typeof process !== 'undefined' && process.isBun ? // the eval in bun doesn't properly closure on position, so we
367
+ // have to manually update it
368
+ (function(reading) {
369
+ let { setPosition, getPosition, readString } = reading;
370
+ return (source) => {
371
+ setPosition(position);
372
+ let value = readString(source);
373
+ position = getPosition();
374
+ return value;
375
+ };
376
+ })((new Function('fromCharCode', 'let position; let pendingSurrogate; ' +
377
+ // finishUtf8 references `position`/`pendingSurrogate`, but new Function compiles
378
+ // in global scope and can't close over the module-level copies, so embed its
379
+ // source here to bind it to this function's local (synced) `position`.
380
+ 'let finishUtf8 = ' + finishUtf8.toString() + '; ' +
381
+ 'let readString = ' + makeStringBuilder() +
382
+ ';return {' +
383
+ 'setPosition(p) { position = p },' +
384
+ 'getPosition() { return position },' +
385
+ 'readString }'))(fromCharCode)) :
386
+ eval(makeStringBuilder())
387
+ function readStringSafely(source, end) {
388
+ if (source[end] > 0) {
389
+ let previous = source[end]
390
+ try {
391
+ // read string expects a null terminator, that is a 0 or undefined from reading past the end of the buffer, so we
392
+ // have to ensure that, but do so safely, restoring the buffer to its original state
393
+ source[end] = 0
394
+ return readString(source)
395
+ } finally {
396
+ source[end] = previous
397
+ }
398
+ } else return readString(source);
399
+ }
400
+ export function compareKeys(a, b) {
401
+ // compare with type consistency that matches binary comparison
402
+ if (typeof a == 'object') {
403
+ if (!a) {
404
+ return b == null ? 0 : -1
405
+ }
406
+ if (a.compare) {
407
+ if (b == null) {
408
+ return 1
409
+ } else if (b.compare) {
410
+ return a.compare(b)
411
+ } else {
412
+ return -1
413
+ }
414
+ }
415
+ let arrayComparison
416
+ if (b instanceof Array) {
417
+ let i = 0
418
+ while((arrayComparison = compareKeys(a[i], b[i])) == 0 && i <= a.length) {
419
+ i++
420
+ }
421
+ return arrayComparison
422
+ }
423
+ arrayComparison = compareKeys(a[0], b)
424
+ if (arrayComparison == 0 && a.length > 1)
425
+ return 1
426
+ return arrayComparison
427
+ } else if (typeof a == typeof b) {
428
+ if (typeof a === 'symbol') {
429
+ a = Symbol.keyFor(a)
430
+ b = Symbol.keyFor(b)
431
+ }
432
+ return a < b ? -1 : a === b ? 0 : 1
433
+ }
434
+ else if (typeof b == 'object') {
435
+ if (b instanceof Array)
436
+ return -compareKeys(b, a)
437
+ return 1
438
+ } else {
439
+ return typeOrder[typeof a] < typeOrder[typeof b] ? -1 : 1
440
+ }
441
+ }
442
+ const typeOrder = {
443
+ symbol: 0,
444
+ undefined: 1,
445
+ boolean: 2,
446
+ number: 3,
447
+ string: 4
448
+ }
449
+ export const MINIMUM_KEY = null
450
+ export const MAXIMUM_KEY = new Uint8Array([0xff])