- Started Tests for Quaternions - Fixed some style issues with constructors
428 lines
13 KiB
C++
428 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_wstringS_wstring_H
|
|
#define FENNEC_FPROC_wstringS_wstring_H
|
|
|
|
#include <fennec/fproc/strings/detail/__ctype.h>
|
|
#include <fennec/fproc/strings/wcstring.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<wchar_t>> struct _wstring;
|
|
|
|
// Alias for default allocator
|
|
using wstring = _wstring<>;
|
|
|
|
///
|
|
/// \brief Struct for wrapping c-style strings
|
|
///
|
|
/// \details behaviour guarantees that the underlying string is null-terminated
|
|
template<typename AllocT>
|
|
struct _wstring
|
|
{
|
|
public:
|
|
static constexpr size_t npos = -1;
|
|
using alloc_t = allocation<wchar_t, AllocT>;
|
|
|
|
|
|
// Constructors ========================================================================================================
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes empty string
|
|
constexpr _wstring()
|
|
: _str() {
|
|
}
|
|
|
|
///
|
|
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null wchar_tacters
|
|
/// \param n the number of wchar_tacters
|
|
///
|
|
/// \details adds additional wchar_tacter 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 wchar_tacter `c`
|
|
/// \param c the wchar_tacter to fill with
|
|
/// \param n the number of wchar_tacters
|
|
///
|
|
/// \details adds additional wchar_tacter for null termination.
|
|
constexpr _wstring(wchar_t c, size_t n)
|
|
: _str(n + 1) {
|
|
fennec::wmemset(_str.data(), c, n); _str[n] = '\0';
|
|
}
|
|
///
|
|
/// \brief Buffer Copy Constructor
|
|
/// \param str the buffer to copy
|
|
/// \param len number of wchar_tacters in the buffer
|
|
///
|
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
|
template<size_t n>
|
|
constexpr _wstring(const wchar_t str[n])
|
|
: _str(str, n + 1) {
|
|
::wcsncpy(_str.data(), str, n);
|
|
_str[n] = '\0';
|
|
}
|
|
|
|
///
|
|
/// \brief Buffer Copy Constructor
|
|
/// \param str the buffer to copy
|
|
/// \param len number of wchar_tacters in the buffer
|
|
///
|
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
|
constexpr _wstring(const wchar_t* str, size_t n)
|
|
: _str(str, n + 1) {
|
|
::wcsncpy(_str.data(), str, n);
|
|
_str[n] = '\0';
|
|
}
|
|
|
|
///
|
|
/// \brief Buffer Copy Constructor
|
|
/// \param str the buffer to copy
|
|
/// \param len number of wchar_tacters in the buffer
|
|
///
|
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
|
constexpr _wstring(const wcstring& str)
|
|
: _str(str, str.size() + 1) {
|
|
_str[str.size()] = '\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)) {
|
|
}
|
|
|
|
///
|
|
/// \brief String Destructor, cleans up the underlying allocation
|
|
constexpr ~_wstring() = 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 wchar_tacter
|
|
constexpr wchar_t& operator[](int i) {
|
|
return _str[i];
|
|
}
|
|
|
|
///
|
|
/// \brief Const-Array Access Operator
|
|
/// \param i the index to access
|
|
/// \returns a copy of the wchar_tacter
|
|
constexpr wchar_t operator[](int i) const {
|
|
assertd(i >= 0 && i < size(), "Array Out of Bounds");
|
|
return _str[i];
|
|
}
|
|
|
|
///
|
|
/// \brief Dereference Operator
|
|
/// \returns A const qualified pointer to the underlying allocation
|
|
constexpr const wchar_t* operator*() const {
|
|
return _str.data();
|
|
}
|
|
|
|
///
|
|
/// \brief Implicit Dereference Cast
|
|
constexpr operator const wchar_t*() 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 wcstring& 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 ::wcsncmp(_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 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);
|
|
}
|
|
|
|
constexpr bool operator==(const _wstring& str) const {
|
|
return compare(str) == 0;
|
|
}
|
|
|
|
///
|
|
/// \brief Finds the index of the first occurrence of `c` in the string
|
|
/// \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
|
|
}
|
|
|
|
///
|
|
/// \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
|
|
if (i >= size()) { // bounds check
|
|
return size();
|
|
}
|
|
|
|
const wchar_t* loc = ::wcsstr(_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 wcstring& str, size_t i = 0) const {
|
|
if (i + str.size() > size()) { // bounds check
|
|
return size();
|
|
}
|
|
|
|
const wchar_t* loc = ::wcsstr(_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(wchar_t 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 wcstring& str, size_t i = npos) const {
|
|
if (size() == 0) {
|
|
return size();
|
|
}
|
|
const wchar_t 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 wstring& str, size_t i = npos) const {
|
|
if (size() == 0) {
|
|
return size();
|
|
}
|
|
const wchar_t 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::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.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 wchar_tacters
|
|
/// \return
|
|
constexpr wstring substring(size_t i, size_t n = npos) const {
|
|
if (i >= size()) {
|
|
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 wchar_tacter to c
|
|
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
|
|
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
|
|
return res;
|
|
}
|
|
|
|
constexpr wstring& operator+=(wchar_t c) {
|
|
size_t x = size();
|
|
resize(x + 1);
|
|
_str[x] = c;
|
|
return *this;
|
|
}
|
|
|
|
constexpr wstring& operator+=(const wcstring& str) {
|
|
size_t x = size();
|
|
resize(x + str.size());
|
|
fennec::wmemcpy(&_str[x], str, str.size());
|
|
return *this;
|
|
}
|
|
|
|
constexpr wstring& operator+=(const wstring& str) {
|
|
size_t x = size();
|
|
resize(x + str.size());
|
|
fennec::wmemcpy(&_str[x], str, str.size());
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
alloc_t _str;
|
|
};
|
|
|
|
}
|
|
|
|
|
|
#endif // FENNEC_FPROC_wstringS_wstring_H
|