ordered-binary 1.2.3 → 1.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/dist/index.cjs CHANGED
@@ -2,338 +2,358 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- /*
6
- control character types:
7
- 1 - metadata
8
- 2 - symbols
9
- 6 - false
10
- 7 - true
11
- 8- 16 - negative doubles
12
- 16-24 positive doubles
13
- 27 - String starts with a character 27 or less or is an empty string
14
- 0 - multipart separator
15
- > 27 normal string characters
16
- */
17
- /*
18
- * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
19
- */
20
-
21
- const float64Array = new Float64Array(2);
22
- const int32Array = new Int32Array(float64Array.buffer, 0, 4);
23
- new Uint8Array(float64Array.buffer, 2, 6);
24
- new Uint8Array(float64Array.buffer, 0, 8);
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
- 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 < 0x80) {
49
- target[position++] = c1;
50
- } else if (c1 < 0x800) {
51
- target[position++] = c1 >> 6 | 0xc0;
52
- target[position++] = c1 & 0x3f | 0x80;
53
- } else if (
54
- (c1 & 0xfc00) === 0xd800 &&
55
- ((c2 = key.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
56
- ) {
57
- c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
58
- i++;
59
- target[position++] = c1 >> 18 | 0xf0;
60
- target[position++] = c1 >> 12 & 0x3f | 0x80;
61
- target[position++] = c1 >> 6 & 0x3f | 0x80;
62
- target[position++] = c1 & 0x3f | 0x80;
63
- } else {
64
- target[position++] = c1 >> 12 | 0xe0;
65
- target[position++] = c1 >> 6 & 0x3f | 0x80;
66
- target[position++] = c1 & 0x3f | 0x80;
67
- }
68
- }
69
- } else {
70
- if (target.utf8Write)
71
- position += target.utf8Write(key, position);
72
- else
73
- position += textEncoder.encodeInto(key, target.subarray(position)).written;
74
- if (position > target.length - 4)
75
- throw new RangeError('String does not fit in target buffer')
76
- }
77
- break
78
- case 'number':
79
- float64Array[0] = key;
80
- let lowInt = int32Array[0];
81
- let highInt = int32Array[1];
82
- let length;
83
- if (key < 0) {
84
- targetView.setInt32(position + 4, ~((lowInt >>> 4) | (highInt << 28)));
85
- targetView.setInt32(position + 0, (highInt ^ 0x7fffffff) >>> 4);
86
- targetView.setInt32(position + 8, ((lowInt & 0xf) ^ 0xf) << 4, true); // just always do the null termination here
87
- return position + 9
88
- } else if ((lowInt & 0xf) || inSequence) {
89
- length = 9;
90
- } else if (lowInt & 0xfffff)
91
- length = 8;
92
- else if (lowInt || (highInt & 0xf))
93
- length = 6;
94
- else
95
- length = 4;
96
- // switching order to go to little endian
97
- targetView.setInt32(position + 0, (highInt >>> 4) | 0x10000000);
98
- targetView.setInt32(position + 4, (lowInt >>> 4) | (highInt << 28));
99
- // if (length == 9 || nullTerminate)
100
- targetView.setInt32(position + 8, (lowInt & 0xf) << 4, true);
101
- return position + length;
102
- case 'object':
103
- if (key) {
104
- if (key instanceof Array) {
105
- for (let i = 0, l = key.length; i < l; i++) {
106
- if (i > 0)
107
- target[position++] = 0;
108
- position = writeKey(key[i], target, position, true);
109
- }
110
- break
111
- } else if (key instanceof Uint8Array) {
112
- target.set(key, position);
113
- position += key.length;
114
- break
115
- } else {
116
- throw new Error('Unable to serialize object as a key')
117
- }
118
- } else // null
119
- target[position++] = 0;
120
- break
121
- case 'boolean':
122
- targetView.setUint32(position++, key ? 7 : 6, true);
123
- return position
124
- case 'bigint':
125
- return writeKey(Number(key), target, position, inSequence)
126
- case 'undefined':
127
- return position
128
- // undefined is interpreted as the absence of a key, signified by zero length
129
- case 'symbol':
130
- target[position++] = 2;
131
- return writeKey(key.description, target, position, inSequence)
132
- default:
133
- throw new Error('Can not serialize key of type ' + typeof key)
134
- }
135
- if (nullTerminate && !inSequence)
136
- targetView.setUint32(position, 0);
137
- return position
138
- }
139
-
140
- let position;
141
- function readKey(buffer, start, end, inSequence) {
142
- buffer[end] = 0; // make sure it is null terminated
143
- position = start;
144
- let controlByte = buffer[position];
145
- let value;
146
- if (controlByte < 24) {
147
- if (controlByte < 8) {
148
- position++;
149
- if (controlByte == 6) {
150
- value = false;
151
- } else if (controlByte == 7) {
152
- value = true;
153
- } else if (controlByte == 0) {
154
- value = null;
155
- } else if (controlByte == 2) {
156
- value = Symbol.for(readString(buffer));
157
- } else
158
- return Uint8Array.prototype.slice.call(buffer, start, end)
159
- } else {
160
- let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2));
161
- let highInt = dataView.getInt32(position) << 4;
162
- let size = end - position;
163
- let lowInt;
164
- if (size > 4) {
165
- lowInt = dataView.getInt32(position + 4);
166
- highInt |= lowInt >>> 28;
167
- if (size <= 6) { // clear the last bits
168
- lowInt &= -0x1000;
169
- }
170
- lowInt = lowInt << 4;
171
- if (size > 8) {
172
- lowInt = lowInt | buffer[position + 8] >> 4;
173
- }
174
- } else
175
- lowInt = 0;
176
- if (controlByte < 16) {
177
- // negative gets negated
178
- highInt = highInt ^ 0x7fffffff;
179
- lowInt = ~lowInt;
180
- }
181
- int32Array[1] = highInt;
182
- int32Array[0] = lowInt;
183
- value = float64Array[0];
184
- position += 9;
185
- }
186
- } else {
187
- if (controlByte == 27) {
188
- position++;
189
- }
190
- value = readString(buffer);
191
- /*let strStart = position
192
- let strEnd = end
193
- for (; position < end; position++) {
194
- if (buffer[position] == 0) {
195
- break
196
- }
197
- }
198
- value = buffer.toString('utf8', strStart, position++)*/
199
- }
200
- while (position < end) {
201
- if (buffer[position] === 0)
202
- position++;
203
- if (inSequence) {
204
- encoder.position = position;
205
- return value
206
- }
207
- let nextValue = readKey(buffer, position, end, true);
208
- if (value instanceof Array) {
209
- value.push(nextValue);
210
- } else
211
- value = [ value, nextValue ];
212
- }
213
- return value
214
- }
215
- const enableNullTermination = () => nullTerminate = true;
216
-
217
- const encoder = {
218
- writeKey,
219
- readKey,
220
- enableNullTermination,
221
- };
222
- const toBufferKey = (key) => {
223
- let buffer = Buffer.alloc(2048);
224
- return buffer.slice(0, writeKey(key, buffer, 0, 2048) + 1)
225
- };
226
- const fromBufferKey = (sourceBuffer) => {
227
- return readKey(sourceBuffer, 0, sourceBuffer.length - 1)
228
- };
229
- const fromCharCode = String.fromCharCode;
230
- function makeStringBuilder() {
231
- let stringBuildCode = '(source) => {';
232
- let previous = [];
233
- for (let i = 0; i < 0x30; i++) {
234
- let v = fromCharCode((i & 0xf) + 97) + fromCharCode((i >> 4) + 97);
235
- stringBuildCode += `
236
- let ${v} = source[position++]
237
- if (${v} === 0)
238
- return fromCharCode(${previous})
239
- else if (${v} >= 0x80)
240
- ${v} = finishUtf8(${v}, source)
241
- `;
242
- previous.push(v);
243
- if (i == 1000000) // this just exists to prevent rollup from doing dead code elimination on finishUtf8
244
- finishUtf8();
245
- }
246
- stringBuildCode += `return fromCharCode(${previous}) + readString(source)}`;
247
- return stringBuildCode
248
- }
249
-
250
- let pendingSurrogate;
251
- function finishUtf8(byte1, src) {
252
- if ((byte1 & 0xe0) === 0xc0) {
253
- // 2 bytes
254
- const byte2 = src[position++] & 0x3f;
255
- return ((byte1 & 0x1f) << 6) | byte2
256
- } else if ((byte1 & 0xf0) === 0xe0) {
257
- // 3 bytes
258
- const byte2 = src[position++] & 0x3f;
259
- const byte3 = src[position++] & 0x3f;
260
- return ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3
261
- } else if ((byte1 & 0xf8) === 0xf0) {
262
- // 4 bytes
263
- if (pendingSurrogate) {
264
- byte1 = pendingSurrogate;
265
- pendingSurrogate = null;
266
- position += 3;
267
- return byte1
268
- }
269
- const byte2 = src[position++] & 0x3f;
270
- const byte3 = src[position++] & 0x3f;
271
- const byte4 = src[position++] & 0x3f;
272
- let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
273
- if (unit > 0xffff) {
274
- unit -= 0x10000;
275
- unit = 0xdc00 | (unit & 0x3ff);
276
- pendingSurrogate = ((unit >>> 10) & 0x3ff) | 0xd800;
277
- position -= 4; // reset so we can return the next part of the surrogate pair
278
- }
279
- return unit
280
- } else {
281
- return byte1
282
- }
283
- }
284
-
285
- const readString = eval(makeStringBuilder());
286
-
287
- function compareKeys(a, b) {
288
- // compare with type consistency that matches binary comparison
289
- if (typeof a == 'object') {
290
- if (!a) {
291
- return b == null ? 0 : -1
292
- }
293
- if (a.compare) {
294
- if (b == null) {
295
- return 1
296
- } else if (b.compare) {
297
- return a.compare(b)
298
- } else {
299
- return -1
300
- }
301
- }
302
- let arrayComparison;
303
- if (b instanceof Array) {
304
- let i = 0;
305
- while((arrayComparison = compareKeys(a[i], b[i])) == 0 && i <= a.length) {
306
- i++;
307
- }
308
- return arrayComparison
309
- }
310
- arrayComparison = compareKeys(a[0], b);
311
- if (arrayComparison == 0 && a.length > 1)
312
- return 1
313
- return arrayComparison
314
- } else if (typeof a == typeof b) {
315
- if (typeof a === 'symbol') {
316
- a = Symbol.keyFor(a);
317
- b = Symbol.keyFor(b);
318
- }
319
- return a < b ? -1 : a === b ? 0 : 1
320
- }
321
- else if (typeof b == 'object') {
322
- if (b instanceof Array)
323
- return -compareKeys(b, a)
324
- return 1
325
- } else {
326
- return typeOrder[typeof a] < typeOrder[typeof b] ? -1 : 1
327
- }
328
- }
329
- const typeOrder = {
330
- symbol: 0,
331
- undefined: 1,
332
- boolean: 2,
333
- number: 3,
334
- string: 4
335
- };
336
- const MINIMUM_KEY = null;
5
+ /*
6
+ control character types:
7
+ 1 - metadata
8
+ 2 - symbols
9
+ 6 - false
10
+ 7 - true
11
+ 8- 16 - negative doubles
12
+ 16-24 positive doubles
13
+ 27 - String starts with a character 27 or less or is an empty string
14
+ 0 - multipart separator
15
+ > 27 normal string characters
16
+ */
17
+ /*
18
+ * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
19
+ */
20
+
21
+ const float64Array = new Float64Array(2);
22
+ const int32Array = new Int32Array(float64Array.buffer, 0, 4);
23
+ let nullTerminate = false;
24
+ let textEncoder;
25
+ try {
26
+ textEncoder = new TextEncoder();
27
+ } catch (error) {}
28
+
29
+ /*
30
+ * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
31
+ */
32
+ function writeKey(key, target, position, inSequence) {
33
+ let targetView = target.dataView;
34
+ if (!targetView)
35
+ targetView = target.dataView = new DataView(target.buffer, target.byteOffset, ((target.byteLength + 3) >> 2) << 2);
36
+ switch (typeof key) {
37
+ case 'string':
38
+ let strLength = key.length;
39
+ let c1 = key.charCodeAt(0);
40
+ if (!(c1 >= 28)) // escape character
41
+ target[position++] = 27;
42
+ if (strLength < 0x40) {
43
+ let i, c2;
44
+ for (i = 0; i < strLength; i++) {
45
+ c1 = key.charCodeAt(i);
46
+ if (c1 < 0x80) {
47
+ target[position++] = c1;
48
+ } else if (c1 < 0x800) {
49
+ target[position++] = c1 >> 6 | 0xc0;
50
+ target[position++] = c1 & 0x3f | 0x80;
51
+ } else if (
52
+ (c1 & 0xfc00) === 0xd800 &&
53
+ ((c2 = key.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
54
+ ) {
55
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
56
+ i++;
57
+ target[position++] = c1 >> 18 | 0xf0;
58
+ target[position++] = c1 >> 12 & 0x3f | 0x80;
59
+ target[position++] = c1 >> 6 & 0x3f | 0x80;
60
+ target[position++] = c1 & 0x3f | 0x80;
61
+ } else {
62
+ target[position++] = c1 >> 12 | 0xe0;
63
+ target[position++] = c1 >> 6 & 0x3f | 0x80;
64
+ target[position++] = c1 & 0x3f | 0x80;
65
+ }
66
+ }
67
+ } else {
68
+ if (target.utf8Write)
69
+ position += target.utf8Write(key, position);
70
+ else
71
+ position += textEncoder.encodeInto(key, target.subarray(position)).written;
72
+ if (position > target.length - 4)
73
+ throw new RangeError('String does not fit in target buffer')
74
+ }
75
+ break
76
+ case 'number':
77
+ float64Array[0] = key;
78
+ let lowInt = int32Array[0];
79
+ let highInt = int32Array[1];
80
+ let length;
81
+ if (key < 0) {
82
+ targetView.setInt32(position + 4, ~((lowInt >>> 4) | (highInt << 28)));
83
+ targetView.setInt32(position + 0, (highInt ^ 0x7fffffff) >>> 4);
84
+ targetView.setInt32(position + 8, ((lowInt & 0xf) ^ 0xf) << 4, true); // just always do the null termination here
85
+ return position + 9
86
+ } else if ((lowInt & 0xf) || inSequence) {
87
+ length = 9;
88
+ } else if (lowInt & 0xfffff)
89
+ length = 8;
90
+ else if (lowInt || (highInt & 0xf))
91
+ length = 6;
92
+ else
93
+ length = 4;
94
+ // switching order to go to little endian
95
+ targetView.setInt32(position + 0, (highInt >>> 4) | 0x10000000);
96
+ targetView.setInt32(position + 4, (lowInt >>> 4) | (highInt << 28));
97
+ // if (length == 9 || nullTerminate)
98
+ targetView.setInt32(position + 8, (lowInt & 0xf) << 4, true);
99
+ return position + length;
100
+ case 'object':
101
+ if (key) {
102
+ if (Array.isArray(key)) {
103
+ for (let i = 0, l = key.length; i < l; i++) {
104
+ if (i > 0)
105
+ target[position++] = 0;
106
+ position = writeKey(key[i], target, position, true);
107
+ }
108
+ break
109
+ } else if (key instanceof Uint8Array) {
110
+ target.set(key, position);
111
+ position += key.length;
112
+ break
113
+ } else {
114
+ throw new Error('Unable to serialize object as a key')
115
+ }
116
+ } else // null
117
+ target[position++] = 0;
118
+ break
119
+ case 'boolean':
120
+ targetView.setUint32(position++, key ? 7 : 6, true);
121
+ return position
122
+ case 'bigint':
123
+ return writeKey(Number(key), target, position, inSequence)
124
+ case 'undefined':
125
+ return position
126
+ // undefined is interpreted as the absence of a key, signified by zero length
127
+ case 'symbol':
128
+ target[position++] = 2;
129
+ return writeKey(key.description, target, position, inSequence)
130
+ default:
131
+ throw new Error('Can not serialize key of type ' + typeof key)
132
+ }
133
+ if (nullTerminate && !inSequence)
134
+ targetView.setUint32(position, 0);
135
+ return position
136
+ }
137
+
138
+ let position;
139
+ function readKey(buffer, start, end, inSequence) {
140
+ buffer[end] = 0; // make sure it is null terminated
141
+ position = start;
142
+ let controlByte = buffer[position];
143
+ let value;
144
+ if (controlByte < 24) {
145
+ if (controlByte < 8) {
146
+ position++;
147
+ if (controlByte == 6) {
148
+ value = false;
149
+ } else if (controlByte == 7) {
150
+ value = true;
151
+ } else if (controlByte == 0) {
152
+ value = null;
153
+ } else if (controlByte == 2) {
154
+ value = Symbol.for(readString(buffer));
155
+ } else
156
+ return Uint8Array.prototype.slice.call(buffer, start, end)
157
+ } else {
158
+ let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2));
159
+ let highInt = dataView.getInt32(position) << 4;
160
+ let size = end - position;
161
+ let lowInt;
162
+ if (size > 4) {
163
+ lowInt = dataView.getInt32(position + 4);
164
+ highInt |= lowInt >>> 28;
165
+ if (size <= 6) { // clear the last bits
166
+ lowInt &= -0x1000;
167
+ }
168
+ lowInt = lowInt << 4;
169
+ if (size > 8) {
170
+ lowInt = lowInt | buffer[position + 8] >> 4;
171
+ }
172
+ } else
173
+ lowInt = 0;
174
+ if (controlByte < 16) {
175
+ // negative gets negated
176
+ highInt = highInt ^ 0x7fffffff;
177
+ lowInt = ~lowInt;
178
+ }
179
+ int32Array[1] = highInt;
180
+ int32Array[0] = lowInt;
181
+ value = float64Array[0];
182
+ position += 9;
183
+ }
184
+ } else {
185
+ if (controlByte == 27) {
186
+ position++;
187
+ }
188
+ value = readString(buffer);
189
+ /*let strStart = position
190
+ let strEnd = end
191
+ for (; position < end; position++) {
192
+ if (buffer[position] == 0) {
193
+ break
194
+ }
195
+ }
196
+ value = buffer.toString('utf8', strStart, position++)*/
197
+ }
198
+ while (position < end) {
199
+ if (buffer[position] === 0)
200
+ position++;
201
+ if (inSequence) {
202
+ encoder.position = position;
203
+ return value
204
+ }
205
+ let nextValue = readKey(buffer, position, end, true);
206
+ if (value instanceof Array) {
207
+ value.push(nextValue);
208
+ } else
209
+ value = [ value, nextValue ];
210
+ }
211
+ return value
212
+ }
213
+ const enableNullTermination = () => nullTerminate = true;
214
+
215
+ const encoder = {
216
+ writeKey,
217
+ readKey,
218
+ enableNullTermination,
219
+ };
220
+ let targetBuffer = [];
221
+ let targetPosition = 0;
222
+ const hasNodeBuffer = typeof Buffer !== 'undefined';
223
+ const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
224
+ const toBufferKey = (key) => {
225
+ let newBuffer;
226
+ if (targetPosition + 100 > targetBuffer.length) {
227
+ targetBuffer = new ByteArrayAllocate(8192);
228
+ targetPosition = 0;
229
+ newBuffer = true;
230
+ }
231
+ try {
232
+ let result = targetBuffer.slice(targetPosition, targetPosition = writeKey(key, targetBuffer, targetPosition));
233
+ if (targetPosition > targetBuffer.length) {
234
+ if (newBuffer)
235
+ throw new Error('Key is too large')
236
+ return toBufferKey(key)
237
+ }
238
+ return result
239
+ } catch(error) {
240
+ if (newBuffer)
241
+ throw error
242
+ targetPosition = targetBuffer.length;
243
+ return toBufferKey(key)
244
+ }
245
+ };
246
+ const fromBufferKey = (sourceBuffer) => {
247
+ return readKey(sourceBuffer, 0, sourceBuffer.length)
248
+ };
249
+ const fromCharCode = String.fromCharCode;
250
+ function makeStringBuilder() {
251
+ let stringBuildCode = '(source) => {';
252
+ let previous = [];
253
+ for (let i = 0; i < 0x30; i++) {
254
+ let v = fromCharCode((i & 0xf) + 97) + fromCharCode((i >> 4) + 97);
255
+ stringBuildCode += `
256
+ let ${v} = source[position++]
257
+ if (!${v})
258
+ return fromCharCode(${previous})
259
+ else if (${v} >= 0x80)
260
+ ${v} = finishUtf8(${v}, source)
261
+ `;
262
+ previous.push(v);
263
+ if (i == 1000000) // this just exists to prevent rollup from doing dead code elimination on finishUtf8
264
+ finishUtf8();
265
+ }
266
+ stringBuildCode += `return fromCharCode(${previous}) + readString(source)}`;
267
+ return stringBuildCode
268
+ }
269
+
270
+ let pendingSurrogate;
271
+ function finishUtf8(byte1, src) {
272
+ if ((byte1 & 0xe0) === 0xc0) {
273
+ // 2 bytes
274
+ const byte2 = src[position++] & 0x3f;
275
+ return ((byte1 & 0x1f) << 6) | byte2
276
+ } else if ((byte1 & 0xf0) === 0xe0) {
277
+ // 3 bytes
278
+ const byte2 = src[position++] & 0x3f;
279
+ const byte3 = src[position++] & 0x3f;
280
+ return ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3
281
+ } else if ((byte1 & 0xf8) === 0xf0) {
282
+ // 4 bytes
283
+ if (pendingSurrogate) {
284
+ byte1 = pendingSurrogate;
285
+ pendingSurrogate = null;
286
+ position += 3;
287
+ return byte1
288
+ }
289
+ const byte2 = src[position++] & 0x3f;
290
+ const byte3 = src[position++] & 0x3f;
291
+ const byte4 = src[position++] & 0x3f;
292
+ let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
293
+ if (unit > 0xffff) {
294
+ unit -= 0x10000;
295
+ unit = 0xdc00 | (unit & 0x3ff);
296
+ pendingSurrogate = ((unit >>> 10) & 0x3ff) | 0xd800;
297
+ position -= 4; // reset so we can return the next part of the surrogate pair
298
+ }
299
+ return unit
300
+ } else {
301
+ return byte1
302
+ }
303
+ }
304
+
305
+ const readString = eval(makeStringBuilder());
306
+
307
+ function compareKeys(a, b) {
308
+ // compare with type consistency that matches binary comparison
309
+ if (typeof a == 'object') {
310
+ if (!a) {
311
+ return b == null ? 0 : -1
312
+ }
313
+ if (a.compare) {
314
+ if (b == null) {
315
+ return 1
316
+ } else if (b.compare) {
317
+ return a.compare(b)
318
+ } else {
319
+ return -1
320
+ }
321
+ }
322
+ let arrayComparison;
323
+ if (b instanceof Array) {
324
+ let i = 0;
325
+ while((arrayComparison = compareKeys(a[i], b[i])) == 0 && i <= a.length) {
326
+ i++;
327
+ }
328
+ return arrayComparison
329
+ }
330
+ arrayComparison = compareKeys(a[0], b);
331
+ if (arrayComparison == 0 && a.length > 1)
332
+ return 1
333
+ return arrayComparison
334
+ } else if (typeof a == typeof b) {
335
+ if (typeof a === 'symbol') {
336
+ a = Symbol.keyFor(a);
337
+ b = Symbol.keyFor(b);
338
+ }
339
+ return a < b ? -1 : a === b ? 0 : 1
340
+ }
341
+ else if (typeof b == 'object') {
342
+ if (b instanceof Array)
343
+ return -compareKeys(b, a)
344
+ return 1
345
+ } else {
346
+ return typeOrder[typeof a] < typeOrder[typeof b] ? -1 : 1
347
+ }
348
+ }
349
+ const typeOrder = {
350
+ symbol: 0,
351
+ undefined: 1,
352
+ boolean: 2,
353
+ number: 3,
354
+ string: 4
355
+ };
356
+ const MINIMUM_KEY = null;
337
357
  const MAXIMUM_KEY = new Uint8Array([0xff]);
338
358
 
339
359
  exports.MAXIMUM_KEY = MAXIMUM_KEY;