summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-15 20:56:26 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-15 20:56:26 -0500
commitf3ac764684c64fbdd2094853a80b23e570cd5d9c (patch)
tree3b7f64480a1247455d28e23b6bb5ff63303c9170
parent71976e927cca43b970cb659c03fd6908c352ea3d (diff)
Conver to using static constructors for string, sqlite, files.
-rw-r--r--.clang-format2
-rw-r--r--include/fud_c_file.hpp39
-rw-r--r--include/fud_result.hpp24
-rw-r--r--include/fud_sqlite.hpp36
-rw-r--r--include/fud_string.hpp79
-rw-r--r--source/fud_c_file.cpp100
-rw-r--r--source/fud_directory.cpp9
-rw-r--r--source/fud_sqlite.cpp87
-rw-r--r--source/fud_string.cpp113
-rw-r--r--source/libfud.cpp8
-rw-r--r--test/test_directory.cpp25
-rw-r--r--test/test_sqlite.cpp21
-rw-r--r--test/test_string.cpp19
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 <cstdint>
#include <cstdio>
@@ -155,7 +155,8 @@ class CFile {
return self.m_file;
}
- FudStatus setPermissions(Permissions permissions) {
+ FudStatus setPermissions(Permissions permissions)
+ {
auto& self = static_cast<Derived&>(*this);
if (!self.isOpen()) {
return FudStatus::OperationInvalid;
@@ -383,14 +384,20 @@ class CFile {
} // namespace detail
+class CBinaryFile;
+using CBinaryFileResult = Result<CBinaryFile, FudStatus>;
+
class CBinaryFile : public detail::CFile<CBinaryFile> {
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> {
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<CTextFile, FudStatus>;
+
class CTextFile : public detail::CFile<CTextFile> {
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> {
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 <typename F>
+ static ResultType okay(const Result<T, F>& okayRes) {
+ return ResultType{okayRes.getOkay()};
+ }
+
+ template <typename F>
+ static ResultType okay(Result<T, F>&& okayRes) {
+ return ResultType{okayRes.takeOkay()};
+ }
+
+ template <typename U>
+ static ResultType error(const Result<U, E>& errorRes) {
+ return ResultType{errorRes.getError()};
+ }
+
+ template <typename U>
+ static ResultType error(Result<U, E>&& 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<E>(m_value);
}
- [[nodiscard]] const T&& getOkay() const&&
+ [[nodiscard]] T&& takeOkay()
{
return std::move(std::get<T>(m_value));
}
- [[nodiscard]] const E&& getError() const&&
+ [[nodiscard]] E&& takeError()
{
return std::move(std::get<E>(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 <fud_result.hpp>
#include <fud_status.hpp>
#include <fud_string.hpp>
+#include <memory>
#include <sqlite3.h>
namespace fud {
@@ -34,11 +35,19 @@ enum class SqliteOpenMode : int
ReadWriteCreate = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
};
+class SqliteErrorMsg;
+
+class SqliteDb;
+using SqliteDbResult = Result<SqliteDb, FudStatus>;
+
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<SqliteErrorMsg> 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<SqliteStatement, FudStatus> 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<String, FudStatus>;
+
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 <typename... Strings>
+ static StringResult makeFromCString(Strings... cStrings)
+ {
+ size_t totalLength = 0;
+ Array<size_t, sizeof...(cStrings)> lengths{};
+ Array<const char*, sizeof...(cStrings)> 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<decltype(cStringItem), const char*>) {
+ cString = cStringItem;
+ } else if constexpr (std::is_same_v<decltype(cStringItem), const utf8*>) {
+ cString = reinterpret_cast<const char*>(cStringItem);
+ } else {
+ static_assert(!std::is_same_v<decltype(cStringItem), const char*>);
+ }
+ strPointers[index] = cString;
+
+ auto lengthResult = cStringLength(cString);
+ if (lengthResult < 0 || lengthResult >= SSIZE_MAX) {
+ return StringResult::error(FudStatus::InvalidInput);
+ }
+ auto stringLength = static_cast<size_t>(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<utf8*>(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<std::optional<DirectoryEntry>, 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<SqliteErrorMsg> 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<SqliteErrorMsg>(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<const char*>(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<size_t>(lenResult) < SSO_BUF_SIZE) {
- m_length = static_cast<size_t>(lenResult);
- fudAssert(copyMem(m_buffer.data(), m_buffer.size(), cString, m_length) == FudStatus::Success);
- fudAssert(nullTerminate() == FudStatus::Success);
- } else {
- m_length = static_cast<size_t>(lenResult);
- m_capacity = m_length + 1;
- m_data = static_cast<utf8*>(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<size_t>(lenResult);
+ if (output.m_length >= output.m_capacity) {
+ output.m_capacity = output.m_length + 1;
+ data = static_cast<utf8*>(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<const char*>(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<size_t>(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<utf8*>(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<void>(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<String, FudStatus> 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<String, 2> 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<utf8, 5> data{"test"};
WriteResult expected{data.size(), FudStatus::Success};
@@ -113,8 +114,8 @@ TEST(FudDirectory, Basic)
ASSERT_EQ(directory.errorCode(), 0);
const Array<DirectoryEntry, 4> 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 <cerrno>
@@ -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<utf8, 2> invalid{0xFF, 0x00};
ASSERT_FALSE(Ascii::valid(invalid[0]));
const Array<utf8, 2> 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<uint8_t, 1> 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<uint8_t, sizeof("test")> data2{"test"};
auto bufferResult2{FixedStringBuffer<data2.size()>::fromArray(data2)};
ASSERT_TRUE(bufferResult2.isOkay());
- FixedStringBuffer<data2.size()> bufferTest{std::move(bufferResult2.getOkay())};
+ FixedStringBuffer<data2.size()> 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);