diff --git a/CMakeLists.txt b/CMakeLists.txt index d4c55e4..5a95b34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,7 @@ add_library(fennec STATIC # EXTRA SOURCES ======================================================================================================== ${FENNEC_EXTRA_SOURCES} + include/fennec/containers/variant.h ) add_dependencies(fennec metaprogramming) diff --git a/gdb/fennec/strings.py b/gdb/fennec/strings.py index 9e5d690..0d18f43 100644 --- a/gdb/fennec/strings.py +++ b/gdb/fennec/strings.py @@ -27,7 +27,7 @@ class CStringPrinter: return 'string' def to_string(self): - value = "\"" + self.val['_str'].string('', 'replace', self.val['_size'] - 1) + "\"" + value = "\"" + self.val['_str'].string('', 'replace', self.val['_size']) + "\"" return value def display_hint(self): diff --git a/include/fennec/containers/list.h b/include/fennec/containers/list.h index c67697f..fc5bc47 100644 --- a/include/fennec/containers/list.h +++ b/include/fennec/containers/list.h @@ -528,7 +528,7 @@ public: }; private: - allocation _table; + allocation _table; dynarray _freed; size_t _root, _last, _size; diff --git a/include/fennec/containers/optional.h b/include/fennec/containers/optional.h index 40759cb..a24546a 100644 --- a/include/fennec/containers/optional.h +++ b/include/fennec/containers/optional.h @@ -141,7 +141,7 @@ public: /// /// \brief Implicit Boolean Check, returns `true` when there is a value contained - constexpr explicit operator bool() const { + constexpr operator bool() const { return _set; } diff --git a/include/fennec/containers/rdtree.h b/include/fennec/containers/rdtree.h index 81e88aa..6b0ec59 100644 --- a/include/fennec/containers/rdtree.h +++ b/include/fennec/containers/rdtree.h @@ -304,7 +304,7 @@ public: /// /// \returns The next node id were `insert` or `emplace` to be called - constexpr size_t next_free() const { + constexpr size_t next_id() const { size_t i = _size; if (not _freed.empty()) { i = _freed.front(); @@ -418,7 +418,10 @@ public: OrderT order; i = order(*this, i); while (i != npos) { - uint8_t mode = visit(*_table[i].value, i); + uint8_t mode = traversal_control_continue; + if (_table[i].value) { + mode = visit(*_table[i].value, i); + } if (mode == traversal_control_break) { break; } diff --git a/include/fennec/containers/set.h b/include/fennec/containers/set.h index 7fa1aec..1008abc 100644 --- a/include/fennec/containers/set.h +++ b/include/fennec/containers/set.h @@ -216,7 +216,7 @@ public: // Check the first element; if (_alloc[i].psl >= psl && _alloc[i].value) { - if (*_alloc[i].value == val) { + if (_equal(*_alloc[i].value, val)) { return iterator(this, i); } } @@ -230,13 +230,13 @@ public: bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range if (c0 && _alloc[i0].value) { - if (*_alloc[i0].value == val) { + if (_equal(*_alloc[i0].value, val)) { return iterator(this, i0); } } if (c1 && _alloc[i1].value) { - if (*_alloc[i1].value == val) { + if (_equal(*_alloc[i1].value, val)) { return iterator(this, i1); } } diff --git a/include/fennec/containers/variant.h b/include/fennec/containers/variant.h new file mode 100644 index 0000000..31b280e --- /dev/null +++ b/include/fennec/containers/variant.h @@ -0,0 +1,34 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +/// +/// \file variant.h +/// \brief Contains the definition for a structure that holds a single value from multiple types +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_CONTAINERS_VARIANT_H +#define FENNEC_CONTAINERS_VARIANT_H + +#endif // FENNEC_CONTAINERS_VARIANT_H \ No newline at end of file diff --git a/include/fennec/langproc/filesystem/file.h b/include/fennec/langproc/filesystem/file.h index 4179df1..657a72b 100644 --- a/include/fennec/langproc/filesystem/file.h +++ b/include/fennec/langproc/filesystem/file.h @@ -248,7 +248,7 @@ public: bool eof() const; -// Read Operations ===================================================================================================== +// Binary Read Operations ============================================================================================== char getc(); wchar_t getwc(); @@ -269,7 +269,7 @@ public: wstring getwline(); -// Write Operations ==================================================================================================== +// Binary Write Operations ============================================================================================= bool putc(char c); bool putwc(wchar_t c); diff --git a/include/fennec/langproc/filesystem/path.h b/include/fennec/langproc/filesystem/path.h index 35b29c8..6cb75d6 100644 --- a/include/fennec/langproc/filesystem/path.h +++ b/include/fennec/langproc/filesystem/path.h @@ -145,6 +145,7 @@ public: } const string& str() const { return _str; } + const char* cstr() const { return _str.cstr(); } bool empty() { size_t size = _str.size(); @@ -195,7 +196,7 @@ public: while (not parse.empty()) { // Handle dots - while (parse._str[0] == '.') { + while (not parse.empty() && parse._str[0] == '.') { // Check for ".." if (parse._str[1] == '.') { // ".." diff --git a/include/fennec/langproc/strings/cstring.h b/include/fennec/langproc/strings/cstring.h index 1167b4c..b26feb5 100644 --- a/include/fennec/langproc/strings/cstring.h +++ b/include/fennec/langproc/strings/cstring.h @@ -171,6 +171,10 @@ public: /// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'` constexpr size_t size() const { return _size; } + /// + /// \returns the size of the string including its null terminator, i.e. `(*str)[capacity() - 1] == '\0'` + constexpr size_t capacity() const { return _size + 1; } + constexpr bool empty() const { return _cstr == nullptr || _size == 0; } @@ -198,9 +202,16 @@ public: } /// - /// \brief Dereference Operator + /// \brief Data Access /// \returns A const qualified pointer to the underlying allocation - constexpr const char* operator*() const { + constexpr char* data() { + return _str; + } + + /// + /// \brief Data Access + /// \returns A const qualified pointer to the underlying allocation + constexpr const char* data() const { return _cstr; } @@ -330,8 +341,8 @@ private: template<> struct hash : hash { - constexpr size_t operator()(const cstring& str) { - return hash::operator()(byte_array(*str, str.size())); + constexpr size_t operator()(const cstring& str) const { + return hash::operator()(byte_array(str, str.size())); } }; diff --git a/include/fennec/langproc/strings/string.h b/include/fennec/langproc/strings/string.h index 3bf4bfa..46cd7fe 100644 --- a/include/fennec/langproc/strings/string.h +++ b/include/fennec/langproc/strings/string.h @@ -59,114 +59,104 @@ public: } /// - /// \brief Sized Constructor, initializes a null-terminated string of size `n` with null characters + /// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...` /// \param n the number of characters - /// - /// \details adds additional character for null termination. - constexpr _string(size_t n) - : _string('\0', n) { - } - - /// - /// \brief Sized Constructor, initializes a null-terminated string of size `n` filled with the character `c` /// \param c the character to fill with - /// \param n the number of characters /// /// \details adds additional character for null termination. - constexpr _string(char c, size_t n) + constexpr _string(size_t n, char c = '\0') : _str(n + 1) { - fennec::memset(_str.data(), c, n); - _str[n] = '\0'; + fennec::memset(_str, c, n); + } + + constexpr _string(const alloc_t& alloc) + : _str(alloc) { + } + + constexpr _string(size_t n, char c, const alloc_t& alloc) + : _str(n + 1, alloc) { + fennec::memset(_str, c, n); + } + + constexpr _string(const cstring& cstr) + : _str(cstr, cstr.size() + 1) { } /// - /// \brief Buffer Copy Constructor - /// \param str the buffer to copy - /// \param n number of characters in the buffer - /// - /// \details adds additional character for null termination. Ignores whether str is null-terminated. - /// This constructor makes the assumption that `n` is the intended number of characters. - constexpr _string(char* str, size_t n) - : _str(str[n - 1] == '\0' ? n : n + 1) { - fennec::memcpy(_str.data(), str, n); - if (str[n - 1] != '\0') _str[n] = '\0'; - } - - /// - /// \brief Buffer Constructor, wraps the provided C-Style string + /// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present /// \param str the buffer to wrap - /// \tparam n the number of characters in the buffer plus the null terminator + /// \tparam n the number of characters in the buffer including the null-terminator, if present template - explicit constexpr _string(char(&str)[n]) - : _str(str, n) { - assert(_str[n - 1] == '\0', "Invalid NTBS."); + explicit constexpr _string(const char (&str)[n]) + : _str(str[n - 1] != '\0' ? n + 1 : n) { + fennec::memcpy(_str, str, n); + if (str[n - 1] != '\0') { + _str[n] = '\0'; + } } /// - /// \brief Buffer Copy Constructor - /// \param str the buffer to copy - /// \param n number of characters in the buffer - /// - /// \details adds additional character for null termination. Ignores whether str is null-terminated. - /// This constructor makes the assumption that `n` is the intended number of characters. - constexpr _string(const char* str, size_t n) - : _str(str[n - 1] == '\0' ? n : n + 1) { - fennec::memcpy(_str.data(), str, n); - if (str[n - 1] != '\0') _str[n] = '\0'; - } - - /// - /// \brief Buffer Constructor, wraps the provided C-Style string + /// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present /// \param str the buffer to wrap - /// \tparam n the number of characters in the buffer plus the null terminator - template - explicit constexpr _string(const char(&str)[n]) - : _str(str, n) { - assert(_str[n - 1] == '\0', "Invalid NTBS."); - } - - /// - /// \brief Buffer Copy Constructor - /// \param str the buffer to copy - constexpr _string(const cstring& str) - : _str(str, str.size() + 1) { + /// \param n the number of characters in the buffer including the null-terminator, if present + constexpr _string(const char* buf, size_t n) + : _str(buf[n - 1] != '\0' ? n + 1 : n) { + fennec::memcpy(_str, buf, n); + if (buf[n - 1] != '\0') { + _str[n] = '\0'; + } } /// /// \brief String Copy Constructor /// \param str the string to copy - constexpr _string(const _string& str) - : _str(str._str) { - } - - constexpr _string(_string&& str) noexcept - : _str(fennec::move(str._str)) { - } + constexpr _string(const _string& str) = default; /// - /// \brief String Destructor, cleans up the underlying allocation - constexpr ~_string() = default; // allocation cleans up itself + /// \brief String Move Constructor + /// \param str the string to take ownership of + constexpr _string(_string&& str) noexcept = default; + + /// + /// \brief Destructor, cleans up underlying allocation + constexpr ~_string() = default; + +// Assignment ========================================================================================================== + + constexpr _string& operator=(const cstring& cstr) { + _str.callocate(cstr.capacity()); + fennec::memcpy(_str, cstr, cstr.capacity()); + return *this; + } + + constexpr _string& operator=(const _string& str) = default; + constexpr _string& operator=(_string&& str) noexcept = default; // Properties ========================================================================================================== /// /// \returns The size of the string excluding null terminator constexpr size_t size() const { - return _str.capacity() - 1; + return _str.capacity() > 0 ? _str.capacity() - 1 : 0; + } + + /// + /// \returns The size of the string including null terminator + constexpr size_t capacity() const { + return _str.capacity(); } constexpr bool empty() const { return size() == 0; } - // Access ============================================================================================================== /// /// \brief Array Access Operator /// \param i the index to access /// \returns a reference to the character - constexpr char& operator[](int i) { + constexpr char& operator[](size_t i) { return _str[i]; } @@ -174,24 +164,21 @@ public: /// \brief Const-Array Access Operator /// \param i the index to access /// \returns a copy of the character - constexpr char operator[](int i) const { - assertd(i >= 0 && (size_t)i < size(), "Array Out of Bounds"); + constexpr const char& operator[](size_t i) const { return _str[i]; } - /// - /// \brief Dereference Operator - /// \returns A const qualified pointer to the underlying allocation - constexpr const char* operator*() const { - return _str.data(); + constexpr char* data() { + return _str; } - /// - /// \brief Implicit Dereference Cast - constexpr operator const char*() const { - return _str.data(); + constexpr const char* data() const { + return _str; } + constexpr const char* cstr() const { + return _str; + } // Examination ========================================================================================================= @@ -212,7 +199,7 @@ public: } n = fennec::min(n, fennec::max(_str, str.size()) + 1); - return ::strncmp(_str.data() + i, str, n); + return ::strncmp(_str + i, str, n); } /// @@ -226,7 +213,7 @@ public: } n = min(n, max(size(), str.size()) + 1); - return ::strncmp(_str.data() + i, str, n); + return ::strncmp(_str + i, str.data(), n); } constexpr bool operator==(const _string& str) const { @@ -242,8 +229,8 @@ public: return size(); } - const char* loc = ::strchr(_str.data() + i, c); // get location using strchr - return loc ? loc - _str.data() : size(); // return size if not found + const char* loc = ::strchr(_str + i, c); // get location using strchr + return loc ? loc - _str : size(); // return size if not found } /// @@ -332,63 +319,6 @@ public: return size(); // base case } -// Manipulation ======================================================================================================== - - /// - /// \brief Resize the string, filling additional bytes with `'\0'` - /// \param n the new size of the string - constexpr void resize(size_t n) { - size_t i = size(); - _str.reallocate(n + 1); - if (n > i) fennec::memset(_str.data() + i, '\0', n + 1 - i); - } - - /// - /// \brief Copy Assignment Operator - /// \param str the string to copy - /// \returns a reference to `this` - constexpr _string& operator=(const cstring& str) { - if (str.empty()) { - _str.deallocate(); - return *this; - } - resize(str.size()); - fennec::memcpy(_str.data(), str, str.size()); - _str[str.size()] = '\0'; - return *this; - } - - /// - /// \brief Copy Assignment Operator - /// \param str the string to copy - /// \returns a reference to `this` - constexpr _string& operator=(const string& str) { - resize(str.size()); - fennec::memcpy(_str.data(), str, str.size()); - _str[str.size()] = '\0'; - return *this; - } - - /// - /// \brief Move Assignment Operator - /// \param str the string to move - /// \returns a reference to `this` - constexpr _string& operator=(string&& str) noexcept { - _str = move(str._str); - return *this; - } - - /// - /// \brief Replace all instances of `x` with `y` - /// \param x the character to search for - /// \param y the character to replace with - void replace(char x, char y) { - size_t i = 0; - while ((i = find(x, 0)) != size()) { - _str[i] = y; - } - } - /// /// \brief Retrieve a substring of a string /// \param i the start index @@ -398,60 +328,98 @@ public: if (i >= size()) { return _string(""); } - n = min(n, size() - i); - return _string(_str.data() + i, n); - } - - /// - /// \brief Returns a string with `c` appended to it - /// \param c - /// \returns - constexpr _string operator+(char c) const { - // Copy contents with one additional byte. - _string res(_str.data(), _str.size() + 1); - res[size()] = c; // Set the last character to c + n = fennec::min(n, size() - i); + _string res; + res._str.callocate(n + 1); + fennec::memcpy(res.data(), _str + i, n); return res; } - friend constexpr _string operator+(char c, _string& str) { - return _string(c, 1) + str; + + +// Modifiers =========================================================================================================== + + constexpr void resize(size_t n) { + _str.creallocate(n + 1); + _str[size()] = '\0'; } - constexpr _string operator+(const cstring& str) const { - _string res(size() + str.size()); // Make a new string with the size of this + str - fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this - fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str + constexpr _string operator+(char c) const { + if (_str == nullptr) { + return _string(1, c); + } + _string res; + res._str.callocate(capacity() + 1); + fennec::memcpy(res.data(), _str, size()); + res[size()] = c; + return res; + } + + friend constexpr _string operator+(char c, const _string& str) { + _string res(1, c); + return res += str; + } + + constexpr _string operator+(const cstring& cstr) const { + if (_str == nullptr) { + return _string(cstr); + } + _string res; + res._str.callocate(size() + cstr.size() + 1); + fennec::memcpy(res.data(), _str, size()); + fennec::memcpy(res.data() + size(), cstr, cstr.size()); return res; } constexpr _string operator+(const _string& str) const { - _string res(size() + str.size()); // Make a new string with the size of this + str - fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this - fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str + if (_str == nullptr) { + return _string(str); + } + if (str.data() == nullptr) { + return _string(*this); + } + _string res; + res._str.callocate(size() + str.size() + 1); + fennec::memcpy(res.data(), _str, size()); + fennec::memcpy(res.data() + size(), str.data(), str.size()); return res; } constexpr _string& operator+=(char c) { - size_t x = size(); - resize(x + 1); - _str[x] = c; + if (_str == nullptr) { + _str.callocate(2); + _str[0] = c; + return *this; + } + _str.creallocate(capacity() + 1); + _str[size() - 1] = c; return *this; } - constexpr _string& operator+=(const cstring& str) { - size_t x = size(); - resize(x + str.size()); - fennec::memcpy(&_str[x], str, str.size()); + constexpr _string& operator+=(const cstring& cstr) { + if (_str == nullptr) { + return *this = cstr; + } + size_t middle = size(); + _str.creallocate(middle + cstr.size() + 1); + fennec::memcpy(_str + middle, cstr, cstr.size()); return *this; } constexpr _string& operator+=(const _string& str) { - size_t x = size(); - resize(x + str.size()); - fennec::memcpy(&_str[x], str, str.size()); + if (_str == nullptr) { + return *this = str; + } + if (str.data() == nullptr) { + return *this; + } + size_t middle = size(); + _str.creallocate(middle + str.size() + 1); + fennec::memcpy(_str + middle, str.data(), str.size()); return *this; } + private: alloc_t _str; }; @@ -459,7 +427,7 @@ private: template<> struct hash : hash { constexpr size_t operator()(const string& str) const { - return hash::operator()(byte_array(*str, str.size())); + return hash::operator()(byte_array(str.data(), str.size())); } }; diff --git a/include/fennec/langproc/strings/wcstring.h b/include/fennec/langproc/strings/wcstring.h index 42a4ead..35d9e9d 100644 --- a/include/fennec/langproc/strings/wcstring.h +++ b/include/fennec/langproc/strings/wcstring.h @@ -66,9 +66,7 @@ public: /// /// \brief Default Constructor, initializes with nullptr constexpr wcstring() - : _str(nullptr) - , _size(0) - , _const(true) { + : _str(nullptr), _size(0), _const(true) { } /// @@ -93,7 +91,7 @@ public: /// \param str the buffer to wrap /// \tparam n the number of characters in the buffer plus the null terminator template - constexpr wcstring(wchar_t(&str)[n]) + constexpr wcstring(wchar_t (&str)[n]) : _str(str) , _size(n - 1) , _const(false) { @@ -116,7 +114,7 @@ public: /// \param str the buffer to wrap /// \tparam n the number of characters in the buffer plus the null terminator template - constexpr wcstring(const wchar_t(&str)[n]) + constexpr wcstring(const wchar_t (&str)[n]) : _cstr(str) , _size(n - 1) , _const(true) { @@ -148,7 +146,7 @@ public: template constexpr wcstring& operator=(wchar_t(&str)[n]) { assert(_str[n - 1] == '\0', "Invalid NTBS."); - _str = str; _size = n - 1; _const = false; + _str = str, _size = n - 1, _const = false; return *this; } @@ -175,6 +173,10 @@ public: /// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'` constexpr size_t size() const { return _size; } + /// + /// \returns the size of the string including its null terminator, i.e. `(*str)[capacity() - 1] == '\0'` + constexpr size_t capacity() const { return _size + 1; } + constexpr bool empty() const { return _cstr == nullptr || _size == 0; } @@ -196,15 +198,22 @@ public: /// \brief Const-Array Access Operator /// \param i the index to access /// \returns a copy of the character - constexpr wchar_t operator[](size_t i) const { + constexpr const wchar_t& operator[](size_t i) const { assertd(i < size(), "Array Out of Bounds"); return _cstr[i]; } /// - /// \brief Dereference Operator + /// \brief Data Access /// \returns A const qualified pointer to the underlying allocation - constexpr const wchar_t* operator*() const { + constexpr wchar_t* data() { + return _str; + } + + /// + /// \brief Data Access + /// \returns A const qualified pointer to the underlying allocation + constexpr const wchar_t* data() const { return _cstr; } @@ -220,12 +229,14 @@ public: /// /// \returns The length of the string to the first null-terminator constexpr size_t length() const { - return find(L'\0'); + return find('\0'); } /// /// \brief String Comparison /// \param str the string to compare against + /// \param i the index to start at + /// \param n the number of characters to compare /// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the /// current locale, otherwise a positive value. constexpr int compare(const wcstring& str, size_t i = 0, size_t n = npos) const { @@ -237,6 +248,15 @@ public: return ::wcsncmp(_cstr + i, str, n); } + /// + /// \brief String Equality + /// \param str the string to compare against + /// \returns True if all characters are equal, false otherwise + template + constexpr bool operator==(const wchar_t (&str)[n]) const { + return compare(wcstring(str)) == 0; + } + /// /// \brief String Equality /// \param str the string to compare against @@ -278,7 +298,7 @@ public: /// \param c the string to find /// \param i the index to start at /// \returns The index of `c` if it occurs in the string, otherwise returns `size()` - constexpr size_t rfind(wchar_t c, size_t i = npos) const { + constexpr size_t rfind(char c, size_t i = npos) const { if (_size == 0) { return _size; } @@ -300,7 +320,7 @@ public: return _size; } - const wchar_t first = str[0]; + const char first = str[0]; i = min(i, _size - str._size); do { if(_cstr[i] == first) { @@ -321,6 +341,13 @@ private: bool _const; }; +template<> +struct hash : hash { + constexpr size_t operator()(const wcstring& str) const { + return hash::operator()(byte_array(str, str.size())); + } +}; + } #endif // FENNEC_LANGPROC_STRINGS_wcstring_H diff --git a/include/fennec/langproc/strings/wstring.h b/include/fennec/langproc/strings/wstring.h index e2af73f..8436650 100644 --- a/include/fennec/langproc/strings/wstring.h +++ b/include/fennec/langproc/strings/wstring.h @@ -16,8 +16,8 @@ // along with this program. If not, see . // ===================================================================================================================== -#ifndef FENNEC_LANGPROC_wstringS_wstring_H -#define FENNEC_LANGPROC_wstringS_wstring_H +#ifndef FENNEC_LANGPROC_wstringS_WSTRING_H +#define FENNEC_LANGPROC_wstringS_WSTRING_H #include #include @@ -59,110 +59,126 @@ public: } /// - /// \brief Sized Constructor, initializes a null-terminated string of size `n` with null characters - /// \param n the number of characters + /// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...` + /// \param n the number of wchar_tacters + /// \param c the wchar_tacter to fill with /// - /// \details adds additional character for null termination. - constexpr _wstring(size_t n) - : _wstring('\0', n) { - } - - /// - /// \brief Sized Constructor, initializes a null-terminated string of size `n` filled with the character `c` - /// \param c the characters to fill with - /// \param n the number of characters - /// - /// \details adds additional character for null termination. - constexpr _wstring(wchar_t c, size_t n) + /// \details adds additional wchar_tacter for null termination. + constexpr _wstring(size_t n, wchar_t c = '\0') : _str(n + 1) { - fennec::wmemset(_str.data(), c, n); _str[n] = '\0'; + fennec::wmemset(_str, c, n); + } + + constexpr _wstring(const alloc_t& alloc) + : _str(alloc) { + } + + constexpr _wstring(size_t n, wchar_t c, const alloc_t& alloc) + : _str(n + 1, alloc) { + fennec::wmemset(_str, c, n); + } + + constexpr _wstring(const wcstring& cstr) + : _str(cstr, cstr.size() + 1) { } /// - /// \brief Buffer Copy Constructor - /// \param str the buffer to copy - /// \param len number of characters in the buffer - /// - /// \details adds additional character for null termination. Ignores whether str is null-terminated. - /// This constructor makes the assumption that `len` is the intended number of characters. - constexpr _wstring(const wchar_t* str, size_t n) - : _str(str, n + 1) { - ::wcsncpy(_str.data(), str, n); - _str[n] = '\0'; + /// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present + /// \param str the buffer to wrap + /// \tparam n the number of wchar_tacters in the buffer including the null-terminator, if present + template + explicit constexpr _wstring(const wchar_t (&str)[n]) + : _str(str[n - 1] != '\0' ? n + 1 : n) { + fennec::wmemcpy(_str, str, n); + if (str[n - 1] != '\0') { + _str[n] = '\0'; + } } /// - /// \brief Buffer Copy Constructor - /// \param str the buffer to copy - /// \param len number of characters in the buffer - /// - /// \details adds additional character for null termination. Ignores whether str is null-terminated. - /// This constructor makes the assumption that `len` is the intended number of characters. - constexpr _wstring(const wcstring& str) - : _str(str, str.size() + 1) { - _str[str.size()] = '\0'; + /// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present + /// \param str the buffer to wrap + /// \param n the number of wchar_tacters in the buffer including the null-terminator, if present + constexpr _wstring(const wchar_t* buf, size_t n) + : _str(buf[n - 1] != '\0' ? n + 1 : n) { + fennec::wmemcpy(_str, buf, n); + if (buf[n - 1] != '\0') { + _str[n] = '\0'; + } } /// /// \brief String Copy Constructor /// \param str the string to copy - constexpr _wstring(const wstring& str) - : _wstring(str, str.size() - 1) { - } - - constexpr _wstring(_wstring&& str) noexcept - : _str(fennec::move(str._str)) { - } + constexpr _wstring(const _wstring& str) = default; /// - /// \brief String Destructor, cleans up the underlying allocation - constexpr ~_wstring() = default; // allocation cleans up itself + /// \brief String Move Constructor + /// \param str the string to take ownership of + constexpr _wstring(_wstring&& str) noexcept = default; + + /// + /// \brief Destructor, cleans up underlying allocation + constexpr ~_wstring() = default; + +// Assignment ========================================================================================================== + + constexpr _wstring& operator=(const wcstring& cstr) { + _str.callocate(cstr.capacity()); + fennec::wmemcpy(_str, cstr, cstr.capacity()); + return *this; + } + + constexpr _wstring& operator=(const _wstring& str) = default; + constexpr _wstring& operator=(_wstring&& str) noexcept = default; // Properties ========================================================================================================== /// /// \returns The size of the string excluding null terminator constexpr size_t size() const { - return _str.capacity() - 1; + return _str.capacity() > 0 ? _str.capacity() - 1 : 0; + } + + /// + /// \returns The size of the string including null terminator + constexpr size_t capacity() const { + return _str.capacity(); } constexpr bool empty() const { return size() == 0; } - // Access ============================================================================================================== /// /// \brief Array Access Operator /// \param i the index to access - /// \returns a reference to the character - constexpr wchar_t& operator[](int i) { + /// \returns a reference to the wchar_tacter + constexpr wchar_t& operator[](size_t i) { return _str[i]; } /// /// \brief Const-Array Access Operator /// \param i the index to access - /// \returns a copy of the character - constexpr wchar_t operator[](int i) const { - assertd(i >= 0 && i < size(), "Array Out of Bounds"); + /// \returns a copy of the wchar_tacter + constexpr const wchar_t& operator[](size_t i) const { return _str[i]; } - /// - /// \brief Dereference Operator - /// \returns A const qualified pointer to the underlying allocation - constexpr const wchar_t* operator*() const { - return _str.data(); + constexpr wchar_t* data() { + return _str; } - /// - /// \brief Implicit Dereference Cast - constexpr operator const wchar_t*() const { - return _str.data(); + constexpr const wchar_t* data() const { + return _str; } + constexpr const wchar_t* cstr() const { + return _str; + } // Examination ========================================================================================================= @@ -183,7 +199,7 @@ public: } n = fennec::min(n, fennec::max(_str, str.size()) + 1); - return ::wcsncmp(_str.data() + i, str, n); + return ::wcsncmp(_str + i, str, n); } /// @@ -191,13 +207,13 @@ public: /// \param ostr the string to compare against /// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the /// current locale, otherwise a positive value. - constexpr int compare(const wstring& str, size_t i = 0, size_t n = npos) const { + constexpr int compare(const _wstring& str, size_t i = 0, size_t n = npos) const { if (i >= size()) { // bounds check return -1; } n = min(n, max(size(), str.size()) + 1); - return ::wcsncmp(_str.data() + i, str, n); + return ::wcsncmp(_str + i, str.data(), n); } constexpr bool operator==(const _wstring& str) const { @@ -206,22 +222,22 @@ public: /// /// \brief Finds the index of the first occurrence of `c` in the string - /// \param c the character to find + /// \param c the wchar_tacter to find /// \returns The index of `c` if it occurs in the string, otherwise returns `size()` constexpr size_t find(wchar_t c, size_t i = 0) const { if (i >= size()) { // bounds check return size(); } - const wchar_t* loc = ::wcschr(_str.data() + i, c); // get location using strchr - return loc ? loc - _str.data() : size(); // return size if not found + const wchar_t* loc = ::wcschr(_str + i, c); // get location using strchr + return loc ? loc - _str : size(); // return size if not found } /// /// \brief Finds the index of the first occurrence of `str` in the string. /// \param str the string to find /// \returns The index of `str` if it occurs in the string, otherwise returns `size()` - constexpr size_t find(const wstring& str, size_t i = 0) const { // bounds check + constexpr size_t find(const _wstring& str, size_t i = 0) const { // bounds check if (i >= size()) { // bounds check return size(); } @@ -287,7 +303,7 @@ public: /// \param str the string to find /// \param i the index to start at /// \returns The index of `str` if it occurs in the string, otherwise returns `size()` - constexpr size_t rfind(const wstring& str, size_t i = npos) const { + constexpr size_t rfind(const string& str, size_t i = npos) const { if (size() == 0) { return size(); } @@ -303,116 +319,119 @@ public: return size(); // base case } -// Manipulation ======================================================================================================== - - /// - /// \brief Resize the string, filling additional bytes with `'\0'` - /// \param n the new size of the string - constexpr void resize(size_t n) { - size_t i = size(); - _str.reallocate(n + 1); - if (n > i) fennec::wmemset(_str.data() + i, L'\0', n - i); - } - - /// - /// \brief Copy Assignment Operator - /// \param str the string to copy - /// \returns a reference to `this` - constexpr wstring& operator=(const wcstring& str) { - if (str.empty()) { - _str.deallocate(); - return *this; - } - if (str.size() > size()) resize(str.size()); - fennec::wmemcpy(_str.data(), str, str.size()); - _str[str.size()] = L'\0'; - return *this; - } - - /// - /// \brief Copy Assignment Operator - /// \param str the string to copy - /// \returns a reference to `this` - constexpr wstring& operator=(const wstring& str) { - if (str.size() > size()) resize(str.size()); - fennec::wmemcpy(_str.data(), str, str.size()); - _str[str.size()] = '\0'; - return *this; - } - - /// - /// \brief Move Assignment Operator - /// \param str the string to move - /// \returns a reference to `this` - constexpr wstring& operator=(wstring&& str) noexcept { - _str = fennec::move(str._str); - return *this; - } - /// /// \brief Retrieve a substring of a string /// \param i the start index - /// \param n the number of characters + /// \param n the number of wchar_tacters /// \return - constexpr wstring substring(size_t i, size_t n = npos) const { + constexpr _wstring substring(size_t i, size_t n = npos) const { if (i >= size()) { - return wstring(""); + return _wstring(""); } - n = min(n, size() - i); - return wstring(_str.data() + i, n); - } - - /// - /// \brief Returns a string with `c` appended to it - /// \param c - /// \returns - constexpr wstring operator+(wchar_t c) const { - // Copy contents with one additional byte. - wstring res(_str, _str.size()); - res[size()] = c; // Set the last character to c + n = fennec::min(n, size() - i); + _wstring res; + res._str.callocate(n + 1); + fennec::wmemcpy(res.data(), _str + i, n); return res; } - constexpr wstring operator+(const wcstring& str) const { - wstring res(size() + str.size()); // Make a new string with the size of this + str - fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this - fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str + + +// Modifiers =========================================================================================================== + + constexpr void resize(size_t n) { + _str.creallocate(n + 1); + _str[size()] = '\0'; + } + + constexpr _wstring operator+(wchar_t c) const { + if (_str == nullptr) { + return _wstring(1, c); + } + _wstring res; + res._str.callocate(capacity() + 1); + fennec::wmemcpy(res.data(), _str, size()); + res[size()] = c; return res; } - constexpr wstring operator+(const wstring& str) const { - wstring res(size() + str.size()); // Make a new string with the size of this + str - fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this - fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str + friend constexpr _wstring operator+(wchar_t c, const _wstring& str) { + _wstring res(1, c); + return res += str; + } + + constexpr _wstring operator+(const wcstring& cstr) const { + if (_str == nullptr) { + return _wstring(cstr); + } + _wstring res; + res._str.callocate(size() + cstr.size() + 1); + fennec::wmemcpy(res.data(), _str, size()); + fennec::wmemcpy(res.data() + size(), cstr, cstr.size()); return res; } - constexpr wstring& operator+=(wchar_t c) { - size_t x = size(); - resize(x + 1); - _str[x] = c; + constexpr _wstring operator+(const _wstring& str) const { + if (_str == nullptr) { + return _wstring(str); + } + if (str.data() == nullptr) { + return _wstring(*this); + } + _wstring res; + res._str.callocate(size() + str.size() + 1); + fennec::wmemcpy(res.data(), _str, size()); + fennec::wmemcpy(res.data() + size(), str.data(), str.size()); + return res; + } + + constexpr _wstring& operator+=(wchar_t c) { + if (_str == nullptr) { + _str.callocate(2); + _str[0] = c; + return *this; + } + _str.creallocate(capacity() + 1); + _str[size() - 1] = c; return *this; } - constexpr wstring& operator+=(const wcstring& str) { - size_t x = size(); - resize(x + str.size()); - fennec::wmemcpy(&_str[x], str, str.size()); + constexpr _wstring& operator+=(const wcstring& cstr) { + if (_str == nullptr) { + return *this = cstr; + } + size_t middle = size(); + _str.creallocate(middle + cstr.size() + 1); + fennec::wmemcpy(_str + middle, cstr, cstr.size()); return *this; } - constexpr wstring& operator+=(const wstring& str) { - size_t x = size(); - resize(x + str.size()); - fennec::wmemcpy(&_str[x], str, str.size()); + constexpr _wstring& operator+=(const _wstring& str) { + if (_str == nullptr) { + return *this = str; + } + if (str.data() == nullptr) { + return *this; + } + size_t middle = size(); + _str.creallocate(middle + str.size() + 1); + fennec::wmemcpy(_str + middle, str.data(), str.size()); return *this; } + private: alloc_t _str; }; +template<> +struct hash : hash { + constexpr size_t operator()(const string& str) const { + return hash::operator()(byte_array(str.data(), str.size())); + } +}; + } -#endif // FENNEC_LANGPROC_wstringS_wstring_H +#endif // FENNEC_LANGPROC_wstringS_WSTRING_H diff --git a/include/fennec/memory/allocator.h b/include/fennec/memory/allocator.h index b2160f2..debb736 100644 --- a/include/fennec/memory/allocator.h +++ b/include/fennec/memory/allocator.h @@ -491,13 +491,7 @@ public: /// If there is already an allocated block of memory, the previous allocation is released. /// \param n The number of elements of type `T` to allocate for constexpr void callocate(size_t n, align_t align = zero()) noexcept { - deallocate(); - - if (align != zero()) { - _data = _alloc.allocate(_capacity = n, _alignment = align); - } else { - _data = _alloc.allocate(_capacity = n); - } + allocate(n, align); fennec::memset(static_cast(_data), 0, _capacity * sizeof(T)); } @@ -523,12 +517,15 @@ public: constexpr void reallocate(size_t n, align_t align = zero()) noexcept { if (_data == nullptr) { allocate(n, align); + return; } - value_t* old = _data; + value_t* old = _data; size_t old_cap = _capacity; _data = nullptr; allocate(n, align); - fennec::memmove(static_cast(_data), old, min(_capacity, n) * sizeof(T)); + + fennec::memmove(static_cast(_data), old, min(_capacity, old_cap) * sizeof(T)); + _alloc.deallocate(old); _capacity = n; } @@ -539,12 +536,19 @@ public: constexpr void creallocate(size_t n, align_t align = zero()) noexcept { if (_data == nullptr) { callocate(n, align); + return; } - value_t* old = _data; + value_t* old = _data; size_t old_cap = _capacity; _data = nullptr; - callocate(n, align); - fennec::memmove(static_cast(_data), old, min(_capacity, n) * sizeof(T)); + allocate(n, align); + + fennec::memmove(static_cast(_data), old, min(_capacity, old_cap) * sizeof(T)); + + if (_capacity > old_cap) { + fennec::memset(static_cast(_data + old_cap), 0, _capacity - old_cap); + } + _alloc.deallocate(old); _capacity = n; } @@ -557,14 +561,17 @@ public: return _data[i]; } - constexpr value_t operator[](size_t i) const requires is_fundamental_v { + constexpr const value_t& operator[](size_t i) const { assertd(i < capacity(), "Array Out of Bounds"); return _data[i]; } - constexpr const value_t& operator[](size_t i) const { - assertd(i < capacity(), "Array Out of Bounds"); - return _data[i]; + constexpr operator value_t*() { + return _data; + } + + constexpr operator const value_t*() const { + return _data; } value_t* begin() { diff --git a/source/langproc/filesystem/file.cpp b/source/langproc/filesystem/file.cpp index 0ab3801..8583468 100644 --- a/source/langproc/filesystem/file.cpp +++ b/source/langproc/filesystem/file.cpp @@ -193,7 +193,7 @@ bool file::open(const string& p, uint8_t mode) { } // Attempt to open the file - _handle = fopen(p, fmode_translate(mode)); + _handle = fopen(p.cstr(), fmode_translate(mode)); // Validate the file if (_handle == nullptr) { @@ -233,7 +233,7 @@ bool file::open(const path& p, uint8_t mode) { } // Attempt to open the file - _handle = fopen(p.str(), fmode_translate(mode)); + _handle = fopen(p.cstr(), fmode_translate(mode)); // Validate the file if (_handle == nullptr) { @@ -313,7 +313,7 @@ bool file::erase() { } // Erase the file - remove(path.str()); + remove(path.cstr()); return false; } @@ -335,7 +335,7 @@ bool file::rename(const cstring& str) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -395,7 +395,7 @@ bool file::rename(const cstring& str) { fclose(_handle); // Reopen the new file - _handle = freopen(fpath.str(), fmode_translate(_mode), fnew); + _handle = freopen(fpath.cstr(), fmode_translate(_mode), fnew); // Check for open failure if (_handle == nullptr) { @@ -404,7 +404,7 @@ bool file::rename(const cstring& str) { } // Erase the old file - remove(_path.str()); + remove(_path.cstr()); // Set the new path _path = fpath; @@ -429,7 +429,7 @@ bool file::rename(const string& str) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -488,7 +488,7 @@ bool file::rename(const string& str) { fclose(_handle); // Reopen the new file - _handle = freopen(_path.str(), fmode_translate(_mode), fnew); + _handle = freopen(_path.cstr(), fmode_translate(_mode), fnew); // Check for open failure if (_handle == nullptr) { @@ -518,7 +518,7 @@ bool file::rename(const path& p) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -577,7 +577,7 @@ bool file::rename(const path& p) { fclose(_handle); // Reopen the new file - _handle = freopen(_path.str(), fmode_translate(_mode), fnew); + _handle = freopen(_path.cstr(), fmode_translate(_mode), fnew); // Check for open failure if (_handle == nullptr) { @@ -608,7 +608,7 @@ file file::copy(const cstring& str) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -698,7 +698,7 @@ file file::copy(const string& str) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -789,7 +789,7 @@ file file::copy(const path& p) { } // Attempt to open the new file - FILE* fnew = fopen(fpath.str(), "wx"); + FILE* fnew = fopen(fpath.cstr(), "wx"); // Check for open failure if (fnew == nullptr) { @@ -989,7 +989,7 @@ wstring file::getwline() { // Check if there is a file if (_handle == nullptr) { - return 0; + return _wstring{ L"" }; } // Read the next line; diff --git a/source/langproc/filesystem/path.cpp b/source/langproc/filesystem/path.cpp index a4515f4..bc4290a 100644 --- a/source/langproc/filesystem/path.cpp +++ b/source/langproc/filesystem/path.cpp @@ -46,7 +46,7 @@ path path::current() { } path path::current(const path& path) { - if (chdir(path._str)) { + if (chdir(path._str.cstr())) { return fennec::path(""); } return current(); diff --git a/source/platform/interface/platform.cpp b/source/platform/interface/platform.cpp index 4c0144a..0ba54ca 100644 --- a/source/platform/interface/platform.cpp +++ b/source/platform/interface/platform.cpp @@ -29,7 +29,7 @@ static constexpr void insert_driver(list>& drvrs, CtorT iter_t it = drvrs.begin(); while (it != drvrs.end()) { - if (priority > it->priority) { + if (priority >= it->priority) { break; } } diff --git a/source/platform/unix/platform.cpp b/source/platform/unix/platform.cpp index 4148783..df35f90 100644 --- a/source/platform/unix/platform.cpp +++ b/source/platform/unix/platform.cpp @@ -38,10 +38,10 @@ void unix_platform::unload_object(shared_object* obj) { platform::symbol unix_platform::find_symbol(shared_object* obj, const cstring& name) { string _name = name; - void* symbol = dlsym(obj, _name); + void* symbol = dlsym(obj, _name.cstr()); if (symbol == nullptr) { _name = '_' + _name; - symbol = dlsym(obj, _name); + symbol = dlsym(obj, _name.cstr()); } return symbol; } diff --git a/test/printing.h b/test/printing.h index 5f7cde4..723cf3e 100644 --- a/test/printing.h +++ b/test/printing.h @@ -60,12 +60,12 @@ inline std::ostream& operator<<(std::ostream& os, const quaternion& q) // Helper for printing strings inline std::ostream& operator<<(std::ostream& os, const cstring& str) { - return os << *str; + return os << str.data(); } // Helper for printing strings inline std::ostream& operator<<(std::ostream& os, const string& str) { - return os << *str; + return os << str.cstr(); } // Helper for printing strings diff --git a/test/tests/containers/test_rdtree.h b/test/tests/containers/test_rdtree.h index da471bd..7a08dfa 100644 --- a/test/tests/containers/test_rdtree.h +++ b/test/tests/containers/test_rdtree.h @@ -55,6 +55,11 @@ inline void fennec_test_containers_rdtree() { fennec_test_spacer(1); + test.traverse([](size_t i, size_t n) -> uint8_t { + assertf(i + 1 == n, "Tree Traverse Test Failed"); + return traversal_control_continue; + }); + test.erase(0); fennec_test_run(test.empty(), true);