/* * Copyright (C) 2022 Riyyi * * SPDX-License-Identifier: MIT */ #include // min #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t #include // setprecision #include // defaultfloat, fixed #include // numeric_limits #include // stringstream #include #include #include "util/format/builder.h" #include "util/format/parser.h" #include "util/meta/assert.h" namespace Util::Format { void Builder::putLiteral(std::string_view literal) { for (size_t i = 0; i < literal.length(); ++i) { putCharacter(literal[i]); if (literal[i] == '{' || literal[i] == '}') { ++i; } } } static constexpr std::string numberToString(size_t value, uint8_t base, bool uppercase) { std::string result; if (value > std::numeric_limits::max()) { result.reserve(64); } else if (value > std::numeric_limits::max()) { result.reserve(32); } constexpr const auto& lookupLowercase = "0123456789abcdef"; constexpr const auto& lookupUppercase = "0123456789ABCDEF"; if (value == 0) { result = '0'; } else if (uppercase) { while (value > 0) { result.insert(0, 1, lookupUppercase[value % base]); value /= base; } } else { while (value > 0) { result.insert(0, 1, lookupLowercase[value % base]); value /= base; } } return result; } void Builder::putU64(size_t value, uint8_t base, bool uppercase, char fill, Align align, Sign sign, bool alternativeForm, bool zeroPadding, size_t width, bool isNegative) const { std::string number = numberToString(value, base, uppercase); // Sign std::string prefix = ""; switch (sign) { case Sign::None: case Sign::Negative: if (isNegative) { prefix = '-'; } break; case Sign::Both: prefix = (isNegative) ? '-' : '+'; break; case Sign::Space: prefix = (isNegative) ? '-' : ' '; break; default: VERIFY_NOT_REACHED(); }; // Alternative form if (alternativeForm) { switch (base) { case 2: prefix += (uppercase) ? "0B" : "0b"; break; case 8: prefix += '0'; break; case 10: break; case 16: prefix += (uppercase) ? "0X" : "0x"; break; default: VERIFY_NOT_REACHED(); } } if (!zeroPadding) { number.insert(0, prefix); } else { if (align != Builder::Align::None) { number.insert(0, prefix); } fill = '0'; } size_t length = number.length(); if (width < length) { m_builder.write(number.data(), length); return; } switch (align) { case Align::Left: m_builder.write(number.data(), length); m_builder << std::string(width - length, fill); break; case Align::Center: { size_t half = (width - length) / 2; m_builder << std::string(half, fill); m_builder.write(number.data(), length); m_builder << std::string(width - half - length, fill); break; } case Align::Right: m_builder << std::string(width - length, fill); m_builder.write(number.data(), length); break; case Align::None: if (zeroPadding) { m_builder << prefix; m_builder << std::string(width - length - prefix.length(), fill); } else { m_builder << std::string(width - length, fill); } m_builder.write(number.data(), length); break; default: VERIFY_NOT_REACHED(); }; } void Builder::putI64(int64_t value, uint8_t base, bool uppercase, char fill, Align align, Sign sign, bool alternativeForm, bool zeroPadding, size_t width) const { bool isNegative = value < 0; value = isNegative ? -value : value; putU64(static_cast(value), base, uppercase, fill, align, sign, alternativeForm, zeroPadding, width, isNegative); } void Builder::putF64(double number, uint8_t precision) const { precision = std::min(precision, static_cast(std::numeric_limits::digits10)); std::stringstream stream; stream << std::fixed << std::setprecision(precision) << number << std::defaultfloat << std::setprecision(6); std::string string = stream.str(); m_builder << string; } void Builder::putString(std::string_view string, char fill, Align align, size_t width) const { size_t length = string.length(); if (width < length) { m_builder.write(string.data(), length); return; } switch (align) { case Align::None: case Align::Left: m_builder.write(string.data(), length); m_builder << std::string(width - length, fill); break; case Align::Center: { size_t half = (width - length) / 2; m_builder << std::string(half, fill); m_builder.write(string.data(), length); m_builder << std::string(width - half - length, fill); break; } case Align::Right: m_builder << std::string(width - length, fill); m_builder.write(string.data(), length); break; default: VERIFY_NOT_REACHED(); }; } } // namespace Util::Format