(** CIDv1 computation — pure OCaml, WASM-safe. Multihash + CIDv1 + multibase base32-lower (RFC 4648, no pad, multibase prefix 'b'). Codecs: dag-cbor 0x71, raw 0x55. Hash codes: sha2-256 0x12, sha3-256 0x16. Reference: the multiformats specs (unsigned-varint, multihash, cid, multibase). No deps. *) open Sx_types (* Unsigned LEB128 (multiformats unsigned-varint). *) let varint (n : int) : string = let buf = Buffer.create 4 in let n = ref n in let cont = ref true in while !cont do let b = !n land 0x7f in n := !n lsr 7; if !n = 0 then (Buffer.add_char buf (Char.chr b); cont := false) else Buffer.add_char buf (Char.chr (b lor 0x80)) done; Buffer.contents buf (* RFC 4648 base32 lowercase, no padding. *) let b32_alpha = "abcdefghijklmnopqrstuvwxyz234567" let base32_lower (s : string) : string = let buf = Buffer.create ((String.length s * 8 + 4) / 5) in let acc = ref 0 and bits = ref 0 in String.iter (fun c -> acc := (!acc lsl 8) lor (Char.code c); bits := !bits + 8; while !bits >= 5 do bits := !bits - 5; Buffer.add_char buf b32_alpha.[(!acc lsr !bits) land 0x1f] done) s; if !bits > 0 then Buffer.add_char buf b32_alpha.[(!acc lsl (5 - !bits)) land 0x1f]; Buffer.contents buf (* "abef" -> the 2 raw bytes. *) let unhex (h : string) : string = let n = String.length h / 2 in let b = Bytes.create n in for i = 0 to n - 1 do Bytes.set b i (Char.chr (int_of_string ("0x" ^ String.sub h (2 * i) 2))) done; Bytes.unsafe_to_string b (* multihash = varint(code) || varint(len) || digest *) let multihash (code : int) (digest : string) : string = varint code ^ varint (String.length digest) ^ digest (* CIDv1 = 0x01 || varint(codec) || multihash ; multibase 'b' base32. *) let cidv1 (codec : int) (mh : string) : string = "b" ^ base32_lower ("\x01" ^ varint codec ^ mh) let codec_dag_cbor = 0x71 let mh_sha2_256 = 0x12 (* Canonicalize an SX value: dag-cbor encode -> sha2-256 -> multihash -> CIDv1 (dag-cbor codec). *) let cid_from_sx (v : value) : string = let cbor = Sx_cbor.encode v in let digest = unhex (Sx_sha2.sha256_hex cbor) in cidv1 codec_dag_cbor (multihash mh_sha2_256 digest)