Files
nostalgia/deps/ox/src/ox/std/istring.hpp

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;
}
}