ordered-binary 1.4.0 → 1.5.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.
@@ -0,0 +1 @@
1
+ {"parent":null,"pid":240712,"argv":["/home/kzyp/.nvm/versions/node/v20.0.0/bin/node","/home/kzyp/dev/ordered-binary/node_modules/mocha/bin/mocha","--ui","tdd","--reporter","/home/kzyp/.local/share/JetBrains/Toolbox/apps/CLion/ch-0/222.4167.35.plugins/NodeJS/js/mocha-intellij/lib/mochaIntellijReporter.js","/home/kzyp/dev/ordered-binary/tests/test.js"],"execArgv":[],"cwd":"/home/kzyp/dev/ordered-binary","time":1688411366364,"ppid":240701,"coverageFilename":"/home/kzyp/dev/ordered-binary/.nyc_output/5b6bd808-0399-4ee6-9395-344688761dfa.json","externalId":"","uuid":"5b6bd808-0399-4ee6-9395-344688761dfa","files":[]}
@@ -0,0 +1 @@
1
+ {"processes":{"5b6bd808-0399-4ee6-9395-344688761dfa":{"parent":null,"children":[]}},"files":{},"externalIds":{}}
package/dist/index.cjs CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ require('constants');
6
+
5
7
  /*
6
8
  control character types:
7
9
  1 - metadata
@@ -14,9 +16,6 @@ control character types:
14
16
  0 - multipart separator
15
17
  > 27 normal string characters
16
18
  */
17
- /*
18
- * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
19
- */
20
19
 
21
20
  const float64Array = new Float64Array(2);
22
21
  const int32Array = new Int32Array(float64Array.buffer, 0, 4);
@@ -69,7 +68,7 @@ function writeKey(key, target, position, inSequence) {
69
68
  }
70
69
  } else {
71
70
  if (target.utf8Write)
72
- position += target.utf8Write(key, position);
71
+ position += target.utf8Write(key, position, 0xffffffff);
73
72
  else
74
73
  position += textEncoder.encodeInto(key, target.subarray(position)).written;
75
74
  if (position > target.length - 4)
@@ -114,7 +113,7 @@ function writeKey(key, target, position, inSequence) {
114
113
  position += key.length;
115
114
  break
116
115
  } else {
117
- throw new Error('Unable to serialize object as a key')
116
+ throw new Error('Unable to serialize object as a key: ' + JSON.stringify(key))
118
117
  }
119
118
  } else // null
120
119
  target[position++] = 0;
@@ -123,7 +122,35 @@ function writeKey(key, target, position, inSequence) {
123
122
  targetView.setUint32(position++, key ? 7 : 6, true);
124
123
  return position
125
124
  case 'bigint':
126
- return writeKey(Number(key), target, position, inSequence)
125
+ let asFloat = Number(key);
126
+ if (BigInt(asFloat) > key) {
127
+ float64Array[0] = asFloat;
128
+ if (int32Array[0])
129
+ int32Array[0]--;
130
+ else {
131
+ int32Array[1]--;
132
+ int32Array[0] = 0xffffffff;
133
+ }
134
+ asFloat = float64Array[0];
135
+ }
136
+ let difference = key - BigInt(asFloat);
137
+ if (difference === 0n)
138
+ return writeKey(asFloat, target, position, inSequence)
139
+ writeKey(asFloat, target, position, inSequence);
140
+ position += 9; // always increment by 9 if we are adding fractional bits
141
+ let exponent = BigInt((int32Array[1] >> 20) - 1079);
142
+ let nextByte = difference >> exponent;
143
+ target[position - 1] |= Number(nextByte);
144
+ difference -= nextByte << exponent;
145
+ let first = true;
146
+ while (difference || first) {
147
+ first = false;
148
+ exponent -= 7n;
149
+ let nextByte = difference >> exponent;
150
+ target[position++] = Number(nextByte) | 0x80;
151
+ difference -= nextByte << exponent;
152
+ }
153
+ return position;
127
154
  case 'undefined':
128
155
  return position
129
156
  // undefined is interpreted as the absence of a key, signified by zero length
@@ -145,45 +172,57 @@ function readKey(buffer, start, end, inSequence) {
145
172
  let controlByte = buffer[position];
146
173
  let value;
147
174
  if (controlByte < 24) {
148
- if (controlByte < 8) {
149
- position++;
150
- if (controlByte == 6) {
151
- value = false;
152
- } else if (controlByte == 7) {
153
- value = true;
154
- } else if (controlByte == 0) {
155
- value = null;
156
- } else if (controlByte == 2) {
157
- value = Symbol.for(readString(buffer));
158
- } else
159
- return Uint8Array.prototype.slice.call(buffer, start, end)
160
- } else {
161
- let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2));
162
- let highInt = dataView.getInt32(position) << 4;
163
- let size = end - position;
164
- let lowInt;
165
- if (size > 4) {
166
- lowInt = dataView.getInt32(position + 4);
167
- highInt |= lowInt >>> 28;
168
- if (size <= 6) { // clear the last bits
169
- lowInt &= -0x1000;
175
+ if (controlByte < 8) {
176
+ position++;
177
+ if (controlByte == 6) {
178
+ value = false;
179
+ } else if (controlByte == 7) {
180
+ value = true;
181
+ } else if (controlByte == 0) {
182
+ value = null;
183
+ } else if (controlByte == 2) {
184
+ value = Symbol.for(readString(buffer));
185
+ } else
186
+ return Uint8Array.prototype.slice.call(buffer, start, end)
187
+ } else {
188
+ let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2));
189
+ let highInt = dataView.getInt32(position) << 4;
190
+ let size = end - position;
191
+ let lowInt;
192
+ if (size > 4) {
193
+ lowInt = dataView.getInt32(position + 4);
194
+ highInt |= lowInt >>> 28;
195
+ if (size <= 6) { // clear the last bits
196
+ lowInt &= -0x1000;
197
+ }
198
+ lowInt = lowInt << 4;
199
+ if (size > 8) {
200
+ lowInt = lowInt | buffer[position + 8] >> 4;
201
+ }
202
+ } else
203
+ lowInt = 0;
204
+ if (controlByte < 16) {
205
+ // negative gets negated
206
+ highInt = highInt ^ 0x7fffffff;
207
+ lowInt = ~lowInt;
170
208
  }
171
- lowInt = lowInt << 4;
172
- if (size > 8) {
173
- lowInt = lowInt | buffer[position + 8] >> 4;
209
+ int32Array[1] = highInt;
210
+ int32Array[0] = lowInt;
211
+ value = float64Array[0];
212
+ position += 9;
213
+ if (size > 9 && buffer[position] > 0) {
214
+ // convert the float to bigint, and then we will add precision as we enumerate through the
215
+ // extra bytes
216
+ value = BigInt(value);
217
+ let exponent = highInt >> 20;
218
+ // TODO: negatives?
219
+ let next_byte = buffer[position - 1] & 0xf;
220
+ value += BigInt(next_byte) << BigInt(exponent - 1079);
221
+ while ((next_byte = buffer[position]) > 0 && position++ < end) {
222
+ value += BigInt(next_byte & 0x7f) << BigInt((start - position) * 7 + exponent - 1016);
223
+ }
174
224
  }
175
- } else
176
- lowInt = 0;
177
- if (controlByte < 16) {
178
- // negative gets negated
179
- highInt = highInt ^ 0x7fffffff;
180
- lowInt = ~lowInt;
181
225
  }
182
- int32Array[1] = highInt;
183
- int32Array[0] = lowInt;
184
- value = float64Array[0];
185
- position += 9;
186
- }
187
226
  } else {
188
227
  if (controlByte == 27) {
189
228
  position++;
@@ -288,20 +327,19 @@ function finishUtf8(byte1, src) {
288
327
  } else if ((byte1 & 0xf8) === 0xf0) {
289
328
  // 4 bytes
290
329
  if (pendingSurrogate) {
291
- byte1 = pendingSurrogate;
292
- pendingSurrogate = null;
293
- position += 3;
294
- return byte1
330
+ byte1 = pendingSurrogate;
331
+ pendingSurrogate = null;
332
+ position += 3;
333
+ return byte1
295
334
  }
296
335
  const byte2 = src[position++] & 0x3f;
297
336
  const byte3 = src[position++] & 0x3f;
298
337
  const byte4 = src[position++] & 0x3f;
299
338
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
300
339
  if (unit > 0xffff) {
301
- unit -= 0x10000;
302
- unit = 0xdc00 | (unit & 0x3ff);
303
- pendingSurrogate = ((unit >>> 10) & 0x3ff) | 0xd800;
304
- position -= 4; // reset so we can return the next part of the surrogate pair
340
+ pendingSurrogate = 0xdc00 | (unit & 0x3ff);
341
+ unit = (((unit - 0x10000) >>> 10) & 0x3ff) | 0xd800;
342
+ position -= 4; // reset so we can return the next part of the surrogate pair
305
343
  }
306
344
  return unit
307
345
  } else {
@@ -309,7 +347,23 @@ function finishUtf8(byte1, src) {
309
347
  }
310
348
  }
311
349
 
312
- const readString = eval(makeStringBuilder());
350
+ const readString =
351
+ typeof process !== 'undefined' && process.isBun ? // the eval in bun doesn't properly closure on position, so we
352
+ // have to manually update it
353
+ (function(reading) {
354
+ let { setPosition, getPosition, readString } = reading;
355
+ return (source) => {
356
+ setPosition(position);
357
+ let value = readString(source);
358
+ position = getPosition();
359
+ return value;
360
+ };
361
+ })((new Function('fromCharCode', 'let position; let readString = ' + makeStringBuilder() +
362
+ ';return {' +
363
+ 'setPosition(p) { position = p },' +
364
+ 'getPosition() { return position },' +
365
+ 'readString }'))(fromCharCode)) :
366
+ eval(makeStringBuilder());
313
367
 
314
368
  function compareKeys(a, b) {
315
369
  // compare with type consistency that matches binary comparison
package/index.js CHANGED
@@ -14,6 +14,8 @@ control character types:
14
14
  * Convert arbitrary scalar values to buffer bytes with type preservation and type-appropriate ordering
15
15
  */
16
16
 
17
+ import exp from "constants"
18
+
17
19
  const float64Array = new Float64Array(2)
18
20
  const int32Array = new Int32Array(float64Array.buffer, 0, 4)
19
21
  let nullTerminate = false
@@ -65,7 +67,7 @@ export function writeKey(key, target, position, inSequence) {
65
67
  }
66
68
  } else {
67
69
  if (target.utf8Write)
68
- position += target.utf8Write(key, position)
70
+ position += target.utf8Write(key, position, 0xffffffff)
69
71
  else
70
72
  position += textEncoder.encodeInto(key, target.subarray(position)).written
71
73
  if (position > target.length - 4)
@@ -110,7 +112,7 @@ export function writeKey(key, target, position, inSequence) {
110
112
  position += key.length
111
113
  break
112
114
  } else {
113
- throw new Error('Unable to serialize object as a key')
115
+ throw new Error('Unable to serialize object as a key: ' + JSON.stringify(key))
114
116
  }
115
117
  } else // null
116
118
  target[position++] = 0
@@ -119,7 +121,35 @@ export function writeKey(key, target, position, inSequence) {
119
121
  targetView.setUint32(position++, key ? 7 : 6, true)
120
122
  return position
121
123
  case 'bigint':
122
- return writeKey(Number(key), target, position, inSequence)
124
+ let asFloat = Number(key)
125
+ if (BigInt(asFloat) > key) {
126
+ float64Array[0] = asFloat;
127
+ if (int32Array[0])
128
+ int32Array[0]--;
129
+ else {
130
+ int32Array[1]--;
131
+ int32Array[0] = 0xffffffff;
132
+ }
133
+ asFloat = float64Array[0];
134
+ }
135
+ let difference = key - BigInt(asFloat);
136
+ if (difference === 0n)
137
+ return writeKey(asFloat, target, position, inSequence)
138
+ writeKey(asFloat, target, position, inSequence)
139
+ position += 9; // always increment by 9 if we are adding fractional bits
140
+ let exponent = BigInt((int32Array[1] >> 20) - 1079);
141
+ let nextByte = difference >> exponent;
142
+ target[position - 1] |= Number(nextByte);
143
+ difference -= nextByte << exponent;
144
+ let first = true;
145
+ while (difference || first) {
146
+ first = false;
147
+ exponent -= 7n;
148
+ let nextByte = difference >> exponent;
149
+ target[position++] = Number(nextByte) | 0x80;
150
+ difference -= nextByte << exponent;
151
+ }
152
+ return position;
123
153
  case 'undefined':
124
154
  return position
125
155
  // undefined is interpreted as the absence of a key, signified by zero length
@@ -141,45 +171,57 @@ export function readKey(buffer, start, end, inSequence) {
141
171
  let controlByte = buffer[position]
142
172
  let value
143
173
  if (controlByte < 24) {
144
- if (controlByte < 8) {
145
- position++
146
- if (controlByte == 6) {
147
- value = false
148
- } else if (controlByte == 7) {
149
- value = true
150
- } else if (controlByte == 0) {
151
- value = null
152
- } else if (controlByte == 2) {
153
- value = Symbol.for(readString(buffer))
154
- } else
155
- return Uint8Array.prototype.slice.call(buffer, start, end)
156
- } else {
157
- let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2))
158
- let highInt = dataView.getInt32(position) << 4
159
- let size = end - position
160
- let lowInt
161
- if (size > 4) {
162
- lowInt = dataView.getInt32(position + 4)
163
- highInt |= lowInt >>> 28
164
- if (size <= 6) { // clear the last bits
165
- lowInt &= -0x1000
174
+ if (controlByte < 8) {
175
+ position++
176
+ if (controlByte == 6) {
177
+ value = false
178
+ } else if (controlByte == 7) {
179
+ value = true
180
+ } else if (controlByte == 0) {
181
+ value = null
182
+ } else if (controlByte == 2) {
183
+ value = Symbol.for(readString(buffer))
184
+ } else
185
+ return Uint8Array.prototype.slice.call(buffer, start, end)
186
+ } else {
187
+ let dataView = buffer.dataView || (buffer.dataView = new DataView(buffer.buffer, buffer.byteOffset, ((buffer.byteLength + 3) >> 2) << 2))
188
+ let highInt = dataView.getInt32(position) << 4
189
+ let size = end - position
190
+ let lowInt
191
+ if (size > 4) {
192
+ lowInt = dataView.getInt32(position + 4)
193
+ highInt |= lowInt >>> 28
194
+ if (size <= 6) { // clear the last bits
195
+ lowInt &= -0x1000
196
+ }
197
+ lowInt = lowInt << 4
198
+ if (size > 8) {
199
+ lowInt = lowInt | buffer[position + 8] >> 4
200
+ }
201
+ } else
202
+ lowInt = 0
203
+ if (controlByte < 16) {
204
+ // negative gets negated
205
+ highInt = highInt ^ 0x7fffffff
206
+ lowInt = ~lowInt
166
207
  }
167
- lowInt = lowInt << 4
168
- if (size > 8) {
169
- lowInt = lowInt | buffer[position + 8] >> 4
208
+ int32Array[1] = highInt
209
+ int32Array[0] = lowInt
210
+ value = float64Array[0]
211
+ position += 9
212
+ if (size > 9 && buffer[position] > 0) {
213
+ // convert the float to bigint, and then we will add precision as we enumerate through the
214
+ // extra bytes
215
+ value = BigInt(value);
216
+ let exponent = highInt >> 20;
217
+ // TODO: negatives?
218
+ let next_byte = buffer[position - 1] & 0xf;
219
+ value += BigInt(next_byte) << BigInt(exponent - 1079);
220
+ while ((next_byte = buffer[position]) > 0 && position++ < end) {
221
+ value += BigInt(next_byte & 0x7f) << BigInt((start - position) * 7 + exponent - 1016);
222
+ }
170
223
  }
171
- } else
172
- lowInt = 0
173
- if (controlByte < 16) {
174
- // negative gets negated
175
- highInt = highInt ^ 0x7fffffff
176
- lowInt = ~lowInt
177
224
  }
178
- int32Array[1] = highInt
179
- int32Array[0] = lowInt
180
- value = float64Array[0]
181
- position += 9
182
- }
183
225
  } else {
184
226
  if (controlByte == 27) {
185
227
  position++
@@ -284,20 +326,19 @@ function finishUtf8(byte1, src) {
284
326
  } else if ((byte1 & 0xf8) === 0xf0) {
285
327
  // 4 bytes
286
328
  if (pendingSurrogate) {
287
- byte1 = pendingSurrogate
288
- pendingSurrogate = null
289
- position += 3
290
- return byte1
329
+ byte1 = pendingSurrogate
330
+ pendingSurrogate = null
331
+ position += 3
332
+ return byte1
291
333
  }
292
334
  const byte2 = src[position++] & 0x3f
293
335
  const byte3 = src[position++] & 0x3f
294
336
  const byte4 = src[position++] & 0x3f
295
337
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4
296
338
  if (unit > 0xffff) {
297
- unit -= 0x10000
298
- unit = 0xdc00 | (unit & 0x3ff)
299
- pendingSurrogate = ((unit >>> 10) & 0x3ff) | 0xd800
300
- position -= 4 // reset so we can return the next part of the surrogate pair
339
+ pendingSurrogate = 0xdc00 | (unit & 0x3ff)
340
+ unit = (((unit - 0x10000) >>> 10) & 0x3ff) | 0xd800
341
+ position -= 4 // reset so we can return the next part of the surrogate pair
301
342
  }
302
343
  return unit
303
344
  } else {
@@ -305,7 +346,23 @@ function finishUtf8(byte1, src) {
305
346
  }
306
347
  }
307
348
 
308
- const readString = eval(makeStringBuilder())
349
+ const readString =
350
+ typeof process !== 'undefined' && process.isBun ? // the eval in bun doesn't properly closure on position, so we
351
+ // have to manually update it
352
+ (function(reading) {
353
+ let { setPosition, getPosition, readString } = reading;
354
+ return (source) => {
355
+ setPosition(position);
356
+ let value = readString(source);
357
+ position = getPosition();
358
+ return value;
359
+ };
360
+ })((new Function('fromCharCode', 'let position; let readString = ' + makeStringBuilder() +
361
+ ';return {' +
362
+ 'setPosition(p) { position = p },' +
363
+ 'getPosition() { return position },' +
364
+ 'readString }'))(fromCharCode)) :
365
+ eval(makeStringBuilder())
309
366
 
310
367
  export function compareKeys(a, b) {
311
368
  // compare with type consistency that matches binary comparison
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ordered-binary",
3
3
  "author": "Kris Zyp",
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "description": "Conversion of JavaScript primitives to and from Buffer with binary order matching natural primitive order",
6
6
  "license": "MIT",
7
7
  "repository": {
package/tests/test.js CHANGED
@@ -13,6 +13,12 @@ function assertBufferComparison(lesser, greater) {
13
13
  }
14
14
  }
15
15
  //var inspector = require('inspector'); inspector.open(9330, null, true); debugger
16
+ let seed = 0;
17
+ export function random() {
18
+ seed++;
19
+ let a = seed * 15485863;
20
+ return ((a * a * a) % 2038074743) / 2038074743;
21
+ }
16
22
 
17
23
  suite('key buffers', () => {
18
24
 
@@ -40,12 +46,35 @@ suite('key buffers', () => {
40
46
  assertBufferComparison(toBufferKey(-0.001), toBufferKey(-0.000001))
41
47
  assertBufferComparison(toBufferKey(-5236532532532), toBufferKey(-5236532532531))
42
48
  })
49
+ test('bigint equivalence', () => {
50
+ assert.strictEqual(fromBufferKey(toBufferKey(6135421331404949076605986n)), 6135421331404949076605986n)
51
+ assert.strictEqual(fromBufferKey(toBufferKey(0xfffffffffffffffffffffn)), 0xfffffffffffffffffffffn)
52
+ assert.strictEqual(fromBufferKey(toBufferKey(12345678901234567890n)), 12345678901234567890n)
53
+ assert.deepEqual(fromBufferKey(toBufferKey([12345678901234567890n, 44])), [12345678901234567890n, 44])
54
+ assert.deepEqual(fromBufferKey(toBufferKey(['hi', 12345678901234567890n])), ['hi', 12345678901234567890n])
55
+ assert.deepEqual(fromBufferKey(toBufferKey([6135421331404949076605986n, 'after'])), [6135421331404949076605986n, 'after'])
56
+ assert.strictEqual(fromBufferKey(toBufferKey(132923456789012345678903533235253252353211125n)), 132923456789012345678903533235253252353211125n)
57
+ assert.strictEqual(fromBufferKey(toBufferKey(352n)), 352)
58
+ let num = 5325n
59
+ for (let i = 0; i < 1100; i++) {
60
+ num *= BigInt(Math.floor(random() * 3 + 1));
61
+ num -= BigInt(Math.floor(random() * 1000));
62
+ assert.strictEqual(BigInt(fromBufferKey(toBufferKey(num))), num)
63
+ }
64
+ assert.strictEqual(fromBufferKey(toBufferKey(-352n)), -352)
65
+ })
66
+ test('bigint comparison', () => {
67
+ assertBufferComparison(toBufferKey(0xfffffffffffffffffffffn), toBufferKey(0x100fffffffffffffffffffn))
68
+ assertBufferComparison(toBufferKey(12345678901234567890), toBufferKey(12345678901234567890n))
69
+ })
70
+
43
71
  test('string equivalence', () => {
44
72
  assert.strictEqual(fromBufferKey(toBufferKey('4')), '4')
45
73
  assert.strictEqual(fromBufferKey(toBufferKey('hello')), 'hello')
46
74
  assert.strictEqual(fromBufferKey(toBufferKey('')), '')
47
75
  assert.strictEqual(fromBufferKey(toBufferKey('\x00')), '\x00')
48
76
  assert.strictEqual(fromBufferKey(toBufferKey('\x03test\x01\x00')), '\x03test\x01\x00')
77
+ assert.strictEqual(fromBufferKey(toBufferKey('prance 🧚🏻‍♀️🩷')), 'prance 🧚🏻‍♀️🩷')
49
78
  })
50
79
  test('string comparison', () => {
51
80
  assertBufferComparison(toBufferKey('4'), toBufferKey('5'))