Files
fennec/include/fennec/string/cstring.h
Medusa Slockbower 7c2f89b331 - A few Vulkan wrapper structs
- 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.
2026-01-02 15:38:03 -05:00

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