1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
open Utils
open Result_lwt.Syntax
type entry = Commit.t =
{ parent : Commit_hash.t option
; index : Index.t
; hash : Commit_hash.t
; info : Index.t
}
type t =
{ storage_context : Storage.t
; mutable commit_tree : Commit_tree.t
; hashf : (module Hashfunc_intf.S)
}
type Error.t += Conflict
let () = Error.register_printer @@ function
| Conflict -> Some "Conflict found in Plebeia commit tree"
| _ -> None
let commit_tree t = t.commit_tree
let compute_hash t = Commit.compute_hash t.hashf
let make_commit t = Commit.make t.hashf
let add_to_commit_tree t entry index =
t.commit_tree <- Commit_tree.add t.commit_tree entry.hash {parent= entry.parent; index}
let mode t =
let smode = Storage.mode t.storage_context in
let ctmode = Commit_tree.mode t.commit_tree in
if smode = ctmode then smode
else assert false
let read_additional_commits t =
match mode t with
| Reader | Private -> Ok 0
| Writer ->
let rec aux cnt = function
| None -> Ok cnt
| Some i ->
let entry, prev = Commit_storage.read t.storage_context i in
match Commit_tree.find t.commit_tree entry.hash with
| Some {parent=_; index= i'} ->
if i <> i' then begin
Log.error "Conflict commit: storage: %a roots: %a"
Index.pp i
Index.pp i';
Error Conflict end
else
Ok cnt
| None ->
add_to_commit_tree t entry i;
aux (cnt + 1) prev
in
match aux 0 (Storage.get_last_root_index t.storage_context) with
| Ok 0 -> Ok 0
| Ok n ->
Log.debug "read %d new commits from %s" n
(Storage.filename t.storage_context);
from_Ok @@ Commit_tree.write t.commit_tree;
Ok n
| Error e -> Error e
let add t commit_entry =
let+=? i = Commit_storage.write t.storage_context commit_entry in
assert (Index.(commit_entry.info + Unsafe.of_int 3 = i));
add_to_commit_tree t commit_entry i;
from_Ok @@ Commit_tree.write t.commit_tree;
Log.debug "Added commit %a" Commit.pp commit_entry
let mem t = Commit_tree.mem t.commit_tree
let find t h =
match Commit_tree.find t.commit_tree h with
| Some {parent=_; index= idx} ->
let ctxt = t.storage_context in
let entry = Commit_storage.read ctxt idx |> fst in
Some entry
| None -> None
let commit t =
let*= () = Storage.commit t.storage_context in
Commit_tree.commit t.commit_tree
let flush t =
let*= () = Storage.flush t.storage_context in
Commit_tree.flush t.commit_tree
let may_forget t =
match Commit_tree.may_forget t.commit_tree with
| Some ct -> t.commit_tree <- ct; true
| None -> false
let create ~hash_func ~storage_context ~commit_tree =
let smode = Storage.mode storage_context in
let ctmode = Commit_tree.mode commit_tree in
if smode <> ctmode then invalid_arg "Commit_db.create";
let hashf = Hashfunc.make hash_func 32 in
let t = { storage_context; commit_tree; hashf } in
match read_additional_commits t with
| Error _ as e -> Lwt.return e
| Ok 0 -> Lwt.return_ok t
| Ok _ ->
let+= _ = flush t in
Ok t
let update_reader t =
match mode t with
| Reader ->
let*= tmp = Commit_tree.update_reader t.commit_tree in
t.commit_tree <- tmp;
Storage.update_reader t.storage_context
| _ -> Lwt.return_unit
let parent t entry =
match entry.parent with
| None -> Ok None
| Some parent ->
match Commit_tree.find t.commit_tree parent with
| None -> Error `Not_found
| Some {parent=_grand_parent; index= i} ->
let ctxt = t.storage_context in
let (entry, _prev_index) = Commit_storage.read ctxt i in
Ok (Some entry)
let fold f t acc =
let*= () = update_reader t in
Commit_tree.fold (fun h {index=i; parent=_} acc ->
let ent, _ = Commit_storage.read t.storage_context i in
assert (ent.hash = h);
let parent = parent t ent in
f ent ~parent acc) t.commit_tree acc
let to_list t = fold (fun ent ~parent:_ acc -> Lwt.return (ent::acc)) t []
let read_the_latest { storage_context; _} = Commit_storage.read_the_latest storage_context
let children t =
let*= () = update_reader t in
let+= f = Commit_tree.children t.commit_tree in
fun h ->
let hents = f h in
List.map (fun (_, {Commit_tree.index; _}) ->
fst @@ Commit_storage.read t.storage_context index)
hents
let geneses t =
let*= () = update_reader t in
let+= hents = Commit_tree.geneses t.commit_tree in
List.map (fun (_, {Commit_tree.index; _}) ->
fst @@ Commit_storage.read t.storage_context index)
hents
let ordered_fold f t acc =
let*= () = update_reader t in
let commit_tree = t.commit_tree in
Commit_tree.ordered_fold (fun _ch ent ~children acc ->
let ent = fst @@ Commit_storage.read t.storage_context ent.index in
let children =
List.map (fun (_,ent) ->
fst @@ Commit_storage.read t.storage_context ent.Commit_tree.index) children
in
f ent ~children acc)
commit_tree acc