400 lines
14 KiB
OCaml
400 lines
14 KiB
OCaml
(*****************************************************************************)
|
|
(* *)
|
|
(* Open Source License *)
|
|
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
(* *)
|
|
(* Permission is hereby granted, free of charge, to any person obtaining a *)
|
|
(* copy of this software and associated documentation files (the "Software"),*)
|
|
(* to deal in the Software without restriction, including without limitation *)
|
|
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
|
|
(* and/or sell copies of the Software, and to permit persons to whom the *)
|
|
(* Software is furnished to do so, subject to the following conditions: *)
|
|
(* *)
|
|
(* The above copyright notice and this permission notice shall be included *)
|
|
(* in all copies or substantial portions of the Software. *)
|
|
(* *)
|
|
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
|
|
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
|
|
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
|
|
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
|
|
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
|
|
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
|
|
(* DEALINGS IN THE SOFTWARE. *)
|
|
(* *)
|
|
(*****************************************************************************)
|
|
|
|
open Store_sigs
|
|
|
|
module Make_value (V : ENCODED_VALUE) = struct
|
|
type t = V.t
|
|
let of_bytes b =
|
|
match Data_encoding.Binary.of_bytes V.encoding b with
|
|
| None -> generic_error "Cannot parse data" (* TODO personalize *)
|
|
| Some v -> ok v
|
|
let to_bytes v =
|
|
try Data_encoding.Binary.to_bytes_exn V.encoding v
|
|
with Data_encoding.Binary.Write_error error ->
|
|
Store_logging.log_error
|
|
"Exception while serializing value %a"
|
|
Data_encoding.Binary.pp_write_error error ;
|
|
MBytes.create 0
|
|
end
|
|
|
|
module Raw_value = struct
|
|
type t = MBytes.t
|
|
let of_bytes b = ok b
|
|
let to_bytes b = b
|
|
end
|
|
|
|
module Make_single_store (S : STORE) (N : NAME) (V : VALUE) = struct
|
|
type t = S.t
|
|
type value = V.t
|
|
let known t = S.known t N.name
|
|
let read t = S.read t N.name >>=? fun b -> Lwt.return (V.of_bytes b)
|
|
let read_opt t =
|
|
read t >|= function
|
|
| Error _ -> None
|
|
| Ok v -> Some v
|
|
let read_exn t =
|
|
read t >>= function
|
|
| Error _ -> Lwt.fail Not_found
|
|
| Ok v -> Lwt.return v
|
|
let store t v = S.store t N.name (V.to_bytes v)
|
|
let remove t = S.remove t N.name
|
|
end
|
|
|
|
let map_key f = function
|
|
|`Key k -> `Key (f k)
|
|
| `Dir k -> `Dir (f k)
|
|
|
|
module Make_substore (S : STORE) (N : NAME)
|
|
: STORE with type t = S.t = struct
|
|
type t = S.t
|
|
type key = string list
|
|
type value = MBytes.t
|
|
let name_length = List.length N.name
|
|
let to_key k = N.name @ k
|
|
let of_key k = List.remove name_length k
|
|
let known t k = S.known t (to_key k)
|
|
let known_dir t k = S.known_dir t (to_key k)
|
|
let read t k = S.read t (to_key k)
|
|
let read_opt t k = S.read_opt t (to_key k)
|
|
let read_exn t k = S.read_exn t (to_key k)
|
|
let store t k v = S.store t (to_key k) v
|
|
let remove t k = S.remove t (to_key k)
|
|
let fold t k ~init ~f =
|
|
S.fold t (to_key k) ~init
|
|
~f:(fun k acc -> f (map_key of_key k) acc)
|
|
let keys t k = S.keys t (to_key k) >|= fun keys -> List.map of_key keys
|
|
let fold_keys t k ~init ~f =
|
|
S.fold_keys t (to_key k) ~init ~f:(fun k acc -> f (of_key k) acc)
|
|
let remove_dir t k = S.remove_dir t (to_key k)
|
|
end
|
|
|
|
module Make_indexed_substore (S : STORE) (I : INDEX) = struct
|
|
|
|
type t = S.t
|
|
type key = I.t
|
|
|
|
module Store = struct
|
|
type t = S.t * I.t
|
|
type key = string list
|
|
type value = MBytes.t
|
|
let to_key i k =
|
|
assert (List.length (I.to_path i []) = I.path_length) ;
|
|
I.to_path i k
|
|
let of_key k = List.remove I.path_length k
|
|
let known (t,i) k = S.known t (to_key i k)
|
|
let known_dir (t,i) k = S.known_dir t (to_key i k)
|
|
let read (t,i) k = S.read t (to_key i k)
|
|
let read_opt (t,i) k = S.read_opt t (to_key i k)
|
|
let read_exn (t,i) k = S.read_exn t (to_key i k)
|
|
let store (t,i) k v = S.store t (to_key i k) v
|
|
let remove (t,i) k = S.remove t (to_key i k)
|
|
let fold (t,i) k ~init ~f =
|
|
S.fold t (to_key i k) ~init
|
|
~f:(fun k acc -> f (map_key of_key k) acc)
|
|
let keys (t,i) k = S.keys t (to_key i k) >|= fun keys -> List.map of_key keys
|
|
let fold_keys (t,i) k ~init ~f =
|
|
S.fold_keys t (to_key i k) ~init ~f:(fun k acc -> f (of_key k) acc)
|
|
let remove_dir (t,i) k = S.remove_dir t (to_key i k)
|
|
end
|
|
|
|
let remove_all t i = Store.remove_dir (t, i) []
|
|
|
|
let fold_indexes t ~init ~f =
|
|
let rec dig i path acc =
|
|
if i <= 0 then
|
|
match I.of_path path with
|
|
| None -> assert false
|
|
| Some path -> f path acc
|
|
else
|
|
S.fold t path ~init:acc ~f:begin fun k acc ->
|
|
match k with
|
|
| `Dir k -> dig (i-1) k acc
|
|
| `Key _ -> Lwt.return acc
|
|
end in
|
|
dig I.path_length [] init
|
|
|
|
let indexes t =
|
|
fold_indexes t ~init:[] ~f:(fun i acc -> Lwt.return (i :: acc))
|
|
|
|
let list t k = S.fold t k ~init:[] ~f:(fun k acc -> Lwt.return (k :: acc))
|
|
let resolve_index t prefix =
|
|
let rec loop i prefix = function
|
|
| [] when i = I.path_length -> begin
|
|
match I.of_path prefix with
|
|
| None -> assert false
|
|
| Some path -> Lwt.return [path]
|
|
end
|
|
| [] ->
|
|
list t prefix >>= fun prefixes ->
|
|
Lwt_list.map_p (function
|
|
| `Key prefix | `Dir prefix -> loop (i+1) prefix []) prefixes
|
|
>|= List.flatten
|
|
| [d] when i = I.path_length - 1 ->
|
|
if (i >= I.path_length) then invalid_arg "IO.resolve" ;
|
|
list t prefix >>= fun prefixes ->
|
|
Lwt_list.map_p (function
|
|
| `Key prefix | `Dir prefix ->
|
|
match String.remove_prefix ~prefix:d (List.hd (List.rev prefix)) with
|
|
| None -> Lwt.return_nil
|
|
| Some _ -> loop (i+1) prefix [])
|
|
prefixes
|
|
>|= List.flatten
|
|
| "" :: ds ->
|
|
list t prefix >>= fun prefixes ->
|
|
Lwt_list.map_p (function
|
|
| `Key prefix | `Dir prefix -> loop (i+1) prefix ds) prefixes
|
|
>|= List.flatten
|
|
| d :: ds ->
|
|
if (i >= I.path_length) then invalid_arg "IO.resolve" ;
|
|
S.known_dir t (prefix @ [d]) >>= function
|
|
| true -> loop (i+1) (prefix @ [d]) ds
|
|
| false -> Lwt.return_nil in
|
|
loop 0 [] prefix
|
|
|
|
module Make_set (N : NAME) = struct
|
|
type t = S.t
|
|
type elt = I.t
|
|
let inited = MBytes.of_string "inited"
|
|
let known s i = Store.known (s, i) N.name
|
|
let store s i = Store.store (s, i) N.name inited
|
|
let remove s i = Store.remove (s, i) N.name
|
|
let remove_all s =
|
|
fold_indexes s ~init:() ~f:(fun i () -> remove s i)
|
|
let fold s ~init ~f =
|
|
fold_indexes s ~init
|
|
~f:(fun i acc ->
|
|
known s i >>= function
|
|
| true -> f i acc
|
|
| false -> Lwt.return acc)
|
|
let elements s =
|
|
fold s ~init:[] ~f:(fun p acc -> Lwt.return (p :: acc))
|
|
let iter s ~f =
|
|
fold s ~init:() ~f:(fun p () -> f p)
|
|
end
|
|
|
|
module Make_buffered_set (N : NAME) (Set : Set.S with type elt = I.t) = struct
|
|
include Make_set (N)
|
|
module Set = Set
|
|
let read_all s =
|
|
fold s ~init:Set.empty ~f:(fun i set -> Lwt.return (Set.add i set))
|
|
let store_all s new_set =
|
|
read_all s >>= fun old_set ->
|
|
Lwt_list.iter_p (remove s)
|
|
Set.(elements (diff old_set new_set)) >>= fun () ->
|
|
Lwt_list.iter_p (store s) Set.(elements (diff new_set old_set))
|
|
end
|
|
|
|
module Make_map (N : NAME) (V : VALUE) = struct
|
|
type t = S.t
|
|
type key = I.t
|
|
type value = V.t
|
|
let known s i = Store.known (s,i) N.name
|
|
let read s i =
|
|
Store.read (s,i) N.name >>=? fun b -> Lwt.return (V.of_bytes b)
|
|
let read_opt s i =
|
|
read s i >>= function
|
|
| Error _ -> Lwt.return_none
|
|
| Ok v -> Lwt.return_some v
|
|
let read_exn s i =
|
|
read s i >>= function
|
|
| Error _ -> Lwt.fail Not_found
|
|
| Ok v -> Lwt.return v
|
|
let store s i v = Store.store (s,i) N.name (V.to_bytes v)
|
|
let remove s i = Store.remove (s,i) N.name
|
|
let remove_all s = fold_indexes s ~init:() ~f:(fun i () -> remove s i)
|
|
let fold s ~init ~f =
|
|
fold_indexes s ~init
|
|
~f:(fun i acc ->
|
|
read_opt s i >>= function
|
|
| None -> Lwt.return acc
|
|
| Some v -> f i v acc)
|
|
let bindings s =
|
|
fold s ~init:[] ~f:(fun p v acc -> Lwt.return ((p,v) :: acc))
|
|
let iter s ~f =
|
|
fold s ~init:() ~f:(fun p v () -> f p v)
|
|
let fold_keys s ~init ~f =
|
|
fold_indexes s ~init
|
|
~f:(fun i acc ->
|
|
known s i >>= function
|
|
| false -> Lwt.return acc
|
|
| true -> f i acc)
|
|
let keys s =
|
|
fold_keys s ~init:[] ~f:(fun p acc -> Lwt.return (p :: acc))
|
|
let iter_keys s ~f =
|
|
fold_keys s ~init:() ~f:(fun p () -> f p)
|
|
end
|
|
|
|
module Make_buffered_map
|
|
(N : NAME) (V : VALUE)
|
|
(Map : Map.S with type key = I.t) = struct
|
|
include Make_map (N) (V)
|
|
module Map = Map
|
|
let read_all s =
|
|
fold s ~init:Map.empty ~f:(fun i v set -> Lwt.return (Map.add i v set))
|
|
let store_all s map =
|
|
remove_all s >>= fun () ->
|
|
Map.fold
|
|
(fun k v acc -> let res = store s k v in acc >>= fun () -> res)
|
|
map Lwt.return_unit
|
|
end
|
|
|
|
end
|
|
|
|
module Make_set (S : STORE) (I : INDEX) = struct
|
|
type t = S.t
|
|
type elt = I.t
|
|
let inited = MBytes.of_string "inited"
|
|
let known s i = S.known s (I.to_path i [])
|
|
let store s i = S.store s (I.to_path i []) inited
|
|
let remove s i = S.remove s (I.to_path i [])
|
|
let remove_all s = S.remove_dir s []
|
|
|
|
let fold s ~init ~f =
|
|
let rec dig i path acc =
|
|
if i <= 1 then
|
|
S.fold s path ~init:acc ~f:begin fun k acc ->
|
|
match k with
|
|
| `Dir _ -> Lwt.return acc
|
|
| `Key file ->
|
|
match I.of_path file with
|
|
| None -> assert false
|
|
| Some p -> f p acc
|
|
end
|
|
else
|
|
S.fold s path ~init:acc ~f:begin fun k acc ->
|
|
match k with
|
|
| `Dir k ->
|
|
dig (i-1) k acc
|
|
| `Key _ ->
|
|
Lwt.return acc
|
|
end in
|
|
dig I.path_length [] init
|
|
|
|
let elements s =
|
|
fold s ~init:[] ~f:(fun p acc -> Lwt.return (p :: acc))
|
|
let iter s ~f =
|
|
fold s ~init:() ~f:(fun p () -> f p)
|
|
end
|
|
|
|
module Make_buffered_set
|
|
(S : STORE) (I : INDEX) (Set : Set.S with type elt = I.t) = struct
|
|
include Make_set (S) (I)
|
|
module Set = Set
|
|
let read_all s =
|
|
fold s ~init:Set.empty ~f:(fun i set -> Lwt.return (Set.add i set))
|
|
let store_all s new_set =
|
|
read_all s >>= fun old_set ->
|
|
Lwt_list.iter_p (remove s) Set.(elements (diff old_set new_set)) >>= fun () ->
|
|
Lwt_list.iter_p (store s) Set.(elements (diff new_set old_set))
|
|
end
|
|
|
|
module Make_map (S : STORE) (I : INDEX) (V : VALUE) = struct
|
|
type t = S.t
|
|
type key = I.t
|
|
type value = V.t
|
|
let known s i = S.known s (I.to_path i [])
|
|
let read s i =
|
|
S.read s (I.to_path i []) >>=? fun b -> Lwt.return (V.of_bytes b)
|
|
let read_opt s i =
|
|
read s i >>= function
|
|
| Error _ -> Lwt.return_none
|
|
| Ok v -> Lwt.return_some v
|
|
let read_exn s i =
|
|
read s i >>= function
|
|
| Error _ -> Lwt.fail Not_found
|
|
| Ok v -> Lwt.return v
|
|
let store s i v = S.store s (I.to_path i []) (V.to_bytes v)
|
|
let remove s i = S.remove s (I.to_path i [])
|
|
let remove_all s = S.remove_dir s []
|
|
let fold s ~init ~f =
|
|
let rec dig i path acc =
|
|
if i <= 1 then
|
|
S.fold s path ~init:acc ~f:begin fun k acc ->
|
|
match k with
|
|
| `Dir _ -> Lwt.return acc
|
|
| `Key file ->
|
|
S.read_opt s file >>= function
|
|
| None -> Lwt.return acc
|
|
| Some b ->
|
|
match V.of_bytes b with
|
|
| Error _ ->
|
|
(* Silently ignore unparsable data *)
|
|
Lwt.return acc
|
|
| Ok v ->
|
|
match I.of_path file with
|
|
| None -> assert false
|
|
| Some path -> f path v acc
|
|
end
|
|
else
|
|
S.fold s path ~init:acc ~f:begin fun k acc ->
|
|
match k with
|
|
| `Dir k -> dig (i-1) k acc
|
|
| `Key _ -> Lwt.return acc
|
|
end in
|
|
dig I.path_length [] init
|
|
|
|
let bindings s =
|
|
fold s ~init:[] ~f:(fun p v acc -> Lwt.return ((p,v) :: acc))
|
|
let iter s ~f =
|
|
fold s ~init:() ~f:(fun p v () -> f p v)
|
|
let fold_keys s ~init ~f =
|
|
S.fold s [] ~init
|
|
~f:(fun p acc ->
|
|
match p with
|
|
| `Dir _ -> Lwt.return acc
|
|
| `Key p ->
|
|
match I.of_path p with
|
|
| None -> assert false
|
|
| Some path -> f path acc)
|
|
let keys s =
|
|
fold_keys s ~init:[] ~f:(fun p acc -> Lwt.return (p :: acc))
|
|
let iter_keys s ~f =
|
|
fold_keys s ~init:() ~f:(fun p () -> f p)
|
|
end
|
|
|
|
module Make_buffered_map
|
|
(S : STORE) (I : INDEX) (V : VALUE)
|
|
(Map : Map.S with type key = I.t) = struct
|
|
include Make_map (S) (I) (V)
|
|
module Map = Map
|
|
let read_all s =
|
|
fold s ~init:Map.empty ~f:(fun i v set -> Lwt.return (Map.add i v set))
|
|
let store_all s map =
|
|
remove_all s >>= fun () ->
|
|
Map.fold
|
|
(fun k v acc -> let res = store s k v in acc >>= fun () -> res)
|
|
map Lwt.return_unit
|
|
end
|
|
|
|
module Integer_index = struct
|
|
type t = int
|
|
let path_length = 1
|
|
let to_path x l = string_of_int x :: l
|
|
let of_path = function
|
|
| [x] -> begin try Some (int_of_string x) with _ -> None end
|
|
| _ -> None
|
|
end
|