Compare commits

..
16 Commits
Author SHA1 Message Date
Riyyi 243442a3b6 Format: Fill float printing with '0's until the precision 2024-09-07 16:57:18 +02:00
Riyyi d5bf50715e System: Add missing include 2024-08-25 13:21:03 +02:00
Riyyi 8520952235 Meta: Add type compare is() function 2024-04-29 13:31:38 +02:00
Riyyi 526948874a Macro: Add string concat macro function 2023-12-03 20:13:58 +01:00
Riyyi c8e4ae884e Format: Improve the double formatting implementation 2023-11-26 10:33:56 +01:00
Riyyi 07c9f9959d Util: Fix singleton resource cleanup 2022-10-05 12:39:29 +02:00
Riyyi 9000d1d968 CMake: Populate list of required include directories for library targets
PUBLIC items will populate the INTERFACE_INCLUDE_DIRECTORIES property of
<target>. This property is a list of public include directories
requirements for a library.

Targets may populate this property to publish the include directories
required to compile against the headers for the target.
2022-09-28 14:32:23 +02:00
Riyyi 50fe09ee56 Util: Fix reading binary files 2022-09-26 02:40:32 +02:00
Riyyi 6cd2fe4e9a Util: Add ArgParser::parse overload for C legacy 2022-09-25 22:41:35 +02:00
Riyyi 703be90792 Util: Revert allow setting instance pointer manually
This is unnecessary as the allocation + placement new works.
2022-09-25 21:40:21 +02:00
Riyyi da9fb1ed0f Util: Allow setting instance pointer manually, ex: in constructor 2022-09-25 20:51:33 +02:00
Riyyi d6f378b6bc Json: Add support for float, deduplicate code 2022-09-25 01:45:23 +02:00
Riyyi e592ee7fd4 Util: Add length() and raw() functions to File class 2022-09-23 01:00:01 +02:00
Riyyi 8e2e1a13df Format: Add support for unsigned char 2022-09-22 14:26:13 +02:00
Riyyi afdd0f8b45 Format: Fix left shift operator log messages 2022-09-22 11:50:53 +02:00
Riyyi 93af096d68 Util: Set singleton instance pointer early 2022-09-21 23:20:19 +02:00
16 changed files with 183 additions and 81 deletions
+3 -7
View File
@@ -61,7 +61,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
file(GLOB_RECURSE LIBRARY_SOURCES "src/*.cpp")
add_library(${PROJECT} ${LIBRARY_SOURCES})
target_include_directories(${PROJECT} PRIVATE
target_include_directories(${PROJECT} PUBLIC
"src")
# ------------------------------------------
@@ -71,7 +71,7 @@ target_include_directories(${PROJECT} PRIVATE
file(GLOB TEST_LIBRARY_SOURCES "test/*.cpp" "src/ruc/timer.cpp")
add_library(${PROJECT}-test ${TEST_LIBRARY_SOURCES})
target_include_directories(${PROJECT}-test PRIVATE
target_include_directories(${PROJECT}-test PUBLIC
"src"
"test")
@@ -83,9 +83,5 @@ if (RUC_BUILD_TESTS)
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)
target_link_libraries(${PROJECT}-unit-test ruc-test)
target_link_libraries(${PROJECT}-unit-test ruc ruc-test)
endif()
+5
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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()
+4
View File
@@ -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);
+47 -8
View File
@@ -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
+5
View File
@@ -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
+13
View File
@@ -199,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>
+2 -1
View File
@@ -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;
}
+6 -26
View File
@@ -18,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 {
@@ -45,36 +46,15 @@ void fromJson(const Json& json, bool& boolean)
boolean = json.asBool();
}
template<typename Json>
void fromJson(const Json& json, int32_t& number)
template<typename Json, Integral T>
void fromJson(const Json& json, T& number)
{
VERIFY(json.type() == Json::Type::Number);
number = static_cast<int32_t>(json.asDouble());
number = static_cast<T>(json.asDouble());
}
template<typename Json>
void fromJson(const Json& json, uint32_t& number)
{
VERIFY(json.type() == Json::Type::Number);
number = static_cast<uint32_t>(json.asDouble());
}
template<typename Json>
void fromJson(const Json& json, int64_t& number)
{
VERIFY(json.type() == Json::Type::Number);
number = static_cast<int64_t>(json.asDouble());
}
template<typename Json>
void fromJson(const Json& json, size_t& number) // uint64_t
{
VERIFY(json.type() == Json::Type::Number);
number = static_cast<size_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();
+5 -28
View File
@@ -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,46 +31,22 @@ struct jsonConstructor {
json.m_value.boolean = boolean;
}
template<typename Json>
static void construct(Json& json, int32_t 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 = static_cast<double>(number);
}
template<typename Json>
static void construct(Json& json, int64_t 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 = static_cast<double>(number);
}
template<typename Json>
static void construct(Json& json, uint32_t number)
{
json.destroy();
json.m_type = Json::Type::Number;
json.m_value.number = static_cast<double>(number);
}
template<typename Json>
static void construct(Json& json, size_t number)
{
json.destroy();
json.m_type = Json::Type::Number;
json.m_value.number = static_cast<double>(number);
}
template<typename Json>
static void construct(Json& json, double number)
{
json.destroy();
json.m_type = Json::Type::Number;
json.m_value.number = number;
}
template<typename Json>
static void construct(Json& json, const char* string)
{
+3
View File
@@ -7,3 +7,6 @@
#pragma once
#define BIT(x) (1 << x)
#define CONCAT_IMPL(a, b) a##b
#define CONCAT(a, b) CONCAT_IMPL(a, b)
+29
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,4 @@
#include <algorithm> // std::sort, std::unique
#include <cerrno> // errno, EAGAIN, EINTR
#include <cstddef> // size_t
#include <cstdio> // perror, ssize_t
+8
View File
@@ -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");