Squashed 'deps/oxlib/' content from commit 85f17c41
git-subtree-dir: deps/oxlib git-subtree-split: 85f17c4188e266b82ba8858d21f67289c72e7b9a
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename It, typename T>
|
||||
constexpr ox::Result<size_t> findIdx(It begin, It end, T const&value) {
|
||||
auto it = begin;
|
||||
for (; it != end; ++it) {
|
||||
if (*it == value) {
|
||||
return it.offset();
|
||||
}
|
||||
}
|
||||
return ox::Error{1, "item not found"};
|
||||
}
|
||||
|
||||
template<typename It, typename T>
|
||||
constexpr It find(It begin, It end, T const&value) {
|
||||
for (; begin != end; ++begin) {
|
||||
if (*begin == value) {
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
constexpr It find_if(It begin, It end, auto predicate) {
|
||||
for (; begin != end; ++begin) {
|
||||
if (predicate(*begin)) {
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename It, typename Size, typename OutIt>
|
||||
constexpr OutIt copy_n(It in, Size cnt, OutIt out) {
|
||||
for (Size i = 0; i < cnt; ++i) {
|
||||
*out = *in;
|
||||
++out;
|
||||
++in;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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 "assert.hpp"
|
||||
#include "array.hpp"
|
||||
#include "def.hpp"
|
||||
#include "span.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<bool unique>
|
||||
class AnyPtrT {
|
||||
private:
|
||||
struct WrapBase {
|
||||
virtual constexpr ~WrapBase() = default;
|
||||
virtual constexpr WrapBase *copyTo(Span<char> s) const noexcept = 0;
|
||||
virtual constexpr operator bool() const noexcept = 0;
|
||||
virtual void free() noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Wrap final: WrapBase {
|
||||
T *data{};
|
||||
explicit constexpr Wrap(T *pData) noexcept: data(pData) {
|
||||
}
|
||||
constexpr WrapBase *copyTo(Span<char> s) const noexcept override {
|
||||
oxAssert(s.size() >= sizeof(Wrap), "too small buffer");
|
||||
if (std::is_constant_evaluated()) {
|
||||
return new Wrap(data);
|
||||
} else {
|
||||
return new(s.data()) Wrap(data);
|
||||
}
|
||||
}
|
||||
constexpr operator bool() const noexcept override {
|
||||
return data != nullptr;
|
||||
}
|
||||
constexpr void free() noexcept override {
|
||||
safeDelete(data);
|
||||
data = {};
|
||||
}
|
||||
};
|
||||
|
||||
union {
|
||||
WrapBase *m_wrapPtr{};
|
||||
AllocAlias<Wrap<void*>> m_wrapData;
|
||||
} m_data;
|
||||
|
||||
public:
|
||||
constexpr AnyPtrT() noexcept = default;
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtrT(T *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
setWrapPtr(new Wrap<T>(ptr));
|
||||
} else {
|
||||
new(m_data.m_wrapData.data()) Wrap<T>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr AnyPtrT(AnyPtrT const &other) noexcept requires(!unique) {
|
||||
if (other) {
|
||||
setWrapPtr(other.getWrapPtr()->copyTo(m_data.m_wrapData.buff));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr AnyPtrT(AnyPtrT &&other) noexcept {
|
||||
if (other) {
|
||||
setWrapPtr(other.getWrapPtr()->copyTo(m_data.m_wrapData.buff));
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
m_data.m_wrapData = {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~AnyPtrT() noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtrT &operator=(T *ptr) noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
setWrapPtr(new Wrap(ptr));
|
||||
} else {
|
||||
new(m_data.m_wrapData.data()) Wrap(ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtrT &operator=(AnyPtrT const &ptr) noexcept requires(!unique) {
|
||||
if (this != &ptr) {
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
if (ptr) {
|
||||
ptr.getWrapPtr()->copyTo(m_data.m_wrapData.buff);
|
||||
}
|
||||
} else {
|
||||
if (ptr) {
|
||||
setWrapPtr(ptr.getWrapPtr()->copyTo(m_data.m_wrapData.buff));
|
||||
} else {
|
||||
setWrapPtr(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtrT &operator=(AnyPtrT &&ptr) noexcept {
|
||||
if (this != &ptr) {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
if (ptr) {
|
||||
setWrapPtr(ptr.getWrapPtr()->copyTo(m_data.m_wrapData.buff));
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(ptr.m_data.m_wrapPtr);
|
||||
setWrapPtr(nullptr);
|
||||
}
|
||||
} else {
|
||||
m_data = {};
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept {
|
||||
return getWrapPtr() && *getWrapPtr();
|
||||
}
|
||||
|
||||
constexpr void free() noexcept {
|
||||
if (auto p = getWrapPtr()) {
|
||||
p->free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_data.m_wrapPtr);
|
||||
}
|
||||
m_data.m_wrapData = {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *get() const noexcept {
|
||||
if constexpr(defines::HasRTTI) {
|
||||
return dynamic_cast<Wrap<T> const*>(getWrapPtr())->data;
|
||||
}
|
||||
return static_cast<Wrap<T> const*>(getWrapPtr())->data;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void setWrapPtr(WrapBase *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_data.m_wrapPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr WrapBase *getWrapPtr() noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
return m_data.m_wrapPtr;
|
||||
} else {
|
||||
return std::launder(reinterpret_cast<WrapBase*>(m_data.m_wrapData.data()));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr WrapBase const *getWrapPtr() const noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
return m_data.m_wrapPtr;
|
||||
} else {
|
||||
return std::launder(reinterpret_cast<WrapBase const*>(m_data.m_wrapData.data()));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AnyPtr = detail::AnyPtrT<false>;
|
||||
using UAnyPtr = detail::AnyPtrT<true>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "def.hpp"
|
||||
#include "error.hpp"
|
||||
#include "initializerlist.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "math.hpp"
|
||||
#include "new.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
class Array {
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template<typename RefType = T&, typename PtrType = T*, bool reverse = false>
|
||||
using iterator = SpanIterator<T, RefType, PtrType, reverse>;
|
||||
|
||||
private:
|
||||
T m_items[ArraySize]{};
|
||||
|
||||
public:
|
||||
constexpr Array() noexcept = default;
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Array(Args ...list) noexcept: m_items{std::move(list)...} {
|
||||
}
|
||||
|
||||
constexpr Array(std::initializer_list<T> list) noexcept;
|
||||
|
||||
constexpr Array(const Array &other);
|
||||
|
||||
constexpr Array(Array &&other) noexcept;
|
||||
|
||||
constexpr ~Array() = default;
|
||||
|
||||
constexpr iterator<> begin() noexcept {
|
||||
return iterator<>(&m_items[0], 0, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<> end() noexcept {
|
||||
return iterator<>(&m_items[0], ArraySize, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<const T&, const T*> begin() const noexcept {
|
||||
return iterator<const T&, const T*>(&m_items[0], 0, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<const T&, const T*> end() const noexcept {
|
||||
return iterator<const T&, const T*>(&m_items[0], ArraySize, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<T&, T*, true> rbegin() noexcept {
|
||||
return iterator<T&, T*, true>(&m_items[0], ArraySize - 1, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<T&, T*, true> rend() noexcept {
|
||||
return iterator<T&, T*, true>(&m_items[0], MaxValue<size_type>, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<const T&, const T*, true> rbegin() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, ArraySize - 1, ArraySize);
|
||||
}
|
||||
|
||||
constexpr iterator<const T&, const T*, true> rend() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, ArraySize);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Array &other) const;
|
||||
|
||||
constexpr Array &operator=(const Array &other);
|
||||
|
||||
constexpr Array &operator=(Array &&other) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &operator[](std::size_t i) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const T &operator[](std::size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T *data() noexcept {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const T *data() const noexcept {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(const T&) const;
|
||||
|
||||
};
|
||||
|
||||
template<typename T, std::size_t ArraySize, typename RefType, bool reverse>
|
||||
using ArrayIt = typename Array<T, ArraySize>::template iterator<RefType, reverse>;
|
||||
|
||||
template<typename T, std::size_t ArraySize, typename RefType, bool reverse>
|
||||
constexpr ArrayIt<T, ArraySize, RefType, reverse> operator+(std::size_t n, const ArrayIt<T, ArraySize, RefType, reverse> &a) {
|
||||
return a + n;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr Array<T, ArraySize>::Array(std::initializer_list<T> list) noexcept {
|
||||
for (auto i = 0ul; auto &item : list) {
|
||||
this->operator[](i) = item;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr Array<T, ArraySize>::Array(const Array &other) {
|
||||
for (std::size_t i = 0; i < ArraySize; ++i) {
|
||||
m_items[i] = T(other.m_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr Array<T, ArraySize>::Array(Array &&other) noexcept {
|
||||
if (this != &other) {
|
||||
for (std::size_t i = 0; i < ArraySize; ++i) {
|
||||
m_items[i] = T(std::move(other.m_items[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr bool Array<T, ArraySize>::operator==(const Array &other) const {
|
||||
if (std::is_constant_evaluated()) {
|
||||
for (std::size_t i = 0; i < ArraySize; i++) {
|
||||
if (!(m_items[i] == other.m_items[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return memcmp(this, &other, sizeof(*this)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr Array<T, ArraySize> &Array<T, ArraySize>::operator=(const Array &other) {
|
||||
if (this != &other) {
|
||||
for (std::size_t i = 0; i < ArraySize; ++i) {
|
||||
m_items[i] = other.m_items[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr Array<T, ArraySize> &Array<T, ArraySize>::operator=(Array &&other) noexcept {
|
||||
if (this != &other) {
|
||||
for (std::size_t i = 0; i < ArraySize; ++i) {
|
||||
m_items[i] = std::move(other.m_items[i]);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr T &Array<T, ArraySize>::operator[](std::size_t i) noexcept {
|
||||
boundsCheck(i, size(), "Array access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr const T &Array<T, ArraySize>::operator[](std::size_t i) const noexcept {
|
||||
boundsCheck(i, size(), "Array access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr std::size_t Array<T, ArraySize>::size() const noexcept {
|
||||
return ArraySize;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t ArraySize>
|
||||
constexpr bool Array<T, ArraySize>::contains(const T &v) const {
|
||||
for (std::size_t i = 0; i < ArraySize; i++) {
|
||||
if (m_items[i] == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "error.hpp"
|
||||
#include "realstd.hpp"
|
||||
#include "stacktrace.hpp"
|
||||
#include "trace.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
[[noreturn]]
|
||||
void panic(
|
||||
Error const&err,
|
||||
StringViewCR panicMsg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept;
|
||||
|
||||
[[noreturn]]
|
||||
inline void panic(
|
||||
StringViewCR panicMsg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
panic(Error{1}, panicMsg, src);
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
constexpr void constexprPanic(
|
||||
StringViewCR panicMsg,
|
||||
Error const &err = {},
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
panic(err, panicMsg, src);
|
||||
} else {
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
|
||||
void assertFailFuncRuntime(
|
||||
StringViewCR assertTxt,
|
||||
StringViewCR msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept;
|
||||
|
||||
void assertFailFuncRuntime(
|
||||
Error const &err,
|
||||
StringViewCR,
|
||||
StringViewCR assertMsg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept;
|
||||
|
||||
constexpr void assertFunc(
|
||||
bool const pass,
|
||||
[[maybe_unused]]StringViewCR assertTxt,
|
||||
[[maybe_unused]]StringViewCR msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if (!pass) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
assertFailFuncRuntime(assertTxt, msg, src);
|
||||
} else {
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void assertFunc(
|
||||
Error const &err,
|
||||
StringViewCR,
|
||||
StringViewCR assertMsg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if (err) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
assertFailFuncRuntime(err, {}, assertMsg, src);
|
||||
} else {
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void expect(
|
||||
auto const &actual,
|
||||
auto const &expected,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if (actual != expected) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
#if defined(OX_USE_STDLIB)
|
||||
oxErrf(
|
||||
"\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n",
|
||||
src.file_name(),
|
||||
src.line(),
|
||||
"Value incorrect");
|
||||
oxErrf(
|
||||
"expected: {}\nactual: {}\n",
|
||||
detail::toStringView<true>(expected),
|
||||
detail::toStringView<true>(actual));
|
||||
printStackTrace(2);
|
||||
oxTracef(
|
||||
"assert.expect", "Failed assert: {} == {} [{}:{}]",
|
||||
detail::toStringView<true>(actual),
|
||||
detail::toStringView<true>(expected),
|
||||
src.file_name(),
|
||||
src.line());
|
||||
std::abort();
|
||||
#else
|
||||
constexprPanic("Comparison failed", {}, src);
|
||||
#endif
|
||||
} else {
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "def.hpp"
|
||||
#include "cstrops.hpp"
|
||||
#include "iterator.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox::detail {
|
||||
|
||||
class BaseStringView {
|
||||
public:
|
||||
template<typename RefType = char&, typename PtrType = char*, bool reverse = false>
|
||||
struct iterator: public Iterator<std::bidirectional_iterator_tag, char> {
|
||||
private:
|
||||
PtrType m_t = nullptr;
|
||||
std::size_t m_offset = 0;
|
||||
std::size_t m_max = 0;
|
||||
|
||||
public:
|
||||
constexpr iterator() noexcept = default;
|
||||
|
||||
constexpr iterator(PtrType t, std::size_t offset, std::size_t max) noexcept {
|
||||
m_t = t;
|
||||
m_offset = offset;
|
||||
m_max = max;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto offset() const noexcept {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
constexpr iterator operator+(std::size_t s) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return iterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
|
||||
} else {
|
||||
return iterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto operator-(const iterator &other) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return m_offset + other.m_offset;
|
||||
} else {
|
||||
return m_offset - other.m_offset;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr iterator operator-(std::size_t s) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return iterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
|
||||
} else {
|
||||
return iterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr iterator &operator+=(std::size_t s) noexcept {
|
||||
if constexpr(reverse) {
|
||||
m_offset = max<std::size_t>(m_offset - s, 0);
|
||||
} else {
|
||||
m_offset = min(m_offset + s, m_max);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator &operator-=(std::size_t s) noexcept {
|
||||
if constexpr(reverse) {
|
||||
m_offset = min(m_offset + s, m_max);
|
||||
} else {
|
||||
m_offset = max<std::size_t>(m_offset - s, 0);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator &operator++() noexcept {
|
||||
return operator+=(1);
|
||||
}
|
||||
|
||||
constexpr iterator &operator--() noexcept {
|
||||
return operator-=(1);
|
||||
}
|
||||
|
||||
constexpr RefType operator*() const noexcept {
|
||||
return m_t[m_offset];
|
||||
}
|
||||
|
||||
constexpr RefType operator[](std::size_t s) const noexcept {
|
||||
return m_t[s];
|
||||
}
|
||||
|
||||
constexpr bool operator<(const iterator &other) const noexcept {
|
||||
return m_offset < other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const iterator &other) const noexcept {
|
||||
return m_offset > other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const iterator &other) const noexcept {
|
||||
return m_offset <= other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const iterator &other) const noexcept {
|
||||
return m_offset >= other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator &other) const noexcept {
|
||||
return m_t == other.m_t && m_offset == other.m_offset && m_max == other.m_max;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const iterator &other) const noexcept {
|
||||
return m_t != other.m_t || m_offset != other.m_offset || m_max != other.m_max;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
const char *m_str = nullptr;
|
||||
std::size_t m_len = 0;
|
||||
protected:
|
||||
constexpr BaseStringView() noexcept = default;
|
||||
|
||||
constexpr BaseStringView(BaseStringView const&sv) noexcept = default;
|
||||
|
||||
constexpr explicit BaseStringView(std::nullptr_t) noexcept {}
|
||||
|
||||
constexpr explicit BaseStringView(const char *str) noexcept: m_str(str), m_len(str ? ox::strlen(str) : 0) {}
|
||||
|
||||
constexpr explicit BaseStringView(const char *str, std::size_t len) noexcept: m_str(str), m_len(len) {}
|
||||
|
||||
public:
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*> begin() const noexcept {
|
||||
return {m_str, 0, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*> end() const noexcept {
|
||||
return {m_str, m_len, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*> cbegin() const noexcept {
|
||||
return {m_str, 0, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*> cend() const noexcept {
|
||||
return {m_str, m_len, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*, true> crbegin() const noexcept {
|
||||
return {m_str, m_len - 1, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*, true> crend() const noexcept {
|
||||
return {m_str, MaxValue<std::size_t>, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*, true> rbegin() const noexcept {
|
||||
return {m_str, m_len - 1, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const char&, const char*, true> rend() const noexcept {
|
||||
return {m_str, MaxValue<std::size_t>, m_len};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto bytes() const noexcept {
|
||||
return m_len;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto size() const noexcept {
|
||||
return m_len;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto *data() const noexcept {
|
||||
return &m_str[0];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &front() const noexcept {
|
||||
return m_str[0];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &back() const noexcept {
|
||||
return m_str[m_len - 1];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &operator[](std::size_t i) const noexcept {
|
||||
return m_str[i];
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr void set(const char *str, std::size_t len) noexcept {
|
||||
m_str = str;
|
||||
m_len = len;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if defined(OX_USE_STDLIB)
|
||||
#include <bit>
|
||||
#endif
|
||||
|
||||
#include "defines.hpp"
|
||||
#include "memops.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
#if !defined(OX_USE_STDLIB)
|
||||
namespace std {
|
||||
|
||||
template<typename To, typename From>
|
||||
[[nodiscard]]
|
||||
constexpr To bit_cast(const From &src) noexcept requires(sizeof(To) == sizeof(From)) {
|
||||
return __builtin_bit_cast(To, src);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename To, typename From>
|
||||
constexpr To cbit_cast(From src) noexcept requires(sizeof(To) == sizeof(From)) {
|
||||
To dst = {};
|
||||
ox::memcpy(&dst, &src, sizeof(src));
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T rotl(T i, int shift) noexcept {
|
||||
constexpr auto bits = sizeof(i) * 8;
|
||||
return (i << static_cast<T>(shift)) | (i >> (bits - static_cast<T>(shift)));
|
||||
}
|
||||
|
||||
template<typename T, typename B = int>
|
||||
[[nodiscard]]
|
||||
constexpr T onMask(B bits = sizeof(T) << 3 /* *8 */) noexcept {
|
||||
T out = T(0);
|
||||
for (B i = 0; i < bits; i++) {
|
||||
out |= static_cast<T>(1) << i;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto MaxValue = onMask<T>(is_signed_v<T> ? sizeof(T) * 8 - 1 : sizeof(T) * 8);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 <ox/std/error.hpp>
|
||||
#include <ox/std/new.hpp>
|
||||
|
||||
#include "point.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
class Bounds {
|
||||
|
||||
public:
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.Bounds";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
constexpr Bounds() noexcept = default;
|
||||
|
||||
constexpr Bounds(int x, int y, int w, int h) noexcept;
|
||||
|
||||
constexpr Bounds(const Point &pt1, const Point &pt2) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool intersects(const Bounds &other) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(int x, int y) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(const Point &pt) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr int x2() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr int y2() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Point pt1() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Point pt2() const noexcept;
|
||||
|
||||
constexpr void setPt2(const Point &pt) noexcept;
|
||||
|
||||
private:
|
||||
constexpr void set(const Point &pt1, const Point &pt2) noexcept;
|
||||
|
||||
};
|
||||
|
||||
constexpr Bounds::Bounds(int x, int y, int w, int h) noexcept {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->width = w;
|
||||
this->height = h;
|
||||
}
|
||||
|
||||
constexpr Bounds::Bounds(const Point &pt1, const Point &pt2) noexcept {
|
||||
set(pt1, pt2);
|
||||
}
|
||||
|
||||
constexpr bool Bounds::intersects(const Bounds &o) const noexcept {
|
||||
return o.x2() >= x && x2() >= o.x && o.y2() >= y && y2() >= o.y;
|
||||
}
|
||||
|
||||
constexpr bool Bounds::contains(int x, int y) const noexcept {
|
||||
return x >= this->x && y >= this->y && x <= x2() && y <= y2();
|
||||
}
|
||||
|
||||
constexpr bool Bounds::contains(const Point &pt) const noexcept {
|
||||
return contains(pt.x, pt.y);
|
||||
}
|
||||
|
||||
constexpr int Bounds::x2() const noexcept {
|
||||
return x + width;
|
||||
}
|
||||
|
||||
constexpr int Bounds::y2() const noexcept {
|
||||
return y + height;
|
||||
}
|
||||
|
||||
constexpr Point Bounds::pt1() const noexcept {
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
constexpr Point Bounds::pt2() const noexcept {
|
||||
return {x2(), y2()};
|
||||
}
|
||||
|
||||
constexpr void Bounds::setPt2(const Point &pt) noexcept {
|
||||
set(pt1(), pt);
|
||||
}
|
||||
|
||||
constexpr void Bounds::set(const Point &pt1, const Point &pt2) noexcept {
|
||||
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
|
||||
if (pt1.x <= pt2.x) {
|
||||
x1 = pt1.x;
|
||||
x2 = pt2.x;
|
||||
} else {
|
||||
x1 = pt2.x;
|
||||
x2 = pt1.x;
|
||||
}
|
||||
if (pt1.y <= pt2.y) {
|
||||
y1 = pt1.y;
|
||||
y2 = pt2.y;
|
||||
} else {
|
||||
y1 = pt2.y;
|
||||
y2 = pt1.y;
|
||||
}
|
||||
this->x = x1;
|
||||
this->y = y1;
|
||||
this->width = x2 - x1;
|
||||
this->height = y2 - y1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<Bounds> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Bounds>());
|
||||
OX_RETURN_ERROR(io->field("x", &obj->x));
|
||||
OX_RETURN_ERROR(io->field("y", &obj->y));
|
||||
OX_RETURN_ERROR(io->field("width", &obj->width));
|
||||
OX_RETURN_ERROR(io->field("height", &obj->height));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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 "error.hpp"
|
||||
#include "reader.hpp"
|
||||
#include "span.hpp"
|
||||
#include "vector.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
extern template class Vector<char>;
|
||||
|
||||
using Buffer = Vector<char>;
|
||||
using BufferView = Span<const char>;
|
||||
|
||||
class BufferWriter {
|
||||
private:
|
||||
std::size_t m_it = 0;
|
||||
ox::Buffer &m_buff;
|
||||
|
||||
public:
|
||||
explicit constexpr BufferWriter(Buffer *buff) noexcept: m_it(buff->size()), m_buff(*buff) {
|
||||
}
|
||||
|
||||
explicit constexpr BufferWriter(Buffer *buff, std::size_t it) noexcept: m_it(it), m_buff(*buff) {
|
||||
}
|
||||
|
||||
constexpr ox::Error seekp(std::size_t p) noexcept {
|
||||
m_it = p;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Error seekp(ox::Signed<std::size_t> off, ox::ios_base::seekdir dir) noexcept {
|
||||
ox::Signed<std::size_t> base = 0;
|
||||
switch (dir) {
|
||||
case ox::ios_base::beg:
|
||||
base = 0;
|
||||
break;
|
||||
case ox::ios_base::end:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_buff.size());
|
||||
break;
|
||||
case ox::ios_base::cur:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_it);
|
||||
break;
|
||||
default:
|
||||
return ox::Error(1, "Invalid seekdir");
|
||||
}
|
||||
m_it = static_cast<std::size_t>(base + off);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto tellp() const noexcept {
|
||||
return m_it;
|
||||
}
|
||||
|
||||
constexpr ox::Error put(char val) noexcept {
|
||||
if (m_it >= m_buff.size()) {
|
||||
m_buff.resize(m_buff.size() + 1);
|
||||
}
|
||||
m_buff[m_it] = val;
|
||||
++m_it;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Error write(const char *inBuff, std::size_t cnt) noexcept {
|
||||
const auto end = m_it + cnt;
|
||||
if (end >= m_buff.size()) {
|
||||
m_buff.resize(end);
|
||||
}
|
||||
if (inBuff) {
|
||||
const auto buff = m_buff.data() + m_it;
|
||||
for (auto i = 0u; i < cnt; ++i) {
|
||||
buff[i] = inBuff[i];
|
||||
}
|
||||
}
|
||||
m_it += cnt;
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const auto &buff() const noexcept {
|
||||
return m_buff;
|
||||
}
|
||||
};
|
||||
|
||||
class CharBuffWriter {
|
||||
private:
|
||||
std::size_t m_it = 0;
|
||||
std::size_t m_cap = 0;
|
||||
std::size_t m_size = 0;
|
||||
char *m_buff = nullptr;
|
||||
|
||||
public:
|
||||
explicit constexpr CharBuffWriter(ox::Span<char> buff) noexcept:
|
||||
m_cap(buff.size()),
|
||||
m_buff(buff.data()) {
|
||||
}
|
||||
|
||||
constexpr ox::Error seekp(std::size_t p) noexcept {
|
||||
m_it = p;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Error seekp(ox::Signed<std::size_t> off, ox::ios_base::seekdir dir) noexcept {
|
||||
ox::Signed<std::size_t> base = 0;
|
||||
switch (dir) {
|
||||
case ox::ios_base::beg:
|
||||
base = 0;
|
||||
break;
|
||||
case ox::ios_base::end:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_size);
|
||||
break;
|
||||
case ox::ios_base::cur:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_it);
|
||||
break;
|
||||
default:
|
||||
return ox::Error(1, "Invalid seekdir");
|
||||
}
|
||||
m_it = static_cast<std::size_t>(base + off);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto tellp() const noexcept {
|
||||
return m_it;
|
||||
}
|
||||
|
||||
constexpr ox::Error put(char val) noexcept {
|
||||
if (m_it >= m_cap) [[unlikely]] {
|
||||
return ox::Error(1, "Buffer overrun");
|
||||
}
|
||||
m_buff[m_it] = val;
|
||||
++m_it;
|
||||
m_size = ox::max(m_it, m_size);
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Error write(const char *buff, std::size_t cnt) noexcept {
|
||||
const auto end = m_it + cnt;
|
||||
if (end > m_cap) [[unlikely]] {
|
||||
return ox::Error(1, "Buffer overrun");
|
||||
}
|
||||
if (buff) {
|
||||
for (auto i = 0u; i < cnt; ++i) {
|
||||
m_buff[m_it + i] = buff[i];
|
||||
}
|
||||
}
|
||||
m_it += cnt;
|
||||
m_size = ox::max(m_it, m_size);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto data() const noexcept {
|
||||
return m_buff;
|
||||
}
|
||||
};
|
||||
|
||||
class BufferReader {
|
||||
private:
|
||||
std::size_t m_it = 0;
|
||||
std::size_t m_size = 0;
|
||||
char const* m_buff = nullptr;
|
||||
|
||||
public:
|
||||
constexpr explicit BufferReader(ox::BufferView buffer) noexcept:
|
||||
m_size(buffer.size()), m_buff(buffer.data()) {}
|
||||
|
||||
constexpr ox::Result<char> peek() const noexcept {
|
||||
if (m_it >= m_size) [[unlikely]] {
|
||||
return ox::Error(1, "Peek failed: buffer overrun");
|
||||
}
|
||||
return m_buff[m_it];
|
||||
}
|
||||
|
||||
constexpr ox::Result<std::size_t> read(void *v, std::size_t sz) noexcept {
|
||||
sz = ox::min(sz, m_size - m_it);
|
||||
if (m_it + sz > m_size) [[unlikely]] {
|
||||
return ox::Error(1, "Read failed: Buffer overrun");
|
||||
}
|
||||
ox::memcpy(v, &m_buff[m_it], sz);
|
||||
m_it += sz;
|
||||
return sz;
|
||||
}
|
||||
|
||||
constexpr ox::Error seekg(std::size_t p) noexcept {
|
||||
if (p > m_size) [[unlikely]] {
|
||||
return ox::Error(1, "Seek failed: Buffer overrun");
|
||||
}
|
||||
m_it = p;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Error seekg(int64_t off, ios_base::seekdir dir) noexcept {
|
||||
ox::Signed<std::size_t> base = 0;
|
||||
switch (dir) {
|
||||
case ox::ios_base::beg:
|
||||
base = 0;
|
||||
break;
|
||||
case ox::ios_base::end:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_size);
|
||||
break;
|
||||
case ox::ios_base::cur:
|
||||
base = static_cast<ox::Signed<std::size_t>>(m_it);
|
||||
break;
|
||||
default:
|
||||
return ox::Error(1, "Invalid seekdir");
|
||||
}
|
||||
auto const newIt = static_cast<std::size_t>(base + off);
|
||||
if (newIt > m_size) [[unlikely]] {
|
||||
return ox::Error(1, "Seek failed: Buffer overrun");
|
||||
}
|
||||
m_it = newIt;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr ox::Result<std::size_t> tellg() const noexcept {
|
||||
return m_it;
|
||||
}
|
||||
};
|
||||
|
||||
extern template class ReaderT<BufferReader>;
|
||||
extern template class WriterT<BufferWriter>;
|
||||
extern template class WriterT<CharBuffWriter>;
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace ox::buildinfo {
|
||||
|
||||
extern const bool UseStdLib;
|
||||
extern const bool Debug;
|
||||
extern const bool NDebug;
|
||||
extern const bool BigEndian;
|
||||
extern const bool LittleEndian;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 "defines.hpp"
|
||||
#include "stddef.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T byteSwap(T const i) noexcept requires(sizeof(T) == 1) {
|
||||
return i;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T byteSwap(T const i) noexcept requires(sizeof(T) == 2) {
|
||||
return static_cast<T>(i << 8) | static_cast<T>(i >> 8);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T byteSwap(T const i) noexcept requires(sizeof(T) == 4) {
|
||||
return ((i >> 24) & 0x000000ff) |
|
||||
((i >> 8) & 0x0000ff00) |
|
||||
((i << 8) & 0x00ff0000) |
|
||||
((i << 24) & 0xff000000);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T byteSwap(T const i) noexcept requires(sizeof(T) == 8) {
|
||||
return ((i >> 56) & 0x00000000000000ff) |
|
||||
((i >> 40) & 0x000000000000ff00) |
|
||||
((i >> 24) & 0x0000000000ff0000) |
|
||||
((i >> 8) & 0x00000000ff000000) |
|
||||
((i << 8) & 0x000000ff00000000) |
|
||||
((i << 24) & 0x0000ff0000000000) |
|
||||
((i << 40) & 0x00ff000000000000) |
|
||||
((i << 56) & 0xff00000000000000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes an int and byte swaps if the platform is the given condition is true.
|
||||
*/
|
||||
template<typename T, bool byteSwap>
|
||||
[[nodiscard]]
|
||||
constexpr T conditionalByteSwap(T i) noexcept {
|
||||
if constexpr(byteSwap) {
|
||||
return ox::byteSwap<T>(i);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T fromLittleEndian(T i) noexcept {
|
||||
return conditionalByteSwap<T, defines::BigEndian>(i);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T fromBigEndian(T i) noexcept {
|
||||
return conditionalByteSwap<T, defines::LittleEndian>(i);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T toLittleEndian(T i) noexcept {
|
||||
return conditionalByteSwap<T, defines::BigEndian>(i);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T toBigEndian(T i) noexcept {
|
||||
return conditionalByteSwap<T, defines::LittleEndian>(i);
|
||||
}
|
||||
|
||||
template<typename T, bool byteSwap>
|
||||
class OX_PACKED ByteSwapInteger {
|
||||
private:
|
||||
T m_value;
|
||||
|
||||
public:
|
||||
constexpr ByteSwapInteger() noexcept = default;
|
||||
|
||||
constexpr ByteSwapInteger(const ByteSwapInteger &other) noexcept {
|
||||
m_value = other.m_value;
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger(T value) noexcept: m_value(conditionalByteSwap<T, byteSwap>(value)) {
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger &operator=(const ByteSwapInteger &other) noexcept {
|
||||
m_value = other.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger &operator=(T value) noexcept {
|
||||
m_value = conditionalByteSwap<T, byteSwap>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr operator T() const noexcept {
|
||||
return conditionalByteSwap<T, byteSwap>(m_value);
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger operator+=(T other) noexcept {
|
||||
const auto newVal = static_cast<T>(*this + other);
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger operator-=(T other) noexcept {
|
||||
const auto newVal = static_cast<T>(*this - other);
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger operator*=(T other) noexcept {
|
||||
const auto newVal = static_cast<T>(*this * other);
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ByteSwapInteger operator/=(T other) noexcept {
|
||||
const auto newVal = static_cast<T>(*this / other);
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Prefix increment
|
||||
constexpr ByteSwapInteger operator++() noexcept {
|
||||
return operator+=(1);
|
||||
}
|
||||
|
||||
// Prefix decrement
|
||||
constexpr ByteSwapInteger operator--() noexcept {
|
||||
return operator-=(1);
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr T operator&=(I other) noexcept {
|
||||
auto newVal = *this & other;
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr T operator|=(I other) noexcept {
|
||||
auto newVal = *this | other;
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr T operator^=(I other) noexcept {
|
||||
auto newVal = *this ^ other;
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr T operator>>=(I other) noexcept {
|
||||
auto newVal = *this >> other;
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr T operator<<=(I other) noexcept {
|
||||
auto newVal = *this << other;
|
||||
m_value = conditionalByteSwap<T, byteSwap>(newVal);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto get() const noexcept -> T {
|
||||
return static_cast<T>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer as it is stored. If it is stored as little endian,
|
||||
* a little endian integer is returned regardless of the endianness of
|
||||
* the system.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr auto raw() const noexcept -> T {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using LittleEndian = ByteSwapInteger<T, defines::BigEndian>;
|
||||
|
||||
template<typename T>
|
||||
using BigEndian = ByteSwapInteger<T, defines::LittleEndian>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T, typename U>
|
||||
concept CommonPtrWith = ox::is_same_v<typename ox::remove_pointer<const T*>::type,
|
||||
typename ox::remove_pointer<const U*>::type>;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept CommonRefWith = ox::is_same_v<typename ox::remove_reference_t<const T&>,
|
||||
typename ox::remove_reference_t<const U&>>;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept same_as = ox::is_same_v<T, T>;
|
||||
|
||||
template<typename T>
|
||||
concept OxString_c = isOxString_v<T>;
|
||||
|
||||
template<typename T>
|
||||
concept Integral_c = ox::is_integral_v<T>;
|
||||
|
||||
template<typename T, size_t max>
|
||||
concept IntegerRange_c = ox::is_integer_v<T> && ox::MaxValue<T> >= max;
|
||||
|
||||
template<typename Union>
|
||||
concept Union_c = is_union_v<Union>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 "point.hpp"
|
||||
#include "size.hpp"
|
||||
#include "vec.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
constexpr Point::Point(Vec2 const&pt) noexcept:
|
||||
x(static_cast<int32_t>(pt.x)),
|
||||
y(static_cast<int32_t>(pt.y)) {}
|
||||
|
||||
constexpr Size::Size(Vec2 const&pt) noexcept:
|
||||
width(static_cast<int32_t>(pt.x)),
|
||||
height(static_cast<int32_t>(pt.y)) {}
|
||||
|
||||
constexpr Vec2::Vec2(Point const&pt) noexcept:
|
||||
x(static_cast<float>(pt.x)),
|
||||
y(static_cast<float>(pt.y)) {}
|
||||
|
||||
constexpr Vec2::Vec2(Size const&pt) noexcept:
|
||||
x(static_cast<float>(pt.width)),
|
||||
y(static_cast<float>(pt.height)) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 "basestringview.hpp"
|
||||
#include "istring.hpp"
|
||||
#include "string.hpp"
|
||||
#include "stringliteral.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
class CStringView: public detail::BaseStringView {
|
||||
public:
|
||||
constexpr CStringView() noexcept = default;
|
||||
|
||||
constexpr CStringView(CStringView const&sv) noexcept = default;
|
||||
|
||||
constexpr CStringView(StringLiteral const&str) noexcept: BaseStringView(str.data(), str.size()) {}
|
||||
|
||||
template<std::size_t SmallStrSz>
|
||||
constexpr CStringView(BasicString<SmallStrSz> const&str) noexcept: BaseStringView(str.data(), str.size()) {}
|
||||
|
||||
template<std::size_t SmallStrSz>
|
||||
constexpr CStringView(IString<SmallStrSz> const&str) noexcept: BaseStringView(str.data(), str.size()) {}
|
||||
|
||||
constexpr CStringView(std::nullptr_t) noexcept {}
|
||||
|
||||
constexpr CStringView(const char *str) noexcept: BaseStringView(str) {}
|
||||
|
||||
constexpr CStringView(const char *str, std::size_t len) noexcept: BaseStringView(str, len) {}
|
||||
|
||||
constexpr auto &operator=(CStringView const&other) noexcept {
|
||||
if (&other != this) {
|
||||
set(other.data(), other.size());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const char *c_str() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using CStringViewCR = CStringView const&;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T1, typename T2>
|
||||
constexpr T1 strncpy(T1 dest, T2 src, std::size_t maxLen) noexcept {
|
||||
using T1Type = typename ox::remove_reference<decltype(dest[0])>::type;
|
||||
std::size_t i = 0;
|
||||
while (i < maxLen && src[i]) {
|
||||
dest[i] = static_cast<T1Type>(src[i]);
|
||||
++i;
|
||||
}
|
||||
// set null terminator
|
||||
dest[i] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t strnlen_s(const char *str, size_t const maxLen) noexcept {
|
||||
if (!str) [[unlikely]] {
|
||||
return 0;
|
||||
}
|
||||
size_t len = 0;
|
||||
for (; len < maxLen && str[len]; len++);
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr auto strlen(T const&str1) noexcept {
|
||||
std::size_t len = 0;
|
||||
for (; str1[len]; len++);
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
[[nodiscard]]
|
||||
constexpr int strcmp(const T1 &str1, const T2 &str2) noexcept {
|
||||
auto retval = 0;
|
||||
auto i = 0u;
|
||||
while (str1[i] || str2[i]) {
|
||||
if (str1[i] < str2[i]) {
|
||||
retval = -1;
|
||||
break;
|
||||
} else if (str1[i] > str2[i]) {
|
||||
retval = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
[[nodiscard]]
|
||||
constexpr int strncmp(T1 const&str1, T2 const&str2, const std::size_t maxLen) noexcept {
|
||||
auto retval = 0;
|
||||
std::size_t i = 0;
|
||||
while (i < maxLen && (str1[i] || str2[i])) {
|
||||
if (str1[i] < str2[i]) {
|
||||
retval = -1;
|
||||
break;
|
||||
} else if (str1[i] > str2[i]) {
|
||||
retval = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const char *strchr(const char *str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept {
|
||||
for (std::size_t i = 0; i <= maxLen; i++) {
|
||||
if (str[i] == character) {
|
||||
return &str[i];
|
||||
} else if (str[i] == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr char *strchr(char *str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept {
|
||||
for (std::size_t i = 0; i < maxLen; i++) {
|
||||
if (str[i] == character) {
|
||||
return &str[i];
|
||||
} else if (str[i] == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr int lastIndexOf(const auto &str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept {
|
||||
int retval = -1;
|
||||
for (std::size_t i = 0; i < maxLen && str[i]; i++) {
|
||||
if (str[i] == character) {
|
||||
retval = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
// Logging
|
||||
|
||||
#define oxLogError(...) ox::trace::logError(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define oxTrace(...) ox::trace::TraceStream(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define oxOut(...) ox::trace::OutStream(__FILE__, __LINE__, "stdout", __VA_ARGS__)
|
||||
#define oxErr(...) ox::trace::OutStream(__FILE__, __LINE__, "stderr", __VA_ARGS__)
|
||||
|
||||
#define oxTracef(ch, fmt, ...) ox::trace::TraceStream(__FILE__, __LINE__, ch, ox::detail::fmtSegments<ox::detail::argCount(fmt)+1>(fmt), __VA_ARGS__)
|
||||
#define oxOutf(fmt, ...) ox::trace::OutStream(__FILE__, __LINE__, "stdout", ox::detail::fmtSegments<ox::detail::argCount(fmt)+1>(fmt), __VA_ARGS__)
|
||||
#define oxErrf(fmt, ...) ox::trace::OutStream(__FILE__, __LINE__, "stderr", ox::detail::fmtSegments<ox::detail::argCount(fmt)+1>(fmt), __VA_ARGS__)
|
||||
|
||||
#define oxInfo(...) oxTrace("info", __VA_ARGS__)
|
||||
#define oxInfof(...) oxTracef("info", __VA_ARGS__)
|
||||
#define oxError(...) oxTrace("error", __VA_ARGS__)
|
||||
#define oxErrorf(...) oxTracef("error", __VA_ARGS__)
|
||||
|
||||
#ifndef OX_NODEBUG
|
||||
#define oxDebug(...) ox::trace::OutStream(__FILE__, __LINE__, "debug", __VA_ARGS__)
|
||||
#define oxDebugf(fmt, ...) ox::trace::OutStream(__FILE__, __LINE__, "debug", ox::detail::fmtSegments<ox::detail::argCount(fmt)+1>(fmt), __VA_ARGS__)
|
||||
#else
|
||||
#define oxDebug(...) static_assert(false, "Debug prints were checked in.");
|
||||
#define oxDebugf(...) static_assert(false, "Debug prints were checked in.");
|
||||
#endif
|
||||
|
||||
|
||||
// Error handling
|
||||
|
||||
#define OX_RETURN_ERROR(x) { if (const auto _ox_error = ox::detail::toError(x)) [[unlikely]] return _ox_error; } (void) 0
|
||||
#define OX_THROW_ERROR(x) { if (const auto _ox_error = ox::detail::toError(x)) [[unlikely]] throw ox::Exception(_ox_error); } (void) 0
|
||||
#define OX_CONCAT_IMPL(a, b) a##b
|
||||
#define OX_CONCAT(a, b) OX_CONCAT_IMPL(a, b)
|
||||
// oxRequire Mutable
|
||||
#define OX_REQUIRE_M(out, x) auto [out, OX_CONCAT(oxRequire_err_, __LINE__)] = x; OX_RETURN_ERROR(OX_CONCAT(oxRequire_err_, __LINE__))
|
||||
#define OX_REQUIRE(out, x) const OX_REQUIRE_M(out, x)
|
||||
|
||||
|
||||
// Asserts
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define oxAssert(pass, msg) ox::assertFunc(pass, #pass, msg)
|
||||
#else
|
||||
namespace ox {
|
||||
struct [[nodiscard]] Error;
|
||||
}
|
||||
constexpr void oxAssert(bool, const char*) noexcept {}
|
||||
constexpr void oxAssert(const ox::Error&, const char*) noexcept {}
|
||||
#endif
|
||||
|
||||
// Alloca
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define ox_alloca(size) _alloca(size)
|
||||
#elif OX_USE_STDLIB
|
||||
#define ox_alloca(size) alloca(size)
|
||||
#else
|
||||
#define ox_alloca(size) __builtin_alloca(size)
|
||||
#endif
|
||||
|
||||
#define OX_PRAGMA(x) _Pragma(#x)
|
||||
#ifdef __clang__
|
||||
#define OX_CLANG_NOWARN_BEGIN(warnoption) \
|
||||
OX_PRAGMA(clang diagnostic push) \
|
||||
OX_PRAGMA(clang diagnostic ignored #warnoption)
|
||||
#define OX_CLANG_NOWARN_END OX_PRAGMA(clang diagnostic pop)
|
||||
#define OX_ALLOW_UNSAFE_BUFFERS_BEGIN OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
#define OX_ALLOW_UNSAFE_BUFFERS_END OX_CLANG_NOWARN_END
|
||||
#else
|
||||
#define OX_CLANG_NOWARN_BEGIN(warnoption)
|
||||
#define OX_CLANG_NOWARN_END
|
||||
#define OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
#define OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return an ox::MallocaPtr of the given type pointing to the requested size memory allocation
|
||||
*/
|
||||
#if defined(OX_USE_STDLIB)
|
||||
#define ox_malloca(size, Type, ...) ox::MallocaPtr<Type>(size > ox::MallocaStackLimit, new (size > ox::MallocaStackLimit ? new uint8_t[size] : ox_alloca(size)) Type(__VA_ARGS__))
|
||||
#else
|
||||
#define ox_malloca(size, Type, ...) ox::MallocaPtr<Type>(false, new (ox_alloca(size)) Type(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
class Defer {
|
||||
private:
|
||||
T m_deferredFunc;
|
||||
|
||||
public:
|
||||
constexpr Defer(T deferredFunc) noexcept: m_deferredFunc(deferredFunc) {
|
||||
}
|
||||
|
||||
Defer(const Defer&) = delete;
|
||||
|
||||
constexpr ~Defer() {
|
||||
m_deferredFunc();
|
||||
}
|
||||
|
||||
Defer &operator=(const Defer&) = delete;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define OX_DEFER ox::Defer const OX_CONCAT(oxDefer_, __LINE__) =
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace ox {
|
||||
|
||||
enum class OS {
|
||||
BareMetal,
|
||||
NetBSD,
|
||||
OpenBSD,
|
||||
FreeBSD,
|
||||
DragonFlyBSD,
|
||||
Linux,
|
||||
Darwin,
|
||||
Windows
|
||||
};
|
||||
|
||||
namespace defines {
|
||||
|
||||
#if defined(OX_NODEBUG)
|
||||
constexpr bool NoDebug = true;
|
||||
#else
|
||||
constexpr bool NoDebug = false;
|
||||
#endif
|
||||
|
||||
#if defined(OX_USE_STDLIB)
|
||||
constexpr auto UseStdLib = true;
|
||||
#else
|
||||
constexpr auto UseStdLib = false;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
constexpr auto Debug = true;
|
||||
#else
|
||||
constexpr auto Debug = false;
|
||||
#endif
|
||||
|
||||
#if defined(OX_CHECK_BOUNDS)
|
||||
constexpr auto CheckBounds = true;
|
||||
#else
|
||||
constexpr auto CheckBounds = Debug;
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG)
|
||||
constexpr auto NDebug = true;
|
||||
#else
|
||||
constexpr auto NDebug = false;
|
||||
#endif
|
||||
|
||||
#if defined(OX_BARE_METAL)
|
||||
constexpr auto HasRTTI = false;
|
||||
#else
|
||||
constexpr auto HasRTTI = true;
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
constexpr auto BigEndian = true;
|
||||
constexpr auto LittleEndian = false;
|
||||
#else
|
||||
constexpr auto BigEndian = false;
|
||||
constexpr auto LittleEndian = true;
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
constexpr OS OS = OS::FreeBSD;
|
||||
#define OX_OS_FreeBSD
|
||||
#elif defined(__NetBSD__)
|
||||
constexpr OS OS = OS::NetBSD;
|
||||
#define OX_OS_NetBSD
|
||||
#elif defined(__OpenBSD__)
|
||||
constexpr OS OS = OS::OpenBSD;
|
||||
#define OX_OS_OpenBSD
|
||||
#elif defined(__DragonFly__)
|
||||
constexpr OS OS = OS::DragonFlyBSD;
|
||||
#define OX_OS_DragonFlyBSD
|
||||
#elif defined(__gnu_linux__)
|
||||
constexpr OS OS = OS::Linux;
|
||||
#define OX_OS_Linux
|
||||
#elif defined(_WIN32)
|
||||
constexpr OS OS = OS::Windows;
|
||||
#define OX_OS_Windows
|
||||
#elif defined(__APPLE__)
|
||||
constexpr OS OS = OS::Darwin;
|
||||
#define OX_OS_Darwin
|
||||
#else
|
||||
constexpr OS OS = OS::BareMetal;
|
||||
#define OX_OS_BareMetal
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 <ox/std/error.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/utility.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename F, typename... Args>
|
||||
constexpr Error call(F const &f, Args&&... args) noexcept {
|
||||
try {
|
||||
f(ox::forward<Args>(args)...);
|
||||
return {};
|
||||
} catch (Exception const &e) {
|
||||
return e.toError();
|
||||
} catch (...) {
|
||||
return Error{1, "unknown exception"};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename Method, typename... Args>
|
||||
constexpr Error call(T &receiver, Method methodPtr, Args&&... args) noexcept {
|
||||
try {
|
||||
(receiver.*methodPtr)(ox::forward<Args>(args)...);
|
||||
return {};
|
||||
} catch (Exception const &e) {
|
||||
return e.toError();
|
||||
} catch (...) {
|
||||
return Error{1, "unknown exception"};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
constexpr void logCatch(F const &f, Args&&... args) noexcept {
|
||||
try {
|
||||
f(ox::forward<Args>(args)...);
|
||||
} catch (Exception const &e) {
|
||||
oxErrf("Caught exception: {} ({}:{})\n", e.what(), e.src.file_name(), e.src.line());
|
||||
} catch (std::exception const &e) {
|
||||
oxErrf("Caught exception: {}\n", e.what());
|
||||
} catch (...) {
|
||||
oxErr("Caught unknown exception\n");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename Method, typename... Args>
|
||||
constexpr void logCatch(T &receiver, Method methodPtr, Args&&... args) noexcept {
|
||||
try {
|
||||
(receiver.*methodPtr)(ox::forward<Args>(args)...);
|
||||
} catch (Exception const &e) {
|
||||
oxErrf("Caught exception: {} ({}:{})\n", e.what(), e.src.file_name(), e.src.line());
|
||||
} catch (std::exception const &e) {
|
||||
oxErrf("Caught exception: {}\n", e.what());
|
||||
} catch (...) {
|
||||
oxErr("Caught unknown exception\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<exception>)
|
||||
#include <exception>
|
||||
#else
|
||||
namespace std {
|
||||
class exception {
|
||||
public:
|
||||
virtual ~exception() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual char const *what() const noexcept {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "defines.hpp"
|
||||
#include "def.hpp"
|
||||
#include "source_location.hpp"
|
||||
#include "typetraits.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
using ErrorCode = uint16_t;
|
||||
|
||||
|
||||
struct [[nodiscard]] Error {
|
||||
std::source_location src;
|
||||
CString msg = nullptr;
|
||||
ErrorCode errCode = 0;
|
||||
|
||||
constexpr Error() noexcept = default;
|
||||
|
||||
explicit constexpr Error(
|
||||
ErrorCode const errCode,
|
||||
std::source_location const &src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
errCode{errCode}
|
||||
{}
|
||||
|
||||
explicit constexpr Error(
|
||||
ErrorCode const errCode,
|
||||
CString const msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
msg{msg},
|
||||
errCode{errCode}
|
||||
{}
|
||||
|
||||
constexpr operator uint64_t() const noexcept {
|
||||
return errCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Error to see if is not OK, and throws the Error as an ox::Exception if not.
|
||||
*/
|
||||
constexpr void checkThrow() const;
|
||||
|
||||
constexpr Error reoriginate(
|
||||
ErrorCode const pErrCode,
|
||||
CString const pMsg = nullptr,
|
||||
std::source_location const &pSrc = std::source_location::current()) const noexcept {
|
||||
if (errCode) {
|
||||
return Error{pErrCode, pMsg, pSrc};
|
||||
}
|
||||
return Error{};
|
||||
}
|
||||
|
||||
constexpr Error reoriginate(
|
||||
std::source_location const &pSrc = std::source_location::current()) const noexcept {
|
||||
return Error{errCode, msg, pSrc};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto errCode(Error const &err) noexcept {
|
||||
return err.errCode;
|
||||
}
|
||||
|
||||
template<typename T = char const*>
|
||||
[[nodiscard]]
|
||||
constexpr auto toStr(Error const &err) noexcept {
|
||||
return err.msg ? T{err.msg} : "";
|
||||
}
|
||||
|
||||
struct Exception: public std::exception {
|
||||
std::source_location src;
|
||||
CString msg = nullptr;
|
||||
ErrorCode errCode = 0;
|
||||
|
||||
explicit Exception(
|
||||
ErrorCode const errCode,
|
||||
std::source_location const &src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
errCode{errCode} {}
|
||||
|
||||
explicit Exception(
|
||||
ErrorCode const errCode,
|
||||
CString const msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
msg{msg},
|
||||
errCode{errCode} {}
|
||||
|
||||
explicit Exception(Error const &err) noexcept:
|
||||
src{err.src},
|
||||
msg{err.msg ? err.msg : ""},
|
||||
errCode{err.errCode} {}
|
||||
|
||||
constexpr Error toError() const noexcept {
|
||||
return Error(errCode, msg, src);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
char const *what() const noexcept override {
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
constexpr void Error::checkThrow() const {
|
||||
if (errCode) [[unlikely]] {
|
||||
throw Exception{*this};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
void panic(
|
||||
Error const &err,
|
||||
CString panicMsg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept;
|
||||
|
||||
template<typename T>
|
||||
struct [[nodiscard]] Result {
|
||||
|
||||
using type = remove_reference_t<T>;
|
||||
|
||||
T value;
|
||||
Error error;
|
||||
|
||||
constexpr Result() noexcept: value(), error(0) {
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr Result(Result<U> const &other) noexcept: value(other.value), error(other.error) {
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr Result(Result<U> &&other) noexcept: value(std::move(other.value)), error(std::move(other.error)) {
|
||||
}
|
||||
|
||||
constexpr Result(Error const &error) noexcept: value(), error(error) {
|
||||
}
|
||||
|
||||
constexpr Result(type const &value, Error const &error = {}) noexcept: value(value), error(error) {
|
||||
}
|
||||
|
||||
constexpr Result(type &&value, Error const &error = {}) noexcept: value(std::move(value)), error(error) {
|
||||
}
|
||||
|
||||
constexpr ~Result() noexcept = default;
|
||||
|
||||
explicit constexpr operator type const &() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
explicit constexpr operator type&() noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool ok() const noexcept {
|
||||
return error == 0;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr Error copyTo(U &val) const & noexcept {
|
||||
if (!error) [[likely]] {
|
||||
val = value;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr Error copyTo(U &val) && noexcept {
|
||||
if (!error) [[likely]] {
|
||||
val = std::move(value);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr Error moveTo(U &val) noexcept {
|
||||
if (!error) [[likely]] {
|
||||
val = std::move(value);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &unwrap() & noexcept {
|
||||
if (error) {
|
||||
ox::panic(error, "Failed unwrap");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &&unwrap() && noexcept {
|
||||
if (error) {
|
||||
ox::panic(error, "Failed unwrap");
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const &unwrap() const & noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
ox::panic(error, "Failed unwrap");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &unwrapThrow() & {
|
||||
if (error) {
|
||||
throw Exception(error);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &&unwrapThrow() && {
|
||||
if (error) {
|
||||
throw Exception(error);
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const &unwrapThrow() const & {
|
||||
if (error) {
|
||||
throw ox::Exception(error);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to() & noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return error;
|
||||
}
|
||||
return U(value);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to() && noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return error;
|
||||
}
|
||||
return U(std::move(value));
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to(auto const &f) & noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return error;
|
||||
}
|
||||
return f(value);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to(auto const &f) && noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return error;
|
||||
}
|
||||
return f(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) const & noexcept {
|
||||
if (error) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) && noexcept {
|
||||
if (error) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const &alt) const & noexcept {
|
||||
if (error) {
|
||||
return alt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const &alt) && noexcept {
|
||||
if (error) {
|
||||
return alt;
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
constexpr Result transformError(ErrorCode const ec, CString const msg) && {
|
||||
if (error) {
|
||||
error = Error{ec, msg};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr Error toError(Error const &e) noexcept {
|
||||
return e;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error toError(Result<T> const &r) noexcept {
|
||||
return r.error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
constexpr void primitiveAssert(
|
||||
bool const pass,
|
||||
char const *msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if constexpr(ox::defines::Debug) {
|
||||
if (!pass) [[unlikely]] {
|
||||
panic(ox::Error{1}, msg, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void boundsCheck(
|
||||
size_t const i,
|
||||
size_t const sz,
|
||||
char const *msg,
|
||||
std::source_location const &src = std::source_location::current()) noexcept {
|
||||
if constexpr(defines::CheckBounds) {
|
||||
if (i >= sz) [[unlikely]] {
|
||||
panic(ox::Error{1}, msg, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<string>)
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#if __has_include(<QString>)
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
#include "realstd.hpp"
|
||||
#include "error.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "istring.hpp"
|
||||
#include "string.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
namespace detail {
|
||||
|
||||
template<bool force = false>
|
||||
constexpr StringView toStringView(StringViewCR s) noexcept {
|
||||
return s;
|
||||
}
|
||||
|
||||
template<bool force = false>
|
||||
constexpr StringView toStringView(CString const s) noexcept {
|
||||
return s;
|
||||
}
|
||||
|
||||
template<bool force = false, std::size_t size>
|
||||
constexpr StringView toStringView(IString<size> const &s) noexcept {
|
||||
return s;
|
||||
}
|
||||
|
||||
template<bool force = false>
|
||||
constexpr StringView toStringView(StringLiteral const &s) noexcept {
|
||||
return s;
|
||||
}
|
||||
|
||||
template<bool force = false, std::size_t size>
|
||||
constexpr StringView toStringView(BasicString<size> const &s) noexcept {
|
||||
return s;
|
||||
}
|
||||
|
||||
#if __has_include(<string>)
|
||||
template<bool force = false>
|
||||
#ifdef OX_OS_Darwin
|
||||
constexpr
|
||||
#else
|
||||
inline
|
||||
#endif
|
||||
StringView toStringView(std::string const &s) noexcept {
|
||||
return s.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_include(<QString>)
|
||||
template<bool force = false>
|
||||
inline StringView toStringView(QString const &s) noexcept {
|
||||
return s.toUtf8().data();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<bool force = false>
|
||||
constexpr StringView toStringView(auto const&) noexcept requires(force) {
|
||||
return "<unstringable>";
|
||||
}
|
||||
|
||||
class FmtArg {
|
||||
|
||||
private:
|
||||
static constexpr auto DataSz = 23;
|
||||
Array<char, DataSz> dataStr{};
|
||||
|
||||
template<typename T>
|
||||
constexpr StringView sv(T const &v, Span<char> dataStr) noexcept {
|
||||
if constexpr(is_bool_v<T>) {
|
||||
return v ? "true" : "false";
|
||||
} else if constexpr(is_integer_v<T>) {
|
||||
ox::CharBuffWriter w{dataStr};
|
||||
std::ignore = ox::writeItoa(v, w);
|
||||
return dataStr.data();
|
||||
} else {
|
||||
return toStringView(v);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
StringView const out;
|
||||
|
||||
template<typename T>
|
||||
constexpr FmtArg(T const &v) noexcept: out(sv(v, dataStr)) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint64_t argCount(StringViewCR str) noexcept {
|
||||
uint64_t cnt = 0;
|
||||
auto const prev = [str](std::size_t i) -> char {
|
||||
if (i > 0) {
|
||||
return str[i - 1];
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
};
|
||||
auto const next = [str](std::size_t i) -> char {
|
||||
if (i < str.bytes() - 1) {
|
||||
return str[i + 1];
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
};
|
||||
for (std::size_t i = 0; i < str.bytes(); ++i) {
|
||||
if (str[i] == '{' && prev(i) != '\\' && next(i) == '}') {
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
struct FmtSegment {
|
||||
CString str{};
|
||||
unsigned length{};
|
||||
|
||||
constexpr bool operator==(FmtSegment const &o) const noexcept {
|
||||
return length == o.length && ox::strncmp(str, o.str, length) == 0;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(FmtSegment const &o) const noexcept {
|
||||
return length != o.length || ox::strncmp(str, o.str, length) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t sz>
|
||||
struct Fmt {
|
||||
static constexpr std::size_t size = sz;
|
||||
Array<FmtSegment, sz> segments;
|
||||
|
||||
constexpr bool operator==(Fmt const &o) const noexcept {
|
||||
for (std::size_t i = 0; i < sz; ++i) {
|
||||
if (segments[i] != o.segments[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t segementCnt>
|
||||
[[nodiscard]]
|
||||
constexpr Fmt<segementCnt> fmtSegments(StringViewCR fmt) noexcept {
|
||||
Fmt<segementCnt> out;
|
||||
auto const prev = [fmt](std::size_t const i) -> char {
|
||||
if (i > 0) {
|
||||
return fmt[i - 1];
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
};
|
||||
auto const next = [fmt](std::size_t const i) -> char {
|
||||
if (i < fmt.bytes() - 1) {
|
||||
return fmt[i + 1];
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
};
|
||||
auto current = &out.segments[0];
|
||||
current->str = fmt.data();
|
||||
for (std::size_t i = 0; i < fmt.bytes(); ++i) {
|
||||
if (fmt[i] == '{' && prev(i) != '\\' && next(i) == '}') {
|
||||
++current;
|
||||
current->str = fmt.data() + i + 2;
|
||||
current->length = 0;
|
||||
i += 1;
|
||||
} else {
|
||||
++current->length;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename StringType = String, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr StringType sfmt(StringViewCR fmt, Args&&... args) noexcept {
|
||||
assert(ox::detail::argCount(fmt) == sizeof...(args));
|
||||
StringType out;
|
||||
auto const fmtSegments = detail::fmtSegments<sizeof...(args)+1>(fmt);
|
||||
auto const &firstSegment = fmtSegments.segments[0];
|
||||
std::ignore = out.append(firstSegment.str, firstSegment.length);
|
||||
detail::FmtArg const elements[sizeof...(args)] = {args...};
|
||||
for (size_t i = 0; i < fmtSegments.size - 1; ++i) {
|
||||
std::ignore = out.append(elements[i].out);
|
||||
auto const &s = fmtSegments.segments[i + 1];
|
||||
std::ignore = out.append(s.str, s.length);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T = String>
|
||||
constexpr Result<T> join(auto const &d, auto const &list) {
|
||||
if (!list.size()) {
|
||||
return T("");
|
||||
}
|
||||
T out;
|
||||
out += list[0];
|
||||
for (auto i = 1ul; i < list.size(); ++i) {
|
||||
out += d;
|
||||
out += list[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_AMD64)
|
||||
#define OX_ARCH_x86_64
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
#define OX_ARCH_x86_32
|
||||
#elif defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define OX_ARCH_ARM64
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
#define OX_ARCH_ARM
|
||||
#endif
|
||||
|
||||
#if defined(OX_ARCH_ARM)
|
||||
|
||||
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__)
|
||||
#define OX_HW_DIV 1
|
||||
#else
|
||||
#define OX_HW_DIV 0
|
||||
#endif
|
||||
|
||||
#elif defined(OX_ARCH_x86_32) || defined(OX_ARCH_x86_64) || defined(OX_ARCH_ARM64)
|
||||
|
||||
#define OX_HW_DIV 1
|
||||
|
||||
#else
|
||||
|
||||
#error "Undefined hardware"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<functional>)
|
||||
#include <functional>
|
||||
#else
|
||||
namespace std {
|
||||
template<typename T>
|
||||
struct hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <cityhash/city.h>
|
||||
|
||||
#include "istring.hpp"
|
||||
#include "string.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
struct hash {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(T const&v) const noexcept {
|
||||
return std::hash<T>{}(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<bool> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(bool v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<char> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(char v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<short> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(short v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<int> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(int v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<long> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(long v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<long long> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(long long v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct hash<unsigned char> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(unsigned char v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<unsigned short> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(unsigned short v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<unsigned int> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(unsigned int v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<unsigned long> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(unsigned long v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<unsigned long long> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(unsigned long long v) const noexcept {
|
||||
return static_cast<size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto hashString(ox::StringView const&v) noexcept {
|
||||
if constexpr(sizeof(void*) == 8) {
|
||||
return cityhash::CityHash64(v.data(), v.bytes());
|
||||
} else {
|
||||
return cityhash::CityHash32(v.data(), v.bytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
struct hash<ox::StringView> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(ox::StringView v) const noexcept {
|
||||
return hashString(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<ox::String> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(ox::StringView v) const noexcept {
|
||||
return hashString(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t sz>
|
||||
struct hash<ox::IString<sz>> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(ox::StringView v) const noexcept {
|
||||
return hashString(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<const char*> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(ox::StringView v) const noexcept {
|
||||
return hashString(v);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 "algorithm.hpp"
|
||||
#include "hash.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "optional.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename K, typename T>
|
||||
class HashMap {
|
||||
|
||||
public:
|
||||
using key_t = K;
|
||||
using value_t = T;
|
||||
|
||||
private:
|
||||
struct Pair {
|
||||
UPtr<Pair> next;
|
||||
K key = {};
|
||||
T value{};
|
||||
};
|
||||
Vector<K> m_keys;
|
||||
Vector<UPtr<Pair>> m_pairs;
|
||||
|
||||
public:
|
||||
explicit constexpr HashMap(std::size_t size = 127);
|
||||
|
||||
constexpr HashMap(HashMap const&other);
|
||||
|
||||
constexpr HashMap(HashMap &&other) noexcept;
|
||||
|
||||
constexpr ~HashMap();
|
||||
|
||||
constexpr bool operator==(HashMap const&other) const;
|
||||
|
||||
constexpr HashMap &operator=(HashMap const&other);
|
||||
|
||||
constexpr HashMap &operator=(HashMap &&other) noexcept;
|
||||
|
||||
constexpr T &operator[](MaybeView_t<K> const&key);
|
||||
|
||||
constexpr Result<T*> at(MaybeView_t<K> const&key) noexcept;
|
||||
|
||||
constexpr Result<const T*> at(MaybeView_t<K> const&key) const noexcept;
|
||||
|
||||
constexpr void erase(MaybeView_t<K> const&key);
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(MaybeView_t<K> const&key) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Vector<K> const&keys() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Vector<T> values() const noexcept;
|
||||
|
||||
constexpr void clear();
|
||||
|
||||
private:
|
||||
constexpr void expand();
|
||||
|
||||
template<typename KK>
|
||||
constexpr UPtr<Pair> const &access(Vector<UPtr<Pair>> const &pairs, KK const &key) const;
|
||||
|
||||
template<typename KK>
|
||||
constexpr UPtr<Pair> &access(Vector<UPtr<Pair>> &pairs, KK const &key);
|
||||
|
||||
};
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T>::HashMap(std::size_t size): m_pairs(size) {
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T>::HashMap(HashMap const &other) {
|
||||
operator=(other);
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T>::HashMap(HashMap &&other) noexcept {
|
||||
operator=(std::move(other));
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T>::~HashMap() {
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr bool HashMap<K, T>::operator==(HashMap const &other) const {
|
||||
if (m_keys != other.m_keys) {
|
||||
return false;
|
||||
}
|
||||
for (auto &k : m_keys) {
|
||||
auto const a = at(k).value;
|
||||
auto const b = other.at(k).value;
|
||||
if (
|
||||
a != b && // handle one being null and other not
|
||||
(a && b && *a != *b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap const &other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
m_keys = other.m_keys;
|
||||
m_pairs.resize(other.m_pairs.size());
|
||||
for (auto const&k : m_keys) {
|
||||
auto const &src = access(other.m_pairs, k);
|
||||
auto &dst = access(m_pairs, k);
|
||||
dst = ox::make_unique<Pair>();
|
||||
dst->key = src->key;
|
||||
dst->value = src->value;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap &&other) noexcept {
|
||||
if (this != &other) {
|
||||
m_keys = std::move(other.m_keys);
|
||||
m_pairs = std::move(other.m_pairs);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr T &HashMap<K, T>::operator[](MaybeView_t<K> const &key) {
|
||||
auto p = &access(m_pairs, key);
|
||||
if (*p == nullptr) {
|
||||
if (static_cast<double>(m_pairs.size()) * 0.7 <
|
||||
static_cast<double>(m_keys.size())) {
|
||||
expand();
|
||||
p = &access(m_pairs, key);
|
||||
}
|
||||
*p = ox::make_unique<Pair>();
|
||||
(*p)->key = key;
|
||||
m_keys.emplace_back(key);
|
||||
}
|
||||
return (*p)->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr Result<T*> HashMap<K, T>::at(MaybeView_t<K> const &key) noexcept {
|
||||
auto &p = access(m_pairs, key);
|
||||
if (!p) {
|
||||
return ox::Error{1, "value not found for given key"};
|
||||
}
|
||||
return &p->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr Result<const T*> HashMap<K, T>::at(MaybeView_t<K> const &key) const noexcept {
|
||||
auto &p = access(m_pairs, key);
|
||||
if (!p) {
|
||||
return ox::Error{1, "value not found for given key"};
|
||||
}
|
||||
return &p->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr void HashMap<K, T>::erase(MaybeView_t<K> const &key) {
|
||||
if (!contains(key)) {
|
||||
return;
|
||||
}
|
||||
auto &c = access(m_pairs, key);
|
||||
c = std::move(c->next);
|
||||
std::ignore = m_keys.erase(ox::find(m_keys.begin(), m_keys.end(), key));
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr bool HashMap<K, T>::contains(MaybeView_t<K> const &key) const noexcept {
|
||||
return access(m_pairs, key).get() != nullptr;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr std::size_t HashMap<K, T>::size() const noexcept {
|
||||
return m_keys.size();
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr Vector<K> const &HashMap<K, T>::keys() const noexcept {
|
||||
return m_keys;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr Vector<T> HashMap<K, T>::values() const noexcept {
|
||||
Vector<T> out;
|
||||
out.reserve(m_keys.size());
|
||||
for (auto const &p : m_pairs) {
|
||||
if (p) {
|
||||
out.emplace_back(*p->value);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr void HashMap<K, T>::clear() {
|
||||
m_pairs.clear();
|
||||
m_pairs.resize(127);
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
constexpr void HashMap<K, T>::expand() {
|
||||
Vector<UPtr<Pair>> r{m_pairs.size() * 2};
|
||||
for (std::size_t i = 0; i < m_keys.size(); ++i) {
|
||||
auto const &k = m_keys[i];
|
||||
access(r, k) = std::move(access(m_pairs, k));
|
||||
}
|
||||
m_pairs = std::move(r);
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
template<typename KK>
|
||||
constexpr UPtr<typename HashMap<K, T>::Pair> const &HashMap<K, T>::access(
|
||||
Vector<UPtr<Pair>> const& pairs,
|
||||
KK const &key) const {
|
||||
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
|
||||
auto const &p = *pairs.at(h).unwrap();
|
||||
if (p == nullptr || p->key == key) {
|
||||
return p;
|
||||
}
|
||||
auto c = &p->next;
|
||||
while (true) {
|
||||
if (*c == nullptr || (*c)->key == key) {
|
||||
return *c;
|
||||
}
|
||||
c = &(*c)->next;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename K, typename T>
|
||||
template<typename KK>
|
||||
constexpr UPtr<typename HashMap<K, T>::Pair> &HashMap<K, T>::access(
|
||||
Vector<UPtr<Pair>> &pairs,
|
||||
KK const &key) {
|
||||
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
|
||||
auto &p = *pairs.at(h).unwrap();
|
||||
if (p == nullptr || p->key == key) {
|
||||
return p;
|
||||
}
|
||||
auto c = &p->next;
|
||||
while (true) {
|
||||
if (*c == nullptr || (*c)->key == key) {
|
||||
return *c;
|
||||
}
|
||||
c = &(*c)->next;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox::heapmgr {
|
||||
|
||||
struct HeapSegment {
|
||||
std::size_t size;
|
||||
uint8_t inUse;
|
||||
|
||||
void init(std::size_t maxSize) noexcept;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
T *data() noexcept;
|
||||
|
||||
template<typename T = uint8_t>
|
||||
[[nodiscard]]
|
||||
T *end() noexcept;
|
||||
|
||||
};
|
||||
|
||||
void initHeap(void *heapBegin, void *heapEnd) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
void *malloc(std::size_t allocSize) noexcept;
|
||||
|
||||
void free(void *ptr) noexcept;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<tuple>)
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#else
|
||||
|
||||
namespace std {
|
||||
|
||||
inline constexpr struct ignore_t {
|
||||
constexpr void operator=(auto&&) const noexcept {}
|
||||
} ignore;
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<initializer_list>)
|
||||
#include <initializer_list>
|
||||
#else
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
class initializer_list {
|
||||
private:
|
||||
T *m_begin = nullptr;
|
||||
size_t m_size = 0;
|
||||
|
||||
public:
|
||||
constexpr initializer_list() noexcept = default;
|
||||
|
||||
constexpr size_t size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
constexpr T *begin() const noexcept {
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
constexpr T *end() const noexcept {
|
||||
return m_begin + m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr initializer_list(T *begin, size_t size) noexcept: m_begin(begin), m_size(size) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
#include <istream>
|
||||
#endif
|
||||
|
||||
#include "reader.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
class StreamReader: public Reader_v {
|
||||
private:
|
||||
std::istream &m_strm;
|
||||
public:
|
||||
constexpr explicit StreamReader(std::istream &stream) noexcept: m_strm(stream) {}
|
||||
Result<char> peek() const noexcept override;
|
||||
Result<size_t> read(char *v, size_t cnt) noexcept override;
|
||||
Error seekg(size_t p) noexcept override;
|
||||
Error seekg(int64_t p, ios_base::seekdir sd) noexcept override;
|
||||
Result<size_t> tellg() noexcept override;
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "error.hpp"
|
||||
#include "math.hpp"
|
||||
|
||||
#if !__has_include(<iterator>)
|
||||
#include "stddef.hpp"
|
||||
|
||||
namespace std {
|
||||
|
||||
struct input_iterator_tag {
|
||||
};
|
||||
|
||||
struct output_iterator_tag {
|
||||
};
|
||||
|
||||
struct forward_iterator_tag: public input_iterator_tag {
|
||||
};
|
||||
|
||||
struct bidirectional_iterator_tag: public forward_iterator_tag {
|
||||
};
|
||||
|
||||
struct random_access_iterator_tag: public bidirectional_iterator_tag {
|
||||
};
|
||||
|
||||
struct contiguous_iterator_tag: public random_access_iterator_tag {
|
||||
};
|
||||
|
||||
}
|
||||
#else
|
||||
#include <iterator>
|
||||
#endif
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename Category, typename T, typename DiffType = std::ptrdiff_t,
|
||||
typename PointerType = T*, typename ReferenceType = T&>
|
||||
struct Iterator {
|
||||
using iterator_category = Category;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using difference_type = DiffType;
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename RefType, typename PtrType, bool reverse = false>
|
||||
struct SpanIterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
private:
|
||||
PtrType m_t = nullptr;
|
||||
std::size_t m_offset = 0;
|
||||
std::size_t m_max = 0;
|
||||
|
||||
public:
|
||||
constexpr SpanIterator() noexcept = default;
|
||||
|
||||
constexpr SpanIterator(PtrType t, std::size_t offset, std::size_t max) noexcept:
|
||||
m_t(t),
|
||||
m_offset(offset),
|
||||
m_max(max) {
|
||||
}
|
||||
|
||||
constexpr auto offset() const noexcept {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
constexpr SpanIterator operator+(std::size_t s) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return SpanIterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
|
||||
} else {
|
||||
return SpanIterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto operator-(const SpanIterator &other) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return m_offset + other.m_offset;
|
||||
} else {
|
||||
return m_offset - other.m_offset;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr SpanIterator operator-(std::size_t s) const noexcept {
|
||||
if constexpr(reverse) {
|
||||
return SpanIterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
|
||||
} else {
|
||||
return SpanIterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr SpanIterator &operator+=(std::size_t s) noexcept {
|
||||
if constexpr(reverse) {
|
||||
m_offset = max<std::size_t>(m_offset - s, 0);
|
||||
} else {
|
||||
m_offset = min(m_offset + s, m_max);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr SpanIterator &operator-=(std::size_t s) noexcept {
|
||||
if constexpr(reverse) {
|
||||
m_offset = min(m_offset + s, m_max);
|
||||
} else {
|
||||
m_offset = max<std::size_t>(m_offset - s, 0);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr SpanIterator &operator++() noexcept {
|
||||
return operator+=(1);
|
||||
}
|
||||
|
||||
constexpr SpanIterator &operator--() noexcept {
|
||||
return operator-=(1);
|
||||
}
|
||||
|
||||
constexpr PtrType operator->() const noexcept {
|
||||
boundsCheck(m_offset, m_max, "SpanIterator access overflow");
|
||||
return &m_t[m_offset];
|
||||
}
|
||||
|
||||
constexpr RefType operator*() const noexcept {
|
||||
boundsCheck(m_offset, m_max, "SpanIterator access overflow");
|
||||
return m_t[m_offset];
|
||||
}
|
||||
|
||||
constexpr RefType operator[](std::size_t s) const noexcept {
|
||||
boundsCheck(s, m_max, "SpanIterator access overflow");
|
||||
return m_t[s];
|
||||
}
|
||||
|
||||
constexpr bool operator<(const SpanIterator &other) const noexcept {
|
||||
return m_offset < other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const SpanIterator &other) const noexcept {
|
||||
return m_offset > other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const SpanIterator &other) const noexcept {
|
||||
return m_offset <= other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const SpanIterator &other) const noexcept {
|
||||
return m_offset >= other.m_offset;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const SpanIterator &other) const noexcept {
|
||||
return m_t == other.m_t && m_offset == other.m_offset && m_max == other.m_max;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const SpanIterator &other) const noexcept {
|
||||
return m_t != other.m_t || m_offset != other.m_offset || m_max != other.m_max;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "typetraits.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T min(T a, T b) noexcept requires(ox::is_integral_v<T>) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr const T &min(const T &a, const T &b) noexcept requires(!ox::is_integral_v<T>) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T max(T a, T b) noexcept requires(ox::is_integral_v<T>) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr const T &max(const T &a, const T &b) noexcept requires(!ox::is_integral_v<T>) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T clamp(T v, T lo, T hi) noexcept requires(ox::is_integral_v<T>) {
|
||||
return min(ox::max(v, lo), hi);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr const T &clamp(const T &v, const T &lo, const T &hi) noexcept requires(!ox::is_integral_v<T>) {
|
||||
return min(ox::max(v, lo), hi);
|
||||
}
|
||||
|
||||
template<typename I, typename E>
|
||||
[[nodiscard]]
|
||||
constexpr I pow(I v, E e) noexcept {
|
||||
I out = 1;
|
||||
for (E i = 0; i < e; ++i) {
|
||||
out *= v;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<string>)
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace ox {
|
||||
|
||||
// Maybe StringView. If T is a string type, MaybeType::type/MaybeView_t is a
|
||||
// StringView. This avoids creating unnecessary Strings when taking a
|
||||
// StringView or C string as a function argument.
|
||||
template<typename T>
|
||||
struct MaybeView {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using MaybeView_t = typename MaybeView<T>::type;
|
||||
|
||||
#if __has_include(<string>)
|
||||
template<typename CharT, typename Traits, typename Allocator>
|
||||
struct MaybeView<std::basic_string<CharT, Traits, Allocator>> {
|
||||
using type = std::basic_string_view<CharT, Traits>;
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
|
||||
#if __has_include(<cstring>)
|
||||
#include<cstring>
|
||||
#else
|
||||
extern "C" {
|
||||
|
||||
void *memcpy(void *dest, const void *src, std::size_t size);
|
||||
|
||||
void *memmove(void *dest, const void *src, std::size_t size);
|
||||
|
||||
void *memset(void *ptr, int val, std::size_t size);
|
||||
|
||||
int memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T1, typename T2>
|
||||
constexpr T1 *listcpy(T1 *dest, T2 *src, std::size_t maxLen) noexcept {
|
||||
using T1Type = typename ox::remove_reference<decltype(dest[0])>::type;
|
||||
std::size_t i = 0;
|
||||
while (i < maxLen) {
|
||||
dest[i] = static_cast<T1Type>(src[i]);
|
||||
++i;
|
||||
}
|
||||
// set null terminator
|
||||
dest[i] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, std::size_t size) noexcept;
|
||||
|
||||
void *memset(void *ptr, int val, std::size_t size) noexcept;
|
||||
|
||||
void *memcpy(void *dest, const void *src, std::size_t size) noexcept;
|
||||
|
||||
int memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void *memsetElements(T *ptr, T val, std::size_t elements) noexcept {
|
||||
return memset(ptr, val, elements * sizeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<memory>)
|
||||
|
||||
#include <memory>
|
||||
|
||||
#else
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace std {
|
||||
|
||||
template<class T>
|
||||
struct allocator {
|
||||
[[nodiscard]]
|
||||
constexpr T *allocate(size_t n) {
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
constexpr void deallocate(T *p, std::size_t) {
|
||||
::operator delete(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr T *construct_at(T *p, Args &&...args ) {
|
||||
return new (p) T(ox::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "error.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
namespace ox {
|
||||
|
||||
/*
|
||||
* safeDelete exists because deleting an incomplete type will simply
|
||||
* free the memory without running the destructor.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
constexpr void safeDelete(T *val) requires(sizeof(T) >= 1) {
|
||||
delete val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void safeDeleteArray(T *val) requires(sizeof(T) >= 1) {
|
||||
delete[] val;
|
||||
}
|
||||
|
||||
struct DefaultDelete {
|
||||
constexpr void operator()(auto *p) noexcept {
|
||||
safeDelete(p);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename Deleter = DefaultDelete>
|
||||
class SharedPtr {
|
||||
|
||||
private:
|
||||
T *m_t = nullptr;
|
||||
int *m_refCnt = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr SharedPtr(T *t = nullptr) noexcept: m_t(t), m_refCnt(new int) {
|
||||
}
|
||||
|
||||
constexpr SharedPtr(SharedPtr &other) {
|
||||
m_t = other.m_t;
|
||||
m_refCnt = other.m_refCnt;
|
||||
++*m_refCnt;
|
||||
}
|
||||
|
||||
constexpr SharedPtr(const SharedPtr&) = delete;
|
||||
|
||||
template<typename U>
|
||||
constexpr SharedPtr(SharedPtr<U> &&other) noexcept {
|
||||
m_t = other.m_t;
|
||||
m_refCnt = other.m_refCnt;
|
||||
other.m_refCnt = nullptr;
|
||||
}
|
||||
|
||||
~SharedPtr() {
|
||||
if (m_refCnt) {
|
||||
--*m_refCnt;
|
||||
if (!*m_refCnt) {
|
||||
Deleter()(m_t);
|
||||
safeDelete(m_refCnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T *get() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
constexpr T *reset() noexcept {
|
||||
if (m_refCnt) {
|
||||
--*m_refCnt;
|
||||
if (!*m_refCnt) {
|
||||
safeDelete(m_refCnt);
|
||||
Deleter()(m_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr void reset(U *other) {
|
||||
reset();
|
||||
m_t = other;
|
||||
m_refCnt = new int(1);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr SharedPtr &operator=(SharedPtr<U> &&other) {
|
||||
reset(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T *operator->() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
constexpr T &operator*() const noexcept {
|
||||
return *m_t;
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(const SharedPtr<T> &p1, const SharedPtr<T> &p2) noexcept {
|
||||
return p1.get() == p2.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(const SharedPtr<T> &p1, std::nullptr_t) noexcept {
|
||||
return p1.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(std::nullptr_t, const SharedPtr<T> &p2) noexcept {
|
||||
return p2.get();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(const SharedPtr<T> &p1, const SharedPtr<T> &p2) noexcept {
|
||||
return p1.get() != p2.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(const SharedPtr<T> &p1, std::nullptr_t) noexcept {
|
||||
return !p1.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(std::nullptr_t, const SharedPtr<T> &p2) noexcept {
|
||||
return !p2.get();
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename Deleter = DefaultDelete>
|
||||
class UPtr {
|
||||
|
||||
private:
|
||||
T *m_t = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr UPtr(T *t = nullptr) noexcept: m_t(t) {
|
||||
}
|
||||
|
||||
constexpr UPtr(UPtr&) = delete;
|
||||
|
||||
constexpr UPtr(const UPtr&) = delete;
|
||||
|
||||
template<typename U, typename UDeleter>
|
||||
constexpr UPtr(UPtr<U, UDeleter> &&other) noexcept {
|
||||
m_t = other.release();
|
||||
}
|
||||
|
||||
constexpr ~UPtr() {
|
||||
Deleter()(m_t);
|
||||
}
|
||||
|
||||
constexpr T *release() noexcept {
|
||||
auto t = m_t;
|
||||
m_t = nullptr;
|
||||
return t;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T *get() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
constexpr void reset(UPtr &&other = UPtr()) {
|
||||
auto t = m_t;
|
||||
m_t = other.release();
|
||||
Deleter()(t);
|
||||
}
|
||||
|
||||
constexpr UPtr &operator=(UPtr const&other) = delete;
|
||||
|
||||
template<typename U, typename UDeleter>
|
||||
constexpr UPtr &operator=(UPtr<U, UDeleter> const&other) = delete;
|
||||
|
||||
constexpr UPtr &operator=(UPtr<T> &&other) noexcept {
|
||||
reset(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U, typename UDeleter>
|
||||
constexpr UPtr &operator=(UPtr<U, UDeleter> &&other) noexcept {
|
||||
reset(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T *operator->() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
constexpr T &operator*() const noexcept {
|
||||
return *m_t;
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept {
|
||||
return m_t;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(const UPtr<T> &p1, const UPtr<T> &p2) noexcept {
|
||||
return p1.get() == p2.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(const UPtr<T> &p1, std::nullptr_t) noexcept {
|
||||
return p1.get() == nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator==(std::nullptr_t, const UPtr<T> &p2) noexcept {
|
||||
return p2.get() == nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(const UPtr<T> &p1, const UPtr<T> &p2) noexcept {
|
||||
return p1.get() != p2.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(const UPtr<T> &p1, std::nullptr_t) noexcept {
|
||||
return !p1.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool operator!=(std::nullptr_t, const UPtr<T> &p2) noexcept {
|
||||
return !p2.get();
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename U = T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr auto make_unique(Args&&... args) {
|
||||
return UPtr<U>(new T(ox::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T, typename U = T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr Result<UPtr<U>> make_unique_catch(Args&&... args) noexcept {
|
||||
try {
|
||||
return UPtr<U>(new T(ox::forward<Args>(args)...));
|
||||
} catch (ox::Exception const&ex) {
|
||||
return ex.toError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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 "error.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <malloc.h>
|
||||
#elif OX_USE_STDLIB
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
|
||||
#if __has_include(<new>)
|
||||
#include <new>
|
||||
#else
|
||||
constexpr void *operator new(std::size_t, void *addr) noexcept {
|
||||
return addr;
|
||||
}
|
||||
|
||||
constexpr void *operator new[](std::size_t, void *addr) noexcept {
|
||||
return addr;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T* launder(T* __p) noexcept {
|
||||
return __builtin_launder(__p);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args, bool noex>
|
||||
void launder(T(*)(Args...) noexcept(noex)) = delete;
|
||||
template<typename T, typename... Args, bool noex>
|
||||
void launder(T(*)(Args......) noexcept(noex)) = delete;
|
||||
void launder(void*) = delete;
|
||||
void launder(void const*) = delete;
|
||||
void launder(volatile void*) = delete;
|
||||
void launder(volatile void const*) = delete;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
namespace ox {
|
||||
|
||||
/**
|
||||
* Aliases type T in size and alignment to allow allocating space for a T
|
||||
* without running the constructor.
|
||||
*/
|
||||
template<typename T, std::size_t sz = sizeof(T)>
|
||||
struct alignas(alignof(T)) AllocAlias {
|
||||
char buff[sz];
|
||||
constexpr AllocAlias() noexcept = default;
|
||||
[[nodiscard]]
|
||||
auto data() noexcept {
|
||||
return reinterpret_cast<T*>(this);
|
||||
}
|
||||
[[nodiscard]]
|
||||
auto data() const noexcept {
|
||||
return reinterpret_cast<T const*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename U = T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr U *make(Args &&...args) noexcept {
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
return new T(ox::forward<Args>(args)...);
|
||||
} catch (std::exception const &ex) {
|
||||
ox::panic(ox::Error(1, ex.what()), ex.what());
|
||||
return nullptr;
|
||||
} catch (...) {
|
||||
ox::panic(ox::Error(2, "Allocation or constructor failed"), "Allocation or constructor failed");
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return new T(ox::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr Result<T*> makeCatch(Args &&...args) noexcept {
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
return new T(ox::forward<Args>(args)...);
|
||||
} catch (const ox::Exception &ex) {
|
||||
return ex.toError();
|
||||
} catch (...) {
|
||||
return ox::Error(1, "Allocation or constructor failed");
|
||||
}
|
||||
#else
|
||||
return new T(ox::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr auto MallocaStackLimit = defines::UseStdLib ? 1024 : 0;
|
||||
|
||||
/**
|
||||
* MallocaPtr will automatically cleanup the pointed to address upon
|
||||
* destruction if the size of the allocation is greater than MallocaStackLimit.
|
||||
*/
|
||||
template<typename T>
|
||||
class MallocaPtr {
|
||||
|
||||
private:
|
||||
bool m_onHeap = false;
|
||||
T *m_val = nullptr;
|
||||
|
||||
public:
|
||||
constexpr MallocaPtr() noexcept = default;
|
||||
|
||||
constexpr MallocaPtr(MallocaPtr &other) = delete;
|
||||
|
||||
constexpr MallocaPtr(const MallocaPtr &other) = delete;
|
||||
|
||||
constexpr MallocaPtr(MallocaPtr &&other) noexcept {
|
||||
m_onHeap = other.m_onHeap;
|
||||
m_val = other.m_val;
|
||||
other.m_onHeap = false;
|
||||
other.m_val = nullptr;
|
||||
}
|
||||
|
||||
constexpr MallocaPtr(bool onHeap, T *val) noexcept {
|
||||
m_onHeap = onHeap;
|
||||
m_val = val;
|
||||
}
|
||||
|
||||
constexpr ~MallocaPtr() noexcept {
|
||||
m_val->~T();
|
||||
if (m_onHeap && m_val) {
|
||||
delete[] reinterpret_cast<uint8_t*>(m_val);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const T *get() const noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr T *get() noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr MallocaPtr &operator=(MallocaPtr &other) = delete;
|
||||
|
||||
constexpr MallocaPtr &operator=(const MallocaPtr &other) = delete;
|
||||
|
||||
constexpr MallocaPtr &operator=(MallocaPtr &&other) noexcept {
|
||||
if (m_onHeap && m_val) {
|
||||
delete[] reinterpret_cast<uint8_t*>(m_val);
|
||||
}
|
||||
m_onHeap = other.m_onHeap;
|
||||
m_val = other.m_val;
|
||||
other.m_onHeap = false;
|
||||
other.m_val = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const T *operator->() const noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr T *operator->() noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr explicit operator const T*() const noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr explicit operator T*() noexcept {
|
||||
return reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr const T &operator*() const noexcept {
|
||||
return *reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr T &operator*() noexcept {
|
||||
return *reinterpret_cast<T*>(m_val);
|
||||
}
|
||||
|
||||
constexpr bool operator==(std::nullptr_t) const noexcept {
|
||||
return m_val == nullptr;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const MallocaPtr<T> &other) const noexcept {
|
||||
return m_val == other.m_val && m_onHeap == other.m_onHeap;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const MallocaPtr<T> &other) const noexcept {
|
||||
return m_val != other.m_val || m_onHeap != other.m_onHeap;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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 "memory.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T, std::size_t buffSize = sizeof(T)>
|
||||
class Optional {
|
||||
private:
|
||||
T *m_ptr = nullptr;
|
||||
AllocAlias<T> m_data = {};
|
||||
|
||||
public:
|
||||
constexpr Optional() noexcept = default;
|
||||
|
||||
template<typename ...Args>
|
||||
explicit constexpr Optional(ox::in_place_t, Args &&... args);
|
||||
|
||||
constexpr Optional(const Optional &other) {
|
||||
if (other.m_ptr) {
|
||||
m_ptr = std::construct_at<T>(m_data.data(), *other.m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr Optional(Optional &&other) noexcept {
|
||||
if (other.m_ptr) {
|
||||
m_ptr = std::construct_at<T>(m_data.data(), std::move(*other.m_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~Optional() {
|
||||
if (m_ptr) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T &value() & noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T &value() const & noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr T &&value() && noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T &&value() const && noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) const& noexcept {
|
||||
if (!m_ptr) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) && noexcept {
|
||||
if (!m_ptr) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return std::move(*m_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) const& noexcept {
|
||||
if (!m_ptr) {
|
||||
return alt;
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) && noexcept {
|
||||
if (!m_ptr) {
|
||||
return alt;
|
||||
}
|
||||
return std::move(*m_ptr);
|
||||
}
|
||||
|
||||
constexpr T &operator*() & noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T &operator*() const & noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr T &&operator*() && noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T &&operator*() const && noexcept {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr T *operator->() noexcept {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T *operator->() const noexcept {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
constexpr Optional &operator=(const Optional &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
if (other.m_ptr) {
|
||||
if (!m_ptr) {
|
||||
emplace();
|
||||
}
|
||||
*m_ptr = *other.m_ptr;
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Optional &operator=(Optional &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
if (other.m_ptr) {
|
||||
if (!m_ptr) {
|
||||
emplace();
|
||||
}
|
||||
*m_ptr = std::move(*other.m_ptr);
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
constexpr T &emplace(Args &&...args) {
|
||||
reset();
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_ptr = new T(ox::forward<Args>(args)...);
|
||||
} else {
|
||||
m_ptr = std::construct_at<T>(reinterpret_cast<T*>(m_data.data()), ox::forward<Args>(args)...);
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
template<typename U, class... Args>
|
||||
constexpr T &emplace_subclass(Args &&...args) {
|
||||
static_assert(sizeof(U) <= buffSize, "Subclass is too large for this Optional");
|
||||
reset();
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_ptr = new U(ox::forward<Args>(args)...);
|
||||
} else {
|
||||
m_ptr = std::construct_at<U>(reinterpret_cast<U*>(m_data.data()), ox::forward<Args>(args)...);
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool has_value() const noexcept {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const noexcept {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
constexpr void reset() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_ptr);
|
||||
} else if (has_value()) {
|
||||
value().~T();
|
||||
}
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T, std::size_t buffSize>
|
||||
template<typename... Args>
|
||||
constexpr Optional<T, buffSize>::Optional(ox::in_place_t, Args &&... args) {
|
||||
emplace(ox::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T, typename U = T>
|
||||
struct Pair {
|
||||
T a{};
|
||||
U b{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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 <ox/std/concepts.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
class Point {
|
||||
public:
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.Point";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
|
||||
constexpr Point() noexcept = default;
|
||||
|
||||
constexpr Point(int x, int y) noexcept;
|
||||
|
||||
explicit constexpr Point(class Vec2 const&pt) noexcept;
|
||||
|
||||
constexpr Point operator+(const Point &p) const noexcept;
|
||||
|
||||
constexpr Point operator-(const Point &p) const noexcept;
|
||||
|
||||
constexpr Point operator*(const Point &p) const noexcept;
|
||||
|
||||
constexpr Point operator/(const Point &p) const noexcept;
|
||||
|
||||
|
||||
constexpr Point operator+=(const Point &p) noexcept;
|
||||
|
||||
constexpr Point operator-=(const Point &p) noexcept;
|
||||
|
||||
constexpr Point operator*=(const Point &p) noexcept;
|
||||
|
||||
constexpr Point operator/=(const Point &p) noexcept;
|
||||
|
||||
|
||||
constexpr Point operator+(int i) const noexcept;
|
||||
|
||||
constexpr Point operator-(int i) const noexcept;
|
||||
|
||||
constexpr Point operator*(int i) const noexcept;
|
||||
|
||||
constexpr Point operator/(int i) const noexcept;
|
||||
|
||||
|
||||
constexpr Point operator+=(int i) noexcept;
|
||||
|
||||
constexpr Point operator-=(int i) noexcept;
|
||||
|
||||
constexpr Point operator*=(int i) noexcept;
|
||||
|
||||
constexpr Point operator/=(int i) noexcept;
|
||||
|
||||
|
||||
constexpr bool operator==(const Point&) const noexcept;
|
||||
|
||||
constexpr bool operator!=(const Point&) const noexcept;
|
||||
|
||||
explicit constexpr operator class Vec2() const noexcept;
|
||||
};
|
||||
|
||||
constexpr Point::Point(int x, int y) noexcept {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator+(const Point &p) const noexcept {
|
||||
auto out = *this;
|
||||
out.x += p.x;
|
||||
out.y += p.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator-(const Point &p) const noexcept {
|
||||
auto out = *this;
|
||||
out.x -= p.x;
|
||||
out.y -= p.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator*(const Point &p) const noexcept {
|
||||
auto out = *this;
|
||||
out.x *= p.x;
|
||||
out.y *= p.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator/(const Point &p) const noexcept {
|
||||
auto out = *this;
|
||||
out.x /= p.x;
|
||||
out.y /= p.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator+=(const Point &p) noexcept {
|
||||
x += p.x;
|
||||
y += p.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator-=(const Point &p) noexcept {
|
||||
x -= p.x;
|
||||
y -= p.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator*=(const Point &p) noexcept {
|
||||
x *= p.x;
|
||||
y *= p.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator/=(const Point &p) noexcept {
|
||||
x /= p.x;
|
||||
y /= p.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
constexpr Point Point::operator+(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.x += i;
|
||||
out.y += i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator-(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.x -= i;
|
||||
out.y -= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator*(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.x *= i;
|
||||
out.y *= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator/(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.x /= i;
|
||||
out.y /= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator+=(int i) noexcept {
|
||||
x += i;
|
||||
y += i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator-=(int i) noexcept {
|
||||
x -= i;
|
||||
y -= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator*=(int i) noexcept {
|
||||
x *= i;
|
||||
y *= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point Point::operator/=(int i) noexcept {
|
||||
x /= i;
|
||||
y /= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool Point::operator==(const Point &p) const noexcept {
|
||||
return x == p.x && y == p.y;
|
||||
}
|
||||
|
||||
constexpr bool Point::operator!=(const Point &p) const noexcept {
|
||||
return x != p.x || y != p.y;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<Point> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Point>());
|
||||
OX_RETURN_ERROR(io->field("x", &obj->x));
|
||||
OX_RETURN_ERROR(io->field("y", &obj->y));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "def.hpp"
|
||||
#include "stddef.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
using RandomSeed = uint64_t[2];
|
||||
|
||||
// An implementation of the Xoroshiro128+ algorithm
|
||||
class OX_PACKED Random {
|
||||
private:
|
||||
RandomSeed m_seed = {540932923848, 540932540932};
|
||||
|
||||
public:
|
||||
constexpr Random() noexcept = default;
|
||||
|
||||
explicit constexpr Random(const RandomSeed &seed) noexcept;
|
||||
|
||||
constexpr void seed(const RandomSeed &seed) noexcept;
|
||||
|
||||
constexpr uint64_t gen() noexcept;
|
||||
};
|
||||
|
||||
constexpr Random::Random(const RandomSeed &seed) noexcept: m_seed{seed[0], seed[1]} {
|
||||
}
|
||||
|
||||
constexpr void Random::seed(const RandomSeed &seed) noexcept {
|
||||
m_seed[0] = seed[0];
|
||||
m_seed[1] = seed[1];
|
||||
}
|
||||
|
||||
constexpr uint64_t Random::gen() noexcept {
|
||||
auto s0 = m_seed[0];
|
||||
auto s1 = m_seed[1];
|
||||
const auto retval = s0 + s1;
|
||||
// reseed for next number
|
||||
s1 ^= s0;
|
||||
m_seed[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
|
||||
m_seed[1] = rotl(s1, 36);
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
class range {
|
||||
|
||||
private:
|
||||
T m_begin = 0;
|
||||
T m_end = 0;
|
||||
|
||||
public:
|
||||
constexpr range(T begin, T end) noexcept: m_begin(begin), m_end(end) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace ox {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool all_of(auto begin, auto end, auto const&pred) noexcept {
|
||||
while (begin != end) {
|
||||
if (!pred(*begin)) {
|
||||
return false;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool any_of(auto begin, auto end, auto const&pred) noexcept {
|
||||
while (begin != end) {
|
||||
if (pred(*begin)) {
|
||||
return true;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 "concepts.hpp"
|
||||
#include "error.hpp"
|
||||
#include "types.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
concept Reader_c = requires(T v) {
|
||||
{v.peek()} -> ox::same_as<ox::Result<char>>;
|
||||
{v.read(static_cast<char*>(nullptr), static_cast<std::size_t>(0))} -> ox::same_as<ox::Result<std::size_t>>;
|
||||
{v.seekg(static_cast<int64_t>(0))} -> ox::same_as<ox::Error>;
|
||||
{v.seekg(static_cast<std::size_t>(0), ios_base::beg)} -> ox::same_as<ox::Error>;
|
||||
{v.tellg()} -> ox::same_as<ox::Result<std::size_t>>;
|
||||
};
|
||||
|
||||
class Reader_v {
|
||||
public:
|
||||
virtual constexpr ~Reader_v() noexcept = default;
|
||||
virtual constexpr ox::Result<char> peek() const noexcept = 0;
|
||||
virtual constexpr ox::Result<std::size_t> read(char*, std::size_t) noexcept = 0;
|
||||
virtual constexpr ox::Result<std::size_t> tellg() noexcept = 0;
|
||||
virtual constexpr ox::Error seekg(std::size_t) noexcept = 0;
|
||||
virtual constexpr ox::Error seekg(int64_t, ios_base::seekdir) = 0;
|
||||
};
|
||||
|
||||
template<Reader_c T>
|
||||
class ReaderT: public Reader_v {
|
||||
private:
|
||||
T m_reader{};
|
||||
public:
|
||||
template<typename ...Args>
|
||||
constexpr explicit ReaderT(Args&&... args) noexcept: m_reader(args...) {
|
||||
}
|
||||
constexpr ox::Result<char> peek() const noexcept override {
|
||||
return m_reader.peek();
|
||||
}
|
||||
constexpr ox::Result<std::size_t> read(char *v, std::size_t cnt) noexcept override {
|
||||
return m_reader.read(v, cnt);
|
||||
}
|
||||
constexpr ox::Error seekg(std::size_t p) noexcept override {
|
||||
return m_reader.seekg(p);
|
||||
}
|
||||
constexpr ox::Error seekg(int64_t p, ios_base::seekdir sd) noexcept override {
|
||||
return m_reader.seekg(p, sd);
|
||||
}
|
||||
constexpr ox::Result<std::size_t> tellg() noexcept override {
|
||||
return m_reader.tellg();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<cassert>)
|
||||
#include <cassert>
|
||||
#else
|
||||
#define assert(e) while (!(e));
|
||||
#endif
|
||||
|
||||
#if __has_include(<cstdlib>)
|
||||
#include <cstdlib>
|
||||
#else
|
||||
extern "C" {
|
||||
[[noreturn]]
|
||||
void abort();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 "buffer.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename PlatSpec>
|
||||
struct VectorMemMap {
|
||||
size_t const smallVecSize = 0; // not a map value
|
||||
typename PlatSpec::size_t size = 0;
|
||||
typename PlatSpec::size_t cap = 0;
|
||||
typename PlatSpec::PtrType items = 0;
|
||||
};
|
||||
|
||||
template<typename PlatSpec>
|
||||
[[nodiscard]]
|
||||
constexpr auto sizeOf(VectorMemMap<PlatSpec> const *t) noexcept {
|
||||
constexpr auto padding = [](size_t const size, size_t const al) {
|
||||
return size % al;
|
||||
};
|
||||
size_t size = 0;
|
||||
if (t->smallVecSize) {
|
||||
size += t->smallVecSize;
|
||||
size += padding(size, PlatSpec::alignOf(t->size));
|
||||
}
|
||||
size += sizeof(t->size);
|
||||
size += padding(size, PlatSpec::alignOf(t->cap));
|
||||
size += sizeof(t->cap);
|
||||
size += padding(size, PlatSpec::alignOf(t->items));
|
||||
size += sizeof(t->items);
|
||||
return size;
|
||||
}
|
||||
|
||||
template<typename PlatSpec, size_t SmallVecSize = 0>
|
||||
[[nodiscard]]
|
||||
constexpr auto alignOf(VectorMemMap<PlatSpec> const&) noexcept {
|
||||
typename PlatSpec::size_t const i = 0;
|
||||
return PlatSpec::alignOf(i);
|
||||
}
|
||||
|
||||
template<typename PlatSpec, typename T>
|
||||
constexpr Error pad(Writer_c auto &w, T const *v) noexcept {
|
||||
auto const a = PlatSpec::alignOf(*v);
|
||||
auto const excess = w.tellp() % a;
|
||||
if (excess) {
|
||||
return w.write(nullptr, a - excess);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename PlatSpec>
|
||||
constexpr Error serialize(Writer_c auto &w, VectorMemMap<PlatSpec> const &vm) noexcept {
|
||||
OX_RETURN_ERROR(w.write(nullptr, vm.smallVecSize));
|
||||
OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.size)));
|
||||
OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.cap)));
|
||||
OX_RETURN_ERROR(serialize(w, PlatSpec::correctEndianness(vm.items)));
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr Error serialize(Writer_c auto &w, Integral_c auto val) noexcept {
|
||||
Array<char, sizeof(val)> tmp;
|
||||
for (auto i = 0u; i < sizeof(val); ++i) {
|
||||
tmp[i] = static_cast<char>((val >> i * 8) & 255);
|
||||
}
|
||||
return w.write(tmp.data(), tmp.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Result<Array<char, sizeof(T)>> serialize(T const &in) noexcept {
|
||||
Array<char, sizeof(T)> out = {};
|
||||
CharBuffWriter w(out);
|
||||
OX_RETURN_ERROR(serialize(w, in));
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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 <ox/std/error.hpp>
|
||||
#include <ox/std/concepts.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
class Size {
|
||||
public:
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.Size";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
|
||||
constexpr Size() noexcept = default;
|
||||
|
||||
constexpr Size(int width, int height) noexcept;
|
||||
|
||||
explicit constexpr Size(class Vec2 const&pt) noexcept;
|
||||
|
||||
constexpr Size operator+(Size p) const noexcept;
|
||||
|
||||
constexpr Size operator-(Size p) const noexcept;
|
||||
|
||||
constexpr Size operator*(Size p) const noexcept;
|
||||
|
||||
constexpr Size operator/(Size p) const noexcept;
|
||||
|
||||
|
||||
constexpr Size operator+=(Size p) noexcept;
|
||||
|
||||
constexpr Size operator-=(Size p) noexcept;
|
||||
|
||||
constexpr Size operator*=(Size p) noexcept;
|
||||
|
||||
constexpr Size operator/=(Size p) noexcept;
|
||||
|
||||
|
||||
constexpr Size operator+(int i) const noexcept;
|
||||
|
||||
constexpr Size operator-(int i) const noexcept;
|
||||
|
||||
constexpr Size operator*(int i) const noexcept;
|
||||
|
||||
constexpr Size operator/(int i) const noexcept;
|
||||
|
||||
|
||||
constexpr Size operator+=(int i) noexcept;
|
||||
|
||||
constexpr Size operator-=(int i) noexcept;
|
||||
|
||||
constexpr Size operator*=(int i) noexcept;
|
||||
|
||||
constexpr Size operator/=(int i) noexcept;
|
||||
|
||||
|
||||
constexpr bool operator==(const Size&) const noexcept;
|
||||
|
||||
constexpr bool operator!=(const Size&) const noexcept;
|
||||
|
||||
explicit constexpr operator class Vec2() const noexcept;
|
||||
};
|
||||
|
||||
constexpr Size::Size(int width, int height) noexcept {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator+(Size p) const noexcept {
|
||||
p.width += width;
|
||||
p.height += height;
|
||||
return p;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator-(Size p) const noexcept {
|
||||
auto out = *this;
|
||||
out.width -= p.width;
|
||||
out.height -= p.height;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator*(Size p) const noexcept {
|
||||
p.width *= width;
|
||||
p.height *= height;
|
||||
return p;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator/(Size p) const noexcept {
|
||||
auto out = *this;
|
||||
out.width /= p.width;
|
||||
out.height /= p.height;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator+=(Size p) noexcept {
|
||||
width += p.width;
|
||||
height += p.height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator-=(Size p) noexcept {
|
||||
width -= p.width;
|
||||
height -= p.height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator*=(Size p) noexcept {
|
||||
width *= p.width;
|
||||
height *= p.height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator/=(Size p) noexcept {
|
||||
width /= p.width;
|
||||
height /= p.height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
constexpr Size Size::operator+(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.width += i;
|
||||
out.height += i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator-(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.width -= i;
|
||||
out.height -= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator*(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.width *= i;
|
||||
out.height *= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator/(int i) const noexcept {
|
||||
auto out = *this;
|
||||
out.width /= i;
|
||||
out.height /= i;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
constexpr Size Size::operator+=(int i) noexcept {
|
||||
width += i;
|
||||
height += i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator-=(int i) noexcept {
|
||||
width -= i;
|
||||
height -= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator*=(int i) noexcept {
|
||||
width *= i;
|
||||
height *= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Size Size::operator/=(int i) noexcept {
|
||||
width /= i;
|
||||
height /= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
constexpr bool Size::operator==(const Size &p) const noexcept {
|
||||
return width == p.width && height == p.height;
|
||||
}
|
||||
|
||||
constexpr bool Size::operator!=(const Size &p) const noexcept {
|
||||
return width != p.width || height != p.height;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<Size> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Size>());
|
||||
OX_RETURN_ERROR(io->field("width", &obj->width));
|
||||
OX_RETURN_ERROR(io->field("height", &obj->height));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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 "def.hpp"
|
||||
#include "hash.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename K, typename T, size_t SmallSz = 0>
|
||||
class SmallMap {
|
||||
|
||||
public:
|
||||
using key_t = K;
|
||||
using value_t = T;
|
||||
struct Pair {
|
||||
K key = {};
|
||||
T value{};
|
||||
};
|
||||
|
||||
protected:
|
||||
using PairVector = Vector<Pair, SmallSz>;
|
||||
PairVector m_pairs;
|
||||
|
||||
public:
|
||||
constexpr SmallMap() = default;
|
||||
|
||||
constexpr SmallMap(SmallMap const&other);
|
||||
|
||||
constexpr SmallMap(SmallMap &&other) noexcept;
|
||||
|
||||
constexpr bool operator==(SmallMap const&other) const;
|
||||
|
||||
constexpr SmallMap &operator=(SmallMap const&other);
|
||||
|
||||
constexpr SmallMap &operator=(SmallMap &&other) noexcept;
|
||||
|
||||
constexpr T &operator[](MaybeView_t<K> const&key);
|
||||
|
||||
constexpr Result<T*> at(MaybeView_t<K> const&key) noexcept;
|
||||
|
||||
constexpr Result<const T*> at(MaybeView_t<K> const&key) const noexcept;
|
||||
|
||||
constexpr void erase(MaybeView_t<K> const&key);
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(MaybeView_t<K> const&key) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Vector<K> keys() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Vector<T> values() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr K const&key(size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const&value(size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &value(size_t i) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Pair const&get(size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Pair &get(size_t i) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::SpanView<Pair> pairs() const noexcept {
|
||||
return m_pairs;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Span<Pair> pairs() noexcept {
|
||||
return m_pairs;
|
||||
}
|
||||
|
||||
constexpr void clear();
|
||||
|
||||
private:
|
||||
template<typename KK>
|
||||
constexpr Pair const*access(PairVector const&pairs, KK const&key, bool &isNew) const;
|
||||
|
||||
template<typename KK>
|
||||
constexpr Pair *access(PairVector &pairs, KK const&key, bool &isNew);
|
||||
|
||||
template<typename KK>
|
||||
constexpr Pair *accessNoCreate(PairVector &pairs, KK const&key);
|
||||
|
||||
};
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr SmallMap<K, T, SmallSz>::SmallMap(SmallMap<K, T, SmallSz> const&other) {
|
||||
m_pairs = other.m_pairs;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr SmallMap<K, T, SmallSz>::SmallMap(SmallMap<K, T, SmallSz> &&other) noexcept {
|
||||
m_pairs = std::move(other.m_pairs);
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr bool SmallMap<K, T, SmallSz>::operator==(SmallMap const&other) const {
|
||||
return m_pairs == other.m_pairs;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr SmallMap<K, T, SmallSz> &SmallMap<K, T, SmallSz>::operator=(SmallMap<K, T, SmallSz> const&other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
m_pairs = other.m_pairs;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr SmallMap<K, T, SmallSz> &SmallMap<K, T, SmallSz>::operator=(SmallMap<K, T, SmallSz> &&other) noexcept {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
m_pairs = std::move(other.m_pairs);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
|
||||
bool isNew{};
|
||||
auto p = access(m_pairs, k, isNew);
|
||||
if (isNew) {
|
||||
p->key = k;
|
||||
}
|
||||
return p->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcept {
|
||||
auto const p = accessNoCreate(m_pairs, k);
|
||||
if (!p) {
|
||||
return {nullptr, ox::Error(1, "value not found for given key")};
|
||||
}
|
||||
return &p->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr Result<const T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) const noexcept {
|
||||
bool isNew{};
|
||||
auto p = access(m_pairs, k, isNew);
|
||||
if (!p) {
|
||||
return {nullptr, ox::Error(1, "value not found for given key")};
|
||||
}
|
||||
return &p->value;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr void SmallMap<K, T, SmallSz>::erase(MaybeView_t<K> const&k) {
|
||||
size_t i{};
|
||||
for (auto const&p : m_pairs) {
|
||||
if (k == p.key) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::ignore = m_pairs.erase(i);
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr bool SmallMap<K, T, SmallSz>::contains(MaybeView_t<K> const&k) const noexcept {
|
||||
bool isNew{};
|
||||
return access(m_pairs, k, isNew) != nullptr;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr std::size_t SmallMap<K, T, SmallSz>::size() const noexcept {
|
||||
return m_pairs.size();
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr Vector<K> SmallMap<K, T, SmallSz>::keys() const noexcept {
|
||||
ox::Vector<K> keys;
|
||||
keys.reserve(m_pairs.size());
|
||||
for (auto const&p : m_pairs) {
|
||||
keys.emplace_back(p.key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr Vector<T> SmallMap<K, T, SmallSz>::values() const noexcept {
|
||||
ox::Vector<T> keys;
|
||||
keys.reserve(m_pairs.size());
|
||||
for (auto const&p : m_pairs) {
|
||||
keys.emplace_back(p.key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept {
|
||||
return m_pairs[i].key;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr T const&SmallMap<K, T, SmallSz>::value(size_t i) const noexcept {
|
||||
return m_pairs[i].value;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr T &SmallMap<K, T, SmallSz>::value(size_t i) noexcept {
|
||||
return m_pairs[i].value;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr typename SmallMap<K, T, SmallSz>::Pair const&SmallMap<K, T, SmallSz>::get(size_t i) const noexcept {
|
||||
return m_pairs[i];
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr typename SmallMap<K, T, SmallSz>::Pair &SmallMap<K, T, SmallSz>::get(size_t i) noexcept {
|
||||
return m_pairs[i];
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
constexpr void SmallMap<K, T, SmallSz>::clear() {
|
||||
m_pairs.clear();
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
template<typename KK>
|
||||
constexpr typename SmallMap<K, T, SmallSz>::Pair const*SmallMap<K, T, SmallSz>::access(
|
||||
PairVector const&pairs, KK const&k, bool &isNew) const {
|
||||
for (auto const&p : pairs) {
|
||||
if (p.key == k) {
|
||||
isNew = false;
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
isNew = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
template<typename KK>
|
||||
constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::access(
|
||||
PairVector &pairs, KK const&k, bool &isNew) {
|
||||
for (auto &p : pairs) {
|
||||
if (p.key == k) {
|
||||
isNew = false;
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
isNew = true;
|
||||
return &pairs.emplace_back();
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
template<typename KK>
|
||||
constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::accessNoCreate(
|
||||
PairVector &pairs, KK const&k) {
|
||||
for (auto &p : pairs) {
|
||||
if (p.key == k) {
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename K, typename V, size_t SmallSz>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<SmallMap<K, V, SmallSz>> auto *obj) noexcept {
|
||||
using Map = SmallMap<K, V, SmallSz>;
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Map>());
|
||||
OX_RETURN_ERROR(io->field("pairs", &obj->m_pairs));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<source_location>)
|
||||
|
||||
#include <source_location>
|
||||
|
||||
#else
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
namespace std {
|
||||
|
||||
class source_location {
|
||||
private:
|
||||
struct __impl {
|
||||
char const*_M_file_name{};
|
||||
char const*_M_function_name{};
|
||||
uint32_t _M_line{};
|
||||
uint32_t _M_column{};
|
||||
};
|
||||
static constexpr __impl Default{
|
||||
._M_file_name = "",
|
||||
._M_function_name = "",
|
||||
._M_line = {},
|
||||
._M_column = {},
|
||||
};
|
||||
__impl const*m_data{&Default};
|
||||
using Raw = decltype(__builtin_source_location());
|
||||
|
||||
public:
|
||||
constexpr source_location() noexcept = default;
|
||||
|
||||
static consteval source_location current(Raw const pSl = __builtin_source_location()) noexcept {
|
||||
source_location sl;
|
||||
if (pSl) {
|
||||
sl.m_data = static_cast<__impl const*>(pSl);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
constexpr uint32_t line() const noexcept {
|
||||
return m_data->_M_line;
|
||||
}
|
||||
|
||||
constexpr uint32_t column() const noexcept {
|
||||
return m_data->_M_column;
|
||||
}
|
||||
|
||||
constexpr ox::CString file_name() const noexcept {
|
||||
return m_data->_M_file_name;
|
||||
}
|
||||
|
||||
constexpr ox::CString function_name() const noexcept {
|
||||
return m_data->_M_function_name;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<array>)
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
#include "array.hpp"
|
||||
#include "bit.hpp"
|
||||
#include "def.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
|
||||
namespace ox {
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Span {
|
||||
|
||||
private:
|
||||
T *m_items{};
|
||||
std::size_t m_size{};
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template<typename RefType = T&, typename PtrType = T*, bool reverse = false>
|
||||
using iterator = SpanIterator<T, RefType, PtrType, reverse>;
|
||||
|
||||
constexpr Span() noexcept = default;
|
||||
|
||||
#if __has_include(<array>)
|
||||
template<std::size_t sz>
|
||||
constexpr Span(std::array<T, sz> &a) noexcept:
|
||||
m_items(a.data()),
|
||||
m_size(a.size()) {
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr Span(std::array<ox::remove_const_t<T>, sz> const &a) noexcept:
|
||||
m_items(a.data()),
|
||||
m_size(a.size()) {
|
||||
}
|
||||
#endif
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr Span(ox::Array<T, sz> &a) noexcept:
|
||||
m_items(a.data()),
|
||||
m_size(a.size()) {
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr Span(ox::Array<ox::remove_const_t<T>, sz> const &a) noexcept:
|
||||
m_items(a.data()),
|
||||
m_size(a.size()) {
|
||||
}
|
||||
|
||||
template<std::size_t sz, typename Allocator>
|
||||
constexpr Span(ox::Vector<T, sz, Allocator> &v) noexcept:
|
||||
m_items(v.data()),
|
||||
m_size(v.size()) {
|
||||
}
|
||||
|
||||
template<std::size_t sz, typename Allocator>
|
||||
constexpr Span(ox::Vector<ox::remove_const_t<T>, sz, Allocator> const &v) noexcept:
|
||||
m_items(v.data()),
|
||||
m_size(v.size()) {
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr Span(T (&a)[sz]) noexcept:
|
||||
m_items(a),
|
||||
m_size(sz) {
|
||||
}
|
||||
|
||||
constexpr Span(T *a, std::size_t sz) noexcept:
|
||||
m_items(a),
|
||||
m_size(sz) {
|
||||
}
|
||||
|
||||
constexpr iterator<> begin() noexcept {
|
||||
return iterator<>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
constexpr iterator<> end() noexcept {
|
||||
return iterator<>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*> begin() const noexcept {
|
||||
return iterator<const T&, const T*>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*> end() const noexcept {
|
||||
return iterator<const T&, const T*>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*> cbegin() const noexcept {
|
||||
return iterator<const T&, const T*>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*> cend() const noexcept {
|
||||
return iterator<const T&, const T*>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T&, T*, true> rbegin() noexcept {
|
||||
return iterator<T&, T*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T&, T*, true> rend() noexcept {
|
||||
return iterator<T&, T*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*, true> crbegin() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*, true> crend() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*, true> rbegin() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<const T&, const T*, true> rend() const noexcept {
|
||||
return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
constexpr T &operator[](std::size_t i) const noexcept {
|
||||
boundsCheck(i, size(), "Span access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
constexpr Span operator+(size_t i) const noexcept {
|
||||
boundsCheck(i, size(), "Span access overflow");
|
||||
return {m_items + i, m_size - i};
|
||||
}
|
||||
|
||||
constexpr Span operator+=(size_t i) noexcept {
|
||||
boundsCheck(i, size(), "Span access overflow");
|
||||
m_items += i;
|
||||
m_size -= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Span operator++(int) noexcept {
|
||||
++m_items;
|
||||
--m_size;
|
||||
if (!m_size) [[unlikely]] {
|
||||
m_items = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Span operator++() noexcept {
|
||||
++m_items;
|
||||
--m_size;
|
||||
if (!m_size) [[unlikely]] {
|
||||
m_items = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto data() const noexcept {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool empty() const noexcept {
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SpanView = Span<T const>;
|
||||
|
||||
template<typename T>
|
||||
constexpr void spancpy(ox::Span<T> const dst, ox::SpanView<T> const src) noexcept {
|
||||
auto const sz = ox::min(dst.size(), src.size());
|
||||
if (std::is_constant_evaluated() || std::is_trivially_copyable_v<T>) {
|
||||
for (size_t i{}; i < sz; ++i) {
|
||||
dst.data()[i] = src.data()[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(dst.data(), src.data(), sz * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 "string.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String genStackTrace([[maybe_unused]]unsigned shave) noexcept;
|
||||
|
||||
/**
|
||||
* Prints a stack trace to stderr.
|
||||
*
|
||||
* @param shave number of call levels to shave off the top
|
||||
*/
|
||||
void printStackTrace(unsigned shave = 1) noexcept;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 "anyptr.hpp"
|
||||
#include "array.hpp"
|
||||
#include "assert.hpp"
|
||||
#include "bit.hpp"
|
||||
#include "bounds.hpp"
|
||||
#include "istring.hpp"
|
||||
#include "byteswap.hpp"
|
||||
#include "concepts.hpp"
|
||||
#include "conv.hpp"
|
||||
#include "cstringview.hpp"
|
||||
#include "cstrops.hpp"
|
||||
#include "def.hpp"
|
||||
#include "defer.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "error.hpp"
|
||||
#include "fmt.hpp"
|
||||
#include "hardware.hpp"
|
||||
#include "hashmap.hpp"
|
||||
#include "heapmgr.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "math.hpp"
|
||||
#include "maybeview.hpp"
|
||||
#include "memops.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "new.hpp"
|
||||
#include "optional.hpp"
|
||||
#include "pair.hpp"
|
||||
#include "point.hpp"
|
||||
#include "random.hpp"
|
||||
#include "realstd.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include "size.hpp"
|
||||
#include "smallmap.hpp"
|
||||
#include "stacktrace.hpp"
|
||||
#include "stddef.hpp"
|
||||
#include "string.hpp"
|
||||
#include "stringliteral.hpp"
|
||||
#include "stringparam.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "strongint.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "trace.hpp"
|
||||
#include "typeinfo.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typetraits.hpp"
|
||||
#include "units.hpp"
|
||||
#include "uuid.hpp"
|
||||
#include "vec.hpp"
|
||||
#include "vector.hpp"
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 "types.hpp"
|
||||
|
||||
#if __has_include(<cstddef>)
|
||||
#include <cstddef>
|
||||
#else
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
|
||||
namespace std {
|
||||
using ptrdiff_t = uintptr_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define OX_PACKED
|
||||
#else
|
||||
#define OX_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define OX_ALIGN4
|
||||
#else
|
||||
#define OX_ALIGN4 __attribute__((aligned(4)))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define OX_ALIGN8
|
||||
#else
|
||||
#define OX_ALIGN8 __attribute__((aligned(8)))
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "error.hpp"
|
||||
#include "math.hpp"
|
||||
#include "types.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<Integer_c Integer>
|
||||
constexpr ox::Error writeItoa(Integer const v, ox::Writer_c auto &writer) noexcept {
|
||||
if (v) {
|
||||
ox::ResizedInt_t<Integer, 64> mod = 1000000000000000000;
|
||||
ox::ResizedInt_t<Integer, 64> val = v;
|
||||
constexpr auto base = 10;
|
||||
auto it = 0;
|
||||
if (val < 0) {
|
||||
OX_RETURN_ERROR(writer.put('-'));
|
||||
val = ~val + 1;
|
||||
}
|
||||
if constexpr(sizeof(v) == 8 && !ox::is_signed_v<Integer>) {
|
||||
auto digit = val / mod;
|
||||
val %= mod;
|
||||
mod /= base;
|
||||
if (digit) {
|
||||
digit -= 10;
|
||||
OX_RETURN_ERROR(writer.put('1'));
|
||||
OX_RETURN_ERROR(writer.put(static_cast<char>('0' + digit)));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
while (mod) {
|
||||
auto const digit = val / mod;
|
||||
val %= mod;
|
||||
mod /= base;
|
||||
if (it || digit) {
|
||||
OX_RETURN_ERROR(writer.put(static_cast<char>('0' + digit)));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 0 is a special case
|
||||
OX_RETURN_ERROR(writer.put('0'));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "istring.hpp"
|
||||
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<string>)
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include "algorithm.hpp"
|
||||
#include "def.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "memops.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include "strconv.hpp"
|
||||
#include "stringliteral.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename Integer>
|
||||
constexpr ox::IString<21> intToStr(Integer v) noexcept;
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
class BasicString {
|
||||
private:
|
||||
Vector<char, SmallStringSize_v> m_buff;
|
||||
|
||||
public:
|
||||
static constexpr auto SmallStringSize = SmallStringSize_v;
|
||||
constexpr BasicString() noexcept;
|
||||
|
||||
constexpr explicit BasicString(std::size_t cap) noexcept;
|
||||
|
||||
constexpr explicit BasicString(const char *str) noexcept;
|
||||
|
||||
constexpr explicit BasicString(const char8_t *str) noexcept;
|
||||
|
||||
constexpr BasicString(const char *str, std::size_t size) noexcept;
|
||||
|
||||
constexpr explicit BasicString(StringLiteral const&str) noexcept;
|
||||
|
||||
constexpr explicit BasicString(StringViewCR str) noexcept;
|
||||
|
||||
constexpr explicit BasicString(BasicString const&) noexcept;
|
||||
|
||||
constexpr BasicString(BasicString&&) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto begin() noexcept {
|
||||
return m_buff.begin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto end() noexcept {
|
||||
return m_buff.end();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto begin() const noexcept {
|
||||
return m_buff.begin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto end() const noexcept {
|
||||
return m_buff.end();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto cbegin() const noexcept {
|
||||
return m_buff.begin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto cend() const noexcept {
|
||||
return m_buff.end();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto rbegin() noexcept {
|
||||
return m_buff.rbegin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto rend() noexcept {
|
||||
return m_buff.rend();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto rbegin() const noexcept {
|
||||
return m_buff.rbegin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto rend() const noexcept {
|
||||
return m_buff.rend();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto crbegin() const noexcept {
|
||||
return m_buff.rbegin();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto crend() const noexcept {
|
||||
return m_buff.rend();
|
||||
}
|
||||
|
||||
constexpr BasicString &operator=(const char *str) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(char c) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(int i) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(int64_t i) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(uint64_t i) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(const BasicString &src) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(BasicString &&src) noexcept;
|
||||
|
||||
constexpr BasicString &operator=(StringViewCR src) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(const char *str) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(char *str) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(char c) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(Integer_c auto i) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(StringViewCR src) noexcept;
|
||||
|
||||
constexpr BasicString &operator+=(BasicString const&src) noexcept;
|
||||
|
||||
constexpr BasicString operator+(const char *str) const noexcept;
|
||||
|
||||
constexpr BasicString operator+(char *str) const noexcept;
|
||||
|
||||
constexpr BasicString operator+(char c) const noexcept;
|
||||
|
||||
constexpr BasicString operator+(Integer_c auto i) const noexcept;
|
||||
|
||||
constexpr BasicString operator+(StringViewCR src) const noexcept;
|
||||
|
||||
constexpr BasicString operator+(BasicString const&src) const noexcept;
|
||||
|
||||
constexpr bool operator==(const char *other) const noexcept;
|
||||
|
||||
constexpr bool operator==(OxString_c auto const&other) const noexcept;
|
||||
|
||||
constexpr bool operator!=(const char *other) const noexcept;
|
||||
|
||||
constexpr bool operator!=(OxString_c auto const&other) const noexcept;
|
||||
|
||||
constexpr bool operator<(BasicString const&other) const noexcept;
|
||||
|
||||
constexpr bool operator>(BasicString const&other) const noexcept;
|
||||
|
||||
constexpr bool operator<=(BasicString const&other) const noexcept;
|
||||
|
||||
constexpr bool operator>=(BasicString const&other) const noexcept;
|
||||
|
||||
constexpr char const&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 {
|
||||
auto currentLen = size();
|
||||
m_buff.resize(m_buff.size() + strLen);
|
||||
ox::listcpy(&m_buff[currentLen], str, strLen);
|
||||
// make sure last element is a null terminator
|
||||
m_buff[currentLen + strLen] = 0;
|
||||
// this can't fail, but it returns an Error to match BString::append
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr Error append(StringViewCR sv) noexcept {
|
||||
return append(sv.data(), sv.size());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr BasicString substr(std::size_t pos) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr BasicString substr(std::size_t begin, std::size_t end) const noexcept;
|
||||
|
||||
constexpr void resize(size_t sz) noexcept {
|
||||
++sz;
|
||||
m_buff.resize(sz);
|
||||
m_buff[sz - 1] = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const char *data() const noexcept {
|
||||
return m_buff.data();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr char *data() noexcept {
|
||||
return m_buff.data();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const char *c_str() const noexcept {
|
||||
return m_buff.data();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr explicit operator const char*() const {
|
||||
return c_str();
|
||||
}
|
||||
|
||||
#if __has_include(<string>)
|
||||
[[nodiscard]]
|
||||
std::string toStdString() const {
|
||||
return c_str();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
explicit operator std::string() const {
|
||||
return c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private:
|
||||
constexpr void set(StringViewCR str) noexcept;
|
||||
|
||||
constexpr void set(const char8_t *str) noexcept;
|
||||
};
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString() noexcept {
|
||||
m_buff.resize(1);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(std::size_t cap) noexcept: m_buff(cap + 1) {}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(const char *str) noexcept {
|
||||
set(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(const char8_t *str) noexcept {
|
||||
set(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(const char *str, std::size_t size) noexcept {
|
||||
m_buff.resize(size + 1);
|
||||
ox::listcpy(m_buff.data(), str, size);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(StringLiteral const&str) noexcept:
|
||||
BasicString(StringView{str.data(), str.size()}) {
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(StringViewCR str) noexcept {
|
||||
set(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(const BasicString &other) noexcept {
|
||||
m_buff = other.m_buff;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v>::BasicString(BasicString &&other) noexcept: m_buff(std::move(other.m_buff)) {
|
||||
other.m_buff.resize(1);
|
||||
other.m_buff[0] = 0;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(const char *str) noexcept {
|
||||
set(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(char c) noexcept {
|
||||
ox::Array<char, 2> str{c, 0};
|
||||
set(str.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(int i) noexcept {
|
||||
this->operator=(static_cast<int64_t>(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(int64_t i) noexcept {
|
||||
set(ox::intToStr(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(uint64_t i) noexcept {
|
||||
set(ox::intToStr(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(const BasicString &src) noexcept {
|
||||
if (this != &src) {
|
||||
set(src);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(BasicString &&src) noexcept {
|
||||
if (this != &src) {
|
||||
m_buff = std::move(src.m_buff);
|
||||
src.m_buff.resize(1);
|
||||
src.m_buff[0] = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(StringViewCR src) noexcept {
|
||||
set(src);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(const char *str) noexcept {
|
||||
std::size_t strLen = ox::strlen(str);
|
||||
std::ignore = append(str, strLen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(char *str) noexcept {
|
||||
return *this += static_cast<const char*>(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(char c) noexcept {
|
||||
const char str[] = {c, 0};
|
||||
return this->operator+=(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(Integer_c auto i) noexcept {
|
||||
auto const str = ox::intToStr(i);
|
||||
return this->operator+=(str.c_str());
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(StringViewCR s) noexcept {
|
||||
std::size_t strLen = s.bytes();
|
||||
std::ignore = append(s.data(), strLen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(BasicString const&src) noexcept {
|
||||
std::ignore = append(src.c_str(), src.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept {
|
||||
const std::size_t strLen = ox::strlen(str);
|
||||
const auto currentLen = size();
|
||||
BasicString<SmallStringSize_v> cpy;
|
||||
cpy.m_buff.resize(m_buff.size() + strLen);
|
||||
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], str, strLen);
|
||||
// make sure last element is a null terminator
|
||||
cpy.m_buff[currentLen + strLen] = 0;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(char *str) const noexcept {
|
||||
return *this + static_cast<const char*>(str);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(char c) const noexcept {
|
||||
const char str[] = {c, 0};
|
||||
return *this + str;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(Integer_c auto i) const noexcept {
|
||||
auto const str = ox::intToStr(i);
|
||||
return *this + str;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(StringViewCR src) const noexcept {
|
||||
const std::size_t strLen = src.size();
|
||||
const auto currentLen = size();
|
||||
BasicString<SmallStringSize_v> cpy(currentLen + strLen);
|
||||
cpy.m_buff.resize(m_buff.size() + strLen);
|
||||
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
|
||||
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(BasicString const&src) const noexcept {
|
||||
const std::size_t strLen = src.size();
|
||||
const auto currentLen = size();
|
||||
BasicString<SmallStringSize_v> cpy(currentLen + strLen);
|
||||
cpy.m_buff.resize(m_buff.size() + strLen);
|
||||
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
|
||||
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
|
||||
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator==(const char *other) const noexcept {
|
||||
bool retval = true;
|
||||
for (auto i = 0u; i < m_buff.size() && (m_buff[i] || other[i]); ++i) {
|
||||
if (m_buff[i] != other[i]) {
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator==(OxString_c auto const&other) const noexcept {
|
||||
return StringView(*this) == StringView(other);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator!=(const char *other) const noexcept {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator!=(OxString_c auto const&other) const noexcept {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator<(BasicString const&other) const noexcept {
|
||||
return ox::strcmp(c_str(), other.c_str()) < 0;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator>(BasicString const&other) const noexcept {
|
||||
return ox::strcmp(c_str(), other.c_str()) > 0;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator<=(BasicString const&other) const noexcept {
|
||||
return ox::strcmp(c_str(), other.c_str()) < 1;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator>=(BasicString const&other) const noexcept {
|
||||
return ox::strcmp(c_str(), other.c_str()) > -1;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr char const&BasicString<SmallStringSize_v>::operator[](std::size_t i) const noexcept {
|
||||
return m_buff[i];
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr char &BasicString<SmallStringSize_v>::operator[](std::size_t i) noexcept {
|
||||
return m_buff[i];
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::substr(std::size_t pos) const noexcept {
|
||||
return BasicString(&m_buff[pos], m_buff.size() - pos - 1);
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::substr(std::size_t begin, std::size_t end) const noexcept {
|
||||
const auto src = &m_buff[begin];
|
||||
const auto size = end - begin;
|
||||
BasicString<SmallStringSize_v> out(size);
|
||||
const auto buff = out.data();
|
||||
ox::listcpy(buff, src, size);
|
||||
buff[size] = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr std::size_t BasicString<SmallStringSize_v>::bytes() const noexcept {
|
||||
return m_buff.size();
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr std::size_t BasicString<SmallStringSize_v>::size() const noexcept {
|
||||
return m_buff.size() - 1;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr void BasicString<SmallStringSize_v>::set(StringViewCR str) noexcept {
|
||||
std::size_t const strBytes = str.bytes();
|
||||
m_buff.resize(strBytes + 1);
|
||||
copy_n(str.data(), strBytes, m_buff.data());
|
||||
*m_buff.back().value = 0;
|
||||
}
|
||||
|
||||
template<std::size_t SmallStringSize_v>
|
||||
constexpr void BasicString<SmallStringSize_v>::set(const char8_t *str) noexcept {
|
||||
std::size_t strBytes = ox::strlen(str) + 1;
|
||||
m_buff.resize(strBytes);
|
||||
ox::listcpy(m_buff.data(), str, strBytes);
|
||||
*m_buff.back().value = 0;
|
||||
}
|
||||
|
||||
extern template class BasicString<8>;
|
||||
|
||||
using String = BasicString<8>;
|
||||
using StringCR = String const&;
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr String toString(StringViewCR sv) noexcept {
|
||||
return ox::String(sv);
|
||||
}
|
||||
|
||||
|
||||
template<typename PlatSpec, std::size_t SmallStringSize_v>
|
||||
[[nodiscard]]
|
||||
constexpr auto sizeOf(BasicString<SmallStringSize_v> const*) noexcept {
|
||||
VectorMemMap<PlatSpec> v{.smallVecSize = SmallStringSize_v};
|
||||
return sizeOf<PlatSpec>(&v);
|
||||
}
|
||||
|
||||
template<typename PlatSpec, std::size_t SmallStringSize_v>
|
||||
[[nodiscard]]
|
||||
constexpr auto alignOf(BasicString<SmallStringSize_v> const&) noexcept {
|
||||
VectorMemMap<PlatSpec> v{.smallVecSize = SmallStringSize_v};
|
||||
return alignOf<PlatSpec>(&v);
|
||||
}
|
||||
|
||||
template<size_t sz>
|
||||
struct MaybeView<BasicString<sz>> {
|
||||
using type = StringView;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "basestringview.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
/**
|
||||
* StringLiteral is used for functions that want to ensure that they are taking
|
||||
* string literals, and not strings outside of the data section of the program
|
||||
* that might get deleted.
|
||||
*/
|
||||
class StringLiteral: public detail::BaseStringView {
|
||||
public:
|
||||
constexpr StringLiteral() noexcept = default;
|
||||
|
||||
constexpr StringLiteral(StringLiteral const &sv) noexcept = default;
|
||||
|
||||
consteval StringLiteral(std::nullptr_t) noexcept {}
|
||||
|
||||
consteval StringLiteral(char const *str, std::size_t const len) noexcept: BaseStringView{str, len} {}
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
consteval StringLiteral(char const *str) noexcept: StringLiteral{str, ox::strlen(str)} {}
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
|
||||
constexpr StringLiteral &operator=(StringLiteral const &other) noexcept {
|
||||
set(other.data(), other.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr char const *c_str() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "cstringview.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
class StringParam {
|
||||
private:
|
||||
ox::String m_value;
|
||||
public:
|
||||
StringParam(StringParam const&) = delete;
|
||||
constexpr StringParam(StringParam &&o) noexcept: m_value{std::move(o.m_value)} {}
|
||||
constexpr StringParam(char const*value) noexcept: m_value{value} {}
|
||||
constexpr StringParam(detail::BaseStringView const&value) noexcept: m_value{value} {}
|
||||
template<size_t sz>
|
||||
constexpr StringParam(ox::IString<sz> const&value) noexcept: m_value{value} {}
|
||||
constexpr StringParam(ox::String const&value) noexcept: m_value{value} {}
|
||||
constexpr StringParam(ox::String &&value) noexcept: m_value{std::move(value)} {}
|
||||
constexpr operator ox::String() && noexcept { return std::move(m_value); }
|
||||
explicit constexpr operator ox::CStringView() const noexcept { return m_value; }
|
||||
explicit constexpr operator ox::StringView() const noexcept { return m_value; }
|
||||
[[nodiscard]]
|
||||
constexpr ox::CStringView view() const noexcept { return m_value; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
#include "basestringview.hpp"
|
||||
#include "cstrops.hpp"
|
||||
#include "maybeview.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<std::size_t buffLen>
|
||||
class IString;
|
||||
|
||||
template<std::size_t buffLen>
|
||||
class BasicString;
|
||||
|
||||
class StringView: public detail::BaseStringView {
|
||||
public:
|
||||
constexpr StringView() noexcept = default;
|
||||
|
||||
constexpr StringView(const StringView &sv) noexcept = default;
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
constexpr StringView(const std::string_view &sv) noexcept: BaseStringView(sv.data(), sv.size()) {}
|
||||
#endif
|
||||
|
||||
constexpr StringView(BaseStringView const&str) noexcept: BaseStringView(str.data(), str.bytes()) {}
|
||||
|
||||
template<std::size_t SmallStrSz>
|
||||
constexpr StringView(const BasicString<SmallStrSz> &str) noexcept: BaseStringView(str.data(), str.size()) {}
|
||||
|
||||
template<std::size_t SmallStrSz>
|
||||
constexpr StringView(const IString<SmallStrSz> &str) noexcept: BaseStringView(str.data(), str.size()) {}
|
||||
|
||||
constexpr StringView(std::nullptr_t) noexcept {}
|
||||
|
||||
constexpr StringView(const char *str) noexcept: BaseStringView(str) {}
|
||||
|
||||
constexpr StringView(const char *str, std::size_t len) noexcept: BaseStringView(str, len) {}
|
||||
|
||||
constexpr auto &operator=(StringView const&other) noexcept {
|
||||
if (&other != this) {
|
||||
set(other.data(), other.size());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using StringViewCR = StringView const&;
|
||||
|
||||
constexpr auto operator==(StringViewCR s1, StringViewCR s2) noexcept {
|
||||
if (s2.size() != s1.size()) {
|
||||
return false;
|
||||
}
|
||||
return ox::strncmp(s1.data(), s2.data(), s1.size()) == 0;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(StringViewCR s1, StringViewCR s2) noexcept {
|
||||
const auto maxLen = ox::min(s1.size(), s2.size());
|
||||
const auto a = &s1.front();
|
||||
const auto b = &s2.front();
|
||||
for (std::size_t i = 0; i < maxLen && (a[i] || b[i]); ++i) {
|
||||
if (a[i] < b[i]) {
|
||||
return -1;
|
||||
} else if (a[i] > b[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (s1.size() > s2.size()) {
|
||||
return 1;
|
||||
} else if (s1.size() < s2.size()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto write(Writer_c auto &writer, StringViewCR sv) noexcept {
|
||||
return writer.write(sv.data(), sv.bytes());
|
||||
}
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
constexpr auto toStdStringView(StringViewCR sv) noexcept {
|
||||
return std::string_view(sv.data(), sv.bytes());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
constexpr ox::Result<int> strToInt(StringViewCR str) noexcept {
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
int total = 0;
|
||||
int multiplier = 1;
|
||||
if (str.size() == 0) [[unlikely]] {
|
||||
return Error{1, "Empty string passed to strToInt"};
|
||||
}
|
||||
for (auto i = static_cast<int64_t>(str.size()) - 1; i != -1; --i) {
|
||||
auto s = static_cast<std::size_t>(i);
|
||||
if (str[s] >= '0' && str[s] <= '9') {
|
||||
total += (str[s] - '0') * multiplier;
|
||||
multiplier *= 10;
|
||||
} else {
|
||||
return ox::Error{1};
|
||||
}
|
||||
}
|
||||
return total;
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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 "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
struct BaseInteger {
|
||||
constexpr BaseInteger() noexcept = default;
|
||||
constexpr BaseInteger(const BaseInteger&) noexcept = default;
|
||||
constexpr BaseInteger &operator=(const BaseInteger&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Integer is a strongly typed integer wrapper used to create strongly typed
|
||||
* integers.
|
||||
*/
|
||||
template<typename T, typename Base = BaseInteger>
|
||||
class Integer: public Base {
|
||||
private:
|
||||
T m_i;
|
||||
|
||||
public:
|
||||
constexpr Integer() noexcept = default;
|
||||
|
||||
constexpr explicit Integer(T i) noexcept;
|
||||
|
||||
constexpr Integer(const Integer<T, Base> &i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> &operator=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator==(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator!=(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator<(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator>(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator<=(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator>=(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator+(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator+=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator-(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator-=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator*(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator*=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator/(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator/=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator%(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator%=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator>>(int i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator>>=(int i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator<<(int i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator<<=(int i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator|(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator|=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator&(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator&=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator^(Integer<T, Base> i) const noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator^=(Integer<T, Base> i) noexcept;
|
||||
|
||||
constexpr Integer<T, Base> operator~() const noexcept;
|
||||
|
||||
// Prefix increment
|
||||
constexpr Integer<T, Base> operator++() noexcept;
|
||||
|
||||
// Postfix increment
|
||||
constexpr Integer<T, Base> operator++(int) noexcept;
|
||||
|
||||
// Prefix decrement
|
||||
constexpr Integer<T, Base> operator--() noexcept;
|
||||
|
||||
// Postfix decrement
|
||||
constexpr Integer<T, Base> operator--(int) noexcept;
|
||||
|
||||
constexpr explicit operator T() const noexcept;
|
||||
|
||||
constexpr operator bool() const noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base>::Integer(T i) noexcept {
|
||||
m_i = i;
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base>::Integer(const Integer<T, Base> &i) noexcept: Base(i), m_i(i.m_i) {
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> &Integer<T, Base>::operator=(Integer<T, Base> i) noexcept {
|
||||
// needed in case T has nodiscard
|
||||
Base::operator=(i);
|
||||
m_i = i.m_i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator==(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i == i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator!=(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i != i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator<(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i < i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator>(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i > i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator<=(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i <= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator>=(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i >= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator+(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i + i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator+=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i += i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator-(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i - i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator-=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i -= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator*(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i * i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator*=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i *= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator/(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i / i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator/=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i /= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator%(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i % i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator%=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i %= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator>>(int i) const noexcept {
|
||||
return Integer<T, Base>(m_i >> i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator>>=(int i) noexcept {
|
||||
return Integer<T, Base>(m_i >>= i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator<<(int i) const noexcept {
|
||||
return Integer<T, Base>(m_i << i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator<<=(int i) noexcept {
|
||||
return Integer<T, Base>(m_i <<= i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator|(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i | i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator|=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i |= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator&(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i & i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator&=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i &= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator^(Integer<T, Base> i) const noexcept {
|
||||
return Integer<T, Base>(m_i ^ i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base> Integer<T, Base>::operator^=(Integer<T, Base> i) noexcept {
|
||||
return Integer<T, Base>(m_i ^= i.m_i);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr inline Integer<T, Base> Integer<T, Base>::operator~() const noexcept {
|
||||
return Integer<T, Base>(~m_i);
|
||||
}
|
||||
|
||||
// Prefix increment
|
||||
template<typename T, class Base>
|
||||
constexpr inline Integer<T, Base> Integer<T, Base>::operator++() noexcept {
|
||||
return Integer<T, Base>(++m_i);
|
||||
}
|
||||
|
||||
// Postfix increment
|
||||
template<typename T, class Base>
|
||||
constexpr inline Integer<T, Base> Integer<T, Base>::operator++(int) noexcept {
|
||||
return Integer<T, Base>(m_i++);
|
||||
}
|
||||
|
||||
// Prefix decrement
|
||||
template<typename T, class Base>
|
||||
constexpr inline Integer<T, Base> Integer<T, Base>::operator--() noexcept {
|
||||
return Integer<T, Base>(--m_i);
|
||||
}
|
||||
|
||||
// Postfix decrement
|
||||
template<typename T, class Base>
|
||||
constexpr inline Integer<T, Base> Integer<T, Base>::operator--(int) noexcept {
|
||||
return Integer<T, Base>(m_i--);
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base>::operator T() const noexcept {
|
||||
return m_i;
|
||||
}
|
||||
|
||||
template<typename T, class Base>
|
||||
constexpr Integer<T, Base>::operator bool() const noexcept {
|
||||
return m_i;
|
||||
}
|
||||
|
||||
using Int8 = Integer<int8_t>;
|
||||
using Int16 = Integer<int16_t>;
|
||||
using Int32 = Integer<int32_t>;
|
||||
using Int64 = Integer<int64_t>;
|
||||
|
||||
using Uint8 = Integer<uint8_t>;
|
||||
using Uint16 = Integer<uint16_t>;
|
||||
using Uint32 = Integer<uint32_t>;
|
||||
using Uint64 = Integer<uint64_t>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 "cstrops.hpp"
|
||||
#include "def.hpp"
|
||||
#include "error.hpp"
|
||||
#include "math.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "types.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr StringView substr(StringViewCR str, std::size_t const pos) noexcept {
|
||||
if (str.size() >= pos) {
|
||||
return {&str[pos], str.size() - pos};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr StringView substr(StringViewCR str, std::size_t const start, std::size_t const end) noexcept {
|
||||
if (str.size() >= start && end >= start) {
|
||||
return {&str[start], end - start};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool beginsWith(StringViewCR base, char const beginning) noexcept {
|
||||
return base.size() && base[0] == beginning;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool endsWith(StringViewCR base, char const ending) noexcept {
|
||||
return base.size() && base[base.size() - 1] == ending;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool beginsWith(StringViewCR base, StringViewCR beginning) noexcept {
|
||||
const auto beginningLen = ox::min(beginning.size(), base.size());
|
||||
return base.size() >= beginning.size() && ox::strncmp(base.data(), beginning, beginningLen) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool endsWith(StringViewCR base, StringViewCR ending) noexcept {
|
||||
const auto endingLen = ending.size();
|
||||
return base.size() >= endingLen && ox::strcmp(base.data() + (base.size() - endingLen), ending) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t find(StringViewCR str, char const search) noexcept {
|
||||
std::size_t i = 0;
|
||||
for (; i < str.size(); ++i) {
|
||||
if (str[i] == search) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t find(StringViewCR str, StringViewCR search) noexcept {
|
||||
std::size_t i = 0;
|
||||
for (; i < str.size(); ++i) {
|
||||
if (beginsWith(substr(str, i), search)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
template<std::size_t smallSz = 0>
|
||||
[[nodiscard]]
|
||||
constexpr ox::Vector<ox::StringView, smallSz> split(StringViewCR str, char const del) noexcept {
|
||||
ox::Vector<ox::StringView, smallSz> out;
|
||||
constexpr auto nextSeg = [](StringViewCR current, char del) {
|
||||
return substr(current, find(current, del) + 1);
|
||||
};
|
||||
for (auto current = str; current.size(); current = nextSeg(current, del)) {
|
||||
const auto next = find(current, del);
|
||||
if (const auto s = substr(current, 0, next); s.size()) {
|
||||
out.emplace_back(s);
|
||||
}
|
||||
current = substr(current, next);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<std::size_t smallSz = 0>
|
||||
[[nodiscard]]
|
||||
constexpr ox::Vector<ox::StringView, smallSz> split(StringViewCR str, StringViewCR del) noexcept {
|
||||
ox::Vector<ox::StringView, smallSz> out;
|
||||
constexpr auto nextSeg = [](StringViewCR current, StringViewCR del) {
|
||||
return substr(current, find(current, del) + del.size());
|
||||
};
|
||||
for (auto current = str; current.size(); current = nextSeg(current, del)) {
|
||||
const auto next = find(current, del);
|
||||
if (const auto s = substr(current, 0, next); s.size()) {
|
||||
out.emplace_back(s);
|
||||
}
|
||||
current = substr(current, next);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Result<std::size_t> lastIndexOf(ox::StringViewCR str, int const character) noexcept {
|
||||
ox::Result<std::size_t> retval = ox::Error(1, "Character not found");
|
||||
for (auto i = static_cast<int>(str.bytes() - 1); i >= 0; --i) {
|
||||
if (str[static_cast<std::size_t>(i)] == character) {
|
||||
retval = static_cast<std::size_t>(i);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef OX_USE_STDLIB
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
#include "istring.hpp"
|
||||
#include "def.hpp"
|
||||
#include "fmt.hpp"
|
||||
#include "hashmap.hpp"
|
||||
#include "string.hpp"
|
||||
#include "typetraits.hpp"
|
||||
#include "units.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void oxTraceInitHook();
|
||||
|
||||
void oxTraceHook(const char *file, int line, const char *ch, const char *msg);
|
||||
|
||||
}
|
||||
|
||||
namespace ox::trace {
|
||||
|
||||
enum class MsgId: char {
|
||||
Init = 2,
|
||||
TraceEvent = 1,
|
||||
Json = '{',
|
||||
};
|
||||
|
||||
struct TraceMsgRcv {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.trace.TraceMsg";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
BasicString<50> file{""};
|
||||
int line = 0;
|
||||
uint64_t time = 0;
|
||||
BasicString<50> ch{""};
|
||||
BasicString<100> msg;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<TraceMsgRcv> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<TraceMsgRcv>());
|
||||
OX_RETURN_ERROR(io->field("file", &obj->file));
|
||||
OX_RETURN_ERROR(io->field("line", &obj->line));
|
||||
OX_RETURN_ERROR(io->field("time", &obj->time));
|
||||
OX_RETURN_ERROR(io->field("ch", &obj->ch));
|
||||
OX_RETURN_ERROR(io->field("msg", &obj->msg));
|
||||
return {};
|
||||
}
|
||||
|
||||
struct TraceMsg {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.trace.TraceMsg";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
const char *file = "";
|
||||
int line = 0;
|
||||
uint64_t time = 0;
|
||||
const char *ch = "";
|
||||
BasicString<100> msg;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<TraceMsg> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<TraceMsg>());
|
||||
OX_RETURN_ERROR(io->fieldCString("file", &obj->file));
|
||||
OX_RETURN_ERROR(io->field("line", &obj->line));
|
||||
OX_RETURN_ERROR(io->field("time", &obj->time));
|
||||
OX_RETURN_ERROR(io->fieldCString("ch", &obj->ch));
|
||||
OX_RETURN_ERROR(io->field("msg", &obj->msg));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
struct InitTraceMsgRcv {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.trace.InitTraceMsg";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String appName;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<InitTraceMsgRcv> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<InitTraceMsgRcv>());
|
||||
OX_RETURN_ERROR(io->field("appName", &obj->appName));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
struct InitTraceMsg {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.trace.InitTraceMsg";
|
||||
static constexpr auto TypeVersion = 2;
|
||||
ox::BasicString<128> appName;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<InitTraceMsg> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<InitTraceMsg>());
|
||||
OX_RETURN_ERROR(io->field("appName", &obj->appName));
|
||||
return {};
|
||||
}
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
constexpr virtual ~Logger() noexcept = default;
|
||||
virtual ox::Error send(const TraceMsg&) noexcept = 0;
|
||||
virtual ox::Error sendInit(const InitTraceMsg&) noexcept = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param logger pointer to the new logger, does NOT take ownership
|
||||
*/
|
||||
void setLogger(Logger *logger) noexcept;
|
||||
|
||||
void send(const TraceMsg &msg) noexcept;
|
||||
|
||||
class OutStream {
|
||||
|
||||
protected:
|
||||
const char *m_delimiter = " ";
|
||||
TraceMsg m_msg;
|
||||
|
||||
public:
|
||||
constexpr OutStream(const char *file, int line, const char *ch, const StringView &msg) noexcept {
|
||||
m_msg.file = file;
|
||||
m_msg.line = line;
|
||||
m_msg.ch = ch;
|
||||
m_msg.msg = BasicString<100>(msg);
|
||||
}
|
||||
|
||||
constexpr OutStream(const char *file, int line, const char *ch, const char *msg = "") noexcept {
|
||||
m_msg.file = file;
|
||||
m_msg.line = line;
|
||||
m_msg.ch = ch;
|
||||
m_msg.msg = msg;
|
||||
}
|
||||
|
||||
template<std::size_t fmtSegmentCnt, typename ...Args>
|
||||
constexpr OutStream(const char *file, int line, const char *ch, detail::Fmt<fmtSegmentCnt> fmtSegments, Args const&...elements) noexcept {
|
||||
static_assert(sizeof...(elements) == fmtSegmentCnt - 1, "Wrong number of trace arguments for format.");
|
||||
m_msg.file = file;
|
||||
m_msg.line = line;
|
||||
m_msg.ch = ch;
|
||||
const auto &firstSegment = fmtSegments.segments[0];
|
||||
std::ignore = m_msg.msg.append(firstSegment.str, firstSegment.length);
|
||||
//const detail::FmtArg elements[sizeof...(args)] = {args...};
|
||||
for (size_t i = 0; auto const&e : std::initializer_list<detail::FmtArg>{elements...}) {
|
||||
m_msg.msg += e.out;
|
||||
const auto &s = fmtSegments.segments[i + 1];
|
||||
std::ignore = m_msg.msg.append(s.str, s.length);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
inline ~OutStream() noexcept {
|
||||
oxTraceHook(m_msg.file, m_msg.line, m_msg.ch, m_msg.msg.c_str());
|
||||
send(m_msg);
|
||||
}
|
||||
|
||||
constexpr OutStream &operator<<(Integer_c auto v) noexcept;
|
||||
|
||||
constexpr OutStream &operator<<(char v) noexcept {
|
||||
if (m_msg.msg.size()) {
|
||||
m_msg.msg += m_delimiter;
|
||||
}
|
||||
m_msg.msg += v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr OutStream &operator<<(StringViewCR v) noexcept {
|
||||
if (m_msg.msg.size()) {
|
||||
m_msg.msg += m_delimiter;
|
||||
}
|
||||
m_msg.msg += v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr OutStream &operator<<(const char *v) noexcept {
|
||||
return operator<<(StringView{v});
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr OutStream &operator<<(const IString<sz> &v) noexcept {
|
||||
return operator<<(v.c_str());
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr OutStream &operator<<(const BasicString<sz> &v) noexcept {
|
||||
return operator<<(v.c_str());
|
||||
}
|
||||
|
||||
constexpr OutStream &operator<<(const Error &err) noexcept {
|
||||
return appendSignedInt(static_cast<int64_t>(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* del sets the delimiter between log segments.
|
||||
*/
|
||||
constexpr OutStream &del(const char *delimiter) noexcept {
|
||||
m_delimiter = delimiter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr OutStream &appendSignedInt(int64_t v) noexcept {
|
||||
if (m_msg.msg.size()) {
|
||||
m_msg.msg += m_delimiter;
|
||||
}
|
||||
m_msg.msg += static_cast<int64_t>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr OutStream &appendUnsignedInt(uint64_t v) noexcept {
|
||||
if (m_msg.msg.size()) {
|
||||
m_msg.msg += m_delimiter;
|
||||
}
|
||||
m_msg.msg += static_cast<int64_t>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
constexpr OutStream &OutStream::operator<<(Integer_c auto v) noexcept {
|
||||
if (m_msg.msg.size()) {
|
||||
m_msg.msg += m_delimiter;
|
||||
}
|
||||
m_msg.msg += v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
class NullStream {
|
||||
|
||||
public:
|
||||
constexpr NullStream(const char*, int, const char*, const StringView&) noexcept {
|
||||
}
|
||||
|
||||
constexpr NullStream(const char*, int, const char*, const char* = "") noexcept {
|
||||
}
|
||||
|
||||
template<std::size_t fmtSegmentCnt, typename ...Args>
|
||||
constexpr NullStream(const char*, int, const char*, detail::Fmt<fmtSegmentCnt>, Args const&...elements) noexcept {
|
||||
static_assert(sizeof...(elements) == fmtSegmentCnt - 1, "Wrong number of trace arguments for format.");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const NullStream &operator<<(const T&) const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const NullStream &del(const char*) const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#if defined(DEBUG)
|
||||
using TraceStream = OutStream;
|
||||
#else
|
||||
using TraceStream = NullStream;
|
||||
#endif
|
||||
|
||||
inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept {
|
||||
if (err) {
|
||||
TraceStream trc(file, line, "ox.error");
|
||||
if (err.src.file_name() != nullptr) {
|
||||
trc << "Error: (" << err.src.file_name() << ":" << err.src.line() << "):";
|
||||
} else {
|
||||
trc << "Error:";
|
||||
}
|
||||
trc << ox::sfmt<BasicString<100>>(fmt, static_cast<uint64_t>(err));
|
||||
}
|
||||
}
|
||||
|
||||
inline void logError(const char *file, int line, const Error &err) noexcept {
|
||||
if (err) {
|
||||
TraceStream trc(file, line, "ox.error");
|
||||
trc << "Error:" << err;
|
||||
if (err.src.file_name() != nullptr) {
|
||||
trc << "(" << err.src.file_name() << ":" << err.src.line() << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init();
|
||||
void init(Logger *logger);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<typeinfo>)
|
||||
#include <typeinfo>
|
||||
#else
|
||||
|
||||
namespace std {
|
||||
|
||||
// this is not at all guaranteed to work, and does not even fully comply with
|
||||
// what little the standard does define
|
||||
struct type_info {
|
||||
private:
|
||||
const char *m_name = "";
|
||||
|
||||
protected:
|
||||
explicit type_info(const char *name): m_name(name) {
|
||||
}
|
||||
|
||||
const char *name() {
|
||||
return m_name;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace std {
|
||||
|
||||
using nullptr_t = decltype(nullptr);
|
||||
using size_t = decltype(alignof(int));
|
||||
|
||||
}
|
||||
|
||||
using size_t = decltype(alignof(int));
|
||||
|
||||
#if __has_include(<cstdint>)
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#else
|
||||
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned uint_t;
|
||||
#if defined(__arm__) || defined(__ppc__)
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef __INTMAX_TYPE__ intmax_t;
|
||||
typedef __UINTMAX_TYPE__ uintmax_t;
|
||||
#else
|
||||
typedef long int64_t;
|
||||
typedef unsigned long uint64_t;
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
#endif
|
||||
|
||||
#if defined(_LP64) || defined(__ppc64__) || defined(__aarch64__)
|
||||
typedef long intptr_t;
|
||||
typedef unsigned long uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef int64_t intptr_t;
|
||||
typedef uint64_t uintptr_t;
|
||||
#elif defined(_LP32) || defined(__ppc__) || defined(_WIN32) || defined(__arm__)
|
||||
typedef int32_t intptr_t;
|
||||
typedef uint32_t uintptr_t;
|
||||
#else
|
||||
#error intptr_t, and uintptr_t undefined
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
using uint_t = unsigned;
|
||||
|
||||
namespace ox {
|
||||
|
||||
using CString = char const*;
|
||||
|
||||
template<std::size_t sz>
|
||||
struct SignedType {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SignedType<8> {
|
||||
using type = int8_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SignedType<16> {
|
||||
using type = int16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SignedType<32> {
|
||||
using type = int32_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SignedType<64> {
|
||||
using type = int64_t;
|
||||
};
|
||||
|
||||
|
||||
template<std::size_t sz>
|
||||
struct UnsignedType {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct UnsignedType<8> {
|
||||
using type = uint8_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct UnsignedType<16> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct UnsignedType<32> {
|
||||
using type = uint32_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct UnsignedType<64> {
|
||||
using type = uint64_t;
|
||||
};
|
||||
|
||||
template<std::size_t bits>
|
||||
using Int = typename SignedType<bits>::type;
|
||||
|
||||
template<std::size_t bits>
|
||||
using Uint = typename UnsignedType<bits>::type;
|
||||
|
||||
template<typename T>
|
||||
using Signed = Int<sizeof(T) * 8>;
|
||||
|
||||
template<typename T>
|
||||
using Unsigned = Uint<sizeof(T) * 8>;
|
||||
|
||||
// ResizedInt retains the sign while granting a new size
|
||||
template<typename T, std::size_t bits, bool si = static_cast<T>(-1) < static_cast<T>(0)>
|
||||
struct ResizedInt {
|
||||
};
|
||||
|
||||
template<typename T, std::size_t bits>
|
||||
struct ResizedInt<T, bits, true> {
|
||||
using type = typename SignedType<bits>::type;
|
||||
};
|
||||
|
||||
template<typename T, std::size_t bits>
|
||||
struct ResizedInt<T, bits, false> {
|
||||
using type = typename UnsignedType<bits>::type;
|
||||
};
|
||||
|
||||
template<typename T, std::size_t bits>
|
||||
using ResizedInt_t = typename ResizedInt<T, bits>::type;
|
||||
|
||||
using ssize_t = Signed<std::size_t>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static_assert(sizeof(int8_t) == 1, "int8_t is wrong size");
|
||||
static_assert(sizeof(int16_t) == 2, "int16_t is wrong size");
|
||||
static_assert(sizeof(int32_t) == 4, "int32_t is wrong size");
|
||||
static_assert(sizeof(int64_t) == 8, "int64_t is wrong size");
|
||||
static_assert(sizeof(intptr_t) == sizeof(void*), "intptr_t is wrong size");
|
||||
|
||||
static_assert(sizeof(uint8_t) == 1, "uint8_t is wrong size");
|
||||
static_assert(sizeof(uint16_t) == 2, "uint16_t is wrong size");
|
||||
static_assert(sizeof(uint32_t) == 4, "uint32_t is wrong size");
|
||||
static_assert(sizeof(uint64_t) == 8, "uint64_t is wrong size");
|
||||
static_assert(sizeof(uintptr_t) == sizeof(void*), "uintptr_t is wrong size");
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 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 "types.hpp"
|
||||
|
||||
#if __has_include(<type_traits>)
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#else
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_union_v = __is_union(T);
|
||||
|
||||
inline constexpr bool is_constant_evaluated() noexcept {
|
||||
return __builtin_is_constant_evaluated();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv<const T> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv<volatile T> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv<const volatile T> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct remove_const {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_const<const T> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_const_t = typename remove_const<T>::type;
|
||||
|
||||
|
||||
template<class T, T v>
|
||||
struct integral_constant {
|
||||
|
||||
using value_type = T;
|
||||
using type = integral_constant;
|
||||
|
||||
static constexpr T value = v;
|
||||
|
||||
constexpr operator value_type() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr value_type operator()() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using false_type = integral_constant<bool, false>;
|
||||
using true_type = integral_constant<bool, true>;
|
||||
|
||||
|
||||
// is_const /////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_const_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_const_v<const T> = true;
|
||||
|
||||
|
||||
// is_integral /////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T> struct is_integral: false_type {};
|
||||
template<> struct is_integral<bool> : true_type {};
|
||||
template<> struct is_integral<wchar_t> : true_type {};
|
||||
template<> struct is_integral<int8_t> : true_type {};
|
||||
template<> struct is_integral<uint8_t> : true_type {};
|
||||
template<> struct is_integral<int16_t> : true_type {};
|
||||
template<> struct is_integral<uint16_t>: true_type {};
|
||||
template<> struct is_integral<int32_t> : true_type {};
|
||||
template<> struct is_integral<uint32_t>: true_type {};
|
||||
template<> struct is_integral<const bool> : true_type {};
|
||||
template<> struct is_integral<const wchar_t> : true_type {};
|
||||
template<> struct is_integral<const int8_t> : true_type {};
|
||||
template<> struct is_integral<const uint8_t> : true_type {};
|
||||
template<> struct is_integral<const int16_t> : true_type {};
|
||||
template<> struct is_integral<const uint16_t>: true_type {};
|
||||
template<> struct is_integral<const int32_t> : true_type {};
|
||||
template<> struct is_integral<const uint32_t>: true_type {};
|
||||
|
||||
// some of these need to be done with the actual language syntax because no one
|
||||
// can agree on what an (u)int64_t is...
|
||||
template<> struct is_integral<long>: true_type {};
|
||||
template<> struct is_integral<long long>: true_type {};
|
||||
template<> struct is_integral<unsigned long>: true_type {};
|
||||
template<> struct is_integral<unsigned long long>: true_type {};
|
||||
template<> struct is_integral<const long>: true_type {};
|
||||
template<> struct is_integral<const long long>: true_type {};
|
||||
template<> struct is_integral<const unsigned long>: true_type {};
|
||||
template<> struct is_integral<const unsigned long long>: true_type {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_integral_v = is_integral<T>::value;
|
||||
|
||||
|
||||
// is_integer /////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T> struct is_integer: false_type {};
|
||||
template<> struct is_integer<int8_t> : true_type {};
|
||||
template<> struct is_integer<uint8_t> : true_type {};
|
||||
template<> struct is_integer<int16_t> : true_type {};
|
||||
template<> struct is_integer<uint16_t>: true_type {};
|
||||
template<> struct is_integer<int32_t> : true_type {};
|
||||
template<> struct is_integer<uint32_t>: true_type {};
|
||||
template<> struct is_integer<int8_t const> : true_type {};
|
||||
template<> struct is_integer<uint8_t const> : true_type {};
|
||||
template<> struct is_integer<int16_t const> : true_type {};
|
||||
template<> struct is_integer<uint16_t const>: true_type {};
|
||||
template<> struct is_integer<int32_t const> : true_type {};
|
||||
template<> struct is_integer<uint32_t const>: true_type {};
|
||||
|
||||
// some of these need to be done with the actual language syntax because no one
|
||||
// can agree on what an (u)int64_t is...
|
||||
template<> struct is_integer<long>: true_type {};
|
||||
template<> struct is_integer<long long>: true_type {};
|
||||
template<> struct is_integer<unsigned long>: true_type {};
|
||||
template<> struct is_integer<unsigned long long>: true_type {};
|
||||
template<> struct is_integer<long const>: true_type {};
|
||||
template<> struct is_integer<long long const>: true_type {};
|
||||
template<> struct is_integer<unsigned long const>: true_type {};
|
||||
template<> struct is_integer<unsigned long long const>: true_type {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_integer_v = is_integer<T>::value;
|
||||
|
||||
template<typename T>
|
||||
concept Integer_c = is_integer<T>::value;
|
||||
|
||||
|
||||
template<typename T> struct is_char: false_type {};
|
||||
template<> struct is_char<char> : true_type {};
|
||||
template<typename T>
|
||||
constexpr bool is_char_v = is_char<T>::value;
|
||||
|
||||
template<typename T> struct is_bool: false_type {};
|
||||
template<> struct is_bool<bool> : true_type {};
|
||||
template<typename T>
|
||||
constexpr bool is_bool_v = is_bool<T>::value;
|
||||
|
||||
template<typename T> struct is_union: integral_constant<bool, std::is_union_v<T>> {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_union_v = is_union<T>();
|
||||
|
||||
// indicates the type can have members, but not that it necessarily does
|
||||
template<typename T>
|
||||
constexpr bool memberable(int T::*) { return true; }
|
||||
template<typename T>
|
||||
constexpr bool memberable(...) { return false; }
|
||||
|
||||
template<typename T>
|
||||
struct is_class: integral_constant<bool, !is_union_v<T> && memberable<T>(nullptr)> {};
|
||||
|
||||
namespace test {
|
||||
struct TestClass {int i;};
|
||||
union TestUnion {int i;};
|
||||
static_assert(is_class<TestClass>::value == true);
|
||||
static_assert(is_class<TestUnion>::value == false);
|
||||
static_assert(is_class<int>::value == false);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_class_v = is_class<T>();
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_copyable_v = std::is_trivially_copyable_v<T>;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_signed_v = integral_constant<bool, T(-1) < T(0)>::value;
|
||||
|
||||
template<typename T, std::size_t bits = sizeof(T) * 8>
|
||||
concept Signed_c = is_signed_v<T> && sizeof(T) == bits / 8;
|
||||
|
||||
template<typename T, std::size_t bits = sizeof(T) * 8>
|
||||
concept Unsigned_c = !is_signed_v<T> && sizeof(T) == bits / 8;
|
||||
|
||||
template<typename T, typename U>
|
||||
struct is_same: false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_same<T, T>: true_type {};
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr auto is_same_v = is_same<T, U>::value;
|
||||
|
||||
template<typename T>
|
||||
struct is_pointer {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_pointer<T*> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_pointer<const T*> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_pointer_v = is_pointer<T>::value;
|
||||
|
||||
template<typename T>
|
||||
struct remove_pointer {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_pointer<T*> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_pointer<T* const> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_pointer<T* volatile> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_pointer<T* const volatile> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_pointer_t = typename remove_pointer<T>::type;
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct remove_reference {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_reference<T&> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_reference<T&&> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_reference_t = typename remove_reference<T>::type;
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
T &&declval();
|
||||
|
||||
template<typename T, typename = decltype(T(declval<T>()))>
|
||||
constexpr bool is_move_constructible(int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_move_constructible(bool) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr bool is_move_constructible_v = detail::is_move_constructible<T>(0);
|
||||
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
T const &declvalCopy();
|
||||
|
||||
template<typename T, typename = decltype(T(declvalCopy<T>()))>
|
||||
constexpr bool is_copy_constructible(int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_copy_constructible(bool) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr bool is_copy_constructible_v = detail::is_copy_constructible<T>(0);
|
||||
|
||||
|
||||
// is String?
|
||||
|
||||
template<std::size_t SmallStringSize>
|
||||
class BasicString;
|
||||
template<std::size_t sz>
|
||||
class IString;
|
||||
class CStringView;
|
||||
class StringLiteral;
|
||||
class StringView;
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr auto isOxString(const auto*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr auto isOxString(const BasicString<sz>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr auto isOxString(const IString<sz>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr auto isOxString(const CStringView*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr auto isOxString(const StringLiteral*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr auto isOxString(const StringView*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto isOxString_v = detail::isOxString(static_cast<T*>(nullptr));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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 "types.hpp"
|
||||
|
||||
namespace ox::units {
|
||||
|
||||
constexpr uint64_t KB = 1024;
|
||||
constexpr uint64_t MB = 1024 * KB;
|
||||
constexpr uint64_t GB = 1024 * MB;
|
||||
constexpr uint64_t TB = 1024 * GB;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<utility>)
|
||||
#include <utility>
|
||||
#include "typetraits.hpp"
|
||||
#else
|
||||
#include "typetraits.hpp"
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
constexpr ox::remove_reference_t<T> &&move(T &&t) noexcept {
|
||||
return static_cast<ox::remove_reference_t<T>&&>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void swap(T &a, T &b) noexcept {
|
||||
auto temp = std::move(a);
|
||||
a = std::move(b);
|
||||
b = std::move(temp);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_equal(T const t, U const u) noexcept {
|
||||
if constexpr(ox::is_signed_v<T> == ox::is_signed_v<U>) {
|
||||
return t == u;
|
||||
} else if constexpr(ox::is_signed_v<T>) {
|
||||
return t >= 0 && static_cast<ox::Unsigned<T>>(t) == u;
|
||||
} else {
|
||||
return u >= 0 && t == static_cast<ox::Unsigned<U>>(u);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_less(T const t, U const u) noexcept {
|
||||
if constexpr(ox::is_signed_v<T> == ox::is_signed_v<U>) {
|
||||
return t < u;
|
||||
} else if constexpr(ox::is_signed_v<T>) {
|
||||
return t < 0 || static_cast<ox::Unsigned<T>>(t) < u;
|
||||
} else {
|
||||
return u >= 0 && t < static_cast<ox::Unsigned<U>>(u);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_not_equal(T const t, U const u) noexcept {
|
||||
return !std::cmp_equal(t, u);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_greater(T const t, U const u) noexcept {
|
||||
return std::cmp_less(u, t);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_less_equal(T const t, U const u) noexcept {
|
||||
return !std::cmp_less(u, t);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool cmp_greater_equal(T const t, U const u) noexcept {
|
||||
return !std::cmp_less(t, u);
|
||||
}
|
||||
|
||||
static_assert(cmp_less(-1, 5u));
|
||||
static_assert(!cmp_less(5u, -1));
|
||||
static_assert(cmp_equal(5u, 5));
|
||||
static_assert(cmp_equal(5, 5u));
|
||||
static_assert(!cmp_equal(-5, 5u));
|
||||
static_assert(!cmp_equal(4u, 5u));
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ox {
|
||||
|
||||
struct in_place_t {
|
||||
explicit constexpr in_place_t() = default;
|
||||
};
|
||||
|
||||
inline constexpr ox::in_place_t in_place;
|
||||
|
||||
template<class T>
|
||||
constexpr T &&forward(remove_reference_t<T> &t) noexcept {
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "def.hpp"
|
||||
#include "ignore.hpp"
|
||||
#include "istring.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "hash.hpp"
|
||||
#include "random.hpp"
|
||||
#include "ranges.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "strops.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
using UUIDStr = ox::IString<36>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto isHexChar(char c) noexcept {
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
constexpr ox::Result<uint8_t> fromHex(ox::StringViewCR v) noexcept {
|
||||
constexpr auto valMap = [] {
|
||||
ox::Array<uint8_t, 128> out;
|
||||
out['A'] = out['a'] = 10;
|
||||
out['B'] = out['b'] = 11;
|
||||
out['C'] = out['c'] = 12;
|
||||
out['D'] = out['d'] = 13;
|
||||
out['E'] = out['e'] = 14;
|
||||
out['F'] = out['f'] = 15;
|
||||
out['0'] = 0;
|
||||
out['1'] = 1;
|
||||
out['2'] = 2;
|
||||
out['3'] = 3;
|
||||
out['4'] = 4;
|
||||
out['5'] = 5;
|
||||
out['6'] = 6;
|
||||
out['7'] = 7;
|
||||
out['8'] = 8;
|
||||
out['9'] = 9;
|
||||
return out;
|
||||
}();
|
||||
if (!detail::isHexChar(v[0]) || !detail::isHexChar(v[1])) {
|
||||
return ox::Error(1, "Invalid UUID");
|
||||
}
|
||||
if (v.size() != 2) {
|
||||
return ox::Error(2);
|
||||
}
|
||||
uint8_t out = 0;
|
||||
out += static_cast<uint8_t>(valMap[static_cast<unsigned char>(v[0])] * 16);
|
||||
out += valMap[static_cast<unsigned char>(v[1])];
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr ox::IString<2> toHex(uint8_t v) noexcept {
|
||||
constexpr ox::Array<char, 16> valMap {
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
};
|
||||
ox::IString<2> out;
|
||||
std::ignore = out.resize(2);
|
||||
out[0] = valMap[static_cast<unsigned>((v & 0xf0) / 16)];
|
||||
out[1] = valMap[static_cast<unsigned>(v & 0x0f)];
|
||||
out[2] = 0;
|
||||
return out.data();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UUID {
|
||||
template<typename T>
|
||||
friend constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept;
|
||||
protected:
|
||||
static bool s_seeded;
|
||||
static Random s_rand;
|
||||
ox::Array<uint8_t, 16> m_value{};
|
||||
|
||||
public:
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.UUID";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
|
||||
static void seedGenerator(const RandomSeed &seed) noexcept;
|
||||
|
||||
static ox::Result<UUID> generate() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Array<uint8_t, 16> const&value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool isNull() const noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
if (ox::all_of(m_value.begin(), m_value.end(), [](auto v) { return v == 0; })) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
constexpr uint64_t zero = 0;
|
||||
return memcmp(&zero, m_value.data() + 0, 8) == 0
|
||||
&& memcmp(&zero, m_value.data() + 8, 8) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ox::Result<ox::UUID> fromString(ox::StringViewCR s) noexcept {
|
||||
if (s.size() < 36) {
|
||||
return ox::Error(1, "Insufficient data to contain a complete UUID");
|
||||
}
|
||||
UUID out;
|
||||
auto valueI = 0u;
|
||||
for (size_t i = 0; i < s.size();) {
|
||||
if (s[i] == '-') {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const auto seg = substr(s, i, i + 2);
|
||||
if (seg.size() != 2) {
|
||||
return ox::Error(1, "Invalid UUID");
|
||||
}
|
||||
OX_REQUIRE(val, detail::fromHex(seg));
|
||||
out.m_value[valueI] = val;
|
||||
i += 2;
|
||||
++valueI;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr bool operator==(UUID const&o) const noexcept {
|
||||
return m_value == o.m_value;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(UUID const&o) const noexcept {
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Error toString(Writer_c auto &writer) const noexcept {
|
||||
auto valueI = 0u;
|
||||
constexpr auto printChars = [](
|
||||
Writer_c auto &writer,
|
||||
const Array<uint8_t, 16> &value,
|
||||
std::size_t cnt,
|
||||
unsigned &valueI) {
|
||||
for (auto i = 0u; i < cnt; ++i) {
|
||||
const auto v = value[valueI];
|
||||
const auto h = detail::toHex(v);
|
||||
std::ignore = writer.write(h.c_str(), h.size());
|
||||
++valueI;
|
||||
}
|
||||
};
|
||||
printChars(writer, m_value, 4, valueI);
|
||||
OX_RETURN_ERROR(writer.put('-'));
|
||||
printChars(writer, m_value, 2, valueI);
|
||||
OX_RETURN_ERROR(writer.put('-'));
|
||||
printChars(writer, m_value, 2, valueI);
|
||||
OX_RETURN_ERROR(writer.put('-'));
|
||||
printChars(writer, m_value, 2, valueI);
|
||||
OX_RETURN_ERROR(writer.put('-'));
|
||||
printChars(writer, m_value, 6, valueI);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr UUIDStr toString() const noexcept {
|
||||
UUIDStr out;
|
||||
std::ignore = out.resize(UUIDStr::cap());
|
||||
ox::CharBuffWriter bw{{out.data(), UUIDStr::cap()}};
|
||||
std::ignore = toString(bw);
|
||||
out[UUIDStr::cap()] = 0;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct hash<ox::UUID> {
|
||||
[[nodiscard]]
|
||||
constexpr size_t operator()(ox::UUID v) const noexcept {
|
||||
size_t out{};
|
||||
if (std::is_constant_evaluated()) {
|
||||
for (auto i = 0u; i < sizeof(out); ++i) {
|
||||
out |= static_cast<size_t>(v.value()[i]) << (i * 8);
|
||||
}
|
||||
} else {
|
||||
memcpy(&out, &v, sizeof(out));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<UUID>());
|
||||
OX_RETURN_ERROR(io->field("value", &obj->m_value));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include(<imgui.h>)
|
||||
#include <imgui.h>
|
||||
#endif
|
||||
|
||||
#include <ox/std/concepts.hpp>
|
||||
#include <ox/std/def.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
class Vec2 {
|
||||
public:
|
||||
using value_type = float;
|
||||
using size_type = std::size_t;
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.Point";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
constexpr Vec2() noexcept = default;
|
||||
|
||||
explicit constexpr Vec2(class Point const&pt) noexcept;
|
||||
|
||||
explicit constexpr Vec2(class Size const&pt) noexcept;
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Vec2(float pX, float pY) noexcept: x(pX), y(pY) {
|
||||
}
|
||||
|
||||
#if __has_include(<imgui.h>)
|
||||
explicit constexpr Vec2(const ImVec2 &v) noexcept: Vec2(v.x, v.y) {
|
||||
}
|
||||
|
||||
explicit inline operator ImVec2() const noexcept {
|
||||
return {x, y};
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr auto operator==(const Vec2 &v) const noexcept {
|
||||
return x == v.x && y == v.y;
|
||||
}
|
||||
|
||||
constexpr auto operator!=(const Vec2 &v) const noexcept {
|
||||
return !operator==(v);
|
||||
}
|
||||
|
||||
constexpr Vec2 operator+(float i) const noexcept {
|
||||
return {x + i, y + i};
|
||||
}
|
||||
|
||||
constexpr Vec2 operator+=(float i) noexcept {
|
||||
x += i;
|
||||
y += i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec2 operator-(float i) const noexcept {
|
||||
return {x - i, y - i};
|
||||
}
|
||||
|
||||
constexpr Vec2 operator-=(float i) noexcept {
|
||||
x -= i;
|
||||
y -= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec2 operator*(float i) const noexcept {
|
||||
return {x * i, y * i};
|
||||
}
|
||||
|
||||
constexpr Vec2 operator*=(float i) noexcept {
|
||||
x *= i;
|
||||
y *= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec2 operator/(float i) const noexcept {
|
||||
return {x / i, y / i};
|
||||
}
|
||||
|
||||
constexpr Vec2 operator/=(float i) noexcept {
|
||||
x /= i;
|
||||
y /= i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, ox::CommonPtrWith<Vec2> auto *obj) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Vec2>());
|
||||
OX_RETURN_ERROR(io->field("x", &obj->x));
|
||||
OX_RETURN_ERROR(io->field("y", &obj->y));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,757 @@
|
||||
/*
|
||||
* 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 "bit.hpp"
|
||||
#include "error.hpp"
|
||||
#include "initializerlist.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "math.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "stringview.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, typename Allocator, size_t Size = 1>
|
||||
struct VectorAllocator {
|
||||
static_assert(sizeof(AllocAlias<T>) == sizeof(T));
|
||||
static_assert(alignof(AllocAlias<T>) == alignof(T));
|
||||
private:
|
||||
ox::Array<AllocAlias<T>, Size> m_data = {};
|
||||
protected:
|
||||
constexpr VectorAllocator() noexcept = default;
|
||||
constexpr VectorAllocator(VectorAllocator const&) noexcept = default;
|
||||
constexpr VectorAllocator(VectorAllocator&&) noexcept = default;
|
||||
|
||||
constexpr void allocate(T **items, size_t const cap) noexcept {
|
||||
// small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr
|
||||
if (std::is_constant_evaluated() || cap > Size) {
|
||||
*items = Allocator{}.allocate(cap);
|
||||
} else {
|
||||
*items = reinterpret_cast<T*>(m_data.data());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void moveConstructItemsFrom(
|
||||
T **items,
|
||||
VectorAllocator *src,
|
||||
size_t const count,
|
||||
size_t const cap) noexcept {
|
||||
// this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM,
|
||||
// try removing it later
|
||||
if (!std::is_constant_evaluated()) {
|
||||
if (cap <= m_data.size() && count <= m_data.size()) {
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
auto const srcItem = std::launder(reinterpret_cast<T*>(&src->m_data[i]));
|
||||
new (&m_data[i]) T(std::move(*srcItem));
|
||||
}
|
||||
if (count) {
|
||||
*items = std::launder(reinterpret_cast<T*>(m_data.data()));
|
||||
} else {
|
||||
*items = reinterpret_cast<T*>(m_data.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void deallocate(T *const items, size_t const cap) noexcept {
|
||||
// small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr
|
||||
if (std::is_constant_evaluated()) {
|
||||
if (items) {
|
||||
Allocator{}.deallocate(items, cap);
|
||||
}
|
||||
} else {
|
||||
if (items && static_cast<void*>(items) != static_cast<void*>(m_data.data())) {
|
||||
Allocator{}.deallocate(items, cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
struct VectorAllocator<T, Allocator, 0> {
|
||||
protected:
|
||||
constexpr VectorAllocator() noexcept = default;
|
||||
constexpr VectorAllocator(VectorAllocator const&) noexcept = default;
|
||||
constexpr VectorAllocator(VectorAllocator&&) noexcept = default;
|
||||
|
||||
constexpr void allocate(T **items, size_t const cap) noexcept {
|
||||
*items = Allocator{}.allocate(cap);
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
constexpr void moveConstructItemsFrom(
|
||||
T**,
|
||||
VectorAllocator*,
|
||||
size_t const,
|
||||
size_t const) noexcept {
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
constexpr void moveItemsFrom(T**, VectorAllocator*, size_t const, size_t const) noexcept {
|
||||
}
|
||||
|
||||
constexpr void deallocate(T *const items, size_t const cap) noexcept {
|
||||
if (items) {
|
||||
Allocator{}.deallocate(items, cap);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize = 0, typename Allocator = std::allocator<T>>
|
||||
class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
|
||||
template<typename RefType = T&, typename PtrType = T*, bool reverse = false>
|
||||
using iterator = SpanIterator<T, RefType, PtrType, reverse>;
|
||||
|
||||
private:
|
||||
static constexpr auto initialCap = SmallVectorSize > 0 ? SmallVectorSize : 50;
|
||||
static constexpr auto useNoexcept = ox::is_integral_v<T> || ox::is_pointer_v<T>;
|
||||
size_t m_size = 0;
|
||||
size_t m_cap = 0;
|
||||
T *m_items = nullptr;
|
||||
|
||||
public:
|
||||
constexpr Vector() noexcept = default;
|
||||
|
||||
explicit constexpr Vector(size_t size) noexcept;
|
||||
|
||||
constexpr Vector(std::initializer_list<T> list) noexcept;
|
||||
|
||||
constexpr Vector(Vector const &other) noexcept(useNoexcept);
|
||||
|
||||
constexpr Vector(Vector &&other) noexcept;
|
||||
|
||||
constexpr ~Vector();
|
||||
|
||||
constexpr iterator<> begin() noexcept {
|
||||
return iterator<>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
constexpr iterator<> end() noexcept {
|
||||
return iterator<>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*> begin() const noexcept {
|
||||
return iterator<T const&, T const*>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*> end() const noexcept {
|
||||
return iterator<T const&, T const*>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*> cbegin() const noexcept {
|
||||
return iterator<T const&, T const*>(m_items, 0, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*> cend() const noexcept {
|
||||
return iterator<T const&, T const*>(m_items, m_size, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T&, T*, true> rbegin() noexcept {
|
||||
return iterator<T&, T*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T&, T*, true> rend() noexcept {
|
||||
return iterator<T&, T*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*, true> crbegin() const noexcept {
|
||||
return iterator<T const&, T const*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*, true> crend() const noexcept {
|
||||
return iterator<T const&, T const*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*, true> rbegin() const noexcept {
|
||||
return iterator<T const&, T const*, true>(m_items, m_size - 1, m_size);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr iterator<T const&, T const*, true> rend() const noexcept {
|
||||
return iterator<T const&, T const*, true>(m_items, MaxValue<size_type>, m_size);
|
||||
}
|
||||
|
||||
constexpr bool operator==(Vector const &other) const noexcept(useNoexcept);
|
||||
|
||||
constexpr Vector &operator=(Vector const &other) noexcept(useNoexcept);
|
||||
|
||||
constexpr Vector &operator=(Vector &&other) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &operator[](size_t i) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const &operator[](size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T*> at(size_t i) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T const*> at(size_t i) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T*> front() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T const*> front() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T*> back() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Result<T const*> back() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t capacity() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t size() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool empty() const noexcept;
|
||||
|
||||
constexpr void clear() noexcept(useNoexcept);
|
||||
|
||||
constexpr void resize(size_t size) noexcept(useNoexcept);
|
||||
|
||||
constexpr void reserveResize(size_t size) noexcept(useNoexcept);
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T *data() noexcept {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const *data() const noexcept {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool contains(MaybeView_t<T> const&) const noexcept;
|
||||
|
||||
constexpr iterator<T&, T*, false> insert(
|
||||
size_t pos, size_t cnt, T const &val) noexcept(useNoexcept);
|
||||
|
||||
constexpr iterator<T&, T*, false> insert(size_t pos, T val) noexcept(useNoexcept);
|
||||
|
||||
template<typename... Args>
|
||||
constexpr iterator<T&, T*, false> emplace(size_t pos, Args&&... args) noexcept(useNoexcept);
|
||||
|
||||
template<typename... Args>
|
||||
constexpr T &emplace_back(Args&&... args) noexcept(useNoexcept);
|
||||
|
||||
constexpr void push_back(T const &item) noexcept(useNoexcept);
|
||||
|
||||
constexpr void push_back(T &&item) noexcept(useNoexcept);
|
||||
|
||||
constexpr void pop_back() noexcept(useNoexcept);
|
||||
|
||||
/**
|
||||
* Removes an item from the Vector.
|
||||
* @param pos iterator at the point to remove
|
||||
* @return Error if index is out of bounds
|
||||
*/
|
||||
constexpr Result<iterator<T&, T*, false>> erase(iterator<> const &pos) noexcept(useNoexcept);
|
||||
|
||||
/**
|
||||
* Removes an item from the Vector.
|
||||
* @param pos position of item to remove
|
||||
* @return Error if index is out of bounds
|
||||
*/
|
||||
constexpr Result<iterator<T&, T*, false>> erase(size_t pos) noexcept(useNoexcept);
|
||||
|
||||
/**
|
||||
* Moves the last item in the Vector to position pos and decrements the
|
||||
* size by 1.
|
||||
* @param pos position of item to remove
|
||||
* @return Error if index is out of bounds
|
||||
*/
|
||||
constexpr Error unordered_erase(size_t pos) noexcept(useNoexcept);
|
||||
|
||||
constexpr Error remove(MaybeView_t<T> const &val);
|
||||
|
||||
constexpr void reserve(size_t cap) noexcept(useNoexcept);
|
||||
|
||||
constexpr void shrink_to_fit() noexcept(useNoexcept);
|
||||
|
||||
private:
|
||||
constexpr void reserveInsert(
|
||||
size_t cap, size_t pos, size_t offset = 1) noexcept(useNoexcept);
|
||||
|
||||
};
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator, typename RefType, bool reverse>
|
||||
using VectorIt = typename Vector<T, SmallVectorSize, Allocator>::template iterator<RefType, reverse>;
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator, typename RefType, bool reverse>
|
||||
constexpr VectorIt<T, SmallVectorSize, Allocator, RefType, reverse> operator+(
|
||||
size_t n,
|
||||
VectorIt<T, SmallVectorSize, Allocator, RefType, reverse> const &a) {
|
||||
return a + n;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(size_t const size) noexcept {
|
||||
m_size = size;
|
||||
m_cap = m_size;
|
||||
this->allocate(&m_items, m_cap);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
std::construct_at(&m_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept {
|
||||
m_size = list.size();
|
||||
m_cap = m_size;
|
||||
this->allocate(&m_items, m_cap);
|
||||
for (size_t i{}; auto &item : list) {
|
||||
std::construct_at(&m_items[i], std::move(item));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(Vector const &other) noexcept(useNoexcept) {
|
||||
m_size = other.m_size;
|
||||
m_cap = other.m_cap;
|
||||
this->allocate(&m_items, other.m_cap);
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
std::construct_at(&m_items[i], other.m_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(Vector &&other) noexcept {
|
||||
m_size = other.m_size;
|
||||
m_cap = other.m_cap;
|
||||
m_items = other.m_items;
|
||||
this->moveConstructItemsFrom(&m_items, &other, m_size, m_cap);
|
||||
other.m_size = 0;
|
||||
other.m_cap = 0;
|
||||
other.m_items = nullptr;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::~Vector() {
|
||||
clear();
|
||||
this->deallocate(m_items, m_cap);
|
||||
m_items = nullptr;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr bool Vector<T, SmallVectorSize, Allocator>::operator==(
|
||||
Vector const &other) const noexcept(useNoexcept) {
|
||||
if (m_size != other.m_size) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < m_size; i++) {
|
||||
if (!(m_items[i] == other.m_items[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator> &Vector<T, SmallVectorSize, Allocator>::operator=(
|
||||
Vector const &other) noexcept(useNoexcept) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
this->deallocate(m_items, m_cap);
|
||||
m_items = nullptr;
|
||||
m_size = other.m_size;
|
||||
m_cap = other.m_cap;
|
||||
this->allocate(&m_items, other.m_cap);
|
||||
for (size_t i = 0; i < m_size; i++) {
|
||||
std::construct_at(&m_items[i], other.m_items[i]);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator> &Vector<T, SmallVectorSize, Allocator>::operator=(
|
||||
Vector &&other) noexcept {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
this->deallocate(m_items, m_cap);
|
||||
m_size = other.m_size;
|
||||
m_cap = other.m_cap;
|
||||
m_items = other.m_items;
|
||||
this->moveConstructItemsFrom(&m_items, &other, m_size, m_cap);
|
||||
other.m_size = 0;
|
||||
other.m_cap = 0;
|
||||
other.m_items = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr T &Vector<T, SmallVectorSize, Allocator>::operator[](size_t const i) noexcept {
|
||||
boundsCheck(i, size(), "Vector access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr T const &Vector<T, SmallVectorSize, Allocator>::operator[](size_t const i) const noexcept {
|
||||
boundsCheck(i, size(), "Vector access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T*> Vector<T, SmallVectorSize, Allocator>::at(size_t const i) noexcept {
|
||||
if (i < size()) [[likely]] {
|
||||
return &operator[](i);
|
||||
}
|
||||
return ox::Error(1, "Vector: Invalid index");
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T const*> Vector<T, SmallVectorSize, Allocator>::at(size_t const i) const noexcept {
|
||||
if (i < size()) [[likely]] {
|
||||
return &operator[](i);
|
||||
}
|
||||
return ox::Error(1, "Vector: Invalid index");
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T*> Vector<T, SmallVectorSize, Allocator>::front() noexcept {
|
||||
if (!m_size) {
|
||||
return {nullptr, ox::Error(1)};
|
||||
}
|
||||
return &m_items[0];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T const*> Vector<T, SmallVectorSize, Allocator>::front() const noexcept {
|
||||
if (!m_size) {
|
||||
return {nullptr, ox::Error(1)};
|
||||
}
|
||||
return &m_items[0];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T*> Vector<T, SmallVectorSize, Allocator>::back() noexcept {
|
||||
if (!m_size) {
|
||||
return {nullptr, ox::Error(1)};
|
||||
}
|
||||
return &m_items[m_size - 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<T const*> Vector<T, SmallVectorSize, Allocator>::back() const noexcept {
|
||||
if (!m_size) {
|
||||
return {nullptr, ox::Error(1)};
|
||||
}
|
||||
return &m_items[m_size - 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr size_t Vector<T, SmallVectorSize, Allocator>::capacity() const noexcept {
|
||||
return m_cap;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr size_t Vector<T, SmallVectorSize, Allocator>::size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr bool Vector<T, SmallVectorSize, Allocator>::empty() const noexcept {
|
||||
return !m_size;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::clear() noexcept(useNoexcept) {
|
||||
if constexpr(is_class<T>()) {
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
m_items[i].~T();
|
||||
}
|
||||
}
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::resize(size_t const size) noexcept(useNoexcept) {
|
||||
if (m_cap < size) {
|
||||
reserve(size * 2);
|
||||
}
|
||||
if (m_size < size) {
|
||||
for (size_t i = m_size; i < size; i++) {
|
||||
std::construct_at(&m_items[i]);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = size; i < m_size; i++) {
|
||||
m_items[i].~T();
|
||||
}
|
||||
}
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::reserveResize(size_t const size) noexcept(useNoexcept) {
|
||||
reserve(size);
|
||||
resize(size);
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr bool Vector<T, SmallVectorSize, Allocator>::contains(MaybeView_t<T> const &v) const noexcept {
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
if (m_items[i] == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr typename Vector<T, SmallVectorSize, Allocator>::template iterator<T&, T*, false>
|
||||
Vector<T, SmallVectorSize, Allocator>::insert(
|
||||
size_t const pos, size_t const cnt, T const &val) noexcept(useNoexcept) {
|
||||
if (m_size + cnt > m_cap) {
|
||||
reserveInsert(m_cap ? m_size + cnt : initialCap, pos, cnt);
|
||||
}
|
||||
if (pos < m_size) {
|
||||
for (auto i = m_size + cnt - 1; i > pos; --i) {
|
||||
std::construct_at(&m_items[i], std::move(m_items[i - cnt]));
|
||||
}
|
||||
for (auto i = pos; i < pos + cnt; ++i) {
|
||||
m_items[i] = val;
|
||||
}
|
||||
} else {
|
||||
for (auto i = 0u; i < cnt; ++i) {
|
||||
std::construct_at(&m_items[pos + i], m_items[pos]);
|
||||
}
|
||||
}
|
||||
m_size += cnt;
|
||||
return begin() + pos;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr typename Vector<T, SmallVectorSize, Allocator>::template iterator<T&, T*, false>
|
||||
Vector<T, SmallVectorSize, Allocator>::insert(size_t pos, T val) noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
reserveInsert(m_cap ? m_cap * 2 : initialCap, pos);
|
||||
}
|
||||
if (pos < m_size) {
|
||||
for (auto i = m_size; i > pos; --i) {
|
||||
std::construct_at(&m_items[i], std::move(m_items[i - 1]));
|
||||
}
|
||||
m_items[pos] = std::move(val);
|
||||
} else {
|
||||
std::construct_at(&m_items[pos], std::move(val));
|
||||
}
|
||||
++m_size;
|
||||
return begin() + pos;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
template<typename... Args>
|
||||
constexpr typename Vector<T, SmallVectorSize, Allocator>::template iterator<T&, T*, false>
|
||||
Vector<T, SmallVectorSize, Allocator>::emplace(size_t pos, Args&&... args) noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
reserveInsert(m_cap ? m_cap * 2 : initialCap, pos);
|
||||
if (pos < m_size) {
|
||||
m_items[pos].~T();
|
||||
}
|
||||
std::construct_at(&m_items[pos], ox::forward<Args>(args)...);
|
||||
} else {
|
||||
if (pos < m_size) {
|
||||
for (auto i = m_size; i > pos; --i) {
|
||||
std::construct_at(&m_items[i], std::move(m_items[i - 1]));
|
||||
}
|
||||
m_items[pos].~T();
|
||||
}
|
||||
std::construct_at(&m_items[pos], ox::forward<Args>(args)...);
|
||||
}
|
||||
++m_size;
|
||||
return begin() + pos;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
template<typename... Args>
|
||||
constexpr T &Vector<T, SmallVectorSize, Allocator>::emplace_back(Args&&... args) noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
reserve(m_cap ? m_cap * 2 : initialCap);
|
||||
}
|
||||
auto out = std::construct_at(&m_items[m_size], ox::forward<Args>(args)...);
|
||||
++m_size;
|
||||
return *out;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::push_back(T const &item) noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
reserve(m_cap ? m_cap * 2 : initialCap);
|
||||
}
|
||||
std::construct_at(&m_items[m_size], item);
|
||||
++m_size;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::push_back(T &&item) noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
reserve(m_cap ? m_cap * 2 : initialCap);
|
||||
}
|
||||
std::construct_at(&m_items[m_size], std::move(item));
|
||||
++m_size;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::pop_back() noexcept(useNoexcept) {
|
||||
--m_size;
|
||||
m_items[m_size].~T();
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<typename Vector<T, SmallVectorSize, Allocator>::template iterator<T&, T*, false>>
|
||||
Vector<T, SmallVectorSize, Allocator>::erase(iterator<> const &pos) noexcept(useNoexcept) {
|
||||
return erase(pos.offset());
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Result<typename Vector<T, SmallVectorSize, Allocator>::template iterator<T&, T*, false>>
|
||||
Vector<T, SmallVectorSize, Allocator>::erase(size_t pos) noexcept(useNoexcept) {
|
||||
if (pos >= m_size) {
|
||||
return ox::Error(1, "Vector::erase failed: pos is greater than Vector size");
|
||||
}
|
||||
--m_size;
|
||||
for (auto i = pos; i < m_size; ++i) {
|
||||
m_items[i] = std::move(m_items[i + 1]);
|
||||
}
|
||||
m_items[m_size].~T();
|
||||
return begin() + pos;
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr Error Vector<T, SmallVectorSize, Allocator>::unordered_erase(size_t pos)
|
||||
noexcept(useNoexcept) {
|
||||
if (pos >= m_size) {
|
||||
return ox::Error(1);
|
||||
}
|
||||
--m_size;
|
||||
m_items[pos] = std::move(m_items[m_size]);
|
||||
m_items[m_size].~T();
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr ox::Error Vector<T, SmallVectorSize, Allocator>::remove(MaybeView_t<T> const &val) {
|
||||
for (size_t i{}; auto const &v : *this) {
|
||||
if (v == val) {
|
||||
return erase(i).error;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return ox::Error{1, "element not found"};
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::reserve(size_t cap) noexcept(useNoexcept) {
|
||||
if (cap <= m_cap) {
|
||||
return;
|
||||
}
|
||||
auto const oldItems = m_items;
|
||||
auto const oldCap = m_cap;
|
||||
m_cap = cap;
|
||||
this->allocate(&m_items, cap);
|
||||
if (oldItems) { // move over old items
|
||||
auto const itRange = ox::min(cap, m_size);
|
||||
for (size_t i = 0; i < itRange; ++i) {
|
||||
std::construct_at(&m_items[i], std::move(oldItems[i]));
|
||||
oldItems[i].~T();
|
||||
}
|
||||
this->deallocate(oldItems, oldCap);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::shrink_to_fit() noexcept(useNoexcept) {
|
||||
if (m_size == m_cap) {
|
||||
return;
|
||||
}
|
||||
auto const oldItems = m_items;
|
||||
auto const oldCap = m_cap;
|
||||
m_cap = m_size;
|
||||
this->allocate(&m_items, m_size);
|
||||
if (oldItems) { // move over old items
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
std::construct_at(&m_items[i], std::move(oldItems[i]));
|
||||
oldItems[i].~T();
|
||||
}
|
||||
this->deallocate(oldItems, oldCap);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t SmallVectorSize, typename Allocator>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert(
|
||||
size_t cap,
|
||||
size_t pos,
|
||||
size_t offset) noexcept(useNoexcept) {
|
||||
if (cap <= m_cap) {
|
||||
return;
|
||||
}
|
||||
auto const oldItems = m_items;
|
||||
auto const oldCap = m_cap;
|
||||
m_cap = cap;
|
||||
this->allocate(&m_items, cap);
|
||||
if (oldItems) { // move over old items
|
||||
auto itRange = ox::min(m_size, pos);
|
||||
for (size_t i = 0; i < itRange; ++i) {
|
||||
std::construct_at(&m_items[i], std::move(oldItems[i]));
|
||||
oldItems[i].~T();
|
||||
}
|
||||
itRange = m_size;
|
||||
for (size_t i = pos; i < itRange; ++i) {
|
||||
std::construct_at(&m_items[i + offset], std::move(oldItems[i]));
|
||||
oldItems[i].~T();
|
||||
}
|
||||
this->deallocate(oldItems, oldCap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PlatSpec, typename T>
|
||||
[[nodiscard]]
|
||||
constexpr auto alignOf(Vector<T> const&) noexcept {
|
||||
typename PlatSpec::size_t const i = 0;
|
||||
return PlatSpec::alignOf(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "concepts.hpp"
|
||||
#include "error.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace ios_base {
|
||||
enum seekdir {
|
||||
beg,
|
||||
end,
|
||||
cur,
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept Writer_c = requires(T v) {
|
||||
{v.put(static_cast<char>(0))} -> ox::same_as<ox::Error>;
|
||||
{v.write(static_cast<const char*>(""), static_cast<std::size_t>(0))} -> ox::same_as<ox::Error>;
|
||||
{v.seekp(static_cast<std::size_t>(0))} -> ox::same_as<ox::Error>;
|
||||
{v.seekp(static_cast<std::size_t>(0), ios_base::beg)} -> ox::same_as<ox::Error>;
|
||||
{v.tellp()} -> ox::same_as<std::size_t>;
|
||||
};
|
||||
|
||||
class Writer_v {
|
||||
public:
|
||||
virtual constexpr ~Writer_v() noexcept = default;
|
||||
virtual constexpr auto put(char) noexcept -> ox::Error = 0;
|
||||
virtual constexpr auto write(const char*, std::size_t) noexcept -> ox::Error = 0;
|
||||
virtual constexpr auto seekp(std::size_t) noexcept -> ox::Error = 0;
|
||||
virtual constexpr auto seekp(int, ios_base::seekdir) -> ox::Error = 0;
|
||||
virtual constexpr auto tellp() noexcept -> std::size_t = 0;
|
||||
};
|
||||
|
||||
template<Writer_c T>
|
||||
class WriterT: public Writer_v {
|
||||
private:
|
||||
T m_writer{};
|
||||
public:
|
||||
template<typename ...Args>
|
||||
constexpr explicit WriterT(Args&&... args) noexcept: m_writer(args...) {
|
||||
}
|
||||
constexpr auto put(char v) noexcept -> ox::Error override {
|
||||
return m_writer.put(v);
|
||||
}
|
||||
constexpr auto write(const char *v, std::size_t cnt) noexcept -> ox::Error override {
|
||||
return m_writer.write(v, cnt);
|
||||
}
|
||||
constexpr auto seekp(std::size_t p) noexcept -> ox::Error override {
|
||||
return m_writer.seekp(p);
|
||||
}
|
||||
constexpr auto seekp(int p, ios_base::seekdir sd) noexcept -> ox::Error override {
|
||||
return m_writer.seekp(p, sd);
|
||||
}
|
||||
constexpr auto tellp() noexcept -> std::size_t override {
|
||||
return m_writer.tellp();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates the specified amount of data at the end of the current write stream.
|
||||
* @param writer
|
||||
* @param sz
|
||||
* @return
|
||||
*/
|
||||
constexpr ox::Result<std::size_t> allocate(Writer_c auto &writer, std::size_t sz) noexcept {
|
||||
const auto p = writer.tellp();
|
||||
OX_RETURN_ERROR(writer.seekp(0, ios_base::end));
|
||||
const auto out = writer.tellp();
|
||||
OX_RETURN_ERROR(writer.write(nullptr, sz));
|
||||
OX_RETURN_ERROR(writer.seekp(p));
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user