- Framework for Vulkan context - Fixed a bug with dynarray where if `resize()` shrinks the array, destructors are not called. - Fixed grammar issues with the containers library and added property tables to existing data structures.
372 lines
10 KiB
C++
372 lines
10 KiB
C++
// =====================================================================================================================
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#ifndef FENNEC_STRING_CSTRING_H
|
|
#define FENNEC_STRING_CSTRING_H
|
|
|
|
#include <fennec/string/detail/_ctype.h>
|
|
#include <fennec/memory/detail/_string.h>
|
|
|
|
#include <fennec/lang/assert.h>
|
|
|
|
#include <fennec/math/common.h>
|
|
#include <fennec/memory/bytes.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
// TODO: Document
|
|
|
|
using ::isalnum;
|
|
using ::isalpha;
|
|
using ::islower;
|
|
using ::isupper;
|
|
using ::isdigit;
|
|
using ::isxdigit;
|
|
using ::iscntrl;
|
|
using ::isgraph;
|
|
using ::isspace;
|
|
using ::isblank;
|
|
using ::isprint;
|
|
using ::ispunct;
|
|
|
|
using ::tolower;
|
|
using ::toupper;
|
|
|
|
|
|
struct string_view {
|
|
const char* str;
|
|
size_t len;
|
|
};
|
|
|
|
///
|
|
/// \brief This struct wraps c-style strings
|
|
///
|
|
/// \details Requires that the string is null-terminated.
|
|
/// Prevents const qualified memory blocks from being manipulated.
|
|
/// This struct should be used when fennec::string would make unnecessary dynamic buffers.
|
|
struct cstring
|
|
{
|
|
public:
|
|
static constexpr size_t npos = -1;
|
|
using char_t = char;
|
|
|
|
// Constructors ========================================================================================================
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes with nullptr
|
|
constexpr cstring()
|
|
: _str(nullptr), _size(0), _const(true) {
|
|
}
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes with nullptr
|
|
constexpr cstring(nullptr_t)
|
|
: _str(nullptr), _size(0), _const(true) {
|
|
}
|
|
|
|
///
|
|
/// \param str the buffer to wrap
|
|
/// \param n the number of characters in the buffer plus the null terminator
|
|
///
|
|
/// \note If used with `::strlen`, the result should be incremented by 1 to include the null terminator
|
|
constexpr cstring(char* str, size_t n)
|
|
: _str(str)
|
|
, _size(n)
|
|
, _const(false) {
|
|
if constexpr(not is_constant_evaluated()) {
|
|
assert(_str[n] == '\0', "Invalid NTBS.");
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \param str the buffer to wrap
|
|
/// \tparam n the number of characters in the buffer plus the null terminator
|
|
template<size_t n>
|
|
constexpr cstring(char(&str)[n])
|
|
: _str(str)
|
|
, _size(n - 1)
|
|
, _const(false) {
|
|
if constexpr(not is_constant_evaluated()) {
|
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \param str the buffer to wrap
|
|
/// \param n the number of characters in the buffer plus the null terminator
|
|
///
|
|
/// \note If used with `::strlen`, the result should be incremented by 1 to include the null terminator
|
|
constexpr cstring(const char* str, size_t n)
|
|
: _cstr(str)
|
|
, _size(n)
|
|
, _const(true) {
|
|
if constexpr(not is_constant_evaluated()) {
|
|
assert(_str[n] == '\0', "Invalid NTBS.");
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Buffer Constructor, wraps the provided C-Style string
|
|
/// \param str the buffer to wrap
|
|
/// \tparam n the number of characters in the buffer plus the null terminator
|
|
template<size_t n>
|
|
constexpr cstring(const char(&str)[n])
|
|
: _cstr(str)
|
|
, _size(n - 1)
|
|
, _const(true) {
|
|
if constexpr(not is_constant_evaluated()) {
|
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Move Constructor
|
|
/// \param str object to move
|
|
constexpr cstring(cstring&& str) noexcept
|
|
: _cstr(str._cstr)
|
|
, _size(str._size)
|
|
, _const(str._const) {
|
|
str._cstr = nullptr;
|
|
str._size = 0;
|
|
str._const = true;
|
|
}
|
|
|
|
// TODO: Document
|
|
constexpr cstring(const cstring&) = delete;
|
|
constexpr ~cstring() = default;
|
|
|
|
constexpr cstring& operator=(nullptr_t) {
|
|
_str = nullptr, _size = 0, _const = true;
|
|
return *this;
|
|
}
|
|
|
|
// TODO: Document
|
|
template<size_t n>
|
|
constexpr cstring& operator=(char(&str)[n]) {
|
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
|
_str = str, _size = n - 1, _const = false;
|
|
return *this;
|
|
}
|
|
|
|
// TODO: Document
|
|
template<size_t n>
|
|
constexpr cstring& operator=(const char(&str)[n]) {
|
|
assert(str[n - 1] == '\0', "Invalid NTBS.");
|
|
_cstr = str; _size = n - 1; _const = true;
|
|
return *this;
|
|
}
|
|
|
|
// TODO: Document
|
|
constexpr cstring& operator=(cstring&& str) noexcept {
|
|
_cstr = str._cstr; str._cstr = nullptr;
|
|
_size = str._size; str._size = 0;
|
|
_const = str._const; str._const = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
// Properties ==========================================================================================================
|
|
|
|
///
|
|
/// \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 is_empty() const {
|
|
return _cstr == nullptr || _size == 0;
|
|
}
|
|
|
|
|
|
// Access ==============================================================================================================
|
|
|
|
///
|
|
/// \brief Array Access Operator
|
|
/// \param i the index to access
|
|
/// \returns a reference to the character
|
|
constexpr char& operator[](size_t i) {
|
|
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
|
|
assertd(i < size(), "Array Out of Bounds");
|
|
return _str[i];
|
|
}
|
|
|
|
///
|
|
/// \brief Const-Array Access Operator
|
|
/// \param i the index to access
|
|
/// \returns a copy of the character
|
|
constexpr char operator[](size_t i) const {
|
|
assertd(i < size(), "Array Out of Bounds");
|
|
return _cstr[i];
|
|
}
|
|
|
|
///
|
|
/// \returns A pointer to the underlying allocation
|
|
constexpr char* data() {
|
|
return _str;
|
|
}
|
|
|
|
///
|
|
/// \brief Data Access
|
|
/// \returns A const qualified pointer to the underlying allocation
|
|
constexpr const char* data() const {
|
|
return _cstr;
|
|
}
|
|
|
|
///
|
|
/// \brief Implicit Dereference Cast
|
|
/// \returns A const qualified pointer to the underlying allocation
|
|
constexpr operator const char*() const {
|
|
return _cstr;
|
|
}
|
|
|
|
|
|
// Examination =========================================================================================================
|
|
|
|
///
|
|
/// \returns The length of the string to the first null-terminator
|
|
constexpr size_t length() const {
|
|
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 cstring& str, size_t i = 0, size_t n = npos) const {
|
|
if (i >= _size) {
|
|
return -1;
|
|
}
|
|
|
|
n = min(n, max(_size, str._size) + 1);
|
|
return ::strncmp(_cstr + i, str, n);
|
|
}
|
|
|
|
///
|
|
/// \brief String Equality
|
|
/// \param str the string to compare against
|
|
/// \returns True if all characters are equal, false otherwise
|
|
template<size_t n>
|
|
constexpr bool operator==(const char (&str)[n]) const {
|
|
return compare(cstring(str)) == 0;
|
|
}
|
|
|
|
///
|
|
/// \brief String Equality
|
|
/// \param str the string to compare against
|
|
/// \returns True if all characters are equal, false otherwise
|
|
constexpr bool operator==(const cstring& str) const {
|
|
return compare(str) == 0;
|
|
}
|
|
|
|
///
|
|
/// \brief Finds the index of the first occurrence of \f$c\f$ in the string
|
|
/// \param c the character to find
|
|
/// \param i the index to start at
|
|
/// \returns The index of \f$c\f$ if it occurs in the string, otherwise returns `size()`
|
|
constexpr size_t find(char c, size_t i = 0) const {
|
|
if (i >= _size) { // bounds check
|
|
return _size;
|
|
}
|
|
|
|
const char* loc = ::strchr(_cstr + i, c); // get location using strchr
|
|
return loc ? loc - _cstr : _size; // return size if not found
|
|
}
|
|
|
|
///
|
|
/// \brief Finds the index of the first occurrence of \f$str\f$ in the string.
|
|
/// \param str the string to find
|
|
/// \param i the index to start at
|
|
/// \returns The index of \f$str\f$ if it occurs in the string, otherwise returns `size()`
|
|
constexpr size_t find(const cstring& str, size_t i = 0) const {
|
|
if (i + str._size > _size) { // bounds check
|
|
return _size;
|
|
}
|
|
|
|
const char* loc = ::strstr(_cstr + i, str); // get location using strstr
|
|
return loc ? loc - _cstr : _size; // return size if not found
|
|
}
|
|
|
|
///
|
|
/// \brief Finds the index of the last occurrence of \f$c\f$ in the string.
|
|
/// \param c the string to find
|
|
/// \param i the index to start at
|
|
/// \returns The index of \f$c\f$ if it occurs in the string, otherwise returns `size()`
|
|
constexpr size_t rfind(char c, size_t i = npos) const {
|
|
if (_size == 0) {
|
|
return _size;
|
|
}
|
|
|
|
i = min(i, _size - 1); // clamp i to bounds
|
|
do {
|
|
if (_cstr[i] == c) return i; // loop backwards looking for c
|
|
} while (i--);
|
|
return _size; // base case
|
|
}
|
|
|
|
///
|
|
/// \brief Finds the index of the last occurrence of \f$str\f$ in the string.
|
|
/// \param str the string to find
|
|
/// \param i the index to start at
|
|
/// \returns The index of \f$str\f$ if it occurs in the string, otherwise returns `size()`
|
|
constexpr size_t rfind(const cstring& str, size_t i = npos) const {
|
|
if (_size == 0) {
|
|
return _size;
|
|
}
|
|
|
|
const char first = str[0];
|
|
i = min(i, _size - str._size);
|
|
do {
|
|
if(_cstr[i] == first) {
|
|
if (compare(str, i, str._size - 1) == 0) {
|
|
return i;
|
|
}
|
|
} // loop backwards looking for str
|
|
} while (i--);
|
|
return _size; // base case
|
|
}
|
|
|
|
private:
|
|
union { // hack to allow both const qualified and non-const strings
|
|
char* _str;
|
|
const char* _cstr;
|
|
};
|
|
size_t _size;
|
|
bool _const;
|
|
};
|
|
|
|
template<>
|
|
struct hash<cstring> : hash<byte_array> {
|
|
constexpr size_t operator()(const cstring& str) const {
|
|
return hash<byte_array>::operator()(byte_array(str, str.size()));
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_STRING_CSTRING_H
|