diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2025-03-30 23:08:43 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2025-03-30 23:08:43 -0500 |
commit | cb9fa588ba8144fcdd52ba4b83d69d93fb18066f (patch) | |
tree | 214574ca68c1551ec76e7fbb9e0263793180231d /source | |
parent | 1d357adfa19725ee69fb267a363f1fd217b1272f (diff) |
Add hash map.
Diffstat (limited to 'source')
-rw-r--r-- | source/fud_hash.cpp | 53 | ||||
-rw-r--r-- | source/fud_sqlite.cpp | 175 | ||||
-rw-r--r-- | source/fud_string.cpp | 11 |
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()) { |