DCCL v4
Loading...
Searching...
No Matches
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
35namespace dccl
36{
37namespace 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,
87 const google::protobuf::Message&)
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
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition bitset.h:43
Bitset & append(const Bitset &bits)
Adds the bitset to the big end.
Definition bitset.h:361
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:54
const google::protobuf::Descriptor * this_descriptor() const
Returns the Descriptor (message schema meta-data) for the immediate parent Message.
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)
std::string name() const
the name of the codec used to identifier it in the .proto custom option extension
Definition field_codec.h:66
bool use_required()
Whether to use the required or optional encoding.
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...
Provides the default codec for encoding a base Google Protobuf message or an embedded message by call...
Dynamic Compact Control Language namespace.
Definition any.h:47
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:36
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:57
void hash_combine(std::size_t &seed, const T &v)
hash combine - from boost::hash_combine
Definition common.h:152