DCCL v4
field_codec_default_message.cpp
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 #include "field_codec_default_message.h"
25 #include "../codec.h"
26 #include "../oneof.h"
27 
28 std::unordered_map<std::string, unsigned> dccl::v4::DefaultMessageCodec::MaxSize::oneofs_max_size;
29 
30 //
31 // DefaultMessageCodec
32 //
33 
34 void dccl::v4::DefaultMessageCodec::any_encode(Bitset* bits, const dccl::any& wire_value)
35 {
36  if (is_empty(wire_value))
37  {
38  *bits = Bitset(min_size());
39  }
40  else
41  {
42  *bits = traverse_const_message<Encoder, Bitset>(wire_value);
43 
44  if (is_optional())
45  bits->push_front(true); // presence bit
46  }
47 }
48 
49 unsigned dccl::v4::DefaultMessageCodec::any_size(const dccl::any& wire_value)
50 {
51  if (is_empty(wire_value))
52  {
53  return min_size();
54  }
55  else
56  {
57  unsigned size = traverse_const_message<Size, unsigned>(wire_value);
58  if (is_optional())
59  {
60  const unsigned presence_bit = 1;
61  size += presence_bit;
62  }
63 
64  return size;
65  }
66 }
67 
68 void dccl::v4::DefaultMessageCodec::any_decode(Bitset* bits, dccl::any* wire_value)
69 {
70  try
71  {
72  auto* msg = dccl::any_cast<google::protobuf::Message*>(*wire_value);
73 
74  if (is_optional())
75  {
76  if (!bits->to_ulong())
77  {
78  *wire_value = dccl::any();
79  return;
80  }
81  else
82  {
83  bits->pop_front(); // presence bit
84  }
85  }
86 
87  const google::protobuf::Descriptor* desc = msg->GetDescriptor();
88  const google::protobuf::Reflection* refl = msg->GetReflection();
89 
90  // First, process the oneof definitions, storing the case value...
91  std::vector<int> oneof_cases(desc->oneof_decl_count());
92  for (auto i = 0, n = desc->oneof_decl_count(); part() != HEAD && i < n; ++i)
93  {
94  Bitset case_bits(bits);
95  case_bits.get_more_bits(oneof_size(desc->oneof_decl(i)));
96 
97  // Store the index of the field set for the i-th oneof (if unset, it will be -1)
98  oneof_cases[i] = static_cast<int>(case_bits.to_ulong()) - 1;
99  }
100 
101  // ... then, process the fields
102  for (int i = 0, n = desc->field_count(); i < n; ++i)
103  {
104  const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
105 
106  if (!check_field(field_desc))
107  continue;
108 
109  std::shared_ptr<FieldCodecBase> codec = find(field_desc);
110  std::shared_ptr<internal::FromProtoCppTypeBase> helper =
111  manager().type_helper().find(field_desc);
112 
113  if (field_desc->is_repeated())
114  {
115  std::vector<dccl::any> field_values;
116  if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
117  {
118  unsigned max_repeat =
119  field_desc->options().GetExtension(dccl::field).max_repeat();
120  for (unsigned j = 0, m = max_repeat; j < m; ++j)
121  field_values.emplace_back(refl->AddMessage(msg, field_desc));
122 
123  codec->field_decode_repeated(bits, &field_values, field_desc);
124 
125  // remove the unused messages
126  for (int j = field_values.size(), m = max_repeat; j < m; ++j)
127  {
128  refl->RemoveLast(msg, field_desc);
129  }
130  }
131  else
132  {
133  // for primitive types
134  codec->field_decode_repeated(bits, &field_values, field_desc);
135  for (auto& field_value : field_values)
136  helper->add_value(field_desc, msg, field_value);
137  }
138  }
139  else
140  {
141  if (is_part_of_oneof(field_desc))
142  {
143  // If the field belongs to a oneof and its index is the one stored for the containing
144  // oneof, decode it; otherwise, skip the field.
145  if (field_desc->index_in_oneof() !=
146  oneof_cases[containing_oneof_index(field_desc)])
147  continue;
148  }
149 
150  // singular field dynamic conditions - repeated fields handled in any_decode_repeated
151  DynamicConditions& dc = dynamic_conditions(field_desc);
152  if (dc.has_omit_if())
153  {
154  // expensive, so don't do this unless we're going to use it
155  dc.regenerate(this_message(), root_message());
156  if (dc.omit())
157  continue;
158  }
159 
160  dccl::any field_value;
161  if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
162  {
163  // allows us to propagate pointers instead of making many copies of entire messages
164  field_value = refl->MutableMessage(msg, field_desc);
165  codec->field_decode(bits, &field_value, field_desc);
166  if (is_empty(field_value))
167  refl->ClearField(msg, field_desc);
168  }
169  else
170  {
171  // for primitive types
172  codec->field_decode(bits, &field_value, field_desc);
173  helper->set_value(field_desc, msg, field_value);
174  }
175  }
176  }
177 
178  std::vector<const google::protobuf::FieldDescriptor*> set_fields;
179  refl->ListFields(*msg, &set_fields);
180  *wire_value = msg;
181  }
182  catch (dccl::bad_any_cast& e)
183  {
184  throw(Exception(
185  "Bad type given to traverse mutable, expecting google::protobuf::Message*, got " +
186  std::string(wire_value->type().name())));
187  }
188 }
189 
190 unsigned dccl::v4::DefaultMessageCodec::max_size()
191 {
192  unsigned u = 0;
193  traverse_descriptor<MaxSize>(&u);
194 
195  if (is_optional())
196  {
197  const unsigned presence_bit = 1;
198  u += presence_bit;
199  }
200 
201  return u;
202 }
203 
204 unsigned dccl::v4::DefaultMessageCodec::min_size()
205 {
206  if (is_optional())
207  {
208  const unsigned presence_bit = 1;
209  return presence_bit;
210  }
211  else
212  {
213  unsigned u = 0;
214  traverse_descriptor<MinSize>(&u);
215  return u;
216  }
217 }
218 
219 void dccl::v4::DefaultMessageCodec::validate()
220 {
221  bool b = false;
222  traverse_descriptor<Validate>(&b);
223 }
224 
225 std::string dccl::v4::DefaultMessageCodec::info()
226 {
227  std::stringstream ss;
228  traverse_descriptor<Info>(&ss);
229  return ss.str();
230 }
231 
232 std::size_t dccl::v4::DefaultMessageCodec::hash()
233 {
234  std::size_t hash = 0;
235  traverse_descriptor<Hash>(&hash);
236  return hash;
237 }
238 
239 bool dccl::v4::DefaultMessageCodec::check_field(const google::protobuf::FieldDescriptor* field)
240 {
241  if (!field)
242  {
243  return true;
244  }
245  else
246  {
247  dccl::DCCLFieldOptions dccl_field_options = field->options().GetExtension(dccl::field);
248  if (dccl_field_options.omit()) // omit
249  {
250  return false;
251  }
252  else if (message_data().current_part() == UNKNOWN) // part not yet explicitly specified
253  {
254  if ((part() == HEAD && !dccl_field_options.in_head()) ||
255  (part() == BODY && dccl_field_options.in_head()))
256  return false;
257  else
258  return true;
259  }
260  else if (message_data().current_part() != part()) // part specified and doesn't match
261  return false;
262  else
263  return true;
264  }
265 }
dccl::DCCLFieldOptions
Definition: option_extensions.pb.h:476
dccl::containing_oneof_index
int containing_oneof_index(const google::protobuf::FieldDescriptor *field_desc)
Returns the index of the containing oneof of the given field, or -1 if the field is not part of a one...
Definition: oneof.h:45
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:57
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:36