summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--cmake/warnings.cmake5
-rw-r--r--include/fud_allocator.hpp11
-rw-r--r--include/fud_assert.hpp13
-rw-r--r--include/fud_c_file.hpp6
-rw-r--r--include/fud_result.hpp55
-rw-r--r--include/fud_span.hpp120
-rw-r--r--include/fud_status.hpp9
-rw-r--r--include/fud_string.hpp3
-rw-r--r--include/libfud.hpp8
-rw-r--r--source/fud_allocator.cpp8
-rw-r--r--source/fud_assert.cpp21
-rw-r--r--source/fud_c_file.cpp8
-rw-r--r--source/fud_directory.cpp2
-rw-r--r--source/fud_memory.cpp10
-rw-r--r--source/fud_sqlite.cpp4
-rw-r--r--source/fud_string.cpp131
-rw-r--r--source/fud_utf8.cpp2
-rw-r--r--source/libfud.cpp9
-rw-r--r--test/CMakeLists.txt6
-rw-r--r--test/test_allocator.cpp54
-rw-r--r--test/test_assert.cpp2
-rw-r--r--test/test_common.cpp29
-rw-r--r--test/test_common.hpp28
-rw-r--r--test/test_directory.cpp6
-rw-r--r--test/test_result.cpp4
-rw-r--r--test/test_span.cpp52
-rwxr-xr-xtools/coverage.sh9
28 files changed, 456 insertions, 162 deletions
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<void*, FudStatus> 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<void*, FudStatus> 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 <stacktrace>
#include <source_location>
+#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<char, ASSERT_MSG_SIZE> 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<T, E>;
+ 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 <typename F>
- static ResultType okay(const Result<T, F>& okayRes) {
+ static ResultType okay(const Result<T, F>& okayRes)
+ {
return ResultType{okayRes.getOkay()};
}
template <typename F>
- static ResultType okay(Result<T, F>&& okayRes) {
+ static ResultType okay(Result<T, F>&& okayRes)
+ {
return ResultType{okayRes.takeOkay()};
}
template <typename U>
- static ResultType error(const Result<U, E>& errorRes) {
+ static ResultType error(const Result<U, E>& errorRes)
+ {
return ResultType{errorRes.getError()};
}
template <typename U>
- static ResultType error(Result<U, E>&& errorRes) {
+ static ResultType error(Result<U, E>&& 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<T, E> 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 <cstdlib>
+
+#include "fud_array.hpp"
+
+namespace fud {
+
+template <typename T, size_t Size>
+struct Span {
+ static_assert(Size > 0);
+ using ValueType = T;
+
+ static Span make(Array<T, Size>& array) {
+ Span<T, Size> output{};
+ output.m_data = array.data();
+ return output;
+ }
+
+ template <typename U>
+ static Span make(const Array<U, Size>& array) {
+ static_assert(std::convertible_to<U, T>);
+ Span<T, Size> 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<T, Size>&) const noexcept = default;
+
+ constexpr auto operator<=>(const Span<T, Size>& 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<size_t>(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<String, FudStatus> getEnv(const char* name);
+Result<String, FudStatus> getEnv(const char* name, Allocator* allocator= &globalFudAllocator);
template <typename T>
concept CStringRepr = requires(T strObj) {
@@ -56,9 +58,9 @@ concept CStringRepr = requires(T strObj) {
};
template <CStringRepr T>
-Result<String, FudStatus> getEnv(const T& name)
+Result<String, FudStatus> 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<void*, FudStatus> 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<void>(bytes);
- static_cast<void>(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<const Allocator*>(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 <climits>
#include <cstdio>
#include <format>
-#include <stdexcept>
namespace fud {
-[[noreturn]] void assertFail(const char* assertion, const std::source_location sourceLocation)
+void assertFormat(
+ const char* assertion,
+ const std::source_location sourceLocation,
+ Span<char, ASSERT_MSG_SIZE> 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<char, ASSERT_MSG_SIZE> buffer{};
// clang-format off
static_cast<void>(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<char, ASSERT_MSG_SIZE> buffer{};
+ assertFormat(assertion, sourceLocation, Span<char, ASSERT_MSG_SIZE>::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, FudStatus> Directory::make(const String& name)
using RetType = Result<Directory, FudStatus>;
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<char*>(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 <type_traits>
+
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<size_t>(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<utf8*>(dataResult.takeOkay());
- if (data == nullptr) {
- return StringResult::error(FudStatus::AllocFailure);
- }
- output.m_data = data;
+ output.m_data = static_cast<utf8*>(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<utf8*>(dataResult.getOkay());
+ output.m_data = static_cast<utf8*>(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<utf8*>(dataResult.getOkay());
- fudAssert(m_data != nullptr);
+ m_data = static_cast<utf8*>(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<void>(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<void>(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<utf8*>(newDataResult.getOkay());
- if (newData == nullptr) {
- return FudStatus::AllocFailure;
+ return deallocStatus != FudStatus::Success ? FudStatus::DeallocFailure : FudStatus::Success;
}
+ auto* newData = static_cast<utf8*>(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<size_t>(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<size_t>(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<utf8*>(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<utf8*>(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<utf8*>(allocResult.takeOkay());
+ output.m_data = static_cast<utf8*>(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<String, FudStatus> getEnv(const char* name)
+Result<String, FudStatus> getEnv(const char* name, Allocator* allocator)
{
using RetType = Result<String, FudStatus>;
@@ -50,12 +50,7 @@ Result<String, FudStatus> 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<void>(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 <cstdlib>
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 <cstddef>
#include <cstdlib>
-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<int, 4> data{1, 2, 3, 4};
+ auto span = Span<int, 4>::make(data);
+ for (auto& elt : span) {
+ elt *= 2;
+ }
+
+ const Array<int, 4> expected{2, 4, 6, 8};
+ EXPECT_EQ(data, expected);
+
+ const int* innerImmutable = span.data();
+ EXPECT_EQ(*innerImmutable, expected[0]);
+}
+
+TEST(SpanTest, ConstSpanTest)
+{
+ const Array<int, 4> data{1, 2, 3, 4};
+ auto span = Span<int const, 4>::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