Compare commits
26
Commits
27489c6cb4
...
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 |
+13
-11
@@ -4,8 +4,13 @@
|
||||
# Set project name
|
||||
set(PROJECT "ruc")
|
||||
|
||||
# Unit tests
|
||||
option(RUC_BUILD_TESTS "Build the RUC test programs" ON)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
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
|
||||
file(GLOB_RECURSE LIBRARY_SOURCES "src/*.cpp")
|
||||
|
||||
add_library(${PROJECT} STATIC ${LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT} PRIVATE
|
||||
add_library(${PROJECT} ${LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
"src")
|
||||
|
||||
# ------------------------------------------
|
||||
@@ -65,8 +70,8 @@ target_include_directories(${PROJECT} PRIVATE
|
||||
# Define source files
|
||||
file(GLOB TEST_LIBRARY_SOURCES "test/*.cpp" "src/ruc/timer.cpp")
|
||||
|
||||
add_library(${PROJECT}-test STATIC ${TEST_LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT}-test PRIVATE
|
||||
add_library(${PROJECT}-test ${TEST_LIBRARY_SOURCES})
|
||||
target_include_directories(${PROJECT}-test PUBLIC
|
||||
"src"
|
||||
"test")
|
||||
|
||||
@@ -75,11 +80,8 @@ target_include_directories(${PROJECT}-test PRIVATE
|
||||
|
||||
if (RUC_BUILD_TESTS)
|
||||
# Define test source files
|
||||
file(GLOB_RECURSE TEST_SOURCES "test/*.cpp")
|
||||
set(TEST_SOURCES ${TEST_SOURCES} ${LIBRARY_SOURCES})
|
||||
file(GLOB_RECURSE TEST_SOURCES "test/unit/*.cpp")
|
||||
|
||||
add_executable(${PROJECT}-unit-test ${TEST_SOURCES})
|
||||
target_include_directories(${PROJECT}-unit-test PRIVATE
|
||||
"src"
|
||||
"test")
|
||||
target_link_libraries(${PROJECT}-unit-test ruc ruc-test)
|
||||
endif()
|
||||
|
||||
@@ -9,3 +9,9 @@ Utility library for C++.
|
||||
- Formatting library
|
||||
- JSON parsing
|
||||
- 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]].
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ fi
|
||||
subDir="$(dirname -- "$0")"
|
||||
|
||||
# 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)"
|
||||
|
||||
failures=0
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ else
|
||||
Either skip this script, or install clang-format-11."
|
||||
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)$')"
|
||||
|
||||
if [ -z "$files" ]; then
|
||||
|
||||
@@ -23,7 +23,7 @@ if ! command -v shellcheck > /dev/null 2>&1; then
|
||||
Either skip this script, or install shellcheck."
|
||||
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$')"
|
||||
|
||||
if [ -z "$files" ]; then
|
||||
|
||||
@@ -259,6 +259,11 @@ bool ArgParser::parseArgument(std::string_view argument)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
};
|
||||
|
||||
bool parse(int argc, const char* argv[]);
|
||||
bool parse(int argc, char* argv[]);
|
||||
|
||||
void addOption(Option&& option);
|
||||
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
|
||||
|
||||
+37
-7
@@ -6,8 +6,8 @@
|
||||
|
||||
#include <cstdint> // int32_t
|
||||
#include <filesystem>
|
||||
#include <fstream> // ifstream, ios, ofstream
|
||||
#include <memory> // make_unique
|
||||
#include <fstream> // std::ifstream, std::ofstream
|
||||
#include <memory> // std::make_shared, std::make_unique, std::shared_ptr
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -20,14 +20,11 @@ File::File(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
// Create input stream object and open file
|
||||
std::ifstream file(path.data(), std::ios::in);
|
||||
std::ifstream file(path.data());
|
||||
VERIFY(file.is_open(), "failed to open file: '{}'", path);
|
||||
|
||||
// Get length of the file
|
||||
file.seekg(0, std::ios::end);
|
||||
int32_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
VERIFY(size != -1, "failed to read file length: '{}'", path);
|
||||
int32_t size = File::length(path, file);
|
||||
|
||||
// Allocate memory filled with zeros
|
||||
auto buffer = std::make_unique<char[]>(size);
|
||||
@@ -54,6 +51,39 @@ File File::create(std::string_view 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()
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // int32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -17,6 +19,8 @@ public:
|
||||
virtual ~File();
|
||||
|
||||
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();
|
||||
File& append(std::string_view data);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <charconv> // std::to_chars
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||
#include <iomanip> // setprecision
|
||||
@@ -178,15 +179,53 @@ void Builder::putI64(int64_t value,
|
||||
|
||||
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;
|
||||
stream
|
||||
<< std::fixed << std::setprecision(precision)
|
||||
<< number
|
||||
<< std::defaultfloat << std::setprecision(6);
|
||||
std::string string = stream.str();
|
||||
m_builder << string;
|
||||
size_t dot = converted.find('.');
|
||||
size_t length = dot + precision + 1;
|
||||
|
||||
// There is no number behind the decimal point
|
||||
if (dot == std::string::npos) {
|
||||
if (precision > 0) {
|
||||
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
|
||||
|
||||
@@ -81,6 +81,11 @@ void Formatter<const char*>::format(Builder& builder, const char* value) const
|
||||
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
|
||||
|
||||
void Formatter<std::nullptr_t>::format(Builder& builder, std::nullptr_t) const
|
||||
|
||||
@@ -108,9 +108,11 @@ struct Formatter<T> {
|
||||
uint8_t base = 0;
|
||||
bool uppercase = false;
|
||||
switch (specifier.type) {
|
||||
case PresentationType::Binary:
|
||||
base = 2;
|
||||
break;
|
||||
case PresentationType::BinaryUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Binary:
|
||||
base = 2;
|
||||
break;
|
||||
case PresentationType::Octal:
|
||||
@@ -120,9 +122,11 @@ struct Formatter<T> {
|
||||
case PresentationType::Decimal:
|
||||
base = 10;
|
||||
break;
|
||||
case PresentationType::Hex:
|
||||
base = 16;
|
||||
break;
|
||||
case PresentationType::HexUppercase:
|
||||
uppercase = true;
|
||||
case PresentationType::Hex:
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
@@ -195,6 +199,19 @@ template<size_t N>
|
||||
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
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -70,7 +70,8 @@ private:
|
||||
template<typename T>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // transform
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <cstddef> // nullptr_t, size_t
|
||||
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "ruc/json/array.h"
|
||||
#include "ruc/json/object.h"
|
||||
#include "ruc/meta/assert.h"
|
||||
#include "ruc/meta/concepts.h"
|
||||
#include "ruc/meta/odr.h"
|
||||
|
||||
namespace ruc::json {
|
||||
@@ -44,15 +46,15 @@ void fromJson(const Json& json, bool& boolean)
|
||||
boolean = json.asBool();
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
void fromJson(const Json& json, int& number)
|
||||
template<typename Json, Integral T>
|
||||
void fromJson(const Json& json, T& number)
|
||||
{
|
||||
VERIFY(json.type() == Json::Type::Number);
|
||||
number = (int)json.asDouble();
|
||||
number = static_cast<T>(json.asDouble());
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
void fromJson(const Json& json, double& number)
|
||||
template<typename Json, FloatingPoint T>
|
||||
void fromJson(const Json& json, T& number)
|
||||
{
|
||||
VERIFY(json.type() == Json::Type::Number);
|
||||
number = json.asDouble();
|
||||
|
||||
@@ -266,7 +266,7 @@ Value Parser::consumeString()
|
||||
break;
|
||||
default:
|
||||
char buffer[7];
|
||||
sprintf(buffer, "\\u%0.4X", character);
|
||||
sprintf(buffer, "\\u%.4X", character);
|
||||
return std::string(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert> // assert
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <cstddef> // nullptr_t, size_t
|
||||
#include <cstdint> // int32_t, int64_t, uint32_t
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "ruc/json/array.h"
|
||||
#include "ruc/json/object.h"
|
||||
#include "ruc/meta/concepts.h"
|
||||
#include "ruc/meta/odr.h"
|
||||
|
||||
namespace ruc::json {
|
||||
@@ -30,20 +31,20 @@ struct jsonConstructor {
|
||||
json.m_value.boolean = boolean;
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
static void construct(Json& json, int number)
|
||||
template<typename Json, Integral T>
|
||||
static void construct(Json& json, T number)
|
||||
{
|
||||
json.destroy();
|
||||
json.m_type = Json::Type::Number;
|
||||
json.m_value.number = (double)number;
|
||||
json.m_value.number = static_cast<double>(number);
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
static void construct(Json& json, double number)
|
||||
template<typename Json, FloatingPoint T>
|
||||
static void construct(Json& json, T number)
|
||||
{
|
||||
json.destroy();
|
||||
json.m_type = Json::Type::Number;
|
||||
json.m_value.number = number;
|
||||
json.m_value.number = static_cast<double>(number);
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib> // free, malloc
|
||||
|
||||
#include "meta/assert.h"
|
||||
|
||||
namespace ruc {
|
||||
|
||||
@@ -16,7 +18,10 @@ public:
|
||||
static inline T& the()
|
||||
{
|
||||
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;
|
||||
@@ -24,10 +29,15 @@ public:
|
||||
|
||||
static inline void destroy()
|
||||
{
|
||||
if (s_instance) {
|
||||
delete s_instance;
|
||||
if (!s_instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the destructor
|
||||
s_instance->~T();
|
||||
// Deallocate memory
|
||||
free(static_cast<void*>(s_instance));
|
||||
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm> // std::sort, std::unique
|
||||
#include <cerrno> // errno, EAGAIN, EINTR
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdio> // perror, ssize_t
|
||||
|
||||
@@ -55,7 +55,7 @@ TEST_CASE(FormatIntegral)
|
||||
result = format("{}", u32);
|
||||
EXPECT_EQ(result, "4294967295");
|
||||
|
||||
size_t u64 = 18446744073709551615; // long unsigned int
|
||||
size_t u64 = 18446744073709551615u; // long unsigned int
|
||||
result = format("{}", u64);
|
||||
EXPECT_EQ(result, "18446744073709551615");
|
||||
}
|
||||
@@ -109,6 +109,14 @@ TEST_CASE(FormatString)
|
||||
result = format("{}", cString);
|
||||
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";
|
||||
result = format("{}", string);
|
||||
EXPECT_EQ(result, "string");
|
||||
|
||||
Reference in New Issue
Block a user