Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a98f8e79f | ||
|
|
25287bc370 | ||
|
|
5eb5e2d51e | ||
|
|
c40650ab67 | ||
|
|
9758c3e425 |
+106
-10
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||
#include <iomanip> // setprecision
|
||||
#include <ios> // defaultfloat, fixed
|
||||
#include <limits> // numeric_limits
|
||||
@@ -28,17 +29,112 @@ void Builder::putLiteral(std::string_view literal)
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::putF32(float number, uint8_t precision) const
|
||||
static constexpr std::string numberToString(size_t value, uint8_t base, bool uppercase)
|
||||
{
|
||||
precision = std::min(precision, static_cast<uint8_t>(std::numeric_limits<float>::digits10));
|
||||
std::string result;
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
<< std::fixed << std::setprecision(precision)
|
||||
<< number
|
||||
<< std::defaultfloat << std::setprecision(6);
|
||||
std::string string = stream.str();
|
||||
m_builder << string;
|
||||
if (value > std::numeric_limits<uint32_t>::max()) {
|
||||
result.reserve(64);
|
||||
}
|
||||
else if (value > std::numeric_limits<uint16_t>::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 zeroPadding, size_t width, bool isNegative) const
|
||||
{
|
||||
std::string string = numberToString(value, base, uppercase);
|
||||
|
||||
// Sign
|
||||
std::string signCharacter = "";
|
||||
switch (sign) {
|
||||
case Sign::None:
|
||||
case Sign::Negative:
|
||||
if (isNegative) {
|
||||
signCharacter = '-';
|
||||
}
|
||||
break;
|
||||
case Sign::Both:
|
||||
signCharacter = (isNegative) ? '-' : '+';
|
||||
break;
|
||||
case Sign::Space:
|
||||
signCharacter = (isNegative) ? '-' : ' ';
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
if (align != Align::None || !zeroPadding) {
|
||||
string.insert(0, signCharacter);
|
||||
}
|
||||
|
||||
// Zero padding
|
||||
if (zeroPadding) {
|
||||
fill = '0';
|
||||
}
|
||||
|
||||
size_t length = string.length();
|
||||
if (width < length) {
|
||||
m_builder.write(string.data(), length);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (align) {
|
||||
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;
|
||||
case Align::None:
|
||||
if (zeroPadding) {
|
||||
m_builder << signCharacter;
|
||||
if (signCharacter.empty()) {
|
||||
m_builder << '0';
|
||||
}
|
||||
}
|
||||
m_builder << std::string(width - length - zeroPadding, fill);
|
||||
m_builder.write(string.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 zeroPadding, size_t width) const
|
||||
{
|
||||
bool isNegative = value < 0;
|
||||
value = isNegative ? -value : value;
|
||||
putU64(static_cast<uint64_t>(value), base, uppercase, fill, align, sign, zeroPadding, width, isNegative);
|
||||
}
|
||||
|
||||
void Builder::putF64(double number, uint8_t precision) const
|
||||
@@ -54,7 +150,7 @@ void Builder::putF64(double number, uint8_t precision) const
|
||||
m_builder << string;
|
||||
}
|
||||
|
||||
void Builder::putString(std::string_view string, size_t width, Align align, char fill) const
|
||||
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
|
||||
{
|
||||
size_t length = string.length();
|
||||
if (width < length) {
|
||||
|
||||
@@ -36,14 +36,11 @@ public:
|
||||
|
||||
void putLiteral(std::string_view literal);
|
||||
|
||||
void putI32(int32_t number) const { m_builder << number; } // int
|
||||
void putU32(uint32_t number) const { m_builder << number; } // unsigned int
|
||||
void putI64(int64_t number) const { m_builder << number; } // long int
|
||||
void putU64(size_t number) const { m_builder << number; } // long unsigned int
|
||||
void putF32(float number, uint8_t precision = 6) const;
|
||||
void putU64(size_t value, uint8_t base = 10, bool uppercase = false, char fill = ' ', Align align = Align::Right, Sign sign = Sign::Negative, bool zeroPadding = false, size_t width = 0, bool isNegative = false) const;
|
||||
void putI64(int64_t value, uint8_t base = 10, bool uppercase = false, char fill = ' ', Align align = Align::Right, Sign sign = Sign::Negative, bool zeroPadding = false, size_t width = 0) const;
|
||||
void putF64(double number, uint8_t precision = 6) const;
|
||||
void putCharacter(char character) const { m_builder.write(&character, 1); }
|
||||
void putString(std::string_view string, size_t width = 0, Align align = Align::Left, char fill = ' ') const;
|
||||
void putString(std::string_view string, char fill = ' ', Align align = Align::Left, size_t width = 0) const;
|
||||
void putPointer(const void* pointer) const { m_builder << pointer; }
|
||||
|
||||
const std::stringstream& builder() const { return m_builder; }
|
||||
|
||||
@@ -16,56 +16,6 @@
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
// Integral
|
||||
|
||||
template<>
|
||||
void Formatter<int32_t>::format(Builder& builder, int32_t value) const
|
||||
{
|
||||
builder.putI32(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<uint32_t>::format(Builder& builder, uint32_t value) const
|
||||
{
|
||||
builder.putU32(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<int64_t>::format(Builder& builder, int64_t value) const
|
||||
{
|
||||
builder.putI64(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<size_t>::format(Builder& builder, size_t value) const
|
||||
{
|
||||
builder.putU64(value);
|
||||
}
|
||||
|
||||
// Floating point
|
||||
|
||||
template<>
|
||||
void Formatter<float>::format(Builder& builder, float value) const
|
||||
{
|
||||
if (specifier.precision < 0) {
|
||||
builder.putF32(value);
|
||||
}
|
||||
else {
|
||||
builder.putF32(value, specifier.precision);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<double>::format(Builder& builder, double value) const
|
||||
{
|
||||
if (specifier.precision < 0) {
|
||||
builder.putF64(value);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.putF64(value, specifier.precision);
|
||||
}
|
||||
|
||||
// Char
|
||||
|
||||
template<>
|
||||
@@ -85,7 +35,7 @@ void Formatter<bool>::format(Builder& builder, bool value) const
|
||||
template<>
|
||||
void Formatter<std::string_view>::format(Builder& builder, std::string_view value) const
|
||||
{
|
||||
builder.putString(value, specifier.width, specifier.align, specifier.fill);
|
||||
builder.putString(value, specifier.fill, specifier.align, specifier.width);
|
||||
}
|
||||
|
||||
void Formatter<const char*>::parse(Parser& parser)
|
||||
@@ -101,7 +51,9 @@ void Formatter<const char*>::format(Builder& builder, const char* value) const
|
||||
return;
|
||||
}
|
||||
|
||||
builder.putString(value != nullptr ? std::string_view { value, strlen(value) } : "nullptr");
|
||||
Formatter<std::string_view>::format(
|
||||
builder,
|
||||
value != nullptr ? std::string_view { value, strlen(value) } : "nullptr");
|
||||
}
|
||||
|
||||
// Pointer
|
||||
|
||||
+66
-20
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int8_t, int32_t, int64_t, uint8_t, uint32_t
|
||||
#include <map>
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/parser.h"
|
||||
#include "util/meta/concepts.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
@@ -66,13 +68,7 @@ struct Formatter {
|
||||
|
||||
constexpr void parse(Parser& parser)
|
||||
{
|
||||
if (std::is_integral_v<T>) {
|
||||
parser.parseSpecifier(specifier, Parser::ParameterType::Integral);
|
||||
}
|
||||
else if (std::is_floating_point_v<T>) {
|
||||
parser.parseSpecifier(specifier, Parser::ParameterType::FloatingPoint);
|
||||
}
|
||||
else if (std::is_same_v<T, char>) {
|
||||
if (std::is_same_v<T, char>) {
|
||||
parser.parseSpecifier(specifier, Parser::ParameterType::Char);
|
||||
}
|
||||
else if (std::is_same_v<T, std::string_view>) {
|
||||
@@ -80,30 +76,80 @@ struct Formatter {
|
||||
}
|
||||
}
|
||||
|
||||
void format(Builder& builder, T value) const { (void)builder, (void)value; }
|
||||
void format(Builder&, T) const {}
|
||||
};
|
||||
|
||||
// Integral
|
||||
|
||||
template<>
|
||||
void Formatter<int32_t>::format(Builder& builder, int32_t value) const;
|
||||
template<Integral T>
|
||||
struct Formatter<T> {
|
||||
Specifier specifier;
|
||||
|
||||
template<>
|
||||
void Formatter<uint32_t>::format(Builder& builder, uint32_t value) const;
|
||||
void parse(Parser& parser)
|
||||
{
|
||||
parser.parseSpecifier(specifier, Parser::ParameterType::Integral);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Formatter<int64_t>::format(Builder& builder, int64_t value) const;
|
||||
void format(Builder& builder, T value) const
|
||||
{
|
||||
uint8_t base = 0;
|
||||
bool uppercase = false;
|
||||
switch (specifier.type) {
|
||||
case PresentationType::BinaryUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Binary:
|
||||
base = 2;
|
||||
break;
|
||||
case PresentationType::Octal:
|
||||
base = 8;
|
||||
break;
|
||||
case PresentationType::None:
|
||||
case PresentationType::Decimal:
|
||||
base = 10;
|
||||
break;
|
||||
case PresentationType::HexUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Hex:
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
};
|
||||
|
||||
template<>
|
||||
void Formatter<size_t>::format(Builder& builder, size_t value) const; // uint64_t
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
builder.putU64(
|
||||
value, base, uppercase, specifier.fill, specifier.align, specifier.sign,
|
||||
specifier.zeroPadding, specifier.width);
|
||||
}
|
||||
else {
|
||||
builder.putI64(
|
||||
value, base, uppercase, specifier.fill, specifier.align, specifier.sign,
|
||||
specifier.zeroPadding, specifier.width);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Floating point
|
||||
|
||||
template<>
|
||||
void Formatter<float>::format(Builder& builder, float value) const;
|
||||
template<FloatingPoint T>
|
||||
struct Formatter<T> {
|
||||
Specifier specifier;
|
||||
|
||||
template<>
|
||||
void Formatter<double>::format(Builder& builder, double value) const;
|
||||
void parse(Parser& parser)
|
||||
{
|
||||
parser.parseSpecifier(specifier, Parser::ParameterType::FloatingPoint);
|
||||
}
|
||||
|
||||
void format(Builder& builder, T value) const
|
||||
{
|
||||
if (specifier.precision < 0) {
|
||||
builder.putF64(value);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.putF64(value, specifier.precision);
|
||||
}
|
||||
};
|
||||
|
||||
// Char
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits> // is_integral_v, floating_point_v
|
||||
|
||||
namespace Util::Concepts {
|
||||
|
||||
template<class T>
|
||||
concept Integral = std::is_integral_v<T>;
|
||||
|
||||
template<typename T>
|
||||
concept FloatingPoint = std::is_floating_point_v<T>;
|
||||
|
||||
} // namespace Util::Concepts
|
||||
|
||||
using Util::Concepts::FloatingPoint;
|
||||
using Util::Concepts::Integral;
|
||||
Reference in New Issue
Block a user