braid-text 0.2.23 → 0.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +106 -33
  2. package/package.json +1 -1
  3. package/test/test.js +107 -2
package/index.js CHANGED
@@ -800,20 +800,8 @@ function dt_len(doc, version) {
800
800
  function dt_get_string(doc, version) {
801
801
  var bytes = doc.toBytes()
802
802
  var oplog = OpLog.fromBytes(bytes)
803
- var [_agents, versions, _parentss] = dt_parse([...bytes])
804
803
 
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
-
816
- if (frontier.size) throw new Error(`version not found: ${version}`)
804
+ var local_version = dt_get_local_version(bytes, version)
817
805
 
818
806
  var b = new Branch()
819
807
  b.merge(oplog, new Uint32Array(local_version))
@@ -1008,25 +996,25 @@ function dt_parse(byte_array) {
1008
996
 
1009
997
  while (byte_array.length) {
1010
998
  let id = byte_array.shift()
1011
- let len = read_varint(byte_array)
999
+ let len = dt_read_varint(byte_array)
1012
1000
  if (id == 1) {
1013
1001
  } else if (id == 3) {
1014
1002
  let goal = byte_array.length - len
1015
1003
  while (byte_array.length > goal) {
1016
- agents.push(read_string(byte_array))
1004
+ agents.push(dt_read_string(byte_array))
1017
1005
  }
1018
1006
  } else if (id == 20) {
1019
1007
  } else if (id == 21) {
1020
1008
  let seqs = {}
1021
1009
  let goal = byte_array.length - len
1022
1010
  while (byte_array.length > goal) {
1023
- let part0 = read_varint(byte_array)
1011
+ let part0 = dt_read_varint(byte_array)
1024
1012
  let has_jump = part0 & 1
1025
1013
  let agent_i = (part0 >> 1) - 1
1026
- let run_length = read_varint(byte_array)
1014
+ let run_length = dt_read_varint(byte_array)
1027
1015
  let jump = 0
1028
1016
  if (has_jump) {
1029
- let part2 = read_varint(byte_array)
1017
+ let part2 = dt_read_varint(byte_array)
1030
1018
  jump = part2 >> 1
1031
1019
  if (part2 & 1) jump *= -1
1032
1020
  }
@@ -1041,12 +1029,12 @@ function dt_parse(byte_array) {
1041
1029
  let count = 0
1042
1030
  let goal = byte_array.length - len
1043
1031
  while (byte_array.length > goal) {
1044
- let run_len = read_varint(byte_array)
1032
+ let run_len = dt_read_varint(byte_array)
1045
1033
 
1046
1034
  let parents = []
1047
1035
  let has_more = 1
1048
1036
  while (has_more) {
1049
- let x = read_varint(byte_array)
1037
+ let x = dt_read_varint(byte_array)
1050
1038
  let is_foreign = 0x1 & x
1051
1039
  has_more = 0x2 & x
1052
1040
  let num = x >> 2
@@ -1056,7 +1044,7 @@ function dt_parse(byte_array) {
1056
1044
  } else if (!is_foreign) {
1057
1045
  parents.push(versions[count - num])
1058
1046
  } else {
1059
- parents.push([agents[num - 1], read_varint(byte_array)])
1047
+ parents.push([agents[num - 1], dt_read_varint(byte_array)])
1060
1048
  }
1061
1049
  }
1062
1050
  parentss.push(parents)
@@ -1072,24 +1060,107 @@ function dt_parse(byte_array) {
1072
1060
  }
1073
1061
  }
1074
1062
 
1075
- function read_string(byte_array) {
1076
- return new TextDecoder().decode(new Uint8Array(byte_array.splice(0, read_varint(byte_array))))
1063
+ return [agents, versions, parentss]
1064
+ }
1065
+
1066
+ function dt_get_local_version(bytes, version) {
1067
+ var looking_for = new Map()
1068
+ for (var event of version) {
1069
+ var [agent, seq] = decode_version(event)
1070
+ if (!looking_for.has(agent)) looking_for.set(agent, [])
1071
+ looking_for.get(agent).push(seq)
1077
1072
  }
1073
+ for (var seqs of looking_for.values())
1074
+ seqs.sort((a, b) => a - b)
1078
1075
 
1079
- function read_varint(byte_array) {
1080
- let result = 0
1081
- let shift = 0
1082
- while (true) {
1083
- if (byte_array.length === 0) throw new Error("byte array does not contain varint")
1076
+ var byte_array = [...bytes]
1077
+ var local_version = []
1078
+ var local_version_base = 0
1079
+
1080
+ if (new TextDecoder().decode(new Uint8Array(byte_array.splice(0, 8))) !== "DMNDTYPS") throw new Error("dt parse error, expected DMNDTYPS")
1081
+
1082
+ if (byte_array.shift() != 0) throw new Error("dt parse error, expected version 0")
1083
+
1084
+ let agents = []
1085
+
1086
+ while (byte_array.length && looking_for.size) {
1087
+ let id = byte_array.shift()
1088
+ let len = dt_read_varint(byte_array)
1089
+ if (id == 1) {
1090
+ } else if (id == 3) {
1091
+ let goal = byte_array.length - len
1092
+ while (byte_array.length > goal) {
1093
+ agents.push(dt_read_string(byte_array))
1094
+ }
1095
+ } else if (id == 20) {
1096
+ } else if (id == 21) {
1097
+ let seqs = {}
1098
+ let goal = byte_array.length - len
1099
+ while (byte_array.length > goal && looking_for.size) {
1100
+ let part0 = dt_read_varint(byte_array)
1101
+ let has_jump = part0 & 1
1102
+ let agent_i = (part0 >> 1) - 1
1103
+ let run_length = dt_read_varint(byte_array)
1104
+ let jump = 0
1105
+ if (has_jump) {
1106
+ let part2 = dt_read_varint(byte_array)
1107
+ jump = part2 >> 1
1108
+ if (part2 & 1) jump *= -1
1109
+ }
1110
+ let base = (seqs[agent_i] || 0) + jump
1111
+
1112
+ var agent = agents[agent_i]
1113
+ looking_for_seqs = looking_for.get(agent)
1114
+ if (looking_for_seqs) {
1115
+ for (var seq of splice_out_range(
1116
+ looking_for_seqs, base, base + run_length - 1))
1117
+ local_version.push(local_version_base + (seq - base))
1118
+ if (!looking_for_seqs.length) looking_for.delete(agent)
1119
+ }
1120
+ local_version_base += run_length
1084
1121
 
1085
- let byte_val = byte_array.shift()
1086
- result |= (byte_val & 0x7f) << shift
1087
- if ((byte_val & 0x80) == 0) return result
1088
- shift += 7
1122
+ seqs[agent_i] = base + run_length
1123
+ }
1124
+ } else {
1125
+ byte_array.splice(0, len)
1089
1126
  }
1090
1127
  }
1091
1128
 
1092
- return [agents, versions, parentss]
1129
+ if (looking_for.size) throw new Error(`version not found: ${version}`)
1130
+ return local_version
1131
+
1132
+ function splice_out_range(a, s, e) {
1133
+ if (!a?.length) return [];
1134
+ let l = 0, r = a.length;
1135
+ while (l < r) {
1136
+ const m = Math.floor((l + r) / 2);
1137
+ if (a[m] < s) l = m + 1; else r = m;
1138
+ }
1139
+ const i = l;
1140
+ l = i; r = a.length;
1141
+ while (l < r) {
1142
+ const m = Math.floor((l + r) / 2);
1143
+ if (a[m] <= e) l = m + 1; else r = m;
1144
+ }
1145
+ return a.splice(i, l - i);
1146
+ }
1147
+ }
1148
+
1149
+ function dt_read_string(byte_array) {
1150
+ return new TextDecoder().decode(new Uint8Array(byte_array.splice(0, dt_read_varint(byte_array))))
1151
+ }
1152
+
1153
+ function dt_read_varint(byte_array) {
1154
+ let result = 0
1155
+ let shift = 0
1156
+ while (true) {
1157
+ if (byte_array.length === 0) throw new Error("byte array does not contain varint")
1158
+
1159
+ let byte_val = byte_array.shift()
1160
+ result |= (byte_val & 0x7f) << shift
1161
+ if ((byte_val & 0x80) == 0) return result
1162
+ shift += 7
1163
+ }
1093
1164
  }
1094
1165
 
1095
1166
  function dt_create_bytes(version, parents, pos, del, ins) {
@@ -1808,8 +1879,10 @@ braid_text.get_files_for_key = get_files_for_key
1808
1879
  braid_text.dt_get = dt_get
1809
1880
  braid_text.dt_get_patches = dt_get_patches
1810
1881
  braid_text.dt_parse = dt_parse
1882
+ braid_text.dt_get_local_version = dt_get_local_version
1811
1883
  braid_text.dt_create_bytes = dt_create_bytes
1812
1884
 
1813
1885
  braid_text.decode_version = decode_version
1886
+ braid_text.RangeSet = RangeSet
1814
1887
 
1815
1888
  module.exports = braid_text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
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
- // let seed = 2746153
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)