Showing the changes to src/vm/string.cpp (1 of 40 files). Show all
Unified · Split
| @@ -0,0 +1,522 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: string.cpp | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #include <cinttypes> | ||
| 8 | #include <runtime.h> | ||
| 9 | #include <utf8.h> | ||
| 10 | #include <vm_memory.h> | ||
| 11 | #include <debug.h> | ||
| 12 | #include <exceptions.h> | ||
| 13 | |||
| 14 | using namespace disvm; | ||
| 15 | using namespace disvm::runtime; | ||
| 16 | |||
| 17 | std::shared_ptr<const type_descriptor_t> vm_string_t::type_desc() | ||
| 18 | { | ||
| 19 | return intrinsic_type_desc::type<vm_string_t>(); | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace | ||
| 23 | { | ||
| 24 | // Enumeration for character type compare cases. | ||
| 25 | enum class compare_case | ||
| 26 | { | ||
| 27 | ascii_ascii, | ||
| 28 | ascii_rune, | ||
| 29 | rune_ascii, | ||
| 30 | rune_rune | ||
| 31 | }; | ||
| 32 | |||
| 33 | // Scenario matrix for compare cases | ||
| 34 | const compare_case scenario_matrix[][2] = | ||
| 35 | { | ||
| 36 | { compare_case::ascii_ascii, compare_case::ascii_rune }, | ||
| 37 | { compare_case::rune_ascii, compare_case::rune_rune }, | ||
| 38 | }; | ||
| 39 | |||
| 40 | // Case indices | ||
| 41 | const auto ascii_idx = std::size_t{ 0 }; | ||
| 42 | const auto rune_idx = std::size_t{ 1 }; | ||
| 43 | |||
| 44 | // Compare function for character types. | ||
| 45 | template <typename CharacterType1, typename CharacterType2> | ||
| 46 | int _compare(word_t len, CharacterType1 *s1, CharacterType2 *s2) | ||
| 47 | { | ||
| 48 | for (auto i = word_t{ 0 }; i < len; ++i) | ||
| 49 | { | ||
| 50 | const auto sc1 = static_cast<rune_t>(s1[i]); | ||
| 51 | const auto sc2 = static_cast<rune_t>(s2[i]); | ||
| 52 | |||
| 53 | if (sc1 != sc2) | ||
| 54 | return (sc1 < sc2) ? (-1) : 1; | ||
| 55 | } | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | int vm_string_t::compare(const vm_string_t *s1, const vm_string_t *s2) | ||
| 62 | { | ||
| 63 | auto len_1 = word_t{ 0 }; | ||
| 64 | auto scen_1 = ascii_idx; | ||
| 65 | void *str_mem_1 = ""; | ||
| 66 | if (s1 != nullptr) | ||
| 67 | { | ||
| 68 | len_1 = s1->_length; | ||
| 69 | scen_1 = s1->_character_size == sizeof(char) ? ascii_idx : rune_idx; | ||
| 70 | str_mem_1 = (s1->_is_alloc) ? s1->_mem.alloc : s1->_mem.local; | ||
| 71 | } | ||
| 72 | |||
| 73 | auto len_2 = word_t{ 0 }; | ||
| 74 | auto scen_2 = ascii_idx; | ||
| 75 | void *str_mem_2 = ""; | ||
| 76 | if (s2 != nullptr) | ||
| 77 | { | ||
| 78 | len_2 = s2->_length; | ||
| 79 | scen_2 = s2->_character_size == sizeof(char) ? ascii_idx : rune_idx; | ||
| 80 | str_mem_2 = (s2->_is_alloc) ? s2->_mem.alloc : s2->_mem.local; | ||
| 81 | } | ||
| 82 | |||
| 83 | // Check the simple compare case between the same string. | ||
| 84 | if (str_mem_1 == str_mem_2) | ||
| 85 | return 0; | ||
| 86 | |||
| 87 | const auto scenario = scenario_matrix[scen_1][scen_2]; | ||
| 88 | |||
| 89 | auto result = int{ 0 }; | ||
| 90 | const auto min_length = std::min(len_1, len_2); | ||
| 91 | switch (scenario) | ||
| 92 | { | ||
| 93 | default: | ||
| 94 | assert(false && "Unknown compare scenario"); | ||
| 95 | case compare_case::ascii_ascii: | ||
| 96 | case compare_case::rune_rune: | ||
| 97 | result = std::memcmp(str_mem_1, str_mem_2, min_length); | ||
| 98 | break; | ||
| 99 | case compare_case::ascii_rune: | ||
| 100 | result = _compare(min_length, reinterpret_cast<char *>(str_mem_1), reinterpret_cast<rune_t *>(str_mem_2)); | ||
| 101 | break; | ||
| 102 | case compare_case::rune_ascii: | ||
| 103 | result = _compare(min_length, reinterpret_cast<rune_t *>(str_mem_1), reinterpret_cast<char *>(str_mem_2)); | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | |||
| 107 | if (result != 0) | ||
| 108 | return result; | ||
| 109 | |||
| 110 | // Comparing lengths if one string is a proper sub-string of the other. | ||
| 111 | return (len_1 - len_2); | ||
| 112 | } | ||
| 113 | |||
| 114 | namespace | ||
| 115 | { | ||
| 116 | template<typename DestCharacterType, typename SourceCharacterType> | ||
| 117 | void copy_characters_to(DestCharacterType *dest, word_t dest_start, const SourceCharacterType *source, word_t source_length) | ||
| 118 | { | ||
| 119 | for (auto i = word_t{ 0 }; i < source_length; ++i) | ||
| 120 | dest[dest_start + i] = static_cast<DestCharacterType>(source[i]); | ||
| 121 | } | ||
| 122 | |||
| 123 | template<> | ||
| 124 | void copy_characters_to(char *dest, word_t dest_start, const char *source, word_t source_length) | ||
| 125 | { | ||
| 126 | assert(dest != nullptr && source != nullptr); | ||
| 127 | std::memcpy((dest + dest_start), source, source_length); | ||
| 128 | } | ||
| 129 | |||
| 130 | template<> | ||
| 131 | void copy_characters_to(rune_t *dest, word_t dest_start, const rune_t *source, word_t source_length) | ||
| 132 | { | ||
| 133 | assert(dest != nullptr && source != nullptr); | ||
| 134 | std::memcpy((dest + dest_start), source, (source_length * sizeof(rune_t))); | ||
| 135 | } | ||
| 136 | |||
| 137 | void combine( | ||
| 138 | bool dest_is_rune, | ||
| 139 | uint8_t *dest, | ||
| 140 | bool source1_is_rune, | ||
| 141 | word_t source1_length, | ||
| 142 | const uint8_t *source1, | ||
| 143 | bool source2_is_rune, | ||
| 144 | word_t source2_length, | ||
| 145 | const uint8_t *source2) | ||
| 146 | { | ||
| 147 | if (dest_is_rune) | ||
| 148 | { | ||
| 149 | auto dest_rune = reinterpret_cast<rune_t *>(dest); | ||
| 150 | if (source1_is_rune) | ||
| 151 | copy_characters_to(dest_rune, 0, reinterpret_cast<const rune_t *>(source1), source1_length); | ||
| 152 | else | ||
| 153 | copy_characters_to(dest_rune, 0, source1, source1_length); | ||
| 154 | |||
| 155 | if (source2_is_rune) | ||
| 156 | copy_characters_to(dest_rune, source1_length, reinterpret_cast<const rune_t *>(source2), source2_length); | ||
| 157 | else | ||
| 158 | copy_characters_to(dest_rune, source1_length, source2, source2_length); | ||
| 159 | } | ||
| 160 | else | ||
| 161 | { | ||
| 162 | copy_characters_to(dest, 0, source1, source1_length); | ||
| 163 | copy_characters_to(dest, source1_length, source2, source2_length); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | word_t compute_max_length(word_t current_length) | ||
| 168 | { | ||
| 169 | // Length + 1 (null) + space for string expansion | ||
| 170 | return (current_length + 1 + (current_length / 4)); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | vm_string_t::vm_string_t() | ||
| 175 | : vm_alloc_t(vm_string_t::type_desc()) | ||
| 176 | , _character_size{ sizeof(char) } | ||
| 177 | , _encoded_str{ nullptr } | ||
| 178 | , _is_alloc{ false } | ||
| 179 | , _length{ 0 } | ||
| 180 | , _length_max{ sizeof(_mem.local) } | ||
| 181 | , _mem{} | ||
| 182 | { } | ||
| 183 | |||
| 184 | vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str) | ||
| 185 | : vm_alloc_t(vm_string_t::type_desc()) | ||
| 186 | , _character_size{ sizeof(char) } | ||
| 187 | , _encoded_str{ nullptr } | ||
| 188 | , _is_alloc{ false } | ||
| 189 | , _length{ 0 } | ||
| 190 | , _length_max{ sizeof(_mem.local) } | ||
| 191 | , _mem{} | ||
| 192 | { | ||
| 193 | assert((encoded_str_len > 0 && encoded_str != nullptr) || (encoded_str_len == 0 && encoded_str == nullptr)); | ||
| 194 | auto character_count = utf8::count_codepoints(encoded_str, encoded_str_len); | ||
| 195 | _character_size = (encoded_str_len != character_count) ? sizeof(rune_t) : sizeof(char); | ||
| 196 | |||
| 197 | _length = static_cast<word_t>(character_count); | ||
| 198 | _length_max = sizeof(_mem.local) / _character_size; | ||
| 199 | |||
| 200 | // Allocate memory based on character count and size | ||
| 201 | auto dest = _mem.local; | ||
| 202 | if ((encoded_str_len + 1) > sizeof(_mem.local)) | ||
| 203 | { | ||
| 204 | // If we are going to allocate, make sure it is larger than needed. | ||
| 205 | _length_max = compute_max_length(_length); | ||
| 206 | _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size); | ||
| 207 | _is_alloc = true; | ||
| 208 | |||
| 209 | dest = _mem.alloc; | ||
| 210 | } | ||
| 211 | |||
| 212 | // Copy over the entire buffer if ASCII or convert and copy if multi-byte | ||
| 213 | if (_character_size == sizeof(char)) | ||
| 214 | { | ||
| 215 | std::memcpy(dest, encoded_str, encoded_str_len); | ||
| 216 | } | ||
| 217 | else | ||
| 218 | { | ||
| 219 | auto rune_dest = reinterpret_cast<rune_t *>(dest); | ||
| 220 | for (auto i = word_t{ 0 }; i < _length; ++i) | ||
| 221 | encoded_str += utf8::decode(encoded_str, rune_dest[i]); | ||
| 222 | } | ||
| 223 | |||
| 224 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | ||
| 225 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm string: %d %d >>%s<<\n", _length, (_length_max * _character_size), dest); | ||
| 226 | } | ||
| 227 | |||
| 228 | vm_string_t::vm_string_t(const vm_string_t &s1, const vm_string_t &s2) | ||
| 229 | : vm_alloc_t(vm_string_t::type_desc()) | ||
| 230 | , _character_size{ std::max(s1._character_size, s2._character_size) } | ||
| 231 | , _encoded_str{ nullptr } | ||
| 232 | , _is_alloc{ true } | ||
| 233 | , _length{ s1._length + s2._length } | ||
| 234 | , _length_max{ s1._length + s2._length } | ||
| 235 | , _mem{} | ||
| 236 | { | ||
| 237 | _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size); | ||
| 238 | |||
| 239 | const auto s1_data = (s1._is_alloc) ? s1._mem.alloc : s1._mem.local; | ||
| 240 | const auto s2_data = (s2._is_alloc) ? s2._mem.alloc : s2._mem.local; | ||
| 241 | |||
| 242 | // Combine the two strings | ||
| 243 | combine( | ||
| 244 | _character_size == sizeof(rune_t), | ||
| 245 | _mem.alloc, | ||
| 246 | s1._character_size == sizeof(rune_t), | ||
| 247 | s1._length, | ||
| 248 | s1_data, | ||
| 249 | s2._character_size == sizeof(rune_t), | ||
| 250 | s2._length, | ||
| 251 | s2_data); | ||
| 252 | } | ||
| 253 | |||
| 254 | vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t end_index) | ||
| 255 | : vm_alloc_t(other.alloc_type) | ||
| 256 | , _character_size{ other._character_size } | ||
| 257 | , _encoded_str{ nullptr } | ||
| 258 | , _is_alloc{ other._is_alloc } | ||
| 259 | , _length{ end_index - begin_index } | ||
| 260 | , _length_max{ compute_max_length(end_index - begin_index) } | ||
| 261 | , _mem{} | ||
| 262 | { | ||
| 263 | if (end_index <= begin_index) | ||
| 264 | throw out_of_range_memory{}; | ||
| 265 | |||
| 266 | if (begin_index < 0) | ||
| 267 | throw out_of_range_memory{ 0, other._length, begin_index }; | ||
| 268 | |||
| 269 | if (other._length < end_index) | ||
| 270 | throw out_of_range_memory{ 0, other._length, end_index }; | ||
| 271 | |||
| 272 | // Allocate memory based on character count and size | ||
| 273 | auto src = other._mem.local; | ||
| 274 | auto dest = _mem.local; | ||
| 275 | const auto length_in_bytes = _length * _character_size; | ||
| 276 | if (_is_alloc) | ||
| 277 | { | ||
| 278 | src = other._mem.alloc; | ||
| 279 | _mem.alloc = calloc_memory<uint8_t>(_length_max); | ||
| 280 | dest = _mem.alloc; | ||
| 281 | } | ||
| 282 | |||
| 283 | // Copy over string content starting at the correct index (char vs. rune_t) | ||
| 284 | ::memcpy(dest, (src + (begin_index * _character_size)), length_in_bytes); | ||
| 285 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "copy: vm string: %#" PRIxPTR " %d %d %#" PRIxPTR "\n", &other, begin_index, end_index, this); | ||
| 286 | } | ||
| 287 | |||
| 288 | vm_string_t::~vm_string_t() | ||
| 289 | { | ||
| 290 | // Free memory and string buffer | ||
| 291 | if (_is_alloc) | ||
| 292 | { | ||
| 293 | if (static_cast<void *>(_mem.alloc) != static_cast<void *>(_encoded_str)) | ||
| 294 | free_memory(_encoded_str); | ||
| 295 | |||
| 296 | free_memory(_mem.alloc); | ||
| 297 | } | ||
| 298 | else | ||
| 299 | { | ||
| 300 | if (static_cast<void *>(_mem.local) != static_cast<void *>(_encoded_str)) | ||
| 301 | free_memory(_encoded_str); | ||
| 302 | } | ||
| 303 | |||
| 304 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm string\n"); | ||
| 305 | |||
| 306 | debug::assign_debug_pointer(&_mem.alloc); | ||
| 307 | debug::assign_debug_pointer(&_encoded_str); | ||
| 308 | } | ||
| 309 | |||
| 310 | vm_string_t &vm_string_t::append(const vm_string_t &other) | ||
| 311 | { | ||
| 312 | // This function does not adhere to the immutable string contract. | ||
| 313 | // Therefore, it is unsafe to call this function if there is more | ||
| 314 | // than 1 reference to this string instance. | ||
| 315 | assert(get_ref_count() <= 1); | ||
| 316 | |||
| 317 | // Check if the new string has any length at all. | ||
| 318 | const auto other_length = other._length; | ||
| 319 | if (other_length == 0) | ||
| 320 | return *this; | ||
| 321 | |||
| 322 | const auto other_is_rune = other._character_size == sizeof(rune_t); | ||
| 323 | const auto other_source = (other._is_alloc) ? other._mem.alloc : other._mem.local; | ||
| 324 | |||
| 325 | const auto local_is_rune = _character_size == sizeof(rune_t); | ||
| 326 | const auto local_length = _length; | ||
| 327 | const auto local_source = (_is_alloc) ? _mem.alloc : _mem.local; | ||
| 328 | |||
| 329 | // Release the encoded string for this instance. | ||
| 330 | { | ||
| 331 | std::lock_guard<std::mutex> lock{ _encoded_str_lock }; | ||
| 332 | if (_encoded_str != reinterpret_cast<char*>(local_source)) | ||
| 333 | { | ||
| 334 | free_memory(_encoded_str); | ||
| 335 | _encoded_str = nullptr; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | // Define the values for the appended string | ||
| 340 | const auto new_length = local_length + other_length; | ||
| 341 | const auto new_length_max = (new_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(new_length); | ||
| 342 | const auto new_is_rune = local_is_rune || other_is_rune; | ||
| 343 | uint8_t *new_source = nullptr; | ||
| 344 | |||
| 345 | // Check if this string and the supplied string have the same character size. | ||
| 346 | if (new_is_rune) | ||
| 347 | new_source = calloc_memory<uint8_t>(new_length_max * sizeof(rune_t)); | ||
| 348 | else | ||
| 349 | new_source = calloc_memory<uint8_t>(new_length_max); | ||
| 350 | |||
| 351 | // Combine the two string sources | ||
| 352 | combine( | ||
| 353 | new_is_rune, | ||
| 354 | new_source, | ||
| 355 | local_is_rune, | ||
| 356 | local_length, | ||
| 357 | local_source, | ||
| 358 | other_is_rune, | ||
| 359 | other_length, | ||
| 360 | other_source); | ||
| 361 | |||
| 362 | _length = new_length; | ||
| 363 | _length_max = new_length_max; | ||
| 364 | |||
| 365 | // Free the local allocation | ||
| 366 | if (_is_alloc) | ||
| 367 | { | ||
| 368 | free_memory(local_source); | ||
| 369 | _mem.alloc = nullptr; | ||
| 370 | } | ||
| 371 | |||
| 372 | _mem.alloc = new_source; | ||
| 373 | _is_alloc = true; | ||
| 374 | _character_size = new_is_rune ? sizeof(rune_t) : sizeof(char); | ||
| 375 | |||
| 376 | return *this; | ||
| 377 | } | ||
| 378 | |||
| 379 | word_t vm_string_t::get_length() const | ||
| 380 | { | ||
| 381 | return _length; | ||
| 382 | } | ||
| 383 | |||
| 384 | rune_t vm_string_t::get_rune(word_t index) const | ||
| 385 | { | ||
| 386 | if (index < 0 && _length <= index) | ||
| 387 | throw out_of_range_memory{ 0, _length - 1, index }; | ||
| 388 | |||
| 389 | // Determine if local or allocated memory | ||
| 390 | auto source = reinterpret_cast<const char *>(_mem.local); | ||
| 391 | if (_is_alloc) | ||
| 392 | source = reinterpret_cast<const char *>(_mem.alloc); | ||
| 393 | |||
| 394 | // Check if multi-byte or ASCII | ||
| 395 | if (_character_size != sizeof(char)) | ||
| 396 | return reinterpret_cast<const rune_t *>(source)[index]; | ||
| 397 | else | ||
| 398 | return static_cast<const rune_t>(source[index]); | ||
| 399 | } | ||
| 400 | |||
| 401 | void vm_string_t::set_rune(word_t index, rune_t value) | ||
| 402 | { | ||
| 403 | // This function does not adhere to the immutable string contract. | ||
| 404 | // Therefore, it is unsafe to call this function if there is more | ||
| 405 | // than 1 reference to this string instance. | ||
| 406 | assert(get_ref_count() <= 1); | ||
| 407 | |||
| 408 | if (value > utf8::constants::max_supported_codepoint) | ||
| 409 | throw invalid_utf8{}; | ||
| 410 | |||
| 411 | // Index equal to length is allowed to grow the string | ||
| 412 | if (index < 0 && _length <= index) | ||
| 413 | throw out_of_range_memory{ 0, _length, index }; | ||
| 414 | |||
| 415 | // Determine if local or allocated memory | ||
| 416 | auto source = static_cast<uint8_t *>(_mem.local); | ||
| 417 | if (_is_alloc) | ||
| 418 | { | ||
| 419 | source = static_cast<uint8_t *>(_mem.alloc); | ||
| 420 | |||
| 421 | // Since we are setting a rune, the string value should be freed. | ||
| 422 | if (_encoded_str != nullptr) | ||
| 423 | free_memory(_encoded_str); | ||
| 424 | } | ||
| 425 | |||
| 426 | // Reset the string so it can be regenerated. | ||
| 427 | _encoded_str = nullptr; | ||
| 428 | |||
| 429 | const auto new_character_size = (value > utf8::constants::max_codepoint_ascii) ? sizeof(rune_t) : sizeof(char); | ||
| 430 | assert(new_character_size < std::numeric_limits<uint8_t>::max()); | ||
| 431 | |||
| 432 | if (new_character_size > _character_size) // Expand the string if the new character is non-ASCII. | ||
| 433 | { | ||
| 434 | _character_size = static_cast<uint8_t>(new_character_size); | ||
| 435 | assert(_character_size == sizeof(rune_t)); | ||
| 436 | |||
| 437 | _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | ||
| 438 | auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size); | ||
| 439 | |||
| 440 | // Copy source into the new alloc. | ||
| 441 | copy_characters_to(new_alloc, 0, source, _length); | ||
| 442 | |||
| 443 | // Free memory, if allocated | ||
| 444 | if (_is_alloc) | ||
| 445 | free_memory(source); | ||
| 446 | |||
| 447 | debug::assign_debug_memory(&_mem, sizeof(_mem)); | ||
| 448 | |||
| 449 | _is_alloc = true; | ||
| 450 | _mem.alloc = reinterpret_cast<uint8_t *>(new_alloc); | ||
| 451 | source = _mem.alloc; | ||
| 452 | } | ||
| 453 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. | ||
| 454 | { | ||
| 455 | _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | ||
| 456 | auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size); | ||
| 457 | ::memcpy(new_alloc, source, _length * _character_size); | ||
| 458 | |||
| 459 | // Free memory, if allocated | ||
| 460 | if (_is_alloc) | ||
| 461 | free_memory(source); | ||
| 462 | |||
| 463 | debug::assign_debug_memory(&_mem, sizeof(_mem)); | ||
| 464 | |||
| 465 | _is_alloc = true; | ||
| 466 | _mem.alloc = new_alloc; | ||
| 467 | source = _mem.alloc; | ||
| 468 | } | ||
| 469 | |||
| 470 | // Check if multi-byte or ASCII. | ||
| 471 | if (_character_size != sizeof(char)) | ||
| 472 | reinterpret_cast<rune_t *>(source)[index] = value; | ||
| 473 | else | ||
| 474 | source[index] = static_cast<uint8_t>(value & utf8::constants::max_codepoint_ascii); | ||
| 475 | |||
| 476 | // Increment the length, if the index matches the length. | ||
| 477 | if (_length == index) | ||
| 478 | _length++; | ||
| 479 | } | ||
| 480 | |||
| 481 | int vm_string_t::compare_to(const vm_string_t *s) const | ||
| 482 | { | ||
| 483 | return vm_string_t::compare(this, s); | ||
| 484 | } | ||
| 485 | |||
| 486 | const char *vm_string_t::str() const | ||
| 487 | { | ||
| 488 | if (_encoded_str != nullptr) | ||
| 489 | return _encoded_str; | ||
| 490 | |||
| 491 | { | ||
| 492 | // [PERF] Use a spin-lock instead of a mutex here. | ||
| 493 | std::lock_guard<std::mutex> lock{ _encoded_str_lock }; | ||
| 494 | |||
| 495 | if (_encoded_str != nullptr) | ||
| 496 | return _encoded_str; | ||
| 497 | |||
| 498 | // If the string is ASCII then just return the memory | ||
| 499 | if (_character_size == sizeof(char)) | ||
| 500 | { | ||
| 501 | _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local); | ||
| 502 | return _encoded_str; | ||
| 503 | } | ||
| 504 | |||
| 505 | const auto rune_str = (_is_alloc) ? reinterpret_cast<rune_t *>(_mem.alloc) : reinterpret_cast<rune_t *>(_mem.local); | ||
| 506 | const auto str_len = (_length * _character_size) + 1; // Plus 1 for a null terminator | ||
| 507 | auto encoded_str_local = alloc_memory<char>(str_len); | ||
| 508 | |||
| 509 | auto s = reinterpret_cast<uint8_t *>(encoded_str_local); | ||
| 510 | for (auto i = word_t{ 0 }; i < _length; ++i) | ||
| 511 | { | ||
| 512 | auto curr_rune = rune_str[i]; | ||
| 513 | s = utf8::encode(curr_rune, s); | ||
| 514 | } | ||
| 515 | |||
| 516 | // Ensure the final value is null. | ||
| 517 | *s = 0; | ||
| 518 | |||
| 519 | _encoded_str = encoded_str_local; | ||
| 520 | return _encoded_str; | ||
| 521 | } | ||
| 522 | } | ||