DCCL v4
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#include "../oneof.h"
27
28std::unordered_map<std::string, unsigned> dccl::v4::DefaultMessageCodec::MaxSize::oneofs_max_size;
29
30//
31// DefaultMessageCodec
32//
33
34void 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
49unsigned 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
68void 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
190unsigned 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
204unsigned 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
219void dccl::v4::DefaultMessageCodec::validate()
220{
221 bool b = false;
222 traverse_descriptor<Validate>(&b);
223}
224
225std::string dccl::v4::DefaultMessageCodec::info()
226{
227 std::stringstream ss;
228 traverse_descriptor<Info>(&ss);
229 return ss.str();
230}
231
232std::size_t dccl::v4::DefaultMessageCodec::hash()
233{
234 std::size_t hash = 0;
235 traverse_descriptor<Hash>(&hash);
236 return hash;
237}
238
239bool 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}
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
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
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