Source file packstream.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
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
405
406
407
408
409
410
411
412
413
414
open Core
type 'a alist = (string * 'a) list [@@deriving show]
module rec Message : sig
type t =
| Null
| True
| False
| Int of int64
| Float of float
| Bytes of bytes
| String of string
| List of t list
| Dict of t alist
| Struct of {length: int; tag: int; fields: t list}
| Node of Node.t
| Relationship of Relationship.t
| UnboundRelationship of UnboundRelationship.t
| Path of Path.t
| Date of {days: int64}
| Time of {nanoseconds: int64; tz_offset_seconds: int64}
| LocalTime of {nanoseconds: int64}
| DateTime of {seconds: int64; nanoseconds: int64; tz_offset_seconds: int64}
| DateTimeZoneId of {seconds: int64; nanoseconds: int64; tz_id: string}
| LocalDateTime of {seconds: int64; nanoseconds: int64}
| Duration of {months: int64; days: int64; seconds: int64; nanoseconds: int64}
| Point2D of {srid: int64; x: float; y: float}
| Point3D of {srid: int64; x: float; y: float; z: float}
[@@deriving show]
val get_string : t -> (string, string) result
val get_node : t -> (Node.t, string) result
val get_relationship : t -> (Relationship.t, string) result
val get_int : t -> (int64, string) result
val get_unboundrelationship : t -> (UnboundRelationship.t, string) result
end = struct
type t =
| Null
| True
| False
| Int of int64
| Float of float
| Bytes of bytes
| String of string
| List of t list
| Dict of t alist
| Struct of {length: int; tag: int; fields: t list}
| Node of Node.t
| Relationship of Relationship.t
| UnboundRelationship of UnboundRelationship.t
| Path of Path.t
| Date of {days: int64}
| Time of {nanoseconds: int64; tz_offset_seconds: int64}
| LocalTime of {nanoseconds: int64}
| DateTime of {seconds: int64; nanoseconds: int64; tz_offset_seconds: int64}
| DateTimeZoneId of {seconds: int64; nanoseconds: int64; tz_id: string}
| LocalDateTime of {seconds: int64; nanoseconds: int64}
| Duration of {months: int64; days: int64; seconds: int64; nanoseconds: int64}
| Point2D of {srid: int64; x: float; y: float}
| Point3D of {srid: int64; x: float; y: float; z: float}
[@@deriving show]
let get_string = function
| String s -> Ok s
| _ -> Error "Could not get string"
let get_node = function
| Node n -> Ok n
| _ -> Error "Could not get string"
let get_relationship = function
| Relationship r -> Ok r
| _ -> Error "Could not get string"
let get_int = function
| Int i -> Ok i
| _ -> Error "Could not get string"
let get_unboundrelationship = function
| UnboundRelationship r -> Ok r
| _ -> Error "Could not get string"
end
and Node : sig
type t = {id: int64; labels: string list; properties: Message.t alist} [@@deriving show]
end = struct
type t = {id: int64; labels: string list; properties: Message.t alist} [@@deriving show]
end
and Relationship : sig
type t = {id: int64; start_node_id: int64; end_node_id: int64; typ: string; properties: Message.t alist}
[@@deriving show]
end = struct
type t = {id: int64; start_node_id: int64; end_node_id: int64; typ: string; properties: Message.t alist}
[@@deriving show]
end
and UnboundRelationship : sig
type t = {id: int64; typ: string; properties: Message.t alist} [@@deriving show]
end = struct
type t = {id: int64; typ: string; properties: Message.t alist} [@@deriving show]
end
and Path : sig
type t = {nodes: Node.t list; rels: UnboundRelationship.t list; ids: int64 list} [@@deriving show]
end = struct
type t = {nodes: Node.t list; rels: UnboundRelationship.t list; ids: int64 list} [@@deriving show]
end
let unwrap_string_list l fn = List.map ~f:Message.get_string l |> Result.all |> Result.map ~f:fn
let unwrap_list l getter fn = List.map ~f:getter l |> Result.all |> Result.map ~f:fn
let cons lst elem = List.cons elem lst
let rec parse_one (bitstring : Bitstring.t) =
let open Message in
match%bitstring bitstring with
| {| 0xC0 : 8; rest : -1 : bitstring |} -> Ok Null, rest
| {| 0xC2 : 8; rest : -1 : bitstring |} -> Ok False, rest
| {| 0xC3 : 8; rest : -1 : bitstring |} -> Ok True, rest
| {| 0xC1 : 8; i : 64; rest : -1 : bitstring |} -> Ok (Float (Int64.float_of_bits i)), rest
| {| 0xC8 : 8; i : 8 : signed; rest : -1 : bitstring |} -> Ok (Int (Int64.of_int i)), rest
| {| 0xC9 : 8; i : 16 : signed; rest : -1 : bitstring |} -> Ok (Int (Int64.of_int i)), rest
| {| 0xCA : 8; i : 32 ; rest : -1 : bitstring |} -> Ok (Int (Int64.of_int32 i)), rest
| {| 0xCB : 8; i : 64 ; rest : -1 : bitstring |} -> Ok (Int i), rest
| {| flag : 1; i : 7; rest : -1 : bitstring |} when not flag -> Ok (Int (Int64.of_int i)), rest
| {| 0x0F : 4; i : 4; rest : -1 : bitstring |} -> Ok (Int (Int64.of_int (i - 16))), rest
| {| 0xCC : 8; length : 8; bytes : length*8 : string; rest : -1 : bitstring |} ->
Ok (Bytes (Bytes.of_string bytes)), rest
| {| 0xCD : 8; length : 16; bytes : length*8 : string; rest : -1 : bitstring |} ->
Ok (Bytes (Bytes.of_string bytes)), rest
| {| 0xCE : 8; length : 32; bytes : (Int32.to_int_exn length)*8 : string; rest : -1 : bitstring |} ->
Ok (Bytes (Bytes.of_string bytes)), rest
| {| 0x8 : 4; length : 4 : unsigned; str : length*8 : string; rest : -1 : bitstring |} -> Ok (String str), rest
| {| 0xD0 : 8; length : 8 : unsigned; str : length*8 : string; rest : -1 : bitstring |} -> Ok (String str), rest
| {| 0xD1 : 8; length : 16 : unsigned; str : length*8 : string; rest : -1 : bitstring |} -> Ok (String str), rest
| {| 0xD2 : 8; length : 32 : unsigned; str : (Int32.to_int_exn length)*8 : string; rest : -1 : bitstring |} ->
Ok (String str), rest
| {| 0x9 : 4; length : 4 : unsigned; data : -1 : bitstring |} -> parse_list length data
| {| 0xD4 : 8; length : 8 : unsigned; data : -1 : bitstring |} -> parse_list length data
| {| 0xD5 : 8; length : 16 : unsigned; data : -1 : bitstring |} -> parse_list length data
| {| 0xD6 : 8; length : 32 : unsigned; data : -1 : bitstring |} -> parse_list (Int32.to_int_exn length) data
| {| 0xA : 4; length : 4 : unsigned; data : -1 : bitstring |} -> parse_dict length data
| {| 0xD8 : 8; length : 8 : unsigned; data : -1 : bitstring |} -> parse_dict length data
| {| 0xD9 : 8; length : 16 : unsigned; data : -1 : bitstring |} -> parse_dict length data
| {| 0xDA : 8; length : 32 : unsigned; data : -1 : bitstring |} -> parse_dict (Int32.to_int_exn length) data
| {| 0xB : 4; length : 4 : unsigned; tag : 8 : unsigned; data : -1 : bitstring |} -> parse_structs length tag data
| {| _ |} -> Error "Invalid message", bitstring
and parse_list length (data : Bitstring.t) =
let open Tuple2 in
let internal (l, bitstring) _ = map_fst (parse_one bitstring) ~f:(cons l) in
let fake_list = List.init length ~f:Fn.id in
List.fold fake_list ~init:([], data) ~f:internal
|> map_fst ~f:List.rev
|> map_fst ~f:Result.all
|> map_fst ~f:(Result.map ~f:(fun x -> Message.List x))
and parse_dict length (data : Bitstring.t) =
let open Tuple2 in
let open Result in
let internal (d, bitstring) _ =
match parse_one bitstring with
| Ok (String s), rst -> map_fst (parse_one rst) ~f:(fun v -> cons d (v >>= fun vv -> Ok (s, vv)))
| _ -> [Error "Key must be a string"], bitstring
in
let fake_list = List.init length ~f:Fn.id in
List.fold fake_list ~init:([], data) ~f:internal
|> map_fst ~f:List.rev
|> map_fst ~f:Result.all
|> map_fst ~f:(Result.map ~f:(fun x -> Message.Dict x))
and parse_structs length tag (data : Bitstring.t) =
let open Message in
match tag with
| 0x4E when length = 3 ->
parse_fields length data (function
| [Int i; List l; Dict p] -> unwrap_list l get_string @@ fun lst -> Node {id = i; labels = lst; properties = p}
| _ -> Error "Cound not parse Node")
| 0x52 when length = 5 ->
parse_fields length data (function
| [Int i; Int s; Int e; String t; Dict p] ->
Ok (Relationship {id = i; start_node_id = s; end_node_id = e; typ = t; properties = p})
| _ -> Error "Cound not parse Relationship")
| 0x72 when length = 3 ->
parse_fields length data (function
| [Int i; String t; Dict p] -> Ok (UnboundRelationship {id = i; typ = t; properties = p})
| _ -> Error "Cound not parse UnboundRelationship")
| 0x50 when length = 3 ->
parse_fields length data (function
| [List n; List r; List p] ->
unwrap_list n get_node (fun nodes ->
unwrap_list r get_unboundrelationship (fun relationships ->
unwrap_list p get_int (fun ids -> Path {nodes; rels = relationships; ids}))
|> Result.join)
|> Result.join
| _ -> Error "Cound not parse Path")
| 0x44 when length = 1 ->
parse_fields length data (function
| [Int d] -> Ok (Date {days = d})
| _ -> Error "Cound not parse Date")
| 0x54 when length = 2 ->
parse_fields length data (function
| [Int n; Int t] -> Ok (Time {nanoseconds = n; tz_offset_seconds = t})
| _ -> Error "Cound not parse Time")
| 0x74 when length = 1 ->
parse_fields length data (function
| [Int t] -> Ok (LocalTime {nanoseconds = t})
| _ -> Error "Cound not parse LocalTime")
| 0x46 when length = 3 ->
parse_fields length data (function
| [Int s; Int n; Int t] -> Ok (DateTime {seconds = s; nanoseconds = n; tz_offset_seconds = t})
| _ -> Error "Cound not parse DateTime")
| 0x66 when length = 3 ->
parse_fields length data (function
| [Int s; Int n; String t] -> Ok (DateTimeZoneId {seconds = s; nanoseconds = n; tz_id = t})
| _ -> Error "Cound not parse DateTimeZoneId")
| 0x64 when length = 2 ->
parse_fields length data (function
| [Int s; Int n] -> Ok (LocalDateTime {seconds = s; nanoseconds = n})
| _ -> Error "Cound not parse LocalDateTime")
| 0x45 when length = 4 ->
parse_fields length data (function
| [Int m; Int d; Int s; Int n] -> Ok (Duration {months = m; days = d; seconds = s; nanoseconds = n})
| _ -> Error "Cound not parse Duration")
| 0x58 when length = 3 ->
parse_fields length data (function
| [Int s; Float x; Float y] -> Ok (Point2D {srid = s; x; y})
| _ -> Error "Cound not parse Point2D")
| 0x59 when length = 4 ->
parse_fields length data (function
| [Int s; Float x; Float y; Float z] -> Ok (Point3D {srid = s; x; y; z})
| _ -> Error "Cound not parse Point3D")
| _ -> parse_fields length data (fun lst -> Ok (Struct {length; tag; fields = lst}))
and parse_fields length (data : Bitstring.t) fn =
let open Tuple2 in
let internal (l, bitstring) _ = map_fst (parse_one bitstring) ~f:(cons l) in
let fake_list = List.init length ~f:Fn.id in
List.fold fake_list ~init:([], data) ~f:internal
|> map_fst ~f:List.rev
|> map_fst ~f:Result.all
|> map_fst ~f:(Result.bind ~f:fn)
let parse (bitstring : Bitstring.t) = Tuple2.get1 @@ parse_one bitstring
let serialize_int (i : int64) =
let open Int64 in
match i with
| _ when i < -2147483648L || i > 2147483647L ->
let%bitstring b = {| 0xCB : 8; i : 64 |} in
b
| _ when i < -32768L || i > 32767L ->
let%bitstring b = {| 0xCA : 8; (Int64.to_int32_exn i) : 32 |} in
b
| _ when i < -128L || i > 127L ->
let%bitstring b = {| 0xC9 : 8; (Int64.to_int_exn i) : 16 : signed |} in
b
| _ when i < -16L ->
let%bitstring b = {| 0xC8 : 8; (Int64.to_int_exn i) : 8 : signed |} in
b
| _ ->
let%bitstring b = {| (Int64.to_int_exn i) : 8 : signed |} in
b
let serialize_float f =
let converted = Int64.bits_of_float f in
let%bitstring b = {| 0xC1 : 8; converted : 64 |} in
b
let serialize_byte_array ba =
match String.length ba with
| l when l < 256 ->
let%bitstring b = {| 0xCC : 8; l : 8 : unsigned; ba : 8*l : string |} in
b
| l when l < 65536 ->
let%bitstring b = {| 0xCD : 8; l : 16 : unsigned; ba : 8*l : string |} in
b
| l ->
let%bitstring b = {| 0xCE : 8; (Int32.of_int_exn l) : 32 : unsigned; ba : 8*l : string |} in
b
let serialize_string str =
match String.length str with
| l when l > 65535 ->
let%bitstring b = {| 0xD2 : 8; (Int32.of_int_exn l) : 32 : unsigned; str : l*8 : string|} in
b
| l when l > 255 ->
let%bitstring b = {| 0xD1 : 8; l : 16 : unsigned; str : l*8 : string|} in
b
| l when l > 15 ->
let%bitstring b = {| 0xD0 : 8; l : 8 : unsigned; str : l*8 : string|} in
b
| l ->
let%bitstring b = {| 0x8 : 4; l : 4 : unsigned; str : l*8 : string |} in
b
let rec serialize message =
let open Message in
match message with
| Null ->
let%bitstring b = {| 0xC0 : 8 |} in
b
| False ->
let%bitstring b = {| 0xC2 : 8 |} in
b
| True ->
let%bitstring b = {| 0xC3 : 8 |} in
b
| Int i -> serialize_int i
| Float f -> serialize_float f
| Bytes ba -> serialize_byte_array (Bytes.to_string ba)
| String str -> serialize_string str
| List lst -> serialize_list lst
| Dict dct -> serialize_dict dct
| Struct {length; tag; fields} ->
| Node {id; labels; properties} ->
| Relationship {id; start_node_id; end_node_id; typ; properties} ->
| UnboundRelationship {id; typ; properties} ->
| Path {nodes; rels; ids} ->
| Date {days} ->
| Time {nanoseconds; tz_offset_seconds} ->
| LocalTime {nanoseconds} ->
| DateTime {seconds; nanoseconds; tz_offset_seconds} ->
| DateTimeZoneId {seconds; nanoseconds; tz_id} ->
| LocalDateTime {seconds; nanoseconds} ->
| Duration {months; days; seconds; nanoseconds} ->
| Point2D {srid; x; y} ->
| Point3D {srid; x; y; z} ->
and serialize_list lst =
let elems = List.map ~f:serialize lst in
match List.length elems with
| l when l > 65535 ->
| l when l > 255 ->
| l when l > 15 ->
| l ->
and serialize_dict dct =
let elems = List.map ~f:serialize_elem dct |> List.join in
match List.length dct with
| l when l > 65535 ->
| l when l > 255 ->
| l when l > 15 ->
| l ->
and serialize_elem (key, value) = [serialize_string key; serialize value]