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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
open Core
module Make_plain (Key : sig
type t [@@deriving sexp_of]
include Comparator.S with type t := t
end) (Value : sig
type t [@@deriving sexp_of]
val equal : t -> t -> bool
end) =
struct
module Update = struct
module Diff = struct
type t =
| Remove of Key.t
| Add of Key.t * Value.t
| Idle of unit
[@@deriving sexp_of]
let idle = Idle ()
end
type t = Diff.t list [@@deriving sexp_of]
end
type t = Value.t Map.M(Key).t [@@deriving sexp_of]
let empty = Map.empty (module Key)
let update t diffs =
List.fold diffs ~init:t ~f:(fun t d ->
match (d : Update.Diff.t) with
| Remove key -> Map.remove t key
| Add (key, data) -> Map.set t ~key ~data
| Idle () -> t)
;;
let diffs ~from ~to_ : Update.t =
let data_equal x1 x2 = phys_equal x1 x2 || Value.equal x1 x2 in
Map.fold_symmetric_diff ~init:[] ~data_equal from to_ ~f:(fun acc (k, d) ->
match d with
| `Left _ -> Update.Diff.Remove k :: acc
| `Right i | `Unequal (_, i) -> Add (k, i) :: acc)
;;
let of_diffs diffs = update empty diffs
let to_diffs t : Update.t =
let l =
Map.fold_right t ~init:[] ~f:(fun ~key ~data tail ->
Update.Diff.Add (key, data) :: tail)
in
match l with
| [] -> [ Update.Diff.idle ]
| l -> l
;;
end
module Make (Key : sig
type t [@@deriving sexp, bin_io]
include Comparator.S with type t := t
end) (Value : sig
type t [@@deriving sexp, bin_io]
val equal : t -> t -> bool
end) =
struct
module Plain = Make_plain (Key) (Value)
module Update = struct
module Diff = struct
type t = Plain.Update.Diff.t =
| Remove of Key.t
| Add of Key.t * Value.t
| Idle of unit
[@@deriving sexp, bin_io]
end
type t = Diff.t list [@@deriving sexp, bin_io]
end
include (
Plain :
module type of struct
include Plain
end
with module Update := Plain.Update)
end
let%test_module "tests of Make and Make_plain" =
(module struct
module T = struct
include Make_plain (Int) (Int)
type t = int Int.Map.t [@@deriving compare, equal, sexp_of]
let quickcheck_generator =
Int.Map.quickcheck_generator Int.quickcheck_generator Int.quickcheck_generator
;;
end
include T
let%expect_test "map round-trip works" =
let module Test_case = struct
type t =
{ t : T.t
; to_diffs : T.Update.t
}
[@@deriving sexp_of]
end
in
Quickcheck.test
~sexp_of:[%sexp_of: Test_case.t]
(let open Quickcheck.Let_syntax in
let%bind t = quickcheck_generator in
let%map to_diffs = List.gen_permutations (to_diffs t) in
({ t; to_diffs } : Test_case.t))
~f:(fun { t; to_diffs } -> [%test_result: t] ~expect:t (of_diffs to_diffs));
[%expect {| |}]
;;
let%expect_test "map diff/update works" =
let module Test_case = struct
type t =
{ from : T.t
; to_ : T.t
; diffs : T.Update.t
}
[@@deriving sexp_of]
end
in
Quickcheck.test
~sexp_of:[%sexp_of: Test_case.t]
(let open Quickcheck.Let_syntax in
let%bind from = quickcheck_generator
and to_ = quickcheck_generator in
let%map diffs = List.gen_permutations (diffs ~from ~to_) in
({ from; to_; diffs } : Test_case.t))
~f:(fun { from; to_; diffs } -> [%test_result: t] ~expect:to_ (update from diffs));
[%expect {| |}]
;;
end)
;;
module Make_plain_with_value_diffs (Key : sig
type t [@@deriving sexp_of]
include Comparator.S with type t := t
end) (Value : sig
type t
include Legacy_diffable_intf.S_plain with type t := t
end) =
struct
module Update = struct
module Diff = struct
type t =
| Remove of Key.t
| Change of Key.t * Value.Update.Diff.t
| Add of Key.t * Value.Update.Diff.t
| Idle of unit
(** We add a [unit] argument so matching the more common cases is more performant
(no check for immediate vs. block). *)
[@@deriving sexp_of]
let idle = Idle ()
end
type t = Diff.t list [@@deriving sexp_of]
end
type t = Value.t Map.M(Key).t
let empty = Map.empty (module Key)
let update =
let group_diffs =
let module State = struct
type t =
| Out_of_group of { diffs : Update.t }
| In_group of
{ key : Key.t
; type_ : [ `Add | `Change ]
; group : Update.t Stored_reversed.t
; diffs : Update.t
}
end
in
let open State in
let step = function
| In_group { key; type_; group; diffs } ->
(match type_, diffs with
| `Change, (Change (next_key, _) as change) :: diffs
when Key.comparator.compare next_key key = 0 ->
Sequence.Step.Skip
{ state =
In_group
{ key; type_; diffs; group = Stored_reversed.snoc group change }
}
| `Add, (Add (next_key, _) as add) :: diffs
when Key.comparator.compare next_key key = 0 ->
Sequence.Step.Skip
{ state =
In_group { key; type_; diffs; group = Stored_reversed.snoc group add }
}
| _, diffs ->
Sequence.Step.Yield { value = group; state = Out_of_group { diffs } })
| Out_of_group { diffs } ->
(match diffs with
| [] -> Sequence.Step.Done
| (Remove _ as single) :: diffs ->
Sequence.Step.Yield
{ value = Stored_reversed.singleton single
; state = Out_of_group { diffs }
}
| (Add (key, _) as add) :: diffs ->
Sequence.Step.Skip
{ state =
In_group
{ key; type_ = `Add; group = Stored_reversed.singleton add; diffs }
}
| (Change (key, _) as change) :: diffs ->
Sequence.Step.Skip
{ state =
In_group
{ key
; type_ = `Change
; group = Stored_reversed.singleton change
; diffs
}
}
| Idle () :: diffs -> Sequence.Step.Skip { state = Out_of_group { diffs } })
in
fun diffs -> Sequence.unfold_step ~init:(Out_of_group { diffs }) ~f:step
in
fun t diffs ->
Sequence.fold ~init:t (group_diffs diffs) ~f:(fun t ds ->
match Stored_reversed.to_list_rev ds with
| Add (key, _) :: _ ->
Map.set
t
~key
~data:
(Value.of_diffs
(Stored_reversed.map_to_list ds ~f:(function
| Change _ | Remove _ | Idle () ->
failwith "BUG: The impossible happened. Change/Remove in add group."
| Add (_, x) -> x)))
| [ Remove key ] -> Map.remove t key
| Change (key, _) :: _ ->
Map.update t key ~f:(function
| None ->
failwith "BUG: The impossible happened. Update to a non existing key."
| Some value ->
Value.update
value
(Stored_reversed.map_to_list ds ~f:(function
| Add _ | Remove _ | Idle () ->
failwith "BUG: The impossible happened. Add/Remove in change group."
| Change (_, x) -> x)))
| _ ->
failwith
"BUG: The impossible happened. Expected single Add/Remove or multiple Change \
in a group.")
;;
let diffs ~from ~to_ : Update.t =
Map.fold_symmetric_diff
~init:Stored_reversed.empty
~data_equal:phys_equal
from
to_
~f:(fun acc (k, d) ->
match d with
| `Left _ -> Stored_reversed.snoc acc (Update.Diff.Remove k)
| `Right to_ ->
let diffs = Value.to_diffs to_ in
Stored_reversed.map_append acc diffs ~f:(fun x -> Update.Diff.Add (k, x))
| `Unequal (from, to_) ->
let diffs = Value.diffs ~from ~to_ in
Stored_reversed.map_append acc diffs ~f:(fun x -> Update.Diff.Change (k, x)))
|> Stored_reversed.to_list
;;
let of_diffs = update empty
let to_diffs t =
let l =
Map.fold_right t ~init:[] ~f:(fun ~key ~data tail ->
let rev_instructions = Stored_reversed.of_list (Value.to_diffs data) in
Stored_reversed.map_to_list ~tail rev_instructions ~f:(fun x ->
Update.Diff.Add (key, x)))
in
match l with
| [] -> [ Update.Diff.idle ]
| l -> l
;;
end
module Make_with_value_diffs (Key : sig
type t [@@deriving sexp, bin_io]
include Comparator.S with type t := t
end) (Value : sig
type t
include Legacy_diffable_intf.S with type t := t
end) =
struct
module Plain = Make_plain_with_value_diffs (Key) (Value)
module Update = struct
module Diff = struct
type t = Plain.Update.Diff.t =
| Remove of Key.t
| Change of Key.t * Value.Update.Diff.t
| Add of Key.t * Value.Update.Diff.t
| Idle of unit
[@@deriving sexp, bin_io]
end
type t = Diff.t list [@@deriving sexp, bin_io]
end
include (
Plain :
module type of struct
include Plain
end
with module Update := Plain.Update)
end
let%test_module "tests of Make_with_value_diffs" =
(module struct
module T = struct
include
Make_with_value_diffs
(Int)
(struct
include Int
include Atomic.Make (Int)
end)
type t = int Int.Map.t [@@deriving compare, equal, sexp_of]
let quickcheck_generator =
Int.Map.quickcheck_generator Int.quickcheck_generator Int.quickcheck_generator
;;
end
include T
let%expect_test "map with value diffs round-trip works" =
let module Test_case = struct
type t =
{ t : T.t
; to_diffs : T.Update.t
}
[@@deriving sexp_of]
end
in
Quickcheck.test
~sexp_of:[%sexp_of: Test_case.t]
(let open Quickcheck.Let_syntax in
let%bind t = quickcheck_generator in
let%map to_diffs = List.gen_permutations (to_diffs t) in
({ t; to_diffs } : Test_case.t))
~f:(fun { t; to_diffs } -> [%test_result: t] ~expect:t (of_diffs to_diffs));
[%expect {| |}]
;;
let%expect_test "map with value diffs diff/update works" =
let module Test_case = struct
type t =
{ from : T.t
; to_ : T.t
; diffs : T.Update.t
}
[@@deriving sexp_of]
end
in
Quickcheck.test
~sexp_of:[%sexp_of: Test_case.t]
(let open Quickcheck.Let_syntax in
let%bind from = quickcheck_generator
and to_ = quickcheck_generator in
let%map diffs = List.gen_permutations (diffs ~from ~to_) in
({ from; to_; diffs } : Test_case.t))
~f:(fun { from; to_; diffs } -> [%test_result: t] ~expect:to_ (update from diffs));
[%expect {| |}]
;;
end)
;;