Compare commits
35
Commits
1361ee04f9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
243442a3b6 | ||
|
|
d5bf50715e | ||
|
|
8520952235 | ||
|
|
526948874a | ||
|
|
c8e4ae884e | ||
|
|
07c9f9959d | ||
|
|
9000d1d968 | ||
|
|
50fe09ee56 | ||
|
|
6cd2fe4e9a | ||
|
|
703be90792 | ||
|
|
da9fb1ed0f | ||
|
|
d6f378b6bc | ||
|
|
e592ee7fd4 | ||
|
|
8e2e1a13df | ||
|
|
afdd0f8b45 | ||
|
|
93af096d68 | ||
|
|
228e290b94 | ||
|
|
cacd3ca8fd | ||
|
|
953df138c0 | ||
|
|
f3e49d8f74 | ||
|
|
3b7d8d1e4a | ||
|
|
d3de1fb635 | ||
|
|
f31f7feb5e | ||
|
|
7093b5ad3d | ||
|
|
145c7e44a2 | ||
|
|
29406fac1f | ||
|
|
27489c6cb4 | ||
|
|
a83615083d | ||
|
|
febb89be9c | ||
|
|
bbeab2efd0 | ||
|
|
0448e01f0a | ||
|
|
94eb1dd173 | ||
|
|
f7157feddf | ||
|
|
b5e55f13d3 | ||
|
|
054980e0f2 |
@@ -14,6 +14,7 @@ AlignTrailingComments: true
|
|||||||
AllowAllArgumentsOnNextLine: false
|
AllowAllArgumentsOnNextLine: false
|
||||||
AllowAllConstructorInitializersOnNextLine: true
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
AllowShortLambdasOnASingleLine: All
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
|||||||
+13
-11
@@ -4,8 +4,13 @@
|
|||||||
# Set project name
|
# Set project name
|
||||||
set(PROJECT "ruc")
|
set(PROJECT "ruc")
|
||||||
|
|
||||||
# Unit tests
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
option(RUC_BUILD_TESTS "Build the RUC test programs" ON)
|
set(RUC_STANDALONE TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||||
|
option(RUC_BUILD_TESTS "Build the RUC test programs" ${RUC_STANDALONE})
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
@@ -55,8 +60,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||||||
# Define library source files
|
# Define library source files
|
||||||
file(GLOB_RECURSE LIBRARY_SOURCES "src/*.cpp")
|
file(GLOB_RECURSE LIBRARY_SOURCES "src/*.cpp")
|
||||||
|
|
||||||
add_library(${PROJECT} STATIC ${LIBRARY_SOURCES})
|
add_library(${PROJECT} ${LIBRARY_SOURCES})
|
||||||
target_include_directories(${PROJECT} PRIVATE
|
target_include_directories(${PROJECT} PUBLIC
|
||||||
"src")
|
"src")
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
@@ -65,8 +70,8 @@ target_include_directories(${PROJECT} PRIVATE
|
|||||||
# Define source files
|
# Define source files
|
||||||
file(GLOB TEST_LIBRARY_SOURCES "test/*.cpp" "src/ruc/timer.cpp")
|
file(GLOB TEST_LIBRARY_SOURCES "test/*.cpp" "src/ruc/timer.cpp")
|
||||||
|
|
||||||
add_library(${PROJECT}-test STATIC ${TEST_LIBRARY_SOURCES})
|
add_library(${PROJECT}-test ${TEST_LIBRARY_SOURCES})
|
||||||
target_include_directories(${PROJECT}-test PRIVATE
|
target_include_directories(${PROJECT}-test PUBLIC
|
||||||
"src"
|
"src"
|
||||||
"test")
|
"test")
|
||||||
|
|
||||||
@@ -75,11 +80,8 @@ target_include_directories(${PROJECT}-test PRIVATE
|
|||||||
|
|
||||||
if (RUC_BUILD_TESTS)
|
if (RUC_BUILD_TESTS)
|
||||||
# Define test source files
|
# Define test source files
|
||||||
file(GLOB_RECURSE TEST_SOURCES "test/*.cpp")
|
file(GLOB_RECURSE TEST_SOURCES "test/unit/*.cpp")
|
||||||
set(TEST_SOURCES ${TEST_SOURCES} ${LIBRARY_SOURCES})
|
|
||||||
|
|
||||||
add_executable(${PROJECT}-unit-test ${TEST_SOURCES})
|
add_executable(${PROJECT}-unit-test ${TEST_SOURCES})
|
||||||
target_include_directories(${PROJECT}-unit-test PRIVATE
|
target_link_libraries(${PROJECT}-unit-test ruc ruc-test)
|
||||||
"src"
|
|
||||||
"test")
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -9,3 +9,9 @@ Utility library for C++.
|
|||||||
- Formatting library
|
- Formatting library
|
||||||
- JSON parsing
|
- JSON parsing
|
||||||
- Unit test macros
|
- Unit test macros
|
||||||
|
|
||||||
|
** Formatting library
|
||||||
|
|
||||||
|
The format specification has been heavily inspired by the ~{fmt}~ library, most of the [[https://fmt.dev/latest/syntax.html#format-specification-mini-language][mini-language]] is supported.
|
||||||
|
|
||||||
|
*Note*: Development was previously done at: [[https://github.com/riyyi/manafiles/tree/6f0e3d6063ab75ad81899135689569e440ddb813/src/util][manafiles/src/util]] and [[https://github.com/riyyi/manafiles/tree/6f0e3d6063ab75ad81899135689569e440ddb813/test][manafiles/test]].
|
||||||
|
|||||||
+26
-11
@@ -7,22 +7,37 @@
|
|||||||
|
|
||||||
b="$(tput bold)"
|
b="$(tput bold)"
|
||||||
red="$(tput setf 4)"
|
red="$(tput setf 4)"
|
||||||
|
yellow="$(tput setf 6)"
|
||||||
|
blue="$(tput setf 1)"
|
||||||
|
green="$(tput setf 2)"
|
||||||
n="$(tput sgr0)"
|
n="$(tput sgr0)"
|
||||||
|
|
||||||
if [ ! -d ".git" ]; then
|
if [ ! -d ".git" ]; then
|
||||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Temporary clear unstaged files from the repository to pass the diff checker
|
||||||
|
unstaged="$(git --no-pager diff --name-only)"
|
||||||
|
if [ -n "$unstaged" ]; then
|
||||||
|
patch=".git/patch-$(date +%s)"
|
||||||
|
echo "[${yellow}WARNING${n}]: Stashing unstaged files to $patch"
|
||||||
|
git --no-pager diff > "$patch"
|
||||||
|
git restore .
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
git apply "$patch"
|
||||||
|
rm -rf "${patch:?}"
|
||||||
|
echo "[${blue}INFO${n}]: Restored changes from $patch"
|
||||||
|
}
|
||||||
|
trap remove EXIT HUP INT TERM
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the path from the project root to the script
|
# Get the path from the project root to the script
|
||||||
subDir="$(dirname -- "$0")"
|
subDir="$(dirname -- "$0")"
|
||||||
|
|
||||||
# Get all files staged for commit
|
# Get all files staged for commit
|
||||||
files="$(git --no-pager diff --cached --name-only)"
|
files="$(git --no-pager diff --cached --name-only --diff-filter=d)"
|
||||||
|
|
||||||
green="$(tput setf 2)"
|
|
||||||
red="$(tput setf 4)"
|
|
||||||
nc="$(tput sgr0)"
|
|
||||||
|
|
||||||
failures=0
|
failures=0
|
||||||
|
|
||||||
@@ -33,9 +48,9 @@ lint-shell-script.sh
|
|||||||
for linter in $linters; do
|
for linter in $linters; do
|
||||||
echo "Running $subDir/$linter"
|
echo "Running $subDir/$linter"
|
||||||
if "$subDir/$linter" "$files"; then
|
if "$subDir/$linter" "$files"; then
|
||||||
echo "[${green}PASS${nc}]: $subDir/$linter"
|
echo "[${green}PASS${n}]: $subDir/$linter"
|
||||||
else
|
else
|
||||||
echo "[${red}FAIL${nc}]: $subDir/$linter"
|
echo "[${red}FAIL${n}]: $subDir/$linter"
|
||||||
failures=$(( failures + 1 ))
|
failures=$(( failures + 1 ))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -43,9 +58,9 @@ done
|
|||||||
echo "Running $subDir/lint-clang-format.sh"
|
echo "Running $subDir/lint-clang-format.sh"
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
if "$subDir/lint-clang-format.sh" "$files" && git diff --exit-code $files; then
|
if "$subDir/lint-clang-format.sh" "$files" && git diff --exit-code $files; then
|
||||||
echo "[${green}PASS${nc}]: $subDir/lint-clang-format.sh"
|
echo "[${green}PASS${n}]: $subDir/lint-clang-format.sh"
|
||||||
else
|
else
|
||||||
echo "[${red}FAIL${nc}]: $subDir/lint-clang-format.sh"
|
echo "[${red}FAIL${n}]: $subDir/lint-clang-format.sh"
|
||||||
failures=$(( failures + 1 ))
|
failures=$(( failures + 1 ))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -5,13 +5,17 @@
|
|||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
b="$(tput bold)"
|
error() {
|
||||||
red="$(tput setf 4)"
|
b="$(tput bold)"
|
||||||
n="$(tput sgr0)"
|
red="$(tput setf 4)"
|
||||||
|
n="$(tput sgr0)"
|
||||||
|
|
||||||
|
echo "${b}${red}Error:${n} $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
if [ ! -d ".git" ]; then
|
if [ ! -d ".git" ]; then
|
||||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
error "please run this script from the project root"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
formatter=false
|
formatter=false
|
||||||
@@ -20,16 +24,14 @@ if command -v clang-format-11 >/dev/null 2>&1; then
|
|||||||
elif command -v clang-format >/dev/null 2>&1; then
|
elif command -v clang-format >/dev/null 2>&1; then
|
||||||
formatter="clang-format"
|
formatter="clang-format"
|
||||||
if ! "$formatter" --version | awk '{ if (substr($NF, 1, index($NF, ".") - 1) < 11) exit 1; }'; then
|
if ! "$formatter" --version | awk '{ if (substr($NF, 1, index($NF, ".") - 1) < 11) exit 1; }'; then
|
||||||
echo "You are using '$("${CLANG_FORMAT}" --version)', which appears to not be clang-format 11 or later."
|
error "you are using '$("${formatter}" --version)', which appears to not be clang-format 11 or later."
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "clang-format-11 is not available, but C++ files need linting!"
|
error "clang-format-11 is not available, but C++ files need linting!
|
||||||
echo "Either skip this script, or install clang-format-11."
|
Either skip this script, or install clang-format-11."
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
files="${1:-$(git --no-pager diff --cached --name-only)}"
|
files="${1:-$(git --no-pager diff --cached --name-only --diff-filter=d)}"
|
||||||
files="$(echo "$files" | grep -E '\.(cpp|h)$')"
|
files="$(echo "$files" | grep -E '\.(cpp|h)$')"
|
||||||
|
|
||||||
if [ -z "$files" ]; then
|
if [ -z "$files" ]; then
|
||||||
|
|||||||
@@ -5,22 +5,25 @@
|
|||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
b="$(tput bold)"
|
error() {
|
||||||
red="$(tput setf 4)"
|
b="$(tput bold)"
|
||||||
n="$(tput sgr0)"
|
red="$(tput setf 4)"
|
||||||
|
n="$(tput sgr0)"
|
||||||
|
|
||||||
|
echo "${b}${red}Error:${n} $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
if [ ! -d ".git" ]; then
|
if [ ! -d ".git" ]; then
|
||||||
echo "${b}${red}Error:${n} please run this script from the project root" >&2
|
error "please run this script from the project root"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v shellcheck > /dev/null 2>&1; then
|
if ! command -v shellcheck > /dev/null 2>&1; then
|
||||||
echo "shellcheck is not available, but shell script files need linting!"
|
error "shellcheck is not available, but shell-script files need linting!
|
||||||
echo "Either skip this script, or install shellcheck."
|
Either skip this script, or install shellcheck."
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
files="${1:-$(git --no-pager diff --cached --name-only)}"
|
files="${1:-$(git --no-pager diff --cached --name-only --diff-filter=d)}"
|
||||||
files="$(echo "$files" | grep -E '\.sh$')"
|
files="$(echo "$files" | grep -E '\.sh$')"
|
||||||
|
|
||||||
if [ -z "$files" ]; then
|
if [ -z "$files" ]; then
|
||||||
|
|||||||
@@ -259,6 +259,11 @@ bool ArgParser::parseArgument(std::string_view argument)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ArgParser::parse(int argc, const char* argv[])
|
bool ArgParser::parse(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
return parse(argc, const_cast<char**>(argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArgParser::parse(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool parse(int argc, const char* argv[]);
|
bool parse(int argc, const char* argv[]);
|
||||||
|
bool parse(int argc, char* argv[]);
|
||||||
|
|
||||||
void addOption(Option&& option);
|
void addOption(Option&& option);
|
||||||
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
|
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
|
||||||
|
|||||||
+44
-13
@@ -6,27 +6,25 @@
|
|||||||
|
|
||||||
#include <cstdint> // int32_t
|
#include <cstdint> // int32_t
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream> // ifstream, ios, ofstream
|
#include <fstream> // std::ifstream, std::ofstream
|
||||||
#include <memory> // make_unique
|
#include <memory> // std::make_shared, std::make_unique, std::shared_ptr
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "ruc/file.h"
|
#include "ruc/file.h"
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
|
|
||||||
namespace ruc {
|
namespace ruc {
|
||||||
|
|
||||||
File::File(const std::string& path)
|
File::File(std::string_view path)
|
||||||
: m_path(path)
|
: m_path(path)
|
||||||
{
|
{
|
||||||
// Create input stream object and open file
|
// Create input stream object and open file
|
||||||
std::ifstream file(path, std::ios::in);
|
std::ifstream file(path.data());
|
||||||
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
||||||
|
|
||||||
// Get length of the file
|
// Get length of the file
|
||||||
file.seekg(0, std::ios::end);
|
int32_t size = File::length(path, file);
|
||||||
int32_t size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
VERIFY(size != -1, "failed to read file length: '{}', path");
|
|
||||||
|
|
||||||
// Allocate memory filled with zeros
|
// Allocate memory filled with zeros
|
||||||
auto buffer = std::make_unique<char[]>(size);
|
auto buffer = std::make_unique<char[]>(size);
|
||||||
@@ -44,15 +42,48 @@ File::~File()
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
File File::create(const std::string& path)
|
File File::create(std::string_view path)
|
||||||
{
|
{
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
std::ofstream { path };
|
std::ofstream { path.data() };
|
||||||
}
|
}
|
||||||
|
|
||||||
return File(path);
|
return File(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t File::length(std::string_view path, std::ifstream& file)
|
||||||
|
{
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
int32_t length = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
VERIFY(length != -1, "failed to read file length: '{}'", path);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Deduplicate code with constructor, this broke binary files only
|
||||||
|
std::shared_ptr<char[]> File::raw(std::string_view path)
|
||||||
|
{
|
||||||
|
// Create input stream object and open file
|
||||||
|
std::ifstream file(path.data());
|
||||||
|
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
||||||
|
|
||||||
|
// Get length of the file
|
||||||
|
int32_t size = File::length(path, file);
|
||||||
|
|
||||||
|
// Allocate memory filled with zeros
|
||||||
|
auto buffer = std::make_shared<char[]>(size + 1);
|
||||||
|
|
||||||
|
// Fill buffer with file contents
|
||||||
|
file.read(buffer.get(), size);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Null termination
|
||||||
|
buffer[size] = '\0';
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
void File::clear()
|
void File::clear()
|
||||||
@@ -60,14 +91,14 @@ void File::clear()
|
|||||||
m_data.clear();
|
m_data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
File& File::append(const std::string& data)
|
File& File::append(std::string_view data)
|
||||||
{
|
{
|
||||||
m_data.append(data);
|
m_data.append(data);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
File& File::replace(size_t index, size_t length, const std::string& data)
|
File& File::replace(size_t index, size_t length, std::string_view data)
|
||||||
{
|
{
|
||||||
m_data.replace(index, length, data);
|
m_data.replace(index, length, data);
|
||||||
|
|
||||||
@@ -77,7 +108,7 @@ File& File::replace(size_t index, size_t length, const std::string& data)
|
|||||||
File& File::flush()
|
File& File::flush()
|
||||||
{
|
{
|
||||||
// Create output stream object and open file
|
// Create output stream object and open file
|
||||||
std::ofstream file(m_path, std::ios::out | std::ios::trunc);
|
std::ofstream file(m_path.data(), std::ios::out | std::ios::trunc);
|
||||||
VERIFY(file.is_open(), "failed to open file: '{}'", m_path);
|
VERIFY(file.is_open(), "failed to open file: '{}'", m_path);
|
||||||
|
|
||||||
// Write data to disk
|
// Write data to disk
|
||||||
|
|||||||
+11
-6
@@ -6,28 +6,33 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // int32_t
|
||||||
|
#include <memory> // std::shared_ptr
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace ruc {
|
namespace ruc {
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
public:
|
public:
|
||||||
File(const std::string& path);
|
File(std::string_view path);
|
||||||
virtual ~File();
|
virtual ~File();
|
||||||
|
|
||||||
static File create(const std::string& path);
|
static File create(std::string_view path);
|
||||||
|
static int32_t length(std::string_view path, std::ifstream& file);
|
||||||
|
static std::shared_ptr<char[]> raw(std::string_view path);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
File& append(const std::string& data);
|
File& append(std::string_view data);
|
||||||
File& replace(size_t index, size_t length, const std::string& data);
|
File& replace(size_t index, size_t length, std::string_view data);
|
||||||
File& flush();
|
File& flush();
|
||||||
|
|
||||||
const char* c_str() const { return m_data.c_str(); }
|
const char* c_str() const { return m_data.c_str(); }
|
||||||
const std::string& data() const { return m_data; }
|
const std::string& data() const { return m_data; }
|
||||||
const std::string& path() const { return m_path; }
|
std::string_view path() const { return m_path; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_path;
|
std::string_view m_path;
|
||||||
std::string m_data;
|
std::string m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm> // min
|
#include <algorithm> // min
|
||||||
|
#include <charconv> // std::to_chars
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||||
#include <iomanip> // setprecision
|
#include <iomanip> // setprecision
|
||||||
@@ -178,15 +179,53 @@ void Builder::putI64(int64_t value,
|
|||||||
|
|
||||||
void Builder::putF64(double number, uint8_t precision) const
|
void Builder::putF64(double number, uint8_t precision) const
|
||||||
{
|
{
|
||||||
precision = std::min(precision, static_cast<uint8_t>(std::numeric_limits<double>::digits10));
|
char buffer[512];
|
||||||
|
auto conversion = std::to_chars(buffer, buffer + sizeof(buffer), number);
|
||||||
|
auto converted = std::string(buffer, conversion.ptr - buffer);
|
||||||
|
|
||||||
std::stringstream stream;
|
size_t dot = converted.find('.');
|
||||||
stream
|
size_t length = dot + precision + 1;
|
||||||
<< std::fixed << std::setprecision(precision)
|
|
||||||
<< number
|
// There is no number behind the decimal point
|
||||||
<< std::defaultfloat << std::setprecision(6);
|
if (dot == std::string::npos) {
|
||||||
std::string string = stream.str();
|
if (precision > 0) {
|
||||||
m_builder << string;
|
converted += '.' + std::string(precision, '0');
|
||||||
|
}
|
||||||
|
m_builder << converted;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are less numbers behind the decimal point than the precision
|
||||||
|
if (converted.length() < length) {
|
||||||
|
converted += std::string(length - converted.length(), '0');
|
||||||
|
m_builder << converted;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only round if there are more numbers behind the decimal point than the precision,
|
||||||
|
// or the number that comes after the maximum precision is higher than 4
|
||||||
|
if (converted.length() > length && converted[length] > '4') {
|
||||||
|
for (size_t i = length - 1; i >= 0 && i < converted.length(); --i) {
|
||||||
|
if (converted[i] == '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (converted[i] < '9') { // Overflow stops here
|
||||||
|
converted[i]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
converted[i] = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut off the characters after the requested precision
|
||||||
|
if (converted.length() > length) {
|
||||||
|
size_t last_included_number = converted.find_last_not_of("0", length - 1);
|
||||||
|
// If precision is zero, also cut off the '.', otherwise include the '0' after the '.'
|
||||||
|
size_t last_character_is_dot_offset = (converted[last_included_number] == '.') ? (precision > 0 ? 1 : -1) : 0;
|
||||||
|
converted = converted.substr(0, last_included_number + last_character_is_dot_offset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_builder << converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
|
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ void Formatter<const char*>::format(Builder& builder, const char* value) const
|
|||||||
value != nullptr ? std::string_view { value, strlen(value) } : "nullptr");
|
value != nullptr ? std::string_view { value, strlen(value) } : "nullptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Formatter<const unsigned char*>::format(Builder& builder, const unsigned char* value) const
|
||||||
|
{
|
||||||
|
return Formatter<const char*>::format(builder, reinterpret_cast<const char*>(value));
|
||||||
|
}
|
||||||
|
|
||||||
// Pointer
|
// Pointer
|
||||||
|
|
||||||
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
|
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
|
||||||
|
|||||||
@@ -108,9 +108,11 @@ struct Formatter<T> {
|
|||||||
uint8_t base = 0;
|
uint8_t base = 0;
|
||||||
bool uppercase = false;
|
bool uppercase = false;
|
||||||
switch (specifier.type) {
|
switch (specifier.type) {
|
||||||
|
case PresentationType::Binary:
|
||||||
|
base = 2;
|
||||||
|
break;
|
||||||
case PresentationType::BinaryUppercase:
|
case PresentationType::BinaryUppercase:
|
||||||
uppercase = true;
|
uppercase = true;
|
||||||
case PresentationType::Binary:
|
|
||||||
base = 2;
|
base = 2;
|
||||||
break;
|
break;
|
||||||
case PresentationType::Octal:
|
case PresentationType::Octal:
|
||||||
@@ -120,9 +122,11 @@ struct Formatter<T> {
|
|||||||
case PresentationType::Decimal:
|
case PresentationType::Decimal:
|
||||||
base = 10;
|
base = 10;
|
||||||
break;
|
break;
|
||||||
|
case PresentationType::Hex:
|
||||||
|
base = 16;
|
||||||
|
break;
|
||||||
case PresentationType::HexUppercase:
|
case PresentationType::HexUppercase:
|
||||||
uppercase = true;
|
uppercase = true;
|
||||||
case PresentationType::Hex:
|
|
||||||
base = 16;
|
base = 16;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -195,6 +199,19 @@ template<size_t N>
|
|||||||
struct Formatter<char[N]> : Formatter<const char*> {
|
struct Formatter<char[N]> : Formatter<const char*> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Formatter<const unsigned char*> : Formatter<const char*> {
|
||||||
|
void format(Builder& builder, const unsigned char* value) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Formatter<unsigned char*> : Formatter<const unsigned char*> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
struct Formatter<unsigned char[N]> : Formatter<const unsigned char*> {
|
||||||
|
};
|
||||||
|
|
||||||
// Pointer
|
// Pointer
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -314,6 +331,8 @@ struct Formatter<Specifier> : Formatter<std::nullptr_t> {
|
|||||||
|
|
||||||
} // namespace ruc::format
|
} // namespace ruc::format
|
||||||
|
|
||||||
|
using ruc::format::Formatter;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ private:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
const LogOperatorStyle& operator<<(const LogOperatorStyle& logOperatorStyle, const T& value)
|
const LogOperatorStyle& operator<<(const LogOperatorStyle& logOperatorStyle, const T& value)
|
||||||
{
|
{
|
||||||
_format(const_cast<LogOperatorStyle&>(logOperatorStyle).builder(), value);
|
Formatter<T> formatter;
|
||||||
|
formatter.format(const_cast<LogOperatorStyle&>(logOperatorStyle).builder(), value);
|
||||||
return logOperatorStyle;
|
return logOperatorStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -411,6 +411,8 @@ constexpr void Parser::checkSpecifierType(const Specifier& specifier, ParameterT
|
|||||||
case ParameterType::Container:
|
case ParameterType::Container:
|
||||||
checkSpecifierContainerType(specifier);
|
checkSpecifierContainerType(specifier);
|
||||||
break;
|
break;
|
||||||
|
case ParameterType::UserDefined:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
String,
|
String,
|
||||||
Pointer,
|
Pointer,
|
||||||
Container,
|
Container,
|
||||||
|
UserDefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser(std::string_view format, size_t parameterCount);
|
Parser(std::string_view format, size_t parameterCount);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstdio> // FILE, fputs, stdout, stderr
|
#include <cstdio> // FILE, fflush, fputs, stdout, stderr
|
||||||
#include <iomanip> // setprecision
|
#include <iomanip> // setprecision
|
||||||
#include <ios> // defaultfloat, fixed
|
#include <ios> // defaultfloat, fixed
|
||||||
#include <sstream> // stringstream
|
#include <sstream> // stringstream
|
||||||
@@ -22,6 +22,7 @@ void variadicPrint(FILE* file, std::string_view format, TypeErasedParameters& pa
|
|||||||
|
|
||||||
std::string string = stream.str();
|
std::string string = stream.str();
|
||||||
fputs(string.c_str(), file);
|
fputs(string.c_str(), file);
|
||||||
|
fflush(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // transform
|
#include <algorithm> // transform
|
||||||
#include <cstddef> // nullptr_t
|
#include <cstddef> // nullptr_t, size_t
|
||||||
|
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#include "ruc/json/array.h"
|
#include "ruc/json/array.h"
|
||||||
#include "ruc/json/object.h"
|
#include "ruc/json/object.h"
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
|
#include "ruc/meta/concepts.h"
|
||||||
#include "ruc/meta/odr.h"
|
#include "ruc/meta/odr.h"
|
||||||
|
|
||||||
namespace ruc::json {
|
namespace ruc::json {
|
||||||
@@ -44,15 +46,15 @@ void fromJson(const Json& json, bool& boolean)
|
|||||||
boolean = json.asBool();
|
boolean = json.asBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Json>
|
template<typename Json, Integral T>
|
||||||
void fromJson(const Json& json, int& number)
|
void fromJson(const Json& json, T& number)
|
||||||
{
|
{
|
||||||
VERIFY(json.type() == Json::Type::Number);
|
VERIFY(json.type() == Json::Type::Number);
|
||||||
number = (int)json.asDouble();
|
number = static_cast<T>(json.asDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Json>
|
template<typename Json, FloatingPoint T>
|
||||||
void fromJson(const Json& json, double& number)
|
void fromJson(const Json& json, T& number)
|
||||||
{
|
{
|
||||||
VERIFY(json.type() == Json::Type::Number);
|
VERIFY(json.type() == Json::Type::Number);
|
||||||
number = json.asDouble();
|
number = json.asDouble();
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ Value Parser::consumeString()
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
char buffer[7];
|
char buffer[7];
|
||||||
sprintf(buffer, "\\u%0.4X", character);
|
sprintf(buffer, "\\u%.4X", character);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cassert> // assert
|
#include <cstddef> // nullptr_t, size_t
|
||||||
#include <cstddef> // nullptr_t
|
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "ruc/json/array.h"
|
#include "ruc/json/array.h"
|
||||||
#include "ruc/json/object.h"
|
#include "ruc/json/object.h"
|
||||||
|
#include "ruc/meta/concepts.h"
|
||||||
#include "ruc/meta/odr.h"
|
#include "ruc/meta/odr.h"
|
||||||
|
|
||||||
namespace ruc::json {
|
namespace ruc::json {
|
||||||
@@ -30,20 +31,20 @@ struct jsonConstructor {
|
|||||||
json.m_value.boolean = boolean;
|
json.m_value.boolean = boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Json>
|
template<typename Json, Integral T>
|
||||||
static void construct(Json& json, int number)
|
static void construct(Json& json, T number)
|
||||||
{
|
{
|
||||||
json.destroy();
|
json.destroy();
|
||||||
json.m_type = Json::Type::Number;
|
json.m_type = Json::Type::Number;
|
||||||
json.m_value.number = (double)number;
|
json.m_value.number = static_cast<double>(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Json>
|
template<typename Json, FloatingPoint T>
|
||||||
static void construct(Json& json, double number)
|
static void construct(Json& json, T number)
|
||||||
{
|
{
|
||||||
json.destroy();
|
json.destroy();
|
||||||
json.m_type = Json::Type::Number;
|
json.m_type = Json::Type::Number;
|
||||||
json.m_value.number = number;
|
json.m_value.number = static_cast<double>(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Json>
|
template<typename Json>
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ inline void __assertion_failed(const char* assertion, const char* file, uint32_t
|
|||||||
fprintf(stderr, ": ");
|
fprintf(stderr, ": ");
|
||||||
// Cant use the formatting library to print asserts caused by the formatting library
|
// Cant use the formatting library to print asserts caused by the formatting library
|
||||||
std::string_view functionString = function;
|
std::string_view functionString = function;
|
||||||
if (functionString.find("ruc::format::") != std::string_view::npos
|
if (functionString.find("ruc::format::") == std::string_view::npos
|
||||||
&& functionString.find("ruc::GenericLexer::") != std::string_view::npos) {
|
&& functionString.find("ruc::GenericLexer::") == std::string_view::npos) {
|
||||||
std::string message;
|
std::string message;
|
||||||
formatTo(message, parameters...);
|
formatTo(message, parameters...);
|
||||||
fprintf(stderr, "%s", message.c_str());
|
fprintf(stderr, "%s", message.c_str());
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define BIT(x) (1 << x)
|
||||||
|
|
||||||
|
#define CONCAT_IMPL(a, b) a##b
|
||||||
|
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
inline bool is(U& input)
|
||||||
|
{
|
||||||
|
if constexpr (requires { input.template fastIs<T>(); }) {
|
||||||
|
return input.template fastIs<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeid(input) == typeid(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
inline bool is(U* input)
|
||||||
|
{
|
||||||
|
return input && is<T>(*input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// References:
|
||||||
|
// - serenity/AK/TypeCasts.h
|
||||||
|
// - serenity/Userland/Libraries/LibJS/AST.h
|
||||||
+14
-4
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cassert>
|
#include <cstdlib> // free, malloc
|
||||||
|
|
||||||
|
#include "meta/assert.h"
|
||||||
|
|
||||||
namespace ruc {
|
namespace ruc {
|
||||||
|
|
||||||
@@ -16,7 +18,10 @@ public:
|
|||||||
static inline T& the()
|
static inline T& the()
|
||||||
{
|
{
|
||||||
if (s_instance == nullptr) {
|
if (s_instance == nullptr) {
|
||||||
s_instance = new T { s {} };
|
// Allocate memory for this instance
|
||||||
|
s_instance = static_cast<T*>(malloc(sizeof(T)));
|
||||||
|
// Call the constructor using "placement new"
|
||||||
|
new (s_instance) T { s {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
return *s_instance;
|
return *s_instance;
|
||||||
@@ -24,10 +29,15 @@ public:
|
|||||||
|
|
||||||
static inline void destroy()
|
static inline void destroy()
|
||||||
{
|
{
|
||||||
if (s_instance) {
|
if (!s_instance) {
|
||||||
delete s_instance;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call the destructor
|
||||||
|
s_instance->~T();
|
||||||
|
// Deallocate memory
|
||||||
|
free(static_cast<void*>(s_instance));
|
||||||
|
|
||||||
s_instance = nullptr;
|
s_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm> // std::sort, std::unique
|
||||||
#include <cerrno> // errno, EAGAIN, EINTR
|
#include <cerrno> // errno, EAGAIN, EINTR
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdio> // perror, ssize_t
|
#include <cstdio> // perror, ssize_t
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ TEST_CASE(FormatIntegral)
|
|||||||
result = format("{}", u32);
|
result = format("{}", u32);
|
||||||
EXPECT_EQ(result, "4294967295");
|
EXPECT_EQ(result, "4294967295");
|
||||||
|
|
||||||
size_t u64 = 18446744073709551615; // long unsigned int
|
size_t u64 = 18446744073709551615u; // long unsigned int
|
||||||
result = format("{}", u64);
|
result = format("{}", u64);
|
||||||
EXPECT_EQ(result, "18446744073709551615");
|
EXPECT_EQ(result, "18446744073709551615");
|
||||||
}
|
}
|
||||||
@@ -109,6 +109,14 @@ TEST_CASE(FormatString)
|
|||||||
result = format("{}", cString);
|
result = format("{}", cString);
|
||||||
EXPECT_EQ(result, "C string");
|
EXPECT_EQ(result, "C string");
|
||||||
|
|
||||||
|
const unsigned char unsignedCharArray[] = "µnsïgned çhãr";
|
||||||
|
result = format("{}", unsignedCharArray);
|
||||||
|
EXPECT_EQ(result, "µnsïgned çhãr");
|
||||||
|
|
||||||
|
const unsigned char* unsignedCharPointer = &unsignedCharArray[0];
|
||||||
|
result = format("{}", unsignedCharPointer);
|
||||||
|
EXPECT_EQ(result, "µnsïgned çhãr");
|
||||||
|
|
||||||
std::string string = "string";
|
std::string string = "string";
|
||||||
result = format("{}", string);
|
result = format("{}", string);
|
||||||
EXPECT_EQ(result, "string");
|
EXPECT_EQ(result, "string");
|
||||||
@@ -513,5 +521,5 @@ TEST_CASE(FormatContainers)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// lsp-in-cpp-project-cache: nil
|
// lsp-in-cpp-project-cache: (nil)
|
||||||
// End:
|
// End:
|
||||||
|
|||||||
Reference in New Issue
Block a user