Add a way to serialize Git objects into a block device
This commit is contained in:
parent
162f633a55
commit
e6254f0439
1 changed files with 86 additions and 0 deletions
|
@ -115,6 +115,92 @@ let pp_error ppf = Mirage_kv.pp_error ppf
|
||||||
|
|
||||||
let disconnect _t = Lwt.return_unit
|
let disconnect _t = Lwt.return_unit
|
||||||
|
|
||||||
|
module SHA1 = struct
|
||||||
|
include Digestif.SHA1
|
||||||
|
|
||||||
|
let hash x = Hashtbl.hash x
|
||||||
|
end
|
||||||
|
|
||||||
|
module Verbose = struct
|
||||||
|
type 'a fiber = 'a Lwt.t
|
||||||
|
|
||||||
|
let succ _ = Lwt.return_unit
|
||||||
|
let print _ = Lwt.return_unit
|
||||||
|
end
|
||||||
|
|
||||||
|
module Delta = Carton_lwt.Enc.Delta (SHA1) (Verbose)
|
||||||
|
|
||||||
|
let pack t ~commit stream =
|
||||||
|
let open Lwt.Infix in
|
||||||
|
let load t hash =
|
||||||
|
Store.read_inflated t hash >|= function
|
||||||
|
| None -> Fmt.failwith "%a not found" Digestif.SHA1.pp hash
|
||||||
|
| Some (`Commit, cs) -> Carton.Dec.v ~kind:`A (Cstruct.to_bigarray cs)
|
||||||
|
| Some (`Tree, cs) -> Carton.Dec.v ~kind:`B (Cstruct.to_bigarray cs)
|
||||||
|
| Some (`Blob, cs) -> Carton.Dec.v ~kind:`C (Cstruct.to_bigarray cs)
|
||||||
|
| Some (`Tag, cs) -> Carton.Dec.v ~kind:`D (Cstruct.to_bigarray cs) in
|
||||||
|
let to_entry ~length hash = function
|
||||||
|
| Git.Value.Commit _ -> Carton_lwt.Enc.make_entry ~kind:`A ~length hash
|
||||||
|
| Git.Value.Tree _ -> Carton_lwt.Enc.make_entry ~kind:`B ~length hash
|
||||||
|
| Git.Value.Blob _ -> Carton_lwt.Enc.make_entry ~kind:`C ~length hash
|
||||||
|
| Git.Value.Tag _ -> Carton_lwt.Enc.make_entry ~kind:`D ~length hash in
|
||||||
|
Store.fold t (fun acc ?name:_ ~length hash value ->
|
||||||
|
Lwt.return ((to_entry ~length:(Int64.to_int length) hash value) :: acc))
|
||||||
|
~path:(Fpath.v ".") [] commit >|= Array.of_list >>= fun entries ->
|
||||||
|
Delta.delta ~threads:(List.init 4 (fun _ -> load t))
|
||||||
|
~weight:10 ~uid_ln:Digestif.SHA1.digest_size entries
|
||||||
|
>>= fun targets ->
|
||||||
|
let offsets = Hashtbl.create (Array.length targets) in
|
||||||
|
let find hash = Lwt.return (Option.map Int64.to_int (Hashtbl.find_opt offsets hash)) in
|
||||||
|
let uid =
|
||||||
|
{ Carton.Enc.uid_ln= SHA1.digest_size
|
||||||
|
; Carton.Enc.uid_rw= SHA1.to_raw_string } in
|
||||||
|
let b =
|
||||||
|
{ Carton.Enc.o= Bigstringaf.create De.io_buffer_size
|
||||||
|
; Carton.Enc.i= Bigstringaf.create De.io_buffer_size
|
||||||
|
; Carton.Enc.q= De.Queue.create 0x10000
|
||||||
|
; Carton.Enc.w= De.Lz77.make_window ~bits:15 } in
|
||||||
|
let ctx = ref SHA1.empty in
|
||||||
|
let cursor = ref 0L in
|
||||||
|
let header = Bigstringaf.create 12 in
|
||||||
|
Carton.Enc.header_of_pack ~length:(Array.length targets) header 0 12 ;
|
||||||
|
stream (Some (Bigstringaf.to_string header)) ;
|
||||||
|
ctx := SHA1.feed_bigstring !ctx header ~off:0 ~len:12 ;
|
||||||
|
cursor := Int64.add !cursor 12L ;
|
||||||
|
let encode_target idx =
|
||||||
|
Hashtbl.add offsets (Carton.Enc.target_uid targets.(idx)) !cursor ;
|
||||||
|
Carton_lwt.Enc.encode_target ~b ~find ~load:(load t) ~uid targets.(idx) ~cursor:(Int64.to_int !cursor)
|
||||||
|
>>= fun (len, encoder) ->
|
||||||
|
let payload = Bigstringaf.substring b.o ~off:0 ~len in
|
||||||
|
stream (Some payload) ;
|
||||||
|
ctx := SHA1.feed_bigstring !ctx b.o ~off:0 ~len ;
|
||||||
|
cursor := Int64.add !cursor (Int64.of_int len) ;
|
||||||
|
let rec go encoder = match Carton.Enc.N.encode ~o:b.o encoder with
|
||||||
|
| `Flush (encoder, len) ->
|
||||||
|
let payload = Bigstringaf.substring b.o ~off:0 ~len in
|
||||||
|
stream (Some payload) ;
|
||||||
|
ctx := SHA1.feed_bigstring !ctx b.o ~off:0 ~len ;
|
||||||
|
cursor := Int64.add !cursor (Int64.of_int len) ;
|
||||||
|
let encoder = Carton.Enc.N.dst encoder b.o 0 (Bigstringaf.length b.o) in
|
||||||
|
go encoder
|
||||||
|
| `End -> Lwt.return_unit in
|
||||||
|
let encoder = Carton.Enc.N.dst encoder b.o 0 (Bigstringaf.length b.o) in
|
||||||
|
go encoder in
|
||||||
|
let rec go idx =
|
||||||
|
if idx < Array.length targets
|
||||||
|
then encode_target idx >>= fun () -> go (succ idx)
|
||||||
|
else Lwt.return_unit in
|
||||||
|
go 0 >>= fun () ->
|
||||||
|
let hash = SHA1.get !ctx in
|
||||||
|
stream (Some (SHA1.to_raw_string hash)) ;
|
||||||
|
stream (Some (SHA1.to_raw_string commit)) ;
|
||||||
|
(* XXX(dinosaure): PACK file + the hash of the commit. The hash of the commit
|
||||||
|
is not really needed if we assert that we store only one commit finally. We
|
||||||
|
can just, for the decoding, unpack everything and find the only commit
|
||||||
|
available into the PACK file. *)
|
||||||
|
stream None ;
|
||||||
|
Lwt.return_unit
|
||||||
|
|
||||||
let to_octets t =
|
let to_octets t =
|
||||||
(* TODO maybe preserve edn and branch as well? *)
|
(* TODO maybe preserve edn and branch as well? *)
|
||||||
let open Lwt.Infix in
|
let open Lwt.Infix in
|
||||||
|
|
Loading…
Reference in a new issue