compact-encoding 2.5.1 → 2.7.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.
Files changed (5) hide show
  1. package/README.md +20 -2
  2. package/index.js +133 -97
  3. package/lexint.js +114 -0
  4. package/package.json +2 -2
  5. package/test.js +243 -3
package/README.md CHANGED
@@ -79,20 +79,38 @@ to build others on top. Feel free to PR more that are missing.
79
79
  * `cenc.uint16` - Encodes a fixed size uint16. Useful for things like ports.
80
80
  * `cenc.uint24` - Encodes a fixed size uint24. Useful for message framing.
81
81
  * `cenc.uint32` - Encodes a fixed size uint32. Useful for very large message framing.
82
+ * `cenc.uint40` - Encodes a fixed size uint40.
83
+ * `cenc.uint48` - Encodes a fixed size uint48.
84
+ * `cenc.uint56` - Encodes a fixed size uint56.
82
85
  * `cenc.uint64` - Encodes a fixed size uint64.
83
86
  * `cenc.int` - Encodes an int using `cenc.uint` with ZigZag encoding.
84
87
  * `cenc.int8` - Encodes a fixed size int8 using `cenc.uint8` with ZigZag encoding.
85
88
  * `cenc.int16` - Encodes a fixed size int16 using `cenc.uint16` with ZigZag encoding.
86
89
  * `cenc.int24` - Encodes a fixed size int24 using `cenc.uint24` with ZigZag encoding.
87
90
  * `cenc.int32` - Encodes a fixed size int32 using `cenc.uint32` with ZigZag encoding.
91
+ * `cenc.int40` - Encodes a fixed size int40 using `cenc.uint40` with ZigZag encoding.
92
+ * `cenc.int48` - Encodes a fixed size int48 using `cenc.uint48` with ZigZag encoding.
93
+ * `cenc.int56` - Encodes a fixed size int56 using `cenc.uint56` with ZigZag encoding.
88
94
  * `cenc.int64` - Encodes a fixed size int64 using `cenc.uint64` with ZigZag encoding.
95
+ * `cenc.lexint` - Encodes an int using [lexicographic-integer](https://github.com/substack/lexicographic-integer) encoding so that encoded values are lexicographically sorted in ascending numerical order.
89
96
  * `cenc.float32` - Encodes a fixed size float32.
90
97
  * `cenc.float64` - Encodes a fixed size float64.
91
98
  * `cenc.buffer` - Encodes a buffer with its length uint prefixed. When decoding an empty buffer, `null` is returned.
92
99
  * `cenc.raw` - Pass through encodes a buffer, i.e. a basic copy.
93
- * `cenc.uint32array` - Encodes a uint32array with its element length uint32 prefixed.
100
+ * `cenc.uint8array` - Encodes a uint8array with its element length uint prefixed.
101
+ * `cenc.uint16array` - Encodes a uint16array with its element length uint prefixed.
102
+ * `cenc.uint32array` - Encodes a uint32array with its element length uint prefixed.
103
+ * `cenc.int8array` - Encodes a int8array with its element length uint prefixed.
104
+ * `cenc.int16array` - Encodes a int16array with its element length uint prefixed.
105
+ * `cenc.int32array` - Encodes a int32array with its element length uint prefixed.
106
+ * `cenc.float32array` - Encodes a float32array with its element length uint prefixed.
107
+ * `cenc.float64array` - Encodes a float64array with its element length uint prefixed.
94
108
  * `cenc.bool` - Encodes a boolean as 1 or 0.
95
- * `cenc.string` - Encodes a utf-8 string, similar to buffer.
109
+ * `cenc.string`, `cenc.utf8` - Encodes a utf-8 string, similar to buffer.
110
+ * `cenc.ascii` - Encodes an ascii string.
111
+ * `cenc.hex` - Encodes a hex string.
112
+ * `cenc.base64` - Encodes a base64 string.
113
+ * `cenc.utf16le`, `cenc.ucs2` - Encodes a utf16le string.
96
114
  * `cenc.fixed32` - Encodes a fixed 32 byte buffer.
97
115
  * `cenc.fixed64` - Encodes a fixed 64 byte buffer.
98
116
  * `cenc.fixed(n)` - Makes a fixed sized encoder.
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
- const b = require('b4a')
1
+ const b4a = require('b4a')
2
2
 
3
- const LE = (new Uint8Array(new Uint16Array([255]).buffer))[0] === 0xff
3
+ const LE = (new Uint8Array(new Uint16Array([0xff]).buffer))[0] === 0xff
4
4
  const BE = !LE
5
5
 
6
6
  exports.state = function () {
@@ -58,7 +58,7 @@ const uint16 = exports.uint16 = {
58
58
  if (state.end - state.start < 2) throw new Error('Out of bounds')
59
59
  return (
60
60
  state.buffer[state.start++] +
61
- state.buffer[state.start++] * 256
61
+ state.buffer[state.start++] * 0x100
62
62
  )
63
63
  }
64
64
  }
@@ -76,8 +76,8 @@ const uint24 = exports.uint24 = {
76
76
  if (state.end - state.start < 3) throw new Error('Out of bounds')
77
77
  return (
78
78
  state.buffer[state.start++] +
79
- state.buffer[state.start++] * 256 +
80
- state.buffer[state.start++] * 65536
79
+ state.buffer[state.start++] * 0x100 +
80
+ state.buffer[state.start++] * 0x10000
81
81
  )
82
82
  }
83
83
  }
@@ -96,25 +96,70 @@ const uint32 = exports.uint32 = {
96
96
  if (state.end - state.start < 4) throw new Error('Out of bounds')
97
97
  return (
98
98
  state.buffer[state.start++] +
99
- state.buffer[state.start++] * 256 +
100
- state.buffer[state.start++] * 65536 +
101
- state.buffer[state.start++] * 16777216
99
+ state.buffer[state.start++] * 0x100 +
100
+ state.buffer[state.start++] * 0x10000 +
101
+ state.buffer[state.start++] * 0x1000000
102
102
  )
103
103
  }
104
104
  }
105
105
 
106
+ const uint40 = exports.uint40 = {
107
+ preencode (state, n) {
108
+ state.end += 5
109
+ },
110
+ encode (state, n) {
111
+ const r = Math.floor(n / 0x100)
112
+ uint8.encode(state, n)
113
+ uint32.encode(state, r)
114
+ },
115
+ decode (state) {
116
+ if (state.end - state.start < 5) throw new Error('Out of bounds')
117
+ return uint8.decode(state) + 0x100 * uint32.decode(state)
118
+ }
119
+ }
120
+
121
+ const uint48 = exports.uint48 = {
122
+ preencode (state, n) {
123
+ state.end += 6
124
+ },
125
+ encode (state, n) {
126
+ const r = Math.floor(n / 0x10000)
127
+ uint16.encode(state, n)
128
+ uint32.encode(state, r)
129
+ },
130
+ decode (state) {
131
+ if (state.end - state.start < 6) throw new Error('Out of bounds')
132
+ return uint16.decode(state) + 0x10000 * uint32.decode(state)
133
+ }
134
+ }
135
+
136
+ const uint56 = exports.uint56 = {
137
+ preencode (state, n) {
138
+ state.end += 7
139
+ },
140
+ encode (state, n) {
141
+ const r = Math.floor(n / 0x1000000)
142
+ uint24.encode(state, n)
143
+ uint32.encode(state, r)
144
+ },
145
+ decode (state) {
146
+ if (state.end - state.start < 7) throw new Error('Out of bounds')
147
+ return uint24.decode(state) + 0x1000000 * uint32.decode(state)
148
+ }
149
+ }
150
+
106
151
  const uint64 = exports.uint64 = {
107
152
  preencode (state, n) {
108
153
  state.end += 8
109
154
  },
110
155
  encode (state, n) {
111
- const r = Math.floor(n / 4294967296)
156
+ const r = Math.floor(n / 0x100000000)
112
157
  uint32.encode(state, n)
113
158
  uint32.encode(state, r)
114
159
  },
115
160
  decode (state) {
116
161
  if (state.end - state.start < 8) throw new Error('Out of bounds')
117
- return uint32.decode(state) + 4294967296 * uint32.decode(state)
162
+ return uint32.decode(state) + 0x100000000 * uint32.decode(state)
118
163
  }
119
164
  }
120
165
 
@@ -123,8 +168,13 @@ exports.int8 = zigZag(uint8)
123
168
  exports.int16 = zigZag(uint16)
124
169
  exports.int24 = zigZag(uint24)
125
170
  exports.int32 = zigZag(uint32)
171
+ exports.int40 = zigZag(uint40)
172
+ exports.int48 = zigZag(uint48)
173
+ exports.int56 = zigZag(uint56)
126
174
  exports.int64 = zigZag(uint64)
127
175
 
176
+ exports.lexint = require('./lexint')
177
+
128
178
  exports.float32 = {
129
179
  preencode (state, n) {
130
180
  state.end += 4
@@ -163,38 +213,28 @@ exports.float64 = {
163
213
 
164
214
  exports.buffer = {
165
215
  preencode (state, b) {
166
- if (b) {
167
- uint.preencode(state, b.length)
168
- state.end += b.length
169
- } else {
170
- state.end++
171
- }
216
+ if (b) uint8array.preencode(state, b)
217
+ else state.end++
172
218
  },
173
219
  encode (state, b) {
174
- if (b) {
175
- uint.encode(state, b.length)
176
- state.buffer.set(b, state.start)
177
- state.start += b.length
178
- } else {
179
- state.buffer[state.start++] = 0
180
- }
220
+ if (b) uint8array.encode(state, b)
221
+ else state.buffer[state.start++] = 0
181
222
  },
182
223
  decode (state) {
183
224
  const len = uint.decode(state)
184
225
  if (len === 0) return null
185
- const b = state.buffer.subarray(state.start, state.start += len)
186
- if (b.length !== len) throw new Error('Out of bounds')
187
- return b
226
+ if (state.end - state.start < len) throw new Error('Out of bounds')
227
+ return state.buffer.subarray(state.start, (state.start += len))
188
228
  }
189
229
  }
190
230
 
191
231
  const raw = exports.raw = {
192
232
  preencode (state, b) {
193
- state.end += b.length
233
+ state.end += b.byteLength
194
234
  },
195
235
  encode (state, b) {
196
236
  state.buffer.set(b, state.start)
197
- state.start += b.length
237
+ state.start += b.byteLength
198
238
  },
199
239
  decode (state) {
200
240
  const b = state.buffer.subarray(state.start, state.end)
@@ -203,61 +243,76 @@ const raw = exports.raw = {
203
243
  }
204
244
  }
205
245
 
206
- exports.uint32array = {
207
- preencode (state, b) {
208
- uint.preencode(state, b.length)
209
- state.end += b.byteLength
210
- },
211
- encode (state, b) {
212
- uint.encode(state, b.length)
213
- const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)
214
- if (BE) hostToLE32(view, b.length)
215
- state.buffer.set(view, state.start)
216
- state.start += b.byteLength
217
- },
218
- decode (state) {
219
- const len = uint.decode(state)
246
+ function typedarray (TypedArray, swap) {
247
+ const n = TypedArray.BYTES_PER_ELEMENT
220
248
 
221
- const byteOffset = state.buffer.byteOffset + state.start
222
- const s = state.start
249
+ return {
250
+ preencode (state, b) {
251
+ uint.preencode(state, b.length)
252
+ state.end += b.byteLength
253
+ },
254
+ encode (state, b) {
255
+ uint.encode(state, b.length)
223
256
 
224
- state.start += len * 4
257
+ const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)
225
258
 
226
- if ((byteOffset & 3) === 0) {
227
- const arr = new Uint32Array(state.buffer.buffer, byteOffset, len)
228
- if (BE) LEToHost32(arr, len)
229
- return arr
230
- }
259
+ if (BE && swap) swap(view)
231
260
 
232
- // align mismatch
233
- const copy = new Uint8Array(len * 4)
234
- const arr = new Uint32Array(copy.buffer, copy.byteOffset, len)
235
- copy.set(state.buffer.subarray(s, state.start), 0)
236
- if (BE) LEToHost32(arr, len)
237
- return arr
261
+ state.buffer.set(view, state.start)
262
+ state.start += b.byteLength
263
+ },
264
+ decode (state) {
265
+ const len = uint.decode(state)
266
+
267
+ let b = state.buffer.subarray(state.start, state.start += len * n)
268
+ if (b.byteLength !== len * n) throw new Error('Out of bounds')
269
+ if ((b.byteOffset % n) !== 0) b = new Uint8Array(b)
270
+
271
+ if (BE && swap) swap(b)
272
+
273
+ return new TypedArray(b.buffer, b.byteOffset, b.byteLength / n)
274
+ }
238
275
  }
239
276
  }
240
277
 
241
- exports.string = {
242
- preencode (state, s) {
243
- const len = b.byteLength(s)
244
- uint.preencode(state, len)
245
- state.end += len
246
- },
247
- encode (state, s) {
248
- const len = b.byteLength(s)
249
- uint.encode(state, len)
250
- b.write(state.buffer, s, state.start)
251
- state.start += len
252
- },
253
- decode (state) {
254
- const len = uint.decode(state)
255
- const s = b.toString(state.buffer, 'utf8', state.start, state.start += len)
256
- if (b.byteLength(s) !== len || state.start > state.end) throw new Error('Out of bounds')
257
- return s
278
+ const uint8array = exports.uint8array = typedarray(Uint8Array)
279
+ exports.uint16array = typedarray(Uint16Array, b4a.swap16)
280
+ exports.uint32array = typedarray(Uint32Array, b4a.swap32)
281
+
282
+ exports.int8array = typedarray(Int8Array)
283
+ exports.int16array = typedarray(Int16Array, b4a.swap16)
284
+ exports.int32array = typedarray(Int32Array, b4a.swap32)
285
+
286
+ exports.float32array = typedarray(Float32Array, b4a.swap32)
287
+ exports.float64array = typedarray(Float64Array, b4a.swap64)
288
+
289
+ function string (encoding) {
290
+ return {
291
+ preencode (state, s) {
292
+ const len = b4a.byteLength(s, encoding)
293
+ uint.preencode(state, len)
294
+ state.end += len
295
+ },
296
+ encode (state, s) {
297
+ const len = b4a.byteLength(s, encoding)
298
+ uint.encode(state, len)
299
+ b4a.write(state.buffer, s, state.start, encoding)
300
+ state.start += len
301
+ },
302
+ decode (state) {
303
+ const len = uint.decode(state)
304
+ if (state.end - state.start < len) throw new Error('Out of bounds')
305
+ return b4a.toString(state.buffer, encoding, state.start, (state.start += len))
306
+ }
258
307
  }
259
308
  }
260
309
 
310
+ exports.string = exports.utf8 = string('utf-8')
311
+ exports.ascii = string('ascii')
312
+ exports.hex = string('hex')
313
+ exports.base64 = string('base64')
314
+ exports.ucs2 = exports.utf16le = string('utf16le')
315
+
261
316
  exports.bool = {
262
317
  preencode (state, b) {
263
318
  state.end++
@@ -281,9 +336,8 @@ const fixed = exports.fixed = function fixed (n) {
281
336
  state.start += n
282
337
  },
283
338
  decode (state) {
284
- const b = state.buffer.subarray(state.start, state.start += n)
285
- if (b.length !== n) throw new Error('Out of bounds')
286
- return b
339
+ if (state.end - state.start < n) throw new Error('Out of bounds')
340
+ return state.buffer.subarray(state.start, (state.start += n))
287
341
  }
288
342
  }
289
343
  }
@@ -315,7 +369,7 @@ exports.array = function array (enc) {
315
369
  },
316
370
  decode (state) {
317
371
  const len = uint.decode(state)
318
- if (len > 1048576) throw new Error('Array is too big')
372
+ if (len > 0x100000) throw new Error('Array is too big')
319
373
  const arr = new Array(len)
320
374
  for (let i = 0; i < len; i++) arr[i] = enc.decode(state)
321
375
  return arr
@@ -337,7 +391,7 @@ function fromCodec (enc) {
337
391
  preencode (state, m) {
338
392
  tmpM = m
339
393
  tmpBuf = enc.encode(m)
340
- state.end += tmpBuf.length
394
+ state.end += tmpBuf.byteLength
341
395
  },
342
396
  encode (state, m) {
343
397
  raw.encode(state, m === tmpM ? tmpBuf : enc.encode(m))
@@ -369,7 +423,7 @@ function fromAbstractEncoder (enc) {
369
423
  exports.encode = function encode (enc, m) {
370
424
  const state = { start: 0, end: 0, buffer: null }
371
425
  enc.preencode(state, m)
372
- state.buffer = b.allocUnsafe(state.end)
426
+ state.buffer = b4a.allocUnsafe(state.end)
373
427
  enc.encode(state, m)
374
428
  return state.buffer
375
429
  }
@@ -378,24 +432,6 @@ exports.decode = function decode (enc, buffer) {
378
432
  return enc.decode({ start: 0, end: buffer.byteLength, buffer })
379
433
  }
380
434
 
381
- function LEToHost32 (arr, len) {
382
- const view = new DataView(arr.buffer, arr.byteOffset)
383
- const host = new Uint32Array(arr.buffer, arr.byteOffset, len)
384
-
385
- for (let i = 0; i < host.length; i++) {
386
- host[i] = view.getUint32(4 * i, BE)
387
- }
388
- }
389
-
390
- function hostToLE32 (arr, len) {
391
- const view = new DataView(arr.buffer, arr.byteOffset)
392
- const host = new Uint32Array(arr.buffer, arr.byteOffset, len)
393
-
394
- for (let i = 0; i < host.length; i++) {
395
- view.setUint32(4 * i, host[i], BE)
396
- }
397
- }
398
-
399
435
  function zigZag (enc) {
400
436
  return {
401
437
  preencode (state, n) {
package/lexint.js ADDED
@@ -0,0 +1,114 @@
1
+ module.exports = {
2
+ preencode,
3
+ encode,
4
+ decode
5
+ }
6
+
7
+ function preencode (state, num) {
8
+ if (num < 251) {
9
+ state.end++
10
+ } else if (num < 256) {
11
+ state.end += 2
12
+ } else if (num < 0x10000) {
13
+ state.end += 3
14
+ } else if (num < 0x1000000) {
15
+ state.end += 4
16
+ } else if (num < 0x100000000) {
17
+ state.end += 5
18
+ } else {
19
+ state.end++
20
+ const exp = Math.floor(Math.log(num) / Math.log(2)) - 32
21
+ preencode(state, exp)
22
+ state.end += 6
23
+ }
24
+ }
25
+
26
+ function encode (state, num) {
27
+ const max = 251
28
+ const x = num - max
29
+
30
+ if (num < max) {
31
+ state.buffer[state.start++] = num
32
+ } else if (num < 256) {
33
+ state.buffer[state.start++] = max
34
+ state.buffer[state.start++] = x
35
+ } else if (num < 0x10000) {
36
+ state.buffer[state.start++] = max + 1
37
+ state.buffer[state.start++] = x >> 8 & 0xff
38
+ state.buffer[state.start++] = x & 0xff
39
+ } else if (num < 0x1000000) {
40
+ state.buffer[state.start++] = max + 2
41
+ state.buffer[state.start++] = x >> 16
42
+ state.buffer[state.start++] = x >> 8 & 0xff
43
+ state.buffer[state.start++] = x & 0xff
44
+ } else if (num < 0x100000000) {
45
+ state.buffer[state.start++] = max + 3
46
+ state.buffer[state.start++] = x >> 24
47
+ state.buffer[state.start++] = x >> 16 & 0xff
48
+ state.buffer[state.start++] = x >> 8 & 0xff
49
+ state.buffer[state.start++] = x & 0xff
50
+ } else {
51
+ // need to use Math here as bitwise ops are 32 bit
52
+ const exp = Math.floor(Math.log(x) / Math.log(2)) - 32
53
+ state.buffer[state.start++] = 0xff
54
+
55
+ encode(state, exp)
56
+ const rem = x / Math.pow(2, exp - 11)
57
+
58
+ for (let i = 5; i >= 0; i--) {
59
+ state.buffer[state.start++] = rem / Math.pow(2, 8 * i) & 0xff
60
+ }
61
+ }
62
+ }
63
+
64
+ function decode (state) {
65
+ const max = 251
66
+
67
+ if (state.end - state.start < 1) throw new Error('Out of bounds')
68
+
69
+ const flag = state.buffer[state.start++]
70
+
71
+ if (flag < max) return flag
72
+
73
+ if (state.end - state.start < flag - max + 1) {
74
+ throw new Error('Out of bounds.')
75
+ }
76
+
77
+ if (flag < 252) {
78
+ return state.buffer[state.start++] +
79
+ max
80
+ }
81
+
82
+ if (flag < 253) {
83
+ return (state.buffer[state.start++] << 8) +
84
+ state.buffer[state.start++] +
85
+ max
86
+ }
87
+
88
+ if (flag < 254) {
89
+ return (state.buffer[state.start++] << 16) +
90
+ (state.buffer[state.start++] << 8) +
91
+ state.buffer[state.start++] +
92
+ max
93
+ }
94
+
95
+ // << 24 result may be interpreted as negative
96
+ if (flag < 255) {
97
+ return (state.buffer[state.start++] * 0x1000000) +
98
+ (state.buffer[state.start++] << 16) +
99
+ (state.buffer[state.start++] << 8) +
100
+ state.buffer[state.start++] +
101
+ max
102
+ }
103
+
104
+ const exp = decode(state)
105
+
106
+ if (state.end - state.start < 6) throw new Error('Out of bounds')
107
+
108
+ let rem = 0
109
+ for (let i = 5; i >= 0; i--) {
110
+ rem += state.buffer[state.start++] * Math.pow(2, 8 * i)
111
+ }
112
+
113
+ return (rem * Math.pow(2, exp - 11)) + max
114
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "compact-encoding",
3
- "version": "2.5.1",
3
+ "version": "2.7.0",
4
4
  "description": "A series of compact encoding schemes for building small and fast parsers and serializers",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
- "b4a": "^1.0.0"
7
+ "b4a": "^1.3.0"
8
8
  },
9
9
  "devDependencies": {
10
10
  "brittle": "^1.5.1",
package/test.js CHANGED
@@ -170,9 +170,6 @@ tape('buffer', function (t) {
170
170
  t.is(state.start, state.end)
171
171
 
172
172
  t.exception(() => enc.buffer.decode(state))
173
- state.buffer = state.buffer.subarray(0, 8)
174
- state.start = 3
175
- t.exception(() => enc.buffer.decode(state), 'partial throws')
176
173
  })
177
174
 
178
175
  tape('raw', function (t) {
@@ -190,6 +187,23 @@ tape('raw', function (t) {
190
187
  t.is(state.start, state.end)
191
188
  })
192
189
 
190
+ tape('uint16array', function (t) {
191
+ const state = enc.state()
192
+
193
+ enc.uint16array.preencode(state, new Uint16Array([1, 2, 3]))
194
+ t.alike(state, { start: 0, end: 7, buffer: null })
195
+
196
+ state.buffer = Buffer.alloc(state.end)
197
+ enc.uint16array.encode(state, new Uint16Array([1, 2, 3]))
198
+ t.alike(state, { start: 7, end: 7, buffer: Buffer.from([3, 1, 0, 2, 0, 3, 0]) })
199
+
200
+ state.start = 0
201
+ t.alike(enc.uint16array.decode(state), new Uint16Array([1, 2, 3]))
202
+ t.is(state.start, state.end)
203
+
204
+ t.exception(() => enc.uint16array.decode(state))
205
+ })
206
+
193
207
  tape('uint32array', function (t) {
194
208
  const state = enc.state()
195
209
 
@@ -212,6 +226,74 @@ tape('uint32array', function (t) {
212
226
  t.exception(() => enc.uint32array.decode(state))
213
227
  })
214
228
 
229
+ tape('int16array', function (t) {
230
+ const state = enc.state()
231
+
232
+ enc.int16array.preencode(state, new Int16Array([1, -2, 3]))
233
+ t.alike(state, { start: 0, end: 7, buffer: null })
234
+
235
+ state.buffer = Buffer.alloc(state.end)
236
+ enc.int16array.encode(state, new Int16Array([1, -2, 3]))
237
+ t.alike(state, { start: 7, end: 7, buffer: Buffer.from([3, 1, 0, 0xfe, 0xff, 3, 0]) })
238
+
239
+ state.start = 0
240
+ t.alike(enc.int16array.decode(state), new Int16Array([1, -2, 3]))
241
+ t.is(state.start, state.end)
242
+
243
+ t.exception(() => enc.int16array.decode(state))
244
+ })
245
+
246
+ tape('int32array', function (t) {
247
+ const state = enc.state()
248
+
249
+ enc.int32array.preencode(state, new Int32Array([1, -2, 3]))
250
+ t.alike(state, { start: 0, end: 13, buffer: null })
251
+
252
+ state.buffer = Buffer.alloc(state.end)
253
+ enc.int32array.encode(state, new Int32Array([1, -2, 3]))
254
+ t.alike(state, { start: 13, end: 13, buffer: Buffer.from([3, 1, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, 3, 0, 0, 0]) })
255
+
256
+ state.start = 0
257
+ t.alike(enc.int32array.decode(state), new Int32Array([1, -2, 3]))
258
+ t.is(state.start, state.end)
259
+
260
+ t.exception(() => enc.int32array.decode(state))
261
+ })
262
+
263
+ tape('float32array', function (t) {
264
+ const state = enc.state()
265
+
266
+ enc.float32array.preencode(state, new Float32Array([1.1, -2.2, 3.3]))
267
+ t.alike(state, { start: 0, end: 13, buffer: null })
268
+
269
+ state.buffer = Buffer.alloc(state.end)
270
+ enc.float32array.encode(state, new Float32Array([1.1, -2.2, 3.3]))
271
+ t.alike(state, { start: 13, end: 13, buffer: Buffer.from([3, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0x0c, 0xc0, 0x33, 0x33, 0x53, 0x40]) })
272
+
273
+ state.start = 0
274
+ t.alike(enc.float32array.decode(state), new Float32Array([1.1, -2.2, 3.3]))
275
+ t.is(state.start, state.end)
276
+
277
+ t.exception(() => enc.float32array.decode(state))
278
+ })
279
+
280
+ tape('float64array', function (t) {
281
+ const state = enc.state()
282
+
283
+ enc.float64array.preencode(state, new Float64Array([1.1, -2.2, 3.3]))
284
+ t.alike(state, { start: 0, end: 25, buffer: null })
285
+
286
+ state.buffer = Buffer.alloc(state.end)
287
+ enc.float64array.encode(state, new Float64Array([1.1, -2.2, 3.3]))
288
+ t.alike(state, { start: 25, end: 25, buffer: Buffer.from([3, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0x01, 0xc0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0a, 0x40]) })
289
+
290
+ state.start = 0
291
+ t.alike(enc.float64array.decode(state), new Float64Array([1.1, -2.2, 3.3]))
292
+ t.is(state.start, state.end)
293
+
294
+ t.exception(() => enc.float64array.decode(state))
295
+ })
296
+
215
297
  tape('string', function (t) {
216
298
  const state = enc.state()
217
299
 
@@ -325,3 +407,161 @@ tape('array', function (t) {
325
407
 
326
408
  t.exception(() => arr.decode(state))
327
409
  })
410
+
411
+ tape('lexint: big numbers', function (t) {
412
+ t.plan(1)
413
+
414
+ let prev = enc.encode(enc.lexint, 0)
415
+
416
+ let n
417
+ let skip = 1
418
+
419
+ for (n = 1; n < Number.MAX_VALUE; n += skip) {
420
+ const cur = enc.encode(enc.lexint, n)
421
+ if (Buffer.compare(cur, prev) < 1) break
422
+ prev = cur
423
+ skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256)))
424
+ }
425
+ t.is(n, Infinity)
426
+ })
427
+
428
+ tape('lexint: range precision', function (t) {
429
+ t.plan(2)
430
+ const a = 1e55
431
+ const b = 1.0000000000001e55
432
+ const ha = enc.encode(enc.lexint, a).toString('hex')
433
+ const hb = enc.encode(enc.lexint, b).toString('hex')
434
+ t.not(a, b)
435
+ t.not(ha, hb)
436
+ })
437
+
438
+ tape('lexint: range precision', function (t) {
439
+ let prev = enc.encode(enc.lexint, 0)
440
+ const skip = 0.000000001e55
441
+ for (let i = 0, n = 1e55; i < 1000; n = 1e55 + skip * ++i) {
442
+ const cur = enc.encode(enc.lexint, n)
443
+ if (Buffer.compare(cur, prev) < 1) t.fail('cur <= prev')
444
+ prev = cur
445
+ }
446
+ t.ok(true)
447
+ t.end()
448
+ })
449
+
450
+ tape('lexint: small numbers', function (t) {
451
+ let prev = enc.encode(enc.lexint, 0)
452
+ for (let n = 1; n < 256 * 256 * 16; n++) {
453
+ const cur = enc.encode(enc.lexint, n)
454
+ if (Buffer.compare(cur, prev) < 1) t.fail('cur <= prev')
455
+ prev = cur
456
+ }
457
+ t.ok(true)
458
+ t.end()
459
+ })
460
+
461
+ tape('lexint: throws', function (t) {
462
+ t.exception(() => {
463
+ enc.decode(enc.lexint, Buffer.alloc(1, 251))
464
+ })
465
+
466
+ let num = 252
467
+
468
+ const state = {
469
+ start: 0,
470
+ end: 0,
471
+ buffer: null
472
+ }
473
+
474
+ enc.lexint.preencode(state, num)
475
+ state.buffer = Buffer.alloc(state.end - state.start)
476
+ enc.lexint.encode(state, num)
477
+
478
+ t.exception(() => {
479
+ enc.decode(enc.lexint, state.buffer.subarray(0, state.buffer.byteLength - 2))
480
+ })
481
+
482
+ num <<= 8
483
+
484
+ state.start = 0
485
+ state.end = 0
486
+ state.buffer = null
487
+
488
+ enc.lexint.preencode(state, num)
489
+ state.buffer = Buffer.alloc(state.end - state.start)
490
+ enc.lexint.encode(state, num)
491
+
492
+ t.exception(() => {
493
+ enc.decode(enc.lexint, state.buffer.subarray(0, state.buffer.byteLength - 2))
494
+ })
495
+
496
+ num <<= 8
497
+
498
+ state.start = 0
499
+ state.end = 0
500
+ state.buffer = null
501
+
502
+ enc.lexint.preencode(state, num)
503
+ state.buffer = Buffer.alloc(state.end - state.start)
504
+ enc.lexint.encode(state, num)
505
+
506
+ t.exception(() => {
507
+ enc.decode(enc.lexint, state.buffer.subarray(0, state.buffer.byteLength - 2))
508
+ })
509
+
510
+ num *= 256
511
+
512
+ state.start = 0
513
+ state.end = 0
514
+ state.buffer = null
515
+
516
+ enc.lexint.preencode(state, num)
517
+ state.buffer = Buffer.alloc(state.end - state.start)
518
+ enc.lexint.encode(state, num)
519
+
520
+ t.exception(() => {
521
+ enc.decode(enc.lexint, state.buffer.subarray(0, state.buffer.byteLength - 2))
522
+ })
523
+
524
+ num *= 256 * 256
525
+
526
+ state.start = 0
527
+ state.end = 0
528
+ state.buffer = null
529
+
530
+ enc.lexint.preencode(state, num)
531
+ state.buffer = Buffer.alloc(state.end - state.start)
532
+ enc.lexint.encode(state, num)
533
+
534
+ t.exception(() => {
535
+ enc.decode(enc.lexint, state.buffer.subarray(0, state.buffer.byteLength - 2))
536
+ })
537
+
538
+ t.end()
539
+ })
540
+
541
+ tape('lexint: unpack', function (t) {
542
+ let n
543
+ let skip = 1
544
+
545
+ for (n = 1; n < Number.MAX_VALUE; n += skip) {
546
+ const cur = enc.encode(enc.lexint, n)
547
+ compare(n, enc.decode(enc.lexint, cur))
548
+ skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256)))
549
+ }
550
+ t.is(n, Infinity)
551
+ t.end()
552
+
553
+ function compare (a, b) {
554
+ const desc = a + ' !=~ ' + b
555
+ if (/e\+\d+$/.test(a) || /e\+\d+$/.test(b)) {
556
+ if (String(a).slice(0, 8) !== String(b).slice(0, 8) ||
557
+ /e\+(\d+)$/.exec(a)[1] !== /e\+(\d+)$/.exec(b)[1]) {
558
+ t.fail(desc)
559
+ }
560
+ } else {
561
+ if (String(a).slice(0, 8) !== String(b).slice(0, 8) ||
562
+ String(a).length !== String(b).length) {
563
+ t.fail(desc)
564
+ }
565
+ }
566
+ }
567
+ })