crdt library in ocaml implementing json-joy
at main 11 kB view raw
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