Persistent store with Git semantics: lazy reads, delayed writes, content-addressing
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(lint): resolve E305 module naming convention across packages

Rename CamelCase internal modules to Snake_case per OCaml convention:
- ocaml-hap: TlvType→Tlv_type, HapError→Hap_error, CharType→Char_type
- ocaml-cookeio: SameSite→Same_site, DateParser→Date_parser
- ocaml-claudeio: PreToolUse→Pre_tool_use, PostToolUse→Post_tool_use,
UserPromptSubmit→User_prompt_submit, SubagentStop→Subagent_stop,
PreCompact→Pre_compact (wire format strings preserved)
- irmin: StringMap→String_map, PathSet→Path_set
- ocaml-block: ReadOnly→Read_only, WithCrc→With_crc

+71 -49
+42 -41
lib/backend.ml
··· 14 14 type stats = { reads : int; writes : int; cache_hits : int; cache_misses : int } 15 15 16 16 module Memory = struct 17 - module StringMap = Map.Make (String) 17 + module String_map = Map.Make (String) 18 18 19 19 type 'hash state = { 20 - mutable objects : string StringMap.t; 21 - mutable refs : 'hash StringMap.t; 20 + mutable objects : string String_map.t; 21 + mutable refs : 'hash String_map.t; 22 22 to_hex : 'hash -> string; 23 23 equal : 'hash -> 'hash -> bool; 24 24 } ··· 26 26 let create_with_hash (type h) (to_hex : h -> string) (equal : h -> h -> bool) 27 27 : h t = 28 28 let state = 29 - { objects = StringMap.empty; refs = StringMap.empty; to_hex; equal } 29 + { objects = String_map.empty; refs = String_map.empty; to_hex; equal } 30 30 in 31 31 { 32 32 read = 33 33 (fun h -> 34 34 let key = state.to_hex h in 35 - StringMap.find_opt key state.objects); 35 + String_map.find_opt key state.objects); 36 36 write = 37 37 (fun h data -> 38 38 let key = state.to_hex h in 39 - state.objects <- StringMap.add key data state.objects); 39 + state.objects <- String_map.add key data state.objects); 40 40 exists = 41 41 (fun h -> 42 42 let key = state.to_hex h in 43 - StringMap.mem key state.objects); 44 - get_ref = (fun name -> StringMap.find_opt name state.refs); 43 + String_map.mem key state.objects); 44 + get_ref = (fun name -> String_map.find_opt name state.refs); 45 45 set_ref = 46 - (fun name hash -> state.refs <- StringMap.add name hash state.refs); 46 + (fun name hash -> state.refs <- String_map.add name hash state.refs); 47 47 test_and_set_ref = 48 48 (fun name ~test ~set -> 49 - let current = StringMap.find_opt name state.refs in 49 + let current = String_map.find_opt name state.refs in 50 50 let matches = 51 51 match (test, current) with 52 52 | None, None -> true ··· 55 55 in 56 56 if matches then ( 57 57 (match set with 58 - | None -> state.refs <- StringMap.remove name state.refs 59 - | Some h -> state.refs <- StringMap.add name h state.refs); 58 + | None -> state.refs <- String_map.remove name state.refs 59 + | Some h -> state.refs <- String_map.add name h state.refs); 60 60 true) 61 61 else false); 62 - list_refs = (fun () -> StringMap.bindings state.refs |> List.map fst); 62 + list_refs = (fun () -> String_map.bindings state.refs |> List.map fst); 63 63 write_batch = 64 64 (fun objects -> 65 65 List.iter 66 66 (fun (h, data) -> 67 67 let key = state.to_hex h in 68 - state.objects <- StringMap.add key data state.objects) 68 + state.objects <- String_map.add key data state.objects) 69 69 objects); 70 70 flush = (fun () -> ()); 71 71 close = (fun () -> ()); ··· 169 169 170 170 Inspired by lavyek's append-only design and LevelDB's WAL pattern. *) 171 171 module Disk = struct 172 - module StringMap = Map.Make (String) 172 + module String_map = Map.Make (String) 173 173 174 174 type index_entry = { offset : int; length : int } 175 175 ··· 178 178 mutable wal : Wal.t option; 179 179 mutable data_file : Eio.File.rw_ty Eio.Resource.t option; 180 180 mutable data_offset : int; 181 - mutable index : index_entry StringMap.t; 181 + mutable index : index_entry String_map.t; 182 182 bloom : string Bloom.t; 183 - mutable refs : 'hash StringMap.t; 183 + mutable refs : 'hash String_map.t; 184 184 to_hex : 'hash -> string; 185 185 equal : 'hash -> 'hash -> bool; 186 186 mutex : Eio.Mutex.t; ··· 208 208 | [ hex; off_s; len_s ] -> 209 209 let offset = int_of_string off_s in 210 210 let length = int_of_string len_s in 211 - StringMap.add hex { offset; length } idx 211 + String_map.add hex { offset; length } idx 212 212 | _ -> idx) 213 - StringMap.empty 214 - else StringMap.empty 213 + String_map.empty 214 + else String_map.empty 215 215 216 216 let save_index root index = 217 217 let path = index_path root in 218 218 let tmp_path = Eio.Path.(root / "objects.idx.tmp") in 219 219 let content = 220 - StringMap.fold 220 + String_map.fold 221 221 (fun hex entry acc -> 222 222 Printf.sprintf "%s %d %d\n" hex entry.offset entry.length :: acc) 223 223 index [] ··· 244 244 let load_ref of_hex acc full_name entry_path = 245 245 let hex = String.trim (Eio.Path.load entry_path) in 246 246 match of_hex hex with 247 - | Ok hash -> StringMap.add full_name hash acc 247 + | Ok hash -> String_map.add full_name hash acc 248 248 | Error _ -> acc 249 249 250 250 let load_refs root of_hex = 251 251 let refs_root = refs_path root in 252 - if not (Eio.Path.is_directory refs_root) then StringMap.empty 252 + if not (Eio.Path.is_directory refs_root) then String_map.empty 253 253 else 254 254 let rec scan_dir prefix path acc = 255 255 let entries = Eio.Path.read_dir path in ··· 264 264 else acc) 265 265 acc entries 266 266 in 267 - scan_dir "" refs_root StringMap.empty 267 + scan_dir "" refs_root String_map.empty 268 268 269 269 let save_ref root name hash to_hex = 270 270 let path = refs_path root in ··· 318 318 match decode_wal_record record with 319 319 | None -> (idx, blm, offset) 320 320 | Some (hex, data) -> 321 - if StringMap.mem hex idx then (idx, blm, offset) 321 + if String_map.mem hex idx then (idx, blm, offset) 322 322 else begin 323 323 (* Write to data file *) 324 324 let len = String.length data in 325 325 Eio.File.pwrite_all data_file 326 326 ~file_offset:(Optint.Int63.of_int offset) 327 327 [ Cstruct.of_string data ]; 328 - let idx' = StringMap.add hex { offset; length = len } idx in 328 + let idx' = String_map.add hex { offset; length = len } idx in 329 329 Bloom.add blm hex; 330 330 (idx', blm, offset + len) 331 331 end) ··· 342 342 let bloom = load_bloom root in 343 343 (* Populate bloom from index if empty (first load after upgrade) *) 344 344 if Bloom.size_estimate bloom = 0 then 345 - StringMap.iter (fun hex _ -> Bloom.add bloom hex) index; 345 + String_map.iter (fun hex _ -> Bloom.add bloom hex) index; 346 346 let refs = load_refs root of_hex in 347 347 let file, offset = open_data_file ~sw root in 348 348 let data_file = (file :> Eio.File.rw_ty Eio.Resource.t) in ··· 368 368 read = 369 369 (fun h -> 370 370 let key = state.to_hex h in 371 - match StringMap.find_opt key state.index with 371 + match String_map.find_opt key state.index with 372 372 | None -> None 373 373 | Some entry -> ( 374 374 match state.data_file with ··· 384 384 Eio.Mutex.use_rw ~protect:true state.mutex (fun () -> 385 385 let key = state.to_hex h in 386 386 (* Fast path: bloom filter says "definitely not present" *) 387 - if Bloom.mem state.bloom key && StringMap.mem key state.index then 388 - () 387 + if Bloom.mem state.bloom key && String_map.mem key state.index 388 + then () 389 389 else 390 390 match (state.wal, state.data_file) with 391 391 | Some wal, Some file -> ··· 400 400 [ Cstruct.of_string data ]; 401 401 state.data_offset <- offset + len; 402 402 state.index <- 403 - StringMap.add key { offset; length = len } state.index; 403 + String_map.add key { offset; length = len } state.index; 404 404 Bloom.add state.bloom key 405 405 | _ -> ())); 406 406 exists = 407 407 (fun h -> 408 408 let key = state.to_hex h in 409 409 (* Fast path: bloom filter for negative lookups *) 410 - Bloom.mem state.bloom key && StringMap.mem key state.index); 411 - get_ref = (fun name -> StringMap.find_opt name state.refs); 410 + Bloom.mem state.bloom key && String_map.mem key state.index); 411 + get_ref = (fun name -> String_map.find_opt name state.refs); 412 412 set_ref = 413 413 (fun name hash -> 414 414 Eio.Mutex.use_rw ~protect:true state.mutex (fun () -> 415 - state.refs <- StringMap.add name hash state.refs; 415 + state.refs <- String_map.add name hash state.refs; 416 416 save_ref state.root name hash state.to_hex)); 417 417 test_and_set_ref = 418 418 (fun name ~test ~set -> 419 419 Eio.Mutex.use_rw ~protect:true state.mutex (fun () -> 420 - let current = StringMap.find_opt name state.refs in 420 + let current = String_map.find_opt name state.refs in 421 421 let matches = 422 422 match (test, current) with 423 423 | None, None -> true ··· 427 427 if matches then begin 428 428 (match set with 429 429 | None -> 430 - state.refs <- StringMap.remove name state.refs; 430 + state.refs <- String_map.remove name state.refs; 431 431 delete_ref state.root name 432 432 | Some h -> 433 - state.refs <- StringMap.add name h state.refs; 433 + state.refs <- String_map.add name h state.refs; 434 434 save_ref state.root name h state.to_hex); 435 435 true 436 436 end 437 437 else false)); 438 - list_refs = (fun () -> StringMap.bindings state.refs |> List.map fst); 438 + list_refs = (fun () -> String_map.bindings state.refs |> List.map fst); 439 439 write_batch = 440 440 (fun objects -> 441 441 Eio.Mutex.use_rw ~protect:true state.mutex (fun () -> ··· 445 445 List.iter 446 446 (fun (h, data) -> 447 447 let key = state.to_hex h in 448 - if not (StringMap.mem key state.index) then 448 + if not (String_map.mem key state.index) then 449 449 Wal.append wal (encode_wal_record key data)) 450 450 objects; 451 451 Wal.sync wal; ··· 453 453 List.iter 454 454 (fun (h, data) -> 455 455 let key = state.to_hex h in 456 - if StringMap.mem key state.index then () 456 + if String_map.mem key state.index then () 457 457 else begin 458 458 let len = String.length data in 459 459 let offset = state.data_offset in ··· 462 462 [ Cstruct.of_string data ]; 463 463 state.data_offset <- offset + len; 464 464 state.index <- 465 - StringMap.add key { offset; length = len } state.index; 465 + String_map.add key { offset; length = len } 466 + state.index; 466 467 Bloom.add state.bloom key 467 468 end) 468 469 objects
+10 -8
lib/proof.ml
··· 43 43 type contents = string 44 44 45 45 (* Path set for tracking accessed paths *) 46 - module PathSet = Set.Make (struct 46 + module Path_set = Set.Make (struct 47 47 type t = string list 48 48 49 49 let compare = compare ··· 54 54 | Producing of { 55 55 backend : hash Backend.t; 56 56 node_hash : hash; 57 - mutable accessed : PathSet.t; 57 + mutable accessed : Path_set.t; 58 58 } 59 59 | From_proof of { tree : (hash, contents) tree } 60 60 ··· 62 62 type t = { state : tree_state } 63 63 64 64 let of_hash backend h = 65 - { state = Producing { backend; node_hash = h; accessed = PathSet.empty } } 65 + { 66 + state = Producing { backend; node_hash = h; accessed = Path_set.empty }; 67 + } 66 68 67 69 let of_proof_tree tree = { state = From_proof { tree } } 68 70 ··· 79 81 (* Record access to a path *) 80 82 let record_access t path = 81 83 match t.state with 82 - | Producing p -> p.accessed <- PathSet.add path p.accessed 84 + | Producing p -> p.accessed <- Path_set.add path p.accessed 83 85 | From_proof _ -> () 84 86 85 87 (* Navigate to a path in a backend-stored node *) ··· 337 339 let build_proof_tree backend node_hash accessed = 338 340 let rec build node prefix = 339 341 let dominated_by_access = 340 - PathSet.exists 342 + Path_set.exists 341 343 (fun path -> 342 344 let plen = List.length prefix in 343 345 List.length path >= plen ··· 354 356 let child_tree = 355 357 match kind with 356 358 | `Contents h -> 357 - if PathSet.mem child_path accessed then 359 + if Path_set.mem child_path accessed then 358 360 match backend.Backend.read h with 359 361 | Some c -> Contents c 360 362 | None -> Blinded_contents h 361 363 else Blinded_contents h 362 364 | `Node h -> 363 365 if 364 - PathSet.exists 366 + Path_set.exists 365 367 (fun p -> 366 368 let clen = List.length child_path in 367 369 List.length p >= clen ··· 395 397 let accessed = 396 398 match tree.state with 397 399 | Producing { accessed; _ } -> accessed 398 - | From_proof _ -> PathSet.empty 400 + | From_proof _ -> Path_set.empty 399 401 in 400 402 let proof_tree = build_proof_tree backend root_hash accessed in 401 403 let proof =
+19
lib/proof.mli
··· 78 78 (** Proof-aware tree that records accesses during [produce]. *) 79 79 80 80 val find : t -> string list -> contents option 81 + (** [find t path] returns the contents at [path], or [None] if not found. *) 82 + 81 83 val find_tree : t -> string list -> t option 84 + (** [find_tree t path] returns the subtree at [path], or [None] if not 85 + found. *) 86 + 82 87 val mem : t -> string list -> bool 88 + (** [mem t path] checks whether a value exists at [path]. *) 89 + 83 90 val list : t -> string list -> (string * [ `Node | `Contents ]) list 91 + (** [list t path] returns the children of [path], each tagged as either a 92 + node or contents. *) 93 + 84 94 val add : t -> string list -> contents -> t 95 + (** [add t path contents] returns a new tree with contents added or updated 96 + at [path]. *) 97 + 85 98 val add_tree : t -> string list -> t -> t 99 + (** [add_tree t path subtree] returns a new tree with [subtree] grafted at 100 + [path]. *) 101 + 86 102 val remove : t -> string list -> t 103 + (** [remove t path] returns a new tree with the value at [path] removed. *) 104 + 87 105 val hash : t -> hash 106 + (** [hash t] computes the root hash of the tree. *) 88 107 end 89 108 90 109 val produce :