msgpackr 1.6.2 → 1.7.0-alpha1
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.js +30 -6
- package/dist/index.min.js +45 -45
- package/dist/node.cjs +275 -6
- package/dist/test.js +2231 -49
- package/node-index.js +1 -0
- package/pack.js +27 -5
- package/package.json +83 -83
- package/struct.js +260 -0
- package/unpack.js +19 -2
package/node-index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Packr, Encoder, addExtension, pack, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './pack.js'
|
|
2
2
|
export { Unpackr, Decoder, C1, unpack, unpackMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './unpack.js'
|
|
3
|
+
import './struct.js'
|
|
3
4
|
export { PackrStream, UnpackrStream, PackrStream as EncoderStream, UnpackrStream as DecoderStream } from './stream.js'
|
|
4
5
|
export { decodeIter, encodeIter } from './iterators.js'
|
|
5
6
|
export const useRecords = false
|
package/pack.js
CHANGED
|
@@ -15,9 +15,10 @@ let targetView
|
|
|
15
15
|
let position = 0
|
|
16
16
|
let safeEnd
|
|
17
17
|
let bundledStrings = null
|
|
18
|
+
let writeStructSlots
|
|
18
19
|
const MAX_BUNDLE_SIZE = 0xf000
|
|
19
20
|
const hasNonLatin = /[\u0080-\uFFFF]/
|
|
20
|
-
const RECORD_SYMBOL = Symbol('record-id')
|
|
21
|
+
export const RECORD_SYMBOL = Symbol('record-id')
|
|
21
22
|
export class Packr extends Unpackr {
|
|
22
23
|
constructor(options) {
|
|
23
24
|
super(options)
|
|
@@ -67,14 +68,14 @@ export class Packr extends Unpackr {
|
|
|
67
68
|
this.pack = this.encode = function(value, encodeOptions) {
|
|
68
69
|
if (!target) {
|
|
69
70
|
target = new ByteArrayAllocate(8192)
|
|
70
|
-
targetView = new DataView(target.buffer, 0, 8192)
|
|
71
|
+
targetView = target.dataView = new DataView(target.buffer, 0, 8192)
|
|
71
72
|
position = 0
|
|
72
73
|
}
|
|
73
74
|
safeEnd = target.length - 10
|
|
74
75
|
if (safeEnd - position < 0x800) {
|
|
75
76
|
// don't start too close to the end,
|
|
76
77
|
target = new ByteArrayAllocate(target.length)
|
|
77
|
-
targetView = new DataView(target.buffer, 0, target.length)
|
|
78
|
+
targetView = target.dataView = new DataView(target.buffer, 0, target.length)
|
|
78
79
|
safeEnd = target.length - 10
|
|
79
80
|
position = 0
|
|
80
81
|
} else
|
|
@@ -122,7 +123,10 @@ export class Packr extends Unpackr {
|
|
|
122
123
|
if (hasSharedUpdate)
|
|
123
124
|
hasSharedUpdate = false
|
|
124
125
|
try {
|
|
125
|
-
|
|
126
|
+
if (packr.randomAccessStructure)
|
|
127
|
+
writeStruct(value);
|
|
128
|
+
else
|
|
129
|
+
pack(value)
|
|
126
130
|
if (bundledStrings) {
|
|
127
131
|
writeBundles(start, pack)
|
|
128
132
|
}
|
|
@@ -596,7 +600,7 @@ export class Packr extends Unpackr {
|
|
|
596
600
|
} else // faster handling for smaller buffers
|
|
597
601
|
newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
|
|
598
602
|
let newBuffer = new ByteArrayAllocate(newSize)
|
|
599
|
-
targetView = new DataView(newBuffer.buffer, 0, newSize)
|
|
603
|
+
targetView = newBuffer.dataView = new DataView(newBuffer.buffer, 0, newSize)
|
|
600
604
|
end = Math.min(end, target.length)
|
|
601
605
|
if (target.copy)
|
|
602
606
|
target.copy(newBuffer, 0, start, end)
|
|
@@ -687,6 +691,21 @@ export class Packr extends Unpackr {
|
|
|
687
691
|
target[insertionOffset + start] = keysTarget[0]
|
|
688
692
|
}
|
|
689
693
|
}
|
|
694
|
+
const writeStruct = (object, safePrototype) => {
|
|
695
|
+
let newPosition = writeStructSlots(object, target, position, structures, makeRoom, (value, newPosition) => {
|
|
696
|
+
position = newPosition;
|
|
697
|
+
if (start > 0) {
|
|
698
|
+
pack(value);
|
|
699
|
+
if (start == 0)
|
|
700
|
+
return { position, targetView }; // indicate the buffer was re-allocated
|
|
701
|
+
} else
|
|
702
|
+
pack(value);
|
|
703
|
+
return position;
|
|
704
|
+
})
|
|
705
|
+
if (newPosition === 0) // bail and go to a msgpack object
|
|
706
|
+
return writeObject(object, true);
|
|
707
|
+
position = newPosition;
|
|
708
|
+
}
|
|
690
709
|
}
|
|
691
710
|
useBuffer(buffer) {
|
|
692
711
|
// this means we are finished using our own buffer and we can write over it safely
|
|
@@ -923,6 +942,9 @@ export function addExtension(extension) {
|
|
|
923
942
|
}
|
|
924
943
|
unpackAddExtension(extension)
|
|
925
944
|
}
|
|
945
|
+
export function setWriteStructSlots(func) {
|
|
946
|
+
writeStructSlots = func;
|
|
947
|
+
}
|
|
926
948
|
|
|
927
949
|
let defaultPackr = new Packr({ useRecords: false })
|
|
928
950
|
export const pack = defaultPackr.pack
|
package/package.json
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "msgpackr",
|
|
3
|
-
"author": "Kris Zyp",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Ultra-fast MessagePack implementation with extensions for records and structured cloning",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"types": "./index.d.ts",
|
|
8
|
-
"main": "./dist/node.cjs",
|
|
9
|
-
"module": "./index.js",
|
|
10
|
-
"keywords": [
|
|
11
|
-
"MessagePack",
|
|
12
|
-
"msgpack",
|
|
13
|
-
"performance",
|
|
14
|
-
"structured",
|
|
15
|
-
"clone"
|
|
16
|
-
],
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "http://github.com/kriszyp/msgpackr"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"benchmark": "node ./tests/benchmark.cjs",
|
|
23
|
-
"build": "rollup -c",
|
|
24
|
-
"dry-run": "npm publish --dry-run",
|
|
25
|
-
"prepare": "npm run build",
|
|
26
|
-
"test": "mocha tests/test**.*js -u tdd --experimental-json-modules"
|
|
27
|
-
},
|
|
28
|
-
"type": "module",
|
|
29
|
-
"exports": {
|
|
30
|
-
".": {
|
|
31
|
-
"node": {
|
|
32
|
-
"require": "./dist/node.cjs",
|
|
33
|
-
"import": "./node-index.js"
|
|
34
|
-
},
|
|
35
|
-
"bun": {
|
|
36
|
-
"require": "./dist/node.cjs",
|
|
37
|
-
"import": "./node-index.js"
|
|
38
|
-
},
|
|
39
|
-
"default": "./index.js"
|
|
40
|
-
},
|
|
41
|
-
"./pack": {
|
|
42
|
-
"node": {
|
|
43
|
-
"import": "./index.js",
|
|
44
|
-
"require": "./dist/node.cjs"
|
|
45
|
-
},
|
|
46
|
-
"bun": {
|
|
47
|
-
"import": "./index.js",
|
|
48
|
-
"require": "./dist/node.cjs"
|
|
49
|
-
},
|
|
50
|
-
"default": "./pack.js"
|
|
51
|
-
},
|
|
52
|
-
"./unpack": {
|
|
53
|
-
"node": {
|
|
54
|
-
"import": "./index.js",
|
|
55
|
-
"require": "./dist/node.cjs"
|
|
56
|
-
},
|
|
57
|
-
"bun": {
|
|
58
|
-
"import": "./index.js",
|
|
59
|
-
"require": "./dist/node.cjs"
|
|
60
|
-
},
|
|
61
|
-
"default": "./unpack.js"
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
"files": [
|
|
65
|
-
"/dist",
|
|
66
|
-
"*.md",
|
|
67
|
-
"/*.js",
|
|
68
|
-
"/*.ts"
|
|
69
|
-
],
|
|
70
|
-
"optionalDependencies": {
|
|
71
|
-
"msgpackr-extract": "^2.
|
|
72
|
-
},
|
|
73
|
-
"devDependencies": {
|
|
74
|
-
"@rollup/plugin-json": "^4.1.0",
|
|
75
|
-
"@types/node": "latest",
|
|
76
|
-
"async": "^3",
|
|
77
|
-
"chai": "^4.3.4",
|
|
78
|
-
"esm": "^3.2.25",
|
|
79
|
-
"mocha": "^8.1.3",
|
|
80
|
-
"rollup": "^1.20.3",
|
|
81
|
-
"rollup-plugin-babel-minify": "^9.0.0"
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "msgpackr",
|
|
3
|
+
"author": "Kris Zyp",
|
|
4
|
+
"version": "1.7.0-alpha1",
|
|
5
|
+
"description": "Ultra-fast MessagePack implementation with extensions for records and structured cloning",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"types": "./index.d.ts",
|
|
8
|
+
"main": "./dist/node.cjs",
|
|
9
|
+
"module": "./index.js",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"MessagePack",
|
|
12
|
+
"msgpack",
|
|
13
|
+
"performance",
|
|
14
|
+
"structured",
|
|
15
|
+
"clone"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "http://github.com/kriszyp/msgpackr"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"benchmark": "node ./tests/benchmark.cjs",
|
|
23
|
+
"build": "rollup -c",
|
|
24
|
+
"dry-run": "npm publish --dry-run",
|
|
25
|
+
"prepare": "npm run build",
|
|
26
|
+
"test": "mocha tests/test**.*js -u tdd --experimental-json-modules"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"node": {
|
|
32
|
+
"require": "./dist/node.cjs",
|
|
33
|
+
"import": "./node-index.js"
|
|
34
|
+
},
|
|
35
|
+
"bun": {
|
|
36
|
+
"require": "./dist/node.cjs",
|
|
37
|
+
"import": "./node-index.js"
|
|
38
|
+
},
|
|
39
|
+
"default": "./index.js"
|
|
40
|
+
},
|
|
41
|
+
"./pack": {
|
|
42
|
+
"node": {
|
|
43
|
+
"import": "./index.js",
|
|
44
|
+
"require": "./dist/node.cjs"
|
|
45
|
+
},
|
|
46
|
+
"bun": {
|
|
47
|
+
"import": "./index.js",
|
|
48
|
+
"require": "./dist/node.cjs"
|
|
49
|
+
},
|
|
50
|
+
"default": "./pack.js"
|
|
51
|
+
},
|
|
52
|
+
"./unpack": {
|
|
53
|
+
"node": {
|
|
54
|
+
"import": "./index.js",
|
|
55
|
+
"require": "./dist/node.cjs"
|
|
56
|
+
},
|
|
57
|
+
"bun": {
|
|
58
|
+
"import": "./index.js",
|
|
59
|
+
"require": "./dist/node.cjs"
|
|
60
|
+
},
|
|
61
|
+
"default": "./unpack.js"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"files": [
|
|
65
|
+
"/dist",
|
|
66
|
+
"*.md",
|
|
67
|
+
"/*.js",
|
|
68
|
+
"/*.ts"
|
|
69
|
+
],
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"msgpackr-extract": "^2.1.1"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
75
|
+
"@types/node": "latest",
|
|
76
|
+
"async": "^3",
|
|
77
|
+
"chai": "^4.3.4",
|
|
78
|
+
"esm": "^3.2.25",
|
|
79
|
+
"mocha": "^8.1.3",
|
|
80
|
+
"rollup": "^1.20.3",
|
|
81
|
+
"rollup-plugin-babel-minify": "^9.0.0"
|
|
82
|
+
}
|
|
83
|
+
}
|
package/struct.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// first four bits
|
|
2
|
+
// 0000 - unsigned int
|
|
3
|
+
// 0010 - float32
|
|
4
|
+
// 0011 - float32
|
|
5
|
+
// 0100 - float32
|
|
6
|
+
// 0101 - float32
|
|
7
|
+
// 0110 - latin string reference
|
|
8
|
+
// 0111 - plain reference
|
|
9
|
+
// 1000 - structure reference
|
|
10
|
+
// 1001 - random access structure reference
|
|
11
|
+
// 1010 - float32
|
|
12
|
+
// 1011 - float32
|
|
13
|
+
// 1100 - float32
|
|
14
|
+
// 1101 - float32
|
|
15
|
+
// 1110 - constants and 3-byte strings
|
|
16
|
+
// 1111 - negative int
|
|
17
|
+
|
|
18
|
+
// first three bits
|
|
19
|
+
// 000 - unsigned int
|
|
20
|
+
// 001 - float32
|
|
21
|
+
// 010 - float32
|
|
22
|
+
// 011 - latin string reference
|
|
23
|
+
// 100 - reference
|
|
24
|
+
// 101 - float32
|
|
25
|
+
// 110 - float32
|
|
26
|
+
// 111 - constants and 3-byte strings
|
|
27
|
+
|
|
28
|
+
import { setWriteStructSlots, RECORD_SYMBOL } from './pack.js'
|
|
29
|
+
import { setReadStruct, unpack, mult10 } from './unpack.js';
|
|
30
|
+
const hasNonLatin = /[\u0080-\uFFFF]/;
|
|
31
|
+
const float32Headers = [false, true, true, false, false, true, true, false]
|
|
32
|
+
setWriteStructSlots(writeStruct);
|
|
33
|
+
function writeStruct(object, target, position, structures, makeRoom, pack) {
|
|
34
|
+
let transition = structures.transitions || false
|
|
35
|
+
let newTransitions = 0
|
|
36
|
+
let keyCount = 0;
|
|
37
|
+
let start = position;
|
|
38
|
+
position += 4;
|
|
39
|
+
let queuedReferences = [];
|
|
40
|
+
let uint32 = target.uint32 || (target.uint32 = new Uint32Array(target.buffer));
|
|
41
|
+
let targetView = target.dataView;
|
|
42
|
+
let encoded;
|
|
43
|
+
let stringData = '';
|
|
44
|
+
let safeEnd = target.length - 10;
|
|
45
|
+
for (let key in object) {
|
|
46
|
+
let nextTransition = transition[key]
|
|
47
|
+
if (!nextTransition) {
|
|
48
|
+
return 0; // bail
|
|
49
|
+
//nextTransition = transition[key] = Object.create(null)
|
|
50
|
+
//newTransitions++
|
|
51
|
+
}
|
|
52
|
+
if (position > safeEnd) {
|
|
53
|
+
let newPosition = position - start;
|
|
54
|
+
target = makeRoom(position)
|
|
55
|
+
position = newPosition;
|
|
56
|
+
start = 0
|
|
57
|
+
safeEnd = target.length - 10
|
|
58
|
+
}
|
|
59
|
+
transition = nextTransition
|
|
60
|
+
let value = object[key];
|
|
61
|
+
switch (typeof value) {
|
|
62
|
+
case 'number':
|
|
63
|
+
if (value >>> 0 === value && value < 0x20000000) {
|
|
64
|
+
encoded = value;
|
|
65
|
+
break;
|
|
66
|
+
} else if (value < 0x100000000 && value >= -0x80000000) {
|
|
67
|
+
targetView.setFloat32(position, value, true)
|
|
68
|
+
if (float32Headers[target[position + 3] >>> 5]) {
|
|
69
|
+
let xShifted
|
|
70
|
+
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
71
|
+
if (((xShifted = value * mult10[((target[position + 3] & 0x7f) << 1) | (target[position + 2] >> 7)]) >> 0) === xShifted) {
|
|
72
|
+
position += 4;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// fall back to msgpack encoding
|
|
78
|
+
queuedReferences.push(value, position - start);
|
|
79
|
+
position += 4;
|
|
80
|
+
continue;
|
|
81
|
+
case 'string':
|
|
82
|
+
if (hasNonLatin.test(value)) {
|
|
83
|
+
queuedReferences.push(value, position - start);
|
|
84
|
+
position += 4;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (value.length < 4) { // we can inline really small strings
|
|
88
|
+
encoded = 0xf8000000 + (value.length << 24) + (value.charCodeAt(0) << 16) + (value.charCodeAt(1) << 8) + (value.charCodeAt(2) || 0)
|
|
89
|
+
// TODO: determining remaining and make max value be a ratio of that (probably 1/256th)
|
|
90
|
+
} else if (value.length < 256 && stringData.length < 61440) {
|
|
91
|
+
// bundle these strings
|
|
92
|
+
encoded = 0x60000000 | (value.length << 16) | stringData.length;
|
|
93
|
+
stringData += value;
|
|
94
|
+
} else { // else queue it
|
|
95
|
+
queuedReferences.push(value, position - start);
|
|
96
|
+
position += 4;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case 'object':
|
|
101
|
+
if (value) {
|
|
102
|
+
queuedReferences.push(value, position - start);
|
|
103
|
+
position += 4;
|
|
104
|
+
continue;
|
|
105
|
+
} else { // null
|
|
106
|
+
encoded = 0xe0000000;
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
case 'boolean':
|
|
110
|
+
encoded = value ? 0xe3000000 : 0xe2000000;
|
|
111
|
+
break;
|
|
112
|
+
case 'undefined':
|
|
113
|
+
encoded = 0xe1000000;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
targetView.setUint32(position, encoded, true);
|
|
117
|
+
position += 4;
|
|
118
|
+
}
|
|
119
|
+
let recordId = transition[RECORD_SYMBOL]
|
|
120
|
+
if (!(recordId < 1024)) {
|
|
121
|
+
// for now just punt and go back to writeObject
|
|
122
|
+
return 0;
|
|
123
|
+
// newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions, true)
|
|
124
|
+
}
|
|
125
|
+
let stringLength = stringData.length;
|
|
126
|
+
if (stringData) {
|
|
127
|
+
if (position + stringLength > safeEnd) {
|
|
128
|
+
target = makeRoom(position + stringLength);
|
|
129
|
+
}
|
|
130
|
+
position += target.latin1Write(stringData, position, 0xffffffff);
|
|
131
|
+
}
|
|
132
|
+
target[start] = recordId >> 8;
|
|
133
|
+
target[start + 1] = recordId & 0xff;
|
|
134
|
+
target[start + 2] = stringLength >> 8;
|
|
135
|
+
target[start + 3] = stringLength & 0xff;
|
|
136
|
+
let queued32BitReferences;
|
|
137
|
+
for (let i = 0, l = queuedReferences.length; i < l;) {
|
|
138
|
+
let value = queuedReferences[i++];
|
|
139
|
+
let slotOffset = queuedReferences[i++] + start;
|
|
140
|
+
let offset = position - slotOffset;
|
|
141
|
+
if (offset < 0x1f000000) {
|
|
142
|
+
targetView.setUint32(slotOffset, 0x80000000 | (offset), true);
|
|
143
|
+
} else {
|
|
144
|
+
if (!queued32BitReferences)
|
|
145
|
+
queued32BitReferences = [];
|
|
146
|
+
queued32BitReferences.push({slotOffset, offset: position - start});
|
|
147
|
+
}
|
|
148
|
+
let newPosition = pack(value, position);
|
|
149
|
+
if (typeof newPosition === 'object') {
|
|
150
|
+
// re-allocated
|
|
151
|
+
position = newPosition.position;
|
|
152
|
+
targetView = newPosition.targetView;
|
|
153
|
+
start = 0;
|
|
154
|
+
} else
|
|
155
|
+
position = newPosition;
|
|
156
|
+
}
|
|
157
|
+
if (queued32BitReferences) {
|
|
158
|
+
// TODO: makeRoom
|
|
159
|
+
for (let i = 0, l = queued32BitReferences.length; i < l; i++) {
|
|
160
|
+
let ref = queued32BitReferences[i];
|
|
161
|
+
targetView.setUint32(ref.slotOffset, 0xa0000000 - ((l - i) << 2), true);
|
|
162
|
+
targetView.setUint32(position, ref.offset, true);
|
|
163
|
+
position += 4;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return position;
|
|
168
|
+
}
|
|
169
|
+
var sourceSymbol = Symbol('source')
|
|
170
|
+
function readStruct(src, position, srcEnd, structure, unpackr) {
|
|
171
|
+
var stringLength = (src[position++] << 8) | src[position++];
|
|
172
|
+
var construct = structure.construct;
|
|
173
|
+
var srcString;
|
|
174
|
+
if (!construct) {
|
|
175
|
+
construct = structure.construct = function() {
|
|
176
|
+
}
|
|
177
|
+
var prototype = construct.prototype;
|
|
178
|
+
Object.defineProperty(prototype, 'toJSON', {
|
|
179
|
+
get() {
|
|
180
|
+
// return an enumerable object with own properties to JSON stringify
|
|
181
|
+
let resolved = {};
|
|
182
|
+
for (let i = 0, l = structure.length; i < l; i++) {
|
|
183
|
+
let key = structure[i];
|
|
184
|
+
resolved[key] = this[key];
|
|
185
|
+
}
|
|
186
|
+
return resolved;
|
|
187
|
+
},
|
|
188
|
+
// not enumerable or anything
|
|
189
|
+
});
|
|
190
|
+
for (let i = 0, l = structure.length; i < l; i++) {
|
|
191
|
+
let key = structure[i];
|
|
192
|
+
Object.defineProperty(prototype, key, {
|
|
193
|
+
get() {
|
|
194
|
+
let source = this[sourceSymbol];
|
|
195
|
+
let src = source.src;
|
|
196
|
+
//let uint32 = src.uint32 || (src.uint32 = new Uint32Array(src.buffer, src.byteOffset, src.byteLength));
|
|
197
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
198
|
+
let position = source.position + (i << 2);
|
|
199
|
+
let value = dataView.getUint32(position, true);
|
|
200
|
+
let start;
|
|
201
|
+
switch (value >>> 29) {
|
|
202
|
+
case 0:
|
|
203
|
+
return value;
|
|
204
|
+
case 3:
|
|
205
|
+
if (value & 0x10000000) {
|
|
206
|
+
start = (value & 0xffff) + position;
|
|
207
|
+
return src.toString('utf8', start, start + ((value >> 16) & 0x7ff));
|
|
208
|
+
} else {
|
|
209
|
+
if (!srcString) {
|
|
210
|
+
start = source.position + (l << 2);
|
|
211
|
+
srcString = src.toString('latin1', start, start + stringLength);
|
|
212
|
+
}
|
|
213
|
+
start = value & 0xffff;
|
|
214
|
+
return srcString.slice(start, start + ((value >> 16) & 0x7ff));
|
|
215
|
+
}
|
|
216
|
+
case 4:
|
|
217
|
+
start = (0x1fffffff & value) + position;
|
|
218
|
+
let end = srcEnd;
|
|
219
|
+
for (let next = i + 1; next < l; next++) {
|
|
220
|
+
position = source.position + (next << 2);
|
|
221
|
+
let nextValue = dataView.getUint32(position, true);;
|
|
222
|
+
if ((nextValue & 0xe0000000) == -0x80000000) {
|
|
223
|
+
end = (0x1fffffff & nextValue) + position;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return unpackr.unpack(src.slice(start, end));
|
|
228
|
+
case 1: case 2: case 5: case 6:
|
|
229
|
+
let fValue = dataView.getFloat32(position, true);
|
|
230
|
+
// this does rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
231
|
+
let multiplier = mult10[((src[position + 3] & 0x7f) << 1) | (src[position + 2] >> 7)]
|
|
232
|
+
return ((multiplier * fValue + (fValue > 0 ? 0.5 : -0.5)) >> 0) / multiplier;
|
|
233
|
+
case 7:
|
|
234
|
+
switch((value >> 24) & 0x1f) {
|
|
235
|
+
case 0: return null;
|
|
236
|
+
case 1: return undefined;
|
|
237
|
+
case 2: return false;
|
|
238
|
+
case 3: return true;
|
|
239
|
+
case 8: return dataView.getFloat64(position + (value & 0x3ffffff), true);
|
|
240
|
+
case 0x18: return '';
|
|
241
|
+
case 0x19: return String.fromCharCode((value >> 16) & 0xff);
|
|
242
|
+
case 0x20: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff);
|
|
243
|
+
case 0x21: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff);
|
|
244
|
+
default: throw new Error('Unknown constant');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
enumerable: true,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
var instance = new construct();
|
|
253
|
+
instance[sourceSymbol] = {
|
|
254
|
+
src,
|
|
255
|
+
uint32: src.uint32,
|
|
256
|
+
position,
|
|
257
|
+
}
|
|
258
|
+
return instance;
|
|
259
|
+
}
|
|
260
|
+
setReadStruct(readStruct)
|
package/unpack.js
CHANGED
|
@@ -28,6 +28,7 @@ export const C1 = new C1Type()
|
|
|
28
28
|
C1.name = 'MessagePack 0xC1'
|
|
29
29
|
var sequentialMode = false
|
|
30
30
|
var inlineObjectReadThreshold = 2
|
|
31
|
+
var readStruct
|
|
31
32
|
try {
|
|
32
33
|
new Function('')
|
|
33
34
|
} catch(error) {
|
|
@@ -170,7 +171,13 @@ export function checkedRead() {
|
|
|
170
171
|
if (sharedLength < currentStructures.length)
|
|
171
172
|
currentStructures.length = sharedLength
|
|
172
173
|
}
|
|
173
|
-
let result
|
|
174
|
+
let result
|
|
175
|
+
if (currentUnpackr.randomAccessStructure && src[position] < 0x40 && readStruct) {
|
|
176
|
+
let id = (src[position++] << 8) + src[position++];
|
|
177
|
+
result = readStruct(src, position, srcEnd, currentStructures[id - 0x40] || loadStructures()[id - 0x40], currentUnpackr)
|
|
178
|
+
position = srcEnd
|
|
179
|
+
} else
|
|
180
|
+
result = read()
|
|
174
181
|
if (bundledStrings) // bundled strings to skip past
|
|
175
182
|
position = bundledStrings.postBundlePosition
|
|
176
183
|
|
|
@@ -247,6 +254,8 @@ export function read() {
|
|
|
247
254
|
for (let i = 0; i < token; i++) {
|
|
248
255
|
array[i] = read()
|
|
249
256
|
}
|
|
257
|
+
if (currentUnpackr.freezeData)
|
|
258
|
+
return Object.freeze(array)
|
|
250
259
|
return array
|
|
251
260
|
}
|
|
252
261
|
} else if (token < 0xc0) {
|
|
@@ -457,7 +466,8 @@ function createStructureReader(structure, firstId) {
|
|
|
457
466
|
function readObject() {
|
|
458
467
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
459
468
|
if (readObject.count++ > inlineObjectReadThreshold) {
|
|
460
|
-
let readObject = structure.read = (new Function('r', 'return function(){return
|
|
469
|
+
let readObject = structure.read = (new Function('r', 'return function(){return ' + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
|
|
470
|
+
'({' + structure.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '})}'))(read)
|
|
461
471
|
if (structure.highByte === 0)
|
|
462
472
|
structure.read = createSecondByteReader(firstId, structure.read)
|
|
463
473
|
return readObject() // second byte is already read, if there is one so immediately read object
|
|
@@ -467,6 +477,8 @@ function createStructureReader(structure, firstId) {
|
|
|
467
477
|
let key = structure[i]
|
|
468
478
|
object[key] = read()
|
|
469
479
|
}
|
|
480
|
+
if (currentUnpackr.freezeData)
|
|
481
|
+
return Object.freeze(object);
|
|
470
482
|
return object
|
|
471
483
|
}
|
|
472
484
|
readObject.count = 0
|
|
@@ -604,6 +616,8 @@ function readArray(length) {
|
|
|
604
616
|
for (let i = 0; i < length; i++) {
|
|
605
617
|
array[i] = read()
|
|
606
618
|
}
|
|
619
|
+
if (currentUnpackr.freezeData)
|
|
620
|
+
return Object.freeze(array)
|
|
607
621
|
return array
|
|
608
622
|
}
|
|
609
623
|
|
|
@@ -1059,3 +1073,6 @@ export function roundFloat32(float32Number) {
|
|
|
1059
1073
|
let multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)]
|
|
1060
1074
|
return ((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) / multiplier
|
|
1061
1075
|
}
|
|
1076
|
+
export function setReadStruct(func) {
|
|
1077
|
+
readStruct = func;
|
|
1078
|
+
}
|