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
27//
28// DefaultMessageCodec
29//
30
31void 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
46unsigned 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
65void 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
166unsigned 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
180unsigned 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
195void 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
207std::string dccl::v3::DefaultMessageCodec::info()
208{
209 std::stringstream ss;
210 traverse_descriptor<Info>(&ss);
211 return ss.str();
212}
213
214std::size_t dccl::v3::DefaultMessageCodec::hash()
215{
216 std::size_t hash = 0;
217 traverse_descriptor<Hash>(&hash);
218 return hash;
219}
220
221bool 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}
const google::protobuf::Descriptor * this_descriptor() const
Returns the Descriptor (message schema meta-data) for the immediate parent Message.