package piqilib

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file piqobj_to_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
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
(*
   Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017 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


(* whether to generate piqi-any in external mode, i.e. only including fields
 * defined for it by piqi.piqi (and not piqi-impl.piqi)
 *)
let is_external_mode = ref false


(* providing special handling for boxed objects, since they are not
 * actual references and can not be uniquely identified. Moreover they can
 * mask integers which are used for enumerating objects *)
let refer obj =
  let count = Piqloc.next_ocount () in
  if not (Obj.is_int (Obj.repr obj))
  then Piqloc.addref obj count
  else ()


let reference f code x =
  refer x;
  f code x


(* XXX: move to Piqi_protobuf? *)
let gen_int ?wire_type code x =
  let wire_type = W.get_wire_type `int wire_type in
  let gen_f =
    match wire_type with
      | `varint -> Piqirun.int64_to_varint
      | `zigzag_varint -> Piqirun.int64_to_zigzag_varint
      | `fixed32 -> Piqirun.int64_to_fixed32
      | `fixed64 -> Piqirun.int64_to_fixed64
      | `signed_varint -> Piqirun.int64_to_signed_varint
      | `signed_fixed32 -> Piqirun.int64_to_signed_fixed32
      | `signed_fixed64 -> Piqirun.int64_to_signed_fixed64
      | `block -> assert false (* XXX *)
  in
  gen_f code x


let gen_packed_int ?wire_type x =
  let wire_type = W.get_wire_type `int wire_type in
  let gen_f =
    match wire_type with
      | `varint -> Piqirun.int64_to_packed_varint
      | `zigzag_varint -> Piqirun.int64_to_packed_zigzag_varint
      | `fixed32 -> Piqirun.int64_to_packed_fixed32
      | `fixed64 -> Piqirun.int64_to_packed_fixed64
      | `signed_varint -> Piqirun.int64_to_packed_signed_varint
      | `signed_fixed32 -> Piqirun.int64_to_packed_signed_fixed32
      | `signed_fixed64 -> Piqirun.int64_to_packed_signed_fixed64
      | `block -> assert false (* XXX *)
  in
  gen_f x


let gen_float ?wire_type code x =
  let wire_type = W.get_wire_type `float wire_type in
  let gen_f =
    match wire_type with
      | `fixed32 -> Piqirun.float_to_fixed32
      | `fixed64 -> Piqirun.float_to_fixed64
      | _ -> assert false (* XXX *)
  in
  gen_f code x


let gen_packed_float ?wire_type x =
  let wire_type = W.get_wire_type `float wire_type in
  let gen_f =
    match wire_type with
      | `fixed32 -> Piqirun.float_to_packed_fixed32
      | `fixed64 -> Piqirun.float_to_packed_fixed64
      | _ -> assert false (* XXX *)
  in
  gen_f x


let gen_bool = Piqirun.gen_bool_field

let gen_packed_bool = Piqirun.bool_to_packed_varint

let gen_string = Piqirun.gen_string_field


let gen_int ?wire_type code x =
  refer x;
  gen_int ?wire_type code x

let gen_float ?wire_type code x =
  refer x;
  gen_float ?wire_type code x

let gen_bool = reference gen_bool
let gen_string = reference gen_string


let compare_field_type a b =
  match a.T.Field.code, b.T.Field.code with
    | Some a, Some b -> Int32.to_int (Int32.sub a b)
    | _ -> assert false


let compare_field a b =
  let open F in
  compare_field_type a.t b.t


(* preorder fields by their codes *)
let order_fields = List.sort compare_field


(*
let rec unalias (x:Piqobj.obj) =
  match x with
    | `alias x -> unalias x.A.obj
    | x -> x
*)


let rec gen_obj code (x:Piqobj.obj) =
  match x with
    (* built-in types *)
    | `int x | `uint x -> gen_int code x
    | `float x -> gen_float code x
    | `bool x -> gen_bool code x
    | `string x -> gen_string code x
    | `binary x -> gen_string code x
    | `any x -> gen_any code x
    (* custom types *)
    | `record x -> reference gen_record code x
    | `variant x -> reference gen_variant code x
    | `enum x -> reference gen_enum code x
    | `list x -> reference gen_list code x
    | `alias x -> gen_alias code x


and gen_packed_obj (x:Piqobj.obj) =
  match x with
    (* built-in types *)
    | `int x | `uint x -> gen_packed_int x
    | `float x -> gen_packed_float x
    | `bool x -> gen_packed_bool x
    | `enum x -> gen_packed_enum x
    | `alias x -> gen_packed_alias x
    | _ ->
        assert false (* other objects can't be packed *)


(* generate obj without leading code/tag element *)
and gen_binobj x =
  Piqirun.gen_binobj gen_obj x


(* generate "Piqi_piqi.any" record from Piqobj.any *)
and gen_any code x =
  let open Any in
  let piqi_any =
    if not !is_external_mode
    then
      (* in internal mode, passing a reference to intermediate Any
       * prepresentation registered using Piqi_objstore *)
      let res = T.Any.({
        (T.default_any ()) with
        ref = Some (Piqobj.put_any x);
      })
      in Piqloc.addrefret x res
    else
      (* in external mode, leave only fields defined by piqi.piqi: protobuf and
       * typename *)
      let typename = x.typename in
      let protobuf = Piqobj.pb_of_any x in
      (* if protobuf is undefined, see if we have untyped JSON or XML *)
      (* XXX, TODO: use unindented JSON and XML to preserve space *)
      let json =
        if protobuf <> None
        then None
        else
          match Piqobj.json_of_any x with
            | None -> None
            | Some json_ast ->
                let s = !Piqobj.string_of_json json_ast in
                Some s
      in
      let xml =
        if protobuf <> None || json <> None
        then None
        else
          match Piqobj.xml_of_any x with
            | None -> None
            | Some xml_elems ->
                let s = !Piqobj.string_of_xml (`Elem ("value", xml_elems)) in
                Some s
      in
      let piq =
        if protobuf <> None || json <> None || xml <> None
        then None
        else
          match Piqobj.piq_of_any x with
            | None -> None
            | Some piq_ast ->
                let s = !Piqobj.string_of_piq piq_ast in
                Some s
      in
      T.Any.({
        (T.default_any ()) with
        typename = typename;
        protobuf = protobuf;
        json = json;
        xml = xml;
        piq = piq;
      })
  in
  T.gen__any code piqi_any


and gen_record code x =
  let open R in
  (* TODO, XXX: doing ordering at every generation step is inefficient *)
  let fields = order_fields x.field in

  let encoded_piq_unparsed =
    match x.unparsed_piq_fields_ref with
      | Some x when not !is_external_mode ->
          let obj = Int64.of_int x in
          (* making Piqloc happy by adding a fake reference *)
          Piqloc.add_fake_loc obj ~label:"_unparsed_piq_fields_ref";

          let encoded_x = gen_int 1 obj ~wire_type:`varint in
          [encoded_x]
      | _ ->
          []
  in
  let encoded_fields = encoded_piq_unparsed @ (gen_fields fields) in
  Piqirun.gen_record code encoded_fields


and gen_fields fields =
  (* check if there's at least one packed field
   * TODO: optimize by keeping track of it statically at the record level *)
  if List.exists is_packed_field fields
  then group_gen_fields fields
  else List.map gen_field fields


and is_packed_field x = x.F.t.T.Field.protobuf_packed


(* generate fields but first group packed repeated fields together because they
 * are represented differently on the wire *)
and group_gen_fields fields =
  let rec aux accu l =
    match l with
      | [] -> List.rev accu
      | h::t ->
          let res, t =
            if is_packed_field h
            then gen_packed_fields h t
            else gen_field h, t
          in
          aux (res::accu) t
  in
  aux [] fields


(* group repeated packed fields that have the same type as the first one,
 * generate their wire representation and return when we encounter some other
 * field that doesn't belong to this repeated group *)
and gen_packed_fields first tail =
  let open F in
  let return accu rest =
    let code = Int32.to_int (some_of first.t.T.Field.code) in
    let res = Piqirun.gen_record code (List.rev accu) in
    res, rest
  in
  let rec aux accu l =
    match l with
      | [] -> return accu l
      | h::_ when h.t != first.t -> return accu l
      | h::t ->
          aux ((gen_packed_field h)::accu) t
  in
  (* putting `first` as a first element in our accumulator *)
  aux [gen_packed_field first] tail


and gen_packed_field x =
  let open F in
  (* NOTE: object for a repeated packed field can't be None *)
  gen_packed_obj (some_of x.obj)


and gen_field x =
  let open F in
  let code = Int32.to_int (some_of x.t.T.Field.code) in
  match x.obj with
    | None ->
        (* using true for encoding flags -- the same encoding as for options
         * (see below) *)
        refer x;
        Piqirun.gen_bool_field code true
    | Some obj ->
        gen_obj code obj


and gen_variant code x =
  let open V in
  (* generate a record with a single field which represents variant's option *)
  Piqirun.gen_record code [gen_option x.option]


and gen_option x =
  let open O in
  let code = Int32.to_int (some_of x.t.T.Option.code) in
  match x.obj with
    | None ->
        (* using true for encoding options w/o value *)
        refer x;
        Piqirun.gen_bool_field code true
    | Some obj ->
        gen_obj code obj


and gen_enum code x =
  let open E in
  gen_enum_option code x.option


and gen_enum_option code x =
  let open O in
  let value = some_of x.t.T.Option.code in
  Piqirun.int32_to_signed_varint code value


and gen_packed_enum x =
  let open E in
  gen_packed_enum_option x.option


and gen_packed_enum_option x =
  let open O in
  let value = some_of x.t.T.Option.code in
  Piqirun.int32_to_packed_signed_varint value


and gen_list code x =
  let open L in
  if not x.t.T.Piqi_list.protobuf_packed
  then Piqirun.gen_list gen_obj code x.obj
  else Piqirun.gen_packed_list gen_packed_obj code x.obj


and gen_alias ?wire_type code x =
  let open A in
  let wire_type = resolve_wire_type ?wire_type x.t.T.Alias.protobuf_wire_type in
  match x.obj with
    | `int x | `uint x -> gen_int code x ?wire_type
    | `float x -> gen_float code x ?wire_type
    | `alias x -> gen_alias code x ?wire_type
    | obj -> gen_obj code obj


and gen_packed_alias ?wire_type x =
  let open A in
  let wire_type = resolve_wire_type ?wire_type x.t.T.Alias.protobuf_wire_type in
  match x.obj with
    | `int x | `uint x -> gen_packed_int x ?wire_type
    | `float x -> gen_packed_float x ?wire_type
    | `alias x -> gen_packed_alias x ?wire_type
    | obj -> gen_packed_obj obj


and resolve_wire_type ?wire_type this_wire_type =
  (* wire-type defined in this alias is overridden by wire-type from the
   * upper-level definition *)
  if wire_type <> None
  then wire_type
  else this_wire_type


let _ =
  Piqobj.to_pb := gen_binobj

OCaml

Innovation. Community. Security.