Serialize the tree entries the same way that irmin-lmdb does
This commit is contained in:
parent
b76e759361
commit
ecbab4fb77
@ -109,16 +109,224 @@ let checkout_exn index key =
|
|||||||
| None -> Lwt.fail Not_found
|
| None -> Lwt.fail Not_found
|
||||||
| Some p -> Lwt.return p
|
| Some p -> Lwt.return p
|
||||||
|
|
||||||
|
|
||||||
let raw_commit ~time ?(message = "") context =
|
let raw_commit ~time ?(message = "") context =
|
||||||
let info =
|
let info =
|
||||||
Irmin.Info.v ~date:(Time.to_seconds time) ~author:"Tezos" message in
|
Irmin.Info.v ~date:(Time.to_seconds time) ~author:"Tezos" message in
|
||||||
GitStore.Commit.v
|
GitStore.Commit.v
|
||||||
context.index.repo ~info ~parents:context.parents context.tree
|
context.index.repo ~info ~parents:context.parents context.tree
|
||||||
|
|
||||||
|
module P = GitStore.Private
|
||||||
|
|
||||||
|
(* --- FIXME(samoht): I am so sorry --- *)
|
||||||
|
module Hack = struct
|
||||||
|
|
||||||
|
module StepMap = struct
|
||||||
|
module X = struct
|
||||||
|
type t = GitStore.step
|
||||||
|
let t = GitStore.step_t
|
||||||
|
let compare = Irmin.Type.compare t
|
||||||
|
end
|
||||||
|
include Map.Make(X)
|
||||||
|
end
|
||||||
|
|
||||||
|
module Contents = struct
|
||||||
|
|
||||||
|
type key = P.Contents.key
|
||||||
|
type contents = P.Contents.value
|
||||||
|
|
||||||
|
type t =
|
||||||
|
| Key of key
|
||||||
|
| Contents of contents
|
||||||
|
| Both of key * contents
|
||||||
|
|
||||||
|
let t =
|
||||||
|
let open Irmin.Type in
|
||||||
|
variant "Node.Contents" (fun key contents both -> function
|
||||||
|
| Key x -> key x
|
||||||
|
| Contents x -> contents x
|
||||||
|
| Both (x, y) -> both (x, y))
|
||||||
|
|~ case1 "Key" P.Contents.Key.t (fun x -> Key x)
|
||||||
|
|~ case1 "Contents" P.Contents.Val.t (fun x -> Contents x)
|
||||||
|
|~ case1 "Both" (pair P.Contents.Key.t P.Contents.Val.t)
|
||||||
|
(fun (x, y) -> Both (x, y))
|
||||||
|
|> sealv
|
||||||
|
|
||||||
|
let hash = function
|
||||||
|
| Key k | Both (k, _) -> k
|
||||||
|
| Contents c -> P.Contents.Key.digest P.Contents.Val.t c
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
type key = P.Node.key
|
||||||
|
|
||||||
|
type value = [ `Node of node | `Contents of Contents.t * Metadata.t ]
|
||||||
|
|
||||||
|
and map = value StepMap.t
|
||||||
|
|
||||||
|
and node =
|
||||||
|
| Map of map
|
||||||
|
| Key of key
|
||||||
|
| Both of key * map
|
||||||
|
|
||||||
|
let value t =
|
||||||
|
let open Irmin.Type in
|
||||||
|
variant "Node.value" (fun node contents -> function
|
||||||
|
| `Node x -> node x
|
||||||
|
| `Contents x -> contents x)
|
||||||
|
|~ case1 "Node" t (fun x -> `Node x)
|
||||||
|
|~ case1 "Contents" (pair Contents.t Metadata.t) (fun x -> `Contents x)
|
||||||
|
|> sealv
|
||||||
|
|
||||||
|
let map value =
|
||||||
|
let open Irmin.Type in
|
||||||
|
let to_map x =
|
||||||
|
List.fold_left (fun acc (k, v) -> StepMap.add k v acc) StepMap.empty x
|
||||||
|
in
|
||||||
|
let of_map m = StepMap.fold (fun k v acc -> (k, v) :: acc) m [] in
|
||||||
|
like (list (pair GitStore.step_t value)) to_map of_map
|
||||||
|
|
||||||
|
let node map =
|
||||||
|
let open Irmin.Type in
|
||||||
|
variant "Node.node" (fun map key both -> function
|
||||||
|
| Map x -> map x
|
||||||
|
| Key y -> key y
|
||||||
|
| Both (y,z) -> both (y, z))
|
||||||
|
|~ case1 "Map" map (fun x -> Map x)
|
||||||
|
|~ case1 "Key" P.Node.Key.t (fun x -> Key x)
|
||||||
|
|~ case1 "Both" (pair P.Node.Key.t map) (fun (x, y) -> Both (x, y))
|
||||||
|
|> sealv
|
||||||
|
|
||||||
|
let node_t = Irmin.Type.mu (fun n ->
|
||||||
|
let value = value n in
|
||||||
|
node (map value)
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Mimick irmin-lmdb ordering *)
|
||||||
|
module Sort_key = struct
|
||||||
|
|
||||||
|
exception Result of int
|
||||||
|
|
||||||
|
let compare (x, vx) (y, vy) = match vx, vy with
|
||||||
|
| `Contents _, `Contents _ -> String.compare x y
|
||||||
|
| _ ->
|
||||||
|
let lenx = String.length x in
|
||||||
|
let leny = String.length y in
|
||||||
|
let i = ref 0 in
|
||||||
|
try
|
||||||
|
while !i < lenx && !i < leny do
|
||||||
|
match
|
||||||
|
Char.compare
|
||||||
|
(String.unsafe_get x !i) (String.unsafe_get y !i)
|
||||||
|
with
|
||||||
|
| 0 -> incr i
|
||||||
|
| i -> raise (Result i)
|
||||||
|
done;
|
||||||
|
let get len k v i =
|
||||||
|
if i < len then String.unsafe_get k i
|
||||||
|
else if i = len then match v with
|
||||||
|
| `Node _ -> '/'
|
||||||
|
| `Contents _ -> '\000'
|
||||||
|
else '\000'
|
||||||
|
in
|
||||||
|
match Char.compare (get lenx x vx !i) (get leny y vy !i) with
|
||||||
|
| 0 -> Char.compare (get lenx x vx (!i + 1)) (get leny y vy (!i + 1))
|
||||||
|
| i -> i
|
||||||
|
with Result i ->
|
||||||
|
i
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
let sort_entries = List.fast_sort Sort_key.compare
|
||||||
|
|
||||||
|
let pp_hex ppf x =
|
||||||
|
let buf = IrminBlake2B.to_raw x in
|
||||||
|
let `Hex hex = Hex.of_cstruct buf in
|
||||||
|
Fmt.string ppf hex
|
||||||
|
|
||||||
|
module Entry = struct
|
||||||
|
type kind = [ `Node | `Contents of Metadata.t ]
|
||||||
|
type entry = { kind : kind; name : string; node : IrminBlake2B.t; }
|
||||||
|
|
||||||
|
let entry_t =
|
||||||
|
let open Irmin.Type in
|
||||||
|
record "Tree.entry"
|
||||||
|
(fun kind name node ->
|
||||||
|
let kind =
|
||||||
|
match kind with
|
||||||
|
| None -> `Node
|
||||||
|
| Some m -> `Contents m in
|
||||||
|
{ kind ; name ; node } )
|
||||||
|
|+ field "kind" (option Metadata.t) (function
|
||||||
|
| { kind = `Node ; _ } -> None
|
||||||
|
| { kind = `Contents m ; _ } -> Some m)
|
||||||
|
|+ field "name" string (fun { name ; _ } -> name)
|
||||||
|
|+ field "node" IrminBlake2B.t (fun { node ; _ } -> node)
|
||||||
|
|> sealr
|
||||||
|
|
||||||
|
let of_entry e = e.name, match e.kind with
|
||||||
|
| `Node -> `Node e.node
|
||||||
|
| `Contents m -> `Contents (e.node, m)
|
||||||
|
|
||||||
|
let to_entry (name, value) = match value with
|
||||||
|
| `Node node -> { name; kind = `Node; node }
|
||||||
|
| `Contents (node, m) -> { name; kind = `Contents m; node }
|
||||||
|
|
||||||
|
let t = Irmin.Type.like entry_t of_entry to_entry
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
let rec export_map map =
|
||||||
|
let alist =
|
||||||
|
StepMap.fold (fun step v acc ->
|
||||||
|
(step, hash_value v) :: acc
|
||||||
|
) map []
|
||||||
|
in
|
||||||
|
let l = sort_entries alist in
|
||||||
|
P.Node.Val.v l
|
||||||
|
|
||||||
|
and hash_value = function
|
||||||
|
| `Contents (c, m) -> `Contents (Contents.hash c, m)
|
||||||
|
| `Node n -> `Node (hash_node n)
|
||||||
|
|
||||||
|
and hash_node = function
|
||||||
|
| Both (k, _) | Key k -> k
|
||||||
|
| Map m ->
|
||||||
|
let v = export_map m in
|
||||||
|
let entries = P.Node.Val.list v in
|
||||||
|
(* This needs to match what is done in the backend... *)
|
||||||
|
let v = Irmin.Type.encode_cstruct (Irmin.Type.list Entry.t) entries in
|
||||||
|
IrminBlake2B.digest Irmin.Type.cstruct v
|
||||||
|
|
||||||
|
let cast: GitStore.node -> node = fun n ->
|
||||||
|
let buf = Irmin.Type.encode_cstruct GitStore.node_t n in
|
||||||
|
match Irmin.Type.decode_cstruct node_t buf with
|
||||||
|
| Error (`Msg e) -> Fmt.failwith "invalid cast\n%s" e
|
||||||
|
| Ok x -> x
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
let tree_hash: GitStore.tree -> GitStore.Tree.hash = function
|
||||||
|
| `Contents (c, m) -> `Contents (P.Contents.Key.digest P.Contents.Val.t c, m)
|
||||||
|
| `Node n -> `Node (Hack.hash_node (Hack.cast n))
|
||||||
|
|
||||||
|
let hash ~time ?(message = "") context =
|
||||||
|
let info =
|
||||||
|
Irmin.Info.v ~date:(Time.to_seconds time) ~author:"Tezos" message
|
||||||
|
in
|
||||||
|
let parents = List.map (fun c -> GitStore.Commit.hash c) context.parents in
|
||||||
|
let node = match tree_hash context.tree with
|
||||||
|
| `Contents _ -> assert false
|
||||||
|
| `Node node -> node
|
||||||
|
in
|
||||||
|
let commit = P.Commit.Val.v ~parents ~node ~info in
|
||||||
|
let x = P.Commit.Key.digest P.Commit.Val.t commit in
|
||||||
|
(* FIXME: this doesn't have to be lwt *)
|
||||||
|
Lwt.return x
|
||||||
|
|
||||||
let commit ~time ?message context =
|
let commit ~time ?message context =
|
||||||
raw_commit ~time ?message context >>= fun commit ->
|
raw_commit ~time ?message context >>= fun commit ->
|
||||||
Lwt.return (GitStore.Commit.hash commit)
|
let h = GitStore.Commit.hash commit in
|
||||||
|
Lwt.return h
|
||||||
|
|
||||||
(*-- Generic Store Primitives ------------------------------------------------*)
|
(*-- Generic Store Primitives ------------------------------------------------*)
|
||||||
|
|
||||||
|
@ -60,6 +60,8 @@ val fold:
|
|||||||
val exists: index -> Context_hash.t -> bool Lwt.t
|
val exists: index -> Context_hash.t -> bool Lwt.t
|
||||||
val checkout: index -> Context_hash.t -> context option Lwt.t
|
val checkout: index -> Context_hash.t -> context option Lwt.t
|
||||||
val checkout_exn: index -> Context_hash.t -> context Lwt.t
|
val checkout_exn: index -> Context_hash.t -> context Lwt.t
|
||||||
|
val hash: time:Time.t ->
|
||||||
|
?message:string -> t -> Context_hash.t Lwt.t
|
||||||
val commit:
|
val commit:
|
||||||
time:Time.t ->
|
time:Time.t ->
|
||||||
?message:string ->
|
?message:string ->
|
||||||
@ -68,6 +70,7 @@ val commit:
|
|||||||
val set_head: index -> Chain_id.t -> Context_hash.t -> unit Lwt.t
|
val set_head: index -> Chain_id.t -> Context_hash.t -> unit Lwt.t
|
||||||
val set_master: index -> Context_hash.t -> unit Lwt.t
|
val set_master: index -> Context_hash.t -> unit Lwt.t
|
||||||
|
|
||||||
|
|
||||||
(** {2 Predefined Fields} ****************************************************)
|
(** {2 Predefined Fields} ****************************************************)
|
||||||
|
|
||||||
val get_protocol: context -> Protocol_hash.t Lwt.t
|
val get_protocol: context -> Protocol_hash.t Lwt.t
|
||||||
|
Loading…
Reference in New Issue
Block a user