Unified · Split
| @@ -171,18 +171,26 @@ void vm_array_t::copy_from(const vm_array_t &source, word_t this_begin_index) · 170 unmodified lines | |||
| 171 | 171 | ||
| 172 | auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes); | 172 | auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes); |
| 173 | 173 | ||
| 174 | // Increment all pointers if we have reference types. | 174 | // If we have reference types, increment references in the |
| 175 | // source array and decrement them in the destination array. | ||
| 175 | if (_element_type->map_in_bytes != 0) | 176 | if (_element_type->map_in_bytes != 0) |
| 176 | { | 177 | { |
| 177 | auto &type_desc = *_element_type; | 178 | // [PERF] There is a chance this copy is within the same array, therefore |
| 179 | // a check could be made to not ref count memory that is going to be moved. | ||
| 180 | // The Inferno implementation of slicela handles this case (libinterp/xec.c). | ||
| 181 | const auto &type_desc = *_element_type; | ||
| 178 | auto source_array_element = source._arr; | 182 | auto source_array_element = source._arr; |
| 183 | auto dest_array_element = dest_arr; | ||
| 179 | for (auto i = word_t{ 0 }; i < source._length; ++i) | 184 | for (auto i = word_t{ 0 }; i < source._length; ++i) |
| 180 | { | 185 | { |
| 181 | source_array_element += (i * type_desc.size_in_bytes); | ||
| 182 | inc_ref_count_in_memory(type_desc, source_array_element); | 186 | inc_ref_count_in_memory(type_desc, source_array_element); |
| 187 | source_array_element += type_desc.size_in_bytes; | ||
| 188 | |||
| 189 | destroy_memory(type_desc, dest_array_element); | ||
| 190 | dest_array_element += type_desc.size_in_bytes; | ||
| 183 | } | 191 | } |
| 184 | } | 192 | } |
| 185 | 193 | ||
| 186 | // Moving data within the same array is permitted so use memmove(). | 194 | // Copy data from within the same array is permitted. |
| 187 | std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes)); | 195 | std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes)); |
| 188 | } | 196 | } |
| @@ -79,7 +79,7 @@ namespace · 78 unmodified lines | |||
| 79 | vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); | 79 | vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); |
| 80 | // Right shifting a negative number is implementation dependent in C++. | 80 | // Right shifting a negative number is implementation dependent in C++. |
| 81 | // Assert the current compiler defaults to arithmetic shifting, not logical. | 81 | // Assert the current compiler defaults to arithmetic shifting, not logical. |
| 82 | assert(vt_ref<word_t>(r.mid) >= 0 || (vt_ref<word_t>(r.mid) < 0 && vt_ref<word_t>(r.dest) < 0)); | 82 | assert(vt_ref<big_t>(r.mid) >= 0 || (vt_ref<big_t>(r.mid) < 0 && vt_ref<big_t>(r.dest) < 0)); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); } | 85 | EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); } |
| @@ -206,8 +206,8 @@ namespace · 120 unmodified lines | |||
| 206 | assert(static_cast<std::size_t>(len) <= buffer.size()); | 206 | assert(static_cast<std::size_t>(len) <= buffer.size()); |
| 207 | 207 | ||
| 208 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); | 208 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 209 | auto str = at_val<vm_string_t>(r.dest); | 209 | auto prev = at_val<vm_alloc_t>(r.dest); |
| 210 | dec_ref_count_and_free(str); | 210 | dec_ref_count_and_free(prev); |
| 211 | 211 | ||
| 212 | pt_ref(r.dest) = new_string->get_allocation(); | 212 | pt_ref(r.dest) = new_string->get_allocation(); |
| 213 | } | 213 | } |
| @@ -233,9 +233,10 @@ namespace · 19 unmodified lines | |||
| 233 | 233 | ||
| 234 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); | 234 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 235 | 235 | ||
| 236 | auto str = at_val<vm_string_t>(r.dest); | 236 | auto prev = at_val<vm_alloc_t>(r.dest); |
| 237 | dec_ref_count_and_free(prev); | ||
| 238 | |||
| 237 | pt_ref(r.dest) = new_string->get_allocation(); | 239 | pt_ref(r.dest) = new_string->get_allocation(); |
| 238 | dec_ref_count_and_free(str); | ||
| 239 | } | 240 | } |
| 240 | 241 | ||
| 241 | EXEC_DECL(cvtcl) | 242 | EXEC_DECL(cvtcl) |
| @@ -259,9 +260,10 @@ namespace · 17 unmodified lines | |||
| 259 | 260 | ||
| 260 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); | 261 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 261 | 262 | ||
| 262 | auto str = at_val<vm_string_t>(r.dest); | 263 | auto prev = at_val<vm_alloc_t>(r.dest); |
| 264 | dec_ref_count_and_free(prev); | ||
| 265 | |||
| 263 | pt_ref(r.dest) = new_string->get_allocation(); | 266 | pt_ref(r.dest) = new_string->get_allocation(); |
| 264 | dec_ref_count_and_free(str); | ||
| 265 | } | 267 | } |
| 266 | 268 | ||
| 267 | // | 269 | // |
| @@ -725,9 +727,9 @@ namespace · 457 unmodified lines | |||
| 725 | auto index = vt_ref<word_t>(r.mid); | 727 | auto index = vt_ref<word_t>(r.mid); |
| 726 | auto str = at_val<vm_string_t>(r.dest); | 728 | auto str = at_val<vm_string_t>(r.dest); |
| 727 | if (str == nullptr) | 729 | if (str == nullptr) |
| 728 | str = new vm_string_t(); | 730 | str = new vm_string_t{}; |
| 729 | else if (str->get_ref_count() > 1) | 731 | else if (str->get_ref_count() > 1) |
| 730 | str = new vm_string_t(*str, 0, str->get_length()); | 732 | str = new vm_string_t{ *str, 0, str->get_length() }; |
| 731 | 733 | ||
| 732 | // Set the rune at the supplied index. | 734 | // Set the rune at the supplied index. |
| 733 | str->set_rune(index, rune); | 735 | str->set_rune(index, rune); |
| @@ -740,7 +742,8 @@ namespace · 6 unmodified lines | |||
| 740 | } | 742 | } |
| 741 | } | 743 | } |
| 742 | 744 | ||
| 743 | vm_string_t *_addc(vm_string_t *s1, const vm_string_t *s2, /* in out */ bool &append_to_s1) | 745 | // Returns a string instance only if it is new |
| 746 | vm_string_t *concat_string(vm_string_t *s1, const vm_string_t *s2, bool try_append_to_s1) | ||
| 744 | { | 747 | { |
| 745 | if (s1 == nullptr) | 748 | if (s1 == nullptr) |
| 746 | { | 749 | { |
| @@ -750,19 +753,23 @@ namespace · 3 unmodified lines | |||
| 750 | return new vm_string_t{ *s2, 0, s2->get_length() }; | 753 | return new vm_string_t{ *s2, 0, s2->get_length() }; |
| 751 | } | 754 | } |
| 752 | 755 | ||
| 753 | // There are other references to this string so we will not alter it. | 756 | // If there are other references to the string, it is immutable. |
| 754 | if (s1->get_ref_count() > 1) | 757 | if (s1->get_ref_count() > 1) |
| 755 | append_to_s1 = false; | 758 | try_append_to_s1 = false; |
| 756 | 759 | ||
| 757 | if (s2 == nullptr) | 760 | if (s2 == nullptr) |
| 758 | { | 761 | { |
| 759 | if (append_to_s1) | 762 | if (try_append_to_s1) |
| 760 | return s1; | 763 | return nullptr; |
| 761 | 764 | ||
| 762 | return new vm_string_t(*s1, 0, s1->get_length()); | 765 | return new vm_string_t(*s1, 0, s1->get_length()); |
| 763 | } | 766 | } |
| 764 | 767 | ||
| 765 | return (append_to_s1) ? &(s1->append(*s2)) : new vm_string_t(*s1, *s2); | 768 | if (!try_append_to_s1) |
| 769 | return new vm_string_t(*s1, *s2); | ||
| 770 | |||
| 771 | s1->append(*s2); | ||
| 772 | return nullptr; | ||
| 766 | } | 773 | } |
| 767 | 774 | ||
| 768 | // [SPEC] 'Concat strings' op code | 775 | // [SPEC] 'Concat strings' op code |
| @@ -775,20 +782,18 @@ namespace · 6 unmodified lines | |||
| 775 | // dst = src2 + src1 | 782 | // dst = src2 + src1 |
| 776 | EXEC_DECL(addc) | 783 | EXEC_DECL(addc) |
| 777 | { | 784 | { |
| 785 | const auto try_append_to_s1 = r.mid == r.dest; | ||
| 778 | auto s1 = at_val<vm_string_t>(r.mid); | 786 | auto s1 = at_val<vm_string_t>(r.mid); |
| 779 | const auto s2 = at_val<vm_string_t>(r.src); | 787 | const auto s2 = at_val<vm_string_t>(r.src); |
| 780 | auto dest_maybe = at_val<vm_string_t>(r.dest); | ||
| 781 | 788 | ||
| 782 | // If the source is the same as the destination and the | 789 | // If the source is the same as the destination, try to append the string during concatenation. |
| 783 | // destination is not null, append the string. This can | 790 | const auto new_str_maybe = concat_string(s1, s2, try_append_to_s1); |
| 784 | // change during the concat process. | 791 | if (new_str_maybe != nullptr) |
| 785 | auto append_to_source = dest_maybe != nullptr && s1 == dest_maybe; | ||
| 786 | const auto str = _addc(s1, s2, append_to_source); | ||
| 787 | |||
| 788 | if (!append_to_source) | ||
| 789 | { | 792 | { |
| 790 | pt_ref(r.dest) = str->get_allocation(); | 793 | auto dest_maybe = at_val<vm_alloc_t>(r.dest); |
| 791 | dec_ref_count_and_free(dest_maybe); | 794 | dec_ref_count_and_free(dest_maybe); |
| 795 | |||
| 796 | pt_ref(r.dest) = new_str_maybe->get_allocation(); | ||
| 792 | } | 797 | } |
| 793 | } | 798 | } |
| 794 | 799 | ||
| @@ -1659,6 +1664,7 @@ namespace · 864 unmodified lines | |||
| 1659 | // The remainder of the type is user-defined. | 1664 | // The remainder of the type is user-defined. |
| 1660 | auto content = e->get_allocation(); | 1665 | auto content = e->get_allocation(); |
| 1661 | exception_id = at_val<vm_string_t>(content); | 1666 | exception_id = at_val<vm_string_t>(content); |
| 1667 | assert(exception_id != nullptr); | ||
| 1662 | 1668 | ||
| 1663 | if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) | 1669 | if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) |
| 1664 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<", exception_id->str()); | 1670 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<", exception_id->str()); |
| @@ -169,6 +169,12 @@ namespace · 168 unmodified lines | |||
| 169 | // Length + 1 (null) + space for string expansion | 169 | // Length + 1 (null) + space for string expansion |
| 170 | return (current_length + 1 + (current_length / 4)); | 170 | return (current_length + 1 + (current_length / 4)); |
| 171 | } | 171 | } |
| 172 | |||
| 173 | template<typename T, word_t S> | ||
| 174 | constexpr word_t compute_max_length(const T(&)[S]) | ||
| 175 | { | ||
| 176 | return S - 1; | ||
| 177 | } | ||
| 172 | } | 178 | } |
| 173 | 179 | ||
| 174 | vm_string_t::vm_string_t() | 180 | vm_string_t::vm_string_t() |
| @@ -177,7 +183,7 @@ vm_string_t::vm_string_t() · 2 unmodified lines | |||
| 177 | , _encoded_str{ nullptr } | 183 | , _encoded_str{ nullptr } |
| 178 | , _is_alloc{ false } | 184 | , _is_alloc{ false } |
| 179 | , _length{ 0 } | 185 | , _length{ 0 } |
| 180 | , _length_max{ sizeof(_mem.local) } | 186 | , _length_max{ compute_max_length(_mem.local) } |
| 181 | , _mem{} | 187 | , _mem{} |
| 182 | { } | 188 | { } |
| 183 | 189 | ||
| @@ -187,7 +193,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 3 unmodified lines | |||
| 187 | , _encoded_str{ nullptr } | 193 | , _encoded_str{ nullptr } |
| 188 | , _is_alloc{ false } | 194 | , _is_alloc{ false } |
| 189 | , _length{ 0 } | 195 | , _length{ 0 } |
| 190 | , _length_max{ sizeof(_mem.local) } | 196 | , _length_max{ compute_max_length(_mem.local) } |
| 191 | , _mem{} | 197 | , _mem{} |
| 192 | { | 198 | { |
| 193 | assert((encoded_str_len > 0 && encoded_str != nullptr) || encoded_str_len == 0); | 199 | assert((encoded_str_len > 0 && encoded_str != nullptr) || encoded_str_len == 0); |
| @@ -203,11 +209,11 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 9 unmodified lines | |||
| 203 | _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char); | 209 | _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char); |
| 204 | 210 | ||
| 205 | _length = static_cast<word_t>(utf8_length.codepoint_count); | 211 | _length = static_cast<word_t>(utf8_length.codepoint_count); |
| 206 | _length_max = sizeof(_mem.local) / _character_size; | 212 | _length_max = compute_max_length(_mem.local) / _character_size; |
| 207 | 213 | ||
| 208 | // Allocate memory based on character count and size | 214 | // Allocate memory based on character count and size |
| 209 | auto dest = _mem.local; | 215 | auto dest = _mem.local; |
| 210 | if ((encoded_str_len + 1) > sizeof(_mem.local)) | 216 | if (encoded_str_len > static_cast<std::size_t>(compute_max_length(_mem.local))) |
| 211 | { | 217 | { |
| 212 | // If we are going to allocate, make sure it is larger than needed. | 218 | // If we are going to allocate, make sure it is larger than needed. |
| 213 | _length_max = compute_max_length(_length); | 219 | _length_max = compute_max_length(_length); |
| @@ -268,7 +274,7 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 54 unmodified lines | |||
| 268 | , _length_max{ compute_max_length(end_index - begin_index) } | 274 | , _length_max{ compute_max_length(end_index - begin_index) } |
| 269 | , _mem{} | 275 | , _mem{} |
| 270 | { | 276 | { |
| 271 | if (end_index <= begin_index) | 277 | if (end_index < begin_index) |
| 272 | throw out_of_range_memory{}; | 278 | throw out_of_range_memory{}; |
| 273 | 279 | ||
| 274 | if (begin_index < 0) | 280 | if (begin_index < 0) |
| @@ -346,7 +352,7 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 71 unmodified lines | |||
| 346 | 352 | ||
| 347 | // Define the values for the appended string | 353 | // Define the values for the appended string |
| 348 | const auto new_length = local_length + other_length; | 354 | const auto new_length = local_length + other_length; |
| 349 | const auto new_length_max = (new_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(new_length); | 355 | const auto new_length_max = (new_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(new_length); |
| 350 | const auto new_is_rune = local_is_rune || other_is_rune; | 356 | const auto new_is_rune = local_is_rune || other_is_rune; |
| 351 | uint8_t *new_source = nullptr; | 357 | uint8_t *new_source = nullptr; |
| 352 | 358 | ||
| @@ -442,7 +448,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 89 unmodified lines | |||
| 442 | _character_size = static_cast<uint8_t>(new_character_size); | 448 | _character_size = static_cast<uint8_t>(new_character_size); |
| 443 | assert(_character_size == sizeof(rune_t)); | 449 | assert(_character_size == sizeof(rune_t)); |
| 444 | 450 | ||
| 445 | _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | 451 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); |
| 446 | auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size); | 452 | auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size); |
| 447 | 453 | ||
| 448 | // Copy source into the new alloc. | 454 | // Copy source into the new alloc. |
| @@ -460,9 +466,9 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 11 unmodified lines | |||
| 460 | } | 466 | } |
| 461 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. | 467 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. |
| 462 | { | 468 | { |
| 463 | _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | 469 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); |
| 464 | auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size); | 470 | auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size); |
| 465 | ::memcpy(new_alloc, source, _length * _character_size); | 471 | std::memcpy(new_alloc, source, _length * _character_size); |
| 466 | 472 | ||
| 467 | // Free memory, if allocated | 473 | // Free memory, if allocated |
| 468 | if (_is_alloc) | 474 | if (_is_alloc) |
| @@ -503,7 +509,7 @@ const char *vm_string_t::str() const · 34 unmodified lines | |||
| 503 | if (_encoded_str != nullptr) | 509 | if (_encoded_str != nullptr) |
| 504 | return _encoded_str; | 510 | return _encoded_str; |
| 505 | 511 | ||
| 506 | // If the string is ASCII then just return the memory | 512 | // If the string is ASCII then return the memory |
| 507 | if (_character_size == sizeof(char)) | 513 | if (_character_size == sizeof(char)) |
| 508 | { | 514 | { |
| 509 | _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local); | 515 | _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local); |
| @@ -44,30 +44,32 @@ word_t disvm::runtime::sys::printf_to_buffer( · 43 unmodified lines | |||
| 44 | const auto fmt_len = msg_fmt.get_length(); | 44 | const auto fmt_len = msg_fmt.get_length(); |
| 45 | auto i = word_t{ 0 }; | 45 | auto i = word_t{ 0 }; |
| 46 | 46 | ||
| 47 | auto format = std::vector<char>{}; | ||
| 48 | format.reserve(32); // Default size in Inferno. | ||
| 49 | |||
| 47 | // Loop over the entire format string | 50 | // Loop over the entire format string |
| 48 | while (i < fmt_len) | 51 | while (i < fmt_len) |
| 49 | { | 52 | { |
| 50 | if (b_curr >= b_end) | 53 | if (b_curr >= b_end) |
| 51 | return -1; | 54 | return -1; |
| 52 | 55 | ||
| 53 | auto c = msg_fmt.get_rune(i); | 56 | auto c = msg_fmt.get_rune(i++); |
| 54 | ++i; | ||
| 55 | if (c != '%') | 57 | if (c != '%') |
| 56 | { | 58 | { |
| 57 | b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr))); | 59 | b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr))); |
| 58 | continue; | 60 | continue; |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | auto format = std::vector<char>{}; | 63 | format.clear(); |
| 62 | format.push_back('%'); | 64 | format.push_back('%'); |
| 63 | auto wb = int{ 0 }; | 65 | auto wb = int{ 0 }; |
| 64 | auto big_flag = false; | 66 | auto big_flag = false; |
| 67 | auto unsigned_flag = false; | ||
| 65 | 68 | ||
| 66 | // Decode the format specifier | 69 | // Decode the format specifier |
| 67 | while (i < fmt_len) | 70 | while (i < fmt_len) |
| 68 | { | 71 | { |
| 69 | c = msg_fmt.get_rune(i); | 72 | c = msg_fmt.get_rune(i++); |
| 70 | ++i; | ||
| 71 | switch (c) | 73 | switch (c) |
| 72 | { | 74 | { |
| 73 | // Format flags | 75 | // Format flags |
| @@ -79,6 +81,21 @@ word_t disvm::runtime::sys::printf_to_buffer( · 5 unmodified lines | |||
| 79 | format.push_back('l'); | 81 | format.push_back('l'); |
| 80 | big_flag = true; | 82 | big_flag = true; |
| 81 | continue; | 83 | continue; |
| 84 | case '*': // Placeholder for word argument | ||
| 85 | { | ||
| 86 | const auto w = *reinterpret_cast<word_t *>(msg_args); | ||
| 87 | std::array<char, sizeof("-2147483648")> buffer; | ||
| 88 | const auto len = std::sprintf(buffer.data(), "%d", w); | ||
| 89 | if (len < 0) | ||
| 90 | return -1; | ||
| 91 | |||
| 92 | format.insert(std::end(format), buffer.data(), buffer.data() + len); | ||
| 93 | msg_args += sizeof(w); | ||
| 94 | continue; | ||
| 95 | } | ||
| 96 | case 'u': | ||
| 97 | unsigned_flag = true; | ||
| 98 | continue; | ||
| 82 | 99 | ||
| 83 | // Format verbs | 100 | // Format verbs |
| 84 | case '%': // Escaped specifier | 101 | case '%': // Escaped specifier |
| @@ -100,10 +117,13 @@ word_t disvm::runtime::sys::printf_to_buffer( · 15 unmodified lines | |||
| 100 | msg_args += sizeof(r); | 117 | msg_args += sizeof(r); |
| 101 | break; | 118 | break; |
| 102 | } | 119 | } |
| 103 | case 'o': // octal | ||
| 104 | case 'd': // decimal | 120 | case 'd': // decimal |
| 121 | if (unsigned_flag) | ||
| 122 | c = 'u'; | ||
| 123 | case 'o': // octal | ||
| 105 | case 'x': // hexadecimal | 124 | case 'x': // hexadecimal |
| 106 | case 'X': // Upper-case hexadecimal | 125 | case 'X': // Upper-case hexadecimal |
| 126 | case 'c': // Unicode | ||
| 107 | format.push_back(static_cast<char>(c)); | 127 | format.push_back(static_cast<char>(c)); |
| 108 | format.push_back('\0'); | 128 | format.push_back('\0'); |
| 109 | if (big_flag) | 129 | if (big_flag) |
| @@ -121,6 +141,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 11 unmodified lines | |||
| 121 | msg_args += sizeof(w); | 141 | msg_args += sizeof(w); |
| 122 | } | 142 | } |
| 123 | break; | 143 | break; |
| 144 | case 'q': | ||
| 145 | assert(false && "'q' format not implemented"); | ||
| 124 | case 's': // string | 146 | case 's': // string |
| 125 | { | 147 | { |
| 126 | auto p = *reinterpret_cast<pointer_t *>(msg_args); | 148 | auto p = *reinterpret_cast<pointer_t *>(msg_args); |