Browse Source

Util: Switch from format() to struct Formatter

master
Riyyi 2 years ago
parent
commit
f3344a0fb9
  1. 9
      src/util/format/builder.cpp
  2. 11
      src/util/format/builder.h
  3. 29
      src/util/format/format.cpp
  4. 3
      src/util/format/format.h
  5. 95
      src/util/format/formatter.cpp
  6. 117
      src/util/format/formatter.h
  7. 72
      src/util/format/parser.cpp
  8. 14
      src/util/format/parser.h

9
src/util/format/builder.cpp

@ -28,7 +28,7 @@ void Builder::putLiteral(std::string_view literal)
void Builder::putF32(float number, size_t precision) const
{
precision = (precision > std::numeric_limits<float>::digits10) ? m_precision : precision;
precision = (precision > std::numeric_limits<float>::digits10) ? 6 : precision;
std::stringstream stream;
stream
@ -41,7 +41,7 @@ void Builder::putF32(float number, size_t precision) const
void Builder::putF64(double number, size_t precision) const
{
precision = (precision > std::numeric_limits<double>::digits10) ? m_precision : precision;
precision = (precision > std::numeric_limits<double>::digits10) ? 6 : precision;
std::stringstream stream;
stream
@ -52,9 +52,4 @@ void Builder::putF64(double number, size_t precision) const
m_builder << string;
}
void Builder::resetSpecifiers()
{
setPrecision();
}
} // namespace Util::Format

11
src/util/format/builder.h

@ -16,8 +16,7 @@ namespace Util::Format {
class Builder {
public:
explicit Builder(std::stringstream& builder)
: m_precision(6)
, m_builder(builder)
: m_builder(builder)
{
}
@ -27,20 +26,16 @@ public:
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, size_t precision = -1) const;
void putF64(double number, size_t precision = -1) const;
void putF32(float number, size_t precision = 6) const;
void putF64(double number, size_t precision = 6) const;
void putCharacter(char character) const { m_builder.write(&character, 1); }
void putString(const std::string_view string) const { m_builder.write(string.data(), string.length()); }
void putPointer(const void* pointer) const { m_builder << pointer; }
void resetSpecifiers();
void setPrecision(size_t precision = 6) { m_precision = precision; };
const std::stringstream& builder() const { return m_builder; }
std::stringstream& builder() { return m_builder; }
private:
size_t m_precision { 6 };
std::stringstream& m_builder;
};

29
src/util/format/format.cpp

@ -11,27 +11,36 @@
#include "util/format/builder.h"
#include "util/format/format.h"
#include "util/format/parser.h"
#include "util/meta/assert.h"
namespace Util::Format {
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters)
{
// Consume everything until '{' or EOF
const auto literal = parser.consumeLiteral();
builder.putLiteral(literal);
if (!parameters.isEOF()) {
std::string_view specifier;
if (parser.consumeSpecifier(specifier)) {
parser.applySpecifier(builder, specifier);
}
// Reached end of format string
if (parser.isEOF()) {
return;
}
auto& parameter = parameters.parameter(parameters.tell());
parameter.format(builder, parameter.value);
parameters.ignore();
// Consume index + ':'
auto indexMaybe = parser.consumeIndex();
builder.resetSpecifiers();
}
// Get parameter at index, or next
size_t index = indexMaybe.has_value() ? indexMaybe.value() : parameters.tell();
VERIFY(index < parameters.size(), "argument not found at index '%zu':'%zu'", index, parameters.size());
auto& parameter = parameters.parameter(index);
// Format the parameter
parameter.format(builder, parser, parameter.value);
// Go to next parameter
parameters.ignore();
// Recurse
if (!parser.isEOF()) {
variadicFormatImpl(builder, parser, parameters);
}

3
src/util/format/format.h

@ -28,7 +28,8 @@ struct Parameter {
template<typename T>
void formatParameterValue(Builder& builder, const void* value)
{
_format(builder, *static_cast<const T*>(value));
Formatter<T> formatter;
formatter.format(builder, *static_cast<const T*>(value));
}
// Type erasure improves both compile time and binary size significantly

95
src/util/format/formatter.cpp

@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t, int64_t,
#include <cstring> // strlen
#include <string>
#include <string_view>
#include "util/format/builder.h"
#include "util/format/formatter.h"
#include "util/format/parser.h"
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
{
builder.putF32(value);
}
template<>
void Formatter<double>::format(Builder& builder, double value) const
{
builder.putF64(value);
}
// Char
template<>
void Formatter<char>::format(Builder& builder, char value) const
{
builder.putCharacter(value);
}
template<>
void Formatter<bool>::format(Builder& builder, bool value) const
{
builder.putString(value ? "true" : "false");
}
// String
void Formatter<const char*>::format(Builder& builder, const char* value) const
{
builder.putString(value != nullptr ? std::string_view { value, strlen(value) } : "(nil)");
}
template<>
void Formatter<std::string_view>::format(Builder& builder, std::string_view value) const
{
builder.putString(value);
}
// Pointer
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
{
Formatter<const void*>::format(builder, 0);
}
// Containers
} // namespace Util::Format

117
src/util/format/formatter.h

@ -0,0 +1,117 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint8_t, uint32_t, int64_t,
#include <string>
#include <string_view>
#include <vector>
#include "util/format/builder.h"
#include "util/format/parser.h"
namespace Util::Format {
template<typename T>
struct Formatter {
void format(Builder& builder, T value) const { (void)builder, (void)value; }
};
// Integral
template<>
void Formatter<int32_t>::format(Builder& builder, int32_t value) const;
template<>
void Formatter<uint32_t>::format(Builder& builder, uint32_t value) const;
template<>
void Formatter<int64_t>::format(Builder& builder, int64_t value) const;
template<>
void Formatter<size_t>::format(Builder& builder, size_t value) const; // uint64_t
// Floating point
template<>
void Formatter<float>::format(Builder& builder, float value) const;
template<>
void Formatter<double>::format(Builder& builder, double value) const;
// Char
template<>
void Formatter<char>::format(Builder& builder, char value) const;
template<>
void Formatter<bool>::format(Builder& builder, bool value) const;
// String
template<>
void Formatter<std::string_view>::format(Builder& builder, std::string_view value) const;
template<>
struct Formatter<std::string> : Formatter<std::string_view> {
};
template<>
struct Formatter<const char*> : Formatter<std::string_view> {
void format(Builder& builder, const char* value) const;
};
template<>
struct Formatter<char*> : Formatter<const char*> {
};
template<size_t N>
struct Formatter<char[N]> : Formatter<const char*> {
};
// Pointer
template<typename T>
struct Formatter<T*> {
void format(Builder& builder, T* value) const
{
value == nullptr
? builder.putString("0x0")
: builder.putPointer(static_cast<const void*>(value));
}
};
template<>
struct Formatter<std::nullptr_t> : Formatter<const void*> {
void format(Builder& builder, std::nullptr_t) const;
};
// Container
template<typename T>
struct Formatter<std::vector<T>> : Formatter<T> {
void format(Builder& builder, const std::vector<T>& value) const
{
builder.putString("{\n");
for (auto it = value.cbegin(); it != value.cend(); ++it) {
builder.putString(" ");
Formatter<T>::format(builder, *it);
// Add comma, except after the last element
if (it != std::prev(value.end(), 1)) {
builder.putCharacter(',');
}
builder.putCharacter('\n');
}
builder.putCharacter('}');
}
};
} // namespace Util::Format

72
src/util/format/parser.cpp

@ -6,6 +6,7 @@
#include <algorithm> // replace
#include <cstddef> // size_t
#include <limits> // numeric_limits
#include <string>
#include <string_view>
@ -53,7 +54,9 @@ void Parser::checkFormatParameterConsistency()
if (peek0 == '{') {
braceOpen++;
VERIFY(peek1 == '}' || peek1 == ':', "invalid parameter reference");
if (peek1 >= '0' && peek1 <= '9') {
m_mode = ArgumentIndexingMode::Manual;
}
}
if (peek0 == '}') {
braceClose++;
@ -63,13 +66,13 @@ void Parser::checkFormatParameterConsistency()
}
m_index = 0;
VERIFY(!(braceOpen < braceClose), "extra open braces in format string");
// VERIFY(!(braceOpen < braceClose), "extra open braces in format string");
VERIFY(!(braceOpen > braceClose), "extra closing braces in format string");
// VERIFY(!(braceOpen > braceClose), "extra closing braces in format string");
VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters");
// VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters");
VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
// VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
}
std::string_view Parser::consumeLiteral()
@ -101,44 +104,63 @@ std::string_view Parser::consumeLiteral()
return m_input.substr(begin);
}
bool Parser::consumeSpecifier(std::string_view& specifier)
std::optional<size_t> Parser::consumeIndex()
{
if (!consumeSpecific('{')) {
return false;
VERIFY_NOT_REACHED();
return {};
}
if (!consumeSpecific(':')) {
VERIFY(consumeSpecific('}'), "invalid parameter reference");
specifier = "";
switch (m_mode) {
case ArgumentIndexingMode::Automatic: {
VERIFY(consumeSpecific('}') || consumeSpecific(':'), "expecting '}' or ':', not '%c'", peek());
return {};
}
else {
case ArgumentIndexingMode::Manual: {
const auto begin = tell();
// Go until AFTER the closing brace
while (peek(-1) != '}') {
consume();
while (!isEOF()) {
char peek0 = peek();
if (peek0 == '}' || peek0 == ':') {
break;
}
VERIFY(peek0 >= '0' && peek0 <= '9', "expecting number, not '%c'", peek0);
ignore();
}
size_t result = stringToNumber(m_input.substr(begin, tell() - begin));
if (peek() == ':') {
ignore();
}
specifier = m_input.substr(begin, tell() - begin - 1);
return result;
}
};
return true;
VERIFY_NOT_REACHED();
}
void Parser::applySpecifier(Builder& builder, std::string_view specifier)
size_t Parser::stringToNumber(std::string_view value)
{
if (specifier[0] == '.') {
size_t value = 0;
size_t result = 0;
for (size_t i = 1; i < value.length(); ++i) {
if (value[i] < '0' || value[i] > '9') {
VERIFY_NOT_REACHED();
}
result *= 10;
result += value[i] - '0'; // Subtract ASCII 48 to get the number
}
return result;
}
for (size_t i = 1; i < specifier.length(); ++i) {
if (specifier[i] < '0' || specifier[i] > '9') {
return;
}
value *= 10;
value += specifier[i] - '0'; // Subtract ASCII 48 to get the number
}
builder.setPrecision(value);
}
}

14
src/util/format/parser.h

@ -6,7 +6,8 @@
#pragma once
#include <cstddef> // size_t
#include <cstddef> // size_t
#include <optional>
#include <string_view>
#include "util/genericlexer.h"
@ -17,17 +18,24 @@ class Builder;
class Parser final : public GenericLexer {
public:
enum class ArgumentIndexingMode {
Automatic, // {} ,{}
Manual, // {0},{1}
};
Parser(std::string_view format, size_t parameterCount);
virtual ~Parser();
void checkFormatParameterConsistency();
std::string_view consumeLiteral();
bool consumeSpecifier(std::string_view& specifier);
std::optional<size_t> consumeIndex();
size_t stringToNumber(std::string_view value);
void applySpecifier(Builder& builder, std::string_view specifier);
private:
ArgumentIndexingMode m_mode { ArgumentIndexingMode::Automatic };
size_t m_parameterCount { 0 };
};

Loading…
Cancel
Save