ordered-binary 1.0.0 → 1.1.3

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