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,476 @@
|
|
|
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 _BYTES_STRING_CALLBACK_H_
|
|
25
|
+
#define _BYTES_STRING_CALLBACK_H_
|
|
26
|
+
|
|
27
|
+
#include "FieldStringBytes.h"
|
|
28
|
+
#include "Functional.h"
|
|
29
|
+
#include "Errors.h"
|
|
30
|
+
#include "Defines.h"
|
|
31
|
+
#include "WireFormatter.h"
|
|
32
|
+
|
|
33
|
+
#include <cstdint>
|
|
34
|
+
#include <type_traits>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
namespace EmbeddedProto
|
|
38
|
+
{
|
|
39
|
+
|
|
40
|
+
//! A bytes/string field whose payload is streamed to/from the user in windows, not stored.
|
|
41
|
+
/*!
|
|
42
|
+
BytesStringCallback is the storage type of a singular bytes or string field
|
|
43
|
+
with the callbackStorage option. It keeps no value resident: the value moves
|
|
44
|
+
across the interface seam a window at a time, so a large blob (a firmware
|
|
45
|
+
image, a log, a picture) streams through the message in slices that never
|
|
46
|
+
add up to a whole value:
|
|
47
|
+
|
|
48
|
+
- on deserialize, every freshly parsed window is offered to the user
|
|
49
|
+
deserialize chunk callback, which returns how many elements it accepted;
|
|
50
|
+
- on serialize, a writable window is handed to the user serialize chunk
|
|
51
|
+
callback, which fills it and returns how many elements it produced.
|
|
52
|
+
|
|
53
|
+
No offset is passed. A stateful callback tracks its own position.
|
|
54
|
+
|
|
55
|
+
Serialize needs the total length up front for the LEN prefix, so the user
|
|
56
|
+
declares it once with set_length() before the first window is pulled. The
|
|
57
|
+
prefix is then written from that length and serialized_size() reports it
|
|
58
|
+
without pulling any data. Deserialize needs no such declaration, the wire
|
|
59
|
+
carries the length.
|
|
60
|
+
|
|
61
|
+
The buffer interfaces hand out no contiguous memory, so every window is
|
|
62
|
+
staged in memory the user binds with set_window(): a view over an array the
|
|
63
|
+
platform owns, a static array, a DMA buffer or a scratch region shared
|
|
64
|
+
between fields. Its size is the largest window a callback is offered. The
|
|
65
|
+
field itself holds only the view, not the window, so the message size does
|
|
66
|
+
not depend on the platform. One window serves both directions, a field never
|
|
67
|
+
serializes and deserializes at the same time. A window of one element gives
|
|
68
|
+
byte-by-byte streaming when that is all the user wants. The class derives
|
|
69
|
+
from internal::BaseStringBytes and satisfies the same static_assert the
|
|
70
|
+
customStorage mechanism checks.
|
|
71
|
+
|
|
72
|
+
\tparam DATA_TYPE The element type: char for a string field, uint8_t for a
|
|
73
|
+
bytes field.
|
|
74
|
+
*/
|
|
75
|
+
template<class DATA_TYPE>
|
|
76
|
+
class BytesStringCallback : public internal::BaseStringBytes
|
|
77
|
+
{
|
|
78
|
+
static_assert(std::is_same<uint8_t, DATA_TYPE>::value || std::is_same<char, DATA_TYPE>::value,
|
|
79
|
+
"This class only supports uint8_t or chars.");
|
|
80
|
+
|
|
81
|
+
public:
|
|
82
|
+
|
|
83
|
+
//! The window handed to a callback: string_view for a string field, bytes_view for a bytes field.
|
|
84
|
+
using VIEW = array_view<DATA_TYPE>;
|
|
85
|
+
|
|
86
|
+
//! Chunk callback shared by both directions, see set_on_serialize_chunk() and set_on_deserialize_chunk().
|
|
87
|
+
using ChunkCallback = Functional<uint32_t(VIEW)>;
|
|
88
|
+
|
|
89
|
+
BytesStringCallback() = default;
|
|
90
|
+
~BytesStringCallback() = default;
|
|
91
|
+
|
|
92
|
+
// --- Binding (reference-taking, no ownership) --------------------------
|
|
93
|
+
|
|
94
|
+
//! Bind the callback that fills windows during serialization.
|
|
95
|
+
/*!
|
|
96
|
+
The callback receives a writable window of up to the bound window's size
|
|
97
|
+
and returns how many it produced, at most the window size. Zero before the
|
|
98
|
+
declared length is reached ends the stream early, which is reported as
|
|
99
|
+
CALLBACK_SIZE_MISMATCH. The callback is not called once the declared
|
|
100
|
+
length is reached.
|
|
101
|
+
*/
|
|
102
|
+
void set_on_serialize_chunk(const ChunkCallback& callback) { on_serialize_chunk_ = callback; }
|
|
103
|
+
|
|
104
|
+
//! Bind the callback that receives windows during deserialization.
|
|
105
|
+
/*!
|
|
106
|
+
The callback receives a window of freshly parsed elements it should read
|
|
107
|
+
and not modify, and returns how many it accepted. Returning less than the
|
|
108
|
+
window size stops the field: deserialization returns CALLBACK_STOPPED.
|
|
109
|
+
Under partial deserialization the accepted elements are consumed and the
|
|
110
|
+
state stays resumable, so a caller may retry after the callback has room
|
|
111
|
+
again.
|
|
112
|
+
*/
|
|
113
|
+
void set_on_deserialize_chunk(const ChunkCallback& callback) { on_deserialize_chunk_ = callback; }
|
|
114
|
+
|
|
115
|
+
//! Bind the memory the windows are staged in; its size is the largest window a callback is offered.
|
|
116
|
+
/*!
|
|
117
|
+
The view does not own the array, which must outlive every serialize or
|
|
118
|
+
deserialize call, like the bound callbacks. Streaming without a window
|
|
119
|
+
bound is CALLBACK_NOT_SET.
|
|
120
|
+
*/
|
|
121
|
+
void set_window(const VIEW& window) { window_ = window; }
|
|
122
|
+
|
|
123
|
+
//! Remove the bound window.
|
|
124
|
+
void clear_window() { window_ = VIEW{nullptr, 0U}; }
|
|
125
|
+
|
|
126
|
+
//! Check whether a usable window is bound.
|
|
127
|
+
bool is_window_set() const { return (nullptr != window_.data) && (0U < window_.size); }
|
|
128
|
+
|
|
129
|
+
//! Remove the bound serialize chunk callback.
|
|
130
|
+
void clear_on_serialize_chunk() { on_serialize_chunk_.clear(); }
|
|
131
|
+
|
|
132
|
+
//! Remove the bound deserialize chunk callback.
|
|
133
|
+
void clear_on_deserialize_chunk() { on_deserialize_chunk_.clear(); }
|
|
134
|
+
|
|
135
|
+
//! Check whether a serialize chunk callback is bound.
|
|
136
|
+
bool is_on_serialize_chunk_set() const { return on_serialize_chunk_.is_set(); }
|
|
137
|
+
|
|
138
|
+
//! Check whether a deserialize chunk callback is bound.
|
|
139
|
+
bool is_on_deserialize_chunk_set() const { return on_deserialize_chunk_.is_set(); }
|
|
140
|
+
|
|
141
|
+
//! Declare the total number of elements the serialize chunk callback will produce, written as the LEN prefix.
|
|
142
|
+
void set_length(const uint32_t length) { length_ = length; }
|
|
143
|
+
|
|
144
|
+
//! Require a binding for the direction being used.
|
|
145
|
+
/*!
|
|
146
|
+
When strict, deserializing without a chunk callback bound returns
|
|
147
|
+
CALLBACK_NOT_SET instead of silently discarding the value.
|
|
148
|
+
*/
|
|
149
|
+
void set_strict(bool strict) { strict_ = strict; }
|
|
150
|
+
|
|
151
|
+
//! Whether strict (require-binding) mode is enabled.
|
|
152
|
+
bool is_strict() const { return strict_; }
|
|
153
|
+
|
|
154
|
+
//! Copy the bindings, the window and the declared length from another chunked field (used by the generated message copy/assignment).
|
|
155
|
+
/*!
|
|
156
|
+
\return Always NO_ERRORS, for signature compatibility with FieldStringBytes::set().
|
|
157
|
+
*/
|
|
158
|
+
Error set(const BytesStringCallback<DATA_TYPE>& rhs)
|
|
159
|
+
{
|
|
160
|
+
on_serialize_chunk_ = rhs.on_serialize_chunk_;
|
|
161
|
+
on_deserialize_chunk_ = rhs.on_deserialize_chunk_;
|
|
162
|
+
window_ = rhs.window_;
|
|
163
|
+
length_ = rhs.length_;
|
|
164
|
+
strict_ = rhs.strict_;
|
|
165
|
+
return Error::NO_ERRORS;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- FieldStringBytes-shaped interface ---------------------------------
|
|
169
|
+
|
|
170
|
+
//! The declared payload length written as the LEN prefix, see set_length().
|
|
171
|
+
uint32_t get_length() const { return length_; }
|
|
172
|
+
|
|
173
|
+
//! Streaming is effectively unbounded; report a large sentinel so capacity checks never fire.
|
|
174
|
+
uint32_t get_max_length() const { return UINT32_MAX; }
|
|
175
|
+
|
|
176
|
+
//! The number of elements the bound window holds, the largest window a callback is offered (0 when unbound).
|
|
177
|
+
uint32_t get_window_size() const { return is_window_set() ? window_.size : 0U; }
|
|
178
|
+
|
|
179
|
+
//! Required of a customStorage bytes/string type.
|
|
180
|
+
/*!
|
|
181
|
+
A stream has no finite serialized size, so report the unbounded sentinel; a
|
|
182
|
+
message with a chunked field therefore cannot be statically buffer-sized,
|
|
183
|
+
which is inherent to streaming.
|
|
184
|
+
*/
|
|
185
|
+
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
|
|
186
|
+
{
|
|
187
|
+
static_cast<void>(field_number);
|
|
188
|
+
return UINT32_MAX;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//! Pull the payload in windows from the bound callback and write it (no tag, no size; the caller frames it with serialize_len).
|
|
192
|
+
/*!
|
|
193
|
+
Windows are pulled until exactly get_length() elements are written. A
|
|
194
|
+
non-empty value with no callback or no window bound cannot be produced:
|
|
195
|
+
CALLBACK_NOT_SET.
|
|
196
|
+
*/
|
|
197
|
+
Error serialize(WriteBufferInterface& buffer) const override
|
|
198
|
+
{
|
|
199
|
+
Error return_value = Error::NO_ERRORS;
|
|
200
|
+
uint32_t remaining = length_;
|
|
201
|
+
if(0U == remaining)
|
|
202
|
+
{
|
|
203
|
+
// Nothing declared to emit; the caller wrote an empty (or no) prefix.
|
|
204
|
+
}
|
|
205
|
+
else if(!on_serialize_chunk_.is_set() || !is_window_set())
|
|
206
|
+
{
|
|
207
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{
|
|
211
|
+
while((0U < remaining) && (Error::NO_ERRORS == return_value))
|
|
212
|
+
{
|
|
213
|
+
return_value = pull_window(buffer, remaining);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return return_value;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//! Read the LEN prefix, then offer each parsed window to the bound callback (drain-and-discard when unbound and not strict).
|
|
220
|
+
Error deserialize(ReadBufferInterface& buffer) override
|
|
221
|
+
{
|
|
222
|
+
uint32_t remaining = 0U;
|
|
223
|
+
Error return_value = WireFormatter::DeserializeVarint(buffer, remaining);
|
|
224
|
+
while((0U < remaining) && (Error::NO_ERRORS == return_value))
|
|
225
|
+
{
|
|
226
|
+
return_value = offer_window(buffer, remaining);
|
|
227
|
+
}
|
|
228
|
+
return return_value;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
//! Validate the wire type is length-delimited, then deserialize.
|
|
232
|
+
Error deserialize_check_type(::EmbeddedProto::ReadBufferInterface& buffer,
|
|
233
|
+
const ::EmbeddedProto::WireFormatter::WireType& wire_type) override
|
|
234
|
+
{
|
|
235
|
+
Error return_value = ::EmbeddedProto::WireFormatter::WireType::LENGTH_DELIMITED == wire_type
|
|
236
|
+
? Error::NO_ERRORS : Error::INVALID_WIRETYPE;
|
|
237
|
+
if(Error::NO_ERRORS == return_value)
|
|
238
|
+
{
|
|
239
|
+
return_value = this->deserialize(buffer);
|
|
240
|
+
}
|
|
241
|
+
return return_value;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
#ifdef PARTIAL_SERIALIZATION_ENABLED
|
|
245
|
+
//! Resumable window-serialize for the partial engine.
|
|
246
|
+
/*!
|
|
247
|
+
The TAG and SIZE phases write the tag and the declared length, so the
|
|
248
|
+
field survives a write buffer that splits before the payload. The DATA
|
|
249
|
+
phase pulls one window at a time, never larger than the room left in the
|
|
250
|
+
buffer, so a produced window is never lost on a BUFFER_FULL resume. The
|
|
251
|
+
callback's own cursor is the resumable state.
|
|
252
|
+
*/
|
|
253
|
+
Error serialize_partial_as_field(uint32_t field_number,
|
|
254
|
+
WriteBufferInterface& buffer,
|
|
255
|
+
MessageState& state,
|
|
256
|
+
bool optional) const override
|
|
257
|
+
{
|
|
258
|
+
Error return_value = Error::NO_ERRORS;
|
|
259
|
+
if(::EmbeddedProto::FieldProcessingPhase::DATA != state.phase)
|
|
260
|
+
{
|
|
261
|
+
return_value = serialize_partial_tag_and_size(field_number, length_, buffer, state, optional);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if((Error::NO_ERRORS == return_value) && (::EmbeddedProto::FieldProcessingPhase::DATA == state.phase))
|
|
265
|
+
{
|
|
266
|
+
if(!on_serialize_chunk_.is_set() || !is_window_set())
|
|
267
|
+
{
|
|
268
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
269
|
+
}
|
|
270
|
+
else
|
|
271
|
+
{
|
|
272
|
+
while((0U < state.bytes_remaining) && (Error::NO_ERRORS == return_value))
|
|
273
|
+
{
|
|
274
|
+
return_value = pull_window(buffer, state.bytes_remaining);
|
|
275
|
+
}
|
|
276
|
+
if(0U == state.bytes_remaining)
|
|
277
|
+
{
|
|
278
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return return_value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
//! Resumable window-deserialize for the partial engine.
|
|
286
|
+
/*!
|
|
287
|
+
The SIZE phase reads the LEN prefix into state.bytes_remaining; the DATA
|
|
288
|
+
phase offers windows to the callback, decrementing bytes_remaining, and
|
|
289
|
+
returns END_OF_BUFFER (leaving the phase at DATA) when the read buffer
|
|
290
|
+
drains mid-value so the next call resumes where it stopped. A window is
|
|
291
|
+
at most what the buffer holds, so a refill boundary simply ends a window.
|
|
292
|
+
*/
|
|
293
|
+
Error deserialize_partial_as_field(ReadBufferInterface& buffer,
|
|
294
|
+
MessageState& state) override
|
|
295
|
+
{
|
|
296
|
+
Error return_value = Error::NO_ERRORS;
|
|
297
|
+
if((::EmbeddedProto::FieldProcessingPhase::SIZE != state.phase) && (::EmbeddedProto::FieldProcessingPhase::DATA != state.phase))
|
|
298
|
+
{
|
|
299
|
+
return_value = Error::STATE_MISMATCH;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if((Error::NO_ERRORS == return_value) && (::EmbeddedProto::FieldProcessingPhase::SIZE == state.phase))
|
|
303
|
+
{
|
|
304
|
+
return_value = deserialize_partial_size_phase(buffer, state);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if((Error::NO_ERRORS == return_value) && (::EmbeddedProto::FieldProcessingPhase::DATA == state.phase))
|
|
308
|
+
{
|
|
309
|
+
while((0U < state.bytes_remaining) && (Error::NO_ERRORS == return_value))
|
|
310
|
+
{
|
|
311
|
+
return_value = offer_window(buffer, state.bytes_remaining);
|
|
312
|
+
}
|
|
313
|
+
if(0U == state.bytes_remaining)
|
|
314
|
+
{
|
|
315
|
+
state.phase = ::EmbeddedProto::FieldProcessingPhase::COMPLETE;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return return_value;
|
|
319
|
+
}
|
|
320
|
+
#endif // PARTIAL_SERIALIZATION_ENABLED
|
|
321
|
+
|
|
322
|
+
//! Forget the declared length; the bindings and the window are kept.
|
|
323
|
+
void clear() override { length_ = 0U; }
|
|
324
|
+
|
|
325
|
+
#ifdef MSG_TO_STRING
|
|
326
|
+
//! A callback field holds no resident value to print, so leave the string unchanged.
|
|
327
|
+
::EmbeddedProto::string_view to_string(::EmbeddedProto::string_view& str, const uint32_t indent_level, char const* name, const bool first_field) const override
|
|
328
|
+
{
|
|
329
|
+
static_cast<void>(indent_level);
|
|
330
|
+
static_cast<void>(name);
|
|
331
|
+
static_cast<void>(first_field);
|
|
332
|
+
return str;
|
|
333
|
+
}
|
|
334
|
+
#endif // MSG_TO_STRING
|
|
335
|
+
|
|
336
|
+
private:
|
|
337
|
+
|
|
338
|
+
//! Pull one window from the serialize callback and push it into the buffer.
|
|
339
|
+
/*!
|
|
340
|
+
The window offered is the smallest of the bound window, the elements
|
|
341
|
+
still to write and the room left in the buffer, so what the callback
|
|
342
|
+
produces always fits. No room at all is BUFFER_FULL before anything is pulled.
|
|
343
|
+
|
|
344
|
+
\param[in] buffer The buffer to write to.
|
|
345
|
+
\param[in,out] remaining The number of elements still to write, decremented by what was produced.
|
|
346
|
+
*/
|
|
347
|
+
Error pull_window(WriteBufferInterface& buffer, uint32_t& remaining) const
|
|
348
|
+
{
|
|
349
|
+
Error return_value = Error::NO_ERRORS;
|
|
350
|
+
const uint32_t offered = min(min(window_.size, remaining), buffer.get_available_size());
|
|
351
|
+
if(0U == offered)
|
|
352
|
+
{
|
|
353
|
+
return_value = Error::BUFFER_FULL;
|
|
354
|
+
}
|
|
355
|
+
else
|
|
356
|
+
{
|
|
357
|
+
uint32_t produced = 0U;
|
|
358
|
+
static_cast<void>(on_serialize_chunk_.invoke(produced, VIEW{window_.data, offered}));
|
|
359
|
+
if((0U == produced) || (offered < produced))
|
|
360
|
+
{
|
|
361
|
+
return_value = Error::CALLBACK_SIZE_MISMATCH;
|
|
362
|
+
}
|
|
363
|
+
else if(!buffer.push(const_bytes_view{window_bytes(), produced}))
|
|
364
|
+
{
|
|
365
|
+
return_value = Error::BUFFER_FULL;
|
|
366
|
+
}
|
|
367
|
+
else
|
|
368
|
+
{
|
|
369
|
+
remaining -= produced;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return return_value;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
//! Peek one window out of the buffer, offer it to the deserialize callback and consume what it accepted.
|
|
376
|
+
/*!
|
|
377
|
+
The window offered is the smallest of the bound window, the elements
|
|
378
|
+
still to read and the bytes the buffer holds. An empty buffer is
|
|
379
|
+
END_OF_BUFFER before anything is consumed. Without a window nothing can
|
|
380
|
+
be offered: CALLBACK_NOT_SET. Without a callback no window is needed to
|
|
381
|
+
drain and discard. The window is peeked, not popped, so only
|
|
382
|
+
the accepted elements leave the buffer and a stopped field can resume.
|
|
383
|
+
|
|
384
|
+
\param[in] buffer The buffer to read from.
|
|
385
|
+
\param[in,out] remaining The number of elements still to read, decremented by what was accepted.
|
|
386
|
+
*/
|
|
387
|
+
Error offer_window(ReadBufferInterface& buffer, uint32_t& remaining)
|
|
388
|
+
{
|
|
389
|
+
Error return_value = Error::NO_ERRORS;
|
|
390
|
+
const uint32_t offered = min(remaining, buffer.get_size());
|
|
391
|
+
if(0U == offered)
|
|
392
|
+
{
|
|
393
|
+
return_value = Error::END_OF_BUFFER;
|
|
394
|
+
}
|
|
395
|
+
else if(on_deserialize_chunk_.is_set())
|
|
396
|
+
{
|
|
397
|
+
if(!is_window_set())
|
|
398
|
+
{
|
|
399
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
400
|
+
}
|
|
401
|
+
else
|
|
402
|
+
{
|
|
403
|
+
return_value = offer_to_callback(buffer, remaining, min(window_.size, offered));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else if(strict_)
|
|
407
|
+
{
|
|
408
|
+
return_value = Error::CALLBACK_NOT_SET;
|
|
409
|
+
}
|
|
410
|
+
else
|
|
411
|
+
{
|
|
412
|
+
// No callback bound and not strict: drain and discard.
|
|
413
|
+
static_cast<void>(buffer.advance(offered));
|
|
414
|
+
remaining -= offered;
|
|
415
|
+
}
|
|
416
|
+
return return_value;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
//! Peek the offered elements into the window, hand it to the callback and consume what it accepted.
|
|
420
|
+
Error offer_to_callback(ReadBufferInterface& buffer, uint32_t& remaining, const uint32_t offered)
|
|
421
|
+
{
|
|
422
|
+
Error return_value = Error::NO_ERRORS;
|
|
423
|
+
// The bytes stay in the buffer until the callback says how many it accepted.
|
|
424
|
+
static_cast<void>(buffer.peek(bytes_view{window_bytes(), offered}));
|
|
425
|
+
|
|
426
|
+
uint32_t accepted = 0U;
|
|
427
|
+
static_cast<void>(on_deserialize_chunk_.invoke(accepted, VIEW{window_.data, offered}));
|
|
428
|
+
if(offered < accepted)
|
|
429
|
+
{
|
|
430
|
+
return_value = Error::CALLBACK_SIZE_MISMATCH;
|
|
431
|
+
}
|
|
432
|
+
else
|
|
433
|
+
{
|
|
434
|
+
static_cast<void>(buffer.advance(accepted));
|
|
435
|
+
remaining -= accepted;
|
|
436
|
+
if(accepted < offered)
|
|
437
|
+
{
|
|
438
|
+
return_value = Error::CALLBACK_STOPPED;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return return_value;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
//! The window as writable bytes, the unit the read buffer fills.
|
|
445
|
+
uint8_t* window_bytes()
|
|
446
|
+
{
|
|
447
|
+
void* void_pointer = static_cast<void*>(window_.data);
|
|
448
|
+
return static_cast<uint8_t*>(void_pointer);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
//! The window as bytes, the unit the write buffer takes.
|
|
452
|
+
const uint8_t* window_bytes() const
|
|
453
|
+
{
|
|
454
|
+
const void* void_pointer = static_cast<const void*>(window_.data);
|
|
455
|
+
return static_cast<const uint8_t*>(void_pointer);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
//! When true, deserializing without a bound callback is an error rather than a silent drain.
|
|
459
|
+
bool strict_ = false;
|
|
460
|
+
|
|
461
|
+
//! The declared payload length written as the LEN prefix.
|
|
462
|
+
uint32_t length_ = 0U;
|
|
463
|
+
|
|
464
|
+
//! The user supplied memory the windows are staged in; its size bounds every window offered.
|
|
465
|
+
VIEW window_{nullptr, 0U};
|
|
466
|
+
|
|
467
|
+
//! Callback invoked to fill a window while serializing.
|
|
468
|
+
ChunkCallback on_serialize_chunk_{};
|
|
469
|
+
|
|
470
|
+
//! Callback invoked with a parsed window while deserializing.
|
|
471
|
+
ChunkCallback on_deserialize_chunk_{};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
} // End of namespace EmbeddedProto
|
|
475
|
+
|
|
476
|
+
#endif // End of _BYTES_STRING_CALLBACK_H_
|
|
@@ -0,0 +1,118 @@
|
|
|
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 _EMBEDDED_PROTO_DEFINES_H_
|
|
25
|
+
#define _EMBEDDED_PROTO_DEFINES_H_
|
|
26
|
+
|
|
27
|
+
#include <type_traits>
|
|
28
|
+
#include <cstdint>
|
|
29
|
+
|
|
30
|
+
#if __cplusplus >= 201703L // C++17 and up
|
|
31
|
+
#include <memory>
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
namespace EmbeddedProto
|
|
35
|
+
{
|
|
36
|
+
|
|
37
|
+
//! Define PARTIAL_SERIALIZATION_ENABLED to enable partial (chunked) serialization
|
|
38
|
+
//! and deserialization. When defined the generated messages and the library gain
|
|
39
|
+
//! the serialize_partial()/deserialize_partial() methods, which allow (de)serialization
|
|
40
|
+
//! to be paused when a buffer is exhausted and resumed with a fresh buffer.
|
|
41
|
+
//! When the macro is not defined only the regular, single-call serialize()/deserialize()
|
|
42
|
+
//! are available.
|
|
43
|
+
|
|
44
|
+
//! Define VIRTUAL_DESTRUCTORS_ENABLED to give the Field, ReadBufferInterface and
|
|
45
|
+
//! WriteBufferInterface base classes a public virtual destructor.
|
|
46
|
+
/*!
|
|
47
|
+
By default those destructors are protected and non-virtual: objects are destroyed as
|
|
48
|
+
their concrete type and deleting one through a pointer to the interface is a compile
|
|
49
|
+
error. A virtual destructor puts a deleting destructor in every vtable, which references
|
|
50
|
+
operator delete and links malloc and free into firmware that never frees anything
|
|
51
|
+
(about 700 bytes of flash and 400 bytes of RAM on a Cortex-M4). Define the macro when
|
|
52
|
+
messages or buffers are owned and deleted through an interface pointer, for example a
|
|
53
|
+
std::unique_ptr<MessageInterface> backed by an RTOS heap.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
#if __cplusplus >= 201703L // C++17 and up
|
|
57
|
+
|
|
58
|
+
template<class T>
|
|
59
|
+
inline constexpr void destroy_at(T* p)
|
|
60
|
+
{
|
|
61
|
+
std::destroy_at(p);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#elif __cplusplus >= 201402L // C++14
|
|
65
|
+
|
|
66
|
+
template<class T>
|
|
67
|
+
constexpr void destroy_at(T* p)
|
|
68
|
+
{
|
|
69
|
+
p->~T();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#elif __cplusplus >= 201103L // C++11
|
|
73
|
+
|
|
74
|
+
template<class T>
|
|
75
|
+
constexpr void destroy_at(T* p)
|
|
76
|
+
{
|
|
77
|
+
p->~T();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#else // Other
|
|
81
|
+
|
|
82
|
+
#error "Unsupported version of C++. Embedded Proto supports C++11 and up."
|
|
83
|
+
|
|
84
|
+
#endif
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
//! A simple struct holding both a pointer to an array and the size of that array.
|
|
88
|
+
/*!
|
|
89
|
+
The view keeps the pointer and its bound together, which is what the array
|
|
90
|
+
interfaces of the library take instead of a separate pointer and length. Use a
|
|
91
|
+
const element type, for example array_view<const uint8_t>, to view an array which
|
|
92
|
+
is only read.
|
|
93
|
+
*/
|
|
94
|
+
template<class T>
|
|
95
|
+
struct array_view {
|
|
96
|
+
T* data; //!< A pointer to the start of an array.
|
|
97
|
+
uint32_t size; //!< The number of elements in the array.
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
using string_view = array_view<char>;
|
|
101
|
+
using bytes_view = array_view<uint8_t>;
|
|
102
|
+
using const_string_view = array_view<const char>; //!< A view on characters which are only read.
|
|
103
|
+
using const_bytes_view = array_view<const uint8_t>; //!< A view on bytes which are only read.
|
|
104
|
+
|
|
105
|
+
//! Simple max function as constexpr
|
|
106
|
+
constexpr uint32_t max(const uint32_t a, const uint32_t b)
|
|
107
|
+
{
|
|
108
|
+
return (a > b) ? a : b;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//! Simple min function as constexpr
|
|
112
|
+
constexpr uint32_t min(const uint32_t a, const uint32_t b)
|
|
113
|
+
{
|
|
114
|
+
return (a < b) ? a : b;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#endif //_EMBEDDED_PROTO_DEFINES_H_
|
|
@@ -0,0 +1,81 @@
|
|
|
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 _EMPTY_ARRAY_H_
|
|
25
|
+
#define _EMPTY_ARRAY_H_
|
|
26
|
+
|
|
27
|
+
#include <cstdint>
|
|
28
|
+
|
|
29
|
+
namespace EmbeddedProto
|
|
30
|
+
{
|
|
31
|
+
namespace internal
|
|
32
|
+
{
|
|
33
|
+
|
|
34
|
+
//! Stand-in for std::array<T, 0> that is one byte on every standard library.
|
|
35
|
+
/*!
|
|
36
|
+
libc++ gives std::array<T, 0> the size of one T so that data() stays aligned, libstdc++
|
|
37
|
+
makes it one byte. A field with a maximum length of zero holds no element, so the fixed size
|
|
38
|
+
containers use this class instead of the array there. Every element access lands on one
|
|
39
|
+
scratch element shared by all instances for the same T. That keeps an out of range access
|
|
40
|
+
memory safe, but a value written to the scratch element is lost by design.
|
|
41
|
+
*/
|
|
42
|
+
template<class T>
|
|
43
|
+
class EmptyArray
|
|
44
|
+
{
|
|
45
|
+
public:
|
|
46
|
+
static constexpr uint32_t size() { return 0U; }
|
|
47
|
+
|
|
48
|
+
T* data() { return &storage(); }
|
|
49
|
+
const T* data() const { return &storage(); }
|
|
50
|
+
|
|
51
|
+
//! Begin and end are equal, a range based loop does not run.
|
|
52
|
+
T* begin() { return data(); }
|
|
53
|
+
T* end() { return data(); }
|
|
54
|
+
const T* begin() const { return data(); }
|
|
55
|
+
const T* end() const { return data(); }
|
|
56
|
+
|
|
57
|
+
//! Any index lands on the scratch element, reset to its default value first.
|
|
58
|
+
T& operator[](uint32_t) { return reset(); }
|
|
59
|
+
const T& operator[](uint32_t) const { return reset(); }
|
|
60
|
+
|
|
61
|
+
void fill(const T&) {}
|
|
62
|
+
|
|
63
|
+
private:
|
|
64
|
+
static T& storage()
|
|
65
|
+
{
|
|
66
|
+
static T element = T();
|
|
67
|
+
return element;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static T& reset()
|
|
71
|
+
{
|
|
72
|
+
T& element = storage();
|
|
73
|
+
element = T();
|
|
74
|
+
return element;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
} // End of namespace internal
|
|
79
|
+
} // End of namespace EmbeddedProto
|
|
80
|
+
|
|
81
|
+
#endif // End of _EMPTY_ARRAY_H_
|