Squashed 'deps/oxlib/' content from commit 85f17c41

git-subtree-dir: deps/oxlib
git-subtree-split: 85f17c4188e266b82ba8858d21f67289c72e7b9a
This commit is contained in:
2026-05-06 01:38:00 -05:00
commit 69fcd7ad10
457 changed files with 53647 additions and 0 deletions
+61
View File
@@ -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
+202
View File
@@ -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>;
}
+211
View File
@@ -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
+118
View File
@@ -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);
}
}
}
}
+222
View File
@@ -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
+61
View File
@@ -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);
}
+138
View File
@@ -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 {};
}
}
+239
View File
@@ -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
+17
View File
@@ -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;
}
+211
View File
@@ -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>;
}
+39
View File
@@ -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>;
}
+33
View File
@@ -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)) {}
}
+55
View File
@@ -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&;
}
+123
View File
@@ -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
+94
View File
@@ -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
+34
View File
@@ -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__) =
+97
View File
@@ -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
}
}
+67
View File
@@ -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");
}
}
}
+386
View File
@@ -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);
}
}
}
}
+231
View File
@@ -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
+38
View File
@@ -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
+170
View File
@@ -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);
}
};
}
+272
View File
@@ -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;
}
}
}
+36
View File
@@ -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;
}
+24
View File
@@ -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
+33
View File
@@ -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
}
+275
View File
@@ -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;
}
}
+178
View File
@@ -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
+61
View File
@@ -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;
}
}
+36
View File
@@ -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
}
+63
View File
@@ -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
+300
View File
@@ -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();
}
}
}
+210
View File
@@ -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;
}
};
}
+214
View File
@@ -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)...);
}
}
+19
View File
@@ -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{};
};
}
+198
View File
@@ -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 {};
}
}
+58
View File
@@ -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
+25
View File
@@ -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) {
}
};
}
+35
View File
@@ -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;
}
}
+62
View File
@@ -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();
}
};
}
+24
View File
@@ -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
+89
View File
@@ -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;
}
}
+199
View File
@@ -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 {};
}
}
+290
View File
@@ -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
+218
View File
@@ -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
+25
View File
@@ -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;
}
+60
View File
@@ -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"
+39
View File
@@ -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
+59
View File
@@ -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"
+577
View File
@@ -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
+46
View File
@@ -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();
}
};
}
+35
View File
@@ -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; }
};
}
+126
View File
@@ -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
+307
View File
@@ -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>;
}
+130
View File
@@ -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
+294
View File
@@ -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);
}
+34
View File
@@ -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
+160
View File
@@ -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");
+374
View File
@@ -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));
}
+20
View File
@@ -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;
}
+101
View File
@@ -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);
}
}
+231
View File
@@ -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
+109
View File
@@ -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 {};
}
}
+757
View File
@@ -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
+84
View File
@@ -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;
}
}