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