EmbeddedProto 4.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- EmbeddedProto/EmbeddedProto.py +122 -0
- EmbeddedProto/Features.py +255 -0
- EmbeddedProto/Field.py +1295 -0
- EmbeddedProto/Oneof.py +74 -0
- EmbeddedProto/ProtoFile.py +210 -0
- EmbeddedProto/TypeDefinitions.py +367 -0
- EmbeddedProto/__init__.py +0 -0
- EmbeddedProto/__main__.py +28 -0
- EmbeddedProto/config.py +173 -0
- EmbeddedProto/custom_header.py +513 -0
- EmbeddedProto/embedded_proto_options.proto +62 -0
- EmbeddedProto/embedded_proto_options_pb2.py +37 -0
- EmbeddedProto/field_options.py +344 -0
- EmbeddedProto/main.py +316 -0
- EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
- EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
- EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
- EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
- EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
- EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
- EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
- EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
- EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
- EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
- EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
- EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
- EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
- EmbeddedProto/src/EmbeddedProto.h +55 -0
- EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
- EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
- EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
- EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
- EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
- EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
- EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
- EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
- EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
- EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
- EmbeddedProto/templates/Header.h.jinja2 +108 -0
- EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
- EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
- EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
- EmbeddedProto/version.json +3 -0
- embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
- embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
- embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
- embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
- embeddedproto-4.0.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Embedded Proto.
|
|
5
|
+
*
|
|
6
|
+
* Embedded Proto is dual licensed. You may use it under the terms of the
|
|
7
|
+
* GNU General Public License version 3 (GPLv3) as published by the Free
|
|
8
|
+
* Software Foundation, or under a commercial license from Embedded AMS B.V.
|
|
9
|
+
*
|
|
10
|
+
* Under the GPLv3 you must release the source code of
|
|
11
|
+
* any product you distribute that includes Embedded Proto or code
|
|
12
|
+
* generated by it. A commercial license removes that obligation.
|
|
13
|
+
* See <https://embeddedproto.com/pricing/>.
|
|
14
|
+
*
|
|
15
|
+
* Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
|
|
16
|
+
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
17
|
+
* See the LICENSE file in the root of the repository for details.
|
|
18
|
+
*
|
|
19
|
+
* SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
|
|
20
|
+
*
|
|
21
|
+
* Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
#ifndef _MESSAGE_CALLBACK_H_
|
|
25
|
+
#define _MESSAGE_CALLBACK_H_
|
|
26
|
+
|
|
27
|
+
#include "MessageInterface.h"
|
|
28
|
+
#include "Functional.h"
|
|
29
|
+
#include "Errors.h"
|
|
30
|
+
#include "WireFormatter.h"
|
|
31
|
+
#include "MessageState.h"
|
|
32
|
+
|
|
33
|
+
#include <cstdint>
|
|
34
|
+
#include <type_traits>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
namespace EmbeddedProto
|
|
38
|
+
{
|
|
39
|
+
|
|
40
|
+
//! A repeated message field whose elements are streamed to/from the user, not stored.
|
|
41
|
+
/*!
|
|
42
|
+
MessageCallback is a drop-in storage type for a repeated message field that
|
|
43
|
+
owns no element array. Instead of holding N sub-messages it moves one across
|
|
44
|
+
the interface seam at a time:
|
|
45
|
+
|
|
46
|
+
- on serialize, each element is pulled from a user *source*, filled into a
|
|
47
|
+
single transient sub-message and emitted;
|
|
48
|
+
- on deserialize, every parsed element lands in the transient and is pushed
|
|
49
|
+
to a user *sink*.
|
|
50
|
+
|
|
51
|
+
Two element framings are offered, the generator picks one at generation time:
|
|
52
|
+
|
|
53
|
+
- DELIMITED groups (serialize_expanded), a START_GROUP / END_GROUP pair per
|
|
54
|
+
element, used for a plain repeated message field with
|
|
55
|
+
features.message_encoding = DELIMITED;
|
|
56
|
+
- length delimited blocks (serialize_expanded_len), tag + size + body per
|
|
57
|
+
element, used for a map field so its entries stay readable by a standard
|
|
58
|
+
protoc peer.
|
|
59
|
+
|
|
60
|
+
Both are single pass: the element sits in the transient when it is written, so
|
|
61
|
+
even the length prefix is measured without pulling it a second time. What a
|
|
62
|
+
stream can not provide is a size for the *whole* field ahead of its elements,
|
|
63
|
+
which is why serialize() refuses with CALLBACK_SEQUENCE when placed under a
|
|
64
|
+
length prefixed ancestor. This lets a device relay arbitrarily many
|
|
65
|
+
sub-messages through a fixed, tiny RAM footprint: the two Functional handles
|
|
66
|
+
plus one transient sub-message (never the whole collection). It plugs in
|
|
67
|
+
through the callbackStorage generator option.
|
|
68
|
+
|
|
69
|
+
Only repeated message fields are supported here; a singular message callback
|
|
70
|
+
is a separate, later capability.
|
|
71
|
+
|
|
72
|
+
\tparam MSG_TYPE The generated message type streamed through this field (a
|
|
73
|
+
MessageInterface).
|
|
74
|
+
*/
|
|
75
|
+
template<class MSG_TYPE>
|
|
76
|
+
class MessageCallback : public MessageInterface
|
|
77
|
+
{
|
|
78
|
+
static_assert(std::is_base_of<::EmbeddedProto::MessageInterface, MSG_TYPE>::value,
|
|
79
|
+
"MessageCallback streams a generated message type (a MessageInterface).");
|
|
80
|
+
|
|
81
|
+
public:
|
|
82
|
+
|
|
83
|
+
//! Pull callback used during serialize: fill \p element, return true when a value was produced, false to signal the end of the stream.
|
|
84
|
+
using SourceCallback = Functional<bool(MSG_TYPE&)>;
|
|
85
|
+
|
|
86
|
+
//! Push callback used during deserialize: consume one parsed \p element, return an Error (NO_ERRORS to continue).
|
|
87
|
+
using SinkCallback = Functional<Error(const MSG_TYPE&)>;
|
|
88
|
+
|
|
89
|
+
MessageCallback() = default;
|
|
90
|
+
~MessageCallback() = default;
|
|
91
|
+
|
|
92
|
+
// --- Binding (reference-taking, no ownership) --------------------------
|
|
93
|
+
|
|
94
|
+
//! Bind the source used to pull elements during serialization.
|
|
95
|
+
void set_source(const SourceCallback& source) { source_ = source; }
|
|
96
|
+
|
|
97
|
+
//! Bind the sink used to push elements during deserialization.
|
|
98
|
+
void set_sink(const SinkCallback& sink) { sink_ = sink; }
|
|
99
|
+
|
|
100
|
+
//! Remove the bound source.
|
|
101
|
+
void clear_source() { source_.clear(); }
|
|
102
|
+
|
|
103
|
+
//! Remove the bound sink.
|
|
104
|
+
void clear_sink() { sink_.clear(); }
|
|
105
|
+
|
|
106
|
+
//! Check whether a source (pull) callback is bound.
|
|
107
|
+
bool is_source_set() const { return source_.is_set(); }
|
|
108
|
+
|
|
109
|
+
//! Check whether a sink (push) callback is bound.
|
|
110
|
+
bool is_sink_set() const { return sink_.is_set(); }
|
|
111
|
+
|
|
112
|
+
//! Require a binding for the direction being used.
|
|
113
|
+
/*!
|
|
114
|
+
When strict, streaming without the relevant callback bound returns
|
|
115
|
+
CALLBACK_NOT_SET instead of silently discarding (deserialize) or
|
|
116
|
+
emitting nothing (serialize).
|
|
117
|
+
*/
|
|
118
|
+
void set_strict(bool strict) { strict_ = strict; }
|
|
119
|
+
|
|
120
|
+
//! Whether strict (require-binding) mode is enabled.
|
|
121
|
+
bool is_strict() const { return strict_; }
|
|
122
|
+
|
|
123
|
+
//! Copy the bindings from another callback field (used by the generated message copy/assignment).
|
|
124
|
+
/*!
|
|
125
|
+
A callback field owns no elements to copy, so copying it copies the source
|
|
126
|
+
and sink handles: the copy streams through the same user callbacks.
|
|
127
|
+
|
|
128
|
+
\return Always NO_ERRORS, for signature compatibility with a resident field's set().
|
|
129
|
+
*/
|
|
130
|
+
Error set(const MessageCallback<MSG_TYPE>& rhs)
|
|
131
|
+
{
|
|
132
|
+
source_ = rhs.source_;
|
|
133
|
+
sink_ = rhs.sink_;
|
|
134
|
+
strict_ = rhs.strict_;
|
|
135
|
+
return Error::NO_ERRORS;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// --- Repeated-field-shaped interface -----------------------------------
|
|
139
|
+
|
|
140
|
+
//! Number of elements streamed so far (a running counter, not a capacity). Presence is get_length() > 0.
|
|
141
|
+
uint32_t get_length() const { return length_; }
|
|
142
|
+
|
|
143
|
+
//! Required of a callbackStorage message type.
|
|
144
|
+
/*!
|
|
145
|
+
A stream has no finite serialized size, so report the unbounded sentinel; a
|
|
146
|
+
message with a callback field therefore cannot be statically buffer-sized,
|
|
147
|
+
which is inherent to streaming.
|
|
148
|
+
*/
|
|
149
|
+
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
|
|
150
|
+
{
|
|
151
|
+
static_cast<void>(field_number);
|
|
152
|
+
return UINT32_MAX;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- MessageInterface / Field interface --------------------------------
|
|
156
|
+
|
|
157
|
+
//! Guard the LEN / size-pass serialization of a streaming field.
|
|
158
|
+
/*!
|
|
159
|
+
A callback message owns no element to write here and is emitted EXPANDED
|
|
160
|
+
as groups only (serialize_expanded). This method is what a size pass
|
|
161
|
+
reaches via Field::serialized_size(), and what serialize_as_group would
|
|
162
|
+
call. Any real call means the field was placed where a length-delimited
|
|
163
|
+
(LEN) framing is required (e.g. under a LEN ancestor), which is
|
|
164
|
+
unsupported for streaming storage: report CALLBACK_SEQUENCE instead of
|
|
165
|
+
draining the source.
|
|
166
|
+
*/
|
|
167
|
+
Error serialize(WriteBufferInterface& buffer) const override
|
|
168
|
+
{
|
|
169
|
+
static_cast<void>(buffer);
|
|
170
|
+
return Error::CALLBACK_SEQUENCE;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
//! Pull elements from the bound source and emit each as a DELIMITED group.
|
|
174
|
+
/*!
|
|
175
|
+
One START_GROUP / body / END_GROUP is written per element (the same wire
|
|
176
|
+
form as an EXPANDED repeated message field), so no field-level size prefix
|
|
177
|
+
is needed and the source is drained exactly once in a single pass.
|
|
178
|
+
Production ends when the source returns false. With no source bound
|
|
179
|
+
nothing is emitted (NO_ERRORS), unless strict mode is on, in which case
|
|
180
|
+
CALLBACK_NOT_SET is returned.
|
|
181
|
+
|
|
182
|
+
\param field_number The field number written in each element's group tags.
|
|
183
|
+
\param buffer The destination buffer.
|
|
184
|
+
*/
|
|
185
|
+
Error serialize_expanded(uint32_t field_number, WriteBufferInterface& buffer) const
|
|
186
|
+
{
|
|
187
|
+
Error return_value = Error::NO_ERRORS;
|
|
188
|
+
if(!source_.is_set())
|
|
189
|
+
{
|
|
190
|
+
if(strict_)
|
|
191
|
+
{
|
|
192
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
193
|
+
}
|
|
194
|
+
// Not strict: no source bound, emit nothing.
|
|
195
|
+
}
|
|
196
|
+
else
|
|
197
|
+
{
|
|
198
|
+
bool more = true;
|
|
199
|
+
while(more && (Error::NO_ERRORS == return_value))
|
|
200
|
+
{
|
|
201
|
+
bool produced = false;
|
|
202
|
+
transient_.clear();
|
|
203
|
+
static_cast<void>(source_.invoke(produced, transient_));
|
|
204
|
+
if(produced)
|
|
205
|
+
{
|
|
206
|
+
return_value = transient_.serialize_as_group(field_number, buffer);
|
|
207
|
+
if(Error::NO_ERRORS == return_value)
|
|
208
|
+
{
|
|
209
|
+
++length_;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else
|
|
213
|
+
{
|
|
214
|
+
more = false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return return_value;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//! Pull elements from the bound source and emit each as a length delimited block.
|
|
222
|
+
/*!
|
|
223
|
+
The wire form a map field needs: one tag + size + body per element, identical to what a
|
|
224
|
+
resident repeated message field writes, so the bytes stay readable by a standard protoc
|
|
225
|
+
peer.
|
|
226
|
+
|
|
227
|
+
A per element length prefix costs no second pull. The element is sitting in the transient
|
|
228
|
+
when its size is measured, so serialized_size() walks the transient and not the user
|
|
229
|
+
callback: the source is still drained exactly once. This is unlike a length prefix around
|
|
230
|
+
the *whole* field, which would have to know the total before any element was pulled; that
|
|
231
|
+
case is still refused by serialize() above.
|
|
232
|
+
|
|
233
|
+
\param field_number The field number written in each element's tag.
|
|
234
|
+
\param buffer The destination buffer.
|
|
235
|
+
*/
|
|
236
|
+
Error serialize_expanded_len(uint32_t field_number, WriteBufferInterface& buffer) const
|
|
237
|
+
{
|
|
238
|
+
Error return_value = Error::NO_ERRORS;
|
|
239
|
+
if(!source_.is_set())
|
|
240
|
+
{
|
|
241
|
+
if(strict_)
|
|
242
|
+
{
|
|
243
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
244
|
+
}
|
|
245
|
+
// Not strict: no source bound, emit nothing.
|
|
246
|
+
}
|
|
247
|
+
else
|
|
248
|
+
{
|
|
249
|
+
bool more = true;
|
|
250
|
+
while(more && (Error::NO_ERRORS == return_value))
|
|
251
|
+
{
|
|
252
|
+
bool produced = false;
|
|
253
|
+
transient_.clear();
|
|
254
|
+
static_cast<void>(source_.invoke(produced, transient_));
|
|
255
|
+
if(produced)
|
|
256
|
+
{
|
|
257
|
+
return_value = transient_.serialize_len(field_number, transient_.serialized_size(),
|
|
258
|
+
buffer, true);
|
|
259
|
+
if(Error::NO_ERRORS == return_value)
|
|
260
|
+
{
|
|
261
|
+
++length_;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else
|
|
265
|
+
{
|
|
266
|
+
more = false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return return_value;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
//! Deserialize one group element and push it to the sink.
|
|
274
|
+
/*!
|
|
275
|
+
The opening START_GROUP tag has been consumed by the caller. The element's
|
|
276
|
+
body is read into the transient (which stops at its matching END_GROUP)
|
|
277
|
+
and then pushed to the bound sink (drain-and-discard when unbound and not
|
|
278
|
+
strict). Called once per element.
|
|
279
|
+
*/
|
|
280
|
+
Error deserialize(ReadBufferInterface& buffer) override
|
|
281
|
+
{
|
|
282
|
+
transient_.clear();
|
|
283
|
+
Error return_value = transient_.deserialize(buffer);
|
|
284
|
+
if(Error::NO_ERRORS == return_value)
|
|
285
|
+
{
|
|
286
|
+
return_value = push_to_sink(transient_);
|
|
287
|
+
if(Error::NO_ERRORS == return_value)
|
|
288
|
+
{
|
|
289
|
+
++length_;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return return_value;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#ifdef PARTIAL_SERIALIZATION_ENABLED
|
|
296
|
+
//! Guard the whole-message partial serialize path.
|
|
297
|
+
/*!
|
|
298
|
+
A callback message is emitted EXPANDED as groups only (via
|
|
299
|
+
serialize_partial_as_field_expanded, which frames each element and streams
|
|
300
|
+
its body through the transient's own serialize_partial). This entry point
|
|
301
|
+
would LEN/size-pass the field, which a stream cannot provide: report
|
|
302
|
+
CALLBACK_SEQUENCE.
|
|
303
|
+
*/
|
|
304
|
+
Error serialize_partial(WriteBufferInterface& buffer, MessageState& state) const override
|
|
305
|
+
{
|
|
306
|
+
static_cast<void>(buffer);
|
|
307
|
+
static_cast<void>(state);
|
|
308
|
+
return Error::CALLBACK_SEQUENCE;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
//! Resumable EXPANDED pull-serialize for the partial engine.
|
|
312
|
+
/*!
|
|
313
|
+
Emits one START_GROUP / body / END_GROUP per element, pulling from the
|
|
314
|
+
source, and survives a write buffer that fills mid-element. The group
|
|
315
|
+
framing is inlined at this field's state level (state.phase drives
|
|
316
|
+
TAG -> DATA -> SIZE(reused for END_GROUP) -> next element; state.child
|
|
317
|
+
streams the element body), so a callback message field consumes no more
|
|
318
|
+
state-stack depth than a resident one. An element is pulled only when a
|
|
319
|
+
new group is started (guarded by element_loaded_), so a BUFFER_FULL resume
|
|
320
|
+
never double-pulls the source.
|
|
321
|
+
*/
|
|
322
|
+
Error serialize_partial_as_field_expanded(uint32_t field_number,
|
|
323
|
+
WriteBufferInterface& buffer,
|
|
324
|
+
MessageState& state,
|
|
325
|
+
bool optional) const
|
|
326
|
+
{
|
|
327
|
+
static_cast<void>(optional);
|
|
328
|
+
Error return_value = Error::NO_ERRORS;
|
|
329
|
+
bool more = true;
|
|
330
|
+
while(more && (Error::NO_ERRORS == return_value))
|
|
331
|
+
{
|
|
332
|
+
if(::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.phase)
|
|
333
|
+
{
|
|
334
|
+
// The whole stream has been drained.
|
|
335
|
+
more = false;
|
|
336
|
+
}
|
|
337
|
+
else if(::EmbeddedProto::FieldProcessingPhase::TAG == state.phase)
|
|
338
|
+
{
|
|
339
|
+
return_value = serialize_partial_begin_group(field_number, buffer, state);
|
|
340
|
+
}
|
|
341
|
+
else if(::EmbeddedProto::FieldProcessingPhase::DATA == state.phase)
|
|
342
|
+
{
|
|
343
|
+
if(nullptr != state.child)
|
|
344
|
+
{
|
|
345
|
+
return_value = transient_.serialize_partial(buffer, *state.child);
|
|
346
|
+
if((Error::NO_ERRORS == return_value)
|
|
347
|
+
&& (::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.child->phase))
|
|
348
|
+
{
|
|
349
|
+
// Body complete; the END_GROUP tag still has to be written. Reuse
|
|
350
|
+
// the (otherwise unused) SIZE phase to mark "END_GROUP pending".
|
|
351
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::SIZE;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else
|
|
355
|
+
{
|
|
356
|
+
return_value = Error::NESTING_TOO_DEEP;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else
|
|
360
|
+
{
|
|
361
|
+
// Phase SIZE (reused): write the END_GROUP tag, then advance to the
|
|
362
|
+
// next element.
|
|
363
|
+
const uint32_t tag = WireFormatter::MakeTag(field_number, WireFormatter::WireType::END_GROUP);
|
|
364
|
+
if(buffer.get_available_size() >= WireFormatter::VarintSize(tag))
|
|
365
|
+
{
|
|
366
|
+
return_value = WireFormatter::SerializeVarint(tag, buffer);
|
|
367
|
+
if(Error::NO_ERRORS == return_value)
|
|
368
|
+
{
|
|
369
|
+
++length_;
|
|
370
|
+
element_loaded_ = false;
|
|
371
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::TAG;
|
|
372
|
+
if(nullptr != state.child)
|
|
373
|
+
{
|
|
374
|
+
state.child->reset();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else
|
|
379
|
+
{
|
|
380
|
+
return_value = Error::BUFFER_FULL;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return return_value;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
//! Resumable, length delimited variant of serialize_partial_as_field_expanded.
|
|
388
|
+
/*!
|
|
389
|
+
The partial counterpart of serialize_expanded_len: one tag + size + body per element,
|
|
390
|
+
resuming where a full buffer stopped. The element stays resident in the transient across a
|
|
391
|
+
resume, so the source is pulled exactly once per element here as well.
|
|
392
|
+
|
|
393
|
+
\param field_number The field number written in each element's tag.
|
|
394
|
+
\param buffer The destination buffer.
|
|
395
|
+
\param state External state tracking progress through the stream.
|
|
396
|
+
\param optional Unused, an entry is always emitted, also when it is empty.
|
|
397
|
+
*/
|
|
398
|
+
Error serialize_partial_as_field_expanded_len(uint32_t field_number,
|
|
399
|
+
WriteBufferInterface& buffer,
|
|
400
|
+
MessageState& state,
|
|
401
|
+
bool optional) const
|
|
402
|
+
{
|
|
403
|
+
static_cast<void>(optional);
|
|
404
|
+
Error return_value = Error::NO_ERRORS;
|
|
405
|
+
bool more = true;
|
|
406
|
+
while(more && (Error::NO_ERRORS == return_value))
|
|
407
|
+
{
|
|
408
|
+
if(::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.phase)
|
|
409
|
+
{
|
|
410
|
+
// The whole stream has been drained.
|
|
411
|
+
more = false;
|
|
412
|
+
}
|
|
413
|
+
else if(::EmbeddedProto::FieldProcessingPhase::DATA == state.phase)
|
|
414
|
+
{
|
|
415
|
+
if(nullptr != state.child)
|
|
416
|
+
{
|
|
417
|
+
return_value = transient_.serialize_partial(buffer, *state.child);
|
|
418
|
+
if((Error::NO_ERRORS == return_value)
|
|
419
|
+
&& (::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.child->phase))
|
|
420
|
+
{
|
|
421
|
+
finish_len_element(state);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else
|
|
425
|
+
{
|
|
426
|
+
return_value = Error::NESTING_TOO_DEEP;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else
|
|
430
|
+
{
|
|
431
|
+
// Phase TAG or SIZE: pull the next element if needed and write its tag and size.
|
|
432
|
+
return_value = serialize_partial_begin_len(field_number, buffer, state);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return return_value;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
//! Resumable push-deserialize of one group element for the partial engine.
|
|
439
|
+
/*!
|
|
440
|
+
Invoked by the inherited deserialize_partial_as_group, which consumes the
|
|
441
|
+
START_GROUP tag and streams the element body here. The body is read into
|
|
442
|
+
the transient; when it consumes its matching END_GROUP the transient's
|
|
443
|
+
state reports COMPLETE, at which point the element is pushed to the sink
|
|
444
|
+
and the transient is reset for the next element. Returns END_OF_BUFFER
|
|
445
|
+
(leaving the state mid-body) when the read buffer drains, so the next call
|
|
446
|
+
resumes where it stopped.
|
|
447
|
+
*/
|
|
448
|
+
Error deserialize_partial(ReadBufferInterface& buffer, MessageState& state) override
|
|
449
|
+
{
|
|
450
|
+
Error return_value = transient_.deserialize_partial(buffer, state);
|
|
451
|
+
if((Error::NO_ERRORS == return_value)
|
|
452
|
+
&& (::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.phase))
|
|
453
|
+
{
|
|
454
|
+
return_value = push_to_sink(transient_);
|
|
455
|
+
if(Error::NO_ERRORS == return_value)
|
|
456
|
+
{
|
|
457
|
+
++length_;
|
|
458
|
+
}
|
|
459
|
+
transient_.clear();
|
|
460
|
+
}
|
|
461
|
+
return return_value;
|
|
462
|
+
}
|
|
463
|
+
//! Resumable push-deserialize of one length delimited element for the partial engine.
|
|
464
|
+
/*!
|
|
465
|
+
The length delimited counterpart of the group path used by deserialize_partial. The base
|
|
466
|
+
class streams the element body into the transient and marks this *field* COMPLETE once the
|
|
467
|
+
declared number of bytes has been consumed. That completion lands on the field state and
|
|
468
|
+
not on the transient's own state, which is why the element is pushed to the sink from here
|
|
469
|
+
instead of from deserialize_partial, the way the group path does it.
|
|
470
|
+
*/
|
|
471
|
+
Error deserialize_partial_as_field(ReadBufferInterface& buffer, MessageState& state) override
|
|
472
|
+
{
|
|
473
|
+
Error return_value = MessageInterface::deserialize_partial_as_field(buffer, state);
|
|
474
|
+
if((Error::NO_ERRORS == return_value)
|
|
475
|
+
&& (::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.phase))
|
|
476
|
+
{
|
|
477
|
+
return_value = push_to_sink(transient_);
|
|
478
|
+
if(Error::NO_ERRORS == return_value)
|
|
479
|
+
{
|
|
480
|
+
++length_;
|
|
481
|
+
}
|
|
482
|
+
transient_.clear();
|
|
483
|
+
}
|
|
484
|
+
return return_value;
|
|
485
|
+
}
|
|
486
|
+
#endif // PARTIAL_SERIALIZATION_ENABLED
|
|
487
|
+
|
|
488
|
+
//! Reset the streaming state (the element counter). Bindings are kept.
|
|
489
|
+
void clear() override { length_ = 0U; }
|
|
490
|
+
|
|
491
|
+
#ifdef MSG_TO_STRING
|
|
492
|
+
//! A callback field holds no resident value to print, so leave the string unchanged.
|
|
493
|
+
::EmbeddedProto::string_view to_string(::EmbeddedProto::string_view& str, const uint32_t indent_level, char const* name, const bool first_field) const override
|
|
494
|
+
{
|
|
495
|
+
static_cast<void>(indent_level);
|
|
496
|
+
static_cast<void>(name);
|
|
497
|
+
static_cast<void>(first_field);
|
|
498
|
+
return str;
|
|
499
|
+
}
|
|
500
|
+
#endif // MSG_TO_STRING
|
|
501
|
+
|
|
502
|
+
private:
|
|
503
|
+
|
|
504
|
+
//! Push one parsed element to the sink, or drain-and-discard when no sink is bound (CALLBACK_NOT_SET in strict mode).
|
|
505
|
+
Error push_to_sink(const MSG_TYPE& element)
|
|
506
|
+
{
|
|
507
|
+
Error return_value = Error::NO_ERRORS;
|
|
508
|
+
if(sink_.is_set())
|
|
509
|
+
{
|
|
510
|
+
Error sink_result = Error::NO_ERRORS;
|
|
511
|
+
static_cast<void>(sink_.invoke(sink_result, element));
|
|
512
|
+
return_value = sink_result;
|
|
513
|
+
}
|
|
514
|
+
else if(strict_)
|
|
515
|
+
{
|
|
516
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
517
|
+
}
|
|
518
|
+
else
|
|
519
|
+
{
|
|
520
|
+
// No sink bound and not strict: drain and discard.
|
|
521
|
+
static_cast<void>(element);
|
|
522
|
+
}
|
|
523
|
+
return return_value;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
#ifdef PARTIAL_SERIALIZATION_ENABLED
|
|
527
|
+
//! Pull the next element (once, guarded by element_loaded_) and write its tag and size.
|
|
528
|
+
/*!
|
|
529
|
+
The length delimited counterpart of serialize_partial_begin_group. The size is measured on
|
|
530
|
+
the transient after the pull, so no element is ever produced twice. When the source is
|
|
531
|
+
exhausted (or unbound and not strict) the field is marked COMPLETE.
|
|
532
|
+
|
|
533
|
+
An entry that serializes to zero bytes is finished here: serialize_partial_tag_and_size
|
|
534
|
+
marks a zero sized field COMPLETE because it has no body to write, which for a stream
|
|
535
|
+
would otherwise read as "the whole field is done" instead of "this element is done".
|
|
536
|
+
*/
|
|
537
|
+
Error serialize_partial_begin_len(uint32_t field_number,
|
|
538
|
+
WriteBufferInterface& buffer,
|
|
539
|
+
MessageState& state) const
|
|
540
|
+
{
|
|
541
|
+
Error return_value = Error::NO_ERRORS;
|
|
542
|
+
if(!element_loaded_)
|
|
543
|
+
{
|
|
544
|
+
if(!source_.is_set())
|
|
545
|
+
{
|
|
546
|
+
if(strict_)
|
|
547
|
+
{
|
|
548
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
549
|
+
}
|
|
550
|
+
else
|
|
551
|
+
{
|
|
552
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else
|
|
556
|
+
{
|
|
557
|
+
bool produced = false;
|
|
558
|
+
transient_.clear();
|
|
559
|
+
static_cast<void>(source_.invoke(produced, transient_));
|
|
560
|
+
if(produced)
|
|
561
|
+
{
|
|
562
|
+
element_loaded_ = true;
|
|
563
|
+
// Measured once, here, and kept in the state so a resume after a full buffer does
|
|
564
|
+
// not walk the transient again.
|
|
565
|
+
state.size_value = transient_.serialized_size();
|
|
566
|
+
if(nullptr != state.child)
|
|
567
|
+
{
|
|
568
|
+
state.child->reset();
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
else
|
|
572
|
+
{
|
|
573
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if((Error::NO_ERRORS == return_value) && element_loaded_)
|
|
579
|
+
{
|
|
580
|
+
return_value = serialize_partial_tag_and_size(field_number, state.size_value, buffer,
|
|
581
|
+
state, true);
|
|
582
|
+
if((Error::NO_ERRORS == return_value)
|
|
583
|
+
&& (::EmbeddedProto::FieldProcessingPhase::COMPLETE == state.phase))
|
|
584
|
+
{
|
|
585
|
+
finish_len_element(state);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return return_value;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
//! Close off one length delimited element and arm the state for the next one.
|
|
592
|
+
void finish_len_element(MessageState& state) const
|
|
593
|
+
{
|
|
594
|
+
++length_;
|
|
595
|
+
element_loaded_ = false;
|
|
596
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::TAG;
|
|
597
|
+
if(nullptr != state.child)
|
|
598
|
+
{
|
|
599
|
+
state.child->reset();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
//! Pull the next element (once, guarded by element_loaded_) and write its START_GROUP tag.
|
|
604
|
+
/*!
|
|
605
|
+
When the source is exhausted (or unbound and not strict) the field is
|
|
606
|
+
marked COMPLETE. Otherwise the pulled element stays resident in the
|
|
607
|
+
transient across a BUFFER_FULL resume so it is never pulled twice.
|
|
608
|
+
*/
|
|
609
|
+
Error serialize_partial_begin_group(uint32_t field_number,
|
|
610
|
+
WriteBufferInterface& buffer,
|
|
611
|
+
MessageState& state) const
|
|
612
|
+
{
|
|
613
|
+
Error return_value = Error::NO_ERRORS;
|
|
614
|
+
if(!element_loaded_)
|
|
615
|
+
{
|
|
616
|
+
if(!source_.is_set())
|
|
617
|
+
{
|
|
618
|
+
if(strict_)
|
|
619
|
+
{
|
|
620
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
621
|
+
}
|
|
622
|
+
else
|
|
623
|
+
{
|
|
624
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else
|
|
628
|
+
{
|
|
629
|
+
bool produced = false;
|
|
630
|
+
transient_.clear();
|
|
631
|
+
static_cast<void>(source_.invoke(produced, transient_));
|
|
632
|
+
if(produced)
|
|
633
|
+
{
|
|
634
|
+
element_loaded_ = true;
|
|
635
|
+
if(nullptr != state.child)
|
|
636
|
+
{
|
|
637
|
+
state.child->reset();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
else
|
|
641
|
+
{
|
|
642
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if((Error::NO_ERRORS == return_value) && element_loaded_)
|
|
648
|
+
{
|
|
649
|
+
const uint32_t tag = WireFormatter::MakeTag(field_number, WireFormatter::WireType::START_GROUP);
|
|
650
|
+
if(buffer.get_available_size() >= WireFormatter::VarintSize(tag))
|
|
651
|
+
{
|
|
652
|
+
return_value = WireFormatter::SerializeVarint(tag, buffer);
|
|
653
|
+
if(Error::NO_ERRORS == return_value)
|
|
654
|
+
{
|
|
655
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::DATA;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else
|
|
659
|
+
{
|
|
660
|
+
return_value = Error::BUFFER_FULL;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return return_value;
|
|
664
|
+
}
|
|
665
|
+
#endif // PARTIAL_SERIALIZATION_ENABLED
|
|
666
|
+
|
|
667
|
+
//! Running count of elements streamed through this field. Mutable because serialize is const yet advances the counter as elements are pulled.
|
|
668
|
+
mutable uint32_t length_ = 0U;
|
|
669
|
+
|
|
670
|
+
//! Set while a pulled element awaits its group being written, so a BUFFER_FULL resume does not pull it again.
|
|
671
|
+
mutable bool element_loaded_ = false;
|
|
672
|
+
|
|
673
|
+
//! When true, streaming without a bound callback is an error rather than a silent drain/no-op.
|
|
674
|
+
bool strict_ = false;
|
|
675
|
+
|
|
676
|
+
//! Single sub-message landing slot required to assemble/parse one element. It is not stream storage: the collection owns no per-element buffer.
|
|
677
|
+
mutable MSG_TYPE transient_ = {};
|
|
678
|
+
|
|
679
|
+
//! Pull callback invoked to produce elements while serializing.
|
|
680
|
+
SourceCallback source_{};
|
|
681
|
+
|
|
682
|
+
//! Push callback invoked to consume elements while deserializing.
|
|
683
|
+
SinkCallback sink_{};
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
} // End of namespace EmbeddedProto
|
|
687
|
+
|
|
688
|
+
#endif // End of _MESSAGE_CALLBACK_H_
|