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 "./base/bytes.h"
25 : :
26 : : #include <lzav/lzav.h>
27 : :
28 : : #include <array>
29 : : #include <charconv>
30 : : #include <limits>
31 : : #include <sstream>
32 : : #include <string>
33 : : #include <utility>
34 : : #include <vector>
35 : :
36 : : #include "./base/logger.h"
37 : : #include "./base/memory_pool.h"
38 : :
39 : : #define VLINK_BYTES_MEM_RESET 0
40 : :
41 : : namespace vlink {
42 : :
43 : : static constexpr uint32_t kCrc32Polynomial = 0xEDB88320U;
44 : :
45 : : static constexpr uint64_t kCrc64Polynomial = 0x42F0E1EBA9EA3693ULL;
46 : :
47 : : static constexpr const char kBase64Table[] =
48 : : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
49 : : "abcdefghijklmnopqrstuvwxyz"
50 : : "0123456789+/";
51 : :
52 : : static constexpr const char* kHexTable[256] = {
53 : : "00 ", "01 ", "02 ", "03 ", "04 ", "05 ", "06 ", "07 ", "08 ", "09 ", "0A ", "0B ", "0C ", "0D ", "0E ", "0F ",
54 : : "10 ", "11 ", "12 ", "13 ", "14 ", "15 ", "16 ", "17 ", "18 ", "19 ", "1A ", "1B ", "1C ", "1D ", "1E ", "1F ",
55 : : "20 ", "21 ", "22 ", "23 ", "24 ", "25 ", "26 ", "27 ", "28 ", "29 ", "2A ", "2B ", "2C ", "2D ", "2E ", "2F ",
56 : : "30 ", "31 ", "32 ", "33 ", "34 ", "35 ", "36 ", "37 ", "38 ", "39 ", "3A ", "3B ", "3C ", "3D ", "3E ", "3F ",
57 : : "40 ", "41 ", "42 ", "43 ", "44 ", "45 ", "46 ", "47 ", "48 ", "49 ", "4A ", "4B ", "4C ", "4D ", "4E ", "4F ",
58 : : "50 ", "51 ", "52 ", "53 ", "54 ", "55 ", "56 ", "57 ", "58 ", "59 ", "5A ", "5B ", "5C ", "5D ", "5E ", "5F ",
59 : : "60 ", "61 ", "62 ", "63 ", "64 ", "65 ", "66 ", "67 ", "68 ", "69 ", "6A ", "6B ", "6C ", "6D ", "6E ", "6F ",
60 : : "70 ", "71 ", "72 ", "73 ", "74 ", "75 ", "76 ", "77 ", "78 ", "79 ", "7A ", "7B ", "7C ", "7D ", "7E ", "7F ",
61 : : "80 ", "81 ", "82 ", "83 ", "84 ", "85 ", "86 ", "87 ", "88 ", "89 ", "8A ", "8B ", "8C ", "8D ", "8E ", "8F ",
62 : : "90 ", "91 ", "92 ", "93 ", "94 ", "95 ", "96 ", "97 ", "98 ", "99 ", "9A ", "9B ", "9C ", "9D ", "9E ", "9F ",
63 : : "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ", "A6 ", "A7 ", "A8 ", "A9 ", "AA ", "AB ", "AC ", "AD ", "AE ", "AF ",
64 : : "B0 ", "B1 ", "B2 ", "B3 ", "B4 ", "B5 ", "B6 ", "B7 ", "B8 ", "B9 ", "BA ", "BB ", "BC ", "BD ", "BE ", "BF ",
65 : : "C0 ", "C1 ", "C2 ", "C3 ", "C4 ", "C5 ", "C6 ", "C7 ", "C8 ", "C9 ", "CA ", "CB ", "CC ", "CD ", "CE ", "CF ",
66 : : "D0 ", "D1 ", "D2 ", "D3 ", "D4 ", "D5 ", "D6 ", "D7 ", "D8 ", "D9 ", "DA ", "DB ", "DC ", "DD ", "DE ", "DF ",
67 : : "E0 ", "E1 ", "E2 ", "E3 ", "E4 ", "E5 ", "E6 ", "E7 ", "E8 ", "E9 ", "EA ", "EB ", "EC ", "ED ", "EE ", "EF ",
68 : : "F0 ", "F1 ", "F2 ", "F3 ", "F4 ", "F5 ", "F6 ", "F7 ", "F8 ", "F9 ", "FA ", "FB ", "FC ", "FD ", "FE ", "FF "};
69 : :
70 : : static constexpr uint8_t kCompressHeaderMagic[4] = {
71 : : 0x17,
72 : : 0x49,
73 : : 0xB2,
74 : : 0x6F,
75 : : };
76 : :
77 : : static constexpr uint8_t kCompressFooterMagic[4] = {
78 : : 0xA7,
79 : : 0x05,
80 : : 0xED,
81 : : 0x71,
82 : : };
83 : :
84 : : static constexpr size_t kMaxCompressCacheSize = 1024UL * 1024UL;
85 : :
86 : 33 : static constexpr auto check_magic(const uint8_t* data, const uint8_t* magic, size_t len) noexcept {
87 [ + + ]: 125 : for (size_t i = 0; i < len; ++i) {
88 [ + + ]: 102 : if (data[i] != magic[i]) {
89 : 10 : return false;
90 : : }
91 : : }
92 : :
93 : 23 : return true;
94 : : }
95 : :
96 : 4 : static auto& bytes_compress_cache() noexcept {
97 [ + + ]: 4 : thread_local std::vector<uint8_t> compress_cache(kMaxCompressCacheSize);
98 : :
99 : 4 : return compress_cache;
100 : : }
101 : :
102 : : // Bytes
103 : 1516 : uint8_t* Bytes::bytes_malloc(size_t size) noexcept {
104 : 1516 : return static_cast<uint8_t*>(MemoryPool::global_instance().allocate(size));
105 : : }
106 : :
107 : 1511 : void Bytes::bytes_free(uint8_t* ptr, size_t size) noexcept { MemoryPool::global_instance().deallocate(ptr, size); }
108 : :
109 : 2111 : void Bytes::init_memory_pool() noexcept { (void)MemoryPool::global_instance(); }
110 : :
111 : 1 : void Bytes::release_memory_pool() noexcept { MemoryPool::global_instance().clear(); }
112 : :
113 : 2543 : Bytes Bytes::create(size_t size, uint8_t offset) noexcept {
114 : : #if defined(__arm__) || defined(__x86__) || defined(__i386__)
115 : : #ifndef __ANDROID__
116 : : #warning "Bytes No support for 32-bit architecture."
117 : : #endif
118 : : #else
119 : : static_assert(sizeof(Bytes) == 128, "Sizeof must be 128 bytes.");
120 : : #endif
121 : :
122 : 2543 : return Bytes(kCreate, nullptr, size, offset, false);
123 : : }
124 : :
125 : 504 : Bytes Bytes::shallow_copy(uint8_t* data, size_t size) noexcept {
126 [ + + ]: 504 : return Bytes(kShallowCopy, size == 0 ? nullptr : data, size, 0, false);
127 : : }
128 : :
129 : 820 : Bytes Bytes::shallow_copy(const uint8_t* data, size_t size) noexcept {
130 [ + + ]: 820 : return Bytes(kShallowCopy, size == 0 ? nullptr : const_cast<uint8_t*>(data), size, 0, false);
131 : : }
132 : :
133 : 3 : Bytes Bytes::shallow_copy_ptr(void* data) noexcept {
134 : 3 : return Bytes(kShallowCopy, static_cast<uint8_t*>(data), 0, 0, false);
135 : : }
136 : :
137 : 15 : Bytes Bytes::deep_copy(uint8_t* data, size_t size, uint8_t offset) noexcept {
138 [ + - ]: 15 : return Bytes(kDeepCopy, size == 0 ? nullptr : data, size, offset, false);
139 : : }
140 : :
141 : 997 : Bytes Bytes::deep_copy(const uint8_t* data, size_t size, uint8_t offset) noexcept {
142 [ + + ]: 997 : return Bytes(kDeepCopy, size == 0 ? nullptr : const_cast<uint8_t*>(data), size, offset, false);
143 : : }
144 : :
145 : 10 : Bytes Bytes::loan_internal(uint8_t* data, size_t size) noexcept {
146 [ + + ]: 10 : return Bytes(kShallowCopy, size == 0 ? nullptr : data, size, 0, true);
147 : : }
148 : :
149 : 1 : Bytes Bytes::loan_internal(const uint8_t* data, size_t size) noexcept {
150 [ + - ]: 1 : return Bytes(kShallowCopy, size == 0 ? nullptr : const_cast<uint8_t*>(data), size, 0, true);
151 : : }
152 : :
153 : 412 : Bytes Bytes::from_string(const std::string& str, uint8_t offset) noexcept {
154 [ + + ]: 412 : if (str.empty()) {
155 : 1 : return Bytes(kDeepCopy, nullptr, 0, offset, false);
156 : : }
157 : :
158 : 411 : return Bytes(kDeepCopy, reinterpret_cast<uint8_t*>(const_cast<char*>(str.data())), str.size(), offset, false);
159 : : }
160 : :
161 : 17 : Bytes Bytes::from_user_input(const std::string& str, bool* ok) noexcept {
162 [ + + ]: 17 : if (str.empty()) {
163 [ + + ]: 2 : if (ok) {
164 : 1 : *ok = false;
165 : : }
166 : :
167 : 2 : return Bytes();
168 : : }
169 : :
170 : 15 : std::vector<uint8_t> vec;
171 : 15 : vec.reserve((str.size() + 1) / 2);
172 : :
173 [ + + ]: 15 : thread_local std::istringstream ss;
174 : 15 : ss.clear();
175 : 15 : ss.str(str);
176 : :
177 : 15 : std::string byte_str;
178 : :
179 : 31 : auto parse_byte = [&vec, ok](std::string_view token) -> bool {
180 [ - + ]: 14 : if (token.empty()) {
181 : : if (ok) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
182 : : *ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
183 : : }
184 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
185 : : }
186 : :
187 : 14 : uint32_t parsed_value = 0;
188 [ + - ]: 14 : auto [p, error] = std::from_chars(token.data(), token.data() + token.size(), parsed_value, 16);
189 : :
190 [ + + + + : 14 : if VUNLIKELY (error != std::errc() || p != token.data() + token.size() || parsed_value > 0xFF) {
+ + - + +
+ ]
191 [ + + ]: 4 : if (ok) {
192 : 3 : *ok = false;
193 : : }
194 : 4 : return false;
195 : : }
196 : :
197 [ + - ]: 10 : vec.emplace_back(static_cast<uint8_t>(parsed_value));
198 : 10 : return true;
199 : 15 : };
200 : :
201 [ + + ]: 23 : while (ss >> std::skipws >> byte_str) {
202 : 19 : std::string_view token(byte_str);
203 : :
204 [ + - + + : 19 : if (token.size() >= 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) {
+ + + + +
+ ]
205 : 6 : token.remove_prefix(2);
206 : : }
207 : :
208 [ + + + + : 19 : if VUNLIKELY (token.empty() || (token.size() > 2 && (token.size() % 2) != 0)) {
+ + + + +
+ ]
209 [ + + ]: 7 : if (ok) {
210 : 4 : *ok = false;
211 : : }
212 : :
213 : 11 : return Bytes();
214 : : }
215 : :
216 [ + + ]: 12 : if (token.size() <= 2) {
217 [ + + ]: 10 : if VUNLIKELY (!parse_byte(token)) {
218 : 3 : return Bytes();
219 : : }
220 : 7 : continue;
221 : : }
222 : :
223 [ + + ]: 5 : for (size_t index = 0; index < token.size(); index += 2) {
224 [ + + ]: 4 : if VUNLIKELY (!parse_byte(token.substr(index, 2))) {
225 : 1 : return Bytes();
226 : : }
227 : : }
228 : : }
229 : :
230 [ + + ]: 4 : if (ok) {
231 : 3 : *ok = true;
232 : : }
233 : :
234 : 4 : return Bytes(vec);
235 : 15 : }
236 : :
237 : 6 : std::string Bytes::convert_to_hex_str(const uint8_t* value, size_t size) noexcept {
238 : 6 : std::string str;
239 : :
240 [ + + + + ]: 6 : if (!value || size == 0) {
241 : 3 : str = "{}";
242 : 3 : return str;
243 : : }
244 : :
245 : : static constexpr uint8_t kCount = 3;
246 : :
247 : 3 : str.resize(size * kCount);
248 : :
249 [ + + ]: 12 : for (size_t i = 0; i < size; ++i) {
250 : 9 : std::memcpy(&str[(i * kCount)], kHexTable[value[i]], kCount);
251 : : }
252 : :
253 : 3 : str.pop_back();
254 : :
255 : 3 : return str;
256 : : }
257 : :
258 : 3 : Bytes Bytes::reverse_order(const Bytes& target) noexcept {
259 [ + + ]: 3 : if (target.empty()) {
260 : 1 : return Bytes();
261 : : }
262 : :
263 : 2 : size_t size = target.size();
264 : :
265 : 2 : Bytes result = Bytes::create(size);
266 : :
267 [ - + ]: 2 : if VUNLIKELY (result.empty()) {
268 : : return result; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
269 : : }
270 : :
271 [ + + ]: 7 : for (size_t i = 0; i < size; ++i) {
272 : 5 : result[i] = target[size - 1 - i];
273 : : }
274 : :
275 : 2 : return result;
276 : 2 : }
277 : :
278 : 6 : std::string Bytes::encode_to_base64(const Bytes& target) noexcept {
279 [ + + ]: 6 : if (target.empty()) {
280 : 1 : return std::string();
281 : : }
282 : :
283 : 5 : std::string encoded;
284 : :
285 : 5 : int val = 0;
286 : 5 : int valb = -6;
287 : :
288 [ + + ]: 63 : for (auto c : target) {
289 : 58 : val = (val << 8) + c;
290 : 58 : valb += 8;
291 : :
292 [ + + ]: 134 : while (valb >= 0) {
293 : 76 : encoded.push_back(kBase64Table[(val >> valb) & 0x3F]);
294 : 76 : valb -= 6;
295 : : }
296 : : }
297 : :
298 [ + + ]: 5 : if (valb > -6) {
299 : 3 : encoded.push_back(kBase64Table[((val << 8) >> (valb + 8)) & 0x3F]);
300 : : }
301 : :
302 : 5 : size_t padding = (4 - (encoded.size() % 4)) % 4;
303 : 5 : encoded.append(padding, '=');
304 : :
305 : 5 : return encoded;
306 : 5 : }
307 : :
308 : 10 : Bytes Bytes::decode_from_base64(const std::string& target) noexcept {
309 [ + + ]: 10 : if (target.size() % 4 != 0) {
310 : 1 : return Bytes();
311 : : }
312 : :
313 : 9 : size_t padding = 0;
314 : 9 : size_t first_padding = target.find('=');
315 : :
316 [ + + ]: 9 : if (first_padding != std::string::npos) {
317 : 5 : padding = target.size() - first_padding;
318 : :
319 [ + + ]: 5 : if VUNLIKELY (padding > 2) {
320 : 1 : return Bytes();
321 : : }
322 : :
323 [ + + ]: 10 : for (size_t i = first_padding; i < target.size(); ++i) {
324 [ + + ]: 7 : if VUNLIKELY (target[i] != '=') {
325 : 1 : return Bytes();
326 : : }
327 : : }
328 : : }
329 : :
330 : 7 : std::vector<uint8_t> buffer;
331 : 7 : buffer.reserve((target.size() * 3) / 4);
332 : :
333 : 1 : static const auto kTable = [] {
334 : 1 : std::array<int, 256> tbl{};
335 [ + - ]: 1 : tbl.fill(-1);
336 : :
337 [ + + ]: 65 : for (size_t i = 0; i < sizeof(kBase64Table) - 1; ++i) {
338 : 64 : tbl[static_cast<unsigned char>(kBase64Table[i])] = i;
339 : : }
340 : :
341 : 1 : return tbl;
342 [ + + + - ]: 7 : }();
343 : :
344 : 7 : uint32_t val = 0;
345 : 7 : int valb = -8;
346 : :
347 [ + + ]: 7 : const size_t decode_size = first_padding == std::string::npos ? target.size() : first_padding;
348 : :
349 [ + + ]: 86 : for (size_t i = 0; i < decode_size; ++i) {
350 : 80 : auto c = target[i];
351 : :
352 : 80 : int idx = kTable[static_cast<unsigned char>(c)];
353 : :
354 [ + + ]: 80 : if VUNLIKELY (idx == -1) {
355 : 1 : return Bytes();
356 : : }
357 : :
358 : 79 : val = (val << 6) + idx;
359 : 79 : valb += 6;
360 : :
361 [ + + ]: 79 : if (valb >= 0) {
362 : 58 : buffer.emplace_back(static_cast<uint8_t>((val >> valb) & 0xFF));
363 : 58 : valb -= 8;
364 : : }
365 : : }
366 : :
367 : 6 : return Bytes(buffer);
368 : 7 : }
369 : :
370 : 9 : uint32_t Bytes::get_crc_32(const Bytes& target) noexcept {
371 : 9 : uint32_t crc = 0xFFFFFFFFU;
372 : :
373 : : static constexpr auto kCrc32Table = []() {
374 : : std::array<uint32_t, 256> table{};
375 : :
376 : : for (uint32_t i = 0; i < 256; ++i) {
377 : : uint32_t crc = i;
378 : :
379 : : for (uint32_t bit = 0; bit < 8; ++bit) {
380 : : crc = (crc & 1U) ? (kCrc32Polynomial ^ (crc >> 1U)) : (crc >> 1U);
381 : : }
382 : :
383 : : table[i] = crc;
384 : : }
385 : :
386 : : return table;
387 : : }();
388 : :
389 [ + + ]: 77 : for (auto u : target) {
390 : 68 : crc = kCrc32Table[(crc ^ u) & 0xFFU] ^ (crc >> 8U);
391 : : }
392 : :
393 : 9 : return crc ^ 0xFFFFFFFFU;
394 : : }
395 : :
396 : 6 : uint64_t Bytes::get_crc_64(const Bytes& target) noexcept {
397 : 6 : uint64_t crc = 0x0000000000000000ULL;
398 : :
399 : : static constexpr auto kCrc64Table = []() {
400 : : std::array<uint64_t, 256> table{};
401 : :
402 : : for (uint64_t i = 0; i < 256; ++i) {
403 : : uint64_t crc = i << 56U;
404 : :
405 : : for (uint64_t bit = 0; bit < 8; ++bit) {
406 : : crc = (crc & 0x8000000000000000ULL) ? (kCrc64Polynomial ^ (crc << 1U)) : (crc << 1U);
407 : : }
408 : :
409 : : table[i] = crc;
410 : : }
411 : :
412 : : return table;
413 : : }();
414 : :
415 [ + + ]: 27 : for (auto u : target) {
416 : 21 : crc = kCrc64Table[((crc >> 56U) ^ u) & 0xFFU] ^ (crc << 8U);
417 : : }
418 : :
419 : 6 : return crc ^ 0x0000000000000000ULL;
420 : : }
421 : :
422 : 21982 : Bytes::Bytes() noexcept = default;
423 : :
424 : 1335 : Bytes::Bytes(const Bytes& target) noexcept {
425 [ - + ]: 1335 : if VUNLIKELY (this == &target) {
426 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
427 : : }
428 : :
429 [ + + + - ]: 1335 : if (target.offset_ > 0 && target.data_) {
430 : 1 : process_type(kCreate, nullptr, target.size_, target.offset_, false);
431 : :
432 [ + - ]: 1 : if VLIKELY (data_) {
433 : 1 : std::memcpy(data_, target.data_, target.size_ + target.offset_);
434 : : }
435 : : } else {
436 : 1334 : process_type(kDeepCopy, target.data_, target.size_, target.offset_, false);
437 : : }
438 : : }
439 : :
440 : 765 : Bytes::Bytes(Bytes&& target) noexcept {
441 [ - + ]: 765 : if VUNLIKELY (this == &target) {
442 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
443 : : }
444 : :
445 : 765 : process_type(kMove, target.data_, target.size_, target.offset_, target.is_loaned_, &target);
446 : : }
447 : :
448 : 153 : Bytes::Bytes(const std::initializer_list<uint8_t>& list) noexcept {
449 : 153 : process_type(kCreate, nullptr, list.size(), 0, false);
450 : :
451 [ + - ]: 153 : if VLIKELY (data_) {
452 : 153 : size_t index = 0;
453 : :
454 [ + + ]: 465 : for (const auto& value : list) {
455 : 312 : data_[index] = value;
456 : 312 : ++index;
457 : : }
458 : : }
459 : 153 : }
460 : :
461 : 17 : Bytes::Bytes(const std::vector<uint8_t>& data) noexcept {
462 : 17 : process_type(kDeepCopy, const_cast<uint8_t*>(data.data()), data.size(), 0, false);
463 : 17 : }
464 : :
465 : 29561 : Bytes::~Bytes() noexcept {
466 [ + + + + : 29561 : if (data_ && data_ != stack_data_ && is_owner_ && capacity_ + offset_ > kStackSize) {
+ + + - ]
467 : 1072 : bytes_free(data_, capacity_ + offset_);
468 : : }
469 : 29560 : }
470 : :
471 : 419 : Bytes& Bytes::operator=(const Bytes& target) noexcept {
472 [ + + ]: 419 : if VUNLIKELY (this == &target) {
473 : 75 : return *this;
474 : : }
475 : :
476 [ + + + - ]: 344 : if (target.offset_ > 0 && target.data_) {
477 : 16 : Bytes tmp;
478 : 16 : tmp.process_type(kCreate, nullptr, target.size_, target.offset_, false);
479 : :
480 [ + - ]: 16 : if VLIKELY (tmp.data_) {
481 : 16 : std::memcpy(tmp.data_, target.data_, target.size_ + target.offset_);
482 : : }
483 : :
484 : 16 : *this = std::move(tmp);
485 : 16 : } else {
486 : 328 : process_type(kDeepCopy, target.data_, target.size_, target.offset_, false);
487 : : }
488 : :
489 : 344 : return *this;
490 : : }
491 : :
492 : 7223 : Bytes& Bytes::operator=(Bytes&& target) noexcept {
493 [ + + ]: 7223 : if VUNLIKELY (this == &target) {
494 : 1 : return *this;
495 : : }
496 : :
497 : 7222 : process_type(kMove, target.data_, target.size_, target.offset_, target.is_loaned_, &target);
498 : :
499 : 7225 : return *this;
500 : : }
501 : :
502 : 1 : Bytes& Bytes::operator=(const std::vector<uint8_t>& data) noexcept {
503 : 1 : process_type(kDeepCopy, const_cast<uint8_t*>(data.data()), data.size(), 0, false);
504 : :
505 : 1 : return *this;
506 : : }
507 : :
508 : 41 : bool Bytes::operator==(const Bytes& target) const noexcept {
509 [ + + ]: 41 : if VUNLIKELY (this == &target) {
510 : 1 : return true;
511 : : }
512 : :
513 [ + + ]: 40 : if (size_ != target.size_) {
514 : 6 : return false;
515 : : }
516 : :
517 [ + + ]: 34 : const uint8_t* this_data = data_ ? (data_ + offset_) : nullptr;
518 [ + + ]: 34 : const uint8_t* target_data = target.data_ ? (target.data_ + target.offset_) : nullptr;
519 : :
520 [ + + ]: 34 : if (this_data == target_data) {
521 : 4 : return true;
522 : : }
523 : :
524 [ + - - + ]: 30 : if (!this_data || !target_data) {
525 : : return size_ == 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
526 : : }
527 : :
528 : 30 : return std::memcmp(this_data, target_data, size_) == 0;
529 : : }
530 : :
531 : 8 : bool Bytes::operator!=(const Bytes& target) const noexcept { return !(*this == target); }
532 : :
533 : 9 : bool Bytes::operator==(const std::vector<uint8_t>& data) const noexcept {
534 [ + + ]: 9 : if (size_ != data.size()) {
535 : 2 : return false;
536 : : }
537 : :
538 [ + + ]: 7 : const uint8_t* this_data = data_ ? (data_ + offset_) : nullptr;
539 : :
540 [ + + ]: 7 : if (!this_data) {
541 : 1 : return data.empty();
542 : : }
543 : :
544 [ + + ]: 6 : if (this_data == data.data()) {
545 : 2 : return true;
546 : : }
547 : :
548 : 4 : return std::memcmp(this_data, data.data(), size_) == 0;
549 : : }
550 : :
551 : 3 : bool Bytes::operator!=(const std::vector<uint8_t>& data) const noexcept { return !(*this == data); }
552 : :
553 : 3 : std::vector<uint8_t> Bytes::to_raw_data() const noexcept {
554 [ + + + + : 3 : if VUNLIKELY (empty() || is_ptr() || !data_) {
+ + - + +
+ ]
555 : 2 : return std::vector<uint8_t>();
556 : : }
557 : :
558 : 1 : return std::vector<uint8_t>(data_ + offset_, data_ + offset_ + size_);
559 : : }
560 : :
561 : 252 : std::string Bytes::to_string() const noexcept {
562 [ + + + + : 252 : if VUNLIKELY (empty() || is_ptr() || !data_) {
+ + - + +
+ ]
563 : 3 : return std::string();
564 : : }
565 : :
566 : 249 : return std::string(reinterpret_cast<const char*>(data_ + offset_), size_);
567 : : }
568 : :
569 : 4 : std::string_view Bytes::to_string_view() const noexcept {
570 [ + + + + : 4 : if VUNLIKELY (empty() || is_ptr() || !data_) {
+ + - + +
+ ]
571 : 2 : return std::string_view();
572 : : }
573 : :
574 : 2 : return std::string_view(reinterpret_cast<const char*>(data_ + offset_), size_);
575 : : }
576 : :
577 : 25 : bool Bytes::is_compress_data(const uint8_t* data, size_t size) noexcept {
578 [ + + ]: 25 : if VUNLIKELY (!data) {
579 : 1 : return false;
580 : : }
581 : :
582 [ + + ]: 24 : if (size < 12 + 1) {
583 : 3 : return false;
584 : : }
585 : :
586 [ + + ]: 21 : if (!check_magic(data, kCompressHeaderMagic, sizeof(kCompressHeaderMagic))) {
587 : 9 : return false;
588 : : }
589 : :
590 [ + + ]: 12 : if (!check_magic(data + size - sizeof(kCompressFooterMagic), kCompressFooterMagic, sizeof(kCompressFooterMagic))) {
591 : 1 : return false;
592 : : }
593 : :
594 : 11 : return true;
595 : : }
596 : :
597 : 17 : Bytes Bytes::compress_data(const uint8_t* data, size_t size, bool high_ratio) noexcept {
598 : 17 : Bytes target_bytes;
599 : :
600 [ + + + + : 17 : if VUNLIKELY (!data || size == 0) {
+ + ]
601 : 2 : return target_bytes;
602 : : }
603 : :
604 [ + + ]: 15 : if VUNLIKELY (size > kMaxCompressCacheSize) {
605 : 2 : CLOG_E("Bytes: Input size too large: %zu.", size);
606 : :
607 : 1 : return target_bytes;
608 : : }
609 : :
610 [ + + - + : 28 : size_t max_compressed_size = high_ratio ? lzav::lzav_compress_bound_hi(size) : lzav::lzav_compress_bound(size);
+ + ]
611 : :
612 : 14 : target_bytes = Bytes::create(sizeof(kCompressHeaderMagic) + 4 + max_compressed_size + sizeof(kCompressFooterMagic));
613 : :
614 [ - + ]: 14 : if VUNLIKELY (target_bytes.empty()) {
615 : : return target_bytes; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
616 : : }
617 : :
618 : 14 : std::memcpy(target_bytes.data(), kCompressHeaderMagic, sizeof(kCompressHeaderMagic));
619 : :
620 : 14 : target_bytes[4] = static_cast<uint8_t>((size >> 24) & 0xFF);
621 : 14 : target_bytes[5] = static_cast<uint8_t>((size >> 16) & 0xFF);
622 : 14 : target_bytes[6] = static_cast<uint8_t>((size >> 8) & 0xFF);
623 : 14 : target_bytes[7] = static_cast<uint8_t>((size) & 0xFF); // NOLINT(readability-redundant-parentheses)
624 : :
625 : 14 : int result = 0;
626 : :
627 [ + + ]: 14 : if (high_ratio) {
628 : 12 : result = lzav::lzav_compress_hi(data, target_bytes.data() + sizeof(kCompressHeaderMagic) + 4,
629 : : static_cast<int>(size), static_cast<int>(max_compressed_size));
630 : : } else {
631 [ + + ]: 2 : if (size < lzav::LZAV_MR5_THR) {
632 : 1 : result = lzav::lzav_compress_mref5(data, target_bytes.data() + sizeof(kCompressHeaderMagic) + 4,
633 : : static_cast<int>(size), static_cast<int>(max_compressed_size),
634 : 1 : bytes_compress_cache().data(), bytes_compress_cache().size());
635 : : } else {
636 : 1 : result = lzav::lzav_compress_mref6(data, target_bytes.data() + sizeof(kCompressHeaderMagic) + 4,
637 : : static_cast<int>(size), static_cast<int>(max_compressed_size),
638 : 1 : bytes_compress_cache().data(), bytes_compress_cache().size());
639 : : }
640 : : }
641 : :
642 [ - + ]: 14 : if VUNLIKELY (result <= 0) {
643 : : target_bytes.clear(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
644 : : return target_bytes; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
645 : : }
646 : :
647 : 14 : size_t final_size = sizeof(kCompressHeaderMagic) + 4 + static_cast<size_t>(result) + sizeof(kCompressFooterMagic);
648 : :
649 [ - + ]: 14 : if VUNLIKELY (!target_bytes.resize(final_size)) {
650 : : target_bytes.clear(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
651 : : return target_bytes; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
652 : : }
653 : :
654 : 14 : std::memcpy(&target_bytes[sizeof(kCompressHeaderMagic) + 4 + result], kCompressFooterMagic,
655 : : sizeof(kCompressFooterMagic));
656 : :
657 : 14 : return target_bytes;
658 : : }
659 : :
660 : 12 : Bytes Bytes::uncompress_data(const uint8_t* data, size_t size, bool check_valid) noexcept {
661 : 12 : Bytes target_bytes;
662 : :
663 : 12 : constexpr size_t kMinCompressedSize = sizeof(kCompressHeaderMagic) + 4 + sizeof(kCompressFooterMagic);
664 : :
665 [ + + + + : 12 : if VUNLIKELY (!data || size < kMinCompressedSize) {
+ + ]
666 : 2 : return target_bytes;
667 : : }
668 : :
669 [ + + ]: 10 : if (check_valid) {
670 [ + + ]: 6 : if VUNLIKELY (!is_compress_data(data, size)) {
671 : 1 : return target_bytes;
672 : : }
673 : : }
674 : :
675 : 9 : uint32_t target_size = (static_cast<uint32_t>(data[4]) << 24) | (static_cast<uint32_t>(data[5]) << 16) |
676 : 9 : (static_cast<uint32_t>(data[6]) << 8) | (static_cast<uint32_t>(data[7]));
677 : :
678 : 9 : constexpr uint32_t kMaxUncompressedSize = 256 * 1024 * 1024; // 256MB
679 : :
680 [ + - + + : 9 : if VUNLIKELY (target_size == 0 || target_size > kMaxUncompressedSize) {
+ + ]
681 : 2 : CLOG_E("Bytes: Invalid uncompressed size: %u.", target_size);
682 : :
683 : 1 : return target_bytes;
684 : : }
685 : :
686 : 8 : target_bytes = Bytes::create(target_size);
687 : :
688 [ - + ]: 8 : if VUNLIKELY (target_bytes.empty()) {
689 : : return target_bytes; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
690 : : }
691 : :
692 : 8 : size_t compressed_size = size - sizeof(kCompressHeaderMagic) - 4 - sizeof(kCompressFooterMagic);
693 : :
694 [ - + ]: 8 : int result = lzav::lzav_decompress(data + sizeof(kCompressHeaderMagic) + 4, target_bytes.data(),
695 : : static_cast<int>(compressed_size), static_cast<int>(target_size));
696 : :
697 [ + + ]: 8 : if VUNLIKELY (result != static_cast<int>(target_size)) {
698 : 1 : target_bytes.clear();
699 : : }
700 : :
701 : 8 : return target_bytes;
702 : : }
703 : :
704 : 544 : void Bytes::clear() noexcept {
705 [ + + + + : 544 : if (data_ && data_ != stack_data_ && is_owner_ && capacity_ + offset_ > kStackSize) {
+ + + - ]
706 : 1 : bytes_free(data_, capacity_ + offset_);
707 : : }
708 : :
709 : : #if VLINK_BYTES_MEM_RESET
710 : : std::memset(stack_data_, 0, kStackSize);
711 : : #endif
712 : :
713 : 544 : data_ = nullptr;
714 : 544 : size_ = 0;
715 : 544 : capacity_ = 0;
716 : 544 : offset_ = 0;
717 : 544 : is_owner_ = false;
718 : 544 : is_loaned_ = false;
719 : 544 : }
720 : :
721 : 3 : bool Bytes::shrink_to(size_t size) noexcept {
722 [ + + ]: 3 : if VUNLIKELY (!is_owner_) {
723 : 2 : VLOG_E("Bytes: Cannot shrink_to on non-owned Bytes.");
724 : 1 : return false;
725 : : }
726 : :
727 [ + + ]: 2 : if VUNLIKELY (size > size_) {
728 : 2 : VLOG_E("Bytes: Cannot shrink_to a size larger than current size.");
729 : 1 : return false;
730 : : }
731 : :
732 [ - + ]: 1 : if VUNLIKELY (size > capacity_) {
733 : : VLOG_E("Bytes: Cannot shrink_to a size larger than capacity."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
734 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
735 : : }
736 : :
737 : 1 : size_ = size;
738 : :
739 : 1 : return true;
740 : : }
741 : :
742 : 6 : bool Bytes::reserve(size_t new_capacity) noexcept {
743 [ + + ]: 6 : if VUNLIKELY (!is_owner_) {
744 : 2 : VLOG_E("Bytes: Cannot reserve on non-owned Bytes.");
745 : 1 : return false;
746 : : }
747 : :
748 [ + + ]: 5 : if (new_capacity <= capacity_) {
749 : 1 : return true;
750 : : }
751 : :
752 [ + + ]: 4 : if VUNLIKELY (new_capacity > std::numeric_limits<size_t>::max() - offset_) {
753 : 2 : VLOG_E("Bytes: Cannot reserve due to size overflow.");
754 : 1 : return false;
755 : : }
756 : :
757 : 3 : size_t total_new_size = new_capacity + offset_;
758 : 3 : size_t total_old_size = capacity_ + offset_;
759 : :
760 : 3 : uint8_t* new_data = nullptr;
761 : :
762 [ + + ]: 3 : if (total_new_size > kStackSize) {
763 : 2 : new_data = bytes_malloc(total_new_size);
764 : :
765 [ - + ]: 2 : if VUNLIKELY (!new_data) {
766 : : VLOG_E("Bytes: Failed to reserve memory."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
767 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
768 : : }
769 : :
770 : : #if VLINK_BYTES_MEM_RESET
771 : : std::memset(new_data, 0, total_new_size);
772 : : #endif
773 : : } else {
774 : 1 : new_data = stack_data_;
775 : : }
776 : :
777 [ + + + - : 3 : if (new_data != data_ && data_ && (size_ + offset_) > 0) {
+ - ]
778 : 2 : std::memcpy(new_data, data_, size_ + offset_);
779 : : }
780 : :
781 [ + - + + : 3 : if (data_ && data_ != stack_data_ && total_old_size > kStackSize) {
+ - ]
782 : 1 : bytes_free(data_, total_old_size);
783 : : }
784 : :
785 : 3 : data_ = new_data;
786 : 3 : capacity_ = new_capacity;
787 : :
788 : 3 : return true;
789 : : }
790 : :
791 : 65 : bool Bytes::resize(size_t size) noexcept {
792 [ + + ]: 65 : if VUNLIKELY (!is_owner_) {
793 : 2 : VLOG_E("Bytes: Cannot resize on non-owned Bytes.");
794 : 1 : return false;
795 : : }
796 : :
797 [ + + ]: 64 : if (size <= capacity_) {
798 : : #if VLINK_BYTES_MEM_RESET
799 : :
800 : : if (size > size_ && data_) {
801 : : size_t available_space = capacity_;
802 : : size_t new_end_offset = size;
803 : :
804 : : if (new_end_offset <= available_space) {
805 : : std::memset(data_ + offset_ + size_, 0, size - size_);
806 : : } else {
807 : : VLOG_E("Bytes: Resize would exceed allocated memory.");
808 : : return false;
809 : : }
810 : : }
811 : : #endif
812 : 63 : size_ = size;
813 : :
814 : 63 : return true;
815 : : }
816 : :
817 [ - + ]: 1 : if VUNLIKELY (!reserve(size)) {
818 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
819 : : }
820 : :
821 : 1 : size_ = size;
822 : :
823 : 1 : return true;
824 : : }
825 : :
826 : 388 : Bytes& Bytes::shallow_copy(const Bytes& bytes) noexcept {
827 : 388 : process_type(kShallowCopy, bytes.data_, bytes.size_, bytes.offset_, false);
828 : :
829 : 388 : return *this;
830 : : }
831 : :
832 : 9 : Bytes& Bytes::deep_copy(const Bytes& bytes) noexcept {
833 [ + + + - ]: 9 : if (bytes.offset_ > 0 && bytes.data_) {
834 : 1 : Bytes tmp = Bytes::deep_copy(bytes.data_, bytes.size_ + bytes.offset_);
835 : 1 : process_type(kCreate, nullptr, bytes.size_, bytes.offset_, false);
836 : :
837 [ + - + - : 1 : if VLIKELY (data_ && tmp.data_) {
+ - ]
838 : 1 : std::memcpy(data_, tmp.data_, bytes.size_ + bytes.offset_);
839 : : }
840 : 1 : } else {
841 : 8 : process_type(kDeepCopy, const_cast<uint8_t*>(bytes.data()), bytes.size_, 0, false);
842 : : }
843 : :
844 : 9 : return *this;
845 : : }
846 : :
847 : 4 : Bytes& Bytes::deep_copy_self() noexcept {
848 [ + + ]: 4 : if VUNLIKELY (is_owner_) {
849 : 1 : return *this;
850 : : }
851 : :
852 [ + + - + : 3 : if VUNLIKELY (!data_ || size_ == 0) {
+ + ]
853 : 1 : clear();
854 : 1 : return *this;
855 : : }
856 : :
857 [ + + ]: 2 : if (offset_ > 0) {
858 : 1 : uint8_t* old_data = data_;
859 : 1 : size_t total_size = size_ + offset_;
860 : :
861 : 1 : process_type(kCreate, nullptr, size_, offset_, false);
862 : :
863 [ + - ]: 1 : if VLIKELY (data_) {
864 : 1 : std::memcpy(data_, old_data, total_size);
865 : : }
866 : : } else {
867 : 1 : process_type(kDeepCopy, data_ + offset_, size_, 0, false);
868 : : }
869 : :
870 : 2 : return *this;
871 : : }
872 : :
873 : 5305 : Bytes::Bytes(Type type, uint8_t* data, size_t size, uint8_t offset, bool loaned) noexcept {
874 : 5305 : process_type(type, data, size, offset, loaned);
875 : 5309 : }
876 : :
877 : 15546 : void Bytes::process_type(Type type, uint8_t* data, size_t size, uint8_t offset, bool loaned, Bytes* tmp) noexcept {
878 [ + + + + : 15546 : switch (type) {
- ]
879 : 2716 : case kCreate: {
880 : : // VLOG_W("kCreate");
881 : :
882 [ + + ]: 2716 : if VUNLIKELY (size > std::numeric_limits<size_t>::max() - offset) {
883 : 2 : VLOG_E("Bytes: Cannot create due to size overflow.");
884 : 1 : clear();
885 : 1 : return;
886 : : }
887 : :
888 : 2715 : size_t total_size = size + offset;
889 : 2715 : uint8_t* new_data = nullptr;
890 : :
891 [ + + ]: 2715 : if (total_size > kStackSize) {
892 : 872 : new_data = bytes_malloc(total_size);
893 : :
894 [ + + ]: 872 : if VUNLIKELY (!new_data) {
895 : : VLOG_E("Bytes: Failed to allocate memory."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
896 : : clear(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
897 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
898 : : }
899 : : #if VLINK_BYTES_MEM_RESET
900 : : std::memset(new_data, 0, total_size);
901 : : #endif
902 [ + + ]: 1843 : } else if (total_size != 0) {
903 : 1841 : new_data = stack_data_;
904 : : }
905 : :
906 [ - + - - ]: 2714 : if (is_owner_ && data_) {
907 : : if (data_ != stack_data_ && capacity_ + offset_ > kStackSize) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
908 : : bytes_free(data_, capacity_ + offset_); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
909 : : data_ = nullptr; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
910 : : }
911 : : }
912 : :
913 : 2714 : data_ = new_data;
914 : :
915 : 2714 : size_ = size;
916 : 2714 : capacity_ = size;
917 : 2714 : offset_ = offset;
918 : 2714 : is_owner_ = true;
919 : 2714 : is_loaned_ = false;
920 : :
921 : 2714 : break;
922 : : }
923 : :
924 : 1726 : case kShallowCopy: {
925 : : // VLOG_W("kShallowCopy");
926 : :
927 [ + + + - ]: 1726 : if (is_owner_ && data_) {
928 [ + - ]: 1 : if VUNLIKELY (data_ == data) {
929 : 2 : VLOG_E("Bytes: Cannot shallow copy self.");
930 : 1 : return;
931 : : }
932 : :
933 : : if (data_ != stack_data_ && capacity_ + offset_ > kStackSize) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
934 : : bytes_free(data_, capacity_ + offset_); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
935 : : }
936 : : }
937 : :
938 : 1725 : data_ = data;
939 : 1725 : size_ = size;
940 : 1725 : capacity_ = 0;
941 : 1725 : offset_ = offset;
942 : 1725 : is_owner_ = false;
943 : 1725 : is_loaned_ = loaned;
944 : :
945 : 1725 : break;
946 : : }
947 : :
948 : 3115 : case kDeepCopy: {
949 : : // VLOG_W("kDeepCopy");
950 : :
951 [ # + ]: 3115 : if VUNLIKELY (size > std::numeric_limits<size_t>::max() - offset) {
952 : 0 : VLOG_E("Bytes: Cannot deep copy due to size overflow.");
953 : : clear(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
954 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
955 : : }
956 : :
957 : 3117 : size_t total_size = size + offset;
958 : :
959 : 3117 : uint8_t* deferred_free_buffer = nullptr;
960 : 3117 : size_t deferred_free_size = 0;
961 : :
962 [ + + + - ]: 3117 : if (is_owner_ && data_) {
963 [ + + ]: 80 : if VUNLIKELY (data_ == data) {
964 : 2 : VLOG_E("Bytes: Cannot deep copy self.");
965 : 1 : return;
966 : : }
967 : :
968 : 79 : bool can_reuse =
969 [ + + + - : 79 : (data_ != stack_data_ && capacity_ + offset_ > kStackSize && capacity_ + offset_ == total_size);
+ + ]
970 : :
971 [ + + + - ]: 79 : if (can_reuse && data) {
972 : 4 : uint8_t* dst_start = data_;
973 : 4 : uint8_t* dst_end = data_ + capacity_ + offset_;
974 : 4 : const uint8_t* src_start = data;
975 : 4 : const uint8_t* src_end = data + size;
976 : :
977 [ + + - + ]: 4 : if (src_start < dst_end && dst_start < src_end) {
978 : : can_reuse = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
979 : : }
980 : : }
981 : :
982 [ + + + - : 79 : if (data_ != stack_data_ && capacity_ + offset_ > kStackSize && !can_reuse) {
+ + ]
983 : 1 : deferred_free_buffer = data_;
984 : 1 : deferred_free_size = capacity_ + offset_;
985 : 1 : data_ = nullptr;
986 : 1 : capacity_ = 0;
987 : 1 : offset_ = 0;
988 : : }
989 : : }
990 : :
991 [ + + ]: 3116 : if VLIKELY (total_size != 0) {
992 [ + + ]: 3011 : if (total_size > kStackSize) {
993 [ + + + + ]: 212 : if (capacity_ + offset_ != total_size || data_ == stack_data_) {
994 : 211 : data_ = bytes_malloc(total_size);
995 : :
996 [ - + ]: 208 : if VUNLIKELY (!data_) {
997 : : // LCOV_EXCL_START GCOVR_EXCL_START
998 : : VLOG_E("Bytes: Failed to allocate memory.");
999 : :
1000 : : if (deferred_free_buffer) {
1001 : : bytes_free(deferred_free_buffer, deferred_free_size);
1002 : : }
1003 : :
1004 : : clear();
1005 : :
1006 : : return;
1007 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1008 : : }
1009 : :
1010 : : #if VLINK_BYTES_MEM_RESET
1011 : : std::memset(data_, 0, total_size);
1012 : : #endif
1013 : : }
1014 : : } else {
1015 : 2799 : data_ = stack_data_;
1016 : : }
1017 : :
1018 [ + + + - : 3008 : if VLIKELY (data && data_) {
+ + ]
1019 [ + - ]: 3004 : if VLIKELY (size > 0) {
1020 : 3004 : std::memcpy(data_ + offset, data, size);
1021 : : }
1022 : : }
1023 : :
1024 : 3008 : size_ = size;
1025 : 3008 : capacity_ = size;
1026 : 3008 : offset_ = offset;
1027 : 3008 : is_owner_ = true;
1028 : 3008 : is_loaned_ = false;
1029 : : } else {
1030 : 105 : data_ = data;
1031 : 105 : size_ = 0;
1032 : 105 : capacity_ = 0;
1033 : 105 : offset_ = 0;
1034 : 105 : is_owner_ = false;
1035 : 105 : is_loaned_ = false;
1036 : : }
1037 : :
1038 [ + + ]: 3113 : if (deferred_free_buffer) {
1039 : 1 : bytes_free(deferred_free_buffer, deferred_free_size);
1040 : : }
1041 : :
1042 : 3113 : break;
1043 : : }
1044 : :
1045 : 7989 : case kMove: {
1046 : : // VLOG_W("kMove");
1047 : :
1048 [ - + ]: 7989 : if VUNLIKELY (!tmp) {
1049 : : VLOG_E("Bytes: Reference tmp is empty."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1050 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1051 : : }
1052 : :
1053 [ + + + - ]: 7989 : if (is_owner_ && data_) {
1054 [ - + ]: 99 : if VUNLIKELY (data_ == data) {
1055 : : VLOG_E("Bytes: Cannot move self."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1056 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1057 : : }
1058 : :
1059 [ + + + - ]: 99 : if (data_ != stack_data_ && capacity_ + offset_ > kStackSize) {
1060 : 7 : bytes_free(data_, capacity_ + offset_);
1061 : : }
1062 : : }
1063 : :
1064 [ + + + + : 7982 : if (tmp->capacity_ + tmp->offset_ != 0 && tmp->capacity_ + tmp->offset_ <= kStackSize && tmp->is_owner_) {
+ - ]
1065 : 3735 : size_t copy_size = tmp->size_ + tmp->offset_;
1066 : :
1067 [ + + + + : 3735 : if VLIKELY (copy_size > 0 && copy_size <= kStackSize) {
+ - ]
1068 : 3735 : std::memcpy(stack_data_, tmp->stack_data_, copy_size);
1069 : : }
1070 : :
1071 : 3735 : data_ = stack_data_;
1072 : 3735 : } else {
1073 : 4247 : data_ = tmp->data_;
1074 : : }
1075 : :
1076 : 7982 : size_ = tmp->size_;
1077 : 7982 : capacity_ = tmp->capacity_;
1078 : 7982 : offset_ = tmp->offset_;
1079 : 7982 : is_owner_ = tmp->is_owner_;
1080 : 7982 : is_loaned_ = tmp->is_loaned_;
1081 : :
1082 : 7982 : tmp->data_ = nullptr;
1083 : 7982 : tmp->size_ = 0;
1084 : 7982 : tmp->capacity_ = 0;
1085 : 7982 : tmp->offset_ = 0;
1086 : 7982 : tmp->is_owner_ = false;
1087 : 7982 : tmp->is_loaned_ = false;
1088 : :
1089 : : #if VLINK_BYTES_MEM_RESET
1090 : : std::memset(tmp->stack_data_, 0, kStackSize);
1091 : : #endif
1092 : :
1093 : 7982 : break;
1094 : : }
1095 : :
1096 : 0 : default:
1097 : 0 : break;
1098 : : }
1099 : : }
1100 : :
1101 : 4 : std::ostream& operator<<(std::ostream& ostream, const Bytes& target) noexcept {
1102 [ + + ]: 4 : if (target.empty()) {
1103 : 1 : ostream << "{}";
1104 : 1 : return ostream;
1105 : : }
1106 : :
1107 [ + + ]: 3 : if (target.is_ptr()) {
1108 : 1 : ostream << "(ptr)" << static_cast<void*>(target.data_);
1109 : 1 : return ostream;
1110 : : }
1111 : :
1112 [ + + ]: 2 : if (target.offset_ > 0) {
1113 : 1 : ostream << "(offset=" << +target.offset_ << ") ";
1114 : : }
1115 : :
1116 : 2 : ostream << Bytes::convert_to_hex_str(target.data_ + target.offset_, target.size());
1117 : :
1118 : 2 : return ostream;
1119 : : }
1120 : :
1121 : : } // namespace vlink
|