Files
fennec/include/fennec/fproc/strings/string.h
Medusa Slockbower 5ab2952e83 - Adjusted Formatting of tests
- Finished map implementation and unit tests

 TODO: Threading
2025-07-23 12:05:18 -04:00

445 lines
13 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_FPROC_STRINGS_STRING_H
#define FENNEC_FPROC_STRINGS_STRING_H
#include <fennec/fproc/strings/detail/__ctype.h>
#include <fennec/fproc/strings/cstring.h>
#include <fennec/lang/assert.h>
#include <fennec/memory/allocator.h>
#include <fennec/memory/common.h>
#include <fennec/math/common.h>
namespace fennec
{
// Forward def
template<typename AllocT = allocator<char>> struct _string;
// Alias for default allocator
using string = _string<>;
///
/// \brief Struct for wrapping c-style strings
///
/// \details behaviour guarantees that the underlying string is null-terminated
template<typename AllocT>
struct _string
{
public:
static constexpr size_t npos = -1;
using alloc_t = allocation<char, AllocT>;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, initializes empty string
constexpr _string()
: _str() {
}
///
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null characters
/// \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)
: _str(n + 1) {
fennec::memset(_str.data(), c, n);
_str[n] = '\0';
}
///
/// \brief Buffer Copy Constructor
/// \param str the buffer to copy
/// \tparam 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 `len` is the intended number of characters.
template<size_t n>
constexpr _string(const char str[n])
: _str(str, n + 1) {
_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(n + 1) {
fennec::memcpy(_str.data(), str, n);
_str[n] = '\0';
}
///
/// \brief Buffer Copy Constructor
/// \param str the buffer to copy
constexpr _string(const cstring& str)
: _str(str, str.size() + 1) {
}
///
/// \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)) {
}
///
/// \brief String Destructor, cleans up the underlying allocation
constexpr ~_string() = default; // allocation cleans up itself
// Properties ==========================================================================================================
///
/// \returns The size of the string excluding null terminator
constexpr size_t size() const {
return _str.capacity() - 1;
}
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) {
return _str[i];
}
///
/// \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");
return _str[i];
}
///
/// \brief Dereference Operator
/// \returns A const qualified pointer to the underlying allocation
constexpr const char* operator*() const {
return _str.data();
}
///
/// \brief Implicit Dereference Cast
constexpr operator const char*() const {
return _str.data();
}
// Examination =========================================================================================================
///
/// \returns The length of the string to the first null-terminator
constexpr size_t length() const {
return find('\0');
}
///
/// \brief String Comparison
/// \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 cstring& str, size_t i = 0, size_t n = npos) const {
if (i >= size()) { // bounds check
return -1;
}
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
return ::strncmp(_str.data() + i, str, n);
}
///
/// \brief String Comparison
/// \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 string& 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 ::strncmp(_str.data() + i, str, n);
}
constexpr bool operator==(const _string& 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
/// \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(_str.data() + i, c); // get location using strchr
return loc ? loc - _str.data() : 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 string& str, size_t i = 0) const { // bounds check
if (i >= size()) { // bounds check
return size();
}
const char* loc = ::strstr(_str, str);
return loc ? loc - _str : size();
}
///
/// \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 cstring& str, size_t i = 0) const {
if (i + str.size() > size()) { // bounds check
return size();
}
const char* loc = ::strstr(_str + i, str); // get location using strstr
return loc ? loc - _str : 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 (_str[i] == c) { // loop backwards looking for c
return i;
}
} 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(_str[i] == first) {
if (compare(str, i) == 0) {
return i;
}
}
} 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 string& str, size_t i = npos) const {
if (size() == 0) {
return size();
}
const char first = str[0];
i = min(i, size() - str.size());
do {
if(_str[i] == first) {
if (compare(str, i) == 0) { // loop backwards looking for str
return i;
}
}
} while (i--);
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) {
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
/// \param n the number of characters
/// \return
constexpr _string substring(size_t i, size_t n = npos) const {
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());
res[size()] = c; // Set the last character to c
return res;
}
friend constexpr _string operator+(char c, _string& str) {
return _string(c, 1) + str;
}
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
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
return res;
}
constexpr _string& operator+=(char c) {
size_t x = size();
resize(x + 1);
_str[x] = c;
return *this;
}
constexpr _string& operator+=(const cstring& str) {
size_t x = size();
resize(x + str.size());
fennec::memcpy(&_str[x], str, str.size());
return *this;
}
constexpr _string& operator+=(const _string& str) {
size_t x = size();
resize(x + str.size());
fennec::memcpy(&_str[x], str, str.size());
return *this;
}
private:
alloc_t _str;
};
template<>
struct hash<string> : hash<byte_array> {
constexpr size_t operator()(const string& str) const {
return hash<byte_array>::operator()(byte_array(*str, str.size()));
}
};
}
#endif // FENNEC_FPROC_STRINGS_STRING_H