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.
- package/README.md +20 -2
- package/index.js +133 -97
- package/lexint.js +114 -0
- package/package.json +2 -2
- 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.
|
|
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
|
|
1
|
+
const b4a = require('b4a')
|
|
2
2
|
|
|
3
|
-
const LE = (new Uint8Array(new Uint16Array([
|
|
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++] *
|
|
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++] *
|
|
80
|
-
state.buffer[state.start++] *
|
|
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++] *
|
|
100
|
-
state.buffer[state.start++] *
|
|
101
|
-
state.buffer[state.start++] *
|
|
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 /
|
|
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) +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
186
|
-
|
|
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.
|
|
233
|
+
state.end += b.byteLength
|
|
194
234
|
},
|
|
195
235
|
encode (state, b) {
|
|
196
236
|
state.buffer.set(b, state.start)
|
|
197
|
-
state.start += b.
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
|
|
257
|
+
const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)
|
|
225
258
|
|
|
226
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
285
|
-
|
|
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 >
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
+
})
|