open Wire let max_value_len = 240 type t = { param_id : int; len : int; generation : int; value : string; crc32 : int; } let codec = let open Codec in record "ParamEntry" (fun param_id len generation value crc32 -> { param_id; len; generation; value; crc32 }) |+ field "param_id" uint32be (fun t -> t.param_id) |+ field "len" uint16be (fun t -> t.len) |+ field "generation" uint16be (fun t -> t.generation) |+ field "value" (byte_array ~size:(int max_value_len)) (fun t -> t.value) |+ field "crc32" uint32be (fun t -> t.crc32) |> seal let crc32c data = Optint.to_unsigned_int (Checkseum.Crc32c.digest_string data 0 (String.length data) Checkseum.Crc32c.default) let compute_crc t = let wire_size = Codec.wire_size codec in let buf = Bytes.create wire_size in Codec.encode codec t buf 0; (* CRC covers everything before the CRC field *) crc32c (Bytes.sub_string buf 0 (wire_size - 4)) let pad_to n s = let slen = String.length s in if slen >= n then String.sub s 0 n else let b = Bytes.make n '\x00' in Bytes.blit_string s 0 b 0 slen; Bytes.unsafe_to_string b let v ~param_id ~generation value = let len = min max_value_len (String.length value) in let value = pad_to max_value_len value in let t = { param_id; len; generation; value; crc32 = 0 } in { t with crc32 = compute_crc t } let value_bytes t = String.sub t.value 0 (min t.len (String.length t.value)) let check_crc t = let expected = compute_crc { t with crc32 = 0 } in t.crc32 = expected let pp ppf t = Fmt.pf ppf "@[param(id=%d gen=%d len=%d crc=0x%08x)@]" t.param_id t.generation t.len t.crc32 let equal a b = a.param_id = b.param_id && a.len = b.len && a.generation = b.generation && String.equal a.value b.value && a.crc32 = b.crc32