summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/fud_allocator.hpp2
-rw-r--r--include/fud_c_file.hpp7
-rw-r--r--include/fud_file.hpp8
-rw-r--r--include/fud_string.hpp194
-rw-r--r--include/libfud.hpp61
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