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