summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/fud_hash.cpp53
-rw-r--r--source/fud_sqlite.cpp175
-rw-r--r--source/fud_string.cpp11
3 files changed, 198 insertions, 41 deletions
diff --git a/source/fud_hash.cpp b/source/fud_hash.cpp
new file mode 100644
index 0000000..faa62ff
--- /dev/null
+++ b/source/fud_hash.cpp
@@ -0,0 +1,53 @@
+/*
+ * libfud
+ * Copyright 2025 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_hash.hpp"
+
+namespace fud::detail {
+
+size_t djb2(const utf8* data, size_t length)
+{
+ /* Allegedly djb chose 5381 after testing showed fewer collisions and better avalanching: see
+ * https://stackoverflow.com/questions/10696223/reason-for-the-number-5381-in-the-djb-hash-function */
+ constexpr size_t hashInit = 5381;
+ constexpr size_t mulBy = 33;
+ size_t hash = hashInit;
+ if (data == nullptr) {
+ return hash;
+ }
+
+ for (size_t index = 0; index < length; ++index) {
+ hash = (hash * mulBy) ^ data[index];
+ }
+
+ return hash;
+}
+
+
+size_t DefaultHash<String>::operator()(const String& value, size_t seed) const
+{
+ static_cast<void>(seed);
+ return djb2(value.data(), value.length());
+}
+
+size_t DefaultHash<StringView>::operator()(const StringView& value, size_t seed) const
+{
+ static_cast<void>(seed);
+ return djb2(value.data(), value.length());
+}
+
+}
diff --git a/source/fud_sqlite.cpp b/source/fud_sqlite.cpp
index 4a7187f..fccbc1e 100644
--- a/source/fud_sqlite.cpp
+++ b/source/fud_sqlite.cpp
@@ -37,7 +37,7 @@ SqliteDbResult SqliteDb::make(StringView name, SqliteOpenMode mode, int extraFla
return SqliteDbResult::error(nameResult.takeError());
}
sqlDb.m_name = nameResult.takeOkay();
- return finishMake(mode, extraFlags, std::move(sqlDb));
+ return finishMake(mode, extraFlags, std::move(sqlDb));
}
SqliteDbResult SqliteDb::make(const char* cStrName, SqliteOpenMode mode, int extraFlags)
@@ -48,7 +48,7 @@ SqliteDbResult SqliteDb::make(const char* cStrName, SqliteOpenMode mode, int ext
return SqliteDbResult::error(nameResult);
}
sqlDb.m_name = nameResult.takeOkay();
- return finishMake(mode, extraFlags, std::move(sqlDb));
+ return finishMake(mode, extraFlags, std::move(sqlDb));
}
SqliteDbResult SqliteDb::finishMake(SqliteOpenMode& mode, int& extraFlags, SqliteDb&& sqlDb)
@@ -104,7 +104,7 @@ SqliteDb& SqliteDb::operator=(SqliteDb&& rhs) noexcept
bool SqliteDb::valid() const
{
- return m_nameValid && m_dbHandle != nullptr && m_errorCode == SQLITE_OK;
+ return m_nameValid && m_dbHandle != nullptr && (m_errorCode == SQLITE_OK || m_errorCode == SQLITE_DONE);
}
bool SqliteDb::revalidate()
@@ -116,11 +116,34 @@ bool SqliteDb::revalidate()
return false;
}
+FudStatus SqliteDb::initialize()
+{
+ m_nameValid = m_name.utf8Valid();
+
+ if (!m_nameValid) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ m_errorCode = open();
+ if (m_errorCode != SQLITE_OK) {
+ return FudStatus::Failure;
+ }
+
+ return FudStatus::Success;
+}
+
+int SqliteDb::open()
+{
+ // use default vfs
+ return sqlite3_open_v2(m_name.c_str(), &m_dbHandle, static_cast<int>(m_mode) | m_extraFlags, nullptr);
+}
+
FudStatus SqliteDb::exec(
const String& statement,
int (*callback)(void*, int, char**, char**),
void* context,
- std::unique_ptr<SqliteErrorMsg> errorMessage)
+ // NOLINTNEXTLINE(performance-copy-param-value)
+ Option<SqliteErrorMsg&>& errorMessage)
{
if (!valid()) {
return FudStatus::ObjectInvalid;
@@ -131,53 +154,135 @@ FudStatus SqliteDb::exec(
}
char* errorMsgPtr = nullptr;
- char** errorMsgPtrAddress = nullptr;
- if (errorMessage != nullptr) {
- errorMsgPtrAddress = &errorMsgPtr;
- }
+ char** errorMsgPtrAddress = errorMessage.isNone() ? nullptr : &errorMsgPtr;
m_errorCode = sqlite3_exec(m_dbHandle, statement.c_str(), callback, context, errorMsgPtrAddress);
- if (errorMessage != nullptr) {
- errorMessage = std::make_unique<SqliteErrorMsg>(errorMsgPtr);
+ if (errorMessage.hasValue()) {
+ errorMessage.value().setMessage(errorMsgPtr);
}
return m_errorCode == SQLITE_OK ? FudStatus::Success : FudStatus::Failure;
}
-FudStatus SqliteDb::initialize()
+FudStatus SqliteDb::step(SqliteStatement& statement)
{
- m_nameValid = m_name.utf8Valid();
-
- if (!m_nameValid) {
- return FudStatus::ArgumentInvalid;
+ if (!statement.valid()) {
+ m_errorCode = SQLITE_MISUSE;
+ } else {
+ m_errorCode = sqlite3_step(statement.statement());
}
- m_errorCode = open();
- if (m_errorCode != SQLITE_OK) {
+ switch (m_errorCode) {
+ case SQLITE_OK:
+ case SQLITE_ROW:
+ return FudStatus::Success;
+ case SQLITE_DONE:
+ return FudStatus::NotFound;
+ default:
return FudStatus::Failure;
}
+}
- return FudStatus::Success;
+Result<int32_t, FudStatus> SqliteDb::columnInt32(SqliteStatement& statement, int index)
+{
+ if (!statement.valid() || statement.status() != FudStatus::Success) {
+ m_errorCode = statement.errorCode();
+ return Error{statement.status()};
+ }
+
+ if (index < 0) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+ if (sqlite3_column_type(statement.statement(), index) != SQLITE_INTEGER) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+
+ auto output = sqlite3_column_int(statement.statement(), index);
+
+ return Okay{output};
}
-int SqliteDb::open()
+Result<int64_t, FudStatus> SqliteDb::columnInt64(SqliteStatement& statement, int index)
{
- // use default vfs
- return sqlite3_open_v2(m_name.c_str(), &m_dbHandle, static_cast<int>(m_mode) | m_extraFlags, nullptr);
+ if (!statement.valid() || statement.status() != FudStatus::Success) {
+ m_errorCode = statement.errorCode();
+ return Error{statement.status()};
+ }
+
+ if (index < 0) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+ if (sqlite3_column_type(statement.statement(), index) != SQLITE_INTEGER) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+
+ auto output = sqlite3_column_int64(statement.statement(), index);
+ static_assert(std::is_convertible_v<decltype(output), int64_t>);
+
+ return Okay{static_cast<int64_t>(output)};
+}
+
+Result<double, FudStatus> SqliteDb::columnDouble(SqliteStatement& statement, int index)
+{
+ if (!statement.valid() || statement.status() != FudStatus::Success) {
+ m_errorCode = statement.errorCode();
+ return Error{statement.status()};
+ }
+
+ if (index < 0) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+ if (sqlite3_column_type(statement.statement(), index) != SQLITE_FLOAT) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+
+ auto output = sqlite3_column_double(statement.statement(), index);
+
+ return Okay{output};
+}
+
+Result<StringView, FudStatus> SqliteDb::columnText(SqliteStatement& statement, int index)
+{
+ if (!statement.valid() || statement.status() != FudStatus::Success) {
+ m_errorCode = statement.errorCode();
+ return Error{statement.status()};
+ }
+
+ if (index < 0) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+
+ StringView output{};
+ if (sqlite3_column_type(statement.statement(), index) != SQLITE_TEXT) {
+ return Error{FudStatus::ArgumentInvalid};
+ }
+
+ auto outputLength = sqlite3_column_bytes(statement.statement(), index);
+ if (outputLength < 0) {
+ return Error{FudStatus::Failure};
+ }
+ output.m_length = static_cast<size_t>(outputLength);
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ output.m_data = reinterpret_cast<const utf8*>(sqlite3_column_text(statement.statement(), index));
+ if (output.m_data == nullptr && output.m_length != 0) {
+ return Error{FudStatus::Failure};
+ }
+
+ return Okay{output};
}
Result<SqliteStatement, FudStatus> SqliteDb::prepare(const String& sql)
{
- using RetType = Result<SqliteStatement, FudStatus>;
SqliteStatement preparedStatement{*this, sql};
if (!preparedStatement.valid() || preparedStatement.status() != FudStatus::Success) {
m_errorCode = preparedStatement.errorCode();
- return RetType::error(preparedStatement.status());
+ return Error{preparedStatement.status()};
}
- return RetType::okay(std::move(preparedStatement));
+ return Okay{std::move(preparedStatement)};
}
SqliteStatement::SqliteStatement(const SqliteDb& sqliteDb, const String& input)
@@ -200,7 +305,6 @@ 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},
@@ -229,16 +333,6 @@ sqlite3_stmt* SqliteStatement::statement()
return m_preparedStatement;
}
-int SqliteStatement::step()
-{
- if (!valid()) {
- m_errorCode = SQLITE_MISUSE;
- } else {
- m_errorCode = sqlite3_step(m_preparedStatement);
- }
- return m_errorCode;
-}
-
FudStatus SqliteStatement::reset()
{
if (!valid()) {
@@ -256,7 +350,8 @@ SqliteErrorMsg::SqliteErrorMsg(SqliteErrorMsg&& rhs) noexcept : m_errorMsg{rhs.m
rhs.m_errorMsg = nullptr;
}
-SqliteErrorMsg& SqliteErrorMsg::operator=(SqliteErrorMsg&& rhs) noexcept {
+SqliteErrorMsg& SqliteErrorMsg::operator=(SqliteErrorMsg&& rhs) noexcept
+{
if (m_errorMsg != nullptr) {
sqlite3_free(m_errorMsg);
m_errorMsg = nullptr;
@@ -275,4 +370,12 @@ SqliteErrorMsg::~SqliteErrorMsg()
}
}
+void SqliteErrorMsg::setMessage(char* newMessage)
+{
+ if (m_errorMsg != nullptr) {
+ sqlite3_free(m_errorMsg);
+ }
+ m_errorMsg = newMessage;
+}
+
} // namespace fud
diff --git a/source/fud_string.cpp b/source/fud_string.cpp
index a2a62f4..69df7e4 100644
--- a/source/fud_string.cpp
+++ b/source/fud_string.cpp
@@ -389,13 +389,13 @@ FudStatus String::reserve(size_t newCapacity)
return resize(newCapacity);
}
-[[nodiscard]] Option<utf8> String::front()
+[[nodiscard]] Option<utf8> String::front() const
{
if (!valid() || length() < 1) {
return NullOpt;
}
- utf8 frontChar = dataMut()[0];
+ utf8 frontChar = data()[0];
if (Ascii::valid(frontChar)) {
return frontChar;
}
@@ -403,13 +403,13 @@ FudStatus String::reserve(size_t newCapacity)
return NullOpt;
}
-[[nodiscard]] Option<utf8> String::back()
+[[nodiscard]] Option<utf8> String::back() const
{
if (!valid() || length() < 1) {
return NullOpt;
}
- utf8 backChar = dataMut()[length() - 1];
+ utf8 backChar = data()[length() - 1];
if (Ascii::valid(backChar)) {
return backChar;
}
@@ -751,7 +751,7 @@ StringResult String::catenate(const String& rhs) const
return StringResult::okay(std::move(output));
}
-bool String::compare(const String& rhs) const
+bool String::operator==(const String& rhs) const
{
if (!valid() || !rhs.valid()) {
return false;
@@ -773,6 +773,7 @@ bool String::compare(const String& rhs) const
return diffResult.getOkay() == 0;
}
+
FudStatus String::clear()
{
if (!valid()) {