Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include <vlink/base/helpers.h>
25 : : #include <vlink/base/name_detector.h>
26 : : #include <vlink/serializer.h>
27 : : #include <vlink/zerocopy/message_parser.h>
28 : :
29 : : #include <algorithm>
30 : : #include <array>
31 : : #include <charconv>
32 : : #include <cstring>
33 : : #include <limits>
34 : : #include <string_view>
35 : : #include <type_traits>
36 : : #include <utility>
37 : :
38 : : namespace vlink {
39 : :
40 : : namespace zerocopy {
41 : :
42 : : template <typename T>
43 : 22 : static T read_unaligned(const uint8_t* data) noexcept {
44 : 22 : T value{};
45 : 22 : std::memcpy(&value, data, sizeof(value));
46 : :
47 : 22 : return value;
48 : : }
49 : :
50 : 2 : static float bits_to_float(uint32_t bits) noexcept {
51 : 2 : float value = 0.0F;
52 : 2 : std::memcpy(&value, &bits, sizeof(value));
53 : :
54 : 2 : return value;
55 : : }
56 : :
57 : 60 : static bool parse_index(std::string_view path, std::string_view prefix, size_t& index,
58 : : std::string_view& remainder) noexcept {
59 [ + + - + : 60 : if VUNLIKELY (!Helpers::has_startwith(path, prefix) || path.size() <= prefix.size() || path[prefix.size()] != '[') {
+ + + + +
+ ]
60 : 35 : return false;
61 : : }
62 : :
63 : 25 : const size_t close = path.find(']', prefix.size() + 1);
64 : :
65 [ - + ]: 25 : if VUNLIKELY (close == std::string_view::npos) {
66 : 0 : return false;
67 : : }
68 : :
69 : 25 : const char* first = path.data() + prefix.size() + 1;
70 : 25 : const char* last = path.data() + close;
71 : 25 : const auto result = std::from_chars(first, last, index);
72 : :
73 [ + - - + : 25 : if VUNLIKELY (result.ec != std::errc() || result.ptr != last) {
- + ]
74 : 0 : return false;
75 : : }
76 : :
77 : 25 : remainder = path.substr(close + 1);
78 : :
79 [ + + ]: 25 : if (remainder.empty()) {
80 : 18 : return true;
81 : : }
82 : :
83 [ + + ]: 7 : if VUNLIKELY (!Helpers::has_startwith(remainder, ".")) {
84 : 1 : return false;
85 : : }
86 : :
87 : 6 : remainder.remove_prefix(1);
88 : :
89 : 6 : return true;
90 : : }
91 : :
92 : : template <typename T>
93 : 92 : static bool store_number(T value, MessageParser::Value& out) {
94 : : if constexpr (std::is_floating_point_v<T>) {
95 : 13 : out = static_cast<double>(value);
96 : : } else if constexpr (std::is_signed_v<T>) {
97 : 9 : out = static_cast<int64_t>(value);
98 : : } else {
99 : 70 : out = static_cast<uint64_t>(value);
100 : : }
101 : :
102 : 92 : return true;
103 : : }
104 : :
105 : : template <typename T>
106 : 264 : static bool read_number(std::string_view path, std::string_view field, T value, MessageParser::Value& out) {
107 [ + + ]: 264 : if (path != field) {
108 : 203 : return false;
109 : : }
110 : :
111 : 61 : return store_number(value, out);
112 : : }
113 : :
114 : 5 : static bool read_string(std::string_view path, std::string_view field, std::string_view value,
115 : : MessageParser::Value& out) {
116 [ + + ]: 5 : if (path != field) {
117 : 2 : return false;
118 : : }
119 : :
120 [ + - ]: 3 : out = std::string(value);
121 : :
122 : 3 : return true;
123 : : }
124 : :
125 : 1 : static double half_to_double(uint16_t value) noexcept {
126 : 1 : const uint32_t sign = static_cast<uint32_t>(value & 0x8000U) << 16U;
127 : :
128 : 1 : uint32_t exponent = (value >> 10U) & 0x1FU;
129 : 1 : uint32_t mantissa = value & 0x03FFU;
130 : 1 : uint32_t bits = 0;
131 : :
132 [ - + ]: 1 : if (exponent == 0) {
133 [ # # ]: 0 : if (mantissa == 0) {
134 : 0 : bits = sign;
135 : : } else {
136 : 0 : exponent = 113U;
137 : :
138 [ # # ]: 0 : while ((mantissa & 0x0400U) == 0) {
139 : 0 : mantissa <<= 1U;
140 : 0 : --exponent;
141 : : }
142 : :
143 : 0 : bits = sign | (exponent << 23U) | ((mantissa & 0x03FFU) << 13U);
144 : : }
145 [ - + ]: 1 : } else if (exponent == 0x1FU) {
146 : 0 : bits = sign | 0x7F800000U | (mantissa << 13U);
147 : : } else {
148 : 1 : bits = sign | ((exponent + 112U) << 23U) | (mantissa << 13U);
149 : : }
150 : :
151 : 1 : return static_cast<double>(bits_to_float(bits));
152 : : }
153 : :
154 : 49 : static bool header_value(const Header& header, std::string_view path, MessageParser::Value& out) {
155 [ + + ]: 49 : if (path == "header.frame_id") {
156 [ + - ]: 10 : out = std::string(header.frame_id_view());
157 : 10 : return true;
158 : : }
159 : :
160 [ + - + + ]: 39 : if (read_number(path, "header.seq", header.seq, out)) {
161 : 12 : return true;
162 : : }
163 : :
164 [ + - + + ]: 27 : if (read_number(path, "header.reserved", header.reserved, out)) {
165 : 7 : return true;
166 : : }
167 : :
168 [ + - + + ]: 20 : if (read_number(path, "header.time_meas", header.time_meas, out)) {
169 : 10 : return true;
170 : : }
171 : :
172 [ + - + - ]: 10 : if (read_number(path, "header.time_pub", header.time_pub, out)) {
173 : 10 : return true;
174 : : }
175 : :
176 : 0 : return false;
177 : : }
178 : :
179 : 28 : static bool convert_numeric(const MessageParser::Value& value, double& out, bool* precision_loss) noexcept {
180 : 28 : bool loss = false;
181 : :
182 [ + + ]: 28 : if (const auto* signed_number = std::get_if<int64_t>(&value)) {
183 : 5 : constexpr int64_t kMaxExactInteger = INT64_C(1) << std::numeric_limits<double>::digits;
184 [ + + - + ]: 5 : loss = *signed_number > kMaxExactInteger || *signed_number < -kMaxExactInteger;
185 : 5 : out = static_cast<double>(*signed_number);
186 [ + + ]: 23 : } else if (const auto* unsigned_number = std::get_if<uint64_t>(&value)) {
187 : 10 : constexpr uint64_t kMaxExactInteger = UINT64_C(1) << std::numeric_limits<double>::digits;
188 : 10 : loss = *unsigned_number > kMaxExactInteger;
189 : 10 : out = static_cast<double>(*unsigned_number);
190 [ + - ]: 13 : } else if (const auto* double_number = std::get_if<double>(&value)) {
191 : 13 : out = *double_number;
192 : : } else {
193 : 0 : return false;
194 : : }
195 : :
196 [ + + ]: 28 : if (precision_loss != nullptr) {
197 : 2 : *precision_loss = loss;
198 : : }
199 : :
200 : 28 : return true;
201 : : }
202 : :
203 : 104 : static std::string_view normalize_collection(std::string_view collection) noexcept {
204 [ + + + + : 104 : if (collection == "objects" || collection == "points" || collection == "cells" || collection == "elements") {
+ + + + +
+ ]
205 : 25 : return "data";
206 : : }
207 : :
208 : 79 : return collection;
209 : : }
210 : :
211 : 17 : static MessageParser::ValueType point_value_type(uint8_t type, uint8_t size) noexcept {
212 [ - - + + ]: 17 : switch (type) {
213 : 0 : case PointCloud::kInt8Type:
214 : : case PointCloud::kInt16Type:
215 : : case PointCloud::kInt32Type:
216 : : case PointCloud::kInt64Type:
217 : 0 : return MessageParser::ValueType::kInt64;
218 : 0 : case PointCloud::kBoolType:
219 : : case PointCloud::kUint8Type:
220 : : case PointCloud::kUint16Type:
221 : : case PointCloud::kUint32Type:
222 : : case PointCloud::kUint64Type:
223 : 0 : return MessageParser::ValueType::kUInt64;
224 : 7 : case PointCloud::kUnknownType:
225 [ + + ]: 7 : if (size == sizeof(uint8_t)) {
226 : 1 : return MessageParser::ValueType::kUInt64;
227 : : }
228 : :
229 [ + + ]: 6 : if (size == sizeof(int16_t)) {
230 : 1 : return MessageParser::ValueType::kInt64;
231 : : }
232 : :
233 [ + + + + ]: 5 : if (size == sizeof(float) || size == sizeof(double)) {
234 : 2 : return MessageParser::ValueType::kDouble;
235 : : }
236 : :
237 : 3 : return MessageParser::ValueType::kValueUnknown;
238 : 10 : default:
239 : 10 : return MessageParser::ValueType::kDouble;
240 : : }
241 : : }
242 : :
243 : 27 : static size_t hash_point_field(std::string_view name) noexcept {
244 : 27 : size_t hash = sizeof(size_t) == sizeof(uint64_t) ? static_cast<size_t>(UINT64_C(14695981039346656037))
245 : : : static_cast<size_t>(UINT32_C(2166136261));
246 : 27 : const size_t prime = sizeof(size_t) == sizeof(uint64_t) ? static_cast<size_t>(UINT64_C(1099511628211))
247 : : : static_cast<size_t>(UINT32_C(16777619));
248 [ + + ]: 79 : for (const char character : name) {
249 : 52 : hash ^= static_cast<uint8_t>(character);
250 : 52 : hash *= prime;
251 : : }
252 : :
253 : 27 : return hash;
254 : : }
255 : :
256 : 0 : static MessageParser::ValueType tensor_value_type(uint8_t type) noexcept {
257 [ # # # ]: 0 : switch (type) {
258 : 0 : case Tensor::kInt8:
259 : : case Tensor::kInt16:
260 : : case Tensor::kInt32:
261 : : case Tensor::kInt64:
262 : 0 : return MessageParser::ValueType::kInt64;
263 : 0 : case Tensor::kBool:
264 : : case Tensor::kUint8:
265 : : case Tensor::kUint16:
266 : : case Tensor::kUint32:
267 : : case Tensor::kUint64:
268 : 0 : return MessageParser::ValueType::kUInt64;
269 : 0 : default:
270 : 0 : return MessageParser::ValueType::kDouble;
271 : : }
272 : : }
273 : :
274 : 6 : static size_t audio_element_size(uint8_t format) noexcept {
275 [ - - + - : 6 : switch (format) {
- - ]
276 : 0 : case AudioFrame::kFormatPcmU8:
277 : 0 : return sizeof(uint8_t);
278 : 0 : case AudioFrame::kFormatPcmS16:
279 : 0 : return sizeof(int16_t);
280 : 6 : case AudioFrame::kFormatPcmS24:
281 : 6 : return 3;
282 : 0 : case AudioFrame::kFormatPcmS32:
283 : 0 : return sizeof(int32_t);
284 : 0 : case AudioFrame::kFormatPcmF32:
285 : 0 : return sizeof(float);
286 : 0 : default:
287 : 0 : return 0;
288 : : }
289 : : }
290 : :
291 : 4 : bool MessageParser::parse(std::string_view serialized_type, const Bytes& bytes) {
292 : 4 : return parse(detect_type(serialized_type), bytes);
293 : : }
294 : :
295 : 36 : bool MessageParser::parse(Type type, const Bytes& bytes) {
296 : 36 : clear();
297 : :
298 : 36 : bool parsed = false;
299 : :
300 [ + + + + : 36 : switch (type) {
+ + + + +
- ]
301 : 2 : case Type::kRawData:
302 : 2 : parsed = Serializer::convert(bytes, message_.emplace<RawData>());
303 : 2 : break;
304 : :
305 : 2 : case Type::kCameraFrame:
306 : 2 : parsed = Serializer::convert(bytes, message_.emplace<CameraFrame>());
307 : 2 : break;
308 : :
309 : 6 : case Type::kPointCloud:
310 : 6 : parsed = Serializer::convert(bytes, message_.emplace<PointCloud>());
311 : 6 : break;
312 : :
313 : 2 : case Type::kProxyData:
314 : 2 : parsed = Serializer::convert(bytes, message_.emplace<ProxyData>());
315 : 2 : break;
316 : :
317 : 2 : case Type::kOccupancyGrid:
318 : 2 : parsed = Serializer::convert(bytes, message_.emplace<OccupancyGrid>());
319 : 2 : break;
320 : :
321 : 15 : case Type::kTensor:
322 : 15 : parsed = Serializer::convert(bytes, message_.emplace<Tensor>());
323 : 15 : break;
324 : :
325 : 2 : case Type::kObjectArray:
326 : 2 : parsed = Serializer::convert(bytes, message_.emplace<ObjectArray>());
327 : 2 : break;
328 : :
329 : 4 : case Type::kAudioFrame:
330 : 4 : parsed = Serializer::convert(bytes, message_.emplace<AudioFrame>());
331 : 4 : break;
332 : 1 : case Type::kFastBuffer:
333 : 1 : parsed = FastBuffer::read_metadata(bytes, message_.emplace<FastBuffer::Metadata>());
334 : 1 : break;
335 : 0 : default:
336 : 0 : return false;
337 : : }
338 : :
339 [ + + ]: 36 : if VUNLIKELY (!parsed) {
340 : 2 : clear();
341 : 2 : return false;
342 : : }
343 : :
344 [ + + ]: 34 : if (type == Type::kPointCloud) {
345 [ + - ]: 5 : const auto& message = std::get<PointCloud>(message_);
346 : 5 : PointCloud::KeyList keys = message.get_key_list();
347 [ + - ]: 5 : point_fields_.reserve(keys.size());
348 : :
349 : 5 : size_t byte_offset = 0;
350 : :
351 [ + + ]: 22 : for (size_t element_index = 0; element_index < keys.size(); ++element_index) {
352 : 17 : const auto& key = keys[element_index];
353 [ + - ]: 17 : Field field{key.name, point_value_type(key.type, key.size), key.type, key.size};
354 : 17 : field.is_bool = key.type == PointCloud::kBoolType;
355 : 17 : field.byte_offset = byte_offset;
356 : 17 : field.element_index = element_index;
357 [ + - ]: 17 : point_fields_.push_back(std::move(field));
358 : 17 : byte_offset += key.size;
359 : 17 : }
360 : :
361 : 5 : size_t bucket_count = 1;
362 : :
363 [ + + ]: 20 : while (bucket_count < point_fields_.size() * 2) {
364 : 15 : bucket_count <<= 1U;
365 : : }
366 : :
367 : 5 : constexpr auto kNoField = static_cast<size_t>(-1);
368 : :
369 [ + - ]: 5 : point_field_buckets_.assign(bucket_count, kNoField);
370 [ + - ]: 5 : point_field_next_.assign(point_fields_.size(), kNoField);
371 : :
372 [ + + ]: 22 : for (size_t i = point_fields_.size(); i-- > 0;) {
373 : 17 : const size_t bucket = hash_point_field(point_fields_[i].name) & (bucket_count - 1);
374 : 17 : point_field_next_[i] = point_field_buckets_[bucket];
375 : 17 : point_field_buckets_[bucket] = i;
376 : : }
377 : 5 : }
378 : :
379 : 34 : type_ = type;
380 : :
381 [ + + + + : 34 : switch (type_) {
+ + + +
+ ]
382 : 2 : case Type::kRawData:
383 : 2 : reserved_[0] = std::get<RawData>(message_).get_reserved();
384 : 2 : break;
385 : 2 : case Type::kCameraFrame:
386 : 2 : reserved_[0] = std::get<CameraFrame>(message_).get_reserved();
387 : 2 : break;
388 : 5 : case Type::kPointCloud:
389 : 5 : reserved_[0] = std::get<PointCloud>(message_).get_reserved();
390 : 5 : reserved_[1] = std::get<PointCloud>(message_).get_reserved2();
391 : 5 : reserved_[2] = std::get<PointCloud>(message_).get_reserved3();
392 : 5 : break;
393 : 2 : case Type::kProxyData:
394 : 2 : reserved_[0] = std::get<ProxyData>(message_).get_reserved();
395 : 2 : reserved_[1] = std::get<ProxyData>(message_).get_reserved2();
396 : 2 : break;
397 : 2 : case Type::kOccupancyGrid:
398 : 2 : reserved_[0] = std::get<OccupancyGrid>(message_).get_reserved();
399 : 2 : reserved_[1] = std::get<OccupancyGrid>(message_).get_reserved2();
400 : 2 : reserved_[2] = std::get<OccupancyGrid>(message_).get_reserved3();
401 : 2 : break;
402 : 15 : case Type::kTensor:
403 : 15 : reserved_[0] = std::get<Tensor>(message_).get_reserved();
404 : 15 : reserved_[1] = std::get<Tensor>(message_).get_reserved2();
405 : 15 : reserved_[2] = std::get<Tensor>(message_).get_reserved3();
406 : 15 : break;
407 : 2 : case Type::kObjectArray:
408 : 2 : reserved_[0] = std::get<ObjectArray>(message_).get_reserved();
409 : 2 : reserved_[1] = std::get<ObjectArray>(message_).get_reserved2();
410 : 2 : reserved_[2] = std::get<ObjectArray>(message_).get_reserved3();
411 : 2 : reserved_[3] = std::get<ObjectArray>(message_).get_reserved4();
412 : 2 : reserved_[4] = std::get<ObjectArray>(message_).get_reserved5();
413 : 2 : break;
414 : 3 : case Type::kAudioFrame:
415 : 3 : reserved_[0] = std::get<AudioFrame>(message_).get_reserved();
416 : 3 : reserved_[1] = std::get<AudioFrame>(message_).get_reserved2();
417 : 3 : break;
418 : 1 : default:
419 : 1 : break;
420 : : }
421 : :
422 : 34 : return true;
423 : : }
424 : :
425 : 40 : void MessageParser::clear() noexcept {
426 : 40 : type_ = Type::kUnknown;
427 : 40 : message_.emplace<std::monostate>();
428 : 40 : reserved_.fill(0);
429 : 40 : point_fields_.clear();
430 : 40 : point_field_buckets_.clear();
431 : 40 : point_field_next_.clear();
432 : 40 : }
433 : :
434 : 107 : bool MessageParser::value(std::string_view path, Value& out) const {
435 [ + + ]: 107 : if (path.find('[') == std::string_view::npos) {
436 [ + - ]: 80 : return root_value(path, out);
437 : : }
438 : :
439 : 27 : size_t index = 0;
440 : 27 : std::string_view field;
441 [ + + ]: 58 : for (const std::string_view collection : {"data", "objects", "points", "cells", "elements", "shape", "strides"}) {
442 [ + + ]: 55 : if (parse_index(path, collection, index, field)) {
443 [ + - ]: 24 : return element_value(normalize_collection(collection), index, field, out);
444 : : }
445 : : }
446 : :
447 [ + - ]: 3 : return root_value(path, out);
448 : : }
449 : :
450 : 14 : bool MessageParser::value(std::string_view collection, size_t index, std::string_view field, Value& out) const {
451 : 14 : return element_value(normalize_collection(collection), index, field, out);
452 : : }
453 : :
454 : 8 : bool MessageParser::value(std::string_view collection, size_t index, const Field& field, Value& out) const {
455 : 8 : collection = normalize_collection(collection);
456 : :
457 [ + + + - : 15 : if (type_ != Type::kPointCloud || collection != "data" || index >= collection_size(collection) ||
+ - - + +
+ ]
458 : 7 : field.element_index >= point_fields_.size()) {
459 : 1 : return false;
460 : : }
461 : :
462 : 7 : const auto& cached = point_fields_[field.element_index];
463 [ + - + - ]: 13 : if (cached.name != field.name || cached.type != field.type || cached.native_type != field.native_type ||
464 [ + + + - : 13 : cached.storage_size != field.storage_size || cached.byte_offset != field.byte_offset) {
- + + + ]
465 : 1 : return false;
466 : : }
467 : :
468 : 6 : return point_value(index, cached, out);
469 : : }
470 : :
471 : 26 : bool MessageParser::numeric(std::string_view path, double& out, bool* precision_loss) const {
472 : 26 : Value parsed;
473 [ + - + + : 52 : return value(path, parsed) && convert_numeric(parsed, out, precision_loss);
+ - ]
474 : 26 : }
475 : :
476 : 5 : bool MessageParser::numeric(std::string_view collection, size_t index, std::string_view field, double& out,
477 : : bool* precision_loss) const {
478 : 5 : Value parsed;
479 [ + - + + : 10 : return value(collection, index, field, parsed) && convert_numeric(parsed, out, precision_loss);
+ - ]
480 : 5 : }
481 : :
482 : 2 : bool MessageParser::numeric(std::string_view collection, size_t index, const Field& field, double& out,
483 : : bool* precision_loss) const {
484 : 2 : Value parsed;
485 [ + - + - : 4 : return value(collection, index, field, parsed) && convert_numeric(parsed, out, precision_loss);
+ - ]
486 : 2 : }
487 : :
488 : 3 : bool MessageParser::text(std::string_view path, std::string& out) const {
489 : 3 : Value parsed;
490 : :
491 [ + - - + ]: 3 : if VUNLIKELY (!value(path, parsed)) {
492 : 0 : return false;
493 : : }
494 : :
495 : 3 : auto* string = std::get_if<std::string>(&parsed);
496 : :
497 [ - + ]: 3 : if VUNLIKELY (string == nullptr) {
498 : 0 : return false;
499 : : }
500 : :
501 : 3 : out = std::move(*string);
502 : :
503 : 3 : return true;
504 : 3 : }
505 : :
506 : 1 : bool MessageParser::text(std::string_view collection, size_t index, std::string_view field, std::string& out) const {
507 : 1 : Value parsed;
508 : :
509 [ + - - + ]: 1 : if VUNLIKELY (!value(collection, index, field, parsed)) {
510 : 0 : return false;
511 : : }
512 : :
513 : 1 : auto* string = std::get_if<std::string>(&parsed);
514 : :
515 [ - + ]: 1 : if VUNLIKELY (string == nullptr) {
516 : 0 : return false;
517 : : }
518 : :
519 : 1 : out = std::move(*string);
520 : :
521 : 1 : return true;
522 : 1 : }
523 : :
524 : 52 : size_t MessageParser::collection_size(std::string_view collection) const noexcept {
525 : 52 : collection = normalize_collection(collection);
526 : :
527 [ + + + - : 52 : if (type_ == Type::kPointCloud && collection == "data") {
+ + ]
528 : 19 : return get<PointCloud>()->size();
529 : : }
530 : :
531 [ + + + - : 33 : if (type_ == Type::kObjectArray && collection == "data") {
+ + ]
532 : 5 : return get<ObjectArray>()->count();
533 : : }
534 : :
535 [ + + + - : 28 : if (type_ == Type::kOccupancyGrid && collection == "data") {
+ + ]
536 : 3 : const auto& message = *get<OccupancyGrid>();
537 : :
538 [ - + ]: 3 : if VUNLIKELY (message.cell_size() == 0) {
539 : 0 : return 0;
540 : : }
541 : :
542 : 3 : const size_t payload_count = message.size() / message.cell_size();
543 : 3 : const size_t declared_count = static_cast<size_t>(message.width()) * message.height();
544 : 3 : return std::min(payload_count, declared_count);
545 : : }
546 : :
547 [ + + ]: 25 : if (type_ == Type::kTensor) {
548 : 19 : const auto& message = *get<Tensor>();
549 : :
550 [ + + - + : 19 : if (collection == "shape" || collection == "strides") {
+ + ]
551 : 3 : return message.rank();
552 : : }
553 : :
554 [ + - + - : 16 : if (collection == "data" && message.element_size() != 0) {
+ - ]
555 : 16 : return std::min<size_t>(message.num_elements(), message.size() / message.element_size());
556 : : }
557 : : }
558 : :
559 [ + + + - : 6 : if (type_ == Type::kAudioFrame && collection == "data") {
+ + ]
560 : 4 : const auto& message = *get<AudioFrame>();
561 : 4 : const size_t element_size = audio_element_size(message.format());
562 : :
563 [ + - - + : 4 : if VUNLIKELY (element_size == 0 || message.bit_depth() != element_size * 8U) {
- + ]
564 : 0 : return 0;
565 : : }
566 : :
567 : 4 : return message.size() / element_size;
568 : : }
569 : :
570 : 2 : return 0;
571 : : }
572 : :
573 : 13 : std::vector<MessageParser::Field> MessageParser::fields() const {
574 : 13 : std::vector<Field> result;
575 : :
576 [ - + ]: 13 : if VUNLIKELY (!valid()) {
577 : 0 : return result;
578 : : }
579 : :
580 [ + - + - ]: 22 : const auto add_text = [&result](std::string_view name) { result.push_back({std::string(name), ValueType::kString}); };
581 : :
582 : 87 : const auto add_number = [&result](std::string_view name, ValueType type) {
583 [ + - + - ]: 87 : result.push_back({std::string(name), type});
584 : 87 : };
585 : :
586 [ + - + - ]: 13 : const auto add_bytes = [&result](std::string_view name) { result.push_back({std::string(name), ValueType::kBytes}); };
587 : :
588 : 56 : const auto add_time = [&result](std::string_view name) {
589 [ + - ]: 28 : Field field{std::string(name), ValueType::kUInt64};
590 : 28 : field.is_time = true;
591 [ + - ]: 28 : result.push_back(std::move(field));
592 : 28 : };
593 : :
594 : 2 : const auto add_bool = [&result](std::string_view name) {
595 [ + - ]: 1 : Field field{std::string(name), ValueType::kUInt64};
596 : 1 : field.is_bool = true;
597 [ + - ]: 1 : result.push_back(std::move(field));
598 : 1 : };
599 : :
600 : 34 : const auto add_enum = [&result](std::string_view name, EnumKind kind) {
601 [ + - ]: 17 : Field field{std::string(name), ValueType::kUInt64};
602 : 17 : field.enum_kind = kind;
603 [ + - ]: 17 : result.push_back(std::move(field));
604 : 17 : };
605 : :
606 : 82 : const auto add_reserved = [&result](std::string_view name) {
607 [ + - ]: 41 : Field field{std::string(name), ValueType::kUInt64};
608 : 41 : field.is_reserved = true;
609 [ + - ]: 41 : result.push_back(std::move(field));
610 : 41 : };
611 : :
612 [ + + ]: 13 : if (type_ != Type::kProxyData) {
613 [ + - ]: 12 : add_text("header.frame_id");
614 [ + - ]: 12 : add_number("header.seq", ValueType::kUInt64);
615 [ + - ]: 12 : add_reserved("header.reserved");
616 [ + - ]: 12 : add_time("header.time_meas");
617 [ + - ]: 12 : add_time("header.time_pub");
618 : : }
619 : :
620 [ + + + + : 13 : switch (type_) {
+ + + + +
- ]
621 : 1 : case Type::kRawData:
622 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
623 [ + - ]: 1 : add_reserved("reserved");
624 [ + - ]: 1 : add_bytes("data");
625 : 1 : break;
626 : :
627 : 3 : case Type::kFastBuffer:
628 [ + - ]: 3 : add_number("size", ValueType::kUInt64);
629 [ + - ]: 3 : add_number("protocol", ValueType::kUInt64);
630 [ + - ]: 3 : add_enum("storage", EnumKind::kEnumFastBufferStorage);
631 [ + - ]: 3 : add_enum("memory_type", EnumKind::kEnumFastBufferMemory);
632 [ + - ]: 3 : add_number("metadata_size", ValueType::kUInt64);
633 [ + - ]: 3 : add_bytes("metadata");
634 [ + - ]: 3 : add_reserved("reserved");
635 [ + - ]: 3 : add_reserved("reserved2");
636 : 3 : break;
637 : :
638 : 3 : case Type::kCameraFrame:
639 [ + - ]: 3 : add_number("channel", ValueType::kUInt64);
640 [ + - ]: 3 : add_number("height", ValueType::kUInt64);
641 [ + - ]: 3 : add_number("width", ValueType::kUInt64);
642 [ + - ]: 3 : add_number("freq", ValueType::kUInt64);
643 [ + - ]: 3 : add_enum("format", EnumKind::kEnumCameraFormat);
644 [ + - ]: 3 : add_enum("stream", EnumKind::kEnumCameraStream);
645 [ + - ]: 3 : add_number("size", ValueType::kUInt64);
646 [ + - ]: 3 : add_reserved("reserved");
647 [ + - ]: 3 : add_bytes("data");
648 : 3 : break;
649 : :
650 : 1 : case Type::kPointCloud:
651 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
652 [ + - ]: 1 : add_number("pack_size", ValueType::kUInt64);
653 [ + - ]: 1 : add_number("extent", ValueType::kUInt64);
654 [ + - ]: 1 : add_number("downsample", ValueType::kUInt64);
655 [ + - ]: 1 : add_bool("vertical");
656 [ + - ]: 1 : add_reserved("reserved_size");
657 [ + - ]: 1 : add_reserved("reserved");
658 [ + - ]: 1 : add_reserved("reserved2");
659 [ + - ]: 1 : add_reserved("reserved3");
660 [ + - ]: 1 : add_bytes("data");
661 : 1 : break;
662 : :
663 : 1 : case Type::kProxyData:
664 [ + - ]: 1 : add_number("control_id", ValueType::kUInt64);
665 [ + - ]: 1 : add_number("mode", ValueType::kUInt64);
666 [ + - ]: 1 : add_number("timestamp", ValueType::kInt64);
667 [ + - ]: 1 : add_number("seq", ValueType::kInt64);
668 [ + - ]: 1 : add_number("schema", ValueType::kUInt64);
669 [ + - ]: 1 : add_text("url");
670 [ + - ]: 1 : add_text("ser");
671 [ + - ]: 1 : add_text("hostname");
672 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
673 [ + - ]: 1 : add_reserved("reserved");
674 [ + - ]: 1 : add_reserved("reserved2");
675 [ + - ]: 1 : add_bytes("raw");
676 : 1 : break;
677 : :
678 : 1 : case Type::kOccupancyGrid:
679 [ + - ]: 1 : add_text("map_id");
680 [ + - ]: 1 : add_number("width", ValueType::kUInt64);
681 [ + - ]: 1 : add_number("height", ValueType::kUInt64);
682 [ + - ]: 1 : add_number("channel", ValueType::kUInt64);
683 [ + - ]: 1 : add_number("freq", ValueType::kUInt64);
684 [ + - ]: 1 : add_number("valid_cell_count", ValueType::kUInt64);
685 [ + - ]: 1 : add_number("default_value", ValueType::kInt64);
686 [ + - ]: 1 : add_number("resolution", ValueType::kDouble);
687 [ + - ]: 1 : add_number("origin_x", ValueType::kDouble);
688 [ + - ]: 1 : add_number("origin_y", ValueType::kDouble);
689 [ + - ]: 1 : add_number("origin_z", ValueType::kDouble);
690 [ + - ]: 1 : add_number("origin_yaw", ValueType::kDouble);
691 [ + - ]: 1 : add_number("value_min", ValueType::kDouble);
692 [ + - ]: 1 : add_number("value_max", ValueType::kDouble);
693 [ + - ]: 1 : add_number("occupied_threshold", ValueType::kDouble);
694 [ + - ]: 1 : add_number("free_threshold", ValueType::kDouble);
695 [ + - ]: 1 : add_enum("cell_type", EnumKind::kEnumGridCellType);
696 [ + - ]: 1 : add_number("cell_size", ValueType::kUInt64);
697 [ + - ]: 1 : add_time("update_time_ns");
698 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
699 [ + - ]: 1 : add_reserved("reserved");
700 [ + - ]: 1 : add_reserved("reserved2");
701 [ + - ]: 1 : add_reserved("reserved3");
702 [ + - ]: 1 : add_bytes("data");
703 : 1 : break;
704 : :
705 : 1 : case Type::kTensor:
706 [ + - ]: 1 : add_text("name");
707 [ + - ]: 1 : add_text("model_id");
708 [ + - ]: 1 : add_text("layout");
709 [ + - ]: 1 : add_enum("dtype", EnumKind::kEnumTensorDataType);
710 [ + - ]: 1 : add_enum("device", EnumKind::kEnumTensorDevice);
711 [ + - ]: 1 : add_number("rank", ValueType::kUInt64);
712 [ + - ]: 1 : add_number("num_elements", ValueType::kUInt64);
713 [ + - ]: 1 : add_number("element_size", ValueType::kUInt64);
714 [ + - ]: 1 : add_number("batch_size", ValueType::kUInt64);
715 [ + - ]: 1 : add_number("channel", ValueType::kUInt64);
716 [ + - ]: 1 : add_number("freq", ValueType::kUInt64);
717 [ + - ]: 1 : add_number("quant_zero_point", ValueType::kInt64);
718 [ + - ]: 1 : add_number("quant_scale", ValueType::kDouble);
719 [ + - ]: 1 : add_time("update_time_ns");
720 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
721 [ + - ]: 1 : add_reserved("reserved");
722 [ + - ]: 1 : add_reserved("reserved2");
723 [ + - ]: 1 : add_reserved("reserved3");
724 [ + - ]: 1 : add_bytes("data");
725 : 1 : break;
726 : :
727 : 1 : case Type::kObjectArray:
728 [ + - ]: 1 : add_text("source_id");
729 [ + - ]: 1 : add_number("channel", ValueType::kUInt64);
730 [ + - ]: 1 : add_number("freq", ValueType::kUInt64);
731 [ + - ]: 1 : add_number("count", ValueType::kUInt64);
732 [ + - ]: 1 : add_number("pack_size", ValueType::kUInt64);
733 [ + - ]: 1 : add_time("update_time_ns");
734 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
735 [ + - ]: 1 : add_reserved("reserved");
736 [ + - ]: 1 : add_reserved("reserved2");
737 [ + - ]: 1 : add_reserved("reserved3");
738 [ + - ]: 1 : add_reserved("reserved4");
739 [ + - ]: 1 : add_reserved("reserved5");
740 [ + - ]: 1 : add_bytes("data");
741 : 1 : break;
742 : :
743 : 1 : case Type::kAudioFrame:
744 [ + - ]: 1 : add_text("codec");
745 [ + - ]: 1 : add_text("language");
746 [ + - ]: 1 : add_number("channel", ValueType::kUInt64);
747 [ + - ]: 1 : add_number("freq", ValueType::kUInt64);
748 [ + - ]: 1 : add_number("sample_rate", ValueType::kUInt64);
749 [ + - ]: 1 : add_number("num_samples", ValueType::kUInt64);
750 [ + - ]: 1 : add_number("num_channels", ValueType::kUInt64);
751 [ + - ]: 1 : add_number("bit_depth", ValueType::kUInt64);
752 [ + - ]: 1 : add_number("bitrate", ValueType::kUInt64);
753 [ + - ]: 1 : add_enum("format", EnumKind::kEnumAudioFormat);
754 [ + - ]: 1 : add_enum("layout", EnumKind::kEnumAudioLayout);
755 [ + - ]: 1 : add_number("duration_ns", ValueType::kUInt64);
756 [ + - ]: 1 : add_time("update_time_ns");
757 [ + - ]: 1 : add_number("size", ValueType::kUInt64);
758 [ + - ]: 1 : add_reserved("reserved");
759 [ + - ]: 1 : add_reserved("reserved2");
760 [ + - ]: 1 : add_bytes("data");
761 : 1 : break;
762 : 0 : default:
763 : 0 : break;
764 : : }
765 : :
766 : 13 : return result;
767 : 0 : }
768 : :
769 : 6 : std::vector<MessageParser::Field> MessageParser::element_fields(std::string_view collection) const {
770 : 6 : collection = normalize_collection(collection);
771 : :
772 [ + + + - : 6 : if (type_ == Type::kPointCloud && collection == "data") {
+ + ]
773 : 3 : return point_fields_;
774 : : }
775 : :
776 [ - + - - : 3 : if (type_ == Type::kTensor && collection == "data") {
- + ]
777 : 0 : const auto& message = *get<Tensor>();
778 [ # # # # : 0 : return {{"value", tensor_value_type(message.dtype()), message.dtype(), message.element_size()}};
# # # # ]
779 : : }
780 : :
781 [ - + - - : 3 : if (type_ == Type::kTensor && (collection == "shape" || collection == "strides")) {
- - - + ]
782 [ # # # # : 0 : return {{"value", ValueType::kUInt64}};
# # # # ]
783 : : }
784 : :
785 [ - + - - : 3 : if (type_ == Type::kOccupancyGrid && collection == "data") {
- + ]
786 : 0 : const auto& message = *get<OccupancyGrid>();
787 : :
788 [ # # # # ]: 0 : switch (message.cell_type()) {
789 : 0 : case OccupancyGrid::kCellInt8:
790 [ # # # # : 0 : return {{"value", ValueType::kInt64, message.cell_type(), message.cell_size()}};
# # # # ]
791 : 0 : case OccupancyGrid::kCellUint8:
792 : : case OccupancyGrid::kCellUint16:
793 [ # # # # : 0 : return {{"value", ValueType::kUInt64, message.cell_type(), message.cell_size()}};
# # # # ]
794 : 0 : case OccupancyGrid::kCellFloat32:
795 [ # # # # : 0 : return {{"value", ValueType::kDouble, message.cell_type(), message.cell_size()}};
# # # # ]
796 : 0 : default:
797 : 0 : return {};
798 : : }
799 : : }
800 : :
801 [ + + + - : 3 : if (type_ == Type::kAudioFrame && collection == "data") {
+ + ]
802 : 1 : const auto& message = *get<AudioFrame>();
803 : :
804 [ - - + - : 1 : switch (message.format()) {
- - ]
805 : 0 : case AudioFrame::kFormatPcmU8:
806 [ # # # # : 0 : return {{"value", ValueType::kUInt64, message.format(), static_cast<uint16_t>(sizeof(uint8_t))}};
# # # # ]
807 : 0 : case AudioFrame::kFormatPcmS16:
808 [ # # # # : 0 : return {{"value", ValueType::kInt64, message.format(), static_cast<uint16_t>(sizeof(int16_t))}};
# # # # ]
809 : 1 : case AudioFrame::kFormatPcmS24:
810 [ + - + - : 2 : return {{"value", ValueType::kInt64, message.format(), 3}};
+ + - - ]
811 : 0 : case AudioFrame::kFormatPcmS32:
812 [ # # # # : 0 : return {{"value", ValueType::kInt64, message.format(), static_cast<uint16_t>(sizeof(int32_t))}};
# # # # ]
813 : 0 : case AudioFrame::kFormatPcmF32:
814 [ # # # # : 0 : return {{"value", ValueType::kDouble, message.format(), static_cast<uint16_t>(sizeof(float))}};
# # # # ]
815 : 0 : default:
816 : 0 : return {};
817 : : }
818 : : }
819 : :
820 [ - + - - : 2 : if (type_ == Type::kObjectArray && collection == "data") {
- + ]
821 : : return {{"label", ValueType::kString},
822 : : {"position[0]", ValueType::kDouble},
823 : : {"position[1]", ValueType::kDouble},
824 : : {"position[2]", ValueType::kDouble},
825 : : {"yaw", ValueType::kDouble},
826 : : {"size[0]", ValueType::kDouble},
827 : : {"size[1]", ValueType::kDouble},
828 : : {"size[2]", ValueType::kDouble},
829 : : {"yaw_rate", ValueType::kDouble},
830 : : {"velocity[0]", ValueType::kDouble},
831 : : {"velocity[1]", ValueType::kDouble},
832 : : {"velocity[2]", ValueType::kDouble},
833 : : {"score", ValueType::kDouble},
834 : : {"acceleration[0]", ValueType::kDouble},
835 : : {"acceleration[1]", ValueType::kDouble},
836 : : {"acceleration[2]", ValueType::kDouble},
837 : : {"existence_probability", ValueType::kDouble},
838 : : {"position_covariance[0]", ValueType::kDouble},
839 : : {"position_covariance[1]", ValueType::kDouble},
840 : : {"position_covariance[2]", ValueType::kDouble},
841 : : {"position_covariance[3]", ValueType::kDouble},
842 : : {"position_covariance[4]", ValueType::kDouble},
843 : : {"position_covariance[5]", ValueType::kDouble},
844 : : {"class_id", ValueType::kUInt64},
845 : : {"track_id", ValueType::kUInt64},
846 : : {"age", ValueType::kUInt64},
847 : : {"num_observations", ValueType::kUInt64},
848 : : {"motion_state", ValueType::kUInt64},
849 : : {"source_type", ValueType::kUInt64},
850 : : {"subtype_id", ValueType::kUInt64},
851 [ # # # # : 0 : {"reserved", ValueType::kUInt64}};
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
852 : : }
853 : :
854 : 2 : return {};
855 : : }
856 : :
857 : 14 : static std::string format_integer(const MessageParser::Value& value, bool hex, uint16_t storage_size) {
858 [ - + ]: 14 : if (const auto* number = std::get_if<int64_t>(&value)) {
859 [ # # ]: 0 : if (!hex) {
860 : 0 : return std::to_string(*number);
861 : : }
862 : :
863 : 0 : auto bits = static_cast<uint64_t>(*number);
864 : :
865 [ # # # # ]: 0 : if (storage_size > 0U && storage_size < sizeof(uint64_t)) {
866 : 0 : bits &= (uint64_t{1} << (storage_size * 8U)) - 1U;
867 : : }
868 : :
869 : 0 : return Helpers::format_hex_number(bits);
870 : : }
871 : :
872 [ + - ]: 14 : if (const auto* number = std::get_if<uint64_t>(&value)) {
873 [ - + ]: 14 : return hex ? Helpers::format_hex_number(*number) : std::to_string(*number);
874 : : }
875 : :
876 [ # # ]: 0 : if (const auto* number = std::get_if<double>(&value)) {
877 : 0 : return std::to_string(*number);
878 : : }
879 : :
880 : 0 : return {};
881 : : }
882 : :
883 : 4 : static std::string_view enum_label(MessageParser::EnumKind kind, uint64_t value) {
884 [ + + - - : 4 : switch (kind) {
- - - + +
- ]
885 : 1 : case MessageParser::EnumKind::kEnumCameraFormat:
886 : 1 : return NameDetector::get_enum(static_cast<CameraFrame::Format>(value));
887 : 1 : case MessageParser::EnumKind::kEnumCameraStream:
888 : 1 : return NameDetector::get_enum(static_cast<CameraFrame::Stream>(value));
889 : 0 : case MessageParser::EnumKind::kEnumGridCellType:
890 : 0 : return NameDetector::get_enum(static_cast<OccupancyGrid::CellType>(value));
891 : 0 : case MessageParser::EnumKind::kEnumTensorDataType:
892 : 0 : return NameDetector::get_enum(static_cast<Tensor::DataType>(value));
893 : 0 : case MessageParser::EnumKind::kEnumTensorDevice:
894 : 0 : return NameDetector::get_enum(static_cast<Tensor::Device>(value));
895 : 0 : case MessageParser::EnumKind::kEnumAudioFormat:
896 : 0 : return NameDetector::get_enum(static_cast<AudioFrame::Format>(value));
897 : 0 : case MessageParser::EnumKind::kEnumAudioLayout:
898 : 0 : return NameDetector::get_enum(static_cast<AudioFrame::Layout>(value));
899 : 1 : case MessageParser::EnumKind::kEnumFastBufferStorage:
900 : 1 : return NameDetector::get_enum(static_cast<FastBuffer::Storage>(value));
901 : 1 : case MessageParser::EnumKind::kEnumFastBufferMemory:
902 : 1 : return NameDetector::get_enum(static_cast<zerocopy::FastBuffer::MemoryType>(value));
903 : 0 : default:
904 : 0 : return {};
905 : : }
906 : : }
907 : :
908 : 29 : static std::string format_scalar(const MessageParser& parser, const MessageParser::Field& field,
909 : : const MessageFormatOptions& options) {
910 : 29 : MessageParser::Value value;
911 : :
912 [ + - - + ]: 29 : if VUNLIKELY (!parser.value(field.name, value)) {
913 : 0 : return {};
914 : : }
915 : :
916 [ + + ]: 29 : if (const auto* text = std::get_if<std::string>(&value)) {
917 [ + - ]: 3 : return *text;
918 : : }
919 : :
920 [ - + ]: 26 : if (field.is_bool) {
921 : 0 : const auto* number = std::get_if<uint64_t>(&value);
922 [ # # # # : 0 : return number != nullptr && *number != 0 ? "true" : "false";
# # ]
923 : : }
924 : :
925 [ + + ]: 26 : if (field.enum_kind != MessageParser::EnumKind::kEnumNone) {
926 : 6 : const auto* number = std::get_if<uint64_t>(&value);
927 [ + - ]: 6 : const uint64_t raw = number != nullptr ? *number : 0;
928 [ + + + - : 12 : return options.enum_name ? std::string(enum_label(field.enum_kind, raw)) : std::to_string(raw);
+ - + + -
- ]
929 : : }
930 : :
931 [ + + ]: 20 : if (field.is_time) {
932 : 6 : const auto* number = std::get_if<uint64_t>(&value);
933 [ + - ]: 6 : const uint64_t raw = number != nullptr ? *number : 0;
934 : :
935 [ - + ]: 6 : if (options.date) {
936 : 0 : return Helpers::format_date(static_cast<int64_t>(raw));
937 : : }
938 : :
939 [ - + + - ]: 6 : return options.hex ? Helpers::format_hex_number(raw) : std::to_string(raw);
940 : : }
941 : :
942 [ + - ]: 14 : return format_integer(value, options.hex, field.storage_size);
943 : 29 : }
944 : :
945 : 0 : static std::string format_pointcloud_protocol(const MessageParser& parser) {
946 : : static constexpr std::array<std::string_view, 12> kTypeNames = {
947 : : "unknown", "bool", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "float", "double"};
948 : :
949 [ # # ]: 0 : const auto fields = parser.element_fields("data");
950 : :
951 : 0 : uint64_t extent = 0;
952 : 0 : MessageParser::Value extent_value;
953 : :
954 [ # # # # ]: 0 : if (parser.value("extent", extent_value)) {
955 [ # # ]: 0 : if (const auto* number = std::get_if<uint64_t>(&extent_value)) {
956 : 0 : extent = *number;
957 : : }
958 : : }
959 : :
960 : 0 : std::string size_list;
961 : 0 : std::string name_list;
962 : 0 : std::string type_list;
963 : :
964 [ # # ]: 0 : for (size_t i = 0; i < fields.size(); ++i) {
965 [ # # ]: 0 : if (i != 0) {
966 [ # # ]: 0 : size_list += ",";
967 [ # # ]: 0 : name_list += ",";
968 [ # # ]: 0 : type_list += ",";
969 : : }
970 : :
971 : 0 : const auto& field = fields[i];
972 : 0 : auto native_type = field.native_type;
973 : :
974 [ # # # # : 0 : if (extent != 0 && i < 3 && native_type == PointCloud::kInt16Type && field.storage_size == sizeof(int16_t)) {
# # # # ]
975 : 0 : native_type = PointCloud::kFloatType;
976 : : }
977 : :
978 [ # # # # ]: 0 : size_list += std::to_string(field.storage_size);
979 [ # # ]: 0 : name_list += field.name;
980 [ # # # # : 0 : type_list += native_type < kTypeNames.size() ? std::string(kTypeNames[native_type]) : "unknown";
# # # # #
# # # # #
# # ]
981 : : }
982 : :
983 [ # # ]: 0 : std::string result = "protocol {\n";
984 [ # # # # : 0 : result += " size_list: " + size_list + "\n";
# # ]
985 [ # # # # : 0 : result += " name_list: " + name_list + "\n";
# # ]
986 [ # # # # : 0 : result += " type_list: " + type_list + "\n";
# # ]
987 [ # # ]: 0 : result += "}\n";
988 : :
989 : 0 : return result;
990 : 0 : }
991 : :
992 : 0 : static std::string format_tensor_shape(const MessageParser& parser) {
993 [ # # ]: 0 : std::string shape = "[";
994 : 0 : const size_t count = parser.collection_size("shape");
995 : :
996 [ # # ]: 0 : for (size_t d = 0; d < count; ++d) {
997 [ # # ]: 0 : if (d != 0) {
998 [ # # ]: 0 : shape += ", ";
999 : : }
1000 : :
1001 : 0 : MessageParser::Value value;
1002 : :
1003 [ # # # # ]: 0 : if (parser.value("shape", d, std::string_view{}, value)) {
1004 [ # # ]: 0 : if (const auto* number = std::get_if<uint64_t>(&value)) {
1005 [ # # # # ]: 0 : shape += std::to_string(*number);
1006 : : }
1007 : : }
1008 : 0 : }
1009 : :
1010 [ # # ]: 0 : shape += "]";
1011 : :
1012 [ # # # # ]: 0 : return "shape: " + shape + "\n";
1013 : 0 : }
1014 : :
1015 : 0 : static std::string format_pointcloud_points(const MessageParser& parser, const MessageFormatOptions& options,
1016 : : bool* truncated) {
1017 [ # # ]: 0 : const auto fields = parser.element_fields("data");
1018 : 0 : const size_t total = parser.collection_size("data");
1019 : 0 : const size_t count = std::min(total, options.max_elements);
1020 : :
1021 [ # # # # ]: 0 : if (truncated != nullptr && total > options.max_elements) {
1022 : 0 : *truncated = true;
1023 : : }
1024 : :
1025 : 0 : std::string result;
1026 : :
1027 [ # # ]: 0 : for (size_t i = 0; i < count; ++i) {
1028 [ # # # # : 0 : result += "data[" + std::to_string(i) + "] {\n";
# # # # ]
1029 : :
1030 [ # # ]: 0 : for (const auto& field : fields) {
1031 : 0 : MessageParser::Value value;
1032 : 0 : std::string text;
1033 : :
1034 [ # # # # ]: 0 : if (parser.value("data", i, field, value)) {
1035 [ # # ]: 0 : if (field.is_bool) {
1036 : 0 : const auto* number = std::get_if<uint64_t>(&value);
1037 [ # # # # : 0 : text = number != nullptr && *number != 0 ? "true" : "false";
# # ]
1038 : : } else {
1039 [ # # ]: 0 : text = format_integer(value, options.hex, field.storage_size);
1040 : : }
1041 : : }
1042 : :
1043 [ # # # # : 0 : result += " " + field.name + ": " + text + "\n";
# # # # #
# ]
1044 : 0 : }
1045 : :
1046 [ # # ]: 0 : result += "}\n";
1047 : : }
1048 : :
1049 : 0 : return result;
1050 : 0 : }
1051 : :
1052 : 9 : MessageParser::Type MessageParser::detect_type(std::string_view serialized_type) noexcept {
1053 : : static constexpr std::pair<std::string_view, Type> kTypes[] = {
1054 : : {"RawData", Type::kRawData}, {"CameraFrame", Type::kCameraFrame}, {"PointCloud", Type::kPointCloud},
1055 : : {"ProxyData", Type::kProxyData}, {"OccupancyGrid", Type::kOccupancyGrid}, {"Tensor", Type::kTensor},
1056 : : {"ObjectArray", Type::kObjectArray}, {"AudioFrame", Type::kAudioFrame}, {"FastBuffer", Type::kFastBuffer},
1057 : : };
1058 : :
1059 [ + + ]: 60 : for (const auto& [name, type] : kTypes) {
1060 [ + + ]: 58 : if (serialized_type == name) {
1061 : 3 : return type;
1062 : : }
1063 : :
1064 [ + + ]: 55 : if (serialized_type.size() <= name.size()) {
1065 : 15 : continue;
1066 : : }
1067 : :
1068 : 40 : const size_t offset = serialized_type.size() - name.size();
1069 : 40 : const char separator = serialized_type[offset - 1];
1070 : :
1071 [ + - + + : 40 : if ((separator == '.' || separator == ':' || separator == '/') && serialized_type.substr(offset) == name) {
+ + + + +
+ ]
1072 : 4 : return type;
1073 : : }
1074 : : }
1075 : :
1076 : 2 : return Type::kUnknown;
1077 : : }
1078 : :
1079 : 1 : std::string_view MessageParser::type_name(Type type) noexcept {
1080 [ - - - - : 1 : switch (type) {
- - - - +
- ]
1081 : 0 : case Type::kRawData:
1082 : 0 : return "RawData";
1083 : 0 : case Type::kCameraFrame:
1084 : 0 : return "CameraFrame";
1085 : 0 : case Type::kPointCloud:
1086 : 0 : return "PointCloud";
1087 : 0 : case Type::kProxyData:
1088 : 0 : return "ProxyData";
1089 : 0 : case Type::kOccupancyGrid:
1090 : 0 : return "OccupancyGrid";
1091 : 0 : case Type::kTensor:
1092 : 0 : return "Tensor";
1093 : 0 : case Type::kObjectArray:
1094 : 0 : return "ObjectArray";
1095 : 0 : case Type::kAudioFrame:
1096 : 0 : return "AudioFrame";
1097 : 1 : case Type::kFastBuffer:
1098 : 1 : return "FastBuffer";
1099 : 0 : default:
1100 : 0 : return {};
1101 : : }
1102 : : }
1103 : :
1104 : 83 : bool MessageParser::root_value(std::string_view path, Value& out) const {
1105 [ + + ]: 83 : if (Helpers::has_startwith(path, "header.")) {
1106 : 50 : const Header* header = nullptr;
1107 [ + + + + : 50 : switch (type_) {
+ + + +
+ ]
1108 : 7 : case Type::kRawData:
1109 : 7 : header = &get<RawData>()->header;
1110 : 7 : break;
1111 : :
1112 : 9 : case Type::kCameraFrame:
1113 : 9 : header = &get<CameraFrame>()->header;
1114 : 9 : break;
1115 : :
1116 : 5 : case Type::kPointCloud:
1117 : 5 : header = &get<PointCloud>()->header;
1118 : 5 : break;
1119 : :
1120 : 5 : case Type::kOccupancyGrid:
1121 : 5 : header = &get<OccupancyGrid>()->header;
1122 : 5 : break;
1123 : :
1124 : 5 : case Type::kTensor:
1125 : 5 : header = &get<Tensor>()->header;
1126 : 5 : break;
1127 : :
1128 : 5 : case Type::kObjectArray:
1129 : 5 : header = &get<ObjectArray>()->header;
1130 : 5 : break;
1131 : :
1132 : 5 : case Type::kAudioFrame:
1133 : 5 : header = &get<AudioFrame>()->header;
1134 : 5 : break;
1135 : 8 : case Type::kFastBuffer:
1136 : 8 : header = &get<FastBuffer::Metadata>()->header;
1137 : 8 : break;
1138 : 1 : default:
1139 : 1 : break;
1140 : : }
1141 : :
1142 [ + + + - ]: 50 : return header != nullptr && header_value(*header, path, out);
1143 : : }
1144 : :
1145 [ + + + + : 33 : switch (type_) {
+ - - - +
+ ]
1146 : 1 : case Type::kRawData: {
1147 : 1 : const auto& message = *get<RawData>();
1148 : :
1149 [ + - - + ]: 1 : if (read_number(path, "size", message.size(), out)) {
1150 : 0 : return true;
1151 : : }
1152 : :
1153 [ + - - + ]: 1 : if (read_number(path, "reserved", reserved_[0], out)) {
1154 : 0 : return true;
1155 : : }
1156 : :
1157 [ + - ]: 1 : if (path == "data") {
1158 : 1 : out = Bytes::shallow_copy(message.data(), message.size());
1159 : 1 : return true;
1160 : : }
1161 : :
1162 : 0 : break;
1163 : : }
1164 : :
1165 : 19 : case Type::kFastBuffer: {
1166 : 19 : const auto& message = *get<FastBuffer::Metadata>();
1167 : :
1168 [ + + ]: 19 : if (path == "metadata") {
1169 : 1 : out = Bytes::shallow_copy(message.buffer.data(), message.buffer.size());
1170 : 1 : return true;
1171 : : }
1172 : :
1173 [ + - + - : 34 : return read_number(path, "size", message.size, out) || read_number(path, "protocol", message.protocol, out) ||
+ + ]
1174 [ + - + + ]: 14 : read_number(path, "storage", message.storage, out) ||
1175 [ + - + + ]: 12 : read_number(path, "memory_type", message.memory_type, out) ||
1176 [ + - + + ]: 10 : read_number(path, "metadata_size", message.buffer.size(), out) ||
1177 [ + + + - : 40 : read_number(path, "reserved", message.reserved[0], out) ||
+ + ]
1178 [ + - + + ]: 24 : read_number(path, "reserved2", message.reserved[1], out);
1179 : : }
1180 : :
1181 : 8 : case Type::kCameraFrame: {
1182 : 8 : const auto& message = *get<CameraFrame>();
1183 : :
1184 [ + - + + ]: 8 : if (read_number(path, "channel", message.channel(), out)) {
1185 : 1 : return true;
1186 : : }
1187 : :
1188 [ + - + + ]: 7 : if (read_number(path, "width", message.width(), out)) {
1189 : 2 : return true;
1190 : : }
1191 : :
1192 [ + - + + ]: 5 : if (read_number(path, "height", message.height(), out)) {
1193 : 1 : return true;
1194 : : }
1195 : :
1196 [ + - + + ]: 4 : if (read_number(path, "freq", message.freq(), out)) {
1197 : 1 : return true;
1198 : : }
1199 : :
1200 [ + - + + ]: 3 : if (read_number(path, "format", message.format(), out)) {
1201 : 1 : return true;
1202 : : }
1203 : :
1204 [ + - + + ]: 2 : if (read_number(path, "stream", message.stream(), out)) {
1205 : 1 : return true;
1206 : : }
1207 : :
1208 [ + - + - ]: 1 : if (read_number(path, "size", message.size(), out)) {
1209 : 1 : return true;
1210 : : }
1211 : :
1212 [ # # # # ]: 0 : if (read_number(path, "reserved", reserved_[0], out)) {
1213 : 0 : return true;
1214 : : }
1215 : :
1216 [ # # ]: 0 : if (path == "data") {
1217 : 0 : out = Bytes::shallow_copy(message.data(), message.size());
1218 : 0 : return true;
1219 : : }
1220 : :
1221 : 0 : break;
1222 : : }
1223 : :
1224 : 1 : case Type::kPointCloud: {
1225 : 1 : const auto& message = *get<PointCloud>();
1226 : :
1227 [ + - - + ]: 1 : if (read_number(path, "size", message.size(), out)) {
1228 : 0 : return true;
1229 : : }
1230 : :
1231 [ + - - + ]: 1 : if (read_number(path, "pack_size", message.pack_size(), out)) {
1232 : 0 : return true;
1233 : : }
1234 : :
1235 [ + - - + ]: 1 : if (read_number(path, "extent", message.get_extent(), out)) {
1236 : 0 : return true;
1237 : : }
1238 : :
1239 [ + - - + ]: 1 : if (read_number(path, "downsample", message.get_downsample(), out)) {
1240 : 0 : return true;
1241 : : }
1242 : :
1243 [ + - - + ]: 1 : if (read_number(path, "vertical", message.get_vertical(), out)) {
1244 : 0 : return true;
1245 : : }
1246 : :
1247 [ + - - + ]: 1 : if (read_number(path, "reserved_size", message.get_reserved_size(), out)) {
1248 : 0 : return true;
1249 : : }
1250 : :
1251 [ + - - + ]: 1 : if (read_number(path, "get_reserved_size", message.get_reserved_size(), out)) {
1252 : 0 : return true;
1253 : : }
1254 : :
1255 [ + - - + ]: 1 : if (read_number(path, "reserved", reserved_[0], out)) {
1256 : 0 : return true;
1257 : : }
1258 : :
1259 [ + - - + ]: 1 : if (read_number(path, "reserved2", reserved_[1], out)) {
1260 : 0 : return true;
1261 : : }
1262 : :
1263 [ + - - + ]: 1 : if (read_number(path, "reserved3", reserved_[2], out)) {
1264 : 0 : return true;
1265 : : }
1266 : :
1267 [ - + ]: 1 : if (path == "data") {
1268 : 0 : out = Bytes::shallow_copy(message.get_internal_data(), message.size() * message.pack_size());
1269 : 0 : return true;
1270 : : }
1271 : :
1272 : 1 : break;
1273 : : }
1274 : :
1275 : 2 : case Type::kProxyData: {
1276 : 2 : const auto& message = *get<ProxyData>();
1277 : :
1278 [ + - - + ]: 2 : if (read_number(path, "control_id", message.control_id(), out)) {
1279 : 0 : return true;
1280 : : }
1281 : :
1282 [ + - - + ]: 2 : if (read_number(path, "mode", message.mode(), out)) {
1283 : 0 : return true;
1284 : : }
1285 : :
1286 [ + - - + ]: 2 : if (read_number(path, "timestamp", message.timestamp(), out)) {
1287 : 0 : return true;
1288 : : }
1289 : :
1290 [ + - - + ]: 2 : if (read_number(path, "seq", message.seq(), out)) {
1291 : 0 : return true;
1292 : : }
1293 : :
1294 [ + - - + ]: 2 : if (read_number(path, "schema", message.schema(), out)) {
1295 : 0 : return true;
1296 : : }
1297 : :
1298 [ + - - + ]: 2 : if (read_number(path, "size", message.raw().size(), out)) {
1299 : 0 : return true;
1300 : : }
1301 : :
1302 [ + - - + ]: 2 : if (read_number(path, "raw.size", message.raw().size(), out)) {
1303 : 0 : return true;
1304 : : }
1305 : :
1306 [ + - - + ]: 2 : if (read_number(path, "reserved", reserved_[0], out)) {
1307 : 0 : return true;
1308 : : }
1309 : :
1310 [ + - - + ]: 2 : if (read_number(path, "reserved2", reserved_[1], out)) {
1311 : 0 : return true;
1312 : : }
1313 : :
1314 [ + - + + ]: 2 : if (read_string(path, "url", message.url(), out)) {
1315 : 1 : return true;
1316 : : }
1317 : :
1318 [ + - + - ]: 1 : if (read_string(path, "ser", message.ser(), out)) {
1319 : 1 : return true;
1320 : : }
1321 : :
1322 [ # # # # ]: 0 : if (read_string(path, "hostname", message.hostname(), out)) {
1323 : 0 : return true;
1324 : : }
1325 : :
1326 [ # # # # : 0 : if (path == "raw" || path == "data") {
# # ]
1327 : 0 : out = message.raw();
1328 : 0 : return true;
1329 : : }
1330 : :
1331 : 0 : break;
1332 : : }
1333 : :
1334 : 0 : case Type::kOccupancyGrid: {
1335 : 0 : const auto& message = *get<OccupancyGrid>();
1336 : :
1337 [ # # # # ]: 0 : if (read_string(path, "map_id", message.map_id(), out)) {
1338 : 0 : return true;
1339 : : }
1340 : :
1341 [ # # # # ]: 0 : if (read_number(path, "channel", message.channel(), out)) {
1342 : 0 : return true;
1343 : : }
1344 : :
1345 [ # # # # ]: 0 : if (read_number(path, "freq", message.freq(), out)) {
1346 : 0 : return true;
1347 : : }
1348 : :
1349 [ # # # # ]: 0 : if (read_number(path, "width", message.width(), out)) {
1350 : 0 : return true;
1351 : : }
1352 : :
1353 [ # # # # ]: 0 : if (read_number(path, "height", message.height(), out)) {
1354 : 0 : return true;
1355 : : }
1356 : :
1357 [ # # # # ]: 0 : if (read_number(path, "resolution", message.resolution(), out)) {
1358 : 0 : return true;
1359 : : }
1360 : :
1361 [ # # # # ]: 0 : if (read_number(path, "origin_x", message.origin_x(), out)) {
1362 : 0 : return true;
1363 : : }
1364 : :
1365 [ # # # # ]: 0 : if (read_number(path, "origin_y", message.origin_y(), out)) {
1366 : 0 : return true;
1367 : : }
1368 : :
1369 [ # # # # ]: 0 : if (read_number(path, "origin_z", message.origin_z(), out)) {
1370 : 0 : return true;
1371 : : }
1372 : :
1373 [ # # # # ]: 0 : if (read_number(path, "origin_yaw", message.origin_yaw(), out)) {
1374 : 0 : return true;
1375 : : }
1376 : :
1377 [ # # # # ]: 0 : if (read_number(path, "cell_type", message.cell_type(), out)) {
1378 : 0 : return true;
1379 : : }
1380 : :
1381 [ # # # # ]: 0 : if (read_number(path, "cell_size", message.cell_size(), out)) {
1382 : 0 : return true;
1383 : : }
1384 : :
1385 [ # # # # ]: 0 : if (read_number(path, "default_value", message.default_value(), out)) {
1386 : 0 : return true;
1387 : : }
1388 : :
1389 [ # # # # ]: 0 : if (read_number(path, "value_min", message.value_min(), out)) {
1390 : 0 : return true;
1391 : : }
1392 : :
1393 [ # # # # ]: 0 : if (read_number(path, "value_max", message.value_max(), out)) {
1394 : 0 : return true;
1395 : : }
1396 : :
1397 [ # # # # ]: 0 : if (read_number(path, "occupied_threshold", message.occupied_threshold(), out)) {
1398 : 0 : return true;
1399 : : }
1400 : :
1401 [ # # # # ]: 0 : if (read_number(path, "free_threshold", message.free_threshold(), out)) {
1402 : 0 : return true;
1403 : : }
1404 : :
1405 [ # # # # ]: 0 : if (read_number(path, "valid_cell_count", message.valid_cell_count(), out)) {
1406 : 0 : return true;
1407 : : }
1408 : :
1409 [ # # # # ]: 0 : if (read_number(path, "update_time_ns", message.update_time_ns(), out)) {
1410 : 0 : return true;
1411 : : }
1412 : :
1413 [ # # # # ]: 0 : if (read_number(path, "size", message.size(), out)) {
1414 : 0 : return true;
1415 : : }
1416 : :
1417 [ # # # # ]: 0 : if (read_number(path, "reserved", reserved_[0], out)) {
1418 : 0 : return true;
1419 : : }
1420 : :
1421 [ # # # # ]: 0 : if (read_number(path, "reserved2", reserved_[1], out)) {
1422 : 0 : return true;
1423 : : }
1424 : :
1425 [ # # # # ]: 0 : if (read_number(path, "reserved3", reserved_[2], out)) {
1426 : 0 : return true;
1427 : : }
1428 : :
1429 [ # # ]: 0 : if (path == "data") {
1430 : 0 : out = Bytes::shallow_copy(message.data(), message.size());
1431 : 0 : return true;
1432 : : }
1433 : :
1434 : 0 : break;
1435 : : }
1436 : :
1437 : 0 : case Type::kTensor: {
1438 : 0 : const auto& message = *get<Tensor>();
1439 : :
1440 [ # # # # ]: 0 : if (read_string(path, "name", message.name(), out)) {
1441 : 0 : return true;
1442 : : }
1443 : :
1444 [ # # # # ]: 0 : if (read_string(path, "model_id", message.model_id(), out)) {
1445 : 0 : return true;
1446 : : }
1447 : :
1448 [ # # # # ]: 0 : if (read_string(path, "layout", message.layout(), out)) {
1449 : 0 : return true;
1450 : : }
1451 : :
1452 [ # # # # ]: 0 : if (read_number(path, "dtype", message.dtype(), out)) {
1453 : 0 : return true;
1454 : : }
1455 : :
1456 [ # # # # ]: 0 : if (read_number(path, "device", message.device(), out)) {
1457 : 0 : return true;
1458 : : }
1459 : :
1460 [ # # # # ]: 0 : if (read_number(path, "rank", message.rank(), out)) {
1461 : 0 : return true;
1462 : : }
1463 : :
1464 [ # # # # ]: 0 : if (read_number(path, "num_elements", message.num_elements(), out)) {
1465 : 0 : return true;
1466 : : }
1467 : :
1468 [ # # # # ]: 0 : if (read_number(path, "element_size", message.element_size(), out)) {
1469 : 0 : return true;
1470 : : }
1471 : :
1472 [ # # # # ]: 0 : if (read_number(path, "batch_size", message.batch_size(), out)) {
1473 : 0 : return true;
1474 : : }
1475 : :
1476 [ # # # # ]: 0 : if (read_number(path, "quant_scale", message.quant_scale(), out)) {
1477 : 0 : return true;
1478 : : }
1479 : :
1480 [ # # # # ]: 0 : if (read_number(path, "quant_zero_point", message.quant_zero_point(), out)) {
1481 : 0 : return true;
1482 : : }
1483 : :
1484 [ # # # # ]: 0 : if (read_number(path, "channel", message.channel(), out)) {
1485 : 0 : return true;
1486 : : }
1487 : :
1488 [ # # # # ]: 0 : if (read_number(path, "freq", message.freq(), out)) {
1489 : 0 : return true;
1490 : : }
1491 : :
1492 [ # # # # ]: 0 : if (read_number(path, "update_time_ns", message.update_time_ns(), out)) {
1493 : 0 : return true;
1494 : : }
1495 : :
1496 [ # # # # ]: 0 : if (read_number(path, "size", message.size(), out)) {
1497 : 0 : return true;
1498 : : }
1499 : :
1500 [ # # # # ]: 0 : if (read_number(path, "reserved", reserved_[0], out)) {
1501 : 0 : return true;
1502 : : }
1503 : :
1504 [ # # # # ]: 0 : if (read_number(path, "reserved2", reserved_[1], out)) {
1505 : 0 : return true;
1506 : : }
1507 : :
1508 [ # # # # ]: 0 : if (read_number(path, "reserved3", reserved_[2], out)) {
1509 : 0 : return true;
1510 : : }
1511 : :
1512 [ # # ]: 0 : if (path == "data") {
1513 : 0 : out = Bytes::shallow_copy(message.data(), message.size());
1514 : 0 : return true;
1515 : : }
1516 : :
1517 : 0 : break;
1518 : : }
1519 : :
1520 : 0 : case Type::kObjectArray: {
1521 : 0 : const auto& message = *get<ObjectArray>();
1522 : :
1523 [ # # # # ]: 0 : if (read_string(path, "source_id", message.source_id(), out)) {
1524 : 0 : return true;
1525 : : }
1526 : :
1527 [ # # # # ]: 0 : if (read_number(path, "channel", message.channel(), out)) {
1528 : 0 : return true;
1529 : : }
1530 : :
1531 [ # # # # ]: 0 : if (read_number(path, "freq", message.freq(), out)) {
1532 : 0 : return true;
1533 : : }
1534 : :
1535 [ # # # # ]: 0 : if (read_number(path, "count", message.count(), out)) {
1536 : 0 : return true;
1537 : : }
1538 : :
1539 [ # # # # ]: 0 : if (read_number(path, "pack_size", message.pack_size(), out)) {
1540 : 0 : return true;
1541 : : }
1542 : :
1543 [ # # # # ]: 0 : if (read_number(path, "size", static_cast<uint64_t>(message.count()) * message.pack_size(), out)) {
1544 : 0 : return true;
1545 : : }
1546 : :
1547 [ # # # # ]: 0 : if (read_number(path, "update_time_ns", message.update_time_ns(), out)) {
1548 : 0 : return true;
1549 : : }
1550 : :
1551 [ # # # # ]: 0 : if (read_number(path, "reserved", reserved_[0], out)) {
1552 : 0 : return true;
1553 : : }
1554 : :
1555 [ # # # # ]: 0 : if (read_number(path, "reserved2", reserved_[1], out)) {
1556 : 0 : return true;
1557 : : }
1558 : :
1559 [ # # # # ]: 0 : if (read_number(path, "reserved3", reserved_[2], out)) {
1560 : 0 : return true;
1561 : : }
1562 : :
1563 [ # # # # ]: 0 : if (read_number(path, "reserved4", reserved_[3], out)) {
1564 : 0 : return true;
1565 : : }
1566 : :
1567 [ # # # # ]: 0 : if (read_number(path, "reserved5", reserved_[4], out)) {
1568 : 0 : return true;
1569 : : }
1570 : :
1571 [ # # ]: 0 : if (path == "data") {
1572 : 0 : out = Bytes::shallow_copy(message.data(), static_cast<size_t>(message.count()) * message.pack_size());
1573 : 0 : return true;
1574 : : }
1575 : :
1576 : 0 : break;
1577 : : }
1578 : :
1579 : 1 : case Type::kAudioFrame: {
1580 : 1 : const auto& message = *get<AudioFrame>();
1581 : :
1582 [ + - - + ]: 1 : if (read_string(path, "codec", message.codec(), out)) {
1583 : 0 : return true;
1584 : : }
1585 : :
1586 [ + - + - ]: 1 : if (read_string(path, "language", message.language(), out)) {
1587 : 1 : return true;
1588 : : }
1589 : :
1590 [ # # # # ]: 0 : if (read_number(path, "channel", message.channel(), out)) {
1591 : 0 : return true;
1592 : : }
1593 : :
1594 [ # # # # ]: 0 : if (read_number(path, "freq", message.freq(), out)) {
1595 : 0 : return true;
1596 : : }
1597 : :
1598 [ # # # # ]: 0 : if (read_number(path, "sample_rate", message.sample_rate(), out)) {
1599 : 0 : return true;
1600 : : }
1601 : :
1602 [ # # # # ]: 0 : if (read_number(path, "num_samples", message.num_samples(), out)) {
1603 : 0 : return true;
1604 : : }
1605 : :
1606 [ # # # # ]: 0 : if (read_number(path, "num_channels", message.num_channels(), out)) {
1607 : 0 : return true;
1608 : : }
1609 : :
1610 [ # # # # ]: 0 : if (read_number(path, "bit_depth", message.bit_depth(), out)) {
1611 : 0 : return true;
1612 : : }
1613 : :
1614 [ # # # # ]: 0 : if (read_number(path, "bitrate", message.bitrate(), out)) {
1615 : 0 : return true;
1616 : : }
1617 : :
1618 [ # # # # ]: 0 : if (read_number(path, "format", message.format(), out)) {
1619 : 0 : return true;
1620 : : }
1621 : :
1622 [ # # # # ]: 0 : if (read_number(path, "layout", message.layout(), out)) {
1623 : 0 : return true;
1624 : : }
1625 : :
1626 [ # # # # ]: 0 : if (read_number(path, "duration_ns", message.duration_ns(), out)) {
1627 : 0 : return true;
1628 : : }
1629 : :
1630 [ # # # # ]: 0 : if (read_number(path, "update_time_ns", message.update_time_ns(), out)) {
1631 : 0 : return true;
1632 : : }
1633 : :
1634 [ # # # # ]: 0 : if (read_number(path, "size", message.size(), out)) {
1635 : 0 : return true;
1636 : : }
1637 : :
1638 [ # # # # ]: 0 : if (read_number(path, "reserved", reserved_[0], out)) {
1639 : 0 : return true;
1640 : : }
1641 : :
1642 [ # # # # ]: 0 : if (read_number(path, "reserved2", reserved_[1], out)) {
1643 : 0 : return true;
1644 : : }
1645 : :
1646 [ # # ]: 0 : if (path == "data") {
1647 : 0 : out = Bytes::shallow_copy(message.data(), message.size());
1648 : 0 : return true;
1649 : : }
1650 : :
1651 : 0 : break;
1652 : : }
1653 : :
1654 : 1 : default:
1655 : 1 : break;
1656 : : }
1657 : :
1658 : 2 : return false;
1659 : : }
1660 : :
1661 : 38 : bool MessageParser::element_value(std::string_view collection, size_t index, std::string_view field, Value& out) const {
1662 [ + + ]: 38 : if VUNLIKELY (index >= collection_size(collection)) {
1663 : 3 : return false;
1664 : : }
1665 : :
1666 [ + + ]: 35 : if (type_ == Type::kTensor) {
1667 : 18 : const auto& message = *get<Tensor>();
1668 : :
1669 [ + + ]: 18 : if (collection == "shape") {
1670 [ + + + - : 3 : if VUNLIKELY (!field.empty() && field != "value") {
+ + ]
1671 : 2 : return false;
1672 : : }
1673 : :
1674 : 1 : return store_number(message.shape_at(static_cast<uint8_t>(index)), out);
1675 : : }
1676 : :
1677 [ - + ]: 15 : if (collection == "strides") {
1678 [ # # # # : 0 : if VUNLIKELY (!field.empty() && field != "value") {
# # ]
1679 : 0 : return false;
1680 : : }
1681 : :
1682 : 0 : return store_number(message.stride_at(static_cast<uint8_t>(index)), out);
1683 : : }
1684 : :
1685 [ + - - + : 15 : if (collection != "data" || (!field.empty() && field != "value")) {
- - - + ]
1686 : 0 : return false;
1687 : : }
1688 : :
1689 : 15 : const uint8_t* data = message.data() + index * message.element_size();
1690 [ + + + + : 15 : switch (message.dtype()) {
+ + + + +
+ + + +
- ]
1691 : 1 : case Tensor::kBool:
1692 : 1 : return store_number(read_unaligned<uint8_t>(data) != 0, out);
1693 : 1 : case Tensor::kInt8:
1694 : 1 : return store_number(read_unaligned<int8_t>(data), out);
1695 : 1 : case Tensor::kUint8:
1696 : 1 : return store_number(read_unaligned<uint8_t>(data), out);
1697 : 1 : case Tensor::kInt16:
1698 : 1 : return store_number(read_unaligned<int16_t>(data), out);
1699 : 1 : case Tensor::kUint16:
1700 : 1 : return store_number(read_unaligned<uint16_t>(data), out);
1701 : 1 : case Tensor::kInt32:
1702 : 1 : return store_number(read_unaligned<int32_t>(data), out);
1703 : 1 : case Tensor::kUint32:
1704 : 1 : return store_number(read_unaligned<uint32_t>(data), out);
1705 : 3 : case Tensor::kInt64:
1706 : 3 : return store_number(read_unaligned<int64_t>(data), out);
1707 : 1 : case Tensor::kUint64:
1708 : 1 : return store_number(read_unaligned<uint64_t>(data), out);
1709 : 1 : case Tensor::kFloat16:
1710 : 1 : out = half_to_double(read_unaligned<uint16_t>(data));
1711 : 1 : return true;
1712 : 1 : case Tensor::kBfloat16: {
1713 : 1 : const uint32_t bits = static_cast<uint32_t>(read_unaligned<uint16_t>(data)) << 16U;
1714 : 1 : out = static_cast<double>(bits_to_float(bits));
1715 : 1 : return true;
1716 : : }
1717 : :
1718 : 1 : case Tensor::kFloat32:
1719 : 1 : return store_number(read_unaligned<float>(data), out);
1720 : 1 : case Tensor::kFloat64:
1721 : 1 : return store_number(read_unaligned<double>(data), out);
1722 : 0 : default:
1723 : 0 : return false;
1724 : : }
1725 : : }
1726 : :
1727 [ + + + - : 17 : if (type_ == Type::kOccupancyGrid && collection == "data") {
+ + ]
1728 : 1 : const auto& message = *get<OccupancyGrid>();
1729 : :
1730 [ - + - - : 1 : if VUNLIKELY (!field.empty() && field != "value") {
- + ]
1731 : 0 : return false;
1732 : : }
1733 : :
1734 : 1 : const uint8_t* data = message.data() + index * message.cell_size();
1735 : :
1736 [ - - + - : 1 : switch (message.cell_type()) {
- ]
1737 : 0 : case OccupancyGrid::kCellInt8:
1738 : 0 : return store_number(read_unaligned<int8_t>(data), out);
1739 : 0 : case OccupancyGrid::kCellUint8:
1740 : 0 : return store_number(read_unaligned<uint8_t>(data), out);
1741 : 1 : case OccupancyGrid::kCellUint16:
1742 : 1 : return store_number(read_unaligned<uint16_t>(data), out);
1743 : 0 : case OccupancyGrid::kCellFloat32:
1744 : 0 : return store_number(read_unaligned<float>(data), out);
1745 : 0 : default:
1746 : 0 : return false;
1747 : : }
1748 : : }
1749 : :
1750 [ + + + - : 16 : if (type_ == Type::kPointCloud && collection == "data") {
+ + ]
1751 [ - + ]: 10 : if VUNLIKELY (point_field_buckets_.empty()) {
1752 : 0 : return false;
1753 : : }
1754 : :
1755 : 10 : constexpr auto kNoField = static_cast<size_t>(-1);
1756 : 10 : size_t field_index = point_field_buckets_[hash_point_field(field) & (point_field_buckets_.size() - 1)];
1757 : :
1758 [ + + - + : 10 : while (field_index != kNoField && point_fields_[field_index].name != field) {
- + ]
1759 : 0 : field_index = point_field_next_[field_index];
1760 : : }
1761 : :
1762 [ + + ]: 10 : if VUNLIKELY (field_index == kNoField) {
1763 : 1 : return false;
1764 : : }
1765 : :
1766 : 9 : return point_value(index, point_fields_[field_index], out);
1767 : : }
1768 : :
1769 [ + + + - : 6 : if (type_ == Type::kAudioFrame && collection == "data") {
+ + ]
1770 : 2 : const auto& message = *get<AudioFrame>();
1771 : :
1772 [ + - - + : 2 : if VUNLIKELY (!field.empty() && field != "value") {
- + ]
1773 : 0 : return false;
1774 : : }
1775 : :
1776 : 2 : const size_t element_size = audio_element_size(message.format());
1777 : 2 : const uint8_t* data = message.data() + index * element_size;
1778 : :
1779 [ - - + - : 2 : switch (message.format()) {
- - ]
1780 : 0 : case AudioFrame::kFormatPcmU8:
1781 : 0 : return store_number(read_unaligned<uint8_t>(data), out);
1782 : 0 : case AudioFrame::kFormatPcmS16:
1783 : 0 : return store_number(read_unaligned<int16_t>(data), out);
1784 : 2 : case AudioFrame::kFormatPcmS24: {
1785 : 2 : const int32_t magnitude = static_cast<int32_t>(read_unaligned<uint8_t>(data)) |
1786 : 2 : (static_cast<int32_t>(read_unaligned<uint8_t>(data + 1)) << 8U) |
1787 : 2 : (static_cast<int32_t>(read_unaligned<uint8_t>(data + 2)) << 16U);
1788 : 2 : return store_number((magnitude ^ 0x800000) - 0x800000, out);
1789 : : }
1790 : :
1791 : 0 : case AudioFrame::kFormatPcmS32:
1792 : 0 : return store_number(read_unaligned<int32_t>(data), out);
1793 : 0 : case AudioFrame::kFormatPcmF32:
1794 : 0 : return store_number(read_unaligned<float>(data), out);
1795 : 0 : default:
1796 : 0 : return false;
1797 : : }
1798 : : }
1799 : :
1800 [ + - - + : 4 : if (type_ != Type::kObjectArray || collection != "data") {
- + ]
1801 : 0 : return false;
1802 : : }
1803 : :
1804 : 4 : const auto* object = get<ObjectArray>()->objects(static_cast<uint32_t>(index));
1805 : :
1806 [ - + ]: 4 : if VUNLIKELY (object == nullptr) {
1807 : 0 : return false;
1808 : : }
1809 : :
1810 [ + + ]: 4 : if (field == "label") {
1811 [ + - ]: 1 : out = std::string(object->label, strnlen(object->label, sizeof(object->label)));
1812 : 1 : return true;
1813 : : }
1814 : :
1815 [ + - + + ]: 3 : if (read_number(field, "yaw", object->yaw, out)) {
1816 : 1 : return true;
1817 : : }
1818 : :
1819 [ + - - + ]: 2 : if (read_number(field, "yaw_rate", object->yaw_rate, out)) {
1820 : 0 : return true;
1821 : : }
1822 : :
1823 [ + - - + ]: 2 : if (read_number(field, "score", object->score, out)) {
1824 : 0 : return true;
1825 : : }
1826 : :
1827 [ + - - + ]: 2 : if (read_number(field, "existence_probability", object->existence_probability, out)) {
1828 : 0 : return true;
1829 : : }
1830 : :
1831 [ + - - + ]: 2 : if (read_number(field, "class_id", object->class_id, out)) {
1832 : 0 : return true;
1833 : : }
1834 : :
1835 [ + - - + ]: 2 : if (read_number(field, "track_id", object->track_id, out)) {
1836 : 0 : return true;
1837 : : }
1838 : :
1839 [ + - - + ]: 2 : if (read_number(field, "age", object->age, out)) {
1840 : 0 : return true;
1841 : : }
1842 : :
1843 [ + - - + ]: 2 : if (read_number(field, "num_observations", object->num_observations, out)) {
1844 : 0 : return true;
1845 : : }
1846 : :
1847 [ + - - + ]: 2 : if (read_number(field, "motion_state", object->motion_state, out)) {
1848 : 0 : return true;
1849 : : }
1850 : :
1851 [ + - - + ]: 2 : if (read_number(field, "source_type", object->source_type, out)) {
1852 : 0 : return true;
1853 : : }
1854 : :
1855 [ + - - + ]: 2 : if (read_number(field, "subtype_id", object->subtype_id, out)) {
1856 : 0 : return true;
1857 : : }
1858 : :
1859 [ + - - + ]: 2 : if (read_number(field, "reserved_buf", object->reserved_buf, out)) {
1860 : 0 : return true;
1861 : : }
1862 : :
1863 [ + + ]: 2 : if (field == "reserved") {
1864 : 1 : return store_number(object->reserved_buf, out);
1865 : : }
1866 : :
1867 : : static constexpr std::pair<std::string_view, uint8_t> kVectorFields[] = {
1868 : : {"position", 3}, {"size", 3}, {"velocity", 3}, {"acceleration", 3}, {"position_covariance", 6}};
1869 : :
1870 [ + + ]: 6 : for (const auto& [name, count] : kVectorFields) {
1871 : 5 : size_t component = 0;
1872 : 5 : std::string_view tail;
1873 : :
1874 [ - + - - : 5 : if (!parse_index(field, name, component, tail) || !tail.empty() || component >= count) {
- - + - ]
1875 : 5 : continue;
1876 : : }
1877 : :
1878 [ # # ]: 0 : if (name == "position") {
1879 : 0 : return store_number(object->position[component], out);
1880 : : }
1881 : :
1882 [ # # ]: 0 : if (name == "size") {
1883 : 0 : return store_number(object->size[component], out);
1884 : : }
1885 : :
1886 [ # # ]: 0 : if (name == "velocity") {
1887 : 0 : return store_number(object->velocity[component], out);
1888 : : }
1889 : :
1890 [ # # ]: 0 : if (name == "acceleration") {
1891 : 0 : return store_number(object->acceleration[component], out);
1892 : : }
1893 : :
1894 : 0 : return store_number(object->position_covariance[component], out);
1895 : : }
1896 : :
1897 : : static constexpr std::pair<std::string_view, std::pair<std::string_view, size_t>> kAliases[] = {
1898 : : {"position_x", {"position", 0}},
1899 : : {"position_y", {"position", 1}},
1900 : : {"position_z", {"position", 2}},
1901 : : {"size_x", {"size", 0}},
1902 : : {"size_y", {"size", 1}},
1903 : : {"size_z", {"size", 2}},
1904 : : {"velocity_x", {"velocity", 0}},
1905 : : {"velocity_y", {"velocity", 1}},
1906 : : {"velocity_z", {"velocity", 2}},
1907 : : {"acceleration_x", {"acceleration", 0}},
1908 : : {"acceleration_y", {"acceleration", 1}},
1909 : : {"acceleration_z", {"acceleration", 2}}};
1910 : :
1911 [ + - ]: 1 : for (const auto& [alias, target] : kAliases) {
1912 [ - + ]: 1 : if (field != alias) {
1913 : 0 : continue;
1914 : : }
1915 : :
1916 [ + - ]: 1 : if (target.first == "position") {
1917 : 1 : return store_number(object->position[target.second], out);
1918 : : }
1919 : :
1920 [ # # ]: 0 : if (target.first == "size") {
1921 : 0 : return store_number(object->size[target.second], out);
1922 : : }
1923 : :
1924 [ # # ]: 0 : if (target.first == "velocity") {
1925 : 0 : return store_number(object->velocity[target.second], out);
1926 : : }
1927 : :
1928 : 0 : return store_number(object->acceleration[target.second], out);
1929 : : }
1930 : :
1931 : 0 : return false;
1932 : : }
1933 : :
1934 : 15 : bool MessageParser::point_value(size_t index, const Field& field, Value& out) const {
1935 : 15 : const auto& message = *get<PointCloud>();
1936 : :
1937 [ + + + - : 15 : if (message.get_extent() != 0 && field.element_index < 3) {
+ + ]
1938 : 5 : return store_number(message.get_value<double>(index, field.byte_offset), out);
1939 : : }
1940 : :
1941 [ - - - - : 10 : switch (field.native_type) {
- - - - -
+ - + - ]
1942 : 0 : case PointCloud::kBoolType:
1943 : 0 : return store_number(message.get_value<bool>(index, field.byte_offset), out);
1944 : 0 : case PointCloud::kInt8Type:
1945 : 0 : return store_number(message.get_value<int8_t>(index, field.byte_offset), out);
1946 : 0 : case PointCloud::kUint8Type:
1947 : 0 : return store_number(message.get_value<uint8_t>(index, field.byte_offset), out);
1948 : 0 : case PointCloud::kInt16Type:
1949 : 0 : return store_number(message.get_value<int16_t>(index, field.byte_offset), out);
1950 : 0 : case PointCloud::kUint16Type:
1951 : 0 : return store_number(message.get_value<uint16_t>(index, field.byte_offset), out);
1952 : 0 : case PointCloud::kInt32Type:
1953 : 0 : return store_number(message.get_value<int32_t>(index, field.byte_offset), out);
1954 : 0 : case PointCloud::kUint32Type:
1955 : 0 : return store_number(message.get_value<uint32_t>(index, field.byte_offset), out);
1956 : 0 : case PointCloud::kInt64Type:
1957 : 0 : return store_number(message.get_value<int64_t>(index, field.byte_offset), out);
1958 : 0 : case PointCloud::kUint64Type:
1959 : 0 : return store_number(message.get_value<uint64_t>(index, field.byte_offset), out);
1960 : 2 : case PointCloud::kFloatType:
1961 : 2 : return store_number(message.get_value<float>(index, field.byte_offset), out);
1962 : 0 : case PointCloud::kDoubleType:
1963 : 0 : return store_number(message.get_value<double>(index, field.byte_offset), out);
1964 : 8 : case PointCloud::kUnknownType:
1965 : 8 : break;
1966 : 0 : default:
1967 : 0 : return false;
1968 : : }
1969 : :
1970 [ + + + + : 8 : switch (field.storage_size) {
+ ]
1971 : 2 : case sizeof(uint8_t):
1972 : 2 : return store_number(message.get_value<uint8_t>(index, field.byte_offset), out);
1973 : 1 : case sizeof(int16_t):
1974 : 1 : return store_number(message.get_value<int16_t>(index, field.byte_offset), out);
1975 : 1 : case sizeof(float):
1976 : 1 : return store_number(message.get_value<float>(index, field.byte_offset), out);
1977 : 1 : case sizeof(double):
1978 : 1 : return store_number(message.get_value<double>(index, field.byte_offset), out);
1979 : 3 : default:
1980 : 3 : return false;
1981 : : }
1982 : : }
1983 : :
1984 : 3 : std::string format_message(const MessageParser& parser, const MessageFormatOptions& options, bool* truncated) {
1985 [ - + ]: 3 : if (truncated != nullptr) {
1986 : 0 : *truncated = false;
1987 : : }
1988 : :
1989 : 3 : std::string result;
1990 : :
1991 [ - + ]: 3 : if VUNLIKELY (!parser.valid()) {
1992 : 0 : return result;
1993 : : }
1994 : :
1995 [ + - ]: 3 : const auto fields = parser.fields();
1996 : 3 : const bool is_point_cloud = parser.type() == MessageParser::Type::kPointCloud;
1997 : 3 : const bool is_tensor = parser.type() == MessageParser::Type::kTensor;
1998 : :
1999 : 3 : std::string header;
2000 : :
2001 : 3 : size_t index = 0;
2002 : :
2003 [ + - ]: 18 : for (; index < fields.size(); ++index) {
2004 : 18 : const auto& field = fields[index];
2005 : :
2006 [ + + ]: 18 : if (!Helpers::has_startwith(field.name, "header.")) {
2007 : 3 : break;
2008 : : }
2009 : :
2010 [ + + ]: 15 : if (field.is_reserved) {
2011 : 3 : continue;
2012 : : }
2013 : :
2014 [ + - + - : 24 : header += " " + field.name.substr(std::string_view("header.").size()) + ": " +
+ - + - ]
2015 [ + - + - : 36 : format_scalar(parser, field, options) + "\n";
+ - ]
2016 : : }
2017 : :
2018 [ + - ]: 3 : if (!header.empty()) {
2019 [ + - ]: 3 : result += "header {\n";
2020 [ + - ]: 3 : result += header;
2021 [ + - ]: 3 : result += "}\n";
2022 : : }
2023 : :
2024 [ - + ]: 3 : if (is_point_cloud) {
2025 [ # # # # ]: 0 : result += format_pointcloud_protocol(parser);
2026 : : }
2027 : :
2028 [ + + ]: 28 : for (; index < fields.size(); ++index) {
2029 : 25 : const auto& field = fields[index];
2030 : :
2031 [ + + ]: 25 : if (field.is_reserved) {
2032 : 5 : continue;
2033 : : }
2034 : :
2035 [ + + ]: 20 : if (field.type == MessageParser::ValueType::kBytes) {
2036 [ - + ]: 3 : if (is_tensor) {
2037 [ # # # # ]: 0 : result += format_tensor_shape(parser);
2038 : : }
2039 : :
2040 [ - + ]: 3 : if (is_point_cloud) {
2041 [ # # ]: 0 : if (options.expand_arrays) {
2042 [ # # # # ]: 0 : result += format_pointcloud_points(parser, options, truncated);
2043 : : }
2044 : : } else {
2045 [ + - + - ]: 3 : result += field.name + ": {...}\n";
2046 : : }
2047 : :
2048 : 3 : continue;
2049 : : }
2050 : :
2051 [ + - + - : 17 : result += field.name + ": " + format_scalar(parser, field, options) + "\n";
+ - + - +
- ]
2052 : : }
2053 : :
2054 : 3 : return result;
2055 : 3 : }
2056 : :
2057 : : } // namespace zerocopy
2058 : :
2059 : : } // namespace vlink
|