Bond
 
Loading...
Searching...
No Matches
parser.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#pragma once
5
6#include <bond/core/config.h>
7
8#include "detail/inheritance.h"
9#include "detail/omit_default.h"
10#include "detail/parser_utils.h"
11#include "detail/typeid_value.h"
12#include "merge.h"
13#include "reflection.h"
14#include "schema.h"
15#include "transforms.h"
16#include "value.h"
17
18#include <bond/protocol/simple_binary_impl.h>
19#include <bond/protocol/simple_json_reader_impl.h>
20
21namespace bond
22{
23
24namespace detail
25{
26
27class ParserCommon
28{
29protected:
30 template <typename Transform>
31 bool
32 ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, const Transform&)
33 {
34 return false;
35 }
36
37 template <typename Fields>
38 void SkipFields(const Fields&)
39 {
40 }
41
42
43 template <typename Transform>
44 struct UnknownFieldBinder
45 : detail::nonassignable
46 {
47 UnknownFieldBinder(Transform& transform)
48 : transform(transform)
49 {}
50
51 template <typename T>
52 bool Field(uint16_t id, const Metadata& /*metadata*/, const T& value) const
53 {
54 return transform.UnknownField(id, value);
55 }
56
57 Transform& transform;
58 };
59
60 template <typename Transform>
61 UnknownFieldBinder<Transform> BindUnknownField(Transform& transform)
62 {
63 return UnknownFieldBinder<Transform>(transform);
64 }
65};
66
67} // namespace detail
68
69//
70// StaticParser iterates serialized data using type schema and calls
71// specified transform for each data field.
72// The schema may be provided at compile-time (schema<T>::type::fields) or at runtime
73// (const RuntimeSchema&). StaticParser is used with protocols which don't
74// tag fields in serialized format with ids or types, e.g. Apache Avro protocol.
75//
76template <typename Input>
77class StaticParser
78 : protected detail::ParserInheritance<Input, StaticParser<Input> >,
79 public detail::ParserCommon
80{
81public:
82 StaticParser(Input input, bool base = false)
83 : detail::ParserInheritance<Input, StaticParser<Input> >(input, base)
84 {}
85
86
87 template <typename Schema, typename Transform>
88 bool
89 Apply(const Transform& transform, const Schema& schema)
90 {
91 detail::StructBegin(_input, _base);
92 bool result = this->Read(schema, transform);
93 detail::StructEnd(_input, _base);
94 return result;
95 }
96
97 friend class detail::ParserInheritance<Input, StaticParser<Input> >;
98
99
100protected:
101 using detail::ParserInheritance<Input, StaticParser<Input> >::_input;
102 using detail::ParserInheritance<Input, StaticParser<Input> >::_base;
103 using detail::ParserCommon::ReadFields;
104
105private:
106 template <typename Fields>
107 void SkipFields(const Fields& fields)
108 {
109 // Skip the structure by reading fields to Null transform
110 ReadFields(fields, Null());
111 }
112
113 // use compile-time schema
114 template <typename Fields, typename Transform, typename I = Input,
115 typename boost::enable_if<detail::implements_field_omitting<I> >::type* = nullptr>
116 bool
117 ReadFields(const Fields&, const Transform& transform)
118 {
119 typedef typename boost::mpl::deref<Fields>::type Head;
120
121 if (detail::ReadFieldOmitted(_input))
122 detail::OmittedField(Head(), transform);
123 else
124 if (bool done = detail::NonBasicTypeField(Head(), transform, _input))
125 return done;
126
127 return ReadFields(typename boost::mpl::next<Fields>::type(), transform);
128 }
129
130 template <typename Fields, typename Transform, typename I = Input,
131 typename boost::disable_if<detail::implements_field_omitting<I> >::type* = nullptr>
132 bool
133 ReadFields(const Fields&, const Transform& transform)
134 {
135 typedef typename boost::mpl::deref<Fields>::type Head;
136
137 if (bool done = detail::NonBasicTypeField(Head(), transform, _input))
138 return done;
139
140 return ReadFields(typename boost::mpl::next<Fields>::type(), transform);
141 }
142
143
144 // use runtime schema
145 template <typename Transform>
146 bool
147 ReadFields(const RuntimeSchema& schema, const Transform& transform)
148 {
149 bool done = false;
150
151 for (const_enumerator<std::vector<FieldDef> > enumerator(schema.GetStruct().fields); enumerator.more() && !done;)
152 {
153 const FieldDef& field = enumerator.next();
154 const auto type = field.type.id;
155
156 if (detail::ReadFieldOmitted(_input))
157 {
158 transform.OmittedField(field.id, field.metadata, type);
159 continue;
160 }
161
162 if (type == bond::BT_STRUCT || type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
163 {
164 done = detail::NonBasicTypeField(field, schema, transform, _input);
165 }
166 else
167 {
168 done = detail::BasicTypeField(field.id, field.metadata, type, transform, _input);
169 }
170 }
171
172 return done;
173 }
174};
175
176
177//
178// DynamicParser iterates serialized data using field tags included in the
179// data by the protocol and calls specified transform for each data field.
180// DynamicParser uses schema only for auxiliary metadata, such as field
181// names or modifiers, and determines what fields are present from the data itself.
182// The schema may be provided at compile-time (schema<T>::type::fields) or at runtime
183// (const RuntimeSchema&).
184// DynamicParser is used with protocols which tag fields in serialized
185// format with ids and types, e.g. Mafia, Thrift or Protocol Buffers.
186//
187template <typename Input>
188class DynamicParser
189 : protected detail::ParserInheritance<Input, DynamicParser<Input> >,
190 public detail::ParserCommon
191{
192public:
193 DynamicParser(Input input, bool base)
194 : detail::ParserInheritance<Input, DynamicParser<Input> >(input, base)
195 {}
196
197
198 template <typename Schema, typename Transform>
199 bool
200 Apply(const Transform& transform, const Schema& schema)
201 {
202 detail::StructBegin(_input, _base);
203 bool result = this->Read(schema, transform);
204 detail::StructEnd(_input, _base);
205 return result;
206 }
207
208 friend class detail::ParserInheritance<Input, DynamicParser<Input> >;
209
210
211protected:
212 using detail::ParserInheritance<Input, DynamicParser<Input> >::_input;
213 using detail::ParserInheritance<Input, DynamicParser<Input> >::_base;
214
215
216private:
217 template <typename Fields, typename Transform>
218 bool
219 ReadFields(const Fields& fields, const Transform& transform)
220 {
221 uint16_t id;
222 BondDataType type;
223
224 _input.ReadFieldBegin(type, id);
225
226 ReadFields(fields, id, type, transform);
227
228 bool done;
229
230 if (!_base)
231 {
232 // If we are not parsing a base class, and we still didn't get to
233 // the end of the struct, it means that:
234 //
235 // 1) Actual data in the payload had deeper hierarchy than payload schema.
236 //
237 // or
238 //
239 // 2) We parsed only part of the hierarchy because that was what
240 // the transform "expected".
241 //
242 // In both cases we emit remaining fields as unknown
243
244 ReadUnknownFields(type, id, transform);
245 done = false;
246 }
247 else
248 {
249 done = (type == bond::BT_STOP);
250 }
251
252 _input.ReadFieldEnd();
253
254 return done;
255 }
256
257
258 // use compile-time schema
259 template <typename Fields, typename Transform>
260 void
261 ReadFields(const Fields&, uint16_t& id, BondDataType& type, const Transform& transform)
262 {
263 typedef typename boost::mpl::deref<Fields>::type Head;
264
265 for (;;)
266 {
267 const bool moveSchemaField = Head::id <= id;
268 if (Head::id == id && get_type_id<typename Head::field_type>::value == type)
269 {
270 // Exact match
271 detail::NonBasicTypeField(Head(), transform, _input);
272 }
273 else if (Head::id >= id && type != bond::BT_STOP && type != bond::BT_STOP_BASE)
274 {
275 // Unknown field or non-exact type match
276 UnknownFieldOrTypeMismatch<is_basic_type<typename Head::field_type>::value>(
277 Head::id,
278 Head::metadata,
279 id,
280 type,
281 transform);
282 }
283 else
284 {
285 detail::OmittedField(Head(), transform);
286 goto NextSchemaField;
287 }
288
289 ReadSubsequentField(type, id);
290
291 if (moveSchemaField)
292 {
293 NextSchemaField: return ReadFields(typename boost::mpl::next<Fields>::type(), id, type, transform);
294 }
295 }
296 }
297
298
299 template <typename Transform>
300 void
301 ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, uint16_t& id, BondDataType& type, const Transform& transform)
302 {
303 for (; type != bond::BT_STOP && type != bond::BT_STOP_BASE; ReadSubsequentField(type, id))
304 {
305 UnknownField(id, type, transform);
306 }
307 }
308
309
310 // This function is called only when payload has unknown field id or type is not
311 // matching exactly. This relativly rare so we don't inline the function to help
312 // the compiler to optimize the common path.
313 template <bool IsBasicType, typename Transform>
314 BOND_NO_INLINE
315 typename boost::enable_if_c<IsBasicType, bool>::type
316 UnknownFieldOrTypeMismatch(uint16_t expected_id, const Metadata& metadata, uint16_t id, BondDataType type, const Transform& transform)
317 {
318 if (id == expected_id &&
319 type != bond::BT_LIST &&
320 type != bond::BT_SET &&
321 type != bond::BT_MAP &&
322 type != bond::BT_STRUCT)
323 {
324 return detail::BasicTypeField(expected_id, metadata, type, transform, _input);
325 }
326 else
327 {
328 return UnknownField(id, type, transform);
329 }
330 }
331
332 template <bool IsBasicType, typename Transform>
333 BOND_NO_INLINE
334 typename boost::disable_if_c<IsBasicType, bool>::type
335 UnknownFieldOrTypeMismatch(uint16_t /*expected_id*/, const Metadata& /*metadata*/, uint16_t id, BondDataType type, const Transform& transform)
336 {
337 return UnknownField(id, type, transform);
338 }
339
340
341 // use runtime schema
342 template <typename Transform>
343 void
344 ReadFields(const RuntimeSchema& schema, uint16_t& id, BondDataType& type, const Transform& transform)
345 {
346 const auto& fields = schema.GetStruct().fields;
347
348 for (auto it = fields.begin(), end = fields.end(); ; ReadSubsequentField(type, id))
349 {
350 while (it != end && (it->id < id || type == bond::BT_STOP || type == bond::BT_STOP_BASE))
351 {
352 const FieldDef& field = *it++;
353 transform.OmittedField(field.id, field.metadata, field.type.id);
354 }
355
356 if (type == bond::BT_STOP || type == bond::BT_STOP_BASE)
357 {
358 break;
359 }
360
361 if (it != end && it->id == id)
362 {
363 const FieldDef& field = *it++;
364
365 if (type == bond::BT_STRUCT || type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
366 {
367 if (field.type.id == type)
368 {
369 detail::NonBasicTypeField(field, schema, transform, _input);
370 continue;
371 }
372 }
373 else
374 {
375 detail::BasicTypeField(id, field.metadata, type, transform, _input);
376 continue;
377 }
378 }
379
380 UnknownField(id, type, transform);
381 }
382 }
383
384
385 template <typename Transform>
386 void ReadUnknownFields(BondDataType& type, uint16_t& id, const Transform& transform)
387 {
388 for (; type != bond::BT_STOP; ReadSubsequentField(type, id))
389 {
390 if (type == bond::BT_STOP_BASE)
391 transform.UnknownEnd();
392 else
393 UnknownField(id, type, transform);
394 }
395 }
396
397
398 template <typename T, typename Protocols, typename Validator>
399 bool UnknownField(uint16_t, BondDataType type, const To<T, Protocols, Validator>&)
400 {
401 _input.Skip(type);
402 return false;
403 }
404
405
406 template <typename Transform>
407 bool UnknownField(uint16_t id, BondDataType type, const Transform& transform)
408 {
409 if (type == bond::BT_STRUCT)
410 {
411 return transform.UnknownField(id, bonded<void, Input>(_input, GetRuntimeSchema<Unknown>()));
412 }
413 else if (type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
414 return transform.UnknownField(id, value<void, Input>(_input, type));
415 else
416 return detail::BasicTypeField(id, schema<Unknown>::type::metadata, type, BindUnknownField(transform), _input);
417 }
418
419
420 void ReadSubsequentField(BondDataType& type, uint16_t& id)
421 {
422 _input.ReadFieldEnd();
423 _input.ReadFieldBegin(type, id);
424 }
425};
426
427
428// DOM parser works with protocol implementations using Document Object Model,
429// e.g. JSON or XML. The parser assumes that fields in DOM are unordered and
430// are identified by either ordinal or metadata. DOM based protocols may loosly
431// map to Bond meta-schema types thus the parser delegates to the protocol for
432// field type match checking.
433template <typename Input>
434class DOMParser
435 : protected detail::ParserInheritance<Input, DOMParser<Input> >
436{
437 typedef typename std::remove_reference<Input>::type Reader;
438
439public:
440 DOMParser(Input input, bool base = false)
441 : detail::ParserInheritance<Input, DOMParser<Input> >(input, base)
442 {}
443
444
445 template <typename Schema, typename Transform>
446 bool Apply(const Transform& transform, const Schema& schema)
447 {
448 if (!_base) _input.Parse();
449 return this->Read(schema, transform);
450 }
451
452 friend class detail::ParserInheritance<Input, DOMParser<Input> >;
453
454
455protected:
456 using detail::ParserInheritance<Input, DOMParser<Input> >::_input;
457 using detail::ParserInheritance<Input, DOMParser<Input> >::_base;
458
459private:
460 template <typename Fields>
461 void SkipFields(const Fields&)
462 {}
463
464 // use compile-time schema
465 template <typename Fields, typename Transform>
466 bool ReadFields(const Fields&, const Transform& transform)
467 {
468 typedef typename boost::mpl::deref<Fields>::type Head;
469
470 if (const typename Reader::Field* field = _input.FindField(
471 Head::id,
472 Head::metadata,
473 get_type_id<typename Head::field_type>::value,
474 std::is_enum<typename Head::field_type>::value))
475 {
476 Reader input(_input, *field);
477 detail::NonBasicTypeField(Head(), transform, input);
478 }
479
480 return ReadFields(typename boost::mpl::next<Fields>::type(), transform);
481 }
482
483 template <typename Transform>
484 bool ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, const Transform&)
485 {
486 return false;
487 }
488
489
490 // use runtime schema
491 template <typename Transform>
492 bool ReadFields(const RuntimeSchema& schema, const Transform& transform)
493 {
494 bool done = false;
495
496 for (const_enumerator<std::vector<FieldDef> > enumerator(schema.GetStruct().fields); enumerator.more() && !done;)
497 {
498 const FieldDef& fieldDef = enumerator.next();
499 const auto type = fieldDef.type.id;
500
501 if (const typename Reader::Field* field = _input.FindField(fieldDef.id, fieldDef.metadata, type))
502 {
503 Reader input(_input, *field);
504
505 if (type == bond::BT_STRUCT || type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
506 {
507 done = detail::NonBasicTypeField(fieldDef, schema, transform, input);
508 }
509 else
510 {
511 done = detail::BasicTypeField(fieldDef.id, fieldDef.metadata, type, transform, input);
512 }
513 }
514 }
515
516 return done;
517 }
518};
519
520
521} // namespace bond
522
523
524#ifdef BOND_LIB_TYPE
525#if BOND_LIB_TYPE != BOND_LIB_TYPE_HEADER
526#include "detail/parser_extern.h"
527#endif
528#else
529#error BOND_LIB_TYPE is undefined
530#endif
namespace bond
Definition: apply.h:17