simdjson bindings with streaming support
1type error = { path : string list; message : string }
2
3exception Decode_error of error
4
5let error_to_string e =
6 let path =
7 match e.path with [] -> "" | p -> String.concat "." (List.rev p) ^ ": "
8 in
9 path ^ e.message
10
11let decode_error path msg = raise (Decode_error { path; message = msg })
12
13type 'a decoder = string list -> Simdjsont_raw.element -> 'a
14type encoder = Buffer.t -> unit
15
16type 'a t = {
17 decode : 'a decoder;
18 encode : 'a -> encoder;
19 to_json : 'a -> Simdjsont_json.t;
20}
21
22let decode_element codec elt =
23 try Ok (codec.decode [] elt)
24 with Decode_error e -> Error (error_to_string e)
25
26let decode_string codec s =
27 let parser = Simdjsont_raw.create_parser () in
28 match Simdjsont_raw.parse_string parser s with
29 | Ok elt ->
30 let result = decode_element codec elt in
31 let _ = Sys.opaque_identity parser in
32 result
33 | Error e -> Error e.Simdjsont_raw.message
34
35let decode_string_exn codec s =
36 match decode_string codec s with Ok v -> v | Error msg -> failwith msg
37
38let encode_to_buffer codec v =
39 let buf = Buffer.create 256 in
40 codec.encode v buf;
41 buf
42
43let encode_string codec v = Buffer.contents (encode_to_buffer codec v)
44let to_json codec v = codec.to_json v
45
46let write_escaped_string buf s =
47 Buffer.add_char buf '"';
48 for i = 0 to String.length s - 1 do
49 match String.unsafe_get s i with
50 | '"' -> Buffer.add_string buf "\\\""
51 | '\\' -> Buffer.add_string buf "\\\\"
52 | '\b' -> Buffer.add_string buf "\\b"
53 | '\012' -> Buffer.add_string buf "\\f"
54 | '\n' -> Buffer.add_string buf "\\n"
55 | '\r' -> Buffer.add_string buf "\\r"
56 | '\t' -> Buffer.add_string buf "\\t"
57 | c when Char.code c < 0x20 ->
58 Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c))
59 | c -> Buffer.add_char buf c
60 done;
61 Buffer.add_char buf '"'
62
63let null : unit t =
64 {
65 decode =
66 (fun path elt ->
67 match Simdjsont_raw.element_type elt with
68 | Simdjsont_raw.Null -> ()
69 | _ -> decode_error path "expected null");
70 encode = (fun () buf -> Buffer.add_string buf "null");
71 to_json = (fun () -> Simdjsont_json.Null);
72 }
73
74let bool : bool t =
75 {
76 decode =
77 (fun path elt ->
78 match Simdjsont_raw.get_bool elt with
79 | Ok b -> b
80 | Error _ -> decode_error path "expected bool");
81 encode =
82 (fun b buf -> Buffer.add_string buf (if b then "true" else "false"));
83 to_json = (fun b -> Simdjsont_json.Bool b);
84 }
85
86let int : int t =
87 {
88 decode =
89 (fun path elt ->
90 match Simdjsont_raw.get_int64 elt with
91 | Ok i -> Int64.to_int i
92 | Error _ -> decode_error path "expected int");
93 encode = (fun i buf -> Buffer.add_string buf (string_of_int i));
94 to_json = (fun i -> Simdjsont_json.Int (Int64.of_int i));
95 }
96
97let int64 : int64 t =
98 {
99 decode =
100 (fun path elt ->
101 match Simdjsont_raw.get_int64 elt with
102 | Ok i -> i
103 | Error _ -> decode_error path "expected int64");
104 encode = (fun i buf -> Buffer.add_string buf (Int64.to_string i));
105 to_json = (fun i -> Simdjsont_json.Int i);
106 }
107
108let float : float t =
109 {
110 decode =
111 (fun path elt ->
112 match Simdjsont_raw.element_type elt with
113 | Simdjsont_raw.Double -> (
114 match Simdjsont_raw.get_double elt with
115 | Ok f -> f
116 | Error _ -> Float.nan)
117 | Simdjsont_raw.Int64 -> (
118 match Simdjsont_raw.get_int64 elt with
119 | Ok i -> Int64.to_float i
120 | Error _ -> Float.nan)
121 | Simdjsont_raw.Uint64 -> (
122 match Simdjsont_raw.get_uint64 elt with
123 | Ok u -> Int64.to_float (Unsigned.UInt64.to_int64 u)
124 | Error _ -> Float.nan)
125 | Simdjsont_raw.Null -> Float.nan
126 | _ -> decode_error path "expected number");
127 encode =
128 (fun f buf ->
129 if Float.is_finite f then
130 Buffer.add_string buf (Printf.sprintf "%.17g" f)
131 else Buffer.add_string buf "null");
132 to_json = (fun f -> Simdjsont_json.Float f);
133 }
134
135let string : string t =
136 {
137 decode =
138 (fun path elt ->
139 match Simdjsont_raw.get_string elt with
140 | Ok s -> s
141 | Error _ -> decode_error path "expected string");
142 encode = (fun s buf -> write_escaped_string buf s);
143 to_json = (fun s -> Simdjsont_json.String s);
144 }
145
146let list (item : 'a t) : 'a list t =
147 {
148 decode =
149 (fun path elt ->
150 match Simdjsont_raw.get_array elt with
151 | Ok arr ->
152 let result = ref [] in
153 let idx = ref 0 in
154 Simdjsont_raw.array_iter
155 (fun e ->
156 let item_path = string_of_int !idx :: path in
157 result := item.decode item_path e :: !result;
158 incr idx)
159 arr;
160 List.rev !result
161 | Error _ -> decode_error path "expected array");
162 encode =
163 (fun items buf ->
164 Buffer.add_char buf '[';
165 (match items with
166 | [] -> ()
167 | x :: xs ->
168 item.encode x buf;
169 List.iter
170 (fun v ->
171 Buffer.add_char buf ',';
172 item.encode v buf)
173 xs);
174 Buffer.add_char buf ']');
175 to_json = (fun items -> Simdjsont_json.Array (List.map item.to_json items));
176 }
177
178let array (item : 'a t) : 'a array t =
179 {
180 decode =
181 (fun path elt ->
182 match Simdjsont_raw.get_array elt with
183 | Ok arr ->
184 let len = Simdjsont_raw.array_length arr in
185 if len = 0 then [||]
186 else begin
187 let result = ref [||] in
188 let idx = ref 0 in
189 Simdjsont_raw.array_iter
190 (fun e ->
191 let item_path = string_of_int !idx :: path in
192 let v = item.decode item_path e in
193 if !idx = 0 then result := Array.make len v
194 else Array.unsafe_set !result !idx v;
195 incr idx)
196 arr;
197 !result
198 end
199 | Error _ -> decode_error path "expected array");
200 encode =
201 (fun items buf ->
202 Buffer.add_char buf '[';
203 let len = Array.length items in
204 if len > 0 then begin
205 item.encode (Array.unsafe_get items 0) buf;
206 for i = 1 to len - 1 do
207 Buffer.add_char buf ',';
208 item.encode (Array.unsafe_get items i) buf
209 done
210 end;
211 Buffer.add_char buf ']');
212 to_json =
213 (fun items ->
214 Simdjsont_json.Array (List.map item.to_json (Array.to_list items)));
215 }
216
217let optional (inner : 'a t) : 'a option t =
218 {
219 decode =
220 (fun path elt ->
221 match Simdjsont_raw.element_type elt with
222 | Simdjsont_raw.Null -> None
223 | _ -> Some (inner.decode path elt));
224 encode =
225 (fun opt buf ->
226 match opt with
227 | None -> Buffer.add_string buf "null"
228 | Some v -> inner.encode v buf);
229 to_json =
230 (function None -> Simdjsont_json.Null | Some v -> inner.to_json v);
231 }
232
233let map (f : 'a -> 'b) (g : 'b -> 'a) (codec : 'a t) : 'b t =
234 {
235 decode = (fun path elt -> f (codec.decode path elt));
236 encode = (fun v buf -> codec.encode (g v) buf);
237 to_json = (fun v -> codec.to_json (g v));
238 }
239
240let decode_obj_field path (obj : Simdjsont_raw.object_) name (dec : 'a decoder)
241 : 'a =
242 let field_path = name :: path in
243 match Simdjsont_raw.object_find obj name with
244 | Ok elt -> dec field_path elt
245 | Error _ -> decode_error path ("missing field: " ^ name)
246
247let decode_obj_opt_field path (obj : Simdjsont_raw.object_) name
248 (dec : 'a decoder) : 'a option =
249 let field_path = name :: path in
250 match Simdjsont_raw.object_find obj name with
251 | Ok elt -> (
252 match Simdjsont_raw.element_type elt with
253 | Simdjsont_raw.Null -> None
254 | _ -> Some (dec field_path elt))
255 | Error _ -> None
256
257module Obj = struct
258 type ('o, 'dec) builder = {
259 dec : string list -> Simdjsont_raw.object_ -> 'dec;
260 enc : 'o -> Buffer.t -> bool -> bool;
261 ast : 'o -> (string * Simdjsont_json.t) list;
262 }
263
264 let field constructor =
265 {
266 dec = (fun _path _obj -> constructor);
267 enc = (fun _v _buf first -> first);
268 ast = (fun _v -> []);
269 }
270
271 let mem name (codec : 'a t) ~enc:(get : 'o -> 'a)
272 (builder : ('o, 'a -> 'b) builder) : ('o, 'b) builder =
273 {
274 dec =
275 (fun path obj ->
276 let value = decode_obj_field path obj name codec.decode in
277 builder.dec path obj value);
278 enc =
279 (fun v buf first ->
280 let first = builder.enc v buf first in
281 if not first then Buffer.add_char buf ',';
282 write_escaped_string buf name;
283 Buffer.add_char buf ':';
284 codec.encode (get v) buf;
285 false);
286 ast = (fun v -> (name, codec.to_json (get v)) :: builder.ast v);
287 }
288
289 let opt_mem name (codec : 'a t) ~enc:(get : 'o -> 'a option)
290 (builder : ('o, 'a option -> 'b) builder) : ('o, 'b) builder =
291 {
292 dec =
293 (fun path obj ->
294 let value = decode_obj_opt_field path obj name codec.decode in
295 builder.dec path obj value);
296 enc =
297 (fun v buf first ->
298 let first = builder.enc v buf first in
299 match get v with
300 | None -> first
301 | Some inner_v ->
302 if not first then Buffer.add_char buf ',';
303 write_escaped_string buf name;
304 Buffer.add_char buf ':';
305 codec.encode inner_v buf;
306 false);
307 ast =
308 (fun v ->
309 match get v with
310 | None -> builder.ast v
311 | Some inner_v -> (name, codec.to_json inner_v) :: builder.ast v);
312 }
313
314 let finish (builder : ('o, 'o) builder) : 'o t =
315 {
316 decode =
317 (fun path elt ->
318 match Simdjsont_raw.get_object elt with
319 | Ok obj -> builder.dec path obj
320 | Error _ -> decode_error path "expected object");
321 encode =
322 (fun v buf ->
323 Buffer.add_char buf '{';
324 let _ = builder.enc v buf true in
325 Buffer.add_char buf '}');
326 to_json = (fun v -> Simdjsont_json.Object (builder.ast v));
327 }
328end
329
330let value : Simdjsont_json.t t =
331 let rec decode_value path elt =
332 match Simdjsont_raw.element_type elt with
333 | Simdjsont_raw.Null -> Simdjsont_json.Null
334 | Simdjsont_raw.Bool ->
335 Simdjsont_json.Bool
336 (match Simdjsont_raw.get_bool elt with Ok b -> b | _ -> false)
337 | Simdjsont_raw.Int64 ->
338 Simdjsont_json.Int
339 (match Simdjsont_raw.get_int64 elt with Ok i -> i | _ -> 0L)
340 | Simdjsont_raw.Uint64 ->
341 let i =
342 match Simdjsont_raw.get_uint64 elt with
343 | Ok u -> Unsigned.UInt64.to_int64 u
344 | _ -> 0L
345 in
346 Simdjsont_json.Int i
347 | Simdjsont_raw.Double ->
348 Simdjsont_json.Float
349 (match Simdjsont_raw.get_double elt with Ok f -> f | _ -> 0.0)
350 | Simdjsont_raw.String ->
351 Simdjsont_json.String
352 (match Simdjsont_raw.get_string elt with Ok s -> s | _ -> "")
353 | Simdjsont_raw.Array -> (
354 match Simdjsont_raw.get_array elt with
355 | Ok arr ->
356 let items = ref [] in
357 let idx = ref 0 in
358 Simdjsont_raw.array_iter
359 (fun e ->
360 items := decode_value (string_of_int !idx :: path) e :: !items;
361 incr idx)
362 arr;
363 Simdjsont_json.Array (List.rev !items)
364 | Error _ -> Simdjsont_json.Array [])
365 | Simdjsont_raw.Object -> (
366 match Simdjsont_raw.get_object elt with
367 | Ok obj ->
368 let members = ref [] in
369 Simdjsont_raw.object_iter
370 (fun k v ->
371 members := (k, decode_value (k :: path) v) :: !members)
372 obj;
373 Simdjsont_json.Object (List.rev !members)
374 | Error _ -> Simdjsont_json.Object [])
375 in
376 {
377 decode = decode_value;
378 encode = (fun v buf -> Simdjsont_json.write buf v);
379 to_json = (fun v -> v);
380 }