DCCL v5
Loading...
Searching...
No Matches
field_codec_message_stack.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_message_stack.h"
25#include "../field_codec.h"
26
27// has_head_field
28//
29
30bool dccl::internal::has_head_field(const google::protobuf::Descriptor* desc)
31{
32 for (int i = 0, n = desc->field_count(); i < n; ++i)
33 {
34 const auto* field = desc->field(i);
35 const auto field_opts = field->options().GetExtension(dccl::field);
36 if (field_opts.in_head())
37 return true;
38 if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
39 !field_opts.has_in_head())
40 {
41 if (has_head_field(field->message_type()))
42 return true;
43 }
44 }
45 return false;
46}
47
48// MessageStack
49//
50
51void dccl::internal::MessageStack::push(const google::protobuf::Descriptor* desc)
52
53{
54 data_.desc.push_back(desc);
55 ++descriptors_pushed_;
56}
57
58void dccl::internal::MessageStack::push(const google::protobuf::FieldDescriptor* field)
59{
60 data_.field.push_back(field);
61 ++fields_pushed_;
62}
63
64void dccl::internal::MessageStack::push(MessagePart part)
65{
66 data_.parts.push_back(part);
67 ++parts_pushed_;
68}
69
70void dccl::internal::MessageStack::__pop_desc()
71{
72 if (!data_.desc.empty())
73 data_.desc.pop_back();
74 --descriptors_pushed_;
75}
76
77void dccl::internal::MessageStack::__pop_field()
78{
79 if (!data_.field.empty())
80 data_.field.pop_back();
81 --fields_pushed_;
82}
83
84void dccl::internal::MessageStack::__pop_parts()
85{
86 if (!data_.parts.empty())
87 data_.parts.pop_back();
88 --parts_pushed_;
89}
90
91void dccl::internal::MessageStack::__pop_messages()
92{
93 if (!data_.messages.empty())
94 data_.messages.pop_back();
95 --messages_pushed_;
96}
97
98dccl::internal::MessageStack::MessageStack(const google::protobuf::Message* root_message,
99 MessageStackData& data,
100 const google::protobuf::FieldDescriptor* field)
101 : data_(data), descriptors_pushed_(0), fields_pushed_(0), parts_pushed_(0), messages_pushed_(0)
102{
103 if (field)
104 {
105 if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
106 {
107 MessagePart part = UNKNOWN;
108 if (field->options().GetExtension(dccl::field).has_in_head())
109 {
110 // if explicitly set, set part (HEAD or BODY) of message for all children of this message
111 part = field->options().GetExtension(dccl::field).in_head() ? HEAD : BODY;
112 }
113 else
114 {
115 // use the parent's current part
116 part = current_part();
117 }
118 push(part);
119 push(field->message_type());
120 }
121 push_message(root_message, field);
122 push(field);
123 }
124}
125
126void dccl::internal::MessageStack::update_index(const google::protobuf::Message* root_message,
127 const google::protobuf::FieldDescriptor* field,
128 int index)
129{
130 push_message(root_message, field, index);
131}
132
133void dccl::internal::MessageStack::push_message(const google::protobuf::Message* root_message,
134 const google::protobuf::FieldDescriptor* field,
135 int index)
136{
137 if (data_.messages.empty() && root_message)
138 {
139 data_.messages.push_back({root_message, nullptr});
140 ++messages_pushed_;
141 }
142
143 if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
144 {
145 // replace if the previous push was the same field
146 if (!data_.messages.empty() && field == data_.messages.back().field &&
147 !data_.messages.empty())
148 {
149 data_.messages.pop_back();
150 --messages_pushed_;
151 }
152
153 // add the new message + field if possible
154 if (data_.messages.size() &&
155 data_.messages.back().msg->GetDescriptor() == field->containing_type())
156 {
157 const auto* refl = data_.messages.back().msg->GetReflection();
158 if (field->is_repeated())
159 {
160 if (index >= 0 && index < refl->FieldSize(*data_.messages.back().msg, field))
161 {
162 data_.messages.push_back(
163 {&refl->GetRepeatedMessage(*data_.messages.back().msg, field, index),
164 field});
165 ++messages_pushed_;
166 }
167 }
168 else
169 {
170 data_.messages.push_back(
171 {&refl->GetMessage(*data_.messages.back().msg, field), field});
172 ++messages_pushed_;
173 }
174 }
175 }
176}
177
178dccl::internal::MessageStack::~MessageStack()
179{
180 while (fields_pushed_ > 0) __pop_field();
181 while (descriptors_pushed_ > 0) __pop_desc();
182 while (parts_pushed_ > 0) __pop_parts();
183 while (messages_pushed_ > 0) __pop_messages();
184}
bool has_head_field(const google::protobuf::Descriptor *desc)
Recursively checks if a message descriptor has any fields with in_head = true.