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 "./zerocopy/point_cloud.h"
25 : :
26 : : #include <tsl/robin_set.h>
27 : :
28 : : #include <array>
29 : : #include <limits>
30 : : #include <sstream>
31 : : #include <string>
32 : : #include <utility>
33 : : #include <vector>
34 : :
35 : : namespace vlink {
36 : :
37 : : namespace zerocopy {
38 : :
39 : 13 : static void pc_pack_to_vertical(uint8_t* dst, const uint8_t* src, size_t count, uint16_t pack, const uint16_t* offsets,
40 : : const uint8_t* sizes, size_t field_count) noexcept {
41 : 13 : size_t out_pos = 0;
42 : :
43 [ + + ]: 56 : for (size_t f = 0; f < field_count; ++f) {
44 : 43 : uint16_t field_offset = offsets[f];
45 : 43 : uint8_t field_size = sizes[f];
46 : :
47 [ + + ]: 201 : for (size_t p = 0; p < count; ++p) {
48 : 158 : std::memcpy(dst + out_pos, src + (p * pack) + field_offset, field_size);
49 : :
50 : 158 : out_pos += field_size;
51 : : }
52 : : }
53 : 13 : }
54 : :
55 : 11 : static void pc_unpack_from_vertical(uint8_t* dst, const uint8_t* src, size_t count, uint16_t pack,
56 : : const uint16_t* offsets, const uint8_t* sizes, size_t field_count) noexcept {
57 : 11 : size_t in_pos = 0;
58 : :
59 [ + + ]: 48 : for (size_t f = 0; f < field_count; ++f) {
60 : 37 : uint16_t field_offset = offsets[f];
61 : 37 : uint8_t field_size = sizes[f];
62 : :
63 [ + + ]: 183 : for (size_t p = 0; p < count; ++p) {
64 : 146 : std::memcpy(dst + (p * pack) + field_offset, src + in_pos, field_size);
65 : :
66 : 146 : in_pos += field_size;
67 : : }
68 : : }
69 : 11 : }
70 : :
71 : 131136 : static int32_t pc_floor_div(int32_t value, int32_t divisor) noexcept {
72 : 131136 : int32_t q = value / divisor;
73 : 131136 : int32_t r = value % divisor;
74 : :
75 [ + + + + ]: 131136 : if (r != 0 && r < 0) {
76 : 32515 : --q;
77 : : }
78 : :
79 : 131136 : return q;
80 : : }
81 : :
82 : 131090 : static uint64_t pc_voxel_key(int32_t fx, int32_t fy, int32_t fz) noexcept {
83 : : static constexpr uint64_t kBias = static_cast<uint64_t>(1) << 20;
84 : : static constexpr uint64_t kMask = (static_cast<uint64_t>(1) << 21) - 1;
85 : :
86 : 131090 : uint64_t kx = (static_cast<uint64_t>(static_cast<int64_t>(fx) + static_cast<int64_t>(kBias))) & kMask;
87 : 131090 : uint64_t ky = (static_cast<uint64_t>(static_cast<int64_t>(fy) + static_cast<int64_t>(kBias))) & kMask;
88 : 131090 : uint64_t kz = (static_cast<uint64_t>(static_cast<int64_t>(fz) + static_cast<int64_t>(kBias))) & kMask;
89 : :
90 : 131090 : return (kx << 42) | (ky << 21) | kz;
91 : : }
92 : :
93 : : struct PcVoxelKeyHash {
94 : 131090 : size_t operator()(uint64_t key) const noexcept {
95 : 131090 : key ^= key >> 33;
96 : 131090 : key *= 0xFF51AFD7ED558CCDULL;
97 : 131090 : key ^= key >> 33;
98 : 131090 : key *= 0xC4CEB9FE1A85EC53ULL;
99 : 131090 : key ^= key >> 33;
100 : :
101 : 131090 : return static_cast<size_t>(key);
102 : : }
103 : : };
104 : :
105 : : static constexpr size_t kVoxelLutSize = 65536;
106 : :
107 : : // PointCloud::Vector3f
108 : 31 : PointCloud::Vector3f::Vector3f() noexcept {
109 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
110 : : #ifndef __ANDROID__
111 : : #warning "[PointCloud::Vector3f] No support for 32-bit architecture."
112 : : #endif
113 : : #else
114 : : static_assert(sizeof(Vector3f) == 12, "Sizeof must be 12 bytes.");
115 : : #endif
116 : 31 : }
117 : :
118 : 3 : PointCloud::Vector3f::Vector3f(float _x, float _y, float _z) noexcept : x(_x), y(_y), z(_z) {}
119 : :
120 : 0 : std::ostream& operator<<(std::ostream& ostream, const PointCloud::Vector3f& v3f) noexcept {
121 : 0 : ostream << "(" << v3f.x << ", " << v3f.y << ", " << v3f.z << ")";
122 : :
123 : 0 : return ostream;
124 : : }
125 : :
126 : : // PointCloud::Vector3d
127 : 4 : PointCloud::Vector3d::Vector3d() noexcept {
128 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
129 : : #ifndef __ANDROID__
130 : : #warning "[PointCloud::Vector3d] No support for 32-bit architecture."
131 : : #endif
132 : : #else
133 : : static_assert(sizeof(Vector3d) == 24, "Sizeof must be 24 bytes.");
134 : : #endif
135 : 4 : }
136 : :
137 : 3 : PointCloud::Vector3d::Vector3d(double _x, double _y, double _z) noexcept : x(_x), y(_y), z(_z) {}
138 : :
139 : 0 : std::ostream& operator<<(std::ostream& ostream, const PointCloud::Vector3d& v3d) noexcept {
140 : 0 : ostream << "(" << v3d.x << ", " << v3d.y << ", " << v3d.z << ")";
141 : :
142 : 0 : return ostream;
143 : : }
144 : :
145 : : // PointCloud
146 : 233 : PointCloud::PointCloud() noexcept {
147 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
148 : : #ifndef __ANDROID__
149 : : #warning "[PointCloud] No support for 32-bit architecture."
150 : : #endif
151 : : #else
152 : : static_assert(sizeof(PointCloud) == 256, "Sizeof must be 256 bytes.");
153 : : #endif
154 : 233 : }
155 : :
156 : 244 : PointCloud::~PointCloud() noexcept {
157 [ + + + - : 244 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
158 : 157 : Bytes::bytes_free(data_, capacity_);
159 : : }
160 : 244 : }
161 : :
162 : 2 : PointCloud::PointCloud(const PointCloud& target) noexcept { deep_copy(target); }
163 : :
164 : 9 : PointCloud::PointCloud(PointCloud&& target) noexcept { move_copy(target); }
165 : :
166 : 2 : PointCloud& PointCloud::operator=(const PointCloud& target) noexcept {
167 [ + + ]: 2 : if VUNLIKELY (this == &target) {
168 : 1 : return *this;
169 : : }
170 : :
171 : 1 : deep_copy(target);
172 : :
173 : 1 : return *this;
174 : : }
175 : :
176 : 5 : PointCloud& PointCloud::operator=(PointCloud&& target) noexcept {
177 [ + + ]: 5 : if VUNLIKELY (this == &target) {
178 : 1 : return *this;
179 : : }
180 : :
181 : 4 : move_copy(target);
182 : :
183 : 4 : return *this;
184 : : }
185 : :
186 : 44 : bool PointCloud::operator<<(const Bytes& bytes) noexcept {
187 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
188 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
189 : : // static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
190 : :
191 [ + + ]: 44 : if VUNLIKELY (bytes.empty()) {
192 : 2 : return false;
193 : : }
194 : :
195 [ + + ]: 42 : if VUNLIKELY (!check_valid(bytes)) {
196 : 5 : return false;
197 : : }
198 : :
199 : 37 : uint32_t wire_version = 0;
200 : 37 : std::memcpy(&wire_version, bytes.data() + kMagicNumberBeginSize, kVersionSize);
201 : :
202 [ - + ]: 37 : if VUNLIKELY (version_major(wire_version) != version_major(kWireVersion)) {
203 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
204 : : }
205 : :
206 [ + + + - : 37 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
207 : 1 : Bytes::bytes_free(data_, capacity_);
208 : : }
209 : :
210 : : #if defined(__GNUC__) && !defined(__clang__)
211 : : #pragma GCC diagnostic push
212 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
213 : : #if __GNUC__ >= 11
214 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
215 : : #endif
216 : : #endif
217 : :
218 : 37 : auto* target_ptr = reinterpret_cast<uint8_t*>(this);
219 : :
220 : 37 : std::memcpy(target_ptr, bytes.data() + kMagicNumberBeginSize + kVersionSize, sizeof(PointCloud));
221 : :
222 : : #if defined(__GNUC__) && !defined(__clang__)
223 : : #pragma GCC diagnostic pop
224 : : #endif
225 : :
226 : 37 : is_owner_ = false;
227 : 37 : index_ = 0;
228 : 37 : data_ = const_cast<uint8_t*>(bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud));
229 : 37 : capacity_ = 0;
230 : :
231 : 37 : uint8_t vertical_raw = 0;
232 : 37 : std::memcpy(&vertical_raw, &vertical_, sizeof(vertical_raw));
233 : :
234 [ + + ]: 37 : if VUNLIKELY (vertical_raw > 1) {
235 : 1 : clear(true);
236 : :
237 : 1 : return false;
238 : : }
239 : :
240 : 36 : vertical_ = (vertical_raw != 0);
241 : :
242 [ + + ]: 36 : if (extent_ == 0) {
243 : 32 : downsample_ = 0;
244 : : }
245 : :
246 : : static constexpr size_t kSerializedOverhead =
247 : : kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud) + sizeof(kMagicNumberEnd);
248 : :
249 [ + - + + : 36 : if VUNLIKELY (pack_size_ != 0 && size_ > (std::numeric_limits<size_t>::max() - kSerializedOverhead) / pack_size_) {
+ + ]
250 : 1 : clear(true);
251 : :
252 : 1 : return false;
253 : : }
254 : :
255 [ + + ]: 35 : if VUNLIKELY (bytes.size() != get_serialized_size()) {
256 : 2 : clear(true);
257 : :
258 : 2 : return false;
259 : : }
260 : :
261 : 33 : std::array<uint16_t, 16> field_offsets{};
262 : 33 : std::array<uint8_t, 16> field_sizes{};
263 : 33 : size_t field_count = 0;
264 : :
265 [ + - ]: 33 : if VLIKELY (pack_size_ != 0) {
266 : 33 : uint16_t field_offset = 0;
267 : 33 : bool leading_zero = true;
268 : :
269 [ + + ]: 561 : for (int i = 15; i >= 0; --i) {
270 : 528 : uint8_t field_size = (protocol_.size_num >> (i * 4)) & 0xF;
271 : :
272 [ + + + + ]: 528 : if (leading_zero && field_size == 0) {
273 : 418 : continue;
274 : : }
275 : :
276 : 110 : leading_zero = false;
277 : 110 : field_offsets[field_count] = field_offset;
278 : 110 : field_sizes[field_count] = field_size;
279 : 110 : ++field_count;
280 : 110 : field_offset += field_size;
281 : : }
282 : :
283 [ - + ]: 33 : if VUNLIKELY (field_offset != pack_size_) {
284 : : clear(true); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
285 : :
286 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
287 : : }
288 : : }
289 : :
290 [ + + + - : 33 : if (vertical_ && size_ != 0 && pack_size_ != 0) {
+ - ]
291 : 11 : const uint8_t* payload = data_;
292 : :
293 : 11 : capacity_ = size_ * pack_size_;
294 : 11 : data_ = Bytes::bytes_malloc(capacity_);
295 : :
296 [ - + ]: 11 : if VUNLIKELY (!data_) {
297 : : clear(true); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
298 : :
299 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
300 : : }
301 : :
302 : 11 : is_owner_ = true;
303 : 11 : index_ = capacity_;
304 : :
305 : 11 : pc_unpack_from_vertical(data_, payload, size_, pack_size_, field_offsets.data(), field_sizes.data(), field_count);
306 : : }
307 : :
308 : 33 : return true;
309 : : }
310 : :
311 : 51 : bool PointCloud::operator>>(Bytes& bytes) const noexcept {
312 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
313 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
314 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
315 : :
316 [ + + + - : 78 : if (vertical_ && data_ != nullptr && size_ != 0 && pack_size_ != 0 &&
+ - + - +
+ + + ]
317 [ - + ]: 27 : (bytes.is_owner() || bytes.size() == get_serialized_size())) {
318 [ + - + + ]: 3 : const bool releases_output = bytes.is_owner() && bytes.size() != get_serialized_size();
319 : 3 : const auto source = reinterpret_cast<uintptr_t>(data_);
320 [ + + ]: 3 : const auto output = reinterpret_cast<uintptr_t>(releases_output ? bytes.real_data() : bytes.data());
321 [ + + ]: 3 : const size_t output_size = releases_output ? bytes.capacity() + bytes.offset() : bytes.size();
322 : :
323 [ + - + + : 3 : if VUNLIKELY ((source >= output && source - output < output_size) ||
+ + - + -
- - + +
+ ]
324 : : (output > source && output - source < size_ * pack_size_)) {
325 : 2 : return false;
326 : : }
327 : : }
328 : :
329 [ + + + + : 49 : if (bytes.empty() || bytes.size() != get_serialized_size()) {
+ + ]
330 : 45 : bytes = Bytes::create(get_serialized_size());
331 : :
332 [ - + ]: 45 : if VUNLIKELY (bytes.empty()) {
333 : 0 : return false;
334 : : }
335 : : }
336 : :
337 : 49 : std::memcpy(bytes.data(), &kMagicNumberBegin, kMagicNumberBeginSize);
338 : :
339 : 49 : std::memcpy(bytes.data() + kMagicNumberBeginSize, &kWireVersion, kVersionSize);
340 : :
341 : : // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
342 : 49 : std::memcpy(bytes.data() + kMagicNumberBeginSize + kVersionSize, this, sizeof(PointCloud));
343 : :
344 : 49 : const auto data_offset = reinterpret_cast<const uint8_t*>(&data_) - reinterpret_cast<const uint8_t*>(this);
345 : 49 : const size_t data_pointer_size = sizeof(data_);
346 : 49 : std::memset(bytes.data() + kMagicNumberBeginSize + kVersionSize + data_offset, 0, data_pointer_size);
347 : :
348 [ + - + + : 49 : if VLIKELY (data_ != nullptr && size_ != 0 && pack_size_ != 0) {
+ + + - +
+ ]
349 : 48 : uint8_t* payload = bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud);
350 : :
351 [ + + ]: 48 : if (vertical_) {
352 : 13 : std::array<uint16_t, 16> field_offsets{};
353 : 13 : std::array<uint8_t, 16> field_sizes{};
354 : 13 : size_t field_count = 0;
355 : 13 : uint16_t field_offset = 0;
356 : 13 : bool leading_zero = true;
357 : :
358 [ + + ]: 221 : for (int i = 15; i >= 0; --i) {
359 : 208 : uint8_t field_size = (protocol_.size_num >> (i * 4)) & 0xF;
360 : :
361 [ + + + + ]: 208 : if (leading_zero && field_size == 0) {
362 : 165 : continue;
363 : : }
364 : :
365 : 43 : leading_zero = false;
366 : 43 : field_offsets[field_count] = field_offset;
367 : 43 : field_sizes[field_count] = field_size;
368 : 43 : ++field_count;
369 : :
370 : 43 : field_offset += field_size;
371 : : }
372 : :
373 : 13 : pc_pack_to_vertical(payload, data_, size_, pack_size_, field_offsets.data(), field_sizes.data(), field_count);
374 : : } else {
375 : 35 : std::memcpy(payload, data_, size_ * pack_size_);
376 : : }
377 : : }
378 : :
379 : 49 : std::memcpy(bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud) + (size_ * pack_size_),
380 : : &kMagicNumberEnd, kMagicNumberEndSize);
381 : :
382 : 49 : return true;
383 : : }
384 : :
385 : 52 : bool PointCloud::check_valid(const Bytes& bytes) noexcept {
386 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
387 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
388 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
389 : :
390 [ + + ]: 52 : if VUNLIKELY (bytes.size() < kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud) + kMagicNumberEndSize) {
391 : 1 : return false;
392 : : }
393 : :
394 : 51 : uint32_t check_magic = 0;
395 : :
396 : 51 : std::memcpy(&check_magic, bytes.begin(), kMagicNumberBeginSize);
397 : :
398 [ + + ]: 51 : if VUNLIKELY (check_magic != kMagicNumberBegin) {
399 : 2 : return false;
400 : : }
401 : :
402 : 49 : uint32_t wire_version = 0;
403 : 49 : std::memcpy(&wire_version, bytes.data() + kMagicNumberBeginSize, kVersionSize);
404 : :
405 [ + + ]: 49 : if VUNLIKELY (version_major(wire_version) != version_major(kWireVersion)) {
406 : 2 : return false;
407 : : }
408 : :
409 : 47 : std::memcpy(&check_magic, bytes.end() - kMagicNumberEndSize, kMagicNumberEndSize);
410 : :
411 [ + + ]: 47 : if VUNLIKELY (check_magic != kMagicNumberEnd) {
412 : 4 : return false;
413 : : }
414 : :
415 : 43 : return true;
416 : : }
417 : :
418 [ + + + - : 14 : bool PointCloud::is_valid() const noexcept { return data_ != nullptr && size_ != 0 && pack_size_ != 0; }
+ - ]
419 : :
420 : 33 : bool PointCloud::shallow_copy(const PointCloud& target) noexcept {
421 [ + + ]: 33 : if VUNLIKELY (this == &target) {
422 : 3 : return false;
423 : : }
424 : :
425 [ + + + - : 30 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
426 : 4 : const auto current = reinterpret_cast<uintptr_t>(data_);
427 : 4 : const auto source = reinterpret_cast<uintptr_t>(target.data_);
428 : :
429 [ + + + + : 4 : if VUNLIKELY (source >= current && source - current < capacity_) {
+ + ]
430 : 2 : return false;
431 : : }
432 : :
433 : 2 : Bytes::bytes_free(data_, capacity_);
434 : : }
435 : :
436 : 28 : header = target.header;
437 : 28 : capacity_ = 0;
438 : 28 : size_ = target.size_;
439 : 28 : reserved_buf_ = target.reserved_buf_;
440 : 28 : reserved_buf2_ = target.reserved_buf2_;
441 : 28 : reserved_buf3_ = target.reserved_buf3_;
442 : 28 : downsample_ = target.downsample_;
443 : 28 : extent_ = target.extent_;
444 : 28 : vertical_ = target.vertical_;
445 : 28 : pack_size_ = target.pack_size_;
446 : 28 : is_owner_ = false;
447 : 28 : index_ = target.index_;
448 : 28 : data_ = target.data_;
449 : :
450 : 28 : protocol_.size_num = target.protocol_.size_num;
451 : 28 : protocol_.type_num = target.protocol_.type_num;
452 : 28 : std::memcpy(protocol_.names, target.protocol_.names, sizeof(protocol_.names));
453 : :
454 : 28 : return true;
455 : : }
456 : :
457 : 13 : bool PointCloud::deep_copy(const PointCloud& target) noexcept {
458 [ + - - + : 13 : if VUNLIKELY (target.pack_size_ != 0 && target.size_ > std::numeric_limits<size_t>::max() / target.pack_size_) {
- + ]
459 : 0 : return false;
460 : : }
461 : :
462 : 13 : const size_t target_size = target.size_ * target.pack_size_;
463 : :
464 [ + + + - : 13 : if VLIKELY (data_ && is_owner_ && target.data_ && capacity_ != 0 && capacity_ == target_size) {
+ + + - +
+ + - + +
+ + + + ]
465 : 1 : const auto current = reinterpret_cast<uintptr_t>(data_);
466 : 1 : const auto source = reinterpret_cast<uintptr_t>(target.data_);
467 : :
468 [ + - - + : 1 : if VUNLIKELY (source >= current && source - current < capacity_) {
- + ]
469 : 0 : return false;
470 : : }
471 : :
472 : 1 : header = target.header;
473 : 1 : size_ = target.size_;
474 : 1 : reserved_buf_ = target.reserved_buf_;
475 : 1 : reserved_buf2_ = target.reserved_buf2_;
476 : 1 : reserved_buf3_ = target.reserved_buf3_;
477 : 1 : downsample_ = target.downsample_;
478 : 1 : extent_ = target.extent_;
479 : 1 : vertical_ = target.vertical_;
480 : 1 : pack_size_ = target.pack_size_;
481 : 1 : index_ = target.index_;
482 : :
483 : 1 : protocol_.size_num = target.protocol_.size_num;
484 : 1 : protocol_.type_num = target.protocol_.type_num;
485 : 1 : std::memcpy(protocol_.names, target.protocol_.names, sizeof(protocol_.names));
486 : :
487 : 1 : std::memcpy(data_, target.data_, capacity_);
488 : :
489 : 1 : return true;
490 : : }
491 : :
492 [ + + ]: 12 : if VUNLIKELY (!shallow_copy(target)) {
493 : 2 : return false;
494 : : }
495 : :
496 [ + - + + : 10 : if VLIKELY (data_ != nullptr && target_size != 0) {
+ + ]
497 : 9 : auto* target_data = data_;
498 : 9 : capacity_ = target_size;
499 : 9 : data_ = Bytes::bytes_malloc(capacity_);
500 : :
501 [ - + ]: 9 : if VUNLIKELY (!data_) {
502 : 0 : capacity_ = 0;
503 : 0 : size_ = 0;
504 : 0 : index_ = 0;
505 : 0 : return false;
506 : : }
507 : :
508 : 9 : std::memcpy(data_, target_data, capacity_);
509 : 9 : is_owner_ = true;
510 : : } else {
511 : 1 : data_ = nullptr;
512 : : }
513 : :
514 : 10 : return true;
515 : : }
516 : :
517 : 15 : bool PointCloud::move_copy(PointCloud& target) noexcept {
518 [ + + ]: 15 : if VUNLIKELY (!shallow_copy(target)) {
519 : 1 : return false;
520 : : }
521 : :
522 : 14 : is_owner_ = target.is_owner_;
523 : 14 : capacity_ = target.capacity_;
524 : :
525 : 14 : target.capacity_ = 0;
526 : 14 : target.size_ = 0;
527 : 14 : target.reserved_buf_ = 0;
528 : 14 : target.reserved_buf2_ = 0;
529 : 14 : target.reserved_buf3_ = 0;
530 : 14 : target.downsample_ = 0;
531 : 14 : target.extent_ = 0;
532 : 14 : target.vertical_ = false;
533 : 14 : target.pack_size_ = 0;
534 : 14 : target.is_owner_ = false;
535 : 14 : target.index_ = 0;
536 : 14 : target.data_ = nullptr;
537 : :
538 : 14 : target.protocol_.size_num = 0;
539 : 14 : target.protocol_.type_num = 0;
540 : 14 : std::memset(&target.protocol_.names, 0, sizeof(protocol_.names));
541 : :
542 : : #if defined(__GNUC__) && !defined(__clang__)
543 : : #pragma GCC diagnostic push
544 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
545 : : #if __GNUC__ >= 11
546 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
547 : : #endif
548 : : #endif
549 : :
550 : 14 : std::memset(&target.header, 0, sizeof(header));
551 : :
552 : : #if defined(__GNUC__) && !defined(__clang__)
553 : : #pragma GCC diagnostic pop
554 : : #endif
555 : :
556 : 14 : return true;
557 : : }
558 : :
559 : 113 : size_t PointCloud::get_serialized_size() const noexcept {
560 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
561 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
562 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
563 : :
564 : 113 : return kMagicNumberBeginSize + kVersionSize + sizeof(PointCloud) + (size_ * pack_size_) + kMagicNumberEndSize;
565 : : }
566 : :
567 : 22 : PointCloud::KeyMap PointCloud::get_key_map(KeyList* key_list) const noexcept {
568 : 22 : KeyMap map;
569 : :
570 : 22 : auto target_key_list = protocol_.get_key_list();
571 : :
572 : 22 : uint16_t index = 0;
573 : :
574 [ + + ]: 120 : for (const auto& key : target_key_list) {
575 : 98 : map.try_emplace(key.name, index);
576 : :
577 : 98 : index += key.size;
578 : : }
579 : :
580 [ + + ]: 22 : if (key_list) {
581 [ - + ]: 4 : if (extent_ != 0) {
582 [ # # # # : 0 : for (size_t i = 0; i < target_key_list.size() && i < 3; ++i) {
# # ]
583 [ # # # # : 0 : if (target_key_list[i].type == kInt16Type && target_key_list[i].size == sizeof(int16_t)) {
# # ]
584 : 0 : target_key_list[i].type = kFloatType;
585 : : }
586 : : }
587 : : }
588 : :
589 : 4 : *key_list = std::move(target_key_list);
590 : : }
591 : :
592 : 22 : return map;
593 : 22 : }
594 : :
595 : 6 : PointCloud::KeyList PointCloud::get_key_list() const noexcept {
596 : 6 : auto key_list = protocol_.get_key_list();
597 : :
598 [ + + ]: 6 : if (extent_ != 0) {
599 [ + + + - : 4 : for (size_t i = 0; i < key_list.size() && i < 3; ++i) {
+ + ]
600 [ + - + - : 3 : if (key_list[i].type == kInt16Type && key_list[i].size == sizeof(int16_t)) {
+ - ]
601 : 3 : key_list[i].type = kFloatType;
602 : : }
603 : : }
604 : : }
605 : :
606 : 6 : return key_list;
607 : : }
608 : :
609 : 70 : size_t PointCloud::size() const noexcept { return size_; }
610 : :
611 : 23 : size_t PointCloud::pack_size() const noexcept { return pack_size_; }
612 : :
613 : 47 : bool PointCloud::is_owner() const noexcept { return is_owner_; }
614 : :
615 : 24 : uint16_t PointCloud::get_extent() const noexcept { return extent_; }
616 : :
617 : 16 : bool PointCloud::get_vertical() const noexcept { return vertical_; }
618 : :
619 : 6 : void PointCloud::set_vertical(bool vertical) noexcept { vertical_ = vertical; }
620 : :
621 : 10 : uint8_t PointCloud::get_downsample() const noexcept { return downsample_; }
622 : :
623 : 9 : bool PointCloud::downsample(uint8_t level) noexcept {
624 [ + + ]: 9 : if (level == 0) {
625 : 1 : downsample_ = 0;
626 : :
627 : 1 : return true;
628 : : }
629 : :
630 [ + + ]: 8 : if VUNLIKELY (extent_ == 0) {
631 : 1 : return false;
632 : : }
633 : :
634 [ + + - + : 7 : if VUNLIKELY (!is_owner_ || !data_ || pack_size_ < (sizeof(int16_t) * 3)) {
+ + - + +
+ ]
635 : 1 : return false;
636 : : }
637 : :
638 : 6 : downsample_ = level;
639 : :
640 [ + + ]: 6 : if VUNLIKELY (size_ == 0) {
641 : 1 : return true;
642 : : }
643 : :
644 : 5 : auto v_q = static_cast<int32_t>((static_cast<uint32_t>(level) * 128U + 127U) / 255U);
645 : :
646 : 5 : const int32_t cell_lo = pc_floor_div(-32768, v_q);
647 : 5 : const int32_t cell_hi = pc_floor_div(32767, v_q);
648 : 5 : const int32_t cells_span = cell_hi - cell_lo;
649 : 5 : const uint64_t cells_per_axis = static_cast<uint64_t>(cells_span) + 1;
650 : 5 : const uint64_t cell_count = cells_per_axis * cells_per_axis * cells_per_axis;
651 [ + - ]: 5 : const size_t reserve_n = (cell_count < static_cast<uint64_t>(size_)) ? static_cast<size_t>(cell_count)
652 : : : size_; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
653 : :
654 : 5 : const uint16_t ps = pack_size_;
655 : :
656 : 5 : tsl::robin_set<uint64_t, PcVoxelKeyHash> seen;
657 : 5 : seen.reserve(reserve_n);
658 : :
659 : 5 : std::vector<int16_t> cell_lut;
660 : 5 : const int16_t* lut = nullptr;
661 : :
662 [ + + ]: 5 : if (size_ >= kVoxelLutSize) {
663 : 2 : cell_lut.resize(kVoxelLutSize);
664 : :
665 [ + + ]: 131074 : for (size_t idx = 0; idx < kVoxelLutSize; ++idx) {
666 : 131072 : const int32_t q = static_cast<int32_t>(idx) - 32768;
667 : 131072 : cell_lut[idx] = static_cast<int16_t>(pc_floor_div(q, v_q));
668 : : }
669 : :
670 : 2 : lut = cell_lut.data();
671 : : }
672 : :
673 : 5 : size_t w = 0;
674 : :
675 [ + + ]: 131095 : for (size_t r = 0; r < size_; ++r) {
676 : 131090 : const uint8_t* src = data_ + (r * ps);
677 : :
678 : 131090 : int16_t qx = 0;
679 : 131090 : int16_t qy = 0;
680 : 131090 : int16_t qz = 0;
681 : :
682 : 131090 : std::memcpy(&qx, src, sizeof(int16_t));
683 : 131090 : std::memcpy(&qy, src + sizeof(int16_t), sizeof(int16_t));
684 : 131090 : std::memcpy(&qz, src + (sizeof(int16_t) * 2), sizeof(int16_t));
685 : :
686 : 131090 : int32_t fx = 0;
687 : 131090 : int32_t fy = 0;
688 : 131090 : int32_t fz = 0;
689 : :
690 [ + + ]: 131090 : if (lut != nullptr) {
691 : 131072 : fx = lut[static_cast<int32_t>(qx) + 32768];
692 : 131072 : fy = lut[static_cast<int32_t>(qy) + 32768];
693 : 131072 : fz = lut[static_cast<int32_t>(qz) + 32768];
694 : : } else {
695 : 18 : fx = pc_floor_div(qx, v_q);
696 : 18 : fy = pc_floor_div(qy, v_q);
697 : 18 : fz = pc_floor_div(qz, v_q);
698 : : }
699 : :
700 [ + + ]: 131090 : if (seen.insert(pc_voxel_key(fx, fy, fz)).second) {
701 [ + + ]: 15 : if (w != r) {
702 : 5 : std::memcpy(data_ + (w * ps), src, ps);
703 : : }
704 : :
705 : 15 : ++w;
706 : : }
707 : : }
708 : :
709 : 5 : size_ = w;
710 : 5 : index_ = w * ps;
711 : :
712 : 5 : return true;
713 : 5 : }
714 : :
715 : 11 : uint64_t PointCloud::get_protocol_size_num() const noexcept { return protocol_.size_num; }
716 : :
717 : 10 : uint64_t PointCloud::get_protocol_type_num() const noexcept { return protocol_.type_num; }
718 : :
719 : 1 : std::string PointCloud::get_protocol_size_str() const noexcept { return protocol_.get_size_for_print(); }
720 : :
721 : 5 : std::string PointCloud::get_protocol_name_str() const noexcept {
722 : 5 : return std::string(protocol_.names, ::strnlen(protocol_.names, sizeof(protocol_.names)));
723 : : }
724 : :
725 : 4 : std::string PointCloud::get_protocol_type_str() const noexcept {
726 [ + + ]: 4 : if (extent_ == 0) {
727 : 3 : return protocol_.get_type_for_print();
728 : : }
729 : :
730 : 1 : auto key_list = protocol_.get_key_list();
731 : :
732 : 1 : std::string print_str;
733 : :
734 [ + + ]: 5 : for (size_t i = 0; i < key_list.size(); ++i) {
735 : 4 : const auto& key = key_list[i];
736 : 4 : uint8_t type = key.type;
737 : :
738 [ + + + - : 4 : if (i < 3 && key.type == kInt16Type && key.size == sizeof(int16_t)) {
+ - ]
739 : 3 : type = kFloatType;
740 : : }
741 : :
742 [ + + ]: 4 : if (!print_str.empty()) {
743 : 3 : print_str += ",";
744 : : }
745 : :
746 [ - - - - : 4 : switch (type) {
- - - - -
+ - - ]
747 : 0 : case kBoolType:
748 : 0 : print_str += "bool";
749 : 0 : break;
750 : 0 : case kInt8Type:
751 : 0 : print_str += "int8";
752 : 0 : break;
753 : 0 : case kUint8Type:
754 : 0 : print_str += "uint8";
755 : 0 : break;
756 : 0 : case kInt16Type:
757 : 0 : print_str += "int16";
758 : 0 : break;
759 : 0 : case kUint16Type:
760 : 0 : print_str += "uint16";
761 : 0 : break;
762 : 0 : case kInt32Type:
763 : 0 : print_str += "int32";
764 : 0 : break;
765 : 0 : case kUint32Type:
766 : 0 : print_str += "uint32";
767 : 0 : break;
768 : 0 : case kInt64Type:
769 : 0 : print_str += "int64";
770 : 0 : break;
771 : 0 : case kUint64Type:
772 : 0 : print_str += "uint64";
773 : 0 : break;
774 : 4 : case kFloatType:
775 : 4 : print_str += "float";
776 : 4 : break;
777 : 0 : case kDoubleType:
778 : 0 : print_str += "double";
779 : 0 : break;
780 : 0 : default:
781 : 0 : break;
782 : : }
783 : : }
784 : :
785 : 1 : return print_str;
786 : 1 : }
787 : :
788 : 31 : const uint8_t* PointCloud::get_internal_data() const noexcept { return data_; }
789 : :
790 : 13 : size_t PointCloud::get_reserved_size() const noexcept {
791 [ + + ]: 13 : if (pack_size_ == 0) {
792 : 2 : return 0;
793 : : }
794 : :
795 : 11 : return capacity_ / pack_size_;
796 : : }
797 : :
798 : 38 : bool PointCloud::get_value_v3f(float& x, float& y, float& z, size_t loop_index) const noexcept {
799 [ + + ]: 38 : if (extent_ != 0) {
800 [ + - + + : 31 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(int16_t) * 3) {
+ + - + +
+ ]
801 : 1 : return false;
802 : : }
803 : :
804 : 30 : const uint8_t* base = data_ + (loop_index * pack_size_);
805 : :
806 : 30 : int16_t qx = 0;
807 : 30 : int16_t qy = 0;
808 : 30 : int16_t qz = 0;
809 : :
810 : 30 : std::memcpy(&qx, base, sizeof(int16_t));
811 : 30 : std::memcpy(&qy, base + sizeof(int16_t), sizeof(int16_t));
812 : 30 : std::memcpy(&qz, base + (sizeof(int16_t) * 2), sizeof(int16_t));
813 : :
814 : 30 : x = Quantize::decode<float>(extent_, qx);
815 : 30 : y = Quantize::decode<float>(extent_, qy);
816 : 30 : z = Quantize::decode<float>(extent_, qz);
817 : :
818 : 30 : return true;
819 : : }
820 : :
821 [ + - + + : 7 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(float) * 3) {
+ + - + +
+ ]
822 : 2 : return false;
823 : : }
824 : :
825 : 5 : std::memcpy(&x, data_ + (loop_index * pack_size_), sizeof(float));
826 : 5 : std::memcpy(&y, data_ + (loop_index * pack_size_) + sizeof(float), sizeof(float));
827 : 5 : std::memcpy(&z, data_ + (loop_index * pack_size_) + sizeof(float) + sizeof(float), sizeof(float));
828 : :
829 : 5 : return true;
830 : : }
831 : :
832 : 31 : bool PointCloud::get_value_v3f(Vector3f& v3f, size_t loop_index) const noexcept {
833 [ + + ]: 31 : if (extent_ != 0) {
834 : 16 : return get_value_v3f(v3f.x, v3f.y, v3f.z, loop_index);
835 : : }
836 : :
837 [ + - + + : 15 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(float) * 3) {
+ + - + +
+ ]
838 : 2 : return false;
839 : : }
840 : :
841 : 13 : std::memcpy(&v3f, data_ + (loop_index * pack_size_), sizeof(float) * 3);
842 : :
843 : 13 : return true;
844 : : }
845 : :
846 : 28 : PointCloud::Vector3f PointCloud::get_value_v3f(size_t loop_index) const noexcept {
847 : 28 : PointCloud::Vector3f v3f;
848 : :
849 : 28 : get_value_v3f(v3f, loop_index);
850 : :
851 : 28 : return v3f;
852 : : }
853 : :
854 : 8 : bool PointCloud::get_value_v3d(double& x, double& y, double& z, size_t loop_index) const noexcept {
855 [ + + ]: 8 : if (extent_ != 0) {
856 [ + - + + : 4 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(int16_t) * 3) {
+ + - + +
+ ]
857 : 2 : return false;
858 : : }
859 : :
860 : 2 : const uint8_t* base = data_ + (loop_index * pack_size_);
861 : :
862 : 2 : int16_t qx = 0;
863 : 2 : int16_t qy = 0;
864 : 2 : int16_t qz = 0;
865 : :
866 : 2 : std::memcpy(&qx, base, sizeof(int16_t));
867 : 2 : std::memcpy(&qy, base + sizeof(int16_t), sizeof(int16_t));
868 : 2 : std::memcpy(&qz, base + (sizeof(int16_t) * 2), sizeof(int16_t));
869 : :
870 : 2 : x = Quantize::decode<double>(extent_, qx);
871 : 2 : y = Quantize::decode<double>(extent_, qy);
872 : 2 : z = Quantize::decode<double>(extent_, qz);
873 : :
874 : 2 : return true;
875 : : }
876 : :
877 [ + - + + : 4 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(double) * 3) {
+ + - + +
+ ]
878 : 2 : return false;
879 : : }
880 : :
881 : 2 : std::memcpy(&x, data_ + (loop_index * pack_size_), sizeof(double));
882 : 2 : std::memcpy(&y, data_ + (loop_index * pack_size_) + sizeof(double), sizeof(double));
883 : 2 : std::memcpy(&z, data_ + (loop_index * pack_size_) + sizeof(double) + sizeof(double), sizeof(double));
884 : :
885 : 2 : return true;
886 : : }
887 : :
888 : 5 : bool PointCloud::get_value_v3d(Vector3d& v3d, size_t loop_index) const noexcept {
889 [ + + ]: 5 : if (extent_ != 0) {
890 : 1 : return get_value_v3d(v3d.x, v3d.y, v3d.z, loop_index);
891 : : }
892 : :
893 [ + - + + : 4 : if VUNLIKELY (!data_ || loop_index >= size_ || pack_size_ < sizeof(double) * 3) {
+ + - + +
+ ]
894 : 2 : return false;
895 : : }
896 : :
897 : 2 : std::memcpy(&v3d, data_ + (loop_index * pack_size_), sizeof(double) * 3);
898 : :
899 : 2 : return true;
900 : : }
901 : :
902 : 1 : PointCloud::Vector3d PointCloud::get_value_v3d(size_t loop_index) const noexcept {
903 : 1 : PointCloud::Vector3d v3d;
904 : :
905 : 1 : get_value_v3d(v3d, loop_index);
906 : :
907 : 1 : return v3d;
908 : : }
909 : :
910 : 16 : double PointCloud::get_value_for_double_float(size_t loop_index, uint16_t offset, uint8_t type) const noexcept {
911 [ + + + + : 16 : switch (type) {
+ + + + +
+ + + ]
912 : 1 : case kBoolType:
913 : 1 : return get_value<uint8_t>(loop_index, offset);
914 : 1 : case kInt8Type:
915 : 1 : return get_value<int8_t>(loop_index, offset);
916 : 2 : case kUint8Type:
917 : 2 : return get_value<uint8_t>(loop_index, offset);
918 : 2 : case kInt16Type:
919 : 2 : return get_value<int16_t>(loop_index, offset);
920 : 1 : case kUint16Type:
921 : 1 : return get_value<uint16_t>(loop_index, offset);
922 : 2 : case kInt32Type:
923 : 2 : return get_value<int32_t>(loop_index, offset);
924 : 1 : case kUint32Type:
925 : 1 : return get_value<uint32_t>(loop_index, offset);
926 : 1 : case kInt64Type:
927 : 1 : return get_value<int64_t>(loop_index, offset);
928 : 1 : case kUint64Type:
929 : 1 : return get_value<uint64_t>(loop_index, offset);
930 : 1 : case kFloatType:
931 : 1 : return get_value<float>(loop_index, offset);
932 : 2 : case kDoubleType:
933 : 2 : return get_value<double>(loop_index, offset);
934 : 1 : default:
935 : 1 : return 0;
936 : : }
937 : : }
938 : :
939 : 13 : double PointCloud::get_value_for_double_float(size_t loop_index, KeyMap& key_map, std::string_view key,
940 : : uint8_t type) const noexcept {
941 [ + + + + : 13 : switch (type) {
+ + + + +
+ + + ]
942 : 1 : case kBoolType:
943 : 1 : return get_value<uint8_t>(loop_index, key_map, key);
944 : 1 : case kInt8Type:
945 : 1 : return get_value<int8_t>(loop_index, key_map, key);
946 : 1 : case kUint8Type:
947 : 1 : return get_value<uint8_t>(loop_index, key_map, key);
948 : 1 : case kInt16Type:
949 : 1 : return get_value<int16_t>(loop_index, key_map, key);
950 : 1 : case kUint16Type:
951 : 1 : return get_value<uint16_t>(loop_index, key_map, key);
952 : 1 : case kInt32Type:
953 : 1 : return get_value<int32_t>(loop_index, key_map, key);
954 : 1 : case kUint32Type:
955 : 1 : return get_value<uint32_t>(loop_index, key_map, key);
956 : 1 : case kInt64Type:
957 : 1 : return get_value<int64_t>(loop_index, key_map, key);
958 : 1 : case kUint64Type:
959 : 1 : return get_value<uint64_t>(loop_index, key_map, key);
960 : 2 : case kFloatType:
961 : 2 : return get_value<float>(loop_index, key_map, key);
962 : 1 : case kDoubleType:
963 : 1 : return get_value<double>(loop_index, key_map, key);
964 : 1 : default:
965 : 1 : return 0;
966 : : }
967 : : }
968 : :
969 : 17 : std::string PointCloud::get_value_for_print(size_t loop_index, uint16_t offset, uint8_t type) const noexcept {
970 [ + + + + : 17 : switch (type) {
+ + + + +
+ + + ]
971 : 2 : case kBoolType:
972 [ + + ]: 2 : return get_value<bool>(loop_index, offset) ? "true" : "false";
973 : 1 : case kInt8Type:
974 : 1 : return std::to_string(get_value<int8_t>(loop_index, offset));
975 : 2 : case kUint8Type:
976 : 2 : return std::to_string(get_value<uint8_t>(loop_index, offset));
977 : 2 : case kInt16Type:
978 : 2 : return std::to_string(get_value<int16_t>(loop_index, offset));
979 : 1 : case kUint16Type:
980 : 1 : return std::to_string(get_value<uint16_t>(loop_index, offset));
981 : 2 : case kInt32Type:
982 : 2 : return std::to_string(get_value<int32_t>(loop_index, offset));
983 : 1 : case kUint32Type:
984 : 1 : return std::to_string(get_value<uint32_t>(loop_index, offset));
985 : 1 : case kInt64Type:
986 : 1 : return std::to_string(get_value<int64_t>(loop_index, offset));
987 : 1 : case kUint64Type:
988 : 1 : return std::to_string(get_value<uint64_t>(loop_index, offset));
989 : 1 : case kFloatType:
990 : 1 : return std::to_string(get_value<float>(loop_index, offset));
991 : 2 : case kDoubleType:
992 : 2 : return std::to_string(get_value<double>(loop_index, offset));
993 : 1 : default:
994 : 1 : return std::string();
995 : : }
996 : : }
997 : :
998 : 14 : std::string PointCloud::get_value_for_print(size_t loop_index, KeyMap& key_map, std::string_view key,
999 : : uint8_t type) const noexcept {
1000 [ + + + + : 14 : switch (type) {
+ + + + +
+ + + ]
1001 : 2 : case kBoolType:
1002 [ + + ]: 2 : return get_value<bool>(loop_index, key_map, key) ? "true" : "false";
1003 : 1 : case kInt8Type:
1004 : 1 : return std::to_string(get_value<int8_t>(loop_index, key_map, key));
1005 : 1 : case kUint8Type:
1006 : 1 : return std::to_string(get_value<uint8_t>(loop_index, key_map, key));
1007 : 1 : case kInt16Type:
1008 : 1 : return std::to_string(get_value<int16_t>(loop_index, key_map, key));
1009 : 1 : case kUint16Type:
1010 : 1 : return std::to_string(get_value<uint16_t>(loop_index, key_map, key));
1011 : 1 : case kInt32Type:
1012 : 1 : return std::to_string(get_value<int32_t>(loop_index, key_map, key));
1013 : 1 : case kUint32Type:
1014 : 1 : return std::to_string(get_value<uint32_t>(loop_index, key_map, key));
1015 : 1 : case kInt64Type:
1016 : 1 : return std::to_string(get_value<int64_t>(loop_index, key_map, key));
1017 : 1 : case kUint64Type:
1018 : 1 : return std::to_string(get_value<uint64_t>(loop_index, key_map, key));
1019 : 2 : case kFloatType:
1020 : 2 : return std::to_string(get_value<float>(loop_index, key_map, key));
1021 : 1 : case kDoubleType:
1022 : 1 : return std::to_string(get_value<double>(loop_index, key_map, key));
1023 : 1 : default:
1024 : 1 : return std::string();
1025 : : }
1026 : : }
1027 : :
1028 : 13 : bool PointCloud::create(size_t size, uint64_t size_num, uint64_t type_num, std::string_view key_str, uint16_t extent,
1029 : : bool vertical) noexcept {
1030 [ + + ]: 13 : if VUNLIKELY (!Protocol::check_valid(size_num, key_str)) {
1031 : 4 : return false;
1032 : : }
1033 : :
1034 : 9 : Protocol new_protocol{};
1035 : 9 : new_protocol.size_num = size_num;
1036 : 9 : std::memset(new_protocol.names, 0, sizeof(new_protocol.names));
1037 : 9 : std::memcpy(new_protocol.names, key_str.data(), key_str.size());
1038 : 9 : new_protocol.type_num = type_num;
1039 : :
1040 [ + + ]: 9 : if (extent != 0) {
1041 : 1 : PointCloud protocol_probe;
1042 : 1 : protocol_probe.protocol_ = new_protocol;
1043 : :
1044 [ - + ]: 1 : if VUNLIKELY (!protocol_probe.compress_protocol_xyz()) {
1045 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1046 : : }
1047 : :
1048 : 1 : new_protocol = protocol_probe.protocol_;
1049 [ + - ]: 1 : }
1050 : :
1051 : 9 : const size_t new_pack_size = new_protocol.get_pack_size();
1052 : :
1053 [ + - - + : 9 : if VUNLIKELY (new_pack_size != 0 && size > std::numeric_limits<size_t>::max() / new_pack_size) {
- + ]
1054 : 0 : return false;
1055 : : }
1056 : :
1057 [ - + - - : 9 : if (is_owner_ && data_ && capacity_ != 0) {
- - ]
1058 : 0 : Bytes::bytes_free(data_, capacity_);
1059 : : }
1060 : :
1061 : 9 : data_ = nullptr;
1062 : 9 : is_owner_ = false;
1063 : 9 : capacity_ = 0;
1064 : 9 : size_ = 0;
1065 : 9 : index_ = 0;
1066 : :
1067 : 9 : protocol_ = new_protocol;
1068 : 9 : extent_ = extent;
1069 : 9 : vertical_ = vertical;
1070 : 9 : downsample_ = 0;
1071 : :
1072 : 9 : pack_size_ = new_pack_size;
1073 : 9 : capacity_ = size * pack_size_;
1074 : :
1075 [ + + ]: 9 : if VLIKELY (capacity_ != 0) {
1076 : 8 : data_ = Bytes::bytes_malloc(capacity_);
1077 : :
1078 [ - + ]: 8 : if VUNLIKELY (!data_) {
1079 : 0 : capacity_ = 0;
1080 : 0 : return false;
1081 : : }
1082 : :
1083 : 8 : is_owner_ = true;
1084 : : }
1085 : :
1086 : 9 : return true;
1087 : : }
1088 : :
1089 : 7 : void PointCloud::clear(bool force) noexcept {
1090 [ + + ]: 7 : if (force) {
1091 [ + + + - : 6 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
1092 : 2 : Bytes::bytes_free(data_, capacity_);
1093 : : }
1094 : :
1095 : 6 : pack_size_ = 0;
1096 : 6 : capacity_ = 0;
1097 : 6 : data_ = nullptr;
1098 : 6 : reserved_buf_ = 0;
1099 : 6 : reserved_buf2_ = 0;
1100 : 6 : reserved_buf3_ = 0;
1101 : 6 : extent_ = 0;
1102 : 6 : vertical_ = false;
1103 : 6 : is_owner_ = false;
1104 : :
1105 : 6 : protocol_.size_num = 0;
1106 : 6 : std::memset(protocol_.names, 0, sizeof(protocol_.names));
1107 : 6 : protocol_.type_num = 0;
1108 : :
1109 : : #if defined(__GNUC__) && !defined(__clang__)
1110 : : #pragma GCC diagnostic push
1111 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
1112 : : #if __GNUC__ >= 11
1113 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
1114 : : #endif
1115 : : #endif
1116 : :
1117 : 6 : std::memset(&header, 0, sizeof(header));
1118 : :
1119 : : #if defined(__GNUC__) && !defined(__clang__)
1120 : : #pragma GCC diagnostic pop
1121 : : #endif
1122 : : }
1123 : :
1124 : 7 : size_ = 0;
1125 : 7 : index_ = 0;
1126 : 7 : downsample_ = 0;
1127 : 7 : }
1128 : :
1129 : : // PointCloud::Protocol
1130 : 13 : bool PointCloud::Protocol::check_valid(uint64_t _size_num, std::string_view _names) noexcept {
1131 [ + + - + : 13 : if VUNLIKELY (_size_num == 0 || _names.empty() || _names.size() > sizeof(names)) {
+ + + + +
+ ]
1132 : 2 : return false;
1133 : : }
1134 : :
1135 : 11 : uint16_t num_count = 0;
1136 : 11 : uint16_t key_count = 0;
1137 : :
1138 : : do {
1139 : 48 : num_count++;
1140 : 48 : _size_num >>= 4;
1141 [ + + ]: 48 : } while (_size_num != 0);
1142 : :
1143 [ + + ]: 269 : for (auto c : _names) {
1144 [ + + ]: 258 : if (c == ',') {
1145 : 37 : ++key_count;
1146 : : }
1147 : : }
1148 : :
1149 [ + - ]: 11 : if (!_names.empty()) {
1150 : 11 : ++key_count;
1151 : : }
1152 : :
1153 [ + + - + : 11 : if VUNLIKELY (key_count != num_count || key_count < 3 || key_count > 16) {
+ + - + +
+ ]
1154 : 2 : return false;
1155 : : }
1156 : :
1157 : 9 : return true;
1158 : : }
1159 : :
1160 : 136 : std::string PointCloud::Protocol::get_names(const std::vector<std::string>& keys) noexcept {
1161 : 136 : std::string key_str;
1162 : :
1163 [ + + ]: 601 : for (const auto& key : keys) {
1164 [ + + ]: 465 : if (!key_str.empty()) {
1165 : 329 : key_str += ',';
1166 : : }
1167 : :
1168 : 465 : key_str += key;
1169 : : }
1170 : :
1171 [ - + ]: 136 : if VUNLIKELY (key_str.size() > sizeof(names)) {
1172 : 0 : return std::string();
1173 : : }
1174 : :
1175 : 136 : return key_str;
1176 : 136 : }
1177 : :
1178 : 29 : PointCloud::KeyList PointCloud::Protocol::get_key_list() const noexcept {
1179 : 29 : KeyList key_list;
1180 : 29 : key_list.reserve(16);
1181 : :
1182 : 29 : uint64_t temp_size_num = size_num;
1183 : 29 : uint64_t temp_type_num = type_num;
1184 : :
1185 : 58 : std::istringstream iss(std::string(names, ::strnlen(names, sizeof(names))));
1186 : 29 : std::string token;
1187 : :
1188 : 29 : bool leading_zero = true;
1189 : :
1190 [ + + ]: 491 : for (int i = 15; i >= 0; --i) {
1191 : 463 : uint8_t size_nibble = (temp_size_num >> (i * 4)) & 0xF;
1192 : 463 : uint8_t type_nibble = (temp_type_num >> (i * 4)) & 0xF;
1193 : :
1194 [ + + + + ]: 463 : if (leading_zero && size_nibble == 0) {
1195 : 339 : continue;
1196 : : }
1197 : :
1198 : 124 : leading_zero = false;
1199 : :
1200 [ + + ]: 124 : if VUNLIKELY (!std::getline(iss, token, ',')) {
1201 : 1 : break;
1202 : : }
1203 : :
1204 : 123 : Key key;
1205 : 123 : key.size = size_nibble;
1206 : 123 : key.type = type_nibble;
1207 : 123 : key.name = token;
1208 : :
1209 : 123 : key_list.emplace_back(std::move(key));
1210 : 123 : }
1211 : :
1212 : 29 : return key_list;
1213 : 29 : }
1214 : :
1215 : 1 : std::string PointCloud::Protocol::get_size_for_print() const noexcept {
1216 : 1 : bool leading_zero = true;
1217 : :
1218 : 1 : uint64_t temp_size_num = size_num;
1219 : :
1220 : 1 : std::string print_str;
1221 : :
1222 [ + + ]: 17 : for (int i = 15; i >= 0; --i) {
1223 : 16 : uint8_t size_nibble = (temp_size_num >> (i * 4)) & 0xF;
1224 : :
1225 [ + + + + ]: 16 : if (leading_zero && size_nibble == 0) {
1226 : 12 : continue;
1227 : : }
1228 : :
1229 : 4 : leading_zero = false;
1230 : :
1231 [ + + ]: 4 : if (!print_str.empty()) {
1232 : 3 : print_str += ",";
1233 : : }
1234 : :
1235 : 4 : print_str += std::to_string(size_nibble);
1236 : : }
1237 : :
1238 : 1 : return print_str;
1239 : : }
1240 : :
1241 : 3 : std::string PointCloud::Protocol::get_type_for_print() const noexcept {
1242 : 3 : bool leading_zero = true;
1243 : :
1244 : 3 : uint64_t temp_type_num = type_num;
1245 : :
1246 : 3 : std::string print_str;
1247 : :
1248 [ + + ]: 51 : for (int i = 15; i >= 0; --i) {
1249 : 48 : uint8_t type_nibble = (temp_type_num >> (i * 4)) & 0xF;
1250 : :
1251 [ + + + + ]: 48 : if (leading_zero && type_nibble == 0) {
1252 : 30 : continue;
1253 : : }
1254 : :
1255 : 18 : leading_zero = false;
1256 : :
1257 [ + + ]: 18 : if (!print_str.empty()) {
1258 : 13 : print_str += ",";
1259 : : }
1260 : :
1261 [ + + + + : 18 : switch (type_nibble) {
+ + + + +
+ + + ]
1262 : 1 : case kBoolType:
1263 : 1 : print_str += "bool";
1264 : 1 : break;
1265 : 1 : case kInt8Type:
1266 : 1 : print_str += "int8";
1267 : 1 : break;
1268 : 1 : case kUint8Type:
1269 : 1 : print_str += "uint8";
1270 : 1 : break;
1271 : 1 : case kInt16Type:
1272 : 1 : print_str += "int16";
1273 : 1 : break;
1274 : 1 : case kUint16Type:
1275 : 1 : print_str += "uint16";
1276 : 1 : break;
1277 : 1 : case kInt32Type:
1278 : 1 : print_str += "int32";
1279 : 1 : break;
1280 : 1 : case kUint32Type:
1281 : 1 : print_str += "uint32";
1282 : 1 : break;
1283 : 1 : case kInt64Type:
1284 : 1 : print_str += "int64";
1285 : 1 : break;
1286 : 1 : case kUint64Type:
1287 : 1 : print_str += "uint64";
1288 : 1 : break;
1289 : 5 : case kFloatType:
1290 : 5 : print_str += "float";
1291 : 5 : break;
1292 : 1 : case kDoubleType:
1293 : 1 : print_str += "double";
1294 : 1 : break;
1295 : 3 : default:
1296 : 3 : break;
1297 : : }
1298 : : }
1299 : :
1300 : 3 : return print_str;
1301 : : }
1302 : :
1303 : 28 : bool PointCloud::compress_protocol_xyz() noexcept {
1304 : 28 : uint16_t num_fields = 0;
1305 : 28 : uint64_t temp_size_num = protocol_.size_num;
1306 : :
1307 : : do {
1308 : 90 : ++num_fields;
1309 : 90 : temp_size_num >>= 4;
1310 [ + + ]: 90 : } while (temp_size_num != 0);
1311 : :
1312 [ - + ]: 28 : if VUNLIKELY (num_fields < 3) {
1313 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1314 : : }
1315 : :
1316 [ + + ]: 109 : for (uint16_t i = 0; i < 3; ++i) {
1317 : 82 : uint64_t shift = static_cast<uint64_t>(num_fields - 1 - i) * 4;
1318 : 82 : uint8_t cur_type = (protocol_.type_num >> shift) & 0xF;
1319 : :
1320 [ + + + + : 82 : if VUNLIKELY (cur_type != kFloatType && cur_type != kDoubleType && cur_type != kInt16Type) {
+ + + - +
+ ]
1321 : 1 : return false;
1322 : : }
1323 : : }
1324 : :
1325 [ + + ]: 108 : for (uint16_t i = 0; i < 3; ++i) {
1326 : 81 : uint64_t shift = static_cast<uint64_t>(num_fields - 1 - i) * 4;
1327 : :
1328 : 81 : protocol_.size_num &= ~(static_cast<uint64_t>(0xF) << shift);
1329 : 81 : protocol_.size_num |= (static_cast<uint64_t>(sizeof(int16_t)) << shift);
1330 : 81 : protocol_.type_num &= ~(static_cast<uint64_t>(0xF) << shift);
1331 : 81 : protocol_.type_num |= (static_cast<uint64_t>(kInt16Type) << shift);
1332 : : }
1333 : :
1334 : 27 : return true;
1335 : : }
1336 : :
1337 : : } // namespace zerocopy
1338 : :
1339 : : } // namespace vlink
|