DCCL v5
Loading...
Searching...
No Matches
field_codec.h
1// Copyright 2011-2023:
2// GobySoft, LLC (2013-)
3// Massachusetts Institute of Technology (2007-2014)
4// Community contributors (see AUTHORS file)
5// File authors:
6// Toby Schneider <toby@gobysoft.org>
7//
8//
9// This file is part of the Dynamic Compact Control Language Library
10// ("DCCL").
11//
12// DCCL is free software: you can redistribute it and/or modify
13// it under the terms of the GNU Lesser General Public License as published by
14// the Free Software Foundation, either version 2.1 of the License, or
15// (at your option) any later version.
16//
17// DCCL is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public License
23// along with DCCL. If not, see <http://www.gnu.org/licenses/>.
24#ifndef DCCLFIELDCODEC20110322H
25#define DCCLFIELDCODEC20110322H
26
27#include <map>
28#include <string>
29
30#include <google/protobuf/descriptor.h>
31#include <google/protobuf/message.h>
32
33#include "any.h"
34#include "binary.h"
35#include "common.h"
36#include "dynamic_conditions.h"
37#include "exception.h"
38#include "internal/field_codec_message_stack.h"
39#include "internal/type_helper.h"
40#include "oneof.h"
41#include "option_extensions.pb.h"
42
43namespace dccl
44{
45class Codec;
46namespace internal
47{
48class MessageStack;
49}
50
53{
54 public:
56
57
59 virtual ~FieldCodecBase() = default;
61
63
64
65 std::string name() const { return name_; }
69 google::protobuf::FieldDescriptor::Type field_type() const { return field_type_; }
74 google::protobuf::FieldDescriptor::CppType wire_type() const { return wire_type_; }
75
79 const google::protobuf::FieldDescriptor* this_field() const;
80
94 const google::protobuf::Descriptor* this_descriptor() const;
95
96 const google::protobuf::Message* this_message();
97
98 // currently encoded or (partially) decoded root message
99 const google::protobuf::Message* root_message();
100
101 const google::protobuf::Descriptor* root_descriptor() const;
102
108 const Bitset* root_bitset() const;
109
110 internal::MessageStackData& message_data();
111
112 const internal::MessageStackData& message_data() const;
113
114 bool has_codec_group();
115
116 static std::string codec_group(const google::protobuf::Descriptor* desc);
117
118 std::string codec_group();
119
120 int codec_version();
121
123 MessagePart part();
124
125 bool strict();
126
128 void set_force_use_required(bool force_required = true) { force_required_ = force_required; }
129
131
142
143
149 void base_encode(Bitset* bits, const google::protobuf::Message& msg, MessagePart part,
150 bool strict);
151
157 void base_size(unsigned* bit_size, const google::protobuf::Message& msg, MessagePart part);
158
164 void base_decode(Bitset* bits, google::protobuf::Message* msg, MessagePart part);
165
171 void base_max_size(unsigned* bit_size, const google::protobuf::Descriptor* desc,
172 MessagePart part);
173
179 void base_min_size(unsigned* bit_size, const google::protobuf::Descriptor* desc,
180 MessagePart part);
181
186 void base_validate(const google::protobuf::Descriptor* desc, MessagePart part);
187
193 void base_info(std::ostream* os, const google::protobuf::Descriptor* desc, MessagePart part);
194
200 void base_hash(std::size_t* hash, const google::protobuf::Descriptor* desc, MessagePart part);
202
204 //
213
214
219 void field_pre_encode(dccl::any* wire_value, const dccl::any& field_value)
220 {
221 any_pre_encode(wire_value, field_value);
222 }
223
228 void field_pre_encode_repeated(std::vector<dccl::any>* wire_values,
229 const std::vector<dccl::any>& field_values)
230 {
231 any_pre_encode_repeated(wire_values, field_values);
232 }
233
234 // traverse const
235
241 void field_encode(Bitset* bits, const dccl::any& field_value,
242 const google::protobuf::FieldDescriptor* field);
243
249 void field_encode_repeated(Bitset* bits, const std::vector<dccl::any>& field_values,
250 const google::protobuf::FieldDescriptor* field);
251
257 void field_size(unsigned* bit_size, const dccl::any& field_value,
258 const google::protobuf::FieldDescriptor* field);
259
265 void field_size_repeated(unsigned* bit_size, const std::vector<dccl::any>& field_values,
266 const google::protobuf::FieldDescriptor* field);
267
268 // traverse mutable
274 void field_decode(Bitset* bits, dccl::any* field_value,
275 const google::protobuf::FieldDescriptor* field);
276
282 void field_decode_repeated(Bitset* bits, std::vector<dccl::any>* field_values,
283 const google::protobuf::FieldDescriptor* field);
284
289 void field_post_decode(const dccl::any& wire_value, dccl::any* field_value)
290 {
291 any_post_decode(wire_value, field_value);
292 }
293
298 void field_post_decode_repeated(const std::vector<dccl::any>& wire_values,
299 std::vector<dccl::any>* field_values)
300 {
301 any_post_decode_repeated(wire_values, field_values);
302 }
303
304 // traverse schema (Descriptor)
305
310 void field_max_size(unsigned* bit_size, const google::protobuf::FieldDescriptor* field);
315 void field_min_size(unsigned* bit_size, const google::protobuf::FieldDescriptor* field);
316
322 void field_validate(bool* b, const google::protobuf::FieldDescriptor* field);
323
328 void field_info(std::ostream* os, const google::protobuf::FieldDescriptor* field);
329
334 void field_hash(std::size_t* hash, const google::protobuf::FieldDescriptor* field);
336
341 {
342 if (this_field())
343 return this_field()->options().GetExtension(dccl::field);
344 else
345 throw(Exception(
346 "Cannot call dccl_field on base message (has no *field* option extension"));
347 }
348
354 void require(bool b, const std::string& description)
355 {
356 if (!b)
357 {
358 if (this_field())
359 throw(Exception("Field " + std::string(this_field()->name()) +
360 " failed validation: " + description,
361 this->this_descriptor()));
362 else
363 throw(Exception("Message " + std::string(this_descriptor()->name()) +
364 " failed validation: " + description,
365 this->this_descriptor()));
366 }
367 }
368
369 DynamicConditions& dynamic_conditions(const google::protobuf::FieldDescriptor* field);
370
371 FieldCodecManagerLocal& manager()
372 {
373 if (manager_)
374 return *manager_;
375 else
376 throw(Exception("FieldCodecManagerLocal is not set"), this->this_descriptor());
377 }
378
379 const FieldCodecManagerLocal& manager() const
380 {
381 if (manager_)
382 return *manager_;
383 else
384 throw(Exception("FieldCodecManagerLocal is not set"), this->this_descriptor());
385 }
386
387 virtual void set_manager(FieldCodecManagerLocal* manager) { manager_ = manager; }
388
389 protected:
392 {
393 if (force_required_)
394 return true;
395
396 const google::protobuf::FieldDescriptor* field = this_field();
397 DynamicConditions& dc = dynamic_conditions(field);
398 // expensive, so don't do this unless we're going to use it
399 if (dc.has_required_if())
400 dc.regenerate(this_message(), root_message());
401
402 if (!field)
403 return true;
404
405 if (codec_version() > 3) // use required for repeated, required and oneof fields, and proto3 "standard" fields with no optional tag
406 return field->is_required() || field->is_repeated() ||
407 is_part_of_oneof(field) || !field->has_presence() || (dc.has_required_if() && dc.required());
408 else if (codec_version() > 2) // use required for both repeated and required fields
409 return field->is_required() || field->is_repeated() ||
410 (dc.has_required_if() && dc.required());
411 else // use required only for required fields
412 return field->is_required();
413 }
414
415 //
416 // VIRTUAL
417 //
418
419 // contain dccl::any
424 virtual void any_encode(Bitset* bits, const dccl::any& wire_value) = 0;
425
430 virtual void any_decode(Bitset* bits, dccl::any* wire_value) = 0;
431
436 virtual void any_pre_encode(dccl::any* wire_value, const dccl::any& field_value)
437 {
438 *wire_value = field_value;
439 }
440
445 virtual void any_post_decode(const dccl::any& wire_value, dccl::any* field_value)
446 {
447 *field_value = wire_value;
448 }
449
454 virtual unsigned any_size(const dccl::any& wire_value) = 0;
455
456 // no dccl::any
458 virtual void validate() {}
459
463 virtual std::string info();
464
466 virtual std::size_t hash() { return 0; }
467
471 virtual unsigned max_size() = 0;
472
476 virtual unsigned min_size() = 0;
477
478 virtual void any_encode_repeated(Bitset* bits, const std::vector<dccl::any>& wire_values);
479 virtual void any_decode_repeated(Bitset* repeated_bits, std::vector<dccl::any>* field_values);
480
481 virtual void any_pre_encode_repeated(std::vector<dccl::any>* wire_values,
482 const std::vector<dccl::any>& field_values);
483
484 virtual void any_post_decode_repeated(const std::vector<dccl::any>& wire_values,
485 std::vector<dccl::any>* field_values);
486
487 virtual unsigned any_size_repeated(const std::vector<dccl::any>& wire_values);
488 virtual unsigned max_size_repeated();
489 virtual unsigned min_size_repeated();
490 void check_repeat_settings() const;
491
492 friend class FieldCodecManagerLocal;
493
494 private:
495 // codec information
496 void set_name(const std::string& name) { name_ = name; }
497 void set_field_type(google::protobuf::FieldDescriptor::Type type) { field_type_ = type; }
498 void set_wire_type(google::protobuf::FieldDescriptor::CppType type) { wire_type_ = type; }
499
500 bool variable_size()
501 {
502 if (this_field() && this_field()->is_repeated())
503 return max_size_repeated() != min_size_repeated();
504 else
505 return max_size() != min_size();
506 }
507
508 int repeated_vector_field_size(int min_repeat, int max_repeat)
509 {
510 return dccl::ceil_log2(max_repeat - min_repeat + 1);
511 }
512
513 void disp_size(const google::protobuf::FieldDescriptor* field, const Bitset& new_bits,
514 int depth, int vector_size = -1);
515
516 private:
517 // sets global statics relating the current message begin processed
518 // and unsets them on destruction
519 struct BaseRAII
520 {
521 BaseRAII(FieldCodecBase* field_codec, MessagePart part,
522 const google::protobuf::Descriptor* root_descriptor, bool strict = false);
523
524 BaseRAII(FieldCodecBase* field_codec, MessagePart part,
525 const google::protobuf::Message* root_message, bool strict = false);
526 ~BaseRAII();
527
528 private:
529 FieldCodecBase* field_codec_;
530 };
531 friend struct BaseRAII;
532
533 std::string name_;
534 google::protobuf::FieldDescriptor::Type field_type_;
535 google::protobuf::FieldDescriptor::CppType wire_type_;
536
537 bool force_required_{false};
538
539 FieldCodecManagerLocal* manager_{nullptr};
540};
541
542std::ostream& operator<<(std::ostream& os, const FieldCodecBase& field_codec);
543
544inline Exception type_error(const std::string& action, const std::type_info& expected,
545 const std::type_info& got)
546{
547 std::string e = "error " + action + ", expected: ";
548 e += expected.name();
549 e += ", got ";
550 e += got.name();
551 return Exception(e);
552}
553
554} // namespace dccl
555
556#endif
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition bitset.h:43
Exception class for DCCL.
Definition exception.h:47
Provides a base class for defining DCCL field encoders / decoders. Most users who wish to define cust...
Definition field_codec.h:53
const Bitset * root_bitset() const
Returns the root Bitset for the current encode or decode operation.
void field_encode(Bitset *bits, const dccl::any &field_value, const google::protobuf::FieldDescriptor *field)
Encode a non-repeated field.
void set_force_use_required(bool force_required=true)
Force the codec to always use the "required" field encoding, regardless of the FieldDescriptor settin...
void field_size(unsigned *bit_size, const dccl::any &field_value, const google::protobuf::FieldDescriptor *field)
Calculate the size of a field.
void base_hash(std::size_t *hash, const google::protobuf::Descriptor *desc, MessagePart part)
Provide a hash of the DCCL message definition to detect changes in the DCCL message.
const google::protobuf::Descriptor * this_descriptor() const
Returns the Descriptor (message schema meta-data) for the immediate parent Message.
virtual void any_pre_encode(dccl::any *wire_value, const dccl::any &field_value)
Virtual method used to pre-encode (convert from FieldType to WireType). The default implementation of...
virtual std::string info()
Write field specific information (in addition to general information such as sizes that are automatic...
google::protobuf::FieldDescriptor::CppType wire_type() const
the C++ type used "on the wire". This is the type visible after pre_encode and before post_decode fun...
Definition field_codec.h:74
virtual void any_post_decode(const dccl::any &wire_value, dccl::any *field_value)
Virtual method used to post-decode (convert from WireType to FieldType). The default implementation o...
void field_post_decode(const dccl::any &wire_value, dccl::any *field_value)
Post-decodes a non-repeated (i.e. optional or required) field by converting the WireType (the type us...
const google::protobuf::FieldDescriptor * this_field() const
Returns the FieldDescriptor (field schema meta-data) for this field.
MessagePart part()
the part of the message currently being encoded (head or body)
void require(bool b, const std::string &description)
Essentially an assertion to be used in the validate() virtual method.
void field_encode_repeated(Bitset *bits, const std::vector< dccl::any > &field_values, const google::protobuf::FieldDescriptor *field)
Encode a repeated field.
virtual void validate()
Validate a field. Use require() inside your overloaded validate() to assert requirements or throw Exc...
std::string name() const
the name of the codec used to identifier it in the .proto custom option extension
Definition field_codec.h:65
virtual unsigned min_size()=0
Calculate minimum size of the field in bits.
void field_post_decode_repeated(const std::vector< dccl::any > &wire_values, std::vector< dccl::any > *field_values)
Post-decodes a repeated field.
void base_validate(const google::protobuf::Descriptor *desc, MessagePart part)
Validate this part of the message to make sure all required extensions are set.
void field_decode(Bitset *bits, dccl::any *field_value, const google::protobuf::FieldDescriptor *field)
Decode a non-repeated field.
dccl::DCCLFieldOptions dccl_field_options() const
Get the DCCL field option extension value for the current field.
void field_pre_encode_repeated(std::vector< dccl::any > *wire_values, const std::vector< dccl::any > &field_values)
Pre-encodes a repeated field.
void base_info(std::ostream *os, const google::protobuf::Descriptor *desc, MessagePart part)
Get human readable information (size of fields, etc.) about this part of the DCCL message.
virtual unsigned any_size(const dccl::any &wire_value)=0
Virtual method for calculating the size of a field (in bits).
void base_min_size(unsigned *bit_size, const google::protobuf::Descriptor *desc, MessagePart part)
Calculate the minimum size of a message given its Descriptor alone (no data)
void base_max_size(unsigned *bit_size, const google::protobuf::Descriptor *desc, MessagePart part)
Calculate the maximum size of a message given its Descriptor alone (no data)
virtual std::size_t hash()
Generate a field specific hash to be combined with the descriptor hash.
void field_hash(std::size_t *hash, const google::protobuf::FieldDescriptor *field)
Provide a hash for this field definition.
void base_decode(Bitset *bits, google::protobuf::Message *msg, MessagePart part)
Decode part of a message.
void base_size(unsigned *bit_size, const google::protobuf::Message &msg, MessagePart part)
Calculate the size (in bits) of a part of the base message when it is encoded.
google::protobuf::FieldDescriptor::Type field_type() const
the type exposed to the user in the original and decoded Protobuf messages
Definition field_codec.h:69
void field_min_size(unsigned *bit_size, const google::protobuf::FieldDescriptor *field)
Calculate the lower bound on this field's size (in bits)
void base_encode(Bitset *bits, const google::protobuf::Message &msg, MessagePart part, bool strict)
Encode this part (body or head) of the base message.
virtual unsigned max_size()=0
Calculate maximum size of the field in bits.
virtual void any_decode(Bitset *bits, dccl::any *wire_value)=0
Virtual method used to decode.
bool use_required()
Whether to use the required or optional encoding.
void field_validate(bool *b, const google::protobuf::FieldDescriptor *field)
Validate this field, checking that all required option extensions are set (e.g. (dccl....
void field_max_size(unsigned *bit_size, const google::protobuf::FieldDescriptor *field)
Calculate the upper bound on this field's size (in bits)
void field_decode_repeated(Bitset *bits, std::vector< dccl::any > *field_values, const google::protobuf::FieldDescriptor *field)
Decode a repeated field.
void field_size_repeated(unsigned *bit_size, const std::vector< dccl::any > &field_values, const google::protobuf::FieldDescriptor *field)
Calculate the size of a repeated field.
void field_info(std::ostream *os, const google::protobuf::FieldDescriptor *field)
Write human readable information about the field and its bounds to the provided stream.
virtual void any_encode(Bitset *bits, const dccl::any &wire_value)=0
Virtual method used to encode.
void field_pre_encode(dccl::any *wire_value, const dccl::any &field_value)
Pre-encodes a non-repeated (i.e. optional or required) field by converting the FieldType representati...
A class for managing the various field codecs. Here you can add and remove field codecs....
Dynamic Compact Control Language namespace.
Definition any.h:28
bool is_part_of_oneof(const google::protobuf::FieldDescriptor *field_desc)
Checks whether a given field is part to a oneof or not.
Definition oneof.h:35
unsigned ceil_log2(dccl::uint64 v)
Definition binary.h:163