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