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/object_array.h"
25 : :
26 : : #include <cstdint>
27 : : #include <limits>
28 : :
29 : : namespace vlink {
30 : :
31 : : namespace zerocopy {
32 : :
33 : : // Object
34 : 43 : ObjectArray::Object::Object() noexcept {
35 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
36 : : #ifndef __ANDROID__
37 : : #warning "[ObjectArray::Object] No support for 32-bit architecture."
38 : : #endif
39 : : #else
40 : : static_assert(sizeof(ObjectArray::Object) == 144, "Sizeof must be 144 bytes.");
41 : : #endif
42 : 43 : }
43 : :
44 : : // ObjectArray
45 : 53 : ObjectArray::ObjectArray() noexcept {
46 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
47 : : #ifndef __ANDROID__
48 : : #warning "[ObjectArray] No support for 32-bit architecture."
49 : : #endif
50 : : #else
51 : : static_assert(sizeof(ObjectArray) == 112, "Sizeof must be 112 bytes.");
52 : : #endif
53 : 53 : }
54 : :
55 : 55 : ObjectArray::~ObjectArray() noexcept {
56 [ + + + - : 55 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
57 : 35 : Bytes::bytes_free(data_, capacity_);
58 : : }
59 : 55 : }
60 : :
61 : 1 : ObjectArray::ObjectArray(const ObjectArray& target) noexcept { deep_copy(target); }
62 : :
63 : 1 : ObjectArray::ObjectArray(ObjectArray&& target) noexcept { move_copy(target); }
64 : :
65 : 1 : ObjectArray& ObjectArray::operator=(const ObjectArray& target) noexcept {
66 [ - + ]: 1 : if VUNLIKELY (this == &target) {
67 : : return *this; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
68 : : }
69 : :
70 : 1 : deep_copy(target);
71 : :
72 : 1 : return *this;
73 : : }
74 : :
75 : 1 : ObjectArray& ObjectArray::operator=(ObjectArray&& target) noexcept {
76 [ - + ]: 1 : if VUNLIKELY (this == &target) {
77 : : return *this; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
78 : : }
79 : :
80 : 1 : move_copy(target);
81 : :
82 : 1 : return *this;
83 : : }
84 : :
85 : 7 : bool ObjectArray::operator<<(const Bytes& bytes) noexcept {
86 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
87 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
88 : : // static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
89 : :
90 [ + + ]: 7 : if VUNLIKELY (bytes.empty()) {
91 : 1 : return false;
92 : : }
93 : :
94 [ + + ]: 6 : if VUNLIKELY (!check_valid(bytes)) {
95 : 2 : return false;
96 : : }
97 : :
98 : 4 : uint32_t wire_version = 0;
99 : 4 : std::memcpy(&wire_version, bytes.data() + kMagicNumberBeginSize, kVersionSize);
100 : :
101 [ - + ]: 4 : if VUNLIKELY (version_major(wire_version) != version_major(kWireVersion)) {
102 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
103 : : }
104 : :
105 [ + + + - : 4 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
106 : 1 : Bytes::bytes_free(data_, capacity_);
107 : : }
108 : :
109 : : #if defined(__GNUC__) && !defined(__clang__)
110 : : #pragma GCC diagnostic push
111 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
112 : : #if __GNUC__ >= 11
113 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
114 : : #endif
115 : : #endif
116 : :
117 : 4 : auto* target_ptr = reinterpret_cast<uint8_t*>(this);
118 : :
119 : 4 : std::memcpy(target_ptr, bytes.data() + kMagicNumberBeginSize + kVersionSize, sizeof(ObjectArray));
120 : :
121 : : #if defined(__GNUC__) && !defined(__clang__)
122 : : #pragma GCC diagnostic pop
123 : : #endif
124 : :
125 : 4 : data_ = const_cast<uint8_t*>(bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(ObjectArray));
126 : :
127 : 4 : capacity_ = 0;
128 : :
129 : 4 : is_owner_ = false;
130 : :
131 [ + + ]: 4 : if VUNLIKELY (bytes.size() != get_serialized_size()) {
132 : 1 : clear();
133 : 1 : return false;
134 : : }
135 : :
136 : 3 : return true;
137 : : }
138 : :
139 : 7 : bool ObjectArray::operator>>(Bytes& bytes) const noexcept {
140 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
141 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
142 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
143 : :
144 [ + + - + : 7 : if (bytes.empty() || bytes.size() != get_serialized_size()) {
+ + ]
145 : 6 : bytes = Bytes::create(get_serialized_size());
146 : : }
147 : :
148 : 7 : std::memcpy(bytes.data(), &kMagicNumberBegin, kMagicNumberBeginSize);
149 : :
150 : 7 : std::memcpy(bytes.data() + kMagicNumberBeginSize, &kWireVersion, kVersionSize);
151 : :
152 : : // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
153 : 7 : std::memcpy(bytes.data() + kMagicNumberBeginSize + kVersionSize, this, sizeof(ObjectArray));
154 : :
155 [ + + + + : 7 : if VLIKELY (data_ != nullptr && count_ != 0 && pack_size_ != 0) {
+ + + - +
+ ]
156 : 5 : std::memcpy(bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(ObjectArray), data_,
157 : 5 : static_cast<size_t>(count_) * pack_size_);
158 : : }
159 : :
160 : 7 : std::memcpy(bytes.data() + kMagicNumberBeginSize + kVersionSize + sizeof(ObjectArray) +
161 : 7 : (static_cast<size_t>(count_) * pack_size_),
162 : : &kMagicNumberEnd, kMagicNumberEndSize);
163 : :
164 : 7 : return true;
165 : : }
166 : :
167 : 13 : bool ObjectArray::check_valid(const Bytes& bytes) noexcept {
168 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
169 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
170 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
171 : :
172 [ + + ]: 13 : if VUNLIKELY (bytes.size() < kMagicNumberBeginSize + kVersionSize + sizeof(ObjectArray) + kMagicNumberEndSize) {
173 : 2 : return false;
174 : : }
175 : :
176 : 11 : uint32_t check_magic = 0;
177 : :
178 : 11 : std::memcpy(&check_magic, bytes.begin(), kMagicNumberBeginSize);
179 : :
180 [ + + ]: 11 : if VUNLIKELY (check_magic != kMagicNumberBegin) {
181 : 1 : return false;
182 : : }
183 : :
184 : 10 : uint32_t wire_version = 0;
185 : 10 : std::memcpy(&wire_version, bytes.data() + kMagicNumberBeginSize, kVersionSize);
186 : :
187 [ + + ]: 10 : if VUNLIKELY (version_major(wire_version) != version_major(kWireVersion)) {
188 : 2 : return false;
189 : : }
190 : :
191 : 8 : std::memcpy(&check_magic, bytes.end() - kMagicNumberEndSize, kMagicNumberEndSize);
192 : :
193 [ + + ]: 8 : if VUNLIKELY (check_magic != kMagicNumberEnd) {
194 : 1 : return false;
195 : : }
196 : :
197 : 7 : return true;
198 : : }
199 : :
200 : 15 : size_t ObjectArray::get_serialized_size() const noexcept {
201 : : static constexpr size_t kMagicNumberBeginSize = sizeof(kMagicNumberBegin);
202 : : static constexpr size_t kVersionSize = sizeof(kWireVersion);
203 : : static constexpr size_t kMagicNumberEndSize = sizeof(kMagicNumberEnd);
204 : :
205 : 15 : return kMagicNumberBeginSize + kVersionSize + sizeof(ObjectArray) + (static_cast<size_t>(count_) * pack_size_) +
206 : 15 : kMagicNumberEndSize;
207 : : }
208 : :
209 [ + + + + : 15 : bool ObjectArray::is_valid() const noexcept { return data_ != nullptr && count_ != 0 && pack_size_ != 0; }
+ - ]
210 : :
211 : 10 : bool ObjectArray::shallow_copy(const ObjectArray& target) noexcept {
212 [ + + ]: 10 : if VUNLIKELY (this == &target) {
213 : 3 : return false;
214 : : }
215 : :
216 [ - + - - : 7 : if (is_owner_ && data_ && capacity_ != 0) {
- - ]
217 : : Bytes::bytes_free(data_, capacity_); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
218 : : }
219 : :
220 : 7 : header = target.header;
221 : :
222 : 7 : update_time_ns_ = target.update_time_ns_;
223 : 7 : std::memcpy(source_id_, target.source_id_, sizeof(source_id_));
224 : 7 : channel_ = target.channel_;
225 : 7 : freq_ = target.freq_;
226 : 7 : count_ = target.count_;
227 : 7 : pack_size_ = target.pack_size_;
228 : 7 : reserved_buf_ = target.reserved_buf_;
229 : 7 : reserved_buf2_ = target.reserved_buf2_;
230 : 7 : reserved_buf3_ = target.reserved_buf3_;
231 : 7 : reserved_buf4_ = target.reserved_buf4_;
232 : 7 : reserved_buf5_ = target.reserved_buf5_;
233 : 7 : is_owner_ = false;
234 : 7 : capacity_ = 0;
235 : 7 : data_ = target.data_;
236 : :
237 : 7 : return true;
238 : : }
239 : :
240 : 4 : bool ObjectArray::deep_copy(const ObjectArray& target) noexcept {
241 : 4 : size_t target_size = static_cast<size_t>(target.count_) * target.pack_size_;
242 : :
243 [ + + + - : 4 : if VLIKELY (data_ && is_owner_ && target.data_ && capacity_ != 0 && capacity_ == target_size) {
+ + + - +
+ + - + +
- + - + ]
244 : : if VUNLIKELY (this == &target) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
245 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
246 : : }
247 : :
248 : 0 : header = target.header;
249 : :
250 : 0 : update_time_ns_ = target.update_time_ns_;
251 : 0 : std::memcpy(source_id_, target.source_id_, sizeof(source_id_));
252 : 0 : channel_ = target.channel_;
253 : 0 : freq_ = target.freq_;
254 : 0 : count_ = target.count_;
255 : 0 : pack_size_ = target.pack_size_;
256 : 0 : reserved_buf_ = target.reserved_buf_;
257 : 0 : reserved_buf2_ = target.reserved_buf2_;
258 : 0 : reserved_buf3_ = target.reserved_buf3_;
259 : 0 : reserved_buf4_ = target.reserved_buf4_;
260 : 0 : reserved_buf5_ = target.reserved_buf5_;
261 : :
262 : 0 : std::memcpy(data_, target.data_, target_size);
263 : :
264 : 0 : return true;
265 : : }
266 : :
267 [ + + ]: 4 : if VUNLIKELY (!shallow_copy(target)) {
268 : 1 : return false;
269 : : }
270 : :
271 [ + - + - ]: 3 : if (data_ && target_size != 0) {
272 : 3 : capacity_ = target_size;
273 : 3 : data_ = Bytes::bytes_malloc(capacity_);
274 : :
275 : 3 : std::memcpy(data_, target.data_, target_size);
276 : :
277 : 3 : is_owner_ = true;
278 : : }
279 : :
280 : 3 : return true;
281 : : }
282 : :
283 : 4 : bool ObjectArray::move_copy(ObjectArray& target) noexcept {
284 [ + + ]: 4 : if VUNLIKELY (!shallow_copy(target)) {
285 : 1 : return false;
286 : : }
287 : :
288 : 3 : is_owner_ = target.is_owner_;
289 : 3 : capacity_ = target.capacity_;
290 : :
291 : 3 : target.update_time_ns_ = 0;
292 : 3 : std::memset(target.source_id_, 0, sizeof(target.source_id_));
293 : 3 : target.channel_ = 0;
294 : 3 : target.freq_ = 0;
295 : 3 : target.count_ = 0;
296 : 3 : target.pack_size_ = 0;
297 : 3 : target.reserved_buf_ = 0;
298 : 3 : target.reserved_buf2_ = 0;
299 : 3 : target.reserved_buf3_ = 0;
300 : 3 : target.reserved_buf4_ = 0;
301 : 3 : target.reserved_buf5_ = 0;
302 : 3 : target.is_owner_ = false;
303 : 3 : target.data_ = nullptr;
304 : 3 : target.capacity_ = 0;
305 : :
306 : : #if defined(__GNUC__) && !defined(__clang__)
307 : : #pragma GCC diagnostic push
308 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
309 : : #if __GNUC__ >= 11
310 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
311 : : #endif
312 : : #endif
313 : :
314 : 3 : std::memset(&target.header, 0, sizeof(header));
315 : :
316 : : #if defined(__GNUC__) && !defined(__clang__)
317 : : #pragma GCC diagnostic pop
318 : : #endif
319 : :
320 : 3 : return true;
321 : : }
322 : :
323 : 39 : bool ObjectArray::create(size_t count) noexcept {
324 [ + + ]: 39 : if VUNLIKELY (count == 0) {
325 : 1 : return false;
326 : : }
327 : :
328 [ - + ]: 38 : if VUNLIKELY (count > std::numeric_limits<size_t>::max() / sizeof(Object)) {
329 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
330 : : }
331 : :
332 [ + + + - : 38 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
333 : 1 : Bytes::bytes_free(data_, capacity_);
334 : : }
335 : :
336 : 38 : pack_size_ = sizeof(Object);
337 : 38 : capacity_ = count * pack_size_;
338 : 38 : count_ = 0;
339 : :
340 : 38 : data_ = Bytes::bytes_malloc(capacity_);
341 : :
342 : 38 : is_owner_ = true;
343 : :
344 : 38 : return true;
345 : : }
346 : :
347 : 5 : void ObjectArray::clear() noexcept {
348 [ + + + - : 5 : if (is_owner_ && data_ && capacity_ != 0) {
+ - ]
349 : 4 : Bytes::bytes_free(data_, capacity_);
350 : : }
351 : :
352 : 5 : update_time_ns_ = 0;
353 : 5 : std::memset(source_id_, 0, sizeof(source_id_));
354 : 5 : channel_ = 0;
355 : 5 : freq_ = 0;
356 : 5 : count_ = 0;
357 : 5 : pack_size_ = 0;
358 : 5 : is_owner_ = false;
359 : 5 : data_ = nullptr;
360 : 5 : capacity_ = 0;
361 : :
362 : : #if defined(__GNUC__) && !defined(__clang__)
363 : : #pragma GCC diagnostic push
364 : : #pragma GCC diagnostic ignored "-Wclass-memaccess"
365 : : #if __GNUC__ >= 11
366 : : #pragma GCC diagnostic ignored "-Wstringop-overread"
367 : : #endif
368 : : #endif
369 : :
370 : 5 : std::memset(&header, 0, sizeof(header));
371 : :
372 : : #if defined(__GNUC__) && !defined(__clang__)
373 : : #pragma GCC diagnostic pop
374 : : #endif
375 : 5 : }
376 : :
377 : 43 : bool ObjectArray::push_value(const Object& object) noexcept {
378 [ + + - + : 43 : if VUNLIKELY (!is_owner_ || !data_ || pack_size_ == 0 || capacity_ == 0) {
+ + - + +
+ - + +
+ ]
379 : 1 : return false;
380 : : }
381 : :
382 [ + + ]: 42 : if VUNLIKELY ((static_cast<size_t>(count_) + 1) * pack_size_ > capacity_) {
383 : 1 : return false;
384 : : }
385 : :
386 [ - + ]: 41 : if VUNLIKELY (pack_size_ != sizeof(Object)) {
387 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
388 : : }
389 : :
390 : 41 : std::memcpy(data_ + (static_cast<size_t>(count_) * pack_size_), &object, sizeof(Object));
391 : :
392 : 41 : ++count_;
393 : :
394 : 41 : return true;
395 : : }
396 : :
397 : 2 : bool ObjectArray::set_value(uint32_t index, const Object& object) noexcept {
398 [ + - - + : 2 : if VUNLIKELY (!is_owner_ || !data_ || pack_size_ == 0 || capacity_ == 0) {
+ - - + +
- - + -
+ ]
399 : 0 : return false;
400 : : }
401 : :
402 [ + + ]: 2 : if VUNLIKELY (index >= count_) {
403 : 1 : return false;
404 : : }
405 : :
406 [ - + ]: 1 : if VUNLIKELY (pack_size_ != sizeof(Object)) {
407 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
408 : : }
409 : :
410 : 1 : std::memcpy(data_ + (static_cast<size_t>(index) * pack_size_), &object, sizeof(Object));
411 : :
412 : 1 : return true;
413 : : }
414 : :
415 : 8 : bool ObjectArray::get_value(uint32_t index, Object& object) const noexcept {
416 [ + - - + : 8 : if VUNLIKELY (!data_ || pack_size_ == 0) {
- + ]
417 : 0 : object = Object{};
418 : 0 : return false;
419 : : }
420 : :
421 [ + + ]: 8 : if VUNLIKELY (index >= count_) {
422 : 2 : object = Object{};
423 : 2 : return false;
424 : : }
425 : :
426 [ - + ]: 6 : if VUNLIKELY (pack_size_ != sizeof(Object)) {
427 : : object = Object{}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
428 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
429 : : }
430 : :
431 : 6 : std::memcpy(&object, data_ + (static_cast<size_t>(index) * pack_size_), sizeof(Object));
432 : :
433 : 6 : return true;
434 : : }
435 : :
436 : 2 : ObjectArray::Object ObjectArray::get_value(uint32_t index) const noexcept {
437 : 2 : Object object;
438 : :
439 : 2 : get_value(index, object);
440 : :
441 : 2 : return object;
442 : : }
443 : :
444 : 3 : bool ObjectArray::resize(uint32_t count) noexcept {
445 [ + + - + : 3 : if VUNLIKELY (!is_owner_ || !data_ || pack_size_ == 0 || capacity_ == 0) {
+ + - + +
+ - + +
+ ]
446 : 1 : return false;
447 : : }
448 : :
449 [ + + ]: 2 : if VUNLIKELY (static_cast<size_t>(count) * pack_size_ > capacity_) {
450 : 1 : return false;
451 : : }
452 : :
453 : 1 : count_ = count;
454 : :
455 : 1 : return true;
456 : : }
457 : :
458 : 3 : uint64_t ObjectArray::update_time_ns() const noexcept { return update_time_ns_; }
459 : :
460 : 8 : std::string_view ObjectArray::source_id() const noexcept {
461 : 8 : return {source_id_, ::strnlen(source_id_, sizeof(source_id_))};
462 : : }
463 : :
464 : 4 : uint32_t ObjectArray::channel() const noexcept { return channel_; }
465 : :
466 : 4 : uint32_t ObjectArray::freq() const noexcept { return freq_; }
467 : :
468 : 22 : uint32_t ObjectArray::count() const noexcept { return count_; }
469 : :
470 : 11 : uint32_t ObjectArray::pack_size() const noexcept { return pack_size_; }
471 : :
472 : 9 : const ObjectArray::Object* ObjectArray::objects(uint32_t index) const noexcept {
473 [ + + - + : 9 : if VUNLIKELY (!data_ || pack_size_ != sizeof(Object) || index >= count_) {
+ + + + +
+ ]
474 : 2 : return nullptr;
475 : : }
476 : :
477 : 7 : return reinterpret_cast<const Object*>(data_ + (static_cast<size_t>(index) * pack_size_));
478 : : }
479 : :
480 : 20 : const uint8_t* ObjectArray::data() const noexcept { return data_; }
481 : :
482 : 13 : size_t ObjectArray::capacity() const noexcept { return capacity_; }
483 : :
484 : 18 : bool ObjectArray::is_owner() const noexcept { return is_owner_; }
485 : :
486 : 2 : void ObjectArray::set_update_time_ns(uint64_t update_time_ns) noexcept { update_time_ns_ = update_time_ns; }
487 : :
488 : 7 : void ObjectArray::set_source_id(std::string_view source_id) noexcept {
489 : 7 : std::memset(source_id_, 0, sizeof(source_id_));
490 : :
491 : 7 : size_t copy_size = source_id.size();
492 : :
493 [ + + ]: 7 : if (copy_size >= sizeof(source_id_)) {
494 : 1 : copy_size = sizeof(source_id_) - 1;
495 : : }
496 : :
497 [ + - ]: 7 : if VLIKELY (copy_size != 0) {
498 : 7 : std::memcpy(source_id_, source_id.data(), copy_size);
499 : : }
500 : 7 : }
501 : :
502 : 3 : void ObjectArray::set_channel(uint32_t channel) noexcept { channel_ = channel; }
503 : :
504 : 3 : void ObjectArray::set_freq(uint32_t freq) noexcept { freq_ = freq; }
505 : :
506 : : } // namespace zerocopy
507 : :
508 : : } // namespace vlink
|