- Switched back to custom window management, taking another stab
- Refactored lang yet again, `fennec/lang` is now C++ language. `fennec/string` `fennec/filesystem` and `fennec/format` are now independent.
This commit is contained in:
370
include/fennec/string/cstring.h
Normal file
370
include/fennec/string/cstring.h
Normal file
@@ -0,0 +1,370 @@
|
||||
// =====================================================================================================================
|
||||
// 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_LANGPROC_STRINGS_CSTRING_H
|
||||
#define FENNEC_LANGPROC_STRINGS_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) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Buffer Constructor, wraps the provided C-Style string
|
||||
/// \param str the buffer to wrap
|
||||
/// \param n the number of characters in the buffer plus the null terminator
|
||||
constexpr cstring(char* str, size_t n)
|
||||
: _str(str)
|
||||
, _size(n - 1)
|
||||
, _const(false) {
|
||||
if constexpr(not is_constant_evaluated()) {
|
||||
assert(_str[n - 1] == '\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(char(&str)[n])
|
||||
: _str(str)
|
||||
, _size(n - 1)
|
||||
, _const(false) {
|
||||
if constexpr(not is_constant_evaluated()) {
|
||||
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Buffer Constructor, wraps the provided C-Style string
|
||||
/// \param str the buffer to wrap
|
||||
/// \param n the number of characters in the buffer plus the null terminator
|
||||
constexpr cstring(const char* str, size_t n)
|
||||
: _cstr(str)
|
||||
, _size(n - 1)
|
||||
, _const(true) {
|
||||
if constexpr(not is_constant_evaluated()) {
|
||||
assert(_str[n - 1] == '\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 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];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Data Access
|
||||
/// \returns A const qualified 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
|
||||
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 `c` in the string
|
||||
/// \param c the character 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 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 `str` in the string.
|
||||
/// \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 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 `c` in the string.
|
||||
/// \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(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 `str` in the string.
|
||||
/// \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 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_LANGPROC_STRINGS_CSTRING_H
|
||||
Reference in New Issue
Block a user