braid-text 0.2.23 → 0.2.25
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/index.js +116 -33
- package/package.json +1 -1
- package/test/test.html +30 -0
- package/test/test.js +107 -2
package/index.js
CHANGED
|
@@ -76,6 +76,16 @@ braid_text.serve = async (req, res, options = {}) => {
|
|
|
76
76
|
if (!req.subscribe) {
|
|
77
77
|
res.setHeader("Accept-Subscribe", "true")
|
|
78
78
|
|
|
79
|
+
// special case for HEAD asking for version/parents,
|
|
80
|
+
// to be faster by not reconstructing body
|
|
81
|
+
if (req.method === "HEAD" && (req.version || req.parents)) {
|
|
82
|
+
if ((req.version || req.parents).every(event => {
|
|
83
|
+
var [actor, seq] = decode_version(event)
|
|
84
|
+
return resource.actor_seqs[actor]?.has(seq)
|
|
85
|
+
})) return my_end(200)
|
|
86
|
+
else return my_end(500, "Unknown Version")
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
let x = null
|
|
80
90
|
try {
|
|
81
91
|
x = await braid_text.get(resource, { version: req.version, parents: req.parents })
|
|
@@ -800,20 +810,8 @@ function dt_len(doc, version) {
|
|
|
800
810
|
function dt_get_string(doc, version) {
|
|
801
811
|
var bytes = doc.toBytes()
|
|
802
812
|
var oplog = OpLog.fromBytes(bytes)
|
|
803
|
-
var [_agents, versions, _parentss] = dt_parse([...bytes])
|
|
804
|
-
|
|
805
|
-
var frontier = new Set(version)
|
|
806
|
-
|
|
807
|
-
var local_version = []
|
|
808
|
-
for (var i = 0; i < versions.length; i++) {
|
|
809
|
-
var v = versions[i].join("-")
|
|
810
|
-
if (frontier.has(v)) {
|
|
811
|
-
local_version.push(i)
|
|
812
|
-
frontier.delete(v)
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
813
|
|
|
816
|
-
|
|
814
|
+
var local_version = dt_get_local_version(bytes, version)
|
|
817
815
|
|
|
818
816
|
var b = new Branch()
|
|
819
817
|
b.merge(oplog, new Uint32Array(local_version))
|
|
@@ -1008,25 +1006,25 @@ function dt_parse(byte_array) {
|
|
|
1008
1006
|
|
|
1009
1007
|
while (byte_array.length) {
|
|
1010
1008
|
let id = byte_array.shift()
|
|
1011
|
-
let len =
|
|
1009
|
+
let len = dt_read_varint(byte_array)
|
|
1012
1010
|
if (id == 1) {
|
|
1013
1011
|
} else if (id == 3) {
|
|
1014
1012
|
let goal = byte_array.length - len
|
|
1015
1013
|
while (byte_array.length > goal) {
|
|
1016
|
-
agents.push(
|
|
1014
|
+
agents.push(dt_read_string(byte_array))
|
|
1017
1015
|
}
|
|
1018
1016
|
} else if (id == 20) {
|
|
1019
1017
|
} else if (id == 21) {
|
|
1020
1018
|
let seqs = {}
|
|
1021
1019
|
let goal = byte_array.length - len
|
|
1022
1020
|
while (byte_array.length > goal) {
|
|
1023
|
-
let part0 =
|
|
1021
|
+
let part0 = dt_read_varint(byte_array)
|
|
1024
1022
|
let has_jump = part0 & 1
|
|
1025
1023
|
let agent_i = (part0 >> 1) - 1
|
|
1026
|
-
let run_length =
|
|
1024
|
+
let run_length = dt_read_varint(byte_array)
|
|
1027
1025
|
let jump = 0
|
|
1028
1026
|
if (has_jump) {
|
|
1029
|
-
let part2 =
|
|
1027
|
+
let part2 = dt_read_varint(byte_array)
|
|
1030
1028
|
jump = part2 >> 1
|
|
1031
1029
|
if (part2 & 1) jump *= -1
|
|
1032
1030
|
}
|
|
@@ -1041,12 +1039,12 @@ function dt_parse(byte_array) {
|
|
|
1041
1039
|
let count = 0
|
|
1042
1040
|
let goal = byte_array.length - len
|
|
1043
1041
|
while (byte_array.length > goal) {
|
|
1044
|
-
let run_len =
|
|
1042
|
+
let run_len = dt_read_varint(byte_array)
|
|
1045
1043
|
|
|
1046
1044
|
let parents = []
|
|
1047
1045
|
let has_more = 1
|
|
1048
1046
|
while (has_more) {
|
|
1049
|
-
let x =
|
|
1047
|
+
let x = dt_read_varint(byte_array)
|
|
1050
1048
|
let is_foreign = 0x1 & x
|
|
1051
1049
|
has_more = 0x2 & x
|
|
1052
1050
|
let num = x >> 2
|
|
@@ -1056,7 +1054,7 @@ function dt_parse(byte_array) {
|
|
|
1056
1054
|
} else if (!is_foreign) {
|
|
1057
1055
|
parents.push(versions[count - num])
|
|
1058
1056
|
} else {
|
|
1059
|
-
parents.push([agents[num - 1],
|
|
1057
|
+
parents.push([agents[num - 1], dt_read_varint(byte_array)])
|
|
1060
1058
|
}
|
|
1061
1059
|
}
|
|
1062
1060
|
parentss.push(parents)
|
|
@@ -1072,24 +1070,107 @@ function dt_parse(byte_array) {
|
|
|
1072
1070
|
}
|
|
1073
1071
|
}
|
|
1074
1072
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1073
|
+
return [agents, versions, parentss]
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function dt_get_local_version(bytes, version) {
|
|
1077
|
+
var looking_for = new Map()
|
|
1078
|
+
for (var event of version) {
|
|
1079
|
+
var [agent, seq] = decode_version(event)
|
|
1080
|
+
if (!looking_for.has(agent)) looking_for.set(agent, [])
|
|
1081
|
+
looking_for.get(agent).push(seq)
|
|
1077
1082
|
}
|
|
1083
|
+
for (var seqs of looking_for.values())
|
|
1084
|
+
seqs.sort((a, b) => a - b)
|
|
1078
1085
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1086
|
+
var byte_array = [...bytes]
|
|
1087
|
+
var local_version = []
|
|
1088
|
+
var local_version_base = 0
|
|
1089
|
+
|
|
1090
|
+
if (new TextDecoder().decode(new Uint8Array(byte_array.splice(0, 8))) !== "DMNDTYPS") throw new Error("dt parse error, expected DMNDTYPS")
|
|
1091
|
+
|
|
1092
|
+
if (byte_array.shift() != 0) throw new Error("dt parse error, expected version 0")
|
|
1093
|
+
|
|
1094
|
+
let agents = []
|
|
1095
|
+
|
|
1096
|
+
while (byte_array.length && looking_for.size) {
|
|
1097
|
+
let id = byte_array.shift()
|
|
1098
|
+
let len = dt_read_varint(byte_array)
|
|
1099
|
+
if (id == 1) {
|
|
1100
|
+
} else if (id == 3) {
|
|
1101
|
+
let goal = byte_array.length - len
|
|
1102
|
+
while (byte_array.length > goal) {
|
|
1103
|
+
agents.push(dt_read_string(byte_array))
|
|
1104
|
+
}
|
|
1105
|
+
} else if (id == 20) {
|
|
1106
|
+
} else if (id == 21) {
|
|
1107
|
+
let seqs = {}
|
|
1108
|
+
let goal = byte_array.length - len
|
|
1109
|
+
while (byte_array.length > goal && looking_for.size) {
|
|
1110
|
+
let part0 = dt_read_varint(byte_array)
|
|
1111
|
+
let has_jump = part0 & 1
|
|
1112
|
+
let agent_i = (part0 >> 1) - 1
|
|
1113
|
+
let run_length = dt_read_varint(byte_array)
|
|
1114
|
+
let jump = 0
|
|
1115
|
+
if (has_jump) {
|
|
1116
|
+
let part2 = dt_read_varint(byte_array)
|
|
1117
|
+
jump = part2 >> 1
|
|
1118
|
+
if (part2 & 1) jump *= -1
|
|
1119
|
+
}
|
|
1120
|
+
let base = (seqs[agent_i] || 0) + jump
|
|
1121
|
+
|
|
1122
|
+
var agent = agents[agent_i]
|
|
1123
|
+
looking_for_seqs = looking_for.get(agent)
|
|
1124
|
+
if (looking_for_seqs) {
|
|
1125
|
+
for (var seq of splice_out_range(
|
|
1126
|
+
looking_for_seqs, base, base + run_length - 1))
|
|
1127
|
+
local_version.push(local_version_base + (seq - base))
|
|
1128
|
+
if (!looking_for_seqs.length) looking_for.delete(agent)
|
|
1129
|
+
}
|
|
1130
|
+
local_version_base += run_length
|
|
1131
|
+
|
|
1132
|
+
seqs[agent_i] = base + run_length
|
|
1133
|
+
}
|
|
1134
|
+
} else {
|
|
1135
|
+
byte_array.splice(0, len)
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (looking_for.size) throw new Error(`version not found: ${version}`)
|
|
1140
|
+
return local_version
|
|
1084
1141
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1142
|
+
function splice_out_range(a, s, e) {
|
|
1143
|
+
if (!a?.length) return [];
|
|
1144
|
+
let l = 0, r = a.length;
|
|
1145
|
+
while (l < r) {
|
|
1146
|
+
const m = Math.floor((l + r) / 2);
|
|
1147
|
+
if (a[m] < s) l = m + 1; else r = m;
|
|
1148
|
+
}
|
|
1149
|
+
const i = l;
|
|
1150
|
+
l = i; r = a.length;
|
|
1151
|
+
while (l < r) {
|
|
1152
|
+
const m = Math.floor((l + r) / 2);
|
|
1153
|
+
if (a[m] <= e) l = m + 1; else r = m;
|
|
1089
1154
|
}
|
|
1155
|
+
return a.splice(i, l - i);
|
|
1090
1156
|
}
|
|
1157
|
+
}
|
|
1091
1158
|
|
|
1092
|
-
|
|
1159
|
+
function dt_read_string(byte_array) {
|
|
1160
|
+
return new TextDecoder().decode(new Uint8Array(byte_array.splice(0, dt_read_varint(byte_array))))
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function dt_read_varint(byte_array) {
|
|
1164
|
+
let result = 0
|
|
1165
|
+
let shift = 0
|
|
1166
|
+
while (true) {
|
|
1167
|
+
if (byte_array.length === 0) throw new Error("byte array does not contain varint")
|
|
1168
|
+
|
|
1169
|
+
let byte_val = byte_array.shift()
|
|
1170
|
+
result |= (byte_val & 0x7f) << shift
|
|
1171
|
+
if ((byte_val & 0x80) == 0) return result
|
|
1172
|
+
shift += 7
|
|
1173
|
+
}
|
|
1093
1174
|
}
|
|
1094
1175
|
|
|
1095
1176
|
function dt_create_bytes(version, parents, pos, del, ins) {
|
|
@@ -1808,8 +1889,10 @@ braid_text.get_files_for_key = get_files_for_key
|
|
|
1808
1889
|
braid_text.dt_get = dt_get
|
|
1809
1890
|
braid_text.dt_get_patches = dt_get_patches
|
|
1810
1891
|
braid_text.dt_parse = dt_parse
|
|
1892
|
+
braid_text.dt_get_local_version = dt_get_local_version
|
|
1811
1893
|
braid_text.dt_create_bytes = dt_create_bytes
|
|
1812
1894
|
|
|
1813
1895
|
braid_text.decode_version = decode_version
|
|
1896
|
+
braid_text.RangeSet = RangeSet
|
|
1814
1897
|
|
|
1815
1898
|
module.exports = braid_text
|
package/package.json
CHANGED
package/test/test.html
CHANGED
|
@@ -266,4 +266,34 @@ runTest(
|
|
|
266
266
|
'retried!'
|
|
267
267
|
)
|
|
268
268
|
|
|
269
|
+
runTest(
|
|
270
|
+
"test asking for a version that should and shouldn't be there",
|
|
271
|
+
async () => {
|
|
272
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
273
|
+
|
|
274
|
+
var r = await braid_fetch(`/${key}`, {
|
|
275
|
+
method: 'PUT',
|
|
276
|
+
version: ['hi-10'],
|
|
277
|
+
parents: [],
|
|
278
|
+
body: 'x'
|
|
279
|
+
})
|
|
280
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
281
|
+
|
|
282
|
+
var r = await braid_fetch(`/${key}`, {
|
|
283
|
+
method: 'HEAD',
|
|
284
|
+
version: ['hi-5']
|
|
285
|
+
})
|
|
286
|
+
if (r.ok) throw 'found version we should not have found'
|
|
287
|
+
|
|
288
|
+
var r = await braid_fetch(`/${key}`, {
|
|
289
|
+
method: 'HEAD',
|
|
290
|
+
version: ['hi-10']
|
|
291
|
+
})
|
|
292
|
+
if (!r.ok) throw 'could not find version we should have found'
|
|
293
|
+
|
|
294
|
+
return 'worked out!'
|
|
295
|
+
},
|
|
296
|
+
'worked out!'
|
|
297
|
+
)
|
|
298
|
+
|
|
269
299
|
</script>
|
package/test/test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
let { Doc } = require("diamond-types-node")
|
|
3
3
|
let braid_text = require('../index.js')
|
|
4
|
-
let {dt_get, dt_get_patches, dt_parse, dt_create_bytes} = braid_text
|
|
4
|
+
let {dt_get, dt_get_patches, dt_parse, dt_create_bytes, dt_get_local_version, RangeSet} = braid_text
|
|
5
5
|
|
|
6
6
|
process.on("unhandledRejection", (x) =>
|
|
7
7
|
console.log(`unhandledRejection: ${x.stack ?? x}`)
|
|
@@ -25,12 +25,117 @@ async function test_db() {
|
|
|
25
25
|
braid_text.db_folder = null
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
async function test_dt_get_local_version() {
|
|
29
|
+
for (var tt = 0; tt < 100; tt++) {
|
|
30
|
+
var st = Date.now()
|
|
31
|
+
|
|
32
|
+
var doc = new Doc('x')
|
|
33
|
+
var n = 100
|
|
34
|
+
var v_array = []
|
|
35
|
+
var p_array = []
|
|
36
|
+
var agents = ['hi']
|
|
37
|
+
var agent_rangesets = {}
|
|
38
|
+
|
|
39
|
+
for (var i = 0; i < n; i++) {
|
|
40
|
+
// console.log(`i = ${i}`)
|
|
41
|
+
|
|
42
|
+
// Math.randomSeed('seed::::' + i)
|
|
43
|
+
|
|
44
|
+
var agent = agents[0]
|
|
45
|
+
|
|
46
|
+
var seq = null
|
|
47
|
+
if (!agent_rangesets[agent]) agent_rangesets[agent] = new RangeSet()
|
|
48
|
+
var ranges = agent_rangesets[agent].ranges
|
|
49
|
+
if (!ranges.length) {
|
|
50
|
+
seq = Math.floor(Math.random() * 10)
|
|
51
|
+
} else {
|
|
52
|
+
var ii
|
|
53
|
+
if (ranges[0]?.[0] === 0)
|
|
54
|
+
ii = Math.floor(Math.random() * ranges.length)
|
|
55
|
+
else
|
|
56
|
+
ii = Math.floor(Math.random() * (ranges.length + 1)) - 1
|
|
57
|
+
var low = ii < 0 ? 0 : ranges[ii][1] + 1
|
|
58
|
+
var high = ii + 1 < ranges.length ? ranges[ii + 1][0] - 1 : ranges[ii][1] + 10
|
|
59
|
+
seq = Math.random() < 0.8 ? low : low + Math.floor(Math.random() * (high - low + 1))
|
|
60
|
+
}
|
|
61
|
+
agent_rangesets[agent].add_range(seq, seq)
|
|
62
|
+
v_array.push(`${agent}-${seq}`)
|
|
63
|
+
|
|
64
|
+
var parents = []
|
|
65
|
+
var shadow = new Set()
|
|
66
|
+
if (p_array.length) {
|
|
67
|
+
var starting_point = Math.floor(Math.random() * p_array.length)
|
|
68
|
+
parents.push(starting_point)
|
|
69
|
+
for (var p of p_array[starting_point]) shadow.add(p)
|
|
70
|
+
for (var ii = starting_point - 1; ii >= 0 && Math.random() < 0.9; ii--) {
|
|
71
|
+
if (!shadow.has(ii) && Math.random() < 0.5) {
|
|
72
|
+
parents.push(ii)
|
|
73
|
+
shadow.add(ii)
|
|
74
|
+
}
|
|
75
|
+
if (shadow.has(ii))
|
|
76
|
+
for (var p of p_array[ii])
|
|
77
|
+
shadow.add(p)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
p_array.push(parents)
|
|
81
|
+
parents = parents.map(p => v_array[p])
|
|
82
|
+
|
|
83
|
+
let args = [`${agent}-${seq}`, parents, 0, 0, 'x']
|
|
84
|
+
// console.log(args)
|
|
85
|
+
|
|
86
|
+
doc.mergeBytes(dt_create_bytes(...args))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
var bytes = doc.toBytes()
|
|
90
|
+
|
|
91
|
+
for (var t = 0; t < 100; t++) {
|
|
92
|
+
// if (t % 10 === 0) console.log(` t = ${t}`)
|
|
93
|
+
|
|
94
|
+
var version = []
|
|
95
|
+
var vv_array = [...v_array]
|
|
96
|
+
while (!version.length || (vv_array.length && Math.random() < 0.9)) {
|
|
97
|
+
version.push(vv_array.splice(Math.floor(Math.random() * vv_array.length), 1)[0])
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
var local_new = dt_get_local_version(bytes, version)
|
|
101
|
+
var local_old = old_dt_get_local_version(bytes, version)
|
|
102
|
+
|
|
103
|
+
if (local_new.some((x, i) => x != local_old[i])) {
|
|
104
|
+
throw new Error('bad!')
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(`[tt=${tt}] total T = ${Date.now() - st}`)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function old_dt_get_local_version(bytes, version) {
|
|
113
|
+
var [_agents, versions, _parentss] = dt_parse([...bytes])
|
|
114
|
+
|
|
115
|
+
var frontier = new Set(version)
|
|
116
|
+
|
|
117
|
+
var local_version = []
|
|
118
|
+
for (var i = 0; i < versions.length; i++) {
|
|
119
|
+
var v = versions[i].join("-")
|
|
120
|
+
if (frontier.has(v)) {
|
|
121
|
+
local_version.push(i)
|
|
122
|
+
frontier.delete(v)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (frontier.size) throw new Error(`version not found: ${version}`)
|
|
127
|
+
|
|
128
|
+
return local_version
|
|
129
|
+
}
|
|
130
|
+
|
|
28
131
|
async function main() {
|
|
29
132
|
let best_seed = NaN
|
|
30
133
|
let best_n = Infinity
|
|
31
134
|
let base = Math.floor(Math.random() * 10000000)
|
|
32
135
|
let st = Date.now()
|
|
33
136
|
|
|
137
|
+
await test_dt_get_local_version()
|
|
138
|
+
|
|
34
139
|
await test_db()
|
|
35
140
|
|
|
36
141
|
let og_log = console.log
|
|
@@ -38,7 +143,7 @@ async function main() {
|
|
|
38
143
|
for (let t = 0; t < 10000; t++) {
|
|
39
144
|
let seed = base + t
|
|
40
145
|
// for (let t = 0; t < 1; t++) {
|
|
41
|
-
|
|
146
|
+
// let seed = 403279
|
|
42
147
|
|
|
43
148
|
og_log(`t = ${t}, seed = ${seed}, best_n = ${best_n} @ ${best_seed}`)
|
|
44
149
|
Math.randomSeed(seed)
|