From 8249b28bea29e8ce17eac12776a60ec3f9e47176 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Thu, 17 Oct 2024 19:42:29 -0500 Subject: Rename InvalidInput to ArgumentInvalid. --- CMakeLists.txt | 3 +- cmake/warnings.cmake | 5 +- include/fud_allocator.hpp | 11 ++-- include/fud_assert.hpp | 13 +++++ include/fud_c_file.hpp | 6 +-- include/fud_result.hpp | 55 +++++++++++-------- include/fud_span.hpp | 120 ++++++++++++++++++++++++++++++++++++++++++ include/fud_status.hpp | 9 ++-- include/fud_string.hpp | 3 +- include/libfud.hpp | 8 +-- source/fud_allocator.cpp | 8 +-- source/fud_assert.cpp | 21 ++++---- source/fud_c_file.cpp | 8 +-- source/fud_directory.cpp | 2 +- source/fud_memory.cpp | 10 ++-- source/fud_sqlite.cpp | 4 +- source/fud_string.cpp | 131 ++++++++++++++++++++++------------------------ source/fud_utf8.cpp | 2 +- source/libfud.cpp | 9 +--- test/CMakeLists.txt | 6 ++- test/test_allocator.cpp | 54 +++++++++++++++++++ test/test_assert.cpp | 2 +- test/test_common.cpp | 29 ++++++++-- test/test_common.hpp | 28 ++++++++-- test/test_directory.cpp | 6 +-- test/test_result.cpp | 4 +- test/test_span.cpp | 52 ++++++++++++++++++ tools/coverage.sh | 9 +++- 28 files changed, 456 insertions(+), 162 deletions(-) create mode 100644 include/fud_span.hpp create mode 100644 test/test_allocator.cpp create mode 100644 test/test_span.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0644d2d..7358151 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ add_library(fud SHARED ) include(cmake/warnings.cmake) -target_compile_options(fud PRIVATE "${FUD_WARNINGS}") +target_compile_options(fud PRIVATE "${FUD_WARNINGS}" -fno-rtti -fno-exceptions) target_include_directories(fud PUBLIC include ${SQLite3_INCLUDE_DIRS}) target_include_directories(fud PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) @@ -97,6 +97,7 @@ set(FUD_HEADERS "include/fud_memory.hpp" "include/fud_permissions.hpp" "include/fud_result.hpp" + "include/fud_span.hpp" "include/fud_status.hpp" "include/fud_string.hpp" "include/fud_string_view.hpp" diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake index cd3cdf9..da2c9ab 100644 --- a/cmake/warnings.cmake +++ b/cmake/warnings.cmake @@ -3,8 +3,9 @@ set(FUD_WARNINGS -Werror -Wall -Wextra - -pedantic - -pedantic-errors + # -pedantic + -Wno-error=pedantic + # -pedantic-errors -Wstack-usage=2048 # GCC specific -Wvla # variable modified types don't play nice in C++ # types diff --git a/include/fud_allocator.hpp b/include/fud_allocator.hpp index 8955fae..a9fbd67 100644 --- a/include/fud_allocator.hpp +++ b/include/fud_allocator.hpp @@ -32,8 +32,7 @@ class Allocator { virtual Result allocate(size_t bytes, size_t alignment = alignof(std::max_align_t)) = 0; - /* ...should this be void? */ - virtual void deallocate(void* pointer, size_t bytes, size_t alignment = alignof(std::max_align_t)) = 0; + virtual FudStatus deallocate(void* pointer, size_t bytes) = 0; virtual bool isEqual(const Allocator& rhs) const = 0; }; @@ -48,8 +47,7 @@ class FudAllocator : public Allocator { virtual Result allocate(size_t bytes, size_t alignment = alignof(std::max_align_t)) override; - /* ...should this be void? */ - virtual void deallocate(void* pointer, size_t bytes, size_t alignment = alignof(std::max_align_t)) override; + virtual FudStatus deallocate(void* pointer, size_t bytes) override; virtual bool isEqual(const Allocator& rhs) const override; }; @@ -59,12 +57,9 @@ extern FudAllocator globalFudAllocator; /** \brief The default allocation function for globalFudAllocator. */ extern void* fudAlloc(size_t size); -/** \brief The default allocation function for globalFudAllocator. */ -extern void* fudRealloc(void* ptr, size_t size); - +/** \brief The default deallocation function for globalFudAllocator. */ extern void fudFree(void* ptr); - } // namespace fud #endif diff --git a/include/fud_assert.hpp b/include/fud_assert.hpp index 6b21fdc..ecfaa2f 100644 --- a/include/fud_assert.hpp +++ b/include/fud_assert.hpp @@ -22,9 +22,22 @@ // #include #include +#include "fud_span.hpp" namespace fud { +constexpr std::size_t MAX_FILE_CHARS = 256; +constexpr std::size_t MAX_FUNCTION_CHARS = 256; +constexpr std::size_t BITS_PER_OCTAL = 3; +constexpr auto MAX_LINE_CHARS = BITS_PER_OCTAL * sizeof(decltype(std::source_location{}.line())) + 3; +constexpr std::size_t MAX_ASSERT_CHARS = 512 - MAX_LINE_CHARS; +constexpr std::size_t ASSERT_MSG_SIZE = MAX_FILE_CHARS + MAX_LINE_CHARS + MAX_FUNCTION_CHARS + MAX_ASSERT_CHARS; + +void assertFormat( + const char* assertion, + std::source_location sourceLocation, + Span buffer); + [[noreturn]] void assertFail( const char* assertion, std::source_location sourceLocation = std::source_location::current()); diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp index 4905643..b54b044 100644 --- a/include/fud_c_file.hpp +++ b/include/fud_c_file.hpp @@ -220,7 +220,7 @@ class CFile { } if (offset > LONG_MAX || SIZE_MAX - offset < length || destinationSize < length) { - result.status = FudStatus::InvalidInput; + result.status = FudStatus::ArgumentInvalid; return result; } @@ -232,7 +232,7 @@ class CFile { auto fileSize = fileSizeResult.getOkay(); if (offset + length > fileSize) { - result.status = FudStatus::InvalidInput; + result.status = FudStatus::ArgumentInvalid; return result; } @@ -306,7 +306,7 @@ class CFile { } if (offset > LONG_MAX || SIZE_MAX - offset < length || sourceSize < length) { - result.status = FudStatus::InvalidInput; + result.status = FudStatus::ArgumentInvalid; return result; } diff --git a/include/fud_result.hpp b/include/fud_result.hpp index 5dabaf5..4bfb819 100644 --- a/include/fud_result.hpp +++ b/include/fud_result.hpp @@ -28,6 +28,22 @@ class [[nodiscard]] Result { public: using ResultType = Result; + Result(const T& value) : m_value{value} + { + } + + Result(const E& value) : m_value{value} + { + } + + Result(T&& value) : m_value{std::move(value)} + { + } + + Result(E&& value) : m_value{std::move(value)} + { + } + static ResultType okay(const T& okay) { return ResultType{okay}; @@ -49,22 +65,26 @@ class [[nodiscard]] Result { } template - static ResultType okay(const Result& okayRes) { + static ResultType okay(const Result& okayRes) + { return ResultType{okayRes.getOkay()}; } template - static ResultType okay(Result&& okayRes) { + static ResultType okay(Result&& okayRes) + { return ResultType{okayRes.takeOkay()}; } template - static ResultType error(const Result& errorRes) { + static ResultType error(const Result& errorRes) + { return ResultType{errorRes.getError()}; } template - static ResultType error(Result&& errorRes) { + static ResultType error(Result&& errorRes) + { return ResultType{errorRes.takeError()}; } @@ -99,29 +119,22 @@ class [[nodiscard]] Result { } private: - explicit Result() : m_value() - { - } - - explicit Result(const T& value) : m_value{value} - { - } - - explicit Result(const E& value) : m_value{value} - { - } - - explicit Result(T&& value): m_value{std::move(value)} - { - } - - explicit Result(E&& value) : m_value{std::move(value)} + Result() : m_value() { } std::variant m_value; }; +#define M_TakeOrReturn(HYGIENE_EXPRESSION) \ + ({ \ + auto HYGIENE_RESULT{(HYGIENE_EXPRESSION)}; \ + if (HYGIENE_RESULT.isError()) { \ + return HYGIENE_RESULT.takeError(); \ + } \ + HYGIENE_RESULT.takeOkay(); \ + }) + } // namespace fud #endif diff --git a/include/fud_span.hpp b/include/fud_span.hpp new file mode 100644 index 0000000..e8c5704 --- /dev/null +++ b/include/fud_span.hpp @@ -0,0 +1,120 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FUD_SPAN_HPP +#define FUD_SPAN_HPP + +#include + +#include "fud_array.hpp" + +namespace fud { + +template +struct Span { + static_assert(Size > 0); + using ValueType = T; + + static Span make(Array& array) { + Span output{}; + output.m_data = array.data(); + return output; + } + + template + static Span make(const Array& array) { + static_assert(std::convertible_to); + Span output{}; + output.m_data = array.data(); + return output; + } + + T* m_data; + + [[nodiscard]] constexpr size_t size() const + { + return Size; + } + + constexpr T& front() + { + return m_data[0]; + } + + constexpr const T& front() const + { + return m_data[0]; + } + + constexpr T& back() + { + return m_data[Size - 1]; + } + + constexpr const T& back() const + { + return m_data[Size - 1]; + } + + constexpr T* data() noexcept + { + return m_data; + } + + constexpr const T* data() const noexcept + { + return m_data; + } + + constexpr T* begin() noexcept + { + return m_data; + } + + constexpr const T* begin() const noexcept + { + return m_data; + } + + constexpr T* end() noexcept + { + return m_data + Size; + } + + constexpr const T* end() const noexcept + { + return m_data + Size; + } + + constexpr T& operator[](size_t index) + { + return m_data[index]; + } + + constexpr const T& operator[](size_t index) const + { + return m_data[index]; + } + + constexpr bool operator==(const Span&) const noexcept = default; + + constexpr auto operator<=>(const Span& other) const noexcept = default; +}; + +} // namespace fud + +#endif diff --git a/include/fud_status.hpp b/include/fud_status.hpp index 5ebf229..bda646b 100644 --- a/include/fud_status.hpp +++ b/include/fud_status.hpp @@ -28,7 +28,8 @@ enum class [[nodiscard]] FudStatus ObjectInvalid, OperationInvalid, AllocFailure, - InvalidInput, + DeallocFailure, + ArgumentInvalid, Utf8Invalid, Failure, NotFound, @@ -58,8 +59,10 @@ constexpr const char* FudStatusToString(FudStatus status) return "OperationInvalid"; case FudStatus::AllocFailure: return "AllocFailure"; - case FudStatus::InvalidInput: - return "InvalidInput"; + case FudStatus::DeallocFailure: + return "DeallocFailure"; + case FudStatus::ArgumentInvalid: + return "ArgumentInvalid"; case FudStatus::Utf8Invalid: return "Utf8Invalid"; case FudStatus::Failure: diff --git a/include/fud_string.hpp b/include/fud_string.hpp index 4367aae..ba05450 100644 --- a/include/fud_string.hpp +++ b/include/fud_string.hpp @@ -72,7 +72,7 @@ class String { auto lengthResult = cStringLength(cString); if (lengthResult < 0 || lengthResult >= SSIZE_MAX) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } auto stringLength = static_cast(lengthResult); if (SIZE_MAX - totalLength < stringLength) { @@ -89,6 +89,7 @@ class String { output.m_allocator = allocator; if (output.m_length >= output.m_capacity) { output.m_capacity = output.m_length + 1; + /* Avoid using compiler expansions in headers */ auto dataResult = output.m_allocator->allocate(output.m_capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); diff --git a/include/libfud.hpp b/include/libfud.hpp index 5bc1630..bff791d 100644 --- a/include/libfud.hpp +++ b/include/libfud.hpp @@ -19,6 +19,7 @@ #define LIBFUD_HPP #include "fud_array.hpp" +#include "fud_allocator.hpp" #include "fud_result.hpp" #include "fud_status.hpp" #include "fud_string.hpp" @@ -43,12 +44,13 @@ FUD fud(); * \brief Get an environmental variable if it exists. * * \param[in] name The name of the variable to look up. + * \param[in] allocator The allocator used by the string returned. * * \retstmt The value of the string bound to the variable if it exists. * \retcode FudStatus::NullPointer if name is a null pointer. * \retcode FudStatus::NotFound if no binding for the variable exists. */ -Result getEnv(const char* name); +Result getEnv(const char* name, Allocator* allocator= &globalFudAllocator); template concept CStringRepr = requires(T strObj) { @@ -56,9 +58,9 @@ concept CStringRepr = requires(T strObj) { }; template -Result getEnv(const T& name) +Result getEnv(const T& name, Allocator* allocator = &globalFudAllocator) { - return getEnv(name.c_str()); + return getEnv(name.c_str(), allocator); } } // namespace fud diff --git a/source/fud_allocator.cpp b/source/fud_allocator.cpp index b5af593..1e4117a 100644 --- a/source/fud_allocator.cpp +++ b/source/fud_allocator.cpp @@ -30,16 +30,18 @@ Result FudAllocator::allocate(size_t bytes, size_t alignment) return RetType::okay(pointer); } -void FudAllocator::deallocate(void* pointer, size_t bytes, size_t alignment) +FudStatus FudAllocator::deallocate(void* pointer, size_t bytes) { + if (pointer == nullptr || bytes == 0) { + return FudStatus::ArgumentInvalid; + } static_cast(bytes); - static_cast(alignment); fudFree(pointer); + return FudStatus::Success; } bool FudAllocator::isEqual(const Allocator& rhs) const { - /* Is this legit? Or is this a bogus check? */ return &rhs == static_cast(this); } diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp index 749aa6b..14bf5eb 100644 --- a/source/fud_assert.cpp +++ b/source/fud_assert.cpp @@ -1,24 +1,20 @@ #include "fud_assert.hpp" #include "fud_array.hpp" +#include "fud_span.hpp" #include "fud_string_view.hpp" #include #include #include -#include namespace fud { -[[noreturn]] void assertFail(const char* assertion, const std::source_location sourceLocation) +void assertFormat( + const char* assertion, + const std::source_location sourceLocation, + Span buffer) { - constexpr size_t MAX_FILE_CHARS = 256; - constexpr size_t MAX_FUNCTION_CHARS = 256; - constexpr size_t BITS_PER_OCTAL = 3; - constexpr auto MAX_LINE_CHARS = BITS_PER_OCTAL * sizeof(decltype(sourceLocation.line())) + 3; - constexpr size_t MAX_ASSERT_CHARS = 512 - MAX_LINE_CHARS; - constexpr size_t ASSERT_MSG_SIZE = MAX_FILE_CHARS + MAX_LINE_CHARS + MAX_FUNCTION_CHARS + MAX_ASSERT_CHARS; - auto lengthResult = cStringLength(sourceLocation.file_name()); size_t filenameLength = 0; auto badLength = lengthResult < 1 || lengthResult > SSIZE_MAX; @@ -36,7 +32,6 @@ namespace fud { filenameLength = MAX_FILE_CHARS; } - Array buffer{}; // clang-format off static_cast(std::format_to_n( buffer.data(), buffer.size() - 1U, @@ -46,6 +41,12 @@ namespace fud { sourceLocation.function_name(), MAX_FUNCTION_CHARS, assertion, MAX_ASSERT_CHARS)); // clang-format on +} + +[[noreturn]] void assertFail(const char* assertion, const std::source_location sourceLocation) +{ + Array buffer{}; + assertFormat(assertion, sourceLocation, Span::make(buffer)); buffer[buffer.size() - 1] = '\0'; fputs(buffer.data(), stderr); std::abort(); diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp index 15c57ab..9fc7734 100644 --- a/source/fud_c_file.cpp +++ b/source/fud_c_file.cpp @@ -22,7 +22,7 @@ namespace fud { CBinaryFileResult CBinaryFile::make(const String& filename, CFileMode mode) { if (!filename.valid()) { - return CBinaryFileResult::error(FudStatus::InvalidInput); + return CBinaryFileResult::error(FudStatus::ArgumentInvalid); } auto modeResult{String::makeFromCString(CBinaryFileModeFromFlags(mode))}; if (modeResult.isError()) { @@ -42,7 +42,7 @@ CBinaryFileResult CBinaryFile::make(const String& filename, CFileMode mode) CBinaryFileResult CBinaryFile::make(const String& filename, CFileMode mode, const char* extraFlags) { if (!filename.valid()) { - return CBinaryFileResult::error(FudStatus::InvalidInput); + return CBinaryFileResult::error(FudStatus::ArgumentInvalid); } auto modeResult{String::makeFromCStrings(CBinaryFileModeFromFlags(mode), extraFlags)}; if (modeResult.isError()) { @@ -90,7 +90,7 @@ CBinaryFile& CBinaryFile::operator=(CBinaryFile&& rhs) noexcept CTextFileResult CTextFile::make(const String& filename, CFileMode mode) { if (!filename.valid()) { - return CTextFileResult::error(FudStatus::InvalidInput); + return CTextFileResult::error(FudStatus::ArgumentInvalid); } auto modeResult{String::makeFromCString(CBinaryFileModeFromFlags(mode))}; if (modeResult.isError()) { @@ -110,7 +110,7 @@ CTextFileResult CTextFile::make(const String& filename, CFileMode mode) CTextFileResult CTextFile::make(const String& filename, CFileMode mode, const char* extraFlags) { if (!filename.valid()) { - return CTextFileResult::error(FudStatus::InvalidInput); + return CTextFileResult::error(FudStatus::ArgumentInvalid); } auto modeResult{String::makeFromCStrings(CTextFileModeFromFlags(mode), extraFlags)}; if (modeResult.isError()) { diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp index 318d7e6..f46d530 100644 --- a/source/fud_directory.cpp +++ b/source/fud_directory.cpp @@ -90,7 +90,7 @@ Result Directory::make(const String& name) using RetType = Result; Directory directory{}; if (!name.valid()) { - return RetType::error(FudStatus::InvalidInput); + return RetType::error(FudStatus::ArgumentInvalid); } if (!name.utf8Valid()) { diff --git a/source/fud_memory.cpp b/source/fud_memory.cpp index c4a4aa8..56701a6 100644 --- a/source/fud_memory.cpp +++ b/source/fud_memory.cpp @@ -26,7 +26,7 @@ FudStatus copyMem(void* destination, size_t destination_size, const void* source } if (destination_size < count) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto* destPtr = static_cast(destination); @@ -45,7 +45,7 @@ FudStatus compareMem(const void* lhs, size_t destination_size, const void* rhs, } if (destination_size < count) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } int localDifference = 0; @@ -83,7 +83,7 @@ FudStatus setMemory(void* data, size_t dataSize, uint8_t pattern, size_t count) if (count > dataSize) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } for (size_t idx = 0; idx < count; ++idx) @@ -104,12 +104,12 @@ FudStatus setMemory( { if (eltOffset >= collectionCount) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } if (eltOffset + eltCount > collectionCount) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto dataSize = collectionCount * eltSize; diff --git a/source/fud_sqlite.cpp b/source/fud_sqlite.cpp index e13feee..735389f 100644 --- a/source/fud_sqlite.cpp +++ b/source/fud_sqlite.cpp @@ -140,7 +140,7 @@ FudStatus SqliteDb::initialize() m_nameValid = m_name.utf8Valid(); if (!m_nameValid) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } m_errorCode = open(); @@ -178,7 +178,7 @@ SqliteStatement::SqliteStatement(const SqliteDb& sqliteDb, const String& input) } if (!input.utf8Valid() || input.length() > INT_MAX) { - m_status = FudStatus::InvalidInput; + m_status = FudStatus::ArgumentInvalid; return; } diff --git a/source/fud_string.cpp b/source/fud_string.cpp index 31ba5ba..d354fe7 100644 --- a/source/fud_string.cpp +++ b/source/fud_string.cpp @@ -19,6 +19,8 @@ #include "fud_assert.hpp" +#include + namespace fud { ssize_t cStringLength(const char* str) @@ -59,31 +61,23 @@ StringResult String::makeFromCString(const char* cString, Allocator* allocator) auto lenResult = cStringLength(cString); if (lenResult < 0 || lenResult >= SSIZE_MAX) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } String output{}; output.m_allocator = allocator; - auto* data = output.m_buffer.data(); output.m_length = static_cast(lenResult); if (output.m_length >= output.m_capacity) { output.m_capacity = output.m_length + 1; - auto dataResult{output.m_allocator->allocate(output.m_capacity)}; - if (dataResult.isError()) { - return StringResult::error(dataResult); - } - - data = static_cast(dataResult.takeOkay()); - if (data == nullptr) { - return StringResult::error(FudStatus::AllocFailure); - } - output.m_data = data; + output.m_data = static_cast(M_TakeOrReturn(output.m_allocator->allocate(output.m_capacity))); + fudAssert(output.m_data != nullptr); } - auto copyStatus = copyMem(data, output.m_capacity, cString, output.m_length); + auto copyStatus = copyMem(output.data(), output.m_capacity, cString, output.m_length); fudAssert(copyStatus == FudStatus::Success); + auto terminateStatus = output.nullTerminate(); fudAssert(terminateStatus == FudStatus::Success); @@ -93,7 +87,7 @@ StringResult String::makeFromCString(const char* cString, Allocator* allocator) StringResult String::from(const String& rhs) { if (!rhs.valid()) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } String output{}; @@ -102,18 +96,16 @@ StringResult String::from(const String& rhs) output.m_allocator = rhs.m_allocator; if (rhs.isLarge()) { - auto dataResult = output.m_allocator->allocate(output.m_capacity); - if (dataResult.isError()) { - return StringResult::error(dataResult.getError()); - } - output.m_data = static_cast(dataResult.getOkay()); + output.m_data = static_cast(M_TakeOrReturn(output.m_allocator->allocate(output.m_capacity))); fudAssert(output.m_data != nullptr); } auto copyResult = copyMem(output.data(), output.m_capacity, rhs.data(), output.m_length); fudAssert(copyResult == FudStatus::Success); + auto nullTerminateStatus = output.nullTerminate(); fudAssert(nullTerminateStatus == FudStatus::Success); + return StringResult::okay(std::move(output)); } @@ -141,7 +133,7 @@ FudStatus String::copy(const String& rhs) } if (!rhs.valid()) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } cleanup(); @@ -151,12 +143,7 @@ FudStatus String::copy(const String& rhs) m_allocator = rhs.m_allocator; if (isLarge()) { - auto dataResult = m_allocator->allocate(m_capacity); - if (dataResult.isError()) { - return dataResult.getError(); - } - m_data = static_cast(dataResult.getOkay()); - fudAssert(m_data != nullptr); + m_data = static_cast(M_TakeOrReturn(m_allocator->allocate(m_capacity))); } auto copyResult = copyMem(data(), m_capacity, rhs.data(), m_length); @@ -187,7 +174,8 @@ String& String::operator=(String&& rhs) noexcept void String::cleanup() { if (isLarge() && m_data != nullptr && m_allocator != nullptr) { - m_allocator->deallocate(m_data, m_capacity); + auto deallocStatus = m_allocator->deallocate(m_data, m_capacity); + static_cast(deallocStatus); m_data = nullptr; } } @@ -209,37 +197,36 @@ FudStatus String::resize(size_t newCapacity) if (newCapacity <= SSO_BUF_SIZE) { BufType temp{BufType::constFill(0)}; - static_cast(copyMem(data(), temp.size(), temp.data(), length())); - m_allocator->deallocate(m_data, m_capacity); + auto copyResult = copyMem(data(), temp.size(), temp.data(), length()); + fudAssert(copyResult == FudStatus::Success); + + auto deallocStatus = m_allocator->deallocate(m_data, m_capacity); m_capacity = SSO_BUF_SIZE; m_data = nullptr; + copyMem(m_buffer, temp); data()[m_length] = '\0'; - return FudStatus::Success; - } - utf8* newData = nullptr; - auto newDataResult = m_allocator->allocate(newCapacity); - if (newDataResult.isError()) { - return newDataResult.getError(); - } - newData = static_cast(newDataResult.getOkay()); - if (newData == nullptr) { - return FudStatus::AllocFailure; + return deallocStatus != FudStatus::Success ? FudStatus::DeallocFailure : FudStatus::Success; } + auto* newData = static_cast(M_TakeOrReturn(m_allocator->allocate(newCapacity))); + fudAssert(newData != nullptr); + auto copyResult = copyMem(newData, newCapacity, data(), length()); fudAssert(copyResult == FudStatus::Success); + auto deallocStatus = FudStatus::Success; if (isLarge()) { - m_allocator->deallocate(data(), m_capacity); + deallocStatus = m_allocator->deallocate(data(), m_capacity); } m_capacity = newCapacity; m_data = newData; data()[m_length] = '\0'; fudAssert(valid()); - return FudStatus::Success; + + return deallocStatus != FudStatus::Success ? FudStatus::DeallocFailure : FudStatus::Success; } bool String::nullTerminated() const @@ -348,12 +335,12 @@ FudStatus String::pushBack(const FudUtf8& letter) } if (!letter.valid()) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } const auto* letterData = letter.data(); if (letterData == nullptr) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto letterSize = letter.size(); @@ -385,7 +372,7 @@ FudStatus String::append(const char* source) { auto lenResult = cStringLength(source); if (lenResult < 0 || lenResult >= SSIZE_MAX) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } return this->append(StringView{static_cast(lenResult), source}); @@ -436,41 +423,48 @@ FudStatus String::append(StringView source) StringResult String::catenate(const char* rhs) const { if (!valid()) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } auto lenResult = cStringLength(rhs); if (lenResult < 0 || lenResult >= SSIZE_MAX) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } size_t rhsLength = static_cast(lenResult); String output{}; - if (SIZE_MAX - m_length < rhsLength) - { + if (SIZE_MAX - m_length < rhsLength) { return StringResult::error(FudStatus::Failure); } + output.m_allocator = m_allocator; + output.m_length = m_length + rhsLength; - auto* destPtr = output.m_buffer.data(); - if (output.m_length >= output.m_capacity) { + if (output.m_length > output.m_capacity) { output.m_capacity = output.m_length + 1; - auto ptrResult = m_allocator->allocate(output.m_capacity); - if (ptrResult.isError()) { - return StringResult::error(ptrResult.getError()); - } - destPtr = static_cast(ptrResult.getOkay()); - if (destPtr == nullptr) { - return StringResult::error(FudStatus::AllocFailure); - } - output.m_data = destPtr; } - auto status = copyMem(destPtr, m_capacity, data(), length()); + if (!output.isLarge()) { + auto status = copyMem(output.m_buffer.data(), output.m_capacity, data(), length()); + fudAssert(status == FudStatus::Success); + + status = copyMem(output.m_buffer.data() + length(), output.m_capacity - length(), rhs, rhsLength); + fudAssert(status == FudStatus::Success); + + auto terminateStatus = output.nullTerminate(); + fudAssert(terminateStatus == FudStatus::Success); + + return StringResult::okay(std::move(output)); + } + + output.m_data = static_cast(M_TakeOrReturn(output.m_allocator->allocate(output.m_capacity))); + fudAssert(output.m_data != nullptr); + + auto status = copyMem(output.m_data, m_capacity, data(), length()); fudAssert(status == FudStatus::Success); - status = copyMem(destPtr + length(), output.m_capacity - length(), rhs, rhsLength); + status = copyMem(output.m_data + length(), output.m_capacity - length(), rhs, rhsLength); fudAssert(status == FudStatus::Success); auto terminateStatus = output.nullTerminate(); @@ -482,26 +476,25 @@ StringResult String::catenate(const char* rhs) const StringResult String::catenate(const String& rhs) const { if (!valid() || !rhs.valid()) { - return StringResult::error(FudStatus::InvalidInput); + return StringResult::error(FudStatus::ArgumentInvalid); } - if (SIZE_MAX - m_length < rhs.length()) - { + if (SIZE_MAX - m_length < rhs.length()) { return StringResult::error(FudStatus::Failure); } String output{}; + output.m_allocator = m_allocator; output.m_length = m_length + rhs.length(); output.m_capacity = output.m_length + 1; + if (output.m_capacity < SSO_BUF_SIZE) { output.m_capacity = SSO_BUF_SIZE; } + if (output.isLarge()) { - auto allocResult{output.m_allocator->allocate(output.m_capacity)}; - if (allocResult.isError()) { - return StringResult::error(allocResult.getError()); - } - output.m_data = static_cast(allocResult.takeOkay()); + output.m_data = static_cast(M_TakeOrReturn(output.m_allocator->allocate(output.m_capacity))); + fudAssert(output.m_data != nullptr); } auto* destPtr = output.data(); diff --git a/source/fud_utf8.cpp b/source/fud_utf8.cpp index 0804531..ee8137a 100644 --- a/source/fud_utf8.cpp +++ b/source/fud_utf8.cpp @@ -104,7 +104,7 @@ FudStatus isAsciiPredicate(FudUtf8* character, bool* pred, Predicate&& predicate auto maybeAscii = character->getAscii(); if (!maybeAscii.has_value()) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto asciiChar = *maybeAscii; diff --git a/source/libfud.cpp b/source/libfud.cpp index 538d3a9..802b2c7 100644 --- a/source/libfud.cpp +++ b/source/libfud.cpp @@ -37,7 +37,7 @@ FUD fud() return fudInfo; } -Result getEnv(const char* name) +Result getEnv(const char* name, Allocator* allocator) { using RetType = Result; @@ -50,12 +50,7 @@ Result getEnv(const char* name) return RetType::error(FudStatus::NotFound); } - auto envVarResult = String::makeFromCString(resultString); - if (envVarResult.isError()) { - return RetType::error(envVarResult); - } - - return RetType::okay(std::move(envVarResult)); + return String::makeFromCString(resultString, allocator); } } // namespace fud diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a20e991..a2fcf7b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -57,11 +57,13 @@ function(fud_add_test test_name) endfunction() fud_add_test(test_fud SOURCES test_fud.cpp) +fud_add_test(test_allocator SOURCES test_allocator.cpp) fud_add_test(test_assert SOURCES test_assert.cpp) +fud_add_test(test_directory SOURCES test_directory.cpp) fud_add_test(test_result SOURCES test_result.cpp) -fud_add_test(test_string SOURCES test_string.cpp) +fud_add_test(test_span SOURCES test_span.cpp) fud_add_test(test_sqlite SOURCES test_sqlite.cpp) -fud_add_test(test_directory SOURCES test_directory.cpp) +fud_add_test(test_string SOURCES test_string.cpp) # fud_add_test(test_deserialize_number SOURCES test_deserialize_number.cpp) # fud_add_test(test_ext_algorithm SOURCES test_algorithm.cpp) # fud_add_test(test_ext_array SOURCES diff --git a/test/test_allocator.cpp b/test/test_allocator.cpp new file mode 100644 index 0000000..d93df1e --- /dev/null +++ b/test/test_allocator.cpp @@ -0,0 +1,54 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_allocator.hpp" +#include "test_common.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +struct FailAllocator : public MockFudAlloc { + virtual void* operator()(size_t size) override { + static_cast(size); + return nullptr; + } +}; + +TEST(AllocatorTest, TestFudAllocator) +{ + FudAllocator fudAllocator{}; + ASSERT_NE(&fudAllocator, &globalFudAllocator); + ASSERT_FALSE(fudAllocator.isEqual(globalFudAllocator)); + + auto allocResult{fudAllocator.allocate(1)}; + ASSERT_TRUE(allocResult.isOkay()); + auto* mem = allocResult.getOkay(); + ASSERT_NE(mem, nullptr); + ASSERT_EQ(fudAllocator.deallocate(mem, 0), FudStatus::ArgumentInvalid); + ASSERT_EQ(fudAllocator.deallocate(mem, 1), FudStatus::Success); + mem = nullptr; + ASSERT_EQ(fudAllocator.deallocate(mem, 1), FudStatus::ArgumentInvalid); + + FailAllocator failAllocator{}; + globalMockFudAlloc.m_allocator = &failAllocator; + + allocResult = fudAllocator.allocate(1); + ASSERT_TRUE(allocResult.isError()); +} + +} // namespace fud diff --git a/test/test_assert.cpp b/test/test_assert.cpp index 41cd20f..1f95388 100644 --- a/test/test_assert.cpp +++ b/test/test_assert.cpp @@ -21,7 +21,7 @@ namespace fud { -TEST(FudTest, FudFud) +TEST(AssertTest, AssertFud) { EXPECT_EXIT(fudAssert(false), ::testing::KilledBySignal(SIGABRT), ".*"); } diff --git a/test/test_common.cpp b/test/test_common.cpp index 5a26e09..d3e1704 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -16,21 +16,40 @@ */ #include "test_common.hpp" -#include "fud_allocator.hpp" #include namespace fud { -void* fudAlloc(size_t size) { +void* MockFudAlloc::operator()(size_t size) +{ return malloc(size); } -void* fudRealloc(void* ptr, size_t size) { - return realloc(ptr, size); +void MockFudDealloc::operator()(void* pointer) +{ + return free(pointer); +} + +MockFudAlloc globalDefaultMockAlloc{}; + +MockFudDealloc globalDefaultMockDealloc{}; + +void* MockFudAllocator::allocate(size_t size) { + return (*m_allocator)(size); +} + +void MockFudAllocator::deallocate(void* pointer) { + return (*m_deallocator)(pointer); +} + +MockFudAllocator globalMockFudAlloc{}; + +void* fudAlloc(size_t size) { + return globalMockFudAlloc.allocate(size); } void fudFree(void* ptr) { - return free(ptr); + return globalMockFudAlloc.deallocate(ptr); } } // namespace fud diff --git a/test/test_common.hpp b/test/test_common.hpp index fa6cf09..05f86db 100644 --- a/test/test_common.hpp +++ b/test/test_common.hpp @@ -15,13 +15,13 @@ * limitations under the License. */ -#ifndef EXT_TEST_COMMON_HPP -#define EXT_TEST_COMMON_HPP +#ifndef FUD_TEST_COMMON_HPP +#define FUD_TEST_COMMON_HPP #include #include -namespace ext_lib { +namespace fud { // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define MULTI_BYTE_LITERAL "test今日素敵はですねƩ®😀z" @@ -38,6 +38,28 @@ static_assert(sizeof(FOUR_BYTE) == 4 + 1); // NOLINTEND(cppcoreguidelines-macro-usage) constexpr size_t charSetSize = sizeof(CHARACTER_SET) - 1; +struct MockFudAlloc { + virtual void* operator()(size_t size); +}; + +extern MockFudAlloc globalDefaultMockAlloc; + +struct MockFudDealloc { + virtual void operator()(void* pointer); +}; + +extern MockFudDealloc globalDefaultMockDealloc; + +struct MockFudAllocator { + void* allocate(size_t size); + void deallocate(void* pointer); + + MockFudAlloc* m_allocator{&globalDefaultMockAlloc}; + MockFudDealloc* m_deallocator{&globalDefaultMockDealloc};; +}; + +extern MockFudAllocator globalMockFudAlloc; + } // namespace ext_lib #endif diff --git a/test/test_directory.cpp b/test/test_directory.cpp index c2af281..2f69dab 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -49,16 +49,16 @@ FudStatus removeRecursive(const String& path) return FudStatus::Utf8Invalid; } if (path.length() < 5) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto prefix{String::makeFromCString("/tmp/").takeOkay()}; auto diffResult = compareMem(path.data(), path.length(), prefix.data(), prefix.length()); if (diffResult.isError()) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } auto diff = diffResult.getOkay(); if (diff != 0) { - return FudStatus::InvalidInput; + return FudStatus::ArgumentInvalid; } constexpr int maxOpenFd = 64; auto status = nftw(path.c_str(), unlink_cb, maxOpenFd, FTW_DEPTH | FTW_PHYS); diff --git a/test/test_result.cpp b/test/test_result.cpp index 172323d..5eadd8c 100644 --- a/test/test_result.cpp +++ b/test/test_result.cpp @@ -43,10 +43,10 @@ TEST(ResultTest, ErrResult) ASSERT_EQ(err, FudStatus::Failure); - const auto status = FudStatus::InvalidInput; + const auto status = FudStatus::ArgumentInvalid; GResult err2 = GResult::error(status); ASSERT_TRUE(err2.isError()); - ASSERT_EQ(err2.getError(), FudStatus::InvalidInput); + ASSERT_EQ(err2.getError(), FudStatus::ArgumentInvalid); } } // namespace fud diff --git a/test/test_span.cpp b/test/test_span.cpp new file mode 100644 index 0000000..28d0cc5 --- /dev/null +++ b/test/test_span.cpp @@ -0,0 +1,52 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_array.hpp" +#include "fud_span.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +TEST(SpanTest, MutableSpanTest) +{ + Array data{1, 2, 3, 4}; + auto span = Span::make(data); + for (auto& elt : span) { + elt *= 2; + } + + const Array expected{2, 4, 6, 8}; + EXPECT_EQ(data, expected); + + const int* innerImmutable = span.data(); + EXPECT_EQ(*innerImmutable, expected[0]); +} + +TEST(SpanTest, ConstSpanTest) +{ + const Array data{1, 2, 3, 4}; + auto span = Span::make(data); + + int sum = 0; + for (const auto& elt : span) { + sum += elt; + } + EXPECT_EQ(sum, 4 * 5 / 2); +} + +} // namespace fud diff --git a/tools/coverage.sh b/tools/coverage.sh index 5caa6f0..95eef03 100755 --- a/tools/coverage.sh +++ b/tools/coverage.sh @@ -1,5 +1,10 @@ #!/bin/sh +PROJ_ROOT=$(git rev-parse --show-toplevel) +cd $PROJ_ROOT + +HTML_DIR=build/coverage/html + ctest --test-dir build/test -j8 -mkdir -p build/html -gcovr --exclude-throw-branches --exclude build/_deps/ --exclude test -r source . --html-details build/html/gcovr_report.html +mkdir -p ${HTML_DIR} +gcovr --exclude-throw-branches --exclude build/_deps/ --exclude test -r . --html-details ${HTML_DIR}/gcovr_report.html -- cgit v1.2.3