DCCL v4
field_codec_default_message.h
1 // Copyright 2009-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 DCCLFIELDCODECDEFAULTMESSAGEV420210701H
25 #define DCCLFIELDCODECDEFAULTMESSAGEV420210701H
26 
27 #include <unordered_map>
28 
29 #include "../field_codec.h"
30 #include "../field_codec_manager.h"
31 #include "../oneof.h"
32 
33 #include "dccl/option_extensions.pb.h"
34 
35 namespace dccl
36 {
37 namespace v4
38 {
41 {
42  private:
43  void any_encode(Bitset* bits, const dccl::any& wire_value) override;
44  void any_decode(Bitset* bits, dccl::any* wire_value) override;
45  unsigned max_size() override;
46  unsigned min_size() override;
47  unsigned any_size(const dccl::any& wire_value) override;
48 
49  std::shared_ptr<FieldCodecBase> find(const google::protobuf::FieldDescriptor* field_desc)
50  {
51  return manager().find(field_desc, this->codec_version(), has_codec_group(), codec_group());
52  }
53 
54  bool is_optional() { return this_field() && this_field()->is_optional() && !use_required(); }
55 
56  void validate() override;
57  std::string info() override;
58  std::size_t hash() override;
59  bool check_field(const google::protobuf::FieldDescriptor* field);
60 
61  struct Size
62  {
63  static void repeated(std::shared_ptr<FieldCodecBase> codec, unsigned* return_value,
64  const std::vector<dccl::any>& field_values,
65  const google::protobuf::FieldDescriptor* field_desc)
66  {
67  codec->field_size_repeated(return_value, field_values, field_desc);
68  }
69 
70  static void single(std::shared_ptr<FieldCodecBase> codec, unsigned* return_value,
71  const dccl::any& field_value,
72  const google::protobuf::FieldDescriptor* field_desc)
73  {
74  if (!is_part_of_oneof(field_desc))
75  codec->field_size(return_value, field_value, field_desc);
76  else
77  {
78  // If the field belongs to a oneof, do nothing if the value is empty,
79  // or add the size of the fiels as if it were required otherwise
80  if (!is_empty(field_value))
81  codec->field_size(return_value, field_value, field_desc);
82  }
83  }
84 
85  static void oneof(unsigned* return_value,
86  const google::protobuf::OneofDescriptor* oneof_desc,
88  {
89  // Add the bits needed to encode the case enumerator
90  *return_value += oneof_size(oneof_desc);
91  }
92  };
93 
94  struct Encoder
95  {
96  static void repeated(std::shared_ptr<FieldCodecBase> codec, Bitset* return_value,
97  const std::vector<dccl::any>& field_values,
98  const google::protobuf::FieldDescriptor* field_desc)
99  {
100  codec->field_encode_repeated(return_value, field_values, field_desc);
101  }
102 
103  static void single(std::shared_ptr<FieldCodecBase> codec, Bitset* return_value,
104  const dccl::any& field_value,
105  const google::protobuf::FieldDescriptor* field_desc)
106  {
107  if (!is_part_of_oneof(field_desc))
108  codec->field_encode(return_value, field_value, field_desc);
109  else
110  {
111  // If the field belongs to a oneof, do nothing if the value is empty,
112  // or encode the fiels as if it were required otherwise
113  if (!is_empty(field_value))
114  codec->field_encode(return_value, field_value, field_desc);
115  }
116  }
117 
118  static void oneof(Bitset* return_value, const google::protobuf::OneofDescriptor* oneof_desc,
119  const google::protobuf::Message& msg)
120  {
121  // Encode 0 if oneof is not set, the index of the field set + 1 otherwise
122  unsigned case_ = 0;
123  auto refl = msg.GetReflection();
124  if (refl->HasOneof(msg, oneof_desc))
125  for (auto i = 0; i < oneof_desc->field_count(); ++i)
126  if (refl->HasField(msg, oneof_desc->field(i)))
127  {
128  case_ = i + 1;
129  break;
130  }
131 
132  Bitset case_bits(oneof_size(oneof_desc), case_);
133  return_value->append(case_bits);
134  }
135  };
136 
137  struct MaxSize
138  {
139  // Keeps track of the maximum size of each oneof
140  static std::unordered_map<std::string, unsigned> oneofs_max_size;
141 
142  static void field(std::shared_ptr<FieldCodecBase> codec, unsigned* return_value,
143  const google::protobuf::FieldDescriptor* field_desc)
144  {
145  if (!is_part_of_oneof(field_desc))
146  codec->field_max_size(return_value, field_desc);
147  else
148  {
149  // For oneof fields the max size is the size of the largest field belonging
150  // to the oneof itself, considering all of them required.
151 
152  // Get the max size of the field
153  auto fld_max_size = 0u;
154  codec->field_max_size(&fld_max_size, field_desc);
155 
156  auto parent_oneof_name = field_desc->containing_oneof()->full_name();
157 
158  // Calculate the maximum between the field's size and the latest maximum size stored
159  auto new_max_size = std::max(fld_max_size, oneofs_max_size[parent_oneof_name]);
160 
161  // Add the difference between the new and the old max size to the result and store the
162  // new max size
163  *return_value += (new_max_size - oneofs_max_size[parent_oneof_name]);
164  oneofs_max_size[parent_oneof_name] = new_max_size;
165  }
166  }
167 
168  static void oneof(unsigned* return_value,
169  const google::protobuf::OneofDescriptor* oneof_desc,
170  FieldCodecBase* /*field_codec*/)
171  {
172  // Add the bits needed to encode the case enumerator
173  *return_value += oneof_size(oneof_desc);
174  // Add the maximum size among the fields (0 if not initialised0
175  *return_value += oneofs_max_size[oneof_desc->full_name()];
176  }
177  };
178 
179  struct MinSize
180  {
181  static void field(std::shared_ptr<FieldCodecBase> codec, unsigned* return_value,
182  const google::protobuf::FieldDescriptor* field_desc)
183  {
184  // defer minimum size calculation for dynamic conditions (since omit == 0)
185  // this may provide an incorrect min_size() value for reporting but allows
186  // use of index based lua scripts in dynamic conditions
187  DynamicConditions& dc = codec->dynamic_conditions(field_desc);
188  if (dc.has_omit_if() || dc.has_required_if())
189  *return_value = 0;
190  // Minimum size of a field belonging to oneof is zero
191  else if (!is_part_of_oneof(field_desc))
192  codec->field_min_size(return_value, field_desc);
193  }
194 
195  static void oneof(unsigned* return_value,
196  const google::protobuf::OneofDescriptor* oneof_desc,
197  FieldCodecBase* /*field_codec*/)
198  {
199  // Add the bits needed to encode the case enumerator
200  *return_value += oneof_size(oneof_desc);
201  }
202  };
203 
204  struct Validate
205  {
206  static void field(std::shared_ptr<FieldCodecBase> codec, bool* return_value,
207  const google::protobuf::FieldDescriptor* field_desc)
208  {
209  codec->field_validate(return_value, field_desc);
210  }
211 
212  static void oneof(bool*, const google::protobuf::OneofDescriptor*,
213  FieldCodecBase* field_codec)
214  { /* Do nothing */
215  }
216  };
217 
218  struct Info
219  {
220  static void field(std::shared_ptr<FieldCodecBase> codec, std::stringstream* return_value,
221  const google::protobuf::FieldDescriptor* field_desc)
222  {
223  if (!is_part_of_oneof(field_desc))
224  codec->field_info(return_value, field_desc);
225  }
226 
227  static void oneof(std::stringstream* return_value,
228  const google::protobuf::OneofDescriptor* oneof_desc,
229  FieldCodecBase* field_codec)
230  {
231  // Do nothing if the oneof descriptor is null
232  if (!oneof_desc)
233  return;
234 
235  // Print it otherwise
236  internal::MessageStack msg_handler(field_codec->root_message(),
237  field_codec->message_data());
238  std::stringstream ss;
239  int depth = msg_handler.count();
240 
241  std::string name =
242  std::to_string(oneof_desc->index()) + ". " + oneof_desc->name() + " [oneof]";
243 
244  // Calculate indentation
245  const int spaces = 8;
246  std::string indent = std::string(spaces * (depth), ' ');
247  const int full_width = 40;
248 
249  int width = full_width - name.size();
250 
251  std::stringstream range;
252  unsigned max_sz = 0, min_sz = 0;
253  MaxSize::oneof(&max_sz, oneof_desc, field_codec);
254  MinSize::oneof(&min_sz, oneof_desc, field_codec);
255  range << min_sz << "-" << max_sz;
256 
257  ss << indent << name << std::setfill('.') << std::setw(std::max(1, width))
258  << range.str();
259 
260  *return_value << ss.str() << " {\n";
261  // Add oneof field's info
262  for (auto i = 0; i < oneof_desc->field_count(); ++i)
263  {
264  auto codec = field_codec->manager().find(
265  oneof_desc->field(i), field_codec->codec_version(),
266  field_codec->has_codec_group(), field_codec->codec_group());
267  codec->field_info(return_value, oneof_desc->field(i));
268  }
269 
270  *return_value << indent << "}\n";
271  }
272  };
273 
274  struct Hash
275  {
276  static void field(std::shared_ptr<FieldCodecBase> codec, std::size_t* return_value,
277  const google::protobuf::FieldDescriptor* field_desc)
278  {
279  codec->field_hash(return_value, field_desc);
280  }
281 
282  static void oneof(std::size_t* return_value,
283  const google::protobuf::OneofDescriptor* oneof_desc,
284  FieldCodecBase* field_codec)
285  {
286  // Do nothing if the oneof descriptor is null
287  if (!oneof_desc)
288  return;
289 
290  hash_combine(*return_value, oneof_desc->DebugString());
291  }
292  };
293 
294  template <typename Action, typename ReturnType>
295  void traverse_descriptor(ReturnType* return_value)
296  {
297  const google::protobuf::Descriptor* desc = FieldCodecBase::this_descriptor();
298 
299  // First, process the oneof definitions...
300  for (auto i = 0, n = desc->oneof_decl_count(); part() != HEAD && i < n; ++i)
301  Action::oneof(return_value, desc->oneof_decl(i), this);
302 
303  // ... then, process the fields
304  for (int i = 0, n = desc->field_count(); i < n; ++i)
305  {
306  const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
307 
308  if (!check_field(field_desc))
309  continue;
310 
311  Action::field(find(field_desc), return_value, field_desc);
312  }
313  }
314 
315  template <typename Action, typename ReturnType>
316  ReturnType traverse_const_message(const dccl::any& wire_value)
317  {
318  try
319  {
320  ReturnType return_value = ReturnType();
321 
322  const auto* msg = dccl::any_cast<const google::protobuf::Message*>(wire_value);
323  const google::protobuf::Descriptor* desc = msg->GetDescriptor();
324  const google::protobuf::Reflection* refl = msg->GetReflection();
325 
326  // First, process the oneof definitions...
327  for (auto i = 0, n = desc->oneof_decl_count(); part() != HEAD && i < n; ++i)
328  Action::oneof(&return_value, desc->oneof_decl(i), *msg);
329 
330  // ... then, process the fields
331  for (int i = 0, n = desc->field_count(); i < n; ++i)
332  {
333  const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
334 
335  if (!check_field(field_desc))
336  continue;
337 
338  std::shared_ptr<FieldCodecBase> codec = find(field_desc);
339  std::shared_ptr<internal::FromProtoCppTypeBase> helper =
340  manager().type_helper().find(field_desc);
341 
342  if (field_desc->is_repeated())
343  {
344  std::vector<dccl::any> field_values;
345  for (int j = 0, m = refl->FieldSize(*msg, field_desc); j < m; ++j)
346  field_values.push_back(helper->get_repeated_value(field_desc, *msg, j));
347 
348  Action::repeated(codec, &return_value, field_values, field_desc);
349  }
350  else
351  {
352  // singular field dynamic conditions - repeated fields handled in any_encode_repeated
353  DynamicConditions& dc = dynamic_conditions(field_desc);
354  if (dc.has_omit_if())
355  {
356  // expensive, so don't do this unless we're going to use it
357  dc.regenerate(this_message(), root_message());
358  if (dc.omit())
359  continue;
360  }
361 
362  Action::single(codec, &return_value, helper->get_value(field_desc, *msg),
363  field_desc);
364  }
365  }
366  return return_value;
367  }
368  catch (dccl::bad_any_cast& e)
369  {
370  throw(Exception("Bad type given to traverse const, expecting const "
371  "google::protobuf::Message*, got " +
372  std::string(wire_value.type().name())));
373  }
374  }
375 };
376 
377 } // namespace v4
378 } // namespace dccl
379 
380 //encode, size, etc.
381 #endif
dccl::FieldCodecBase::this_descriptor
const google::protobuf::Descriptor * this_descriptor() const
Returns the Descriptor (message schema meta-data) for the immediate parent Message.
Definition: field_codec.cpp:658
dccl::v4::DefaultMessageCodec
Provides the default codec for encoding a base Google Protobuf message or an embedded message by call...
Definition: field_codec_default_message.h:40
dccl::FieldCodecBase
Provides a base class for defining DCCL field encoders / decoders. Most users who wish to define cust...
Definition: field_codec.h:54
dccl::FieldCodecManagerLocal::find
std::shared_ptr< FieldCodecBase > find(const google::protobuf::FieldDescriptor *field, int codec_version, bool has_codec_group, const std::string &codec_group) const
Find the codec for a given field. For embedded messages, prefers (dccl.field).codec (inside field) ov...
Definition: field_codec_manager.h:114
dccl::DynamicConditions
Definition: dynamic_conditions.h:39
dccl
Dynamic Compact Control Language namespace.
Definition: any.h:49
dccl::FieldCodecBase::this_field
const google::protobuf::FieldDescriptor * this_field() const
Returns the FieldDescriptor (field schema meta-data) for this field.
Definition: field_codec.cpp:653
dccl::Exception
Exception class for DCCL.
Definition: exception.h:47
dccl::internal::MessageStack
Definition: field_codec_message_stack.h:72
dccl::oneof_size
int oneof_size(const google::protobuf::OneofDescriptor *oneof_desc)
Returns the number of bits needed to represent the oneof cases (including the unset case).
Definition: oneof.h:56
dccl::Bitset::append
Bitset & append(const Bitset &bits)
Adds the bitset to the big end.
Definition: bitset.h:360
dccl::Bitset
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition: bitset.h:41
Message
dccl::FieldCodecBase::use_required
bool use_required()
Whether to use the required or optional encoding.
Definition: field_codec.h:386
dccl::FieldCodecBase::part
MessagePart part()
the part of the message currently being encoded (head or body)
Definition: field_codec.cpp:699
dccl::FieldCodecBase::name
std::string name() const
the name of the codec used to identifier it in the .proto custom option extension
Definition: field_codec.h:67
dccl::is_part_of_oneof
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