zkjson 0.3.1 → 0.4.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/collection.js +2 -2
- package/db.js +4 -3
- package/doc.js +2 -2
- package/encoder-v2.js +1221 -0
- package/encoder.js +1 -1
- package/package.json +1 -1
package/collection.js
CHANGED
@@ -14,8 +14,8 @@ class Collection {
|
|
14
14
|
size_json: this.size_json,
|
15
15
|
})
|
16
16
|
}
|
17
|
-
async getInputs({ id, json, path, val }) {
|
18
|
-
const doc_inputs = await this.doc.getInputs({ json, path, val })
|
17
|
+
async getInputs({ id, json, path, val, query }) {
|
18
|
+
const doc_inputs = await this.doc.getInputs({ json, path, val, query })
|
19
19
|
const res = await this.get(id)
|
20
20
|
let siblings = res.siblings
|
21
21
|
for (let i = 0; i < siblings.length; i++)
|
package/db.js
CHANGED
@@ -69,13 +69,14 @@ class DB {
|
|
69
69
|
return this._getVal(j, p.split("."))
|
70
70
|
}
|
71
71
|
|
72
|
-
async genProof({ json, col_id, path, id }) {
|
72
|
+
async genProof({ json, col_id, path, id, query }) {
|
73
73
|
const inputs = await this.getInputs({
|
74
74
|
id,
|
75
75
|
col_id,
|
76
76
|
json,
|
77
77
|
path,
|
78
78
|
val: this.getVal(json, path),
|
79
|
+
query,
|
79
80
|
})
|
80
81
|
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
81
82
|
inputs,
|
@@ -254,7 +255,7 @@ class DB {
|
|
254
255
|
}
|
255
256
|
}
|
256
257
|
|
257
|
-
async getInputs({ id, col_id, json, path, val }) {
|
258
|
+
async getInputs({ id, col_id, json, path, val, query }) {
|
258
259
|
const col_root = this.tree.F.toObject(this.tree.root).toString()
|
259
260
|
const col_res = await this.getCol(col_id)
|
260
261
|
|
@@ -265,7 +266,7 @@ class DB {
|
|
265
266
|
col_siblings = col_siblings.map(s => s.toString())
|
266
267
|
const col_key = col_id
|
267
268
|
const col = this.getColTree(col_id)
|
268
|
-
const col_inputs = await col.getInputs({ id, json, path, val })
|
269
|
+
const col_inputs = await col.getInputs({ id, json, path, val, query })
|
269
270
|
return {
|
270
271
|
path: col_inputs.path,
|
271
272
|
val: col_inputs.val,
|
package/doc.js
CHANGED
@@ -46,8 +46,8 @@ module.exports = class Doc {
|
|
46
46
|
if (p === "") return j
|
47
47
|
return this._getVal(j, p.split("."))
|
48
48
|
}
|
49
|
-
async genProof(json, path) {
|
50
|
-
const inputs = await this.getInputs(json, path)
|
49
|
+
async genProof({ json, path, query }) {
|
50
|
+
const inputs = await this.getInputs({ json, path, query })
|
51
51
|
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
52
52
|
inputs,
|
53
53
|
this.wasm,
|
package/encoder-v2.js
ADDED
@@ -0,0 +1,1221 @@
|
|
1
|
+
const {
|
2
|
+
clone,
|
3
|
+
uniq,
|
4
|
+
sortBy,
|
5
|
+
map,
|
6
|
+
concat,
|
7
|
+
compose,
|
8
|
+
is,
|
9
|
+
descend,
|
10
|
+
ascend,
|
11
|
+
sortWith,
|
12
|
+
prop,
|
13
|
+
equals,
|
14
|
+
append,
|
15
|
+
isNil,
|
16
|
+
includes,
|
17
|
+
splitEvery,
|
18
|
+
flatten,
|
19
|
+
} = require("ramda")
|
20
|
+
const ops = {
|
21
|
+
$eq: 10,
|
22
|
+
$ne: 11,
|
23
|
+
$gt: 12,
|
24
|
+
$gte: 13,
|
25
|
+
$lt: 14,
|
26
|
+
$lte: 15,
|
27
|
+
$in: 16,
|
28
|
+
$nin: 17,
|
29
|
+
$contains: 18,
|
30
|
+
$contains_any: 19,
|
31
|
+
$contains_all: 20,
|
32
|
+
$contains_none: 21,
|
33
|
+
}
|
34
|
+
const opMap = {}
|
35
|
+
for (let k in ops) opMap[ops[k]] = k
|
36
|
+
|
37
|
+
const base64Map = {
|
38
|
+
A: "00",
|
39
|
+
B: "01",
|
40
|
+
C: "02",
|
41
|
+
D: "03",
|
42
|
+
E: "04",
|
43
|
+
F: "05",
|
44
|
+
G: "06",
|
45
|
+
H: "07",
|
46
|
+
I: "08",
|
47
|
+
J: "09",
|
48
|
+
K: "10",
|
49
|
+
L: "11",
|
50
|
+
M: "12",
|
51
|
+
N: "13",
|
52
|
+
O: "14",
|
53
|
+
P: "15",
|
54
|
+
Q: "16",
|
55
|
+
R: "17",
|
56
|
+
S: "18",
|
57
|
+
T: "19",
|
58
|
+
U: "20",
|
59
|
+
V: "21",
|
60
|
+
W: "22",
|
61
|
+
X: "23",
|
62
|
+
Y: "24",
|
63
|
+
Z: "25",
|
64
|
+
a: "26",
|
65
|
+
b: "27",
|
66
|
+
c: "28",
|
67
|
+
d: "29",
|
68
|
+
e: "30",
|
69
|
+
f: "31",
|
70
|
+
g: "32",
|
71
|
+
h: "33",
|
72
|
+
i: "34",
|
73
|
+
j: "35",
|
74
|
+
k: "36",
|
75
|
+
l: "37",
|
76
|
+
m: "38",
|
77
|
+
n: "39",
|
78
|
+
o: "40",
|
79
|
+
p: "41",
|
80
|
+
q: "42",
|
81
|
+
r: "43",
|
82
|
+
s: "44",
|
83
|
+
t: "45",
|
84
|
+
u: "46",
|
85
|
+
v: "47",
|
86
|
+
w: "48",
|
87
|
+
x: "49",
|
88
|
+
y: "50",
|
89
|
+
z: "51",
|
90
|
+
0: "52",
|
91
|
+
1: "53",
|
92
|
+
2: "54",
|
93
|
+
3: "55",
|
94
|
+
4: "56",
|
95
|
+
5: "57",
|
96
|
+
6: "58",
|
97
|
+
7: "59",
|
98
|
+
8: "60",
|
99
|
+
9: "61",
|
100
|
+
"-": "62",
|
101
|
+
_: "63",
|
102
|
+
}
|
103
|
+
|
104
|
+
let strMap = {}
|
105
|
+
for (const k in base64Map) strMap[base64Map[k]] = k
|
106
|
+
|
107
|
+
function pad(arr, max = 0) {
|
108
|
+
arr = arr.map(n => n.toString())
|
109
|
+
for (let i = arr.length; i < max; i++) {
|
110
|
+
arr.push("0")
|
111
|
+
}
|
112
|
+
return arr
|
113
|
+
}
|
114
|
+
|
115
|
+
function encodePath(path) {
|
116
|
+
const parts = []
|
117
|
+
let str = ""
|
118
|
+
let num = 0
|
119
|
+
for (const s of path) {
|
120
|
+
if (num == 2 && !(s == "." || s == "[")) throw Error()
|
121
|
+
if (s == ".") {
|
122
|
+
if (num == 2) {
|
123
|
+
num = 0
|
124
|
+
} else {
|
125
|
+
parts.push(str)
|
126
|
+
str = ""
|
127
|
+
}
|
128
|
+
} else if (s == "[") {
|
129
|
+
if (num != 2) {
|
130
|
+
if (str != "" || parts.length > 0) parts.push(str)
|
131
|
+
str = ""
|
132
|
+
}
|
133
|
+
num = 1
|
134
|
+
} else if (s == "]") {
|
135
|
+
if (num != 1) throw Error()
|
136
|
+
num = 2
|
137
|
+
if (str == "" || Number.isNaN(+str)) throw Error()
|
138
|
+
parts.push(+str)
|
139
|
+
str = ""
|
140
|
+
} else {
|
141
|
+
str += s
|
142
|
+
}
|
143
|
+
}
|
144
|
+
if (str != "") parts.push(str)
|
145
|
+
if (parts.length == 0) parts.push("")
|
146
|
+
let encoded = [parts.length]
|
147
|
+
for (const p of parts) {
|
148
|
+
if (typeof p == "number") {
|
149
|
+
encoded = encoded.concat([0, 0, p])
|
150
|
+
} else {
|
151
|
+
let plen = [p.length]
|
152
|
+
if (p.length == 0) plen.push(1)
|
153
|
+
encoded = encoded.concat([
|
154
|
+
...plen,
|
155
|
+
...p.split("").map(c => c.charCodeAt(0)),
|
156
|
+
])
|
157
|
+
}
|
158
|
+
}
|
159
|
+
return encoded
|
160
|
+
}
|
161
|
+
|
162
|
+
function decodePath(path) {
|
163
|
+
let str = ""
|
164
|
+
let p = []
|
165
|
+
let len = path.shift()
|
166
|
+
while (path.length > 0) {
|
167
|
+
const type = path.shift()
|
168
|
+
let val = null
|
169
|
+
if (type == 0) {
|
170
|
+
const type2 = path.shift()
|
171
|
+
if (type2 == 0) {
|
172
|
+
val = [type2, path.shift()]
|
173
|
+
} else {
|
174
|
+
val = [type2]
|
175
|
+
}
|
176
|
+
} else {
|
177
|
+
val = []
|
178
|
+
for (let i = 0; i < type; i++) {
|
179
|
+
val.push(path.shift())
|
180
|
+
}
|
181
|
+
}
|
182
|
+
p.push([type, ...val])
|
183
|
+
}
|
184
|
+
let i = 0
|
185
|
+
for (let s of p) {
|
186
|
+
if (s[0] == 0 && s[1] == 0) str += `[${s[2]}]`
|
187
|
+
else if (s[0] == 0 && s[1] == 1) {
|
188
|
+
if (str != "") str += "."
|
189
|
+
} else {
|
190
|
+
str += `${i == 0 ? "" : "."}${s
|
191
|
+
.slice(1)
|
192
|
+
.map(c => String.fromCharCode(Number(c)))
|
193
|
+
.join("")}`
|
194
|
+
}
|
195
|
+
i++
|
196
|
+
}
|
197
|
+
return str
|
198
|
+
}
|
199
|
+
|
200
|
+
function flattenPath(path) {
|
201
|
+
let p = [path.length]
|
202
|
+
for (const v of path) p = p.concat(v)
|
203
|
+
return p
|
204
|
+
}
|
205
|
+
|
206
|
+
function _encode(v, path = []) {
|
207
|
+
let vals = []
|
208
|
+
if (typeof v == "number") vals.push([path, encodeVal(v)])
|
209
|
+
else if (typeof v == "boolean") vals.push([path, encodeVal(v)])
|
210
|
+
else if (v == null) vals.push([path, encodeVal(v)])
|
211
|
+
else if (typeof v == "string") vals.push([path, encodeVal(v)])
|
212
|
+
else if (Array.isArray(v)) {
|
213
|
+
let i = 0
|
214
|
+
for (const v2 of v) {
|
215
|
+
for (const v3 of _encode(v2, [...path, i])) vals.push(v3)
|
216
|
+
i++
|
217
|
+
}
|
218
|
+
} else if (typeof v == "object") {
|
219
|
+
for (const k in v) for (let v4 of _encode(v[k], [...path, k])) vals.push(v4)
|
220
|
+
}
|
221
|
+
return vals
|
222
|
+
}
|
223
|
+
|
224
|
+
const filterDic = keys => keys.filter(entry => entry.count > 1)
|
225
|
+
|
226
|
+
function countKeys(keys) {
|
227
|
+
let keys2 = []
|
228
|
+
for (let v of keys) {
|
229
|
+
let i = 0
|
230
|
+
for (let v2 of v) {
|
231
|
+
const _key = v.slice(0, i + 1)
|
232
|
+
let exists = false
|
233
|
+
for (let v3 of keys2) {
|
234
|
+
if (equals(_key, v3.key)) {
|
235
|
+
v3.count += 1
|
236
|
+
exists = true
|
237
|
+
}
|
238
|
+
}
|
239
|
+
if (!exists) keys2.push({ key: _key, count: 1 })
|
240
|
+
i++
|
241
|
+
}
|
242
|
+
}
|
243
|
+
return keys2
|
244
|
+
}
|
245
|
+
|
246
|
+
function sortKeys(keys) {
|
247
|
+
return keys.sort((a, b) => {
|
248
|
+
if (b.count !== a.count) return b.count - a.count
|
249
|
+
for (let i = 0; i < Math.min(a.key.length, b.key.length); i++) {
|
250
|
+
const aVal = a.key[i]
|
251
|
+
const bVal = b.key[i]
|
252
|
+
if (typeof aVal === "number" && typeof bVal === "string") return -1
|
253
|
+
if (typeof aVal === "string" && typeof bVal === "number") return 1
|
254
|
+
if (aVal < bVal) return -1
|
255
|
+
if (aVal > bVal) return 1
|
256
|
+
}
|
257
|
+
|
258
|
+
return a.key.length - b.key.length
|
259
|
+
})
|
260
|
+
}
|
261
|
+
|
262
|
+
function buildDic(data) {
|
263
|
+
// --- Step 1. (Optional) Save original input order if needed.
|
264
|
+
data.forEach((entry, idx) => (entry._origIdx = idx))
|
265
|
+
|
266
|
+
// --- Step 2. Sort the data in "dictionary order."
|
267
|
+
// Primary: by key array length (shorter arrays come first).
|
268
|
+
// Secondary: for keys of equal length, by the total character length (ascending)
|
269
|
+
// so that, for example, ["jane"] (4 chars) comes before ["alice"] (5 chars).
|
270
|
+
// Tertiary: if still equal, compare element-by-element using natural order.
|
271
|
+
data.sort((a, b) => {
|
272
|
+
const keyA = a.key
|
273
|
+
const keyB = b.key
|
274
|
+
|
275
|
+
// Primary: Compare array lengths.
|
276
|
+
if (keyA.length !== keyB.length) return keyA.length - keyB.length
|
277
|
+
|
278
|
+
// Secondary: Compare total character lengths (ascending).
|
279
|
+
const totalA = keyA.reduce((acc, x) => acc + x.toString().length, 0)
|
280
|
+
const totalB = keyB.reduce((acc, x) => acc + x.toString().length, 0)
|
281
|
+
if (totalA !== totalB) return totalA - totalB
|
282
|
+
// Tertiary: Compare element-by-element using natural order.
|
283
|
+
for (let i = 0; i < keyA.length; i++) {
|
284
|
+
const elA = keyA[i]
|
285
|
+
const elB = keyB[i]
|
286
|
+
|
287
|
+
if (typeof elA === typeof elB) {
|
288
|
+
if (typeof elA === "number") {
|
289
|
+
if (elA !== elB) return elA - elB
|
290
|
+
} else if (typeof elA === "string") {
|
291
|
+
const cmp = elA.localeCompare(elB, undefined, { numeric: true })
|
292
|
+
if (cmp !== 0) return cmp
|
293
|
+
} else {
|
294
|
+
// Fallback: compare string representations.
|
295
|
+
const cmp = elA
|
296
|
+
.toString()
|
297
|
+
.localeCompare(elB.toString(), undefined, { numeric: true })
|
298
|
+
if (cmp !== 0) return cmp
|
299
|
+
}
|
300
|
+
} else {
|
301
|
+
// If types differ, compare string representations.
|
302
|
+
const cmp = elA
|
303
|
+
.toString()
|
304
|
+
.localeCompare(elB.toString(), undefined, { numeric: true })
|
305
|
+
if (cmp !== 0) return cmp
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
return 0
|
310
|
+
})
|
311
|
+
|
312
|
+
// --- Step 3. Build the dictionary.
|
313
|
+
// Each dictionary entry will be stored as an object with:
|
314
|
+
// - original: the original key (an array)
|
315
|
+
// - compressed: the computed compressed representation.
|
316
|
+
const dict = []
|
317
|
+
|
318
|
+
// Helper: For a given string, look for a previously defined simple key (an array of length 1).
|
319
|
+
function getPointerIndex(str) {
|
320
|
+
for (let i = 0; i < dict.length; i++) {
|
321
|
+
if (dict[i].original.length === 1 && dict[i].original[0] === str) return i
|
322
|
+
}
|
323
|
+
return -1
|
324
|
+
}
|
325
|
+
|
326
|
+
// Helper: Element-by-element compression.
|
327
|
+
// For each element in a composite key, if it is a string that already exists as a simple key,
|
328
|
+
// replace one or more consecutive occurrences with a pointer.
|
329
|
+
// A single occurrence becomes [dictIndex]; a group becomes [dictIndex, 0].
|
330
|
+
function compressElementByElement(key) {
|
331
|
+
const rep = []
|
332
|
+
let i = 0
|
333
|
+
while (i < key.length) {
|
334
|
+
const el = key[i]
|
335
|
+
if (typeof el === "string") {
|
336
|
+
const ptrIndex = getPointerIndex(el)
|
337
|
+
if (ptrIndex !== -1) {
|
338
|
+
let j = i
|
339
|
+
while (j < key.length && key[j] === el) {
|
340
|
+
j++
|
341
|
+
}
|
342
|
+
const groupLen = j - i
|
343
|
+
rep.push(groupLen === 1 ? [ptrIndex] : [ptrIndex, 0])
|
344
|
+
i = j
|
345
|
+
continue
|
346
|
+
}
|
347
|
+
}
|
348
|
+
rep.push(el)
|
349
|
+
i++
|
350
|
+
}
|
351
|
+
return rep
|
352
|
+
}
|
353
|
+
|
354
|
+
// Helper: Compute a "cost" for a given representation.
|
355
|
+
// Each literal (number or string) counts as 1; a pointer array counts as the number of numbers it holds.
|
356
|
+
function computeCost(rep) {
|
357
|
+
let cost = 0
|
358
|
+
for (const token of rep) cost += Array.isArray(token) ? token.length : 1
|
359
|
+
return cost
|
360
|
+
}
|
361
|
+
|
362
|
+
// Helper: Full segmentation compression.
|
363
|
+
// Try to segment the entire key as a concatenation of one or more previously defined dictionary entries.
|
364
|
+
// Uses dynamic programming over the key array.
|
365
|
+
// Returns an object { cost, seg } where seg is an array of dictionary indices.
|
366
|
+
function segmentKey(key) {
|
367
|
+
const n = key.length
|
368
|
+
const dp = Array(n + 1).fill(null)
|
369
|
+
dp[n] = { cost: 0, seg: [] }
|
370
|
+
|
371
|
+
for (let i = n - 1; i >= 0; i--) {
|
372
|
+
let best = null
|
373
|
+
// Try every dictionary entry.
|
374
|
+
for (let d = 0; d < dict.length; d++) {
|
375
|
+
const candidate = dict[d].original
|
376
|
+
const m = candidate.length
|
377
|
+
if (i + m <= n) {
|
378
|
+
let match = true
|
379
|
+
for (let k = 0; k < m; k++) {
|
380
|
+
if (key[i + k] !== candidate[k]) {
|
381
|
+
match = false
|
382
|
+
break
|
383
|
+
}
|
384
|
+
}
|
385
|
+
if (match && dp[i + m] !== null) {
|
386
|
+
const candidateCost = 1 + dp[i + m].cost // cost 1 for using this pointer.
|
387
|
+
if (best === null || candidateCost < best.cost) {
|
388
|
+
best = { cost: candidateCost, seg: [d].concat(dp[i + m].seg) }
|
389
|
+
}
|
390
|
+
}
|
391
|
+
}
|
392
|
+
}
|
393
|
+
dp[i] = best
|
394
|
+
}
|
395
|
+
return dp[0]
|
396
|
+
}
|
397
|
+
|
398
|
+
// Process each entry (in the sorted, deterministic order).
|
399
|
+
for (const entry of data) {
|
400
|
+
const key = entry.key
|
401
|
+
let compressed
|
402
|
+
if (key.length === 1) {
|
403
|
+
// For simple keys, copy as-is.
|
404
|
+
compressed = key.slice()
|
405
|
+
} else {
|
406
|
+
// Try element-by-element compression.
|
407
|
+
const repA = compressElementByElement(key)
|
408
|
+
const costA = computeCost(repA)
|
409
|
+
|
410
|
+
// Also try full segmentation.
|
411
|
+
const segRes = segmentKey(key)
|
412
|
+
if (segRes !== null) {
|
413
|
+
const repB = [segRes.seg] // Represent segmentation as a pointer.
|
414
|
+
const costB = segRes.cost
|
415
|
+
compressed = costB < costA ? repB : repA
|
416
|
+
} else compressed = repA
|
417
|
+
}
|
418
|
+
dict.push({ original: key, compressed })
|
419
|
+
}
|
420
|
+
|
421
|
+
// --- Step 4. Return the dictionary and key map.
|
422
|
+
// "dictionary" is an array of compressed keys.
|
423
|
+
// "keyMap" is the array of original keys (in the same, deterministic order).
|
424
|
+
return {
|
425
|
+
dictionary: dict.map(entry => {
|
426
|
+
return entry.compressed.length === 1 && !is(Array, entry.compressed[0])
|
427
|
+
? entry.compressed[0]
|
428
|
+
: entry.compressed
|
429
|
+
}),
|
430
|
+
keyMap: dict.map(entry => entry.original),
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
function buildDic(data) {
|
435
|
+
// --- Step 1. (Optional) Save original input order if needed.
|
436
|
+
data.forEach((entry, idx) => (entry._origIdx = idx))
|
437
|
+
|
438
|
+
// --- Step 2. Sort the data in "dictionary order."
|
439
|
+
// Primary: by key array length (shorter arrays come first).
|
440
|
+
// Secondary: for keys of equal length, by the total character length (ascending)
|
441
|
+
// so that, for example, ["jane"] (4 chars) comes before ["alice"] (5 chars).
|
442
|
+
// Tertiary: if still equal, compare element-by-element using natural order.
|
443
|
+
data.sort((a, b) => {
|
444
|
+
const keyA = a.key
|
445
|
+
const keyB = b.key
|
446
|
+
|
447
|
+
// Primary: Compare array lengths.
|
448
|
+
if (keyA.length !== keyB.length) return keyA.length - keyB.length
|
449
|
+
|
450
|
+
// Secondary: Compare total character lengths (ascending).
|
451
|
+
const totalA = keyA.reduce((acc, x) => acc + x.toString().length, 0)
|
452
|
+
const totalB = keyB.reduce((acc, x) => acc + x.toString().length, 0)
|
453
|
+
if (totalA !== totalB) return totalA - totalB
|
454
|
+
// Tertiary: Compare element-by-element using natural order.
|
455
|
+
for (let i = 0; i < keyA.length; i++) {
|
456
|
+
const elA = keyA[i]
|
457
|
+
const elB = keyB[i]
|
458
|
+
|
459
|
+
if (typeof elA === typeof elB) {
|
460
|
+
if (typeof elA === "number") {
|
461
|
+
if (elA !== elB) return elA - elB
|
462
|
+
} else if (typeof elA === "string") {
|
463
|
+
const cmp = elA.localeCompare(elB, undefined, { numeric: true })
|
464
|
+
if (cmp !== 0) return cmp
|
465
|
+
} else {
|
466
|
+
// Fallback: compare string representations.
|
467
|
+
const cmp = elA
|
468
|
+
.toString()
|
469
|
+
.localeCompare(elB.toString(), undefined, { numeric: true })
|
470
|
+
if (cmp !== 0) return cmp
|
471
|
+
}
|
472
|
+
} else {
|
473
|
+
// If types differ, compare string representations.
|
474
|
+
const cmp = elA
|
475
|
+
.toString()
|
476
|
+
.localeCompare(elB.toString(), undefined, { numeric: true })
|
477
|
+
if (cmp !== 0) return cmp
|
478
|
+
}
|
479
|
+
}
|
480
|
+
|
481
|
+
return 0
|
482
|
+
})
|
483
|
+
|
484
|
+
// --- Step 3. Build the dictionary.
|
485
|
+
// Each dictionary entry will be stored as an object with:
|
486
|
+
// - original: the original key (an array)
|
487
|
+
// - compressed: the computed compressed representation.
|
488
|
+
const dict = []
|
489
|
+
|
490
|
+
// Helper: For a given string, look for a previously defined simple key (an array of length 1).
|
491
|
+
function getPointerIndex(str) {
|
492
|
+
for (let i = 0; i < dict.length; i++) {
|
493
|
+
if (dict[i].original.length === 1 && dict[i].original[0] === str) return i
|
494
|
+
}
|
495
|
+
return -1
|
496
|
+
}
|
497
|
+
|
498
|
+
// Helper: Element-by-element compression.
|
499
|
+
// For each element in a composite key, if it is a string that already exists as a simple key,
|
500
|
+
// replace one or more consecutive occurrences with a pointer.
|
501
|
+
// A single occurrence becomes [dictIndex]; a group becomes [dictIndex, 0].
|
502
|
+
function compressElementByElement(key) {
|
503
|
+
const rep = []
|
504
|
+
let i = 0
|
505
|
+
while (i < key.length) {
|
506
|
+
const el = key[i]
|
507
|
+
if (typeof el === "string") {
|
508
|
+
const ptrIndex = getPointerIndex(el)
|
509
|
+
if (ptrIndex !== -1) {
|
510
|
+
let j = i
|
511
|
+
while (j < key.length && key[j] === el) {
|
512
|
+
j++
|
513
|
+
}
|
514
|
+
const groupLen = j - i
|
515
|
+
rep.push(groupLen === 1 ? [ptrIndex] : [ptrIndex, 0])
|
516
|
+
i = j
|
517
|
+
continue
|
518
|
+
}
|
519
|
+
}
|
520
|
+
rep.push(el)
|
521
|
+
i++
|
522
|
+
}
|
523
|
+
return rep
|
524
|
+
}
|
525
|
+
|
526
|
+
// Helper: Compute a "cost" for a given representation.
|
527
|
+
// Each literal (number or string) counts as 1; a pointer array counts as the number of numbers it holds.
|
528
|
+
function computeCost(rep) {
|
529
|
+
let cost = 0
|
530
|
+
for (const token of rep) cost += Array.isArray(token) ? token.length : 1
|
531
|
+
return cost
|
532
|
+
}
|
533
|
+
|
534
|
+
// Helper: Full segmentation compression.
|
535
|
+
// Try to segment the entire key as a concatenation of one or more previously defined dictionary entries.
|
536
|
+
// Uses dynamic programming over the key array.
|
537
|
+
// Returns an object { cost, seg } where seg is an array of dictionary indices.
|
538
|
+
function segmentKey(key) {
|
539
|
+
const n = key.length
|
540
|
+
const dp = Array(n + 1).fill(null)
|
541
|
+
dp[n] = { cost: 0, seg: [] }
|
542
|
+
|
543
|
+
for (let i = n - 1; i >= 0; i--) {
|
544
|
+
let best = null
|
545
|
+
// Try every dictionary entry.
|
546
|
+
for (let d = 0; d < dict.length; d++) {
|
547
|
+
const candidate = dict[d].original
|
548
|
+
const m = candidate.length
|
549
|
+
if (i + m <= n) {
|
550
|
+
let match = true
|
551
|
+
for (let k = 0; k < m; k++) {
|
552
|
+
if (key[i + k] !== candidate[k]) {
|
553
|
+
match = false
|
554
|
+
break
|
555
|
+
}
|
556
|
+
}
|
557
|
+
if (match && dp[i + m] !== null) {
|
558
|
+
const candidateCost = 1 + dp[i + m].cost // cost 1 for using this pointer.
|
559
|
+
if (best === null || candidateCost < best.cost) {
|
560
|
+
best = { cost: candidateCost, seg: [d].concat(dp[i + m].seg) }
|
561
|
+
}
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
dp[i] = best
|
566
|
+
}
|
567
|
+
return dp[0]
|
568
|
+
}
|
569
|
+
|
570
|
+
// Process each entry (in the sorted, deterministic order).
|
571
|
+
for (const entry of data) {
|
572
|
+
const key = entry.key
|
573
|
+
let compressed
|
574
|
+
if (key.length === 1) {
|
575
|
+
// For simple keys, copy as-is.
|
576
|
+
compressed = key.slice()
|
577
|
+
} else {
|
578
|
+
// Try element-by-element compression.
|
579
|
+
const repA = compressElementByElement(key)
|
580
|
+
let bestCost = computeCost(repA)
|
581
|
+
let bestRep = repA
|
582
|
+
|
583
|
+
// Also try full segmentation over the entire key.
|
584
|
+
const segRes = segmentKey(key)
|
585
|
+
if (segRes !== null) {
|
586
|
+
const repB = [segRes.seg] // Represent segmentation as a pointer.
|
587
|
+
const costB = segRes.cost
|
588
|
+
if (costB < bestCost) {
|
589
|
+
bestCost = costB
|
590
|
+
bestRep = repB
|
591
|
+
}
|
592
|
+
}
|
593
|
+
|
594
|
+
// Now try partial segmentation: try segmenting a prefix and then appending the literal remainder.
|
595
|
+
const n = key.length
|
596
|
+
for (let i = 1; i < n; i++) {
|
597
|
+
const prefixSeg = segmentKey(key.slice(0, i))
|
598
|
+
if (prefixSeg !== null) {
|
599
|
+
const literalPart = key.slice(i)
|
600
|
+
const candidateCost = prefixSeg.cost + computeCost(literalPart)
|
601
|
+
if (candidateCost < bestCost) {
|
602
|
+
bestCost = candidateCost
|
603
|
+
// Build candidate representation: pointer for the segmented prefix followed by literal remainder.
|
604
|
+
bestRep = [prefixSeg.seg].concat(literalPart)
|
605
|
+
}
|
606
|
+
}
|
607
|
+
}
|
608
|
+
|
609
|
+
compressed = bestRep
|
610
|
+
}
|
611
|
+
dict.push({ original: key, compressed })
|
612
|
+
}
|
613
|
+
|
614
|
+
// --- Step 4. Return the dictionary and key map.
|
615
|
+
// "dictionary" is an array of compressed keys.
|
616
|
+
// "keyMap" is the array of original keys (in the same, deterministic order).
|
617
|
+
return {
|
618
|
+
dictionary: dict.map(entry => {
|
619
|
+
return entry.compressed.length === 1 && !is(Array, entry.compressed[0])
|
620
|
+
? entry.compressed[0]
|
621
|
+
: entry.compressed
|
622
|
+
}),
|
623
|
+
keyMap: dict.map(entry => entry.original),
|
624
|
+
}
|
625
|
+
}
|
626
|
+
const genDic = compose(buildDic, filterDic, sortKeys, countKeys, listKeys)
|
627
|
+
|
628
|
+
function listKeys(v, key = [], keys = []) {
|
629
|
+
if (Array.isArray(v)) {
|
630
|
+
let i = 0
|
631
|
+
for (const v2 of v) {
|
632
|
+
listKeys(v2, append(i, key), keys)
|
633
|
+
i++
|
634
|
+
}
|
635
|
+
} else if (typeof v == "object") {
|
636
|
+
for (const k in v) listKeys(v[k], append(k, key), keys)
|
637
|
+
} else {
|
638
|
+
keys.push(key)
|
639
|
+
}
|
640
|
+
return keys
|
641
|
+
}
|
642
|
+
|
643
|
+
function isPrefix(path, prefix) {
|
644
|
+
if (prefix.length > path.length) return false
|
645
|
+
for (let i = 0; i < prefix.length; i++) {
|
646
|
+
if (!equals(path[i], prefix[i])) return false
|
647
|
+
}
|
648
|
+
return true
|
649
|
+
}
|
650
|
+
function applyDicToPath(path, sortedDic) {
|
651
|
+
// Base case: if the path is empty, nothing to replace.
|
652
|
+
if (path.length === 0) return []
|
653
|
+
// Iterate over the dictionary entries in descending order.
|
654
|
+
for (const { entry, index } of sortedDic) {
|
655
|
+
if (isPrefix(path, entry)) {
|
656
|
+
// Found a match: remove the matched prefix.
|
657
|
+
const remainder = path.slice(entry.length)
|
658
|
+
// Recursively apply dictionary replacement on the remainder.
|
659
|
+
const replacedRemainder = applyDicToPath(remainder, sortedDic)
|
660
|
+
// If the remainder is completely replaced (i.e. replacedRemainder is a single dictionary reference array),
|
661
|
+
// then merge the current dictionary index with that.
|
662
|
+
if (replacedRemainder.length === 0) {
|
663
|
+
// No remainder: simply return the dictionary reference.
|
664
|
+
return [[index]]
|
665
|
+
}
|
666
|
+
if (Array.isArray(replacedRemainder[0])) {
|
667
|
+
// The first component is already a dictionary reference: merge the indices.
|
668
|
+
return [[index, ...replacedRemainder[0]]]
|
669
|
+
} else {
|
670
|
+
// Otherwise, return the dictionary reference for the prefix and then the literal remainder.
|
671
|
+
return [[index]].concat(replacedRemainder)
|
672
|
+
}
|
673
|
+
}
|
674
|
+
}
|
675
|
+
// If no dictionary entry applies, return the original literal path.
|
676
|
+
return path
|
677
|
+
}
|
678
|
+
|
679
|
+
function applyDic(arr, dic) {
|
680
|
+
// Build sorted dictionary entries in descending order by length.
|
681
|
+
const sortedDic = dic
|
682
|
+
.map((entry, index) => ({ entry, index }))
|
683
|
+
.sort((a, b) => b.entry.length - a.entry.length)
|
684
|
+
// For each pair, apply dictionary replacement to the path.
|
685
|
+
return arr.map(pair => {
|
686
|
+
const newPath = applyDicToPath(pair[0], sortedDic)
|
687
|
+
return [newPath, pair[1]]
|
688
|
+
})
|
689
|
+
}
|
690
|
+
|
691
|
+
function encodePaths(data) {
|
692
|
+
for (let v of data) {
|
693
|
+
let path = []
|
694
|
+
for (let v2 of v[0]) {
|
695
|
+
if (is(Number, v2)) path.push([0, 0, v2])
|
696
|
+
else if (is(String, v2)) {
|
697
|
+
const key = v2.split("").map(c => c.charCodeAt(0))
|
698
|
+
path.push([key.length, ...(key.length == 0 ? [1] : key)])
|
699
|
+
} else if (is(Array, v2)) {
|
700
|
+
if (v2.length === 1) path.push([0, 3, v2[0]])
|
701
|
+
else path.push([0, 4, v2.length, ...v2])
|
702
|
+
}
|
703
|
+
}
|
704
|
+
v[0] = path
|
705
|
+
}
|
706
|
+
return data
|
707
|
+
}
|
708
|
+
|
709
|
+
function mapDic(dic, len) {
|
710
|
+
let _map = []
|
711
|
+
while (dic.length > 0) {
|
712
|
+
let dlen = dic.shift()
|
713
|
+
let _elms = []
|
714
|
+
while (dlen > 0) {
|
715
|
+
let type = dic.shift()
|
716
|
+
let elms = []
|
717
|
+
if (type == 3) {
|
718
|
+
let slen = dic.shift()
|
719
|
+
elms.push(type)
|
720
|
+
for (let i = 0; i < slen; i++) elms.push(dic.shift())
|
721
|
+
_elms.push(elms)
|
722
|
+
} else if (type == 2) {
|
723
|
+
elms = concat(elms, [0, 0, dic.shift()])
|
724
|
+
_elms.push(elms)
|
725
|
+
} else if (type == 5) {
|
726
|
+
for (let v2 of _map[dic.shift()]) _elms.push(v2)
|
727
|
+
}
|
728
|
+
dlen--
|
729
|
+
}
|
730
|
+
_map.push(_elms)
|
731
|
+
if (_map.length === len) break
|
732
|
+
}
|
733
|
+
return _map
|
734
|
+
}
|
735
|
+
|
736
|
+
function encodeDic(dict) {
|
737
|
+
let enc = []
|
738
|
+
for (let v of dict) {
|
739
|
+
let len = 1
|
740
|
+
let elms = []
|
741
|
+
if (!is(String, v)) {
|
742
|
+
len = 0
|
743
|
+
for (let v2 of v) {
|
744
|
+
if (is(Array, v2)) {
|
745
|
+
len += v2.length
|
746
|
+
for (let v3 of v2) {
|
747
|
+
elms = concat(elms, [5, v3])
|
748
|
+
}
|
749
|
+
} else {
|
750
|
+
len += 1
|
751
|
+
if (is(String, v2)) {
|
752
|
+
elms.push(3)
|
753
|
+
elms.push(v2.length)
|
754
|
+
elms = concat(
|
755
|
+
elms,
|
756
|
+
v2.split("").map(c => c.charCodeAt(0)),
|
757
|
+
)
|
758
|
+
} else {
|
759
|
+
elms = concat(elms, [2, v2])
|
760
|
+
}
|
761
|
+
}
|
762
|
+
}
|
763
|
+
} else {
|
764
|
+
elms.push(3)
|
765
|
+
elms.push(v.length)
|
766
|
+
elms = concat(
|
767
|
+
elms,
|
768
|
+
v.split("").map(c => c.charCodeAt(0)),
|
769
|
+
)
|
770
|
+
}
|
771
|
+
enc = concat(enc, [len, ...elms])
|
772
|
+
}
|
773
|
+
return enc
|
774
|
+
}
|
775
|
+
function encode(json, nodic = false) {
|
776
|
+
let dic = null
|
777
|
+
let dictionary, keyMap
|
778
|
+
if (nodic !== true) {
|
779
|
+
;({ dictionary, keyMap } = genDic(json))
|
780
|
+
if (dictionary.length > 0) dic = encodeDic(dictionary)
|
781
|
+
}
|
782
|
+
let enc = _encode(json)
|
783
|
+
if (dic) enc = applyDic(enc, keyMap)
|
784
|
+
|
785
|
+
enc = encodePaths(enc)
|
786
|
+
enc.sort((a, b) => {
|
787
|
+
const isUndefined = v => typeof v == "undefined"
|
788
|
+
const max = Math.max(a[0].length, b[0].length)
|
789
|
+
if (max > 0) {
|
790
|
+
for (let i = 0; i < max; i++) {
|
791
|
+
const exA = !isUndefined(a[0][i])
|
792
|
+
const exB = !isUndefined(b[0][i])
|
793
|
+
if (exA && !exB) return 1
|
794
|
+
if (!exA && exB) return -1
|
795
|
+
const max2 = Math.max(a[0][i].length, b[0][i].length)
|
796
|
+
if (max2 > 0) {
|
797
|
+
for (let i2 = 0; i2 < max2; i2++) {
|
798
|
+
const vA = a[0][i][i2]
|
799
|
+
const vB = b[0][i][i2]
|
800
|
+
const exA = !isUndefined(vA)
|
801
|
+
const exB = !isUndefined(vB)
|
802
|
+
if (exA && !exB) return 1
|
803
|
+
if (!exA && exB) return -1
|
804
|
+
if (vA > vB) return 1
|
805
|
+
if (vA < vB) return -1
|
806
|
+
}
|
807
|
+
}
|
808
|
+
}
|
809
|
+
}
|
810
|
+
return 0
|
811
|
+
})
|
812
|
+
const _dic = dic ? [1, 0, 2, dictionary.length, ...dic] : []
|
813
|
+
return concat(
|
814
|
+
_dic,
|
815
|
+
enc.reduce((arr, v) => arr.concat([...flattenPath(v[0]), ...v[1]]), []),
|
816
|
+
)
|
817
|
+
}
|
818
|
+
|
819
|
+
function _decode(arr) {
|
820
|
+
let vals = []
|
821
|
+
let dic = []
|
822
|
+
while (arr.length > 0) {
|
823
|
+
let plen = arr.shift()
|
824
|
+
let keys = []
|
825
|
+
let val = null
|
826
|
+
let skip = false
|
827
|
+
while (plen > 0) {
|
828
|
+
const plen2 = arr.shift()
|
829
|
+
if (plen2 == 0) {
|
830
|
+
const plen3 = arr.shift()
|
831
|
+
if (plen3 == 1) {
|
832
|
+
keys.push([plen2, plen3])
|
833
|
+
} else if (plen3 == 0) {
|
834
|
+
keys.push([plen2, plen3, arr.shift()])
|
835
|
+
} else if (plen3 == 2) {
|
836
|
+
const dict = arr.shift()
|
837
|
+
dic = mapDic(arr, dict)
|
838
|
+
skip = true
|
839
|
+
} else if (plen3 == 3) {
|
840
|
+
const _keys = dic[arr.shift()]
|
841
|
+
for (const k of _keys) keys.push(k)
|
842
|
+
} else if (plen3 == 4) {
|
843
|
+
let key = []
|
844
|
+
let plen4 = arr.shift()
|
845
|
+
for (let i2 = 0; i2 < plen4; i2++) {
|
846
|
+
const _keys = dic[arr.shift()]
|
847
|
+
for (const k of _keys) keys.push(k)
|
848
|
+
}
|
849
|
+
}
|
850
|
+
} else if (plen2 != 0) {
|
851
|
+
const plen3 = plen2
|
852
|
+
let key = []
|
853
|
+
for (let i2 = 0; i2 < plen3; i2++) key.push(arr.shift())
|
854
|
+
keys.push([plen2, ...key])
|
855
|
+
}
|
856
|
+
plen--
|
857
|
+
}
|
858
|
+
if (skip) continue
|
859
|
+
const type = arr.shift()
|
860
|
+
val = [type]
|
861
|
+
if (type == 5 || type == 6) {
|
862
|
+
val.push(arr.shift())
|
863
|
+
val.push(arr.shift())
|
864
|
+
} else if (type == 3 || type == 4) {
|
865
|
+
val.push(arr.shift())
|
866
|
+
} else if (type == 7) {
|
867
|
+
const strlen = arr.shift()
|
868
|
+
val.push(strlen)
|
869
|
+
for (let i2 = 0; i2 < strlen; i2++) val.push(arr.shift())
|
870
|
+
}
|
871
|
+
vals.push([keys, val])
|
872
|
+
}
|
873
|
+
return vals
|
874
|
+
}
|
875
|
+
|
876
|
+
// 0: null, 1: true, 2: false, 3: positive integer, 4: negative integer, 5: positive float, 6: negative float, 7: string, 8: object, 9: ref
|
877
|
+
|
878
|
+
function encodeVal(v) {
|
879
|
+
let vals = []
|
880
|
+
if (typeof v == "number" || typeof v == "bigint") {
|
881
|
+
const int = Number.isInteger(v)
|
882
|
+
let moved = 0
|
883
|
+
let num = v
|
884
|
+
while (num % 1 != 0) {
|
885
|
+
num *= 10
|
886
|
+
moved += 1
|
887
|
+
}
|
888
|
+
let type = 3
|
889
|
+
if (v >= 0) {
|
890
|
+
if (moved > 0) type = 5
|
891
|
+
} else {
|
892
|
+
if (moved > 0) type = 6
|
893
|
+
else type = 4
|
894
|
+
}
|
895
|
+
vals.push(type)
|
896
|
+
if (moved > 0) vals.push(moved)
|
897
|
+
if (v < 0) vals.push(-num)
|
898
|
+
else vals.push(num)
|
899
|
+
} else if (typeof v == "boolean") vals.push(v ? 1 : 2)
|
900
|
+
else if (v == null) vals = [0]
|
901
|
+
else if (typeof v == "string") {
|
902
|
+
vals = [7, v.length, ...v.split("").map(c => c.charCodeAt(0))]
|
903
|
+
} else vals = [8, ...encode(v)]
|
904
|
+
return vals
|
905
|
+
}
|
906
|
+
|
907
|
+
function decodeVal(arr) {
|
908
|
+
const type = arr[0]
|
909
|
+
const _val = arr[1]
|
910
|
+
let val = null
|
911
|
+
if (type == 0) {
|
912
|
+
val = null
|
913
|
+
} else if (type == 1) {
|
914
|
+
val = true
|
915
|
+
} else if (type == 2) {
|
916
|
+
val = false
|
917
|
+
} else if (type == 3) {
|
918
|
+
val = arr[1]
|
919
|
+
} else if (type == 4) {
|
920
|
+
val = arr[1] * -1
|
921
|
+
} else if (type == 5) {
|
922
|
+
val = arr[2]
|
923
|
+
for (let i = 0; i < arr[1]; i++) {
|
924
|
+
val /= 10
|
925
|
+
}
|
926
|
+
} else if (type == 6) {
|
927
|
+
val = arr[2] * -1
|
928
|
+
for (let i = 0; i < arr[1]; i++) {
|
929
|
+
val /= 10
|
930
|
+
}
|
931
|
+
} else if (type == 7) {
|
932
|
+
val = arr
|
933
|
+
.slice(2)
|
934
|
+
.map(c => String.fromCharCode(Number(c)))
|
935
|
+
.join("")
|
936
|
+
} else if (type == 8) {
|
937
|
+
val = decode(arr.slice(1))
|
938
|
+
}
|
939
|
+
return val
|
940
|
+
}
|
941
|
+
|
942
|
+
function decode(arr) {
|
943
|
+
const decoded = _decode(arr)
|
944
|
+
let json =
|
945
|
+
decoded[0]?.[0]?.[0]?.[0] == 0 && decoded[0]?.[0]?.[0]?.[1] == 0 ? [] : {}
|
946
|
+
for (const v of decoded) {
|
947
|
+
const keys = v[0].map(v2 => {
|
948
|
+
if (v2[0] == 0) {
|
949
|
+
if (v2[1] == 1) return ""
|
950
|
+
return v2[2]
|
951
|
+
} else {
|
952
|
+
return v2
|
953
|
+
.slice(1)
|
954
|
+
.map(c => String.fromCharCode(Number(c)))
|
955
|
+
.join("")
|
956
|
+
}
|
957
|
+
})
|
958
|
+
if (keys.length == 0) {
|
959
|
+
json = decodeVal(v[1])
|
960
|
+
} else {
|
961
|
+
let obj = json
|
962
|
+
let i = 0
|
963
|
+
for (const k of keys) {
|
964
|
+
if (typeof k == "number") {
|
965
|
+
if (typeof keys[i + 1] == "undefined") {
|
966
|
+
obj[k] = decodeVal(v[1])
|
967
|
+
} else {
|
968
|
+
if (typeof obj[k] == "undefined") {
|
969
|
+
if (typeof keys[i + 1] == "string") {
|
970
|
+
obj[k] = {}
|
971
|
+
} else {
|
972
|
+
obj[k] = []
|
973
|
+
}
|
974
|
+
}
|
975
|
+
}
|
976
|
+
} else {
|
977
|
+
if (typeof obj[k] == "undefined") {
|
978
|
+
if (typeof keys[i + 1] == "undefined") {
|
979
|
+
obj[k] = decodeVal(v[1])
|
980
|
+
} else if (typeof keys[i + 1] == "string") {
|
981
|
+
obj[k] = {}
|
982
|
+
} else {
|
983
|
+
obj[k] = []
|
984
|
+
}
|
985
|
+
}
|
986
|
+
}
|
987
|
+
obj = obj[k]
|
988
|
+
i++
|
989
|
+
}
|
990
|
+
}
|
991
|
+
}
|
992
|
+
return json
|
993
|
+
}
|
994
|
+
|
995
|
+
const toIndex = str => {
|
996
|
+
return (
|
997
|
+
"1" +
|
998
|
+
str
|
999
|
+
.split("")
|
1000
|
+
.map(s => base64Map[s])
|
1001
|
+
.join("")
|
1002
|
+
)
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
const fromIndex = id => {
|
1006
|
+
let _id = id.toString().split("")
|
1007
|
+
_id.shift()
|
1008
|
+
return splitEvery(2, _id)
|
1009
|
+
.map(s => {
|
1010
|
+
return strMap[s.join("")]
|
1011
|
+
})
|
1012
|
+
.join("")
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
function toSignal(arr, uint_len = 75) {
|
1016
|
+
const _arr = flatten(
|
1017
|
+
arr.map(n => {
|
1018
|
+
let str = splitEvery(8, n.toString().split(""))
|
1019
|
+
let i = 0
|
1020
|
+
str = str.map(s => {
|
1021
|
+
const len = i == str.length - 1 ? s.length : 9
|
1022
|
+
i++
|
1023
|
+
return len.toString() + s.join("")
|
1024
|
+
})
|
1025
|
+
return str
|
1026
|
+
}),
|
1027
|
+
)
|
1028
|
+
let _arr2 = []
|
1029
|
+
let one = 0
|
1030
|
+
let i = 0
|
1031
|
+
let start = null
|
1032
|
+
if (!uint_len) uint_len = _arr * 100
|
1033
|
+
for (let v of _arr) {
|
1034
|
+
_arr2.push(v)
|
1035
|
+
if (v.length - 1 == 1) {
|
1036
|
+
if (start == null) start = i
|
1037
|
+
one += v.length - 1
|
1038
|
+
if (one == 9) {
|
1039
|
+
_arr2[start] = `0${one}${_arr2[start][1]}`
|
1040
|
+
for (let i2 = start + 1; i2 <= i; i2++) _arr2[i2] = `${_arr2[i2][1]}`
|
1041
|
+
one = 0
|
1042
|
+
start = null
|
1043
|
+
}
|
1044
|
+
} else {
|
1045
|
+
if (one > 2) {
|
1046
|
+
_arr2[start] = `0${one}${_arr2[start][1]}`
|
1047
|
+
for (let i2 = start + 1; i2 < i; i2++) _arr2[i2] = `${_arr2[i2][1]}`
|
1048
|
+
}
|
1049
|
+
one = 0
|
1050
|
+
start = null
|
1051
|
+
}
|
1052
|
+
i++
|
1053
|
+
}
|
1054
|
+
if (one > 2) {
|
1055
|
+
_arr2[start] = `0${one}${_arr2[start][1]}`
|
1056
|
+
for (let i2 = start + 1; i2 <= i - 1; i2++) _arr2[i2] = `${_arr2[i2][1]}`
|
1057
|
+
}
|
1058
|
+
let _arr3 = []
|
1059
|
+
let chain = null
|
1060
|
+
let cur = 0
|
1061
|
+
let num = ""
|
1062
|
+
for (let v of _arr2) {
|
1063
|
+
if (chain == null && +v[0] == 0) {
|
1064
|
+
chain = +v[1]
|
1065
|
+
cur = 1
|
1066
|
+
num = v
|
1067
|
+
} else if (chain != null) {
|
1068
|
+
num += v
|
1069
|
+
cur++
|
1070
|
+
if (chain == cur) {
|
1071
|
+
_arr3.push(num)
|
1072
|
+
chain = null
|
1073
|
+
num = ""
|
1074
|
+
cur = 0
|
1075
|
+
}
|
1076
|
+
} else {
|
1077
|
+
_arr3.push(v)
|
1078
|
+
}
|
1079
|
+
}
|
1080
|
+
if (chain != null) _arr3.push(num)
|
1081
|
+
let arrs2 = []
|
1082
|
+
let len2 = 0
|
1083
|
+
let str2 = ""
|
1084
|
+
for (let v of _arr3) {
|
1085
|
+
if (len2 + v.length > uint_len) {
|
1086
|
+
arrs2.push("1" + str2)
|
1087
|
+
if (+v[0] == 0) {
|
1088
|
+
let len3 = uint_len - len2
|
1089
|
+
if (len3 == 2 || len3 == 3) {
|
1090
|
+
arrs2[arrs2.length - 1] += `1${v[2]}`
|
1091
|
+
let new_len = +v[1] - 1
|
1092
|
+
if (new_len == 2) {
|
1093
|
+
v = `1${v[3]}1${v[4]}`
|
1094
|
+
} else {
|
1095
|
+
v = `0${new_len}${v.slice(3)}`
|
1096
|
+
}
|
1097
|
+
} else if (len3 > 3) {
|
1098
|
+
let new_len = +v[1] - 2
|
1099
|
+
let old_len = 2
|
1100
|
+
if (len3 == 4) {
|
1101
|
+
arrs2[arrs2.length - 1] += `1${v[2]}1${v[3]}`
|
1102
|
+
} else {
|
1103
|
+
old_len = len3 - 2
|
1104
|
+
new_len = +v[1] - old_len
|
1105
|
+
arrs2[arrs2.length - 1] += `0${old_len}${v.slice(2, 2 + old_len)}`
|
1106
|
+
}
|
1107
|
+
if (new_len == 1) {
|
1108
|
+
v = `1${v[old_len + 2]}`
|
1109
|
+
} else if (new_len == 2) {
|
1110
|
+
v = `1${v[old_len + 2]}1${v[old_len + 3]}`
|
1111
|
+
} else {
|
1112
|
+
v = `0${new_len}${v.slice(old_len + 2)}`
|
1113
|
+
}
|
1114
|
+
}
|
1115
|
+
}
|
1116
|
+
len2 = 0
|
1117
|
+
str2 = ""
|
1118
|
+
}
|
1119
|
+
len2 += v.length
|
1120
|
+
str2 += v
|
1121
|
+
}
|
1122
|
+
if (str2 != "") arrs2.push("1" + str2)
|
1123
|
+
return arrs2
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
function fromSignal(arr) {
|
1127
|
+
let _arr = []
|
1128
|
+
let prev = ""
|
1129
|
+
for (let s of arr) {
|
1130
|
+
s = s.slice(1)
|
1131
|
+
let str = s.split("")
|
1132
|
+
while (str.length > 0) {
|
1133
|
+
const len = +str.shift()
|
1134
|
+
if (len == 0) {
|
1135
|
+
const len2 = +str.shift()
|
1136
|
+
for (let i2 = 0; i2 < len2; i2++) {
|
1137
|
+
_arr.push(+str[i2])
|
1138
|
+
}
|
1139
|
+
str = str.slice(len2)
|
1140
|
+
} else if (len == 9) {
|
1141
|
+
prev += str.slice(0, 8).join("")
|
1142
|
+
str = str.slice(8)
|
1143
|
+
} else {
|
1144
|
+
const nums = str.slice(0, len).join("")
|
1145
|
+
str = str.slice(len)
|
1146
|
+
_arr.push(+(prev + nums))
|
1147
|
+
prev = ""
|
1148
|
+
}
|
1149
|
+
}
|
1150
|
+
}
|
1151
|
+
return _arr
|
1152
|
+
}
|
1153
|
+
|
1154
|
+
const path = p => toSignal(encodePath(p))
|
1155
|
+
const val = v => toSignal(encodeVal(v))
|
1156
|
+
const query = v => toSignal(encodeQuery(v))
|
1157
|
+
|
1158
|
+
function encodeQuery(v) {
|
1159
|
+
if (!Array.isArray(v)) throw Error("query must be an array")
|
1160
|
+
const op = v[0]
|
1161
|
+
if (isNil(ops[op])) throw Error(`query not supported: ${op}`)
|
1162
|
+
return [ops[op], ...encodeVal(v[1])]
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
function decodeQuery(v) {
|
1166
|
+
const op = opMap[v[0]]
|
1167
|
+
if (isNil(op)) throw Error("op doens't exist")
|
1168
|
+
return [op, decodeVal(v.slice(1))]
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
function toUint8(sig) {
|
1172
|
+
let num = BigInt(sig)
|
1173
|
+
let byteArray = []
|
1174
|
+
while (num > 0) {
|
1175
|
+
byteArray.push(Number(num % 256n))
|
1176
|
+
num /= 256n
|
1177
|
+
}
|
1178
|
+
return new Uint8Array(byteArray.reverse())
|
1179
|
+
}
|
1180
|
+
function compress(arr) {
|
1181
|
+
let sig = toSignal(arr, false)
|
1182
|
+
return toUint8(sig)
|
1183
|
+
}
|
1184
|
+
|
1185
|
+
function decompress(arr) {
|
1186
|
+
const str = fromUint8(arr)
|
1187
|
+
return fromSignal([str])
|
1188
|
+
}
|
1189
|
+
|
1190
|
+
function fromUint8(arr) {
|
1191
|
+
let num = 0n
|
1192
|
+
for (let byte of arr) {
|
1193
|
+
num = num * 256n + BigInt(byte)
|
1194
|
+
}
|
1195
|
+
return num.toString()
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
module.exports = {
|
1199
|
+
encode,
|
1200
|
+
decode,
|
1201
|
+
encodePath,
|
1202
|
+
decodePath,
|
1203
|
+
encodeVal,
|
1204
|
+
decodeVal,
|
1205
|
+
pad,
|
1206
|
+
_encode,
|
1207
|
+
flattenPath,
|
1208
|
+
toSignal,
|
1209
|
+
fromSignal,
|
1210
|
+
toIndex,
|
1211
|
+
fromIndex,
|
1212
|
+
path,
|
1213
|
+
val,
|
1214
|
+
query,
|
1215
|
+
encodeQuery,
|
1216
|
+
decodeQuery,
|
1217
|
+
compress,
|
1218
|
+
decompress,
|
1219
|
+
toUint8,
|
1220
|
+
fromUint8,
|
1221
|
+
}
|
package/encoder.js
CHANGED