276 lines
6.5 KiB
C++
276 lines
6.5 KiB
C++
/*
|
|
* Copyright 2015 - 2025 gary@drinkingtea.net
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "array.hpp"
|
|
#include "concepts.hpp"
|
|
#include "def.hpp"
|
|
#include "cstrops.hpp"
|
|
#include "memops.hpp"
|
|
#include "error.hpp"
|
|
#include "buffer.hpp"
|
|
#include "ignore.hpp"
|
|
#include "stringview.hpp"
|
|
#include "typetraits.hpp"
|
|
#include "strconv.hpp"
|
|
|
|
namespace ox {
|
|
|
|
// Inline String
|
|
template<std::size_t StrCap>
|
|
class IString {
|
|
private:
|
|
size_t m_size{};
|
|
ox::Array<char, StrCap + 1> m_buff;
|
|
|
|
public:
|
|
constexpr IString() noexcept;
|
|
|
|
constexpr IString(StringView str) noexcept;
|
|
|
|
constexpr IString(const char *str) noexcept;
|
|
|
|
constexpr IString &operator=(StringViewCR str) noexcept;
|
|
|
|
constexpr IString &operator=(const char *str) noexcept;
|
|
|
|
constexpr IString &operator=(Integer_c auto i) noexcept;
|
|
|
|
|
|
constexpr bool operator==(const char *other) const noexcept;
|
|
|
|
constexpr bool operator==(const OxString_c auto &other) const noexcept;
|
|
|
|
constexpr bool operator!=(const char *other) const noexcept;
|
|
|
|
constexpr bool operator!=(const OxString_c auto &other) noexcept;
|
|
|
|
constexpr char operator[](std::size_t i) const noexcept;
|
|
|
|
constexpr char &operator[](std::size_t i) noexcept;
|
|
|
|
constexpr Error append(const char *str, std::size_t strLen) noexcept;
|
|
|
|
constexpr Error append(ox::StringView str) noexcept;
|
|
|
|
[[nodiscard]]
|
|
constexpr const char *data() const noexcept;
|
|
|
|
[[nodiscard]]
|
|
constexpr char *data() noexcept;
|
|
|
|
[[nodiscard]]
|
|
constexpr const char *c_str() const noexcept;
|
|
|
|
/**
|
|
* Returns the number of characters in this string.
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr std::size_t size() const noexcept;
|
|
|
|
/**
|
|
* Returns the number of bytes used for this string.
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr std::size_t bytes() const noexcept;
|
|
|
|
constexpr ox::Error resize(size_t sz) noexcept;
|
|
|
|
/**
|
|
* Resizes without clearing memory between current end and new end.
|
|
*/
|
|
constexpr ox::Error unsafeResize(size_t sz) noexcept;
|
|
|
|
/**
|
|
* Returns the capacity of bytes for this string.
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr static std::size_t cap() noexcept {
|
|
return StrCap;
|
|
}
|
|
};
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size>::IString() noexcept: m_buff{{0}} {
|
|
}
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size>::IString(StringView str) noexcept: m_buff{{0}} {
|
|
operator=(str);
|
|
}
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size>::IString(const char *str) noexcept: m_buff{{0}} {
|
|
operator=(str);
|
|
}
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size> &IString<size>::operator=(Integer_c auto i) noexcept {
|
|
ox::Array<char, 22> s;
|
|
ox::CharBuffWriter w(s);
|
|
std::ignore = ox::writeItoa(i, w);
|
|
this->operator=({s.data(), w.tellp()});
|
|
return *this;
|
|
}
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size> &IString<size>::operator=(ox::StringViewCR str) noexcept {
|
|
std::size_t strLen = str.size();
|
|
if (cap() < strLen) {
|
|
strLen = cap();
|
|
}
|
|
m_size = strLen;
|
|
ox::listcpy(m_buff.data(), str.data(), strLen);
|
|
// make sure last element is a null terminator
|
|
m_buff[strLen] = 0;
|
|
return *this;
|
|
}
|
|
|
|
template<std::size_t size>
|
|
constexpr IString<size> &IString<size>::operator=(const char *str) noexcept {
|
|
operator=(ox::StringView{str});
|
|
return *this;
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr bool IString<StrCap>::operator==(const char *other) const noexcept {
|
|
return ox::StringView(*this) == other;
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr bool IString<StrCap>::operator==(const OxString_c auto &other) const noexcept {
|
|
return ox::StringView(*this) == ox::StringView(other);
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr bool IString<StrCap>::operator!=(const char *other) const noexcept {
|
|
return !operator==(other);
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr bool IString<StrCap>::operator!=(const OxString_c auto &other) noexcept {
|
|
return !operator==(other);
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr char IString<StrCap>::operator[](std::size_t i) const noexcept {
|
|
return m_buff[i];
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr char &IString<StrCap>::operator[](std::size_t i) noexcept {
|
|
return m_buff[i];
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr Error IString<StrCap>::append(const char *str, std::size_t strLen) noexcept {
|
|
Error err{};
|
|
auto const currentLen = size();
|
|
if (cap() < currentLen + strLen) {
|
|
strLen = cap() - currentLen;
|
|
err = ox::Error(1, "Insufficient space for full string");
|
|
}
|
|
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
|
ox::strncpy(m_buff.data() + currentLen, str, strLen);
|
|
OX_CLANG_NOWARN_END
|
|
// make sure last element is a null terminator
|
|
m_buff[currentLen + strLen] = 0;
|
|
m_size += strLen;
|
|
return err;
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr Error IString<StrCap>::append(ox::StringView str) noexcept {
|
|
return append(str.data(), str.size());
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr const char *IString<StrCap>::data() const noexcept {
|
|
return static_cast<const char*>(m_buff.data());
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr char *IString<StrCap>::data() noexcept {
|
|
return static_cast<char*>(m_buff.data());
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr const char *IString<StrCap>::c_str() const noexcept {
|
|
return static_cast<const char*>(m_buff.data());
|
|
}
|
|
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr std::size_t IString<StrCap>::size() const noexcept {
|
|
return m_size;
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr std::size_t IString<StrCap>::bytes() const noexcept {
|
|
return m_size + 1;
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr ox::Error IString<StrCap>::resize(size_t sz) noexcept {
|
|
if (sz > StrCap) [[unlikely]] {
|
|
return ox::Error(1, "Trying to extend IString beyond its cap");
|
|
}
|
|
for (auto i = m_size; i < sz; ++i) {
|
|
m_buff[i] = 0;
|
|
}
|
|
m_size = sz;
|
|
return {};
|
|
}
|
|
|
|
template<std::size_t StrCap>
|
|
constexpr ox::Error IString<StrCap>::unsafeResize(size_t sz) noexcept {
|
|
if (sz > StrCap) [[unlikely]] {
|
|
return ox::Error(1, "Trying to extend IString beyond its cap");
|
|
}
|
|
m_size = sz;
|
|
return {};
|
|
}
|
|
|
|
template<size_t sz>
|
|
struct MaybeView<ox::IString<sz>> {
|
|
using type = ox::StringView;
|
|
};
|
|
|
|
|
|
template<Integer_c Integer>
|
|
[[nodiscard]]
|
|
constexpr auto intToStr(Integer v) noexcept {
|
|
constexpr auto Cap = [] {
|
|
auto out = 0;
|
|
switch (sizeof(Integer)) {
|
|
case 1:
|
|
out = 3;
|
|
break;
|
|
case 2:
|
|
out = 5;
|
|
break;
|
|
case 4:
|
|
out = 10;
|
|
break;
|
|
case 8:
|
|
out = 21;
|
|
break;
|
|
}
|
|
return out + ox::is_signed_v<Integer>;
|
|
}();
|
|
ox::IString<Cap> out;
|
|
std::ignore = out.resize(out.cap());
|
|
ox::CharBuffWriter w{{out.data(), out.cap()}};
|
|
std::ignore = writeItoa(v, w);
|
|
std::ignore = out.resize(w.tellp());
|
|
return out;
|
|
}
|
|
|
|
}
|