From f3ac764684c64fbdd2094853a80b23e570cd5d9c Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Tue, 15 Oct 2024 20:56:26 -0500 Subject: Conver to using static constructors for string, sqlite, files. --- .clang-format | 2 +- include/fud_c_file.hpp | 39 ++++++++++------ include/fud_result.hpp | 24 +++++++++- include/fud_sqlite.hpp | 36 ++++++++++++--- include/fud_string.hpp | 79 ++++++++++++++++++++++++++++++--- source/fud_c_file.cpp | 100 ++++++++++++++++++++++++++++++----------- source/fud_directory.cpp | 9 ++-- source/fud_sqlite.cpp | 87 ++++++++++++++++++++++++++++-------- source/fud_string.cpp | 113 +++++++++++++++++++++++++++++++++-------------- source/libfud.cpp | 8 ++-- test/test_directory.cpp | 25 ++++++----- test/test_sqlite.cpp | 21 +++++---- test/test_string.cpp | 19 +++++--- 13 files changed, 423 insertions(+), 139 deletions(-) diff --git a/.clang-format b/.clang-format index 689c8e6..50883a6 100644 --- a/.clang-format +++ b/.clang-format @@ -133,7 +133,7 @@ ObjCBlockIndentWidth: 2 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true -PackConstructorInitializers: BinPack +PackConstructorInitializers: NextLine PenaltyBreakAssignment: 1000000 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp index c81a340..4905643 100644 --- a/include/fud_c_file.hpp +++ b/include/fud_c_file.hpp @@ -18,10 +18,10 @@ #ifndef FUD_C_FILE_HPP #define FUD_C_FILE_HPP +#include "fud_permissions.hpp" #include "fud_result.hpp" #include "fud_status.hpp" #include "fud_string.hpp" -#include "fud_permissions.hpp" #include #include @@ -155,7 +155,8 @@ class CFile { return self.m_file; } - FudStatus setPermissions(Permissions permissions) { + FudStatus setPermissions(Permissions permissions) + { auto& self = static_cast(*this); if (!self.isOpen()) { return FudStatus::OperationInvalid; @@ -383,14 +384,20 @@ class CFile { } // namespace detail +class CBinaryFile; +using CBinaryFileResult = Result; + class CBinaryFile : public detail::CFile { friend class CFile; public: - CBinaryFile(const String& filename, CFileMode mode); + static CBinaryFileResult make(const String& filename, CFileMode mode); + static CBinaryFileResult make(const String& filename, CFileMode mode, const char* extraFlags); - CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); + private: + CBinaryFile() = default; + public: CBinaryFile(const CBinaryFile& rhs) = delete; CBinaryFile(CBinaryFile&& rhs) noexcept; @@ -402,21 +409,26 @@ class CBinaryFile : public detail::CFile { CBinaryFile& operator=(CBinaryFile&& rhs) noexcept; private: - String m_filename; - String m_extraFlags{}; - String m_mode; - CFileMode m_modeFlags; + String m_filename{}; + String m_mode{}; + CFileMode m_modeFlags{}; FILE* m_file{nullptr}; }; +class CTextFile; +using CTextFileResult = Result; + class CTextFile : public detail::CFile { friend class CFile; public: - CTextFile(const String& filename, CFileMode mode); + static CTextFileResult make(const String& filename, CFileMode mode); + static CTextFileResult make(const String& filename, CFileMode mode, const char* extraFlags); - CTextFile(const String& filename, CFileMode mode, const String& extraFlags); + private: + CTextFile() = default; + public: CTextFile(const CTextFile& rhs) = delete; CTextFile(CTextFile&& rhs) noexcept; @@ -428,10 +440,9 @@ class CTextFile : public detail::CFile { CTextFile& operator=(CTextFile&& rhs) noexcept; private: - String m_filename; - String m_extraFlags{}; - String m_mode; - CFileMode m_modeFlags; + String m_filename{}; + String m_mode{}; + CFileMode m_modeFlags{}; FILE* m_file{nullptr}; }; diff --git a/include/fud_result.hpp b/include/fud_result.hpp index dae084a..3acf776 100644 --- a/include/fud_result.hpp +++ b/include/fud_result.hpp @@ -47,6 +47,26 @@ class [[nodiscard]] Result { return ResultType{std::move(error)}; } + template + static ResultType okay(const Result& okayRes) { + return ResultType{okayRes.getOkay()}; + } + + template + static ResultType okay(Result&& okayRes) { + return ResultType{okayRes.takeOkay()}; + } + + template + static ResultType error(const Result& errorRes) { + return ResultType{errorRes.getError()}; + } + + template + static ResultType error(Result&& errorRes) { + return ResultType{errorRes.takeError()}; + } + [[nodiscard]] constexpr bool isOkay() const { return (m_value.index() == 0); @@ -67,12 +87,12 @@ class [[nodiscard]] Result { return std::get(m_value); } - [[nodiscard]] const T&& getOkay() const&& + [[nodiscard]] T&& takeOkay() { return std::move(std::get(m_value)); } - [[nodiscard]] const E&& getError() const&& + [[nodiscard]] E&& takeError() { return std::move(std::get(m_value)); } diff --git a/include/fud_sqlite.hpp b/include/fud_sqlite.hpp index d879ed0..c56b4a1 100644 --- a/include/fud_sqlite.hpp +++ b/include/fud_sqlite.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace fud { @@ -34,11 +35,19 @@ enum class SqliteOpenMode : int ReadWriteCreate = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE }; +class SqliteErrorMsg; + +class SqliteDb; +using SqliteDbResult = Result; + class SqliteDb { + private: + constexpr SqliteDb() = default; + public: - SqliteDb(const String& name, SqliteOpenMode mode, int extraFlags = 0); + static SqliteDbResult make(const String& name, SqliteOpenMode mode, int extraFlags = 0); - SqliteDb(const char* name, SqliteOpenMode mode, int extraFlags = 0); + static SqliteDbResult make(const char* cStrName, SqliteOpenMode mode, int extraFlags = 0); SqliteDb(const SqliteDb&) = delete; @@ -58,7 +67,7 @@ class SqliteDb { const String& statement, int (*callback)(void*, int, char**, char**), void* context, - String* errorMessage); + std::unique_ptr errorMessage); constexpr int errorCode() { @@ -70,16 +79,15 @@ class SqliteDb { return m_dbHandle; } - // private methods private: - void initialize(); + // private methods + FudStatus initialize(); [[nodiscard]] int open(); Result prepare(const String& sql); // private data members - private: String m_name{}; bool m_nameValid{false}; @@ -133,6 +141,22 @@ class SqliteStatement { sqlite3_stmt* m_preparedStatement{nullptr}; }; +class SqliteErrorMsg { + public: + constexpr SqliteErrorMsg() = default; + explicit constexpr SqliteErrorMsg(char* errorMsg) : m_errorMsg{errorMsg} + { + } + SqliteErrorMsg(const SqliteErrorMsg&) = delete; + SqliteErrorMsg(SqliteErrorMsg&& rhs) noexcept; + ~SqliteErrorMsg(); + SqliteErrorMsg& operator=(const SqliteErrorMsg&) = delete; + SqliteErrorMsg& operator=(SqliteErrorMsg&& rhs) noexcept; + + private: + char* m_errorMsg{nullptr}; +}; + } // namespace fud #endif diff --git a/include/fud_string.hpp b/include/fud_string.hpp index 1939c7d..6880cb7 100644 --- a/include/fud_string.hpp +++ b/include/fud_string.hpp @@ -18,6 +18,8 @@ #ifndef FUD_STRING_HPP #define FUD_STRING_HPP +#include "fud_assert.hpp" +#include "fud_result.hpp" #include "fud_status.hpp" #include "fud_string_view.hpp" #include "fud_utf8.hpp" @@ -32,13 +34,77 @@ namespace fud { constexpr size_t SSO_BUF_LENGTH = 15; constexpr size_t SSO_BUF_SIZE = SSO_BUF_LENGTH + 1; +using StringResult = Result; + class String { public: - String() = default; - explicit String(const utf8* cString); - explicit String(const char* cString); + static StringResult makeFromCString(const char* cString); + + static StringResult makeFromUtf8(const utf8* utf8String); + + template + static StringResult makeFromCString(Strings... cStrings) + { + size_t totalLength = 0; + Array lengths{}; + Array strPointers{}; + size_t index = 0; + for (const auto* cStringItem: {cStrings...}) { + // for (size_t index = 0; index < strPointers.size(); ++index) { + // const auto* cString = strPointers[index]; + const char* cString = nullptr; + if constexpr (std::is_same_v) { + cString = cStringItem; + } else if constexpr (std::is_same_v) { + cString = reinterpret_cast(cStringItem); + } else { + static_assert(!std::is_same_v); + } + strPointers[index] = cString; + + auto lengthResult = cStringLength(cString); + if (lengthResult < 0 || lengthResult >= SSIZE_MAX) { + return StringResult::error(FudStatus::InvalidInput); + } + auto stringLength = static_cast(lengthResult); + if (SIZE_MAX - totalLength < stringLength) { + return StringResult::error(FudStatus::Failure); + } + totalLength += stringLength; + lengths[index] = stringLength; + index++; + } + + String output{}; + auto* data = output.m_buffer.data(); + output.m_length = totalLength; + if (output.m_length >= output.m_capacity) { + output.m_capacity = output.m_length + 1; + data = static_cast(fudAlloc(output.m_capacity)); + if (data == nullptr) { + return StringResult::error(FudStatus::AllocFailure); + } + output.m_data = data; + } + + size_t cumulativeLength = 0; + for (size_t idx = 0; idx < strPointers.size(); ++idx) { + const auto* cString = strPointers[idx]; + auto copyStatus = copyMem(data, output.m_capacity - cumulativeLength, cString, lengths[idx]); + fudAssert(copyStatus == FudStatus::Success); + cumulativeLength += lengths[idx]; + } + + auto terminateStatus = output.nullTerminate(); + fudAssert(terminateStatus == FudStatus::Success); + + return StringResult::okay(std::move(output)); + } + + String() noexcept = default; String(const String& rhs); String(String&& rhs) noexcept; + ~String(); String& operator=(const String& rhs); @@ -109,7 +175,8 @@ class String { return m_capacity - 1U - m_length; } - [[nodiscard]] inline StringView asView() const { + [[nodiscard]] inline StringView asView() const + { return StringView(*this); } @@ -127,9 +194,9 @@ class String { FudStatus append(StringView source); - [[nodiscard]] String catenate(const String& rhs) const; + [[nodiscard]] StringResult catenate(const String& rhs) const; - [[nodiscard]] String catenate(const char* rhs) const; + [[nodiscard]] StringResult catenate(const char* rhs) const; [[nodiscard]] bool compare(const String& rhs) const; diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp index 3961042..a63923a 100644 --- a/source/fud_c_file.cpp +++ b/source/fud_c_file.cpp @@ -19,28 +19,51 @@ namespace fud { -CBinaryFile::CBinaryFile(const String& filename, CFileMode mode) - : m_filename{filename}, - m_mode{CBinaryFileModeFromFlags(mode)}, - m_modeFlags{mode} +CBinaryFileResult CBinaryFile::make(const String& filename, CFileMode mode) { + if (!filename.valid()) { + return CBinaryFileResult::error(FudStatus::InvalidInput); + } + auto modeResult{String::makeFromCString(CBinaryFileModeFromFlags(mode))}; + if (modeResult.isError()) { + return CBinaryFileResult::error(modeResult); + } + + CBinaryFile binaryFile{}; + binaryFile.m_filename = filename; + binaryFile.m_mode = modeResult.takeOkay(); + binaryFile.m_modeFlags = mode; + return CBinaryFileResult::okay(std::move(binaryFile)); } -CBinaryFile::CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags) - : m_filename{filename}, - m_extraFlags{extraFlags}, - m_mode{String(CBinaryFileModeFromFlags(mode)).catenate(extraFlags)}, - m_modeFlags{mode} +CBinaryFileResult CBinaryFile::make(const String& filename, CFileMode mode, const char* extraFlags) { + if (!filename.valid()) { + return CBinaryFileResult::error(FudStatus::InvalidInput); + } + auto modeResult{String::makeFromCString(CBinaryFileModeFromFlags(mode), extraFlags)}; + if (modeResult.isError()) { + return CBinaryFileResult::error(modeResult); + } + + CBinaryFile binaryFile{}; + binaryFile.m_filename = filename; + binaryFile.m_mode = modeResult.takeOkay(); + binaryFile.m_modeFlags = mode; + return CBinaryFileResult::okay(std::move(binaryFile)); } CBinaryFile::CBinaryFile(CBinaryFile&& rhs) noexcept : - m_filename{std::move(rhs.m_filename)}, m_extraFlags{std::move(rhs.m_extraFlags)}, m_mode{std::move(rhs.m_mode)}, - m_modeFlags{rhs.m_modeFlags}, m_file{rhs.m_file} + m_filename{std::move(rhs.m_filename)}, + m_mode{std::move(rhs.m_mode)}, + m_modeFlags{rhs.m_modeFlags}, + m_file{rhs.m_file} { + rhs.m_file = nullptr; } -CBinaryFile::~CBinaryFile() { +CBinaryFile::~CBinaryFile() +{ close(); } @@ -49,36 +72,60 @@ CBinaryFile& CBinaryFile::operator=(CBinaryFile&& rhs) noexcept close(); m_filename = std::move(rhs.m_filename); - m_extraFlags = std::move(rhs.m_extraFlags); m_mode = std::move(rhs.m_mode); m_modeFlags = rhs.m_modeFlags; m_file = rhs.m_file; + rhs.m_file = nullptr; + return *this; } -CTextFile::CTextFile(const String& filename, CFileMode mode) - : m_filename{filename}, - m_mode{CTextFileModeFromFlags(mode)}, - m_modeFlags{mode} +CTextFileResult CTextFile::make(const String& filename, CFileMode mode) { + if (!filename.valid()) { + return CTextFileResult::error(FudStatus::InvalidInput); + } + auto modeResult{String::makeFromCString(CBinaryFileModeFromFlags(mode))}; + if (modeResult.isError()) { + return CTextFileResult::error(modeResult); + } + + CTextFile textFile{}; + textFile.m_filename = filename; + textFile.m_mode = modeResult.takeOkay(); + textFile.m_modeFlags = mode; + return CTextFileResult::okay(std::move(textFile)); } -CTextFile::CTextFile(const String& filename, CFileMode mode, const String& extraFlags) - : m_filename{filename}, - m_extraFlags{extraFlags}, - m_mode{String(CTextFileModeFromFlags(mode)).catenate(extraFlags)}, - m_modeFlags{mode} +CTextFileResult CTextFile::make(const String& filename, CFileMode mode, const char* extraFlags) { + if (!filename.valid()) { + return CTextFileResult::error(FudStatus::InvalidInput); + } + auto modeResult{String::makeFromCString(CTextFileModeFromFlags(mode), extraFlags)}; + if (modeResult.isError()) { + return CTextFileResult::error(modeResult); + } + + CTextFile textFile{}; + textFile.m_filename = filename; + textFile.m_mode = modeResult.takeOkay(); + textFile.m_modeFlags = mode; + return CTextFileResult::okay(std::move(textFile)); } CTextFile::CTextFile(CTextFile&& rhs) noexcept : - m_filename{std::move(rhs.m_filename)}, m_extraFlags{std::move(rhs.m_extraFlags)}, m_mode{std::move(rhs.m_mode)}, - m_modeFlags{rhs.m_modeFlags}, m_file{rhs.m_file} + m_filename{std::move(rhs.m_filename)}, + m_mode{std::move(rhs.m_mode)}, + m_modeFlags{rhs.m_modeFlags}, + m_file{rhs.m_file} { + rhs.m_file = nullptr; } -CTextFile::~CTextFile() { +CTextFile::~CTextFile() +{ close(); } @@ -87,11 +134,12 @@ CTextFile& CTextFile::operator=(CTextFile&& rhs) noexcept close(); m_filename = std::move(rhs.m_filename); - m_extraFlags = std::move(rhs.m_extraFlags); m_mode = std::move(rhs.m_mode); m_modeFlags = rhs.m_modeFlags; m_file = rhs.m_file; + rhs.m_file = nullptr; + return *this; } diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp index 1fcebe1..1697692 100644 --- a/source/fud_directory.cpp +++ b/source/fud_directory.cpp @@ -199,13 +199,16 @@ Result, FudStatus> Directory::getNextEntry() return RetType::error(m_status); } - auto entryName = String{entryNameCString}; - auto retValue = DirectoryEntry::fromStat(entryName, sBuffer); + auto entryNameResult = String::makeFromCString(entryNameCString); + if (entryNameResult.isError()) { + return RetType::error(entryNameResult); + } + auto retValue = DirectoryEntry::fromStat(entryNameResult.getOkay(), sBuffer); if (retValue.isOkay()) { m_errorCode = 0; m_status = FudStatus::Success; - return RetType::okay(std::move(retValue.getOkay())); + return RetType::okay(retValue.takeOkay()); } m_status = retValue.getError(); diff --git a/source/fud_sqlite.cpp b/source/fud_sqlite.cpp index 3e39474..449be8b 100644 --- a/source/fud_sqlite.cpp +++ b/source/fud_sqlite.cpp @@ -19,21 +19,43 @@ namespace fud { -SqliteDb::SqliteDb(const String& name, SqliteOpenMode mode, int extraFlags) : - m_name{name}, m_mode{mode}, m_extraFlags{extraFlags} +SqliteDbResult SqliteDb::make(const String& name, SqliteOpenMode mode, int extraFlags) { - initialize(); + SqliteDb sqlDb{}; + sqlDb.m_name = name; + sqlDb.m_mode = mode; + sqlDb.m_extraFlags = extraFlags; + auto status = sqlDb.initialize(); + if (status != FudStatus::Success) { + return SqliteDbResult::error(status); + } + return SqliteDbResult::okay(std::move(sqlDb)); } -SqliteDb::SqliteDb(const char* name, SqliteOpenMode mode, int extraFlags) : - m_name{name}, m_mode{mode}, m_extraFlags{extraFlags} +SqliteDbResult SqliteDb::make(const char* cStrName, SqliteOpenMode mode, int extraFlags) { - initialize(); + auto nameResult = String::makeFromCString(cStrName); + if (nameResult.isError()) { + return SqliteDbResult::error(nameResult); + } + SqliteDb sqlDb{}; + sqlDb.m_name = nameResult.takeOkay(); + sqlDb.m_mode = mode; + sqlDb.m_extraFlags = extraFlags; + auto status = sqlDb.initialize(); + if (status != FudStatus::Success) { + return SqliteDbResult::error(status); + } + return SqliteDbResult::okay(std::move(sqlDb)); } SqliteDb::SqliteDb(SqliteDb&& rhs) noexcept : - m_name{std::move(rhs.m_name)}, m_nameValid{rhs.m_nameValid}, m_dbHandle{rhs.m_dbHandle}, - m_errorCode{rhs.m_errorCode}, m_mode{rhs.m_mode}, m_extraFlags{rhs.m_extraFlags} + m_name{std::move(rhs.m_name)}, + m_nameValid{rhs.m_nameValid}, + m_dbHandle{rhs.m_dbHandle}, + m_errorCode{rhs.m_errorCode}, + m_mode{rhs.m_mode}, + m_extraFlags{rhs.m_extraFlags} { rhs.m_nameValid = false; rhs.m_dbHandle = nullptr; @@ -85,7 +107,7 @@ FudStatus SqliteDb::exec( const String& statement, int (*callback)(void*, int, char**, char**), void* context, - String* errorMessage) + std::unique_ptr errorMessage) { if (!valid()) { return FudStatus::ObjectInvalid; @@ -101,29 +123,29 @@ FudStatus SqliteDb::exec( errorMsgPtrAddress = &errorMsgPtr; } - m_errorCode = sqlite3_exec( - m_dbHandle, - statement.c_str(), - callback, - context, - errorMsgPtrAddress); + m_errorCode = sqlite3_exec(m_dbHandle, statement.c_str(), callback, context, errorMsgPtrAddress); if (errorMessage != nullptr) { - *errorMessage = String{errorMsgPtr}; + errorMessage = std::make_unique(errorMsgPtr); } return m_errorCode == SQLITE_OK ? FudStatus::Success : FudStatus::Failure; } -void SqliteDb::initialize() +FudStatus SqliteDb::initialize() { m_nameValid = m_name.utf8Valid(); if (!m_nameValid) { - return; + return FudStatus::InvalidInput; } m_errorCode = open(); + if (m_errorCode != SQLITE_OK) { + return FudStatus::Failure; + } + + return FudStatus::Success; } int SqliteDb::open() @@ -165,7 +187,10 @@ SqliteStatement::SqliteStatement(const SqliteDb& sqliteDb, const String& input) } SqliteStatement::SqliteStatement(SqliteStatement&& rhs) noexcept : - m_input{std::move(rhs.m_input)}, m_tail{rhs.m_tail}, m_status{rhs.m_status}, m_errorCode{rhs.m_errorCode}, + m_input{std::move(rhs.m_input)}, + m_tail{rhs.m_tail}, + m_status{rhs.m_status}, + m_errorCode{rhs.m_errorCode}, m_preparedStatement{rhs.m_preparedStatement} { rhs.m_tail = nullptr; @@ -213,4 +238,28 @@ FudStatus SqliteStatement::reset() return FudStatus::Success; } +SqliteErrorMsg::SqliteErrorMsg(SqliteErrorMsg&& rhs) noexcept : m_errorMsg{rhs.m_errorMsg} +{ + rhs.m_errorMsg = nullptr; +} + +SqliteErrorMsg& SqliteErrorMsg::operator=(SqliteErrorMsg&& rhs) noexcept { + if (m_errorMsg != nullptr) { + sqlite3_free(m_errorMsg); + m_errorMsg = nullptr; + } + m_errorMsg = rhs.m_errorMsg; + rhs.m_errorMsg = nullptr; + + return *this; +} + +SqliteErrorMsg::~SqliteErrorMsg() +{ + if (m_errorMsg != nullptr) { + sqlite3_free(m_errorMsg); + m_errorMsg = nullptr; + } +} + } // namespace fud diff --git a/source/fud_string.cpp b/source/fud_string.cpp index 3a9aca0..82a9fe5 100644 --- a/source/fud_string.cpp +++ b/source/fud_string.cpp @@ -46,30 +46,37 @@ ssize_t cStringLength(const char* str, size_t maxLength) return size; } -// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) -String::String(const utf8* cString) : String(reinterpret_cast(cString)) -{ -} -// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) - -String::String(const char* cString) +StringResult String::makeFromCString(const char* cString) { auto lenResult = cStringLength(cString); if (lenResult < 0 || lenResult >= SSIZE_MAX) { - m_length = 1; - m_capacity = 0; - } else if (static_cast(lenResult) < SSO_BUF_SIZE) { - m_length = static_cast(lenResult); - fudAssert(copyMem(m_buffer.data(), m_buffer.size(), cString, m_length) == FudStatus::Success); - fudAssert(nullTerminate() == FudStatus::Success); - } else { - m_length = static_cast(lenResult); - m_capacity = m_length + 1; - m_data = static_cast(fudAlloc(m_capacity)); - fudAssert(m_data != nullptr); - fudAssert(copyMem(m_data, m_capacity, cString, m_length) == FudStatus::Success); - fudAssert(nullTerminate() == FudStatus::Success); + return StringResult::error(FudStatus::InvalidInput); + } + + String output{}; + 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; + data = static_cast(fudAlloc(output.m_capacity)); + if (data == nullptr) { + return StringResult::error(FudStatus::AllocFailure); + } + output.m_data = data; } + auto copyStatus = copyMem(data, output.m_capacity, cString, output.m_length); + fudAssert(copyStatus == FudStatus::Success); + auto terminateStatus = output.nullTerminate(); + fudAssert(terminateStatus == FudStatus::Success); + + return StringResult::okay(std::move(output)); +} + +StringResult String::makeFromUtf8(const utf8* utf8String) +{ + // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) + return makeFromCString(reinterpret_cast(utf8String)); + // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) } String::String(const String& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity} @@ -364,7 +371,7 @@ FudStatus String::append(StringView source) return FudStatus::OperationInvalid; } const size_t newSize = newLength + 1; // cppcheck-suppress knownConditionTrueFalse - if (newSize < newLength) { // cppcheck-suppress knownConditionTrueFalse + if (newSize < newLength) { // cppcheck-suppress knownConditionTrueFalse return FudStatus::OperationInvalid; } if (newSize >= m_capacity) { @@ -386,22 +393,60 @@ FudStatus String::append(StringView source) return status; } -String String::catenate(const char* rhs) const +StringResult String::catenate(const char* rhs) const { - String rhsString{rhs}; - return catenate(rhsString); -} + if (!valid()) { + return StringResult::error(FudStatus::InvalidInput); + } + + auto lenResult = cStringLength(rhs); + if (lenResult < 0 || lenResult >= SSIZE_MAX) { + return StringResult::error(FudStatus::InvalidInput); + } + size_t rhsLength = static_cast(lenResult); -String String::catenate(const String& rhs) const -{ String output{}; - output.m_length = 1; - output.m_capacity = 0; + if (SIZE_MAX - m_length < rhsLength) + { + return StringResult::error(FudStatus::Failure); + } + + output.m_length = m_length + rhsLength; + auto* destPtr = output.m_buffer.data(); + if (output.m_length >= output.m_capacity) { + output.m_capacity = output.m_length + 1; + destPtr = static_cast(fudAlloc(output.m_capacity)); + if (destPtr == nullptr) { + return StringResult::error(FudStatus::AllocFailure); + } + output.m_data = destPtr; + } + + auto status = copyMem(destPtr, m_capacity, data(), length()); + fudAssert(status == FudStatus::Success); + + status = copyMem(destPtr + 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)); +} + +StringResult String::catenate(const String& rhs) const +{ if (!valid() || !rhs.valid()) { - return output; + return StringResult::error(FudStatus::InvalidInput); } + if (SIZE_MAX - m_length < rhs.length()) + { + return StringResult::error(FudStatus::Failure); + } + + String output{}; output.m_length = m_length + rhs.length(); output.m_capacity = output.m_length + 1; if (output.m_capacity < SSO_BUF_SIZE) { @@ -414,12 +459,14 @@ String String::catenate(const String& rhs) const auto* destPtr = output.data(); auto status = copyMem(destPtr, m_capacity, data(), length()); fudAssert(status == FudStatus::Success); + status = copyMem(destPtr + length(), output.m_capacity - length(), rhs.data(), rhs.length()); fudAssert(status == FudStatus::Success); - static_cast(status); - fudAssert(output.nullTerminate() == FudStatus::Success); - return output; + auto terminateStatus = output.nullTerminate(); + fudAssert(terminateStatus == FudStatus::Success); + + return StringResult::okay(std::move(output)); } bool String::compare(const String& rhs) const diff --git a/source/libfud.cpp b/source/libfud.cpp index 8c962ba..cec544d 100644 --- a/source/libfud.cpp +++ b/source/libfud.cpp @@ -50,12 +50,12 @@ Result getEnv(const char* name) return RetType::error(FudStatus::NotFound); } - String envVar{resultString}; - if (!envVar.valid()) { - return RetType::error(FudStatus::Failure); + auto envVarResult = String::makeFromCString(resultString); + if (envVarResult.isError()) { + return RetType::error(envVarResult); } - return RetType::okay(std::move(envVar)); + return RetType::okay(envVarResult); } } // namespace fud diff --git a/test/test_directory.cpp b/test/test_directory.cpp index ff10c2d..6597195 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -51,7 +51,7 @@ FudStatus removeRecursive(const String& path) if (path.length() < 5) { return FudStatus::InvalidInput; } - const String prefix{"/tmp/"}; + auto prefix{String::makeFromCString("/tmp/").takeOkay()}; auto diffResult = compareMem(path.data(), path.length(), prefix.data(), prefix.length()); if (diffResult.isError()) { return FudStatus::InvalidInput; @@ -75,13 +75,12 @@ FudStatus removeRecursive(const String& path) TEST(FudDirectory, Basic) { - const String testDirName{"/tmp/fud_directory_test"}; + const auto testDirName{String::makeFromCString("/tmp/fud_directory_test").takeOkay()}; ASSERT_TRUE(testDirName.utf8Valid()); constexpr mode_t pathMode = 0777; const Array files{ - String{"file1"}, - String{"file2"}, - }; + String::makeFromCString("file1").takeOkay(), + String::makeFromCString("file2").takeOkay()}; ASSERT_TRUE(files[0].utf8Valid()); ASSERT_TRUE(files[1].utf8Valid()); @@ -94,12 +93,14 @@ TEST(FudDirectory, Basic) return; } - const String testDirNamePrefix = testDirName.catenate("/"); + const String testDirNamePrefix{testDirName.catenate("/").takeOkay()}; ASSERT_TRUE(testDirNamePrefix.utf8Valid()); for (const auto& fnameBase : files) { - const auto fname = testDirNamePrefix.catenate(fnameBase); + const auto fname{testDirNamePrefix.catenate(fnameBase).takeOkay()}; ASSERT_TRUE(fname.utf8Valid()); - CBinaryFile file{fname, CFileMode::ReadWriteTruncate}; + auto fileResult{CBinaryFile::make(fname, CFileMode::ReadWriteTruncate)}; + ASSERT_TRUE(fileResult.isOkay()); + CBinaryFile file{std::move(fileResult).takeOkay()}; ASSERT_EQ(file.open(), FudStatus::Success); Array data{"test"}; WriteResult expected{data.size(), FudStatus::Success}; @@ -113,8 +114,8 @@ TEST(FudDirectory, Basic) ASSERT_EQ(directory.errorCode(), 0); const Array expectedFiles{ - DirectoryEntry{String{"."}, 0, 0, 2, 0, DirectoryEntryType::Directory}, - DirectoryEntry{String{".."}, 0, 0, 1, 0, DirectoryEntryType::Directory}, + DirectoryEntry{String::makeFromCString(".").takeOkay(), 0, 0, 2, 0, DirectoryEntryType::Directory}, + DirectoryEntry{String::makeFromCString("..").takeOkay(), 0, 0, 1, 0, DirectoryEntryType::Directory}, DirectoryEntry{files[0], 0, files[0].size(), 1, 0, DirectoryEntryType::RegularFile}, DirectoryEntry{files[1], 0, files[1].size(), 1, 0, DirectoryEntryType::RegularFile}, }; @@ -131,7 +132,9 @@ TEST(FudDirectory, Basic) const auto* expected = std::find_if( expectedFiles.begin(), expectedFiles.end(), - [&dirEntry](const DirectoryEntry& entry) { return entry.name.compare(dirEntry.name) && entry.entryType == dirEntry.entryType; }); + [&dirEntry](const DirectoryEntry& entry) { + return entry.name.compare(dirEntry.name) && entry.entryType == dirEntry.entryType; + }); EXPECT_NE(expected, nullptr); EXPECT_NE(expected, expectedFiles.end()); } diff --git a/test/test_sqlite.cpp b/test/test_sqlite.cpp index 8349324..2bb91a0 100644 --- a/test/test_sqlite.cpp +++ b/test/test_sqlite.cpp @@ -17,7 +17,6 @@ #include "fud_sqlite.hpp" #include "fud_string.hpp" -#include "test_common.hpp" #include "gtest/gtest.h" #include @@ -27,23 +26,27 @@ namespace fud { TEST(FudSqlite, Basic) { - const String testName{"test.db.sqlite"}; + auto testNameResult = String::makeFromCString("./test.db.sqlite"); + ASSERT_TRUE(testNameResult.isOkay()); + auto testName{std::move(testNameResult).takeOkay()}; auto result = unlink(testName.c_str()); if (result != 0) { ASSERT_EQ(errno, ENOENT); } - SqliteDb sqliteDb{testName, SqliteOpenMode::ReadOnly}; - ASSERT_FALSE(sqliteDb.valid()); - ASSERT_NE(sqliteDb.errorCode(), SQLITE_OK); + auto sqliteDbResult{SqliteDb::make(testName, SqliteOpenMode::ReadOnly)}; + ASSERT_FALSE(sqliteDbResult.isOkay()); - sqliteDb = SqliteDb{testName, SqliteOpenMode::ReadWrite}; - ASSERT_FALSE(sqliteDb.valid()); - ASSERT_NE(sqliteDb.errorCode(), SQLITE_OK); + sqliteDbResult = SqliteDb::make(testName, SqliteOpenMode::ReadWrite); + ASSERT_FALSE(sqliteDbResult.isOkay()); - sqliteDb = SqliteDb{testName, SqliteOpenMode::ReadWriteCreate}; + sqliteDbResult = SqliteDb::make(testName, SqliteOpenMode::ReadWriteCreate); + ASSERT_TRUE(sqliteDbResult.isOkay()); + + auto sqliteDb{std::move(sqliteDbResult).takeOkay()}; ASSERT_TRUE(sqliteDb.valid()); ASSERT_EQ(sqliteDb.errorCode(), SQLITE_OK); + } } // namespace fud diff --git a/test/test_string.cpp b/test/test_string.cpp index 15646bd..6d963c4 100644 --- a/test/test_string.cpp +++ b/test/test_string.cpp @@ -37,13 +37,20 @@ TEST(FudString, BasicStringOps) const Array invalid{0xFF, 0x00}; ASSERT_FALSE(Ascii::valid(invalid[0])); const Array invalid2{0xFF, 0x00}; - String fudString{invalid2.data()}; + + auto stringResult = String::makeFromCString(invalid2.data()); + ASSERT_TRUE(stringResult.isOkay()); + + String fudString{stringResult.getOkay()}; ASSERT_EQ(fudString.length(), 1); ASSERT_EQ(fudString.data()[0], invalid[0]); ASSERT_FALSE(Ascii::valid(fudString.data()[0])); ASSERT_FALSE(fudString.utf8Valid()); - fudString = String{"TEST"}; + stringResult = String::makeFromCString("TEST"); + ASSERT_TRUE(stringResult.isOkay()); + fudString = stringResult.getOkay(); + ASSERT_TRUE(fudString.utf8Valid()); StringView view1{}; @@ -56,7 +63,9 @@ TEST(FudString, BasicStringOps) TEST(FudString, HeapAlloc) { constexpr const char filenameLiteral[] = "Amazing Saga Volume 01/000.jpg"; - String filename{filenameLiteral}; + auto filenameResult{String::makeFromCString(filenameLiteral)}; + ASSERT_TRUE(filenameResult.isOkay()); + auto filename{filenameResult.getOkay()}; ASSERT_EQ(filename.length(), sizeof(filenameLiteral) - 1); } @@ -102,7 +111,7 @@ TEST(TestFudString, StringBuffer) Array data1{}; bufferResult = FixedStringBuffer<1>::fromArray(data1); ASSERT_TRUE(bufferResult.isOkay()); - FixedStringBuffer<1> buffer1{std::move(bufferResult.getOkay())}; + FixedStringBuffer<1> buffer1{std::move(bufferResult).getOkay()}; ASSERT_EQ(buffer1.m_string.m_data[0], '\0'); ASSERT_EQ(buffer1.m_string.m_length, 0); ASSERT_EQ(buffer1.m_string.m_size, 1); @@ -137,7 +146,7 @@ TEST(TestFudString, StringBuffer) Array data2{"test"}; auto bufferResult2{FixedStringBuffer::fromArray(data2)}; ASSERT_TRUE(bufferResult2.isOkay()); - FixedStringBuffer bufferTest{std::move(bufferResult2.getOkay())}; + FixedStringBuffer bufferTest{std::move(bufferResult2).getOkay()}; ASSERT_EQ(bufferTest.m_string.m_data[0], 't'); ASSERT_EQ(bufferTest.m_string.m_data[4], '\0'); ASSERT_EQ(bufferTest.m_string.m_length, data2.size() - 1); -- cgit v1.2.3