From db968eccee931abc8ad00d74d152625a2ab3174e Mon Sep 17 00:00:00 2001 From: Estevan Castilho Date: Sat, 22 Aug 2026 17:55:26 +0000 Subject: Tidy up --- lib/concepts/Blob.ml | 17 +++ lib/concepts/Blob.mli | 26 ++++ lib/concepts/Branch.ml | 49 -------- lib/concepts/Branch.mli | 9 -- lib/concepts/BranchState.ml | 87 ------------- lib/concepts/BranchState.mli | 17 --- lib/concepts/Codec.ml | 182 --------------------------- lib/concepts/Encoding.ml | 271 ++++++++++++++++++++++++++++++++++++++++ lib/concepts/Hash.ml | 5 +- lib/concepts/Hash.mli | 5 +- lib/concepts/Multigroup.ml | 51 -------- lib/concepts/Multigroup.mli | 9 -- lib/concepts/Object.ml | 64 ---------- lib/concepts/Object.mli | 45 ------- lib/concepts/Ordering.mli | 3 - lib/concepts/Path.ml | 16 --- lib/concepts/Path.mli | 8 -- lib/concepts/Relation.ml | 81 ------------ lib/concepts/Relation.mli | 15 --- lib/concepts/Representation.ml | 36 ------ lib/concepts/Representation.mli | 16 --- lib/concepts/Tuple.ml | 48 ------- lib/concepts/Tuple.mli | 9 -- 23 files changed, 316 insertions(+), 753 deletions(-) create mode 100644 lib/concepts/Blob.ml create mode 100644 lib/concepts/Blob.mli delete mode 100644 lib/concepts/Branch.ml delete mode 100644 lib/concepts/Branch.mli delete mode 100644 lib/concepts/BranchState.ml delete mode 100644 lib/concepts/BranchState.mli delete mode 100644 lib/concepts/Codec.ml create mode 100644 lib/concepts/Encoding.ml delete mode 100644 lib/concepts/Multigroup.ml delete mode 100644 lib/concepts/Multigroup.mli delete mode 100644 lib/concepts/Object.ml delete mode 100644 lib/concepts/Object.mli delete mode 100644 lib/concepts/Ordering.mli delete mode 100644 lib/concepts/Path.ml delete mode 100644 lib/concepts/Path.mli delete mode 100644 lib/concepts/Relation.ml delete mode 100644 lib/concepts/Relation.mli delete mode 100644 lib/concepts/Representation.ml delete mode 100644 lib/concepts/Representation.mli delete mode 100644 lib/concepts/Tuple.ml delete mode 100644 lib/concepts/Tuple.mli (limited to 'lib/concepts') diff --git a/lib/concepts/Blob.ml b/lib/concepts/Blob.ml new file mode 100644 index 0000000..c5d7d5c --- /dev/null +++ b/lib/concepts/Blob.ml @@ -0,0 +1,17 @@ +(* @needs Value *) + +(* The intent of this being an abstract type is to allow for switching + the implementation to one backed by a C array later on, which would + let us avoid copying when passing it through FFI boundaries. *) +type t = bytes + +let blob_of_bytes (b : bytes) = b +let bytes_of_blob (b : t) = b +let create = Bytes.create +let set = Bytes.set +let set_int64_be = Bytes.set_int64_be +let blit_string = Bytes.blit_string +let length = Bytes.length +let get = Bytes.get +let sub_string = Bytes.sub_string +let get_int64_be = Bytes.get_int64_be diff --git a/lib/concepts/Blob.mli b/lib/concepts/Blob.mli new file mode 100644 index 0000000..b7c5da3 --- /dev/null +++ b/lib/concepts/Blob.mli @@ -0,0 +1,26 @@ +(** an abstract representation of an encoded value *) +type t + +(* TODO: see the comments on Backend.Storage.LMDB *) + +(** create a blob from the data represented by bytes *) +val blob_of_bytes : bytes -> t + +(** return a bytes containing the data underlying a blob *) +val bytes_of_blob : t -> bytes + +val create : int -> t + +val set : t -> int -> char -> unit + +val set_int64_be : t -> int -> int64 -> unit + +val blit_string : string -> int -> t -> int -> int -> unit + +val length : t -> int + +val get : t -> int -> char + +val sub_string : t -> int -> int -> string + +val get_int64_be : t -> int -> int64 diff --git a/lib/concepts/Branch.ml b/lib/concepts/Branch.ml deleted file mode 100644 index cd4213b..0000000 --- a/lib/concepts/Branch.ml +++ /dev/null @@ -1,49 +0,0 @@ -type address = Hash.hash - -type t = - { name: string; - target: address } - -module Field = struct - let name = "name" - let target = "target" -end - -module Error = struct - open Condition - - let malformed_branch () = - condition "branch-malformed" "A branch representation did not conform to what was expected" - empty - - let malformed_hash length = - condition "branch-malformed-hash" "A branch target root was not a valid hash" - ("length" |=| Value.Integer length) -end - -let make ~name ~target = {name; target} - -let name branch = branch.name -let target branch = branch.target - -module Body = struct - type nonrec t = t - - let tag = 'b' - let malformed = Error.malformed_branch - - let equal left right = - left.name = right.name && Hash.hash_equals left.target right.target - - let fields branch = - [ Field.name, Object.Field.string branch.name; - Field.target, Object.Field.address branch.target ] - - let of_fields data = - let open Utilities.Result in - let* name = Object.Field.require_string Field.name data in - let* target = Object.Field.require_address ~malformed:Error.malformed_hash Field.target data in - Ok {name; target} -end - -include (Object.Codec.Make (Body) : Object.S with type t := t) diff --git a/lib/concepts/Branch.mli b/lib/concepts/Branch.mli deleted file mode 100644 index 0e7e9f9..0000000 --- a/lib/concepts/Branch.mli +++ /dev/null @@ -1,9 +0,0 @@ -type t -type address = Hash.hash - -include Object.S with type t := t - -val make : name:string -> target:address -> t - -val name : t -> string -val target : t -> address diff --git a/lib/concepts/BranchState.ml b/lib/concepts/BranchState.ml deleted file mode 100644 index e8c6636..0000000 --- a/lib/concepts/BranchState.ml +++ /dev/null @@ -1,87 +0,0 @@ -type address = Hash.hash - -(* A branch state records durable branch data only. The runtime registry paths - mounted around it, such as /system/branches/master, are derived at - initialization and should not be persisted as part of [current]. *) -type t = {current: address; history: address; tip: address option} - -module Field = struct - let current = "current" - let history = "history" - let tip = "tip" -end - -module Error = struct - open Condition - - let malformed_branch_state () = - condition "branch-state-malformed" - "A branch state representation did not conform to what was expected" empty - - let malformed_hash length = - condition "branch-state-malformed-hash" "A branch state address was not a valid hash" - ("length" |=| Value.Integer length) -end - -let make ~current ~history ~tip = {current; history; tip} -let current branch_state = branch_state.current -let history branch_state = branch_state.history -let tip branch_state = branch_state.tip -let address_bencode address = Object.Field.address address - -let address_option_bencode = function - | None -> Codec.Bencode.List [] - | Some address -> Codec.Bencode.List [address_bencode address] - -let require_address_bencode = function - | Codec.Bencode.String raw -> - if String.length raw = Hash.size then Ok (Hash.of_raw_string raw) - else Error (Error.malformed_hash (String.length raw)) - | _ -> Error (Error.malformed_branch_state ()) - -let require_address_option key data = - let open Utilities.Result in - let* value = Codec.Bencode.field key data in - let* entries = Codec.Bencode.as_list value in - match entries with - | [] -> Ok None - | [value] -> - let* address = require_address_bencode value in - Ok (Some address) - | _ -> Error (Error.malformed_branch_state ()) - -let equal_address_option left right = - match left, right with - | None, None -> true - | Some left, Some right -> Hash.hash_equals left right - | _ -> false - -module Body = struct - type nonrec t = t - - let tag = 's' - let malformed = Error.malformed_branch_state - - let equal left right = - Hash.hash_equals left.current right.current - && Hash.hash_equals left.history right.history - && equal_address_option left.tip right.tip - - let fields branch_state = - [ Field.current, address_bencode branch_state.current; - Field.history, address_bencode branch_state.history; - Field.tip, address_option_bencode branch_state.tip ] - - let of_fields data = - let open Utilities.Result in - let* current = - Object.Field.require_address ~malformed:Error.malformed_hash Field.current data - in - let* history = - Object.Field.require_address ~malformed:Error.malformed_hash Field.history data - in - let* tip = require_address_option Field.tip data in - Ok {current; history; tip} -end - -include (Object.Codec.Make (Body) : Object.S with type t := t) diff --git a/lib/concepts/BranchState.mli b/lib/concepts/BranchState.mli deleted file mode 100644 index 45792cc..0000000 --- a/lib/concepts/BranchState.mli +++ /dev/null @@ -1,17 +0,0 @@ -type t -type address = Hash.hash - -include Object.S with type t := t - -(** Durable state of one branch head. - - [current] is the branch-visible catalog root, not the runtime namespace - rooted at [/system]. Runtime scaffolding is rebuilt during initialization. - [history] is the ordered commit-stream tree root. [tip] is absent until the - first real commit, so initialization does not create a synthetic commit just - to make the branch non-empty. *) -val make : current:address -> history:address -> tip:address option -> t - -val current : t -> address -val history : t -> address -val tip : t -> address option diff --git a/lib/concepts/Codec.ml b/lib/concepts/Codec.ml deleted file mode 100644 index 758d718..0000000 --- a/lib/concepts/Codec.ml +++ /dev/null @@ -1,182 +0,0 @@ - module Error = struct - open Condition - - let unexpected_end = - condition "codec-unexpected-end" "Unexpected end of input while decoding" empty - - let malformed_integer digits = - condition "codec-malformed-integer" "A byte run is not a valid integer" - ("digits" |=| Value.String digits) - - let unexpected_byte c = - condition "codec-unexpected-byte" "Unexpected byte while decoding" - ("byte" |=| Value.String (String.make 1 c)) - - let string_out_of_bounds length = - condition "codec-string-out-of-bounds" "A string length runs past the end of input" - ("length" |=| Value.Integer length) - - let trailing_bytes = - condition "codec-trailing-bytes" "Bytes remain after a complete value" empty - - let tag_mismatch ~expected ~actual = - condition "codec-tag-mismatch" "A value with a different tag was expected" - ("expected" |=| Value.String (String.make 1 expected) & - "actual" |=| Value.String (String.make 1 actual)) - - let type_mismatch ~expected = - condition "codec-type-mismatch" "A Bencode value of a different shape was expected" - ("expected" |=| Value.String expected) - - let missing_field key = - condition "codec-missing-field" "A required field is absent" - ("field" |=| Value.String key) - - let unexpected_object ~expected ~actual = - condition "codec-unexpected-object" "The decoded object is not of the expected kind" - ("expected" |=| Value.String expected & "actual" |=| Value.String actual) -end - -(* A self-describing serialization used as the uniform wire format for - durable objects. Chosen over an ad-hoc key=value scheme because it - nests (a multigroup holds a map of relation hashes) and has a - single byte form for a given value (dict keys are emitted sorted), - which matters once these bytes are content-addressed. - - Grammar: - - Int "i" "e" (negatives allowed) - - String ":" - - List "l" * "e" - - Dict "d" ( )* "e" keys sorted on encode *) -module Bencode = struct - type t = Tagged of char * t | Int of int | String of string | List of t list | Dict of (string * t) list - - let rec encode buf = function - | Tagged (tag, value) -> - Buffer.add_char buf 't'; - Buffer.add_char buf tag; - encode buf value - | Int n -> - Buffer.add_char buf 'i'; - Buffer.add_string buf (string_of_int n); - Buffer.add_char buf 'e' - | String s -> - Buffer.add_string buf (string_of_int (String.length s)); - Buffer.add_char buf ':'; - Buffer.add_string buf s - | List xs -> - Buffer.add_char buf 'l'; - List.iter (encode buf) xs; - Buffer.add_char buf 'e' - | Dict kvs -> - Buffer.add_char buf 'd'; - List.iter - (fun (k, v) -> - encode buf (String k); - encode buf v ) - (List.sort (fun (a, _) (b, _) -> String.compare a b) kvs); - Buffer.add_char buf 'e' - - let to_bytes (value : t) : bytes = - let buf = Buffer.create 256 in - encode buf value; - Buffer.to_bytes buf - - let as_string = function String s -> Ok s | _ -> Error (Error.type_mismatch ~expected:"string") - let as_int = function Int n -> Ok n | _ -> Error (Error.type_mismatch ~expected:"integer") - let as_list = function List l -> Ok l | _ -> Error (Error.type_mismatch ~expected:"list") - let as_dict = function Dict d -> Ok d | _ -> Error (Error.type_mismatch ~expected:"dict") - - let of_bytes (bytes : bytes) : (t, Condition.condition) result = - let open Utilities.Result in - let s = Bytes.to_string bytes in - let n = String.length s in - let pos = ref 0 in - let peek () = if !pos >= n then Error (Error.unexpected_end) else Ok (s.[!pos]) in - let advance () = incr pos in - (* Read the decimal run up to (and consuming) [term]. *) - let read_int_from start term = - let rec scan () = - let* c = peek () in - if c = term then begin - let digits = String.sub s start (!pos - start) in - advance (); - match int_of_string_opt digits with - | Some i -> Ok i - | None -> Error (Error.malformed_integer digits) - end - else begin - advance (); - scan () - end - in - scan () - in - let read_int term = read_int_from !pos term in - let read_char () = - let* c = peek () in - advance (); - Ok c - in - let rec parse () = - let* c = peek () in - advance (); - match c with - | 't' -> - let* tag = read_char () in - let* data = parse () in - Ok (Tagged (tag, data)) - | 'i' -> - let* n = read_int 'e' in - Ok (Int n) - | 'l' -> parse_list [] - | 'd' -> parse_dict [] - | c when c >= '0' && c <= '9' -> parse_string_from (!pos - 1) - | c -> Error (Error.unexpected_byte c) - and parse_string () = - parse_string_from !pos - and parse_string_from start = - let* len = read_int_from start ':' in - if len < 0 || !pos + len > n then - Error (Error.string_out_of_bounds len) - else - let str = String.sub s !pos len in - pos := !pos + len; - Ok (String str) - and parse_list acc = - let* c = peek () in - if c = 'e' then (advance (); Ok (List (List.rev acc))) - else - let* result = parse () in - parse_list (result :: acc) - and parse_dict acc = - let* c = peek () in - if c = 'e' then (advance (); Ok (Dict (List.rev acc))) - else - let* key = parse_string () in - let* key = as_string key in - let* value = parse () in - parse_dict ((key, value) :: acc) - in - let* value = parse () in - if !pos <> n then Error Error.trailing_bytes else - Ok value - - let to_blob x = to_bytes x |> Representation.blob_of_bytes - let of_blob b = Representation.bytes_of_blob b |> of_bytes - - let with_tag t = function - | Tagged (t', v) -> - if t = t' then - Ok v - else - Error (Error.tag_mismatch ~expected:t ~actual:t') - | _ -> Error (Error.type_mismatch ~expected:"tagged") - - let field key = function - | Dict kvs -> ( - match List.assoc_opt key kvs with - | Some v -> Ok v - | None -> Error (Error.missing_field key) ) - | _ -> Error (Error.type_mismatch ~expected:"dict") -end diff --git a/lib/concepts/Encoding.ml b/lib/concepts/Encoding.ml new file mode 100644 index 0000000..121e98d --- /dev/null +++ b/lib/concepts/Encoding.ml @@ -0,0 +1,271 @@ +(* A blob is a one-byte constructor tag followed by its payload. The + tag makes the encoding self-describing so [value_of_blob] can + recover the constructor: + - String ['\000'] ++ raw utf-8 bytes + - Integer ['\001'] ++ 8-byte big-endian int64 *) +let tag_string = '\000' +let tag_integer = '\001' + +let blob_of_value = function + | Value.String s -> + let b = Blob.create (1 + String.length s) in + Blob.set b 0 tag_string; + Blob.blit_string s 0 b 1 (String.length s); + b + | Value.Integer n -> + let b = Blob.create 9 in + Blob.set b 0 tag_integer; + Blob.set_int64_be b 1 (Int64.of_int n); + b + +let value_of_blob (b : Blob.t) = + let n = Blob.length b in + if n = 0 then invalid_arg "value_of_blob: empty blob"; + match Blob.get b 0 with + | c when c = tag_string -> Value.String (Blob.sub_string b 1 (n - 1)) + | c when c = tag_integer -> + if n <> 9 then invalid_arg "value_of_blob: malformed integer payload"; + Value.Integer (Int64.to_int (Blob.get_int64_be b 1)) + | c -> invalid_arg (Printf.sprintf "value_of_blob: unknown tag %C" c) + + module Error = struct + open Condition + + let unexpected_end = + condition "codec-unexpected-end" "Unexpected end of input while decoding" empty + + let malformed_integer digits = + condition "codec-malformed-integer" "A byte run is not a valid integer" + ("digits" |=| Value.String digits) + + let unexpected_byte c = + condition "codec-unexpected-byte" "Unexpected byte while decoding" + ("byte" |=| Value.String (String.make 1 c)) + + let string_out_of_bounds length = + condition "codec-string-out-of-bounds" "A string length runs past the end of input" + ("length" |=| Value.Integer length) + + let trailing_bytes = + condition "codec-trailing-bytes" "Bytes remain after a complete value" empty + + let tag_mismatch ~expected ~actual = + condition "codec-tag-mismatch" "A value with a different tag was expected" + ("expected" |=| Value.String (String.make 1 expected) & + "actual" |=| Value.String (String.make 1 actual)) + + let type_mismatch ~expected = + condition "codec-type-mismatch" "A Bencode value of a different shape was expected" + ("expected" |=| Value.String expected) + + let missing_field key = + condition "codec-missing-field" "A required field is absent" + ("field" |=| Value.String key) + + let unexpected_object ~expected ~actual = + condition "codec-unexpected-object" "The decoded object is not of the expected kind" + ("expected" |=| Value.String expected & "actual" |=| Value.String actual) +end + +(* A self-describing serialization used as the uniform wire format for + durable objects. Chosen over an ad-hoc key=value scheme because it + nests (a multigroup holds a map of relation hashes) and has a + single byte form for a given value (dict keys are emitted sorted), + which matters once these bytes are content-addressed. + + Grammar: + - Int "i" "e" (negatives allowed) + - String ":" + - List "l" * "e" + - Dict "d" ( )* "e" keys sorted on encode *) +module Bencode = struct + type t = Tagged of char * t | Int of int | String of string | List of t list | Dict of (string * t) list + + let rec encode buf = function + | Tagged (tag, value) -> + Buffer.add_char buf 't'; + Buffer.add_char buf tag; + encode buf value + | Int n -> + Buffer.add_char buf 'i'; + Buffer.add_string buf (string_of_int n); + Buffer.add_char buf 'e' + | String s -> + Buffer.add_string buf (string_of_int (String.length s)); + Buffer.add_char buf ':'; + Buffer.add_string buf s + | List xs -> + Buffer.add_char buf 'l'; + List.iter (encode buf) xs; + Buffer.add_char buf 'e' + | Dict kvs -> + Buffer.add_char buf 'd'; + List.iter + (fun (k, v) -> + encode buf (String k); + encode buf v ) + (List.sort (fun (a, _) (b, _) -> String.compare a b) kvs); + Buffer.add_char buf 'e' + + let to_bytes (value : t) : bytes = + let buf = Buffer.create 256 in + encode buf value; + Buffer.to_bytes buf + + let as_string = function String s -> Ok s | _ -> Error (Error.type_mismatch ~expected:"string") + let as_int = function Int n -> Ok n | _ -> Error (Error.type_mismatch ~expected:"integer") + let as_list = function List l -> Ok l | _ -> Error (Error.type_mismatch ~expected:"list") + let as_dict = function Dict d -> Ok d | _ -> Error (Error.type_mismatch ~expected:"dict") + + let of_bytes (bytes : bytes) : (t, Condition.condition) result = + let open Utilities.Result in + let s = Bytes.to_string bytes in + let n = String.length s in + let pos = ref 0 in + let peek () = if !pos >= n then Error (Error.unexpected_end) else Ok (s.[!pos]) in + let advance () = incr pos in + (* Read the decimal run up to (and consuming) [term]. *) + let read_int_from start term = + let rec scan () = + let* c = peek () in + if c = term then begin + let digits = String.sub s start (!pos - start) in + advance (); + match int_of_string_opt digits with + | Some i -> Ok i + | None -> Error (Error.malformed_integer digits) + end + else begin + advance (); + scan () + end + in + scan () + in + let read_int term = read_int_from !pos term in + let read_char () = + let* c = peek () in + advance (); + Ok c + in + let rec parse () = + let* c = peek () in + advance (); + match c with + | 't' -> + let* tag = read_char () in + let* data = parse () in + Ok (Tagged (tag, data)) + | 'i' -> + let* n = read_int 'e' in + Ok (Int n) + | 'l' -> parse_list [] + | 'd' -> parse_dict [] + | c when c >= '0' && c <= '9' -> parse_string_from (!pos - 1) + | c -> Error (Error.unexpected_byte c) + and parse_string () = + parse_string_from !pos + and parse_string_from start = + let* len = read_int_from start ':' in + if len < 0 || !pos + len > n then + Error (Error.string_out_of_bounds len) + else + let str = String.sub s !pos len in + pos := !pos + len; + Ok (String str) + and parse_list acc = + let* c = peek () in + if c = 'e' then (advance (); Ok (List (List.rev acc))) + else + let* result = parse () in + parse_list (result :: acc) + and parse_dict acc = + let* c = peek () in + if c = 'e' then (advance (); Ok (Dict (List.rev acc))) + else + let* key = parse_string () in + let* key = as_string key in + let* value = parse () in + parse_dict ((key, value) :: acc) + in + let* value = parse () in + if !pos <> n then Error Error.trailing_bytes else + Ok value + + let to_blob x = to_bytes x |> Blob.blob_of_bytes + let of_blob b = Blob.bytes_of_blob b |> of_bytes + + let with_tag t = function + | Tagged (t', v) -> + if t = t' then + Ok v + else + Error (Error.tag_mismatch ~expected:t ~actual:t') + | _ -> Error (Error.type_mismatch ~expected:"tagged") + + let field key = function + | Dict kvs -> ( + match List.assoc_opt key kvs with + | Some v -> Ok v + | None -> Error (Error.missing_field key) ) + | _ -> Error (Error.type_mismatch ~expected:"dict") +end + +module Field = struct + let string value = Bencode.String value + let address address = Bencode.String (Hash.to_raw_string address) + + let require_string key data = + Bencode.field key data |> Utilities.Result.fmap Bencode.as_string + + let require_address ~malformed key data = + let open Utilities.Result in + let* raw = require_string key data in + if String.length raw = Hash.size then Ok (Hash.of_raw_string raw) + else Error (malformed (String.length raw)) +end + +module Record = struct + module type S = sig + type t + + val equal : t -> t -> bool + + val to_bencode : t -> Bencode.t + val of_bencode : Bencode.t -> (t, Condition.condition) result + + val to_blob : t -> Blob.t + val of_blob : Blob.t -> (t, Condition.condition) result + + val to_bytes : t -> bytes + val of_bytes : bytes -> (t, Condition.condition) result + end + + module type BODY = sig + type t + + val tag : char + val malformed : unit -> Condition.condition + val equal : t -> t -> bool + val fields : t -> (string * Bencode.t) list + val of_fields : Bencode.t -> (t, Condition.condition) result + end + + module Make (B : BODY) = struct + type t = B.t + + let equal = B.equal + let to_bencode object_ = Bencode.Tagged (B.tag, Bencode.Dict (B.fields object_)) + + let of_bencode = function + | Bencode.Tagged (tag, (Bencode.Dict _ as data)) when Char.equal tag B.tag -> + B.of_fields data + | _ -> Error (B.malformed ()) + + let to_blob object_ = to_bencode object_ |> Bencode.to_blob + let of_blob blob = Bencode.of_blob blob |> Utilities.Result.fmap of_bencode + + let to_bytes object_ = to_blob object_ |> Blob.bytes_of_blob + let of_bytes bytes = Bencode.of_bytes bytes |> Utilities.Result.fmap of_bencode + end +end diff --git a/lib/concepts/Hash.ml b/lib/concepts/Hash.ml index 62c7789..ae0a178 100644 --- a/lib/concepts/Hash.ml +++ b/lib/concepts/Hash.ml @@ -7,12 +7,9 @@ let size = 256/8 let compare h1 h2 = Digestif.SHA256.unsafe_compare h1 h2 |> Ordering.of_int -let hash_of_value (v : Value.value) = - Representation.(blob_of_value v |> bytes_of_blob) |> Digestif.SHA256.digest_bytes - let hash_of_bytes bytes = Digestif.SHA256.digest_bytes bytes -let hash_of_blob blob = Representation.bytes_of_blob blob |> Digestif.SHA256.digest_bytes +let hash_of_blob blob = Blob.bytes_of_blob blob |> Digestif.SHA256.digest_bytes let to_raw_string hash = Digestif.SHA256.to_raw_string hash diff --git a/lib/concepts/Hash.mli b/lib/concepts/Hash.mli index f40d9aa..b3ae2dc 100644 --- a/lib/concepts/Hash.mli +++ b/lib/concepts/Hash.mli @@ -4,14 +4,11 @@ type hash (** the size of a hash, in bytes *) val size : int -(** given a value, compute it's hash *) -val hash_of_value : Value.value -> hash - (** given a bag of bytes, compute it's hash *) val hash_of_bytes : bytes -> hash (** given a blob, compute it's hash *) -val hash_of_blob : Representation.blob -> hash +val hash_of_blob : Blob.t -> hash (** given a hash, return a representation of it as a string (/not/ the usual hex representation!) *) val to_raw_string : hash -> string diff --git a/lib/concepts/Multigroup.ml b/lib/concepts/Multigroup.ml deleted file mode 100644 index 863cd26..0000000 --- a/lib/concepts/Multigroup.ml +++ /dev/null @@ -1,51 +0,0 @@ -type address = Hash.hash - -type t = - { name: string; - relations: address } - -module Field = struct - let name = "name" - let relations = "relations" -end - -module Error = struct - open Condition - - let malformed_multigroup () = - condition "multigroup-malformed" - "A multigroup representation did not conform to what was expected" empty - - let malformed_hash length = - condition "multigroup-malformed-hash" "A multigroup relation root was not a valid hash" - ("length" |=| Value.Integer length) -end - -let make ~name ~relations = {name; relations} - -let name multigroup = multigroup.name -let relations multigroup = multigroup.relations - -module Body = struct - type nonrec t = t - - let tag = 'm' - let malformed = Error.malformed_multigroup - - let equal left right = - left.name = right.name && Hash.hash_equals left.relations right.relations - - let fields multigroup = - [ Field.name, Object.Field.string multigroup.name; - Field.relations, Object.Field.address multigroup.relations ] - - let of_fields data = - let open Utilities.Result in - let* name = Object.Field.require_string Field.name data in - let* relations = - Object.Field.require_address ~malformed:Error.malformed_hash Field.relations data - in - Ok {name; relations} -end - -include (Object.Codec.Make (Body) : Object.S with type t := t) diff --git a/lib/concepts/Multigroup.mli b/lib/concepts/Multigroup.mli deleted file mode 100644 index 1a76dce..0000000 --- a/lib/concepts/Multigroup.mli +++ /dev/null @@ -1,9 +0,0 @@ -type t -type address = Hash.hash - -include Object.S with type t := t - -val make : name:string -> relations:address -> t - -val name : t -> string -val relations : t -> address diff --git a/lib/concepts/Object.ml b/lib/concepts/Object.ml deleted file mode 100644 index 22fb14a..0000000 --- a/lib/concepts/Object.ml +++ /dev/null @@ -1,64 +0,0 @@ -type bencode = Codec.Bencode.t - -module Bencode = Codec.Bencode - -module type S = sig - type t - - val equal : t -> t -> bool - - val to_bencode : t -> bencode - val of_bencode : bencode -> (t, Condition.condition) result - - val to_blob : t -> Representation.blob - val of_blob : Representation.blob -> (t, Condition.condition) result - - val to_bytes : t -> bytes - val of_bytes : bytes -> (t, Condition.condition) result -end - -module type OBJECT = S - -module Field = struct - let string value = Bencode.String value - let address address = Bencode.String (Hash.to_raw_string address) - - let require_string key data = - Codec.Bencode.field key data |> Utilities.Result.fmap Codec.Bencode.as_string - - let require_address ~malformed key data = - let open Utilities.Result in - let* raw = require_string key data in - if String.length raw = Hash.size then Ok (Hash.of_raw_string raw) - else Error (malformed (String.length raw)) -end - -module Codec = struct - module type BODY = sig - type t - - val tag : char - val malformed : unit -> Condition.condition - val equal : t -> t -> bool - val fields : t -> (string * bencode) list - val of_fields : bencode -> (t, Condition.condition) result - end - - module Make (B : BODY) = struct - type t = B.t - - let equal = B.equal - let to_bencode object_ = Bencode.Tagged (B.tag, Bencode.Dict (B.fields object_)) - - let of_bencode = function - | Bencode.Tagged (tag, (Bencode.Dict _ as data)) when Char.equal tag B.tag -> - B.of_fields data - | _ -> Error (B.malformed ()) - - let to_blob object_ = to_bencode object_ |> Bencode.to_blob - let of_blob blob = Bencode.of_blob blob |> Utilities.Result.fmap of_bencode - - let to_bytes object_ = to_blob object_ |> Representation.bytes_of_blob - let of_bytes bytes = Bencode.of_bytes bytes |> Utilities.Result.fmap of_bencode - end -end diff --git a/lib/concepts/Object.mli b/lib/concepts/Object.mli deleted file mode 100644 index e132673..0000000 --- a/lib/concepts/Object.mli +++ /dev/null @@ -1,45 +0,0 @@ -type bencode = Codec.Bencode.t - -module type S = sig - type t - - val equal : t -> t -> bool - - val to_bencode : t -> bencode - val of_bencode : bencode -> (t, Condition.condition) result - - val to_blob : t -> Representation.blob - val of_blob : Representation.blob -> (t, Condition.condition) result - - val to_bytes : t -> bytes - val of_bytes : bytes -> (t, Condition.condition) result -end - -module type OBJECT = S - -module Field : sig - val string : string -> bencode - val address : Hash.hash -> bencode - - val require_string : string -> bencode -> (string, Condition.condition) result - - val require_address : - malformed:(int -> Condition.condition) -> - string -> - bencode -> - (Hash.hash, Condition.condition) result -end - -module Codec : sig - module type BODY = sig - type t - - val tag : char - val malformed : unit -> Condition.condition - val equal : t -> t -> bool - val fields : t -> (string * bencode) list - val of_fields : bencode -> (t, Condition.condition) result - end - - module Make (B : BODY) : S with type t = B.t -end diff --git a/lib/concepts/Ordering.mli b/lib/concepts/Ordering.mli deleted file mode 100644 index ab8a3f4..0000000 --- a/lib/concepts/Ordering.mli +++ /dev/null @@ -1,3 +0,0 @@ -type ordering = Equal | Smaller | Greater - -val of_int : int -> ordering diff --git a/lib/concepts/Path.ml b/lib/concepts/Path.ml deleted file mode 100644 index c751301..0000000 --- a/lib/concepts/Path.ml +++ /dev/null @@ -1,16 +0,0 @@ -type t = string BatFingerTree.t - -let empty : t = BatFingerTree.empty -let of_list (components : string list) : t = BatFingerTree.of_list components -let to_list (path : t) : string list = BatFingerTree.to_list path -let snoc (path : t) (component : string) : t = BatFingerTree.snoc path component -let to_string (path : t) : string = String.concat "/" (to_list path) - -let is_prefix ~(prefix : t) (path : t) : bool = - let rec walk prefix path = - match prefix, path with - | [], _ -> true - | p :: prefix, x :: path -> p = x && walk prefix path - | _ :: _, [] -> false - in - walk (to_list prefix) (to_list path) diff --git a/lib/concepts/Path.mli b/lib/concepts/Path.mli deleted file mode 100644 index bec4221..0000000 --- a/lib/concepts/Path.mli +++ /dev/null @@ -1,8 +0,0 @@ -type t = string BatFingerTree.t - -val empty : t -val of_list : string list -> t -val to_list : t -> string list -val snoc : t -> string -> t -val to_string : t -> string -val is_prefix : prefix:t -> t -> bool diff --git a/lib/concepts/Relation.ml b/lib/concepts/Relation.ml deleted file mode 100644 index a4fb341..0000000 --- a/lib/concepts/Relation.ml +++ /dev/null @@ -1,81 +0,0 @@ -type address = Hash.hash -type schema = string BatMap.String.t - -type t = - { name: string; - tuples: address; - schema: schema } - -module Field = struct - let name = "name" - let tuples = "tuples" - let schema = "schema" -end - -module Error = struct - open Condition - - let malformed_relation () = - condition "relation-malformed" "A relation representation did not conform to what was expected" - empty - - let malformed_hash length = - condition "relation-malformed-hash" "A relation tuple root was not a valid hash" - ("length" |=| Value.Integer length) -end - -let make ~name ~tuples ~schema = {name; tuples; schema} - -let name relation = relation.name -let tuples relation = relation.tuples -let schema relation = relation.schema - -let schema_empty = BatMap.String.empty - -let schema_of_list fields = - List.fold_left (fun acc (name, domain) -> BatMap.String.add name domain acc) schema_empty - fields - -let schema_to_list schema = BatMap.String.bindings schema - -let schema_to_bencode schema = - Codec.Bencode.Dict - (schema_to_list schema - |> List.map (fun (name, domain) -> name, Codec.Bencode.String domain) ) - -let schema_of_bencode value = - let open Utilities.Result in - let* fields = Codec.Bencode.as_dict value in - fields - |> List.map (fun (name, domain) -> - let* domain = Codec.Bencode.as_string domain in - Ok (name, domain) ) - |> sequence - |> Result.map schema_of_list - -module Body = struct - type nonrec t = t - - let tag = 'r' - let malformed = Error.malformed_relation - - let equal left right = - left.name = right.name - && Hash.hash_equals left.tuples right.tuples - && BatMap.String.equal String.equal left.schema right.schema - - let fields relation = - [ Field.name, Object.Field.string relation.name; - Field.schema, schema_to_bencode relation.schema; - Field.tuples, Object.Field.address relation.tuples ] - - let of_fields data = - let open Utilities.Result in - let* name = Object.Field.require_string Field.name data in - let* tuples = Object.Field.require_address ~malformed:Error.malformed_hash Field.tuples data in - let* schema_data = Codec.Bencode.field Field.schema data in - let* schema = schema_of_bencode schema_data in - Ok {name; tuples; schema} -end - -include (Object.Codec.Make (Body) : Object.S with type t := t) diff --git a/lib/concepts/Relation.mli b/lib/concepts/Relation.mli deleted file mode 100644 index 5319a3d..0000000 --- a/lib/concepts/Relation.mli +++ /dev/null @@ -1,15 +0,0 @@ -type t -type address = Hash.hash -type schema - -include Object.S with type t := t - -val make : name:string -> tuples:address -> schema:schema -> t - -val name : t -> string -val tuples : t -> address -val schema : t -> schema - -val schema_empty : schema -val schema_of_list : (string * string) list -> schema -val schema_to_list : schema -> (string * string) list diff --git a/lib/concepts/Representation.ml b/lib/concepts/Representation.ml deleted file mode 100644 index 12c3624..0000000 --- a/lib/concepts/Representation.ml +++ /dev/null @@ -1,36 +0,0 @@ -(* @needs Value *) - -type blob = bytes - -let blob_of_bytes (b : bytes) = b -let bytes_of_blob (b : blob) = b - -(* A blob is a one-byte constructor tag followed by its payload. The - tag makes the encoding self-describing so [value_of_blob] can - recover the constructor: - - String ['\000'] ++ raw utf-8 bytes - - Integer ['\001'] ++ 8-byte big-endian int64 *) -let tag_string = '\000' -let tag_integer = '\001' - -let blob_of_value = function - | Value.String s -> - let b = Bytes.create (1 + String.length s) in - Bytes.set b 0 tag_string; - Bytes.blit_string s 0 b 1 (String.length s); - b - | Value.Integer n -> - let b = Bytes.create 9 in - Bytes.set b 0 tag_integer; - Bytes.set_int64_be b 1 (Int64.of_int n); - b - -let value_of_blob (b : blob) = - let n = Bytes.length b in - if n = 0 then invalid_arg "value_of_blob: empty blob"; - match Bytes.get b 0 with - | c when c = tag_string -> Value.String (Bytes.sub_string b 1 (n - 1)) - | c when c = tag_integer -> - if n <> 9 then invalid_arg "value_of_blob: malformed integer payload"; - Value.Integer (Int64.to_int (Bytes.get_int64_be b 1)) - | c -> invalid_arg (Printf.sprintf "value_of_blob: unknown tag %C" c) diff --git a/lib/concepts/Representation.mli b/lib/concepts/Representation.mli deleted file mode 100644 index 21c43a4..0000000 --- a/lib/concepts/Representation.mli +++ /dev/null @@ -1,16 +0,0 @@ -(** an abstract representation of an encoded value *) -type blob - -(** encode a blob into a value *) -val blob_of_value : Value.value -> blob - -(** decode a value from a blob *) -val value_of_blob : blob -> Value.value - -(* TODO: see the comments on Backend.Storage.LMDB *) - -(** create a blob from the data represented by bytes *) -val blob_of_bytes : bytes -> blob - -(** return a bytes containing the data underlying a blob *) -val bytes_of_blob : blob -> bytes diff --git a/lib/concepts/Tuple.ml b/lib/concepts/Tuple.ml deleted file mode 100644 index 8fac426..0000000 --- a/lib/concepts/Tuple.ml +++ /dev/null @@ -1,48 +0,0 @@ -(* @needs Value *) - -module AttributeMap = BatMap.String - -(** A tuple: a finite map from attribute name to attribute. *) -type t = Value.value AttributeMap.t - -(** [empty] The tuple with no attributes. *) -let empty : t = AttributeMap.empty - -(** [access name t] Looks up the attribute bound to [name] in [t]. - @param name attribute name to look up. - @param t tuple to search. - @return the attribute, or [None] when [name] is unbound. *) -let access (name : string) (t : t) : Value.value option = AttributeMap.find_opt name t - -(** [merge left right] Unions two tuples. Shared attributes take the - [left] value; one-sided attributes are kept. Precondition: shared - attributes agree (the natural-join matcher guarantees this), so the - [left] bias is never observable. - @param left first tuple. - @param right second tuple. - @return the merged tuple. *) -let merge (left : t) (right : t) : t = - let merger _ (left : Value.value option) (right : Value.value option) : Value.value option = - match left, right with (Some _ as v), _ | None, (Some _ as v) -> v | None, None -> None - in - AttributeMap.merge merger left right - -(** [project keep t] Keeps only the attributes of [t] whose names are - in [keep]. - @param keep set of attribute names to retain. - @param t tuple to project. - @return the projected tuple. *) -let project (keep : BatSet.String.t) (t : t) : t = - AttributeMap.filter (fun name _ -> BatSet.String.mem name keep) t - -(** [rename mapping t] Renames the attributes of [t] through - [mapping]; names absent from [mapping] are left unchanged. - @param mapping map from old name to new name. - @param t tuple to rename. - @return the renamed tuple. *) -let rename (mapping : string BatMap.String.t) (t : t) : t = - AttributeMap.fold - (fun name attr acc -> - let name = BatMap.String.find_default name name mapping in - AttributeMap.add name attr acc ) - t AttributeMap.empty diff --git a/lib/concepts/Tuple.mli b/lib/concepts/Tuple.mli deleted file mode 100644 index 07f7a24..0000000 --- a/lib/concepts/Tuple.mli +++ /dev/null @@ -1,9 +0,0 @@ -module AttributeMap = BatMap.String - -type t = Value.value AttributeMap.t - -val empty : t -val access : string -> t -> Value.value option -val merge : t -> t -> t -val project : BatSet.String.t -> t -> t -val rename : string BatMap.String.t -> t -> t -- cgit v1.2.3