From cb9fa588ba8144fcdd52ba4b83d69d93fb18066f Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Sun, 30 Mar 2025 23:08:43 -0500 Subject: Add hash map. --- source/fud_hash.cpp | 53 +++++++++++++++ source/fud_sqlite.cpp | 175 +++++++++++++++++++++++++++++++++++++++----------- source/fud_string.cpp | 11 ++-- 3 files changed, 198 insertions(+), 41 deletions(-) create mode 100644 source/fud_hash.cpp (limited to 'source') 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::operator()(const String& value, size_t seed) const +{ + static_cast(seed); + return djb2(value.data(), value.length()); +} + +size_t DefaultHash::operator()(const StringView& value, size_t seed) const +{ + static_cast(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(m_mode) | m_extraFlags, nullptr); +} + FudStatus SqliteDb::exec( const String& statement, int (*callback)(void*, int, char**, char**), void* context, - std::unique_ptr errorMessage) + // NOLINTNEXTLINE(performance-copy-param-value) + Option& 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(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 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 SqliteDb::columnInt64(SqliteStatement& statement, int index) { - // use default vfs - return sqlite3_open_v2(m_name.c_str(), &m_dbHandle, static_cast(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); + + return Okay{static_cast(output)}; +} + +Result 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 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(outputLength); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + output.m_data = reinterpret_cast(sqlite3_column_text(statement.statement(), index)); + if (output.m_data == nullptr && output.m_length != 0) { + return Error{FudStatus::Failure}; + } + + return Okay{output}; } Result SqliteDb::prepare(const String& sql) { - using RetType = Result; 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 String::front() +[[nodiscard]] Option 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 String::back() +[[nodiscard]] Option 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()) { -- cgit v1.2.3