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