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