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