diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-28 23:49:50 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-28 23:49:50 -0500 |
commit | afc11065bb151349090d8ae89cb61d1c35bdddae (patch) | |
tree | 41a7133a6e143d6333594d899556831b5a914fc1 /include | |
parent | c3cf6df863828798ed8230b0f0966bcf3b2d08dd (diff) |
Prepare for new SSO.
Diffstat (limited to 'include')
-rw-r--r-- | include/fud_allocator.hpp | 2 | ||||
-rw-r--r-- | include/fud_c_file.hpp | 7 | ||||
-rw-r--r-- | include/fud_file.hpp | 8 | ||||
-rw-r--r-- | include/fud_string.hpp | 194 | ||||
-rw-r--r-- | include/libfud.hpp | 61 |
5 files changed, 228 insertions, 44 deletions
diff --git a/include/fud_allocator.hpp b/include/fud_allocator.hpp index a9fbd67..693111b 100644 --- a/include/fud_allocator.hpp +++ b/include/fud_allocator.hpp @@ -26,7 +26,7 @@ namespace fud { -class Allocator { +class alignas(size_t) Allocator { public: virtual ~Allocator() = default; diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp index 197abd3..bf148c2 100644 --- a/include/fud_c_file.hpp +++ b/include/fud_c_file.hpp @@ -97,9 +97,6 @@ class CFile { FudStatus open() { auto& self = static_cast<Derived&>(*this); - if (!self.m_filename.valid()) { - return FudStatus::ObjectInvalid; - } self.m_file = fopen(self.m_filename.c_str(), self.m_mode.c_str()); return self.m_file != nullptr ? FudStatus::Success : FudStatus::Failure; } @@ -111,10 +108,6 @@ class CFile { return FudStatus::OperationInvalid; } - if (!self.m_filename.valid()) { - return FudStatus::ObjectInvalid; - } - auto fdResult = ::open(self.m_filename.c_str(), O_CREAT, permissions.mode()); if (fdResult == -1) { return FudStatus::Failure; diff --git a/include/fud_file.hpp b/include/fud_file.hpp index b468b4b..9ef2cbc 100644 --- a/include/fud_file.hpp +++ b/include/fud_file.hpp @@ -20,6 +20,7 @@ #include "fud_allocator.hpp" #include "fud_option.hpp" +#include "fud_permissions.hpp" #include "fud_result.hpp" #include "fud_status.hpp" #include "fud_string_view.hpp" @@ -135,12 +136,17 @@ class RegularFile { StringView filename, FileAccessMode mode, OpenFlags flags, + Permissions permissions, bool exclusive, Option<int> dirFdOption, Allocator* allocator = &globalFudAllocator); FudStatus close(); + FudStatus take(RegularFile& rhs); + + Result<size_t, FudStatus> size() const; + private: RegularFile() = default; @@ -155,8 +161,6 @@ class RegularFile { RegularFile& operator=(RegularFile&& rhs) noexcept; - FudStatus take(RegularFile& rhs); - private: int m_fd{-1}; FileAccessMode m_modeFlags{}; diff --git a/include/fud_string.hpp b/include/fud_string.hpp index d2d3761..29b887a 100644 --- a/include/fud_string.hpp +++ b/include/fud_string.hpp @@ -32,36 +32,100 @@ static_assert(CHAR_BIT == 8); -namespace fud { +/** @file */ -constexpr size_t SSO_BUF_LENGTH = 15; -constexpr size_t SSO_BUF_SIZE = SSO_BUF_LENGTH + 1; - -using StringResult = Result<String, FudStatus>; +namespace fud { struct DrainResult { size_t bytesWritten; FudStatus status; }; +/** \brief The maximum length of a string using the small string optimization + * buffer. */ +constexpr size_t SSO_BUF_LENGTH = 15; + +/** \brief The size of the small string optimization buffer, to include space + * for the null terminator. */ +constexpr size_t SSO_BUF_SIZE = SSO_BUF_LENGTH + 1; + +class String; + +/** \brief A result containing a valid @String or the @FudStatus error + * encountered during its creation. */ +using StringResult = Result<String, FudStatus>; + +/** \brief A null terminated, growable, array of utf8 code points, with a custom + * allocator. */ class String { public: + /** \brief Create a string from a C String. + * + * \param [in] cString a pointer to a C string to populate the String. + * + * \returns String on success. + * \returns NullPointer if cString is null. + * \returns ArgumentInvalid if the length of cString is greater than or + * equal to SSIZE_MAX. + * \returns FudStatus::AllocFailure if the allocator fails. + */ static StringResult makeFromCString(const char* cString); + /** \brief Create a string from a C String, specifying the allocator. + * + * \param [in] cString a pointer to a C string to populate the String. + * \param [in] allocator the allocator the string will use. + * + * \returns String on success. + * \returns NullPointer if cString or allocator is null. + * \returns ArgumentInvalid if the length of cString is greater than or + * equal to SSIZE_MAX. + * \returns FudStatus::AllocFailure if the allocator fails. + */ static StringResult makeFromCString(const char* cString, Allocator* allocator); + /** \brief Create a string from concatenating multiple C Strings. + * + * This function uses the default globalFudAllocator. + * + * \tparam CStrings a parameter pack of types convertible to const char*. + * \param [in] cStrings pointers to C strings to concatenate into a single String. + * + * \returns String on success. + * \returns NullPointer if cString or allocator is null. + * \returns ArgumentInvalid if the length of cString is greater than or + * equal to SSIZE_MAX. + * \returns FudStatus::AllocFailure if the allocator fails. + */ template <typename... CStrings> static StringResult makeFromCStrings(CStrings... cStrings) { return makeFromCStringsAlloc(&globalFudAllocator, cStrings...); } + /** \brief Create a string from concatenating multiple C Strings. + * + * \tparam CStrings a parameter pack of types convertible to const char*. + * \param [in] cStrings pointers to C strings to concatenate into a single String. + * \param [in] allocator the allocator the string will use. + * + * \returns String on success. + * \returns NullPointer if cString or allocator is null. + * \returns ArgumentInvalid if the length of cString is greater than or + * equal to SSIZE_MAX. + * \returns FudStatus::AllocFailure if the allocator fails. + */ template <typename... CStrings> static StringResult makeFromCStringsAlloc(Allocator* allocator, CStrings... cStrings) { if (allocator == nullptr) { return StringResult::error(FudStatus::NullPointer); } + + if (!String::allocatorValid(allocator)) { + return StringResult::error(FudStatus::ArgumentInvalid); + } + size_t totalLength = 0; Array<size_t, sizeof...(cStrings)> lengths{}; Array<const char*, sizeof...(cStrings)> strPointers{}; @@ -96,14 +160,14 @@ class String { if (output.m_length >= output.m_capacity) { output.m_capacity = output.m_length + 1; /* Avoid using compiler expansions in headers */ - auto dataResult = output.m_allocator->allocate(output.m_capacity); + auto dataResult = output.allocator()->allocate(output.m_capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); } output.m_data = static_cast<utf8*>(dataResult.getOkay()); } - auto* data = output.data(); + auto* data = output.dataMut(); size_t cumulativeLength = 0; for (size_t idx = 0; idx < strPointers.size(); ++idx) { const auto* cString = strPointers[idx]; @@ -140,58 +204,46 @@ class String { FudStatus copy(const String& rhs); + /** \brief The raw length of the string's data, excluding the null terminator. */ [[nodiscard]] constexpr size_t length() const { return m_length; } + /** \brief Indicates if no characters are present in the string's data. */ [[nodiscard]] constexpr bool empty() const { return m_length == 0; } + /** \brief The total size of the string's data, including the null terminator. */ [[nodiscard]] constexpr size_t size() const { return m_length + 1; } + /** \brief The current capacity of the string, excluding the reserved slot + * for the null terminator. */ [[nodiscard]] constexpr size_t capacity() const { + fudAssert(m_capacity > 0); return m_capacity - 1; } - /** \brier The underlying data, guaranteed to have c string representation. */ + /** \brief The underlying data, guaranteed to have c string representation. */ [[nodiscard]] constexpr const utf8* data() const { return isLarge() ? m_data : m_buffer.data(); } - /** \brier The underlying data as an explicit c string. */ + /** \brief The underlying data as an explicit c string. */ [[nodiscard]] inline const char* c_str() const { - return isLarge() ? reinterpret_cast<const char*>(m_data) : reinterpret_cast<const char*>(m_buffer.data()); + return reinterpret_cast<const char*>(data()); } - /** \brier The underlying data, guaranteed to have c string representation. */ - [[nodiscard]] constexpr utf8* data() - { - return isLarge() ? m_data : m_buffer.data(); - } - - /** \brier The underlying data as an explicit c string. */ - [[nodiscard]] inline char* c_str() - { - return isLarge() ? reinterpret_cast<char*>(m_data) : reinterpret_cast<char*>(m_buffer.data()); - } - - [[nodiscard]] bool nullTerminated() const; - - [[nodiscard]] bool valid() const; - [[nodiscard]] bool utf8Valid() const; - FudStatus nullTerminate(); - FudStatus reserve(size_t newCapacity); [[nodiscard]] Option<utf8> back(); @@ -243,26 +295,108 @@ class String { const utf8* end() const; private: + static constexpr uint8_t capacityMask = 0x07; + + [[nodiscard]] static bool allocatorValid(Allocator* allocator) + { + return ((reinterpret_cast<uintptr_t>(allocator) & capacityMask) == 0); + } + + [[nodiscard]] bool nullTerminated() const; + + [[nodiscard]] bool valid() const; + + FudStatus nullTerminate(); + + Allocator* allocator() const + { + constexpr uintptr_t ALLOCATOR_MASK = ~static_cast<uintptr_t>(capacityMask); + const auto allocptr = reinterpret_cast<uintptr_t>(m_allocator); + return reinterpret_cast<Allocator*>(allocptr & ALLOCATOR_MASK); + } + + /** \brief The underlying data, guaranteed to have c string + * representation. */ + [[nodiscard]] constexpr utf8* dataMut() + { + return isLarge() ? m_data : m_buffer.data(); + } + void cleanup(); FudStatus resize(size_t newCapacity); + [[nodiscard]] bool optIsLarge() const + { + return (reinterpret_cast<uintptr_t>(m_allocator) & capacityMask) != 0; + } + + public: + static constexpr size_t OptBufSize = 2 * sizeof(size_t) + sizeof(utf8*) - 1; + using OptBufType = Array<utf8, OptBufSize>; + size_t optLength() const + { + if (optIsLarge()) { + return m_large.optLength; + } + return m_small.optLength; + } + + size_t optCapacity() const + { + if (optIsLarge()) { + return m_large.optCapacity; + } + return OptBufSize - 1U; + } + + private: + void setLength(size_t newLength) + { + m_length = newLength; + if (optIsLarge()) { + m_large.optLength = newLength; + } else { + m_small.optLength = static_cast<uint8_t>(newLength); + } + } + + /** \brief The allocator used to get storage for characters when the string + * is large. */ + Allocator* m_allocator{&globalFudAllocator}; + + union { + struct { + size_t optLength; + size_t optCapacity; + utf8* optData; + } m_large; + struct { + uint8_t optLength; + OptBufType optBuffer; + } m_small; + }; + using BufType = Array<utf8, SSO_BUF_SIZE>; union { + /** \brief The storage for string characters when using SSO. */ BufType m_buffer{BufType::constFill(0)}; + /** \brief The storage for string characters when the string is + * large. */ utf8* m_data; }; + /** \brief The length of the string excluding the null terminator. */ size_t m_length{0}; + /** \brief The capacity of the string, including the null terminator. */ size_t m_capacity{SSO_BUF_SIZE}; + /** \brief Whether or not the string must use its allocator for storage. */ [[nodiscard]] constexpr bool isLarge() const { return m_capacity > SSO_BUF_SIZE; } - - Allocator* m_allocator{&globalFudAllocator}; }; } // namespace fud diff --git a/include/libfud.hpp b/include/libfud.hpp index bff791d..f0a2517 100644 --- a/include/libfud.hpp +++ b/include/libfud.hpp @@ -26,6 +26,8 @@ #include <cstdint> +/** @file */ + namespace fud { constexpr size_t GIT_REV_CHARS = 13; @@ -37,7 +39,10 @@ struct FUD { Array<char, GIT_REV_CHARS> revision; }; -/** \brief Get the version of FUD including git revision. */ +/** \brief Get the version of FUD including git revision. + * + * \returns FUD object with verison information. + */ FUD fud(); /** @@ -46,23 +51,71 @@ FUD fud(); * \param[in] name The name of the variable to look up. * \param[in] allocator The allocator used by the string returned. * - * \retstmt The value of the string bound to the variable if it exists. - * \retcode FudStatus::NullPointer if name is a null pointer. - * \retcode FudStatus::NotFound if no binding for the variable exists. + * \returns The value of the string bound to the variable if it exists. + * \returns FudStatus::NullPointer if name is a null pointer. + * \returns FudStatus::NotFound if no binding for the variable exists. */ Result<String, FudStatus> getEnv(const char* name, Allocator* allocator= &globalFudAllocator); +/** \brief A concept requiring the object T to have a method c_str returning a + * pointer to a C string. */ template <typename T> concept CStringRepr = requires(T strObj) { { strObj.c_str() } -> std::convertible_to<const char*>; }; +/** + * \brief Get an environmental variable if it exists. + * + * \tparam T A type which has a method c_str returning a pointer convertible to + * a C string. + * \param[in] name The name of the variable to look up. + * \param[in] allocator The allocator used by the string returned. + * + * \returns @getEnv return values. + */ template <CStringRepr T> Result<String, FudStatus> getEnv(const T& name, Allocator* allocator = &globalFudAllocator) { return getEnv(name.c_str(), allocator); } +/** \mainpage libfud + * + * \section intro_sec Introduction + * + * libfud is an exception-free, UB-minimizing, library for safely making system + * calls, handling UTF8 encoded strings, and managing containers with user + * controlled allocators. + * + * \subsection design_principles Design Principles + * + * - Full control over allocations, even the default allocator. + * - Zero exceptions, zero exceptions to zero exceptions. + * - Assertions in production release for invariants. + * - Configurable run-time assertions for indexing. + * - Safe API for users intolerant to run-time assertions for indexing. + * - Readable, understandable code. + * - Minimize undefined behavior in the API to a subset which is only decidable at + * run-time. + * - Minimize runtime undefined behavior as much as possible. + * + * \subsection rel_mature_features Relatively Mature Features + * + * - Statically sized Array + * - StringView + * + * \subsection unstable_features Unstable Features + * + * - Customizable allocator model + * - Dynamically sized Vector taking an allocator + * - Dynamically sized String taking an allocator + * - Unicode support with UTF8 encoding + * - format à la =std::format= + * - Wrappers around C files + * + */ + } // namespace fud #endif |