-
Notifications
You must be signed in to change notification settings - Fork 36
/
piqobj_of_protobuf.ml
356 lines (287 loc) · 10.9 KB
/
piqobj_of_protobuf.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
(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017, 2018 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)
module C = Piqi_common
open C
open Piqobj_common
module W = Piqi_protobuf
let next_count = Piqloc.next_icount
let reference0 f x =
let count = next_count () in
let obj = f x in
Piqloc.addrefret count obj
let reference f t x =
let count = next_count () in
let obj = f t x in
Piqloc.addrefret count obj
(* XXX: move to Piqi_protobuf? *)
let parse_int ?wire_type x =
let r0 = reference0 in
let wire_type = W.get_wire_type `int wire_type in
match wire_type with
| `varint -> `uint (r0 Piqirun.int64_of_varint x)
| `zigzag_varint -> `int (r0 Piqirun.int64_of_zigzag_varint x)
| `fixed32 -> `uint (r0 Piqirun.int64_of_fixed32 x)
| `fixed64 -> `uint (r0 Piqirun.int64_of_fixed64 x)
| `signed_varint -> `int (r0 Piqirun.int64_of_signed_varint x)
| `signed_fixed32 -> `int (r0 Piqirun.int64_of_signed_fixed32 x)
| `signed_fixed64 -> `int (r0 Piqirun.int64_of_signed_fixed64 x)
| `block -> assert false (* XXX *)
let parse_packed_int ?wire_type x =
let wire_type = W.get_wire_type `int wire_type in
match wire_type with
| `varint -> `uint (Piqirun.int64_of_packed_varint x)
| `zigzag_varint -> `int (Piqirun.int64_of_packed_zigzag_varint x)
| `fixed32 -> `uint (Piqirun.int64_of_packed_fixed32 x)
| `fixed64 -> `uint (Piqirun.int64_of_packed_fixed64 x)
| `signed_varint -> `int (Piqirun.int64_of_packed_signed_varint x)
| `signed_fixed32 -> `int (Piqirun.int64_of_packed_signed_fixed32 x)
| `signed_fixed64 -> `int (Piqirun.int64_of_packed_signed_fixed64 x)
| `block -> assert false (* XXX *)
let parse_float ?wire_type x =
let r0 = reference0 in
let wire_type = W.get_wire_type `float wire_type in
match wire_type with
| `fixed32 -> r0 Piqirun.float_of_fixed32 x
| `fixed64 -> r0 Piqirun.float_of_fixed64 x
| _ -> assert false (* XXX *)
let parse_packed_float ?wire_type x =
let wire_type = W.get_wire_type `float wire_type in
match wire_type with
| `fixed32 -> Piqirun.float_of_packed_fixed32 x
| `fixed64 -> Piqirun.float_of_packed_fixed64 x
| _ -> assert false (* XXX *)
let rec parse_obj0 (t:T.piqtype) x :Piqobj.obj =
let r0 = reference0 in
let r = reference in
match t with
(* built-in types *)
| `int -> parse_int x
| `float -> `float (parse_float x)
| `bool -> `bool (r0 Piqirun.parse_bool_field x)
| `string -> `string (r0 Piqirun.parse_string_field x)
| `binary -> `binary (r0 Piqirun.parse_binary_field x)
| `any -> `any (parse_any x)
(* custom types *)
| `record t -> `record (r parse_record t x)
| `variant t -> `variant (r parse_variant t x)
| `enum t -> `enum (r parse_enum t x)
| `list t -> `list (r parse_list t x)
| `alias t -> `alias (parse_alias0 t x)
and parse_packed_obj (t:T.piqtype) x :Piqobj.obj =
match t with
(* built-in types *)
| `int -> parse_packed_int x
| `float -> `float (parse_packed_float x)
| `bool -> `bool (Piqirun.bool_of_packed_varint x)
| `enum t -> `enum (parse_packed_enum t x)
| `alias t -> `alias (parse_packed_alias t x)
| _ ->
assert false (* objects of other types can't be packed *)
and parse_obj t x =
(* using the same count here as we will use for parsing the objects themselves
*)
let count = !Piqloc.icount in
let res = parse_obj0 t x in
Piqloc.addrefret count res
and parse_binobj piqtype binobj =
Piqirun.parse_binobj (parse_obj piqtype) binobj
and parse_any x =
let piqi_any = T.parse_any x in
match piqi_any.T.Any.ref with
| Some ref ->
(* recover internally passed Piqobj.any from an integer reference *)
C.debug "Piqobj_of_protobuf.parse_any: recovering any from existing ref %d\n" ref;
Piqobj.get_any ref
| None ->
(* external mode *)
let any = Any.({
Piqobj.default_any with
typename = piqi_any.Piqi_impl_piqi.Any.typename;
pb = piqi_any.Piqi_impl_piqi.Any.protobuf;
})
in
(* if there's no typed protobuf, look for untyped JSON or XML values *)
if any.Any.pb = None
then (
let json_string = piqi_any.T.Any.json in
let xml_string = piqi_any.T.Any.xml in
let piq_string = piqi_any.T.Any.piq in
if json_string <> None
then (
let json_ast = !Piqobj.json_of_string (some_of json_string) in
any.Any.json_ast <- Some json_ast; (* same as in Piqobj_of_piq.parse_any *)
any.Any.json_string <- json_string
)
else if xml_string <> None
then (
let xml_list = !Piqobj.xml_of_string (some_of xml_string) in
any.Any.xml_ast <- Some ("undefined", xml_list) (* same as in Piqobj_of_piq.parse_any *)
)
else if piq_string <> None
then (
let piq_ast = !Piqobj.piq_of_string (some_of piq_string) in
any.Any.piq_ast <- Some piq_ast; (* same as in Piqobj_of_piq.parse_any *)
)
else
(* XXX: hmm... no protobuf, JSON, XML, Piq -- what are we
* dealing with here? *)
assert false
);
any
and parse_record t x =
let l = Piqirun.parse_record x in
(* obtain the reference to unparsed piq fields if we are dealing with
* serialized Piqi object *)
let unparsed_piq_fields_ref, l =
if !C.is_inside_parse_piqi
then parse_unparsed_piq_fields_ref l
else None, l
in
(* NOTE: fields are pre-order by wire code *)
let fields_spec = t.T.Record.wire_field in
let fields, rem = List.fold_left parse_field ([], l) fields_spec in
Piqirun.check_unparsed_fields rem;
R.({t = t; field = List.rev fields; unparsed_piq_fields_ref = unparsed_piq_fields_ref})
and parse_unparsed_piq_fields_ref l =
let res = Piqirun.parse_optional_field 1 (fun x -> parse_int ~wire_type:`varint x) l in
match res with
| Some (`uint x), rem -> Some (Int64.to_int x), rem
| None, l -> None, l
| _ -> assert false
and parse_field (accu, rem) t =
let fields, rem = do_parse_field t rem in
(List.rev_append fields accu, rem)
and do_parse_field t l =
let open T.Field in
let code = Int32.to_int (some_of t.code) in
let field_type = some_of t.piqtype in
let values, rem =
match t.mode with
| `required ->
let x, rem = parse_required_field code field_type l in
[x], rem
| `optional ->
(* XXX: location reference for default? *)
let x, rem = parse_optional_field code field_type t.default l in
let res = (match x with Some x -> [x] | None -> []) in
res, rem
| `repeated ->
if not t.protobuf_packed
then parse_repeated_field code field_type l
else parse_packed_repeated_field code field_type l
in
let fields =
List.map (fun x ->
let res = F.({t = t; obj = Some x}) in
Piqloc.addrefret x res) values
in
fields, rem
and parse_required_field code field_type l =
Piqirun.parse_required_field code (parse_obj field_type) l
and parse_optional_field code field_type default l =
let res = Piqirun.parse_optional_field code (parse_obj field_type) l in
match res with
| Some _, _ -> res
| None, rem ->
Piqobj_common.parse_default field_type default, rem
and parse_repeated_field code field_type l =
Piqirun.parse_repeated_field code (parse_obj field_type) l
and parse_packed_repeated_field code field_type l =
Piqirun.parse_packed_repeated_field code
(parse_packed_obj field_type) (parse_obj field_type) l
and parse_variant t x =
let code, obj = Piqirun.parse_variant x in
let code32 = Int32.of_int code in
let options = t.T.Variant.option in
let option =
try
let o = List.find (fun o -> some_of o.T.Option.code = code32) options in
parse_option o obj
with Not_found ->
Piqirun.error_variant x code
in
V.({t = t; option = option})
and parse_option t x =
let open T.Option in
match t.piqtype with
| None ->
if Piqirun.parse_bool_field x = true
then
let res = O.({t = t; obj = None}) in
(* skip boolean used to encode empty option value *)
let count = next_count () in
Piqloc.addrefret count res
else
piqi_error "invalid representation of untyped option"
| Some option_type ->
let obj = parse_obj option_type x in
let res = O.({t = t; obj = Some obj}) in
Piqloc.addrefret obj res
and parse_enum t x =
let code32 = Piqirun.int32_of_signed_varint x in
let option =
try parse_enum_option t code32
with Not_found ->
Piqirun.error_enum_const x
in
(* add location reference which is equal to the enum location *)
Piqloc.addref !Piqloc.icount option;
E.({t = t; option = option})
and parse_packed_enum t x =
let code32 = Piqirun.int32_of_packed_signed_varint x in
let option =
try parse_enum_option t code32
with Not_found ->
Piqirun.error_enum_const x
in
E.({t = t; option = option})
and parse_enum_option t code32 =
let options = t.T.Enum.option in
let o = List.find (fun o -> some_of o.T.Option.code = code32) options in
O.({t = o; obj = None})
and parse_list t x =
let obj_type = some_of t.T.Piqi_list.piqtype in
let contents =
if not t.T.Piqi_list.protobuf_packed
then Piqirun.parse_list (parse_obj obj_type) x
else Piqirun.parse_packed_list
(parse_packed_obj obj_type) (parse_obj obj_type) x
in
L.({t = t; obj = contents})
and parse_alias0 t x =
parse_alias t x
(* XXX: roll-up multiple enclosed aliases into one? *)
and parse_alias t ?wire_type x =
let open T.Alias in
let wire_type = Piqobj_to_protobuf.resolve_wire_type ?wire_type t.protobuf_wire_type in
let obj =
match some_of t.piqtype with
| `int -> parse_int x ?wire_type
| `float -> `float (parse_float x ?wire_type)
| `alias t -> `alias (parse_alias t x ?wire_type)
| t -> parse_obj t x
in
A.({t = t; obj = obj})
and parse_packed_alias t ?wire_type x =
let open T.Alias in
let wire_type = Piqobj_to_protobuf.resolve_wire_type ?wire_type t.protobuf_wire_type in
let obj =
match some_of t.piqtype with
| `int -> parse_packed_int x ?wire_type
| `float -> `float (parse_packed_float x ?wire_type)
| `alias t -> `alias (parse_packed_alias t x ?wire_type)
| t -> parse_packed_obj t x
in
A.({t = t; obj = obj})
let _ =
Piqobj.of_pb := parse_binobj