crdt library in ocaml implementing json-joy
1(** CRDT patch operations.
2
3 This module defines all 18 patch opcodes from json-joy with their full data
4 structures, matching the json-crdt-patch specification. *)
5
6(** {1 Core Types} *)
7
8type timestamp = Clock.timestamp
9(** A timestamp reference [sid, time] *)
10
11type timespan = Clock.timespan
12(** A timespan [sid, time, span] for delete operations *)
13
14type obj_entry = string * timestamp
15(** Object entry: key and value reference *)
16
17(** {1 Operation Types} *)
18
19type new_con = { con_value : Value.t }
20(** Create constant node - stores a JSON primitive value. The value can be null,
21 boolean, number (int/float), or string. *)
22
23type new_val = unit
24(** Create mutable value node - wrapper for a single reference. Initially points
25 to nothing, set via ins_val. *)
26
27type new_obj = unit
28(** Create object node - LWW-Map of string keys to node references. *)
29
30type new_vec = unit
31(** Create vector node - fixed-size tuple with positional slots. *)
32
33type new_str = unit
34(** Create string node - RGA of UTF-16 characters. *)
35
36type new_bin = unit
37(** Create binary node - RGA of bytes. *)
38
39type new_arr = unit
40(** Create array node - RGA of node references. *)
41
42type ins_val = {
43 ins_val_obj : timestamp; (** Target value node *)
44 ins_val_value : timestamp; (** Reference to set *)
45}
46(** Set value node reference.
47 @param obj The value node to modify
48 @param value Reference to the new value node *)
49
50type ins_obj = {
51 ins_obj_obj : timestamp; (** Target object node *)
52 ins_obj_value : obj_entry list; (** Key-value pairs *)
53}
54(** Insert/update object entries.
55 @param obj The object node to modify
56 @param value List of (key, node_ref) pairs to set *)
57
58type ins_vec = {
59 ins_vec_obj : timestamp; (** Target vector node *)
60 ins_vec_idx : int; (** Slot index *)
61 ins_vec_value : timestamp; (** Reference to set *)
62}
63(** Set vector slot.
64 @param obj The vector node to modify
65 @param idx Slot index (0-based)
66 @param value Reference to the node to set *)
67
68type ins_str = {
69 ins_str_obj : timestamp; (** Target string node *)
70 ins_str_after : timestamp; (** Insert after this position *)
71 ins_str_value : string; (** Text to insert *)
72}
73(** Insert text into string node (RGA insert).
74 @param obj The string node to modify
75 @param after Insert position (after this element)
76 @param value Text to insert (UTF-16) *)
77
78type ins_bin = {
79 ins_bin_obj : timestamp; (** Target binary node *)
80 ins_bin_after : timestamp; (** Insert after this position *)
81 ins_bin_value : bytes; (** Bytes to insert *)
82}
83(** Insert bytes into binary node (RGA insert).
84 @param obj The binary node to modify
85 @param after Insert position (after this element)
86 @param value Bytes to insert (base64 encoded in JSON) *)
87
88type ins_arr = {
89 ins_arr_obj : timestamp; (** Target array node *)
90 ins_arr_after : timestamp; (** Insert after this position *)
91 ins_arr_value : timestamp; (** Reference to insert *)
92}
93(** Insert element into array node (RGA insert).
94 @param obj The array node to modify
95 @param after Insert position (after this element)
96 @param value Reference to node to insert *)
97
98type upd_arr = {
99 upd_arr_obj : timestamp; (** Target array node *)
100 upd_arr_pos : timestamp; (** Position to update *)
101 upd_arr_value : timestamp; (** New reference *)
102}
103(** Update array element (overwrite at existing position).
104 @param obj The array node to modify
105 @param pos Element position to update
106 @param value New reference to set *)
107
108type del = {
109 del_obj : timestamp; (** Target node *)
110 del_what : timespan list; (** Ranges to delete *)
111}
112(** Delete ranges from an RGA (string, binary, or array).
113 @param obj The node to delete from
114 @param what List of timespans to delete *)
115
116type nop = { nop_len : int }
117(** No-op operation - used for padding/alignment.
118 @param len Number of IDs consumed by this nop *)
119
120(** {1 Operation Sum Type} *)
121
122(** An operation with all its data *)
123type op_data =
124 | Op_new_con of new_con (** Create constant *)
125 | Op_new_val (** Create value node *)
126 | Op_new_obj (** Create object node *)
127 | Op_new_vec (** Create vector node *)
128 | Op_new_str (** Create string node *)
129 | Op_new_bin (** Create binary node *)
130 | Op_new_arr (** Create array node *)
131 | Op_ins_val of ins_val (** Set value reference *)
132 | Op_ins_obj of ins_obj (** Insert object entries *)
133 | Op_ins_vec of ins_vec (** Set vector slot *)
134 | Op_ins_str of ins_str (** Insert string text *)
135 | Op_ins_bin of ins_bin (** Insert binary data *)
136 | Op_ins_arr of ins_arr (** Insert array element *)
137 | Op_upd_arr of upd_arr (** Update array element *)
138 | Op_del of del (** Delete ranges *)
139 | Op_nop of nop (** No-op *)
140
141(** {1 Opcode Enumeration} *)
142
143(** Operation opcodes (numeric encoding) *)
144type opcode =
145 | New_con (** 0: Create constant node *)
146 | New_val (** 1: Create mutable value node *)
147 | New_obj (** 2: Create object node *)
148 | New_vec (** 3: Create vector node *)
149 | New_str (** 4: Create string node *)
150 | New_bin (** 5: Create binary node *)
151 | New_arr (** 6: Create array node *)
152 | Ins_val (** 7: Set value node reference *)
153 | Ins_obj (** 8: Insert/update object key *)
154 | Ins_vec (** 9: Set vector slot *)
155 | Ins_str (** 10: Insert text into string *)
156 | Ins_bin (** 11: Insert bytes into binary *)
157 | Ins_arr (** 12: Insert element into array *)
158 | Upd_arr (** 13: Update array element *)
159 | Del (** 14: Delete range *)
160 | Nop (** 15: No-op *)
161
162(** Get the opcode number *)
163let opcode_to_int = function
164 | New_con -> 0
165 | New_val -> 1
166 | New_obj -> 2
167 | New_vec -> 3
168 | New_str -> 4
169 | New_bin -> 5
170 | New_arr -> 6
171 | Ins_val -> 7
172 | Ins_obj -> 8
173 | Ins_vec -> 9
174 | Ins_str -> 10
175 | Ins_bin -> 11
176 | Ins_arr -> 12
177 | Upd_arr -> 13
178 | Del -> 14
179 | Nop -> 15
180
181(** Get opcode from number *)
182let int_to_opcode = function
183 | 0 -> Some New_con
184 | 1 -> Some New_val
185 | 2 -> Some New_obj
186 | 3 -> Some New_vec
187 | 4 -> Some New_str
188 | 5 -> Some New_bin
189 | 6 -> Some New_arr
190 | 7 -> Some Ins_val
191 | 8 -> Some Ins_obj
192 | 9 -> Some Ins_vec
193 | 10 -> Some Ins_str
194 | 11 -> Some Ins_bin
195 | 12 -> Some Ins_arr
196 | 13 -> Some Upd_arr
197 | 14 -> Some Del
198 | 15 -> Some Nop
199 | _ -> None
200
201(** Get the opcode of an operation *)
202let opcode_of_op = function
203 | Op_new_con _ -> New_con
204 | Op_new_val -> New_val
205 | Op_new_obj -> New_obj
206 | Op_new_vec -> New_vec
207 | Op_new_str -> New_str
208 | Op_new_bin -> New_bin
209 | Op_new_arr -> New_arr
210 | Op_ins_val _ -> Ins_val
211 | Op_ins_obj _ -> Ins_obj
212 | Op_ins_vec _ -> Ins_vec
213 | Op_ins_str _ -> Ins_str
214 | Op_ins_bin _ -> Ins_bin
215 | Op_ins_arr _ -> Ins_arr
216 | Op_upd_arr _ -> Upd_arr
217 | Op_del _ -> Del
218 | Op_nop _ -> Nop
219
220(** Get the operation name (string) *)
221let opcode_name = function
222 | New_con -> "new_con"
223 | New_val -> "new_val"
224 | New_obj -> "new_obj"
225 | New_vec -> "new_vec"
226 | New_str -> "new_str"
227 | New_bin -> "new_bin"
228 | New_arr -> "new_arr"
229 | Ins_val -> "ins_val"
230 | Ins_obj -> "ins_obj"
231 | Ins_vec -> "ins_vec"
232 | Ins_str -> "ins_str"
233 | Ins_bin -> "ins_bin"
234 | Ins_arr -> "ins_arr"
235 | Upd_arr -> "upd_arr"
236 | Del -> "del"
237 | Nop -> "nop"
238
239(** Get opcode from name *)
240let opcode_of_name = function
241 | "new_con" -> Some New_con
242 | "new_val" -> Some New_val
243 | "new_obj" -> Some New_obj
244 | "new_vec" -> Some New_vec
245 | "new_str" -> Some New_str
246 | "new_bin" -> Some New_bin
247 | "new_arr" -> Some New_arr
248 | "ins_val" -> Some Ins_val
249 | "ins_obj" -> Some Ins_obj
250 | "ins_vec" -> Some Ins_vec
251 | "ins_str" -> Some Ins_str
252 | "ins_bin" -> Some Ins_bin
253 | "ins_arr" -> Some Ins_arr
254 | "upd_arr" -> Some Upd_arr
255 | "del" -> Some Del
256 | "nop" -> Some Nop
257 | _ -> None
258
259(** Get the operation name from op_data *)
260let op_name op = opcode_name (opcode_of_op op)
261
262(** {1 ID Span Calculation} *)
263
264(** Calculate how many IDs an operation consumes. Most operations use exactly 1
265 ID, but some use more:
266 - new_con: 1
267 - new_val/new_obj/new_vec/new_str/new_bin/new_arr: 1
268 - ins_val: 1
269 - ins_obj: 1 (the entries reference existing IDs)
270 - ins_vec: 1
271 - ins_str: length of string (UTF-16 code units)
272 - ins_bin: length of bytes
273 - ins_arr: 1
274 - upd_arr: 1
275 - del: 1
276 - nop: nop_len *)
277let id_span = function
278 | Op_new_con _ -> 1
279 | Op_new_val -> 1
280 | Op_new_obj -> 1
281 | Op_new_vec -> 1
282 | Op_new_str -> 1
283 | Op_new_bin -> 1
284 | Op_new_arr -> 1
285 | Op_ins_val _ -> 1
286 | Op_ins_obj _ -> 1
287 | Op_ins_vec _ -> 1
288 | Op_ins_str { ins_str_value; _ } ->
289 (* UTF-16 code units - for ASCII this equals string length *)
290 String.length ins_str_value
291 | Op_ins_bin { ins_bin_value; _ } -> Bytes.length ins_bin_value
292 | Op_ins_arr _ -> 1
293 | Op_upd_arr _ -> 1
294 | Op_del _ -> 1
295 | Op_nop { nop_len } -> nop_len
296
297(** {1 Constructors} *)
298
299(** Create a new_con operation *)
300let make_new_con value = Op_new_con { con_value = value }
301
302(** Create a new_val operation *)
303let make_new_val () = Op_new_val
304
305(** Create a new_obj operation *)
306let make_new_obj () = Op_new_obj
307
308(** Create a new_vec operation *)
309let make_new_vec () = Op_new_vec
310
311(** Create a new_str operation *)
312let make_new_str () = Op_new_str
313
314(** Create a new_bin operation *)
315let make_new_bin () = Op_new_bin
316
317(** Create a new_arr operation *)
318let make_new_arr () = Op_new_arr
319
320(** Create an ins_val operation *)
321let make_ins_val ~obj ~value =
322 Op_ins_val { ins_val_obj = obj; ins_val_value = value }
323
324(** Create an ins_obj operation *)
325let make_ins_obj ~obj ~entries =
326 Op_ins_obj { ins_obj_obj = obj; ins_obj_value = entries }
327
328(** Create an ins_vec operation *)
329let make_ins_vec ~obj ~idx ~value =
330 Op_ins_vec { ins_vec_obj = obj; ins_vec_idx = idx; ins_vec_value = value }
331
332(** Create an ins_str operation *)
333let make_ins_str ~obj ~after ~value =
334 Op_ins_str { ins_str_obj = obj; ins_str_after = after; ins_str_value = value }
335
336(** Create an ins_bin operation *)
337let make_ins_bin ~obj ~after ~value =
338 Op_ins_bin { ins_bin_obj = obj; ins_bin_after = after; ins_bin_value = value }
339
340(** Create an ins_arr operation *)
341let make_ins_arr ~obj ~after ~value =
342 Op_ins_arr { ins_arr_obj = obj; ins_arr_after = after; ins_arr_value = value }
343
344(** Create an upd_arr operation *)
345let make_upd_arr ~obj ~pos ~value =
346 Op_upd_arr { upd_arr_obj = obj; upd_arr_pos = pos; upd_arr_value = value }
347
348(** Create a del operation *)
349let make_del ~obj ~what = Op_del { del_obj = obj; del_what = what }
350
351(** Create a nop operation *)
352let make_nop len = Op_nop { nop_len = len }
353
354(** {1 Legacy compatibility} *)
355
356type t = { id : timestamp; op : op_data }
357(** Legacy type alias - an operation with its ID *)
358
359(** Get operation name from legacy type *)
360let name { op; _ } = op_name op