Source file mirage_kv_mem.ml

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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
type write_error = [ Mirage_kv.write_error | `Rename_source_prefix | `Rename_source_is_root ]
let pp_write_error ppf = function
  | #Mirage_kv.write_error as e -> Mirage_kv.pp_write_error ppf e
  | `Rename_source_prefix -> Fmt.string ppf "rename: source is a prefix of dest"
  | `Rename_source_is_root -> Fmt.string ppf "rename: source is root"

type error = Mirage_kv.error
let pp_error = Mirage_kv.pp_error

module Pure = struct

  module M = Map.Make(String)

  let ( let* ) = Result.bind

  type t =
   | Dictionary of Ptime.t * t M.t
   | Value of Ptime.t * string

  type key = Mirage_kv.Key.t

  let empty now () = Dictionary (now, M.empty)

  let get_node t key =
    let rec find t = function
      | [] -> Ok t
      | hd::tl -> match t with
        | Value _ -> Error (`Dictionary_expected key)
        | Dictionary (_, m) ->
          match M.find_opt hd m with
          | Some t' -> find t' tl
          | None -> Error (`Not_found key)
    in
    find t (Mirage_kv.Key.segments key)

  let get t key =
    let* v = get_node t key in
    match v with
    | Dictionary _ -> Error (`Value_expected key)
    | Value (_, value) -> Ok value

  let size t key =
    let* v = get t key in
    Ok (String.length v)

  let get_partial t key ~offset ~length =
    let* v = get t key in
    if String.length v < offset then
      Ok ""
    else
      Ok (String.sub v offset ((min (String.length v) (offset + length)) - offset))

  let last_modified t key =
    let* v = get_node t key in
    match v with
    | Dictionary (mtime, _) -> Ok mtime
    | Value (mtime, _) -> Ok mtime

  let remove t key now =
    let rec remove t = function
      | [] -> Ok (Dictionary (now, M.empty))
      | [x] -> begin match t with
          | Value _ -> Error (`Dictionary_expected key)
          | Dictionary (_, m) ->
            let m' = M.remove x m in
            Ok (Dictionary (now, m'))
        end
      | hd::tl -> match t with
        | Value _ -> Error (`Dictionary_expected key)
        | Dictionary (mtime, m) ->
          let* node =
            match M.find_opt hd m with
            | None -> Error (`Dictionary_expected key)
            | Some t' -> Ok t'
          in
          let* t' = remove node tl in
          let m' = M.add hd t' m in
          Ok (Dictionary (mtime, m'))
    in
    remove t (Mirage_kv.Key.segments key)

  let list t key =
    let* v = get_node t key in
    match v with
    | Value _ -> Error (`Dictionary_expected key)
    | Dictionary (_, m) ->
      let name_and_kind (k, v) =
        k, match v with Value _ -> `Value | Dictionary _ -> `Dictionary
      in
      Ok (List.map name_and_kind @@ M.bindings m)

  let set t key now data =
    let value = Value (now, data) in
    let rec add t' = function
      | [] -> Ok value
      | [x] ->
        begin match t' with
          | Value _ -> Error (`Dictionary_expected key)
          | Dictionary (_, m) -> Ok (Dictionary (now, M.add x value m))
        end
      | hd::tl ->
        begin
          match t' with
          | Value _ -> Error (`Dictionary_expected key)
          | Dictionary (mtime, m) ->
            let node = match M.find_opt hd m with
              | None -> Dictionary (now, M.empty)
              | Some t'' -> t''
            in
            let* t''' = add node tl in
            let m' = M.add hd t''' m in
            Ok (Dictionary (mtime, m'))
        end
    in
    add t (Mirage_kv.Key.segments key)

  let set_partial t key now ~offset data =
    match get t key with
    | Ok v ->
      let v' = String.sub v 0 (min offset (String.length v)) in
      let v'' =
        let start = min (String.length v) (offset + String.length data) in
        String.sub v start (String.length v - start)
      in
      set t key now (v' ^ data ^ v'')
    | Error (`Not_found _) -> set t key now data
    | Error _ as e -> e

  let rename t ~source ~dest now =
    match get_node t source with
    | Error _ as e -> e
    | Ok (Value (n, v)) ->
      let* t = remove t source now in
      begin match get_node t dest with
        | Error _ -> set t dest n v
        | Ok (Value _) -> set t dest n v
        | Ok (Dictionary _) ->
          let* last_seg = match List.rev (Mirage_kv.Key.segments source) with
            | hd::_ -> Ok hd
            | [] -> Error `Rename_source_is_root
          in
          set t (Mirage_kv.Key.add dest last_seg) n v
      end
    | Ok (Dictionary _ as d) ->
      let set_dictionary t name =
        let rec go t' = function
          | [] -> Ok d
          | [x] ->
            begin match t' with
              | Value _ -> Error (`Dictionary_expected name)
              | Dictionary (_, m) -> Ok (Dictionary (now, M.add x d m))
            end
          | hd::tl ->
            begin
              match t' with
              | Value _ -> Error (`Dictionary_expected name)
              | Dictionary (mtime, m) ->
                let node = match M.find_opt hd m with
                  | None -> Dictionary (now, M.empty)
                  | Some t'' -> t''
                in
                let* t''' = go node tl in
                let m' = M.add hd t''' m in
                Ok (Dictionary (mtime, m'))
            end
        in
        go t (Mirage_kv.Key.segments name)
      in
      match get_node t dest with
      | Error _ ->
        let* t = remove t source now in
        set_dictionary t dest
      | Ok (Value _) -> Error (`Value_expected source)
      | Ok (Dictionary _) ->
        let srcstr = Mirage_kv.Key.to_string source in
        let dststr = Mirage_kv.Key.to_string dest in
        if String.length dststr >= String.length srcstr &&
           String.(equal srcstr (String.sub dststr 0 (String.length srcstr)))
        then
          Error `Rename_source_prefix
        else
          let* last_seg =
            match List.rev (Mirage_kv.Key.segments source) with
            | [] -> Error `Rename_source_is_root
            | last_seg :: _ -> Ok last_seg
          in
          let* t = remove t source now in
          set_dictionary t (Mirage_kv.Key.add dest last_seg)

  let pp fmt t =
    let rec pp_things ?(prefix = "") () fmt = function
      | Value (mtime, v) -> Fmt.pf fmt "Value %s %d (modified %a): %s@."
                              prefix (String.length v) (Ptime.pp_rfc3339 ()) mtime v
      | Dictionary (_, m) ->
        List.iter (fun (k, v) ->
            pp_things ~prefix:(prefix ^ "/" ^ k) () fmt v)
          (M.bindings m)
    in
    pp_things () fmt t

  let rec equal t t' = match t, t' with
    | Value (_, v), Value (_, v') -> String.equal v v'
    | Dictionary (_, m), Dictionary (_, m') -> M.equal equal m m'
    | _ -> false

end

module Make (CLOCK : Mirage_clock.PCLOCK) = struct
  type key = Mirage_kv.Key.t

  [@@@warning "-34"]
  type nonrec error = error
  let pp_error = pp_error

  [@@@warning "-34"]
  type nonrec write_error = write_error
  let pp_write_error = pp_write_error

  let now () = Ptime.v (CLOCK.now_d_ps ())

  let connect () = Lwt.return (ref (Pure.empty (now ()) ()))
  let disconnect _t = Lwt.return ()

  type t = Pure.t ref

  let last_modified dict key =
    Lwt.return @@ match Pure.last_modified !dict key with
    | Ok mtime -> Ok Ptime.(Span.to_d_ps (to_span mtime))
    | Error e -> Error e

  let digest dict key =
    Lwt.return @@ match Pure.get_node !dict key with
    | Ok (Value (_, data)) -> Ok (Digest.string data)
    | Ok (Dictionary (mtime, dict)) ->
      let data = Fmt.to_to_string Pure.pp (Dictionary (mtime, dict)) in
      Ok (Digest.string data)
    | Error e -> Error e

  let batch dict ?retries:_ f = f dict
  (* //cc samoht is this correct for in-memory store behaviour? *)

  let exists dict key =
    Lwt.return @@ match Pure.get_node !dict key with
    | Ok (Value _) -> Ok (Some `Value)
    | Ok (Dictionary _) -> Ok (Some `Dictionary)
    | Error (`Not_found _) -> Ok None
    | Error e -> Error e

  let get dict key = Lwt.return @@ Pure.get !dict key

  let get_partial dict key ~offset ~length =
    Lwt.return @@ Pure.get_partial !dict key ~offset ~length

  let size dict key = Lwt.return @@ Pure.size !dict key

  let remove dict key = Lwt.return @@ match Pure.remove !dict key (now ()) with
    | Error e -> Error e
    | Ok dict' -> dict := dict'; Ok ()

  let list dict key = Lwt.return @@ Pure.list !dict key

  let set dict key data = Lwt.return @@ match Pure.set !dict key (now ()) data with
    | Error e -> Error e
    | Ok dict' -> dict := dict'; Ok ()

  let set_partial dict key ~offset data =
    Lwt.return @@ match Pure.set_partial !dict key (now ()) ~offset data with
    | Error e -> Error e
    | Ok dict' -> dict := dict'; Ok ()

  let rename dict ~source ~dest =
    Lwt.return @@ match Pure.rename !dict ~source ~dest (now ()) with
    | Error e -> Error e
    | Ok dict' -> dict := dict'; Ok ()

  let pp fmt dict = Pure.pp fmt !dict

  let equal a b = Pure.equal !a !b
end