// // Dis VM // File: print.cpp // Author: arr // #include #include #include #include "sys_utils.h" using namespace disvm::runtime; using namespace disvm::runtime::utf8; namespace { // Align the supplied pointer for the template type relative to base. template byte_t *align_for(byte_t *p, pointer_t base) { for (;;) { const auto d = reinterpret_cast(p) - reinterpret_cast(base); if (d % sizeof(T) == 0) return p; p++; } } } // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c) // [TODO] Handle UTF-8 word_t disvm::runtime::sys::printf_to_buffer( const disvm::runtime::vm_string_t &msg_fmt, disvm::runtime::byte_t *msg_args, disvm::runtime::pointer_t base, const std::size_t buffer_size, char *buffer) { auto b_curr = buffer; const auto b_end = buffer + (buffer_size - 1); const auto fmt_len = msg_fmt.get_length(); auto i = word_t{ 0 }; // Loop over the entire format string while (i < fmt_len) { if (b_curr >= b_end) return -1; auto c = msg_fmt.get_rune(i); ++i; if (c != '%') { b_curr = reinterpret_cast(utf8::encode(c, reinterpret_cast(b_curr))); continue; } auto format = std::vector{}; format.push_back('%'); auto wb = int{ 0 }; auto big_flag = false; // Decode the format specifier while (i < fmt_len) { c = msg_fmt.get_rune(i); ++i; switch (c) { // Format flags default: continue; case 'b': // Print big type (64-bit) format.push_back('l'); format.push_back('l'); big_flag = true; continue; // Format verbs case '%': // Escaped specifier wb = std::snprintf(b_curr, (b_end - b_curr), "%%%%"); break; case 'f': // floating point verbs case 'e': case 'E': case 'g': case 'G': { format.push_back(static_cast(c)); format.push_back('\0'); msg_args = align_for(msg_args, base); const auto r = *reinterpret_cast(msg_args); wb = std::snprintf(b_curr, (b_end - b_curr), format.data(), r); msg_args += sizeof(r); break; } case 'o': // octal case 'd': // decimal case 'x': // hexidecimal case 'X': // Upper-case hexidecimal format.push_back(static_cast(c)); format.push_back('\0'); if (big_flag) { msg_args = align_for(msg_args, base); const auto b = *reinterpret_cast(msg_args); wb = std::snprintf(b_curr, (b_end - b_curr), format.data(), b); msg_args += sizeof(b); } else { const auto w = *reinterpret_cast(msg_args); wb = std::snprintf(b_curr, (b_end - b_curr), format.data(), w); msg_args += sizeof(w); } break; case 's': // string { auto p = *reinterpret_cast(msg_args); auto str = vm_alloc_t::from_allocation(p); if (str != nullptr) wb = std::snprintf(b_curr, (b_end - b_curr), "%s", str->str()); msg_args += sizeof(p); break; } // [SPEC] Undocumented format verb for debugging VM objects. case 'H': { auto p = *reinterpret_cast(msg_args); auto alloc = vm_alloc_t::from_allocation(p); auto rc = static_cast(-1); const void *type_alloc = nullptr; if (alloc != nullptr) { rc = alloc->get_ref_count(); type_alloc = alloc->alloc_type.get(); } wb = std::snprintf(b_curr, (b_end - b_curr), "%" PRIuPTR ".%#08" PRIxPTR, rc, reinterpret_cast(type_alloc)); msg_args += sizeof(p); break; } } break; } if (wb < 0) return -1; b_curr += wb; } assert(*b_curr == '\0' && "The buffer should be null-terminated"); return (b_curr - buffer); } word_t disvm::runtime::sys::printf_to_dynamic_buffer( const disvm::runtime::vm_string_t &msg_fmt, disvm::runtime::byte_t *msg_args, disvm::runtime::pointer_t base, std::vector &buffer) { auto result = word_t{ 0 }; while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data()))) buffer.resize(buffer.size() * 2); return result; }