Initial commit
This commit is contained in:
commit
219734a813
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"clangd.arguments": [
|
||||||
|
"--compile-commands-dir=${workspaceFolder}/build",
|
||||||
|
"-header-insertion=never"
|
||||||
|
],
|
||||||
|
"search.exclude": {
|
||||||
|
".cache/**": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
|
||||||
|
#if __is_kernel
|
||||||
|
|
||||||
|
#include <kernel/Panic.h>
|
||||||
|
|
||||||
|
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||||
|
{
|
||||||
|
Kernel::panic_impl(location, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <BAN/Debug.h>
|
||||||
|
|
||||||
|
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||||
|
{
|
||||||
|
derrorln("{}: {}", location, msg);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <BAN/New.h>
|
||||||
|
|
||||||
|
void* operator new(size_t size) { return BAN::allocator(size); }
|
||||||
|
void* operator new[](size_t size) { return BAN::allocator(size); }
|
||||||
|
|
||||||
|
void operator delete(void* addr) { BAN::deallocator(addr); }
|
||||||
|
void operator delete[](void* addr) { BAN::deallocator(addr); }
|
||||||
|
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
|
||||||
|
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <BAN/String.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
StringView::StringView(const String& other)
|
||||||
|
: StringView(other.data(), other.size())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include <BAN/Time.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr bool is_leap_year(uint64_t year)
|
||||||
|
{
|
||||||
|
if (year % 400 == 0)
|
||||||
|
return true;
|
||||||
|
if (year % 100 == 0)
|
||||||
|
return false;
|
||||||
|
if (year % 4 == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t leap_days_since_epoch(const BAN::Time& time)
|
||||||
|
{
|
||||||
|
uint64_t leap_years = 0;
|
||||||
|
for (uint32_t year = 1970; year < time.year; year++)
|
||||||
|
if (is_leap_year(year))
|
||||||
|
leap_years++;
|
||||||
|
if (is_leap_year(time.year) && time.month >= 3)
|
||||||
|
leap_years++;
|
||||||
|
return leap_years;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t month_days[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||||
|
|
||||||
|
uint64_t to_unix_time(const BAN::Time& time)
|
||||||
|
{
|
||||||
|
uint64_t years = time.year - 1970;
|
||||||
|
uint64_t days = years * 365 + month_days[time.month - 1] + leap_days_since_epoch(time) + (time.day - 1);
|
||||||
|
uint64_t hours = days * 24 + time.hour;
|
||||||
|
uint64_t minutes = hours * 60 + time.minute;
|
||||||
|
uint64_t seconds = minutes * 60 + time.second;
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Time from_unix_time(uint64_t unix_time)
|
||||||
|
{
|
||||||
|
BAN::Time time {};
|
||||||
|
|
||||||
|
time.second = unix_time % 60; unix_time /= 60;
|
||||||
|
time.minute = unix_time % 60; unix_time /= 60;
|
||||||
|
time.hour = unix_time % 24; unix_time /= 24;
|
||||||
|
|
||||||
|
uint64_t total_days = unix_time;
|
||||||
|
|
||||||
|
time.week_day = (total_days + 4) % 7 + 1;
|
||||||
|
|
||||||
|
time.year = 1970;
|
||||||
|
while (total_days >= 365U + is_leap_year(time.year))
|
||||||
|
{
|
||||||
|
total_days -= 365U + is_leap_year(time.year);
|
||||||
|
time.year++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_leap_day = is_leap_year(time.year) && total_days == month_days[2];
|
||||||
|
bool had_leap_day = is_leap_year(time.year) && total_days > month_days[2];
|
||||||
|
|
||||||
|
for (time.month = 1; time.month < 12; time.month++)
|
||||||
|
if (total_days < month_days[time.month] + (is_leap_day || had_leap_day))
|
||||||
|
break;
|
||||||
|
|
||||||
|
time.day = total_days - month_days[time.month - 1] + !had_leap_day;
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,16 @@
|
||||||
|
set(BAN_SOURCES
|
||||||
|
BAN/Assert.cpp
|
||||||
|
BAN/New.cpp
|
||||||
|
BAN/StringView.cpp
|
||||||
|
BAN/Time.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ban ${BAN_SOURCES})
|
||||||
|
|
||||||
|
set_target_properties(ban PROPERTIES OUTPUT_NAME libban)
|
||||||
|
|
||||||
|
# set SONAME as cmake doesn't set it for some reason??
|
||||||
|
set_target_properties(ban PROPERTIES LINK_FLAGS "-Wl,-soname,libban.so")
|
||||||
|
|
||||||
|
banan_install_headers(ban)
|
||||||
|
install(TARGETS ban OPTIONAL)
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
class Array
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = decltype(S);
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Array>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Array() = default;
|
||||||
|
constexpr Array(const T&);
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_data); }
|
||||||
|
iterator end() { return iterator(m_data + size()); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
const_iterator end() const { return const_iterator(m_data + size()); }
|
||||||
|
|
||||||
|
constexpr const T& operator[](size_type) const;
|
||||||
|
constexpr T& operator[](size_type);
|
||||||
|
|
||||||
|
constexpr const T& back() const;
|
||||||
|
constexpr T& back();
|
||||||
|
constexpr const T& front() const;
|
||||||
|
constexpr T& front();
|
||||||
|
|
||||||
|
Span<T> span() { return Span(m_data, size()); }
|
||||||
|
Span<const T> span() const { return Span(m_data, size()); }
|
||||||
|
|
||||||
|
constexpr size_type size() const;
|
||||||
|
|
||||||
|
constexpr const T* data() const { return m_data; }
|
||||||
|
constexpr T* data() { return m_data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_data[S] {};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr Array<T, S>::Array(const T& value)
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < S; i++)
|
||||||
|
m_data[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr const T& Array<T, S>::operator[](size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < S);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr T& Array<T, S>::operator[](size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < S);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr const T& Array<T, S>::back() const
|
||||||
|
{
|
||||||
|
ASSERT(S != 0);
|
||||||
|
return m_data[S - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr T& Array<T, S>::back()
|
||||||
|
{
|
||||||
|
ASSERT(S != 0);
|
||||||
|
return m_data[S - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr const T& Array<T, S>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(S != 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr T& Array<T, S>::front()
|
||||||
|
{
|
||||||
|
ASSERT(S != 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
constexpr typename Array<T, S>::size_type Array<T, S>::size() const
|
||||||
|
{
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define __ban_assert_stringify_helper(s) #s
|
||||||
|
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s)
|
||||||
|
|
||||||
|
#define ASSERT(cond) \
|
||||||
|
(__builtin_expect(!(cond), 0) \
|
||||||
|
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \
|
||||||
|
: (void)0)
|
||||||
|
|
||||||
|
#define ASSERT_NOT_REACHED() \
|
||||||
|
__ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT_NOT_REACHED() reached")
|
||||||
|
|
||||||
|
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg);
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
enum MemoryOrder
|
||||||
|
{
|
||||||
|
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||||
|
memory_order_consume = __ATOMIC_CONSUME,
|
||||||
|
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||||
|
memory_order_release = __ATOMIC_RELEASE,
|
||||||
|
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||||
|
memory_order_seq_cst = __ATOMIC_SEQ_CST,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> concept atomic_c = is_integral_v<T> || is_pointer_v<T>;
|
||||||
|
template<typename T> concept atomic_lockfree_c = (is_integral_v<T> || is_pointer_v<T>) && __atomic_always_lock_free(sizeof(T), 0);
|
||||||
|
|
||||||
|
template<atomic_lockfree_c T, atomic_c U>
|
||||||
|
inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); }
|
||||||
|
template<atomic_lockfree_c T>
|
||||||
|
inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); }
|
||||||
|
|
||||||
|
template<atomic_lockfree_c T, atomic_c U>
|
||||||
|
inline T atomic_exchange(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_exchange_n(&obj, value, mem_order); }
|
||||||
|
template<atomic_lockfree_c T, atomic_lockfree_c U, atomic_c V>
|
||||||
|
inline bool atomic_compare_exchange(T& obj, U& expected, V value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_compare_exchange_n(&obj, &expected, value, false, mem_order, mem_order); }
|
||||||
|
|
||||||
|
#define DECL_ATOMIC_INLINE template<atomic_lockfree_c T, atomic_c U> inline
|
||||||
|
DECL_ATOMIC_INLINE T atomic_add_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_add_fetch (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_sub_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_sub_fetch (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_and_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_and_fetch (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_xor_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_xor_fetch (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_or_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_or_fetch (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_nand_fetch(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_nand_fetch(&obj, value, mem_order); }
|
||||||
|
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_add (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_add (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_sub (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_sub (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_and (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_and (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_xor (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_xor (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_or (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_or (&obj, value, mem_order); }
|
||||||
|
DECL_ATOMIC_INLINE T atomic_fetch_nand(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_nand(&obj, value, mem_order); }
|
||||||
|
#undef DECL_ATOMIC_INLINE
|
||||||
|
|
||||||
|
template<atomic_lockfree_c T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
|
||||||
|
class Atomic
|
||||||
|
{
|
||||||
|
Atomic(const Atomic&) = delete;
|
||||||
|
Atomic(Atomic&&) = delete;
|
||||||
|
Atomic& operator=(const Atomic&) volatile = delete;
|
||||||
|
Atomic& operator=(Atomic&&) volatile = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Atomic() : m_value(0) {}
|
||||||
|
constexpr Atomic(T val) : m_value(val) {}
|
||||||
|
|
||||||
|
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return atomic_load(m_value, mem_order); }
|
||||||
|
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { atomic_store(m_value, val, mem_order); }
|
||||||
|
|
||||||
|
inline T operator=(T val) volatile { store(val); return val; }
|
||||||
|
|
||||||
|
inline operator T() const volatile { return load(); }
|
||||||
|
|
||||||
|
inline T operator+=(T val) volatile { return atomic_add_fetch(m_value, val, MEM_ORDER); }
|
||||||
|
inline T operator-=(T val) volatile { return atomic_sub_fetch(m_value, val, MEM_ORDER); }
|
||||||
|
inline T operator&=(T val) volatile { return atomic_and_fetch(m_value, val, MEM_ORDER); }
|
||||||
|
inline T operator^=(T val) volatile { return atomic_xor_fetch(m_value, val, MEM_ORDER); }
|
||||||
|
inline T operator|=(T val) volatile { return atomic_or_fetch(m_value, val, MEM_ORDER); }
|
||||||
|
|
||||||
|
inline T operator--() volatile { return atomic_sub_fetch(m_value, 1, MEM_ORDER); }
|
||||||
|
inline T operator++() volatile { return atomic_add_fetch(m_value, 1, MEM_ORDER); }
|
||||||
|
|
||||||
|
inline T operator--(int) volatile { return atomic_fetch_sub(m_value, 1, MEM_ORDER); }
|
||||||
|
inline T operator++(int) volatile { return atomic_fetch_add(m_value, 1, MEM_ORDER); }
|
||||||
|
|
||||||
|
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_compare_exchange(m_value, expected, desired, mem_order); }
|
||||||
|
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_exchange(m_value, desired, mem_order); };
|
||||||
|
|
||||||
|
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_add_fetch (m_value, val, mem_order); }
|
||||||
|
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_sub_fetch (m_value, val, mem_order); }
|
||||||
|
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_and_fetch (m_value, val, mem_order); }
|
||||||
|
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_xor_fetch (m_value, val, mem_order); }
|
||||||
|
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_or_fetch (m_value, val, mem_order); }
|
||||||
|
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_nand_fetch(m_value, val, mem_order); }
|
||||||
|
|
||||||
|
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_add (m_value, val, mem_order); }
|
||||||
|
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_sub (m_value, val, mem_order); }
|
||||||
|
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_and (m_value, val, mem_order); }
|
||||||
|
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_xor (m_value, val, mem_order); }
|
||||||
|
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_or (m_value, val, mem_order); }
|
||||||
|
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_nand(m_value, val, mem_order); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename To, typename From>
|
||||||
|
constexpr To bit_cast(const From& from)
|
||||||
|
{
|
||||||
|
return __builtin_bit_cast(To, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Array.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
class Bitset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using internal_type = uint64_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::Array<internal_type, BAN::Math::div_round_up(N, sizeof(internal_type) * 8)> m_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
sizeof(Bitset<65>);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<bool CONST>
|
||||||
|
class ByteSpanGeneral
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = maybe_const_t<CONST, uint8_t>;
|
||||||
|
using size_type = size_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ByteSpanGeneral() = default;
|
||||||
|
ByteSpanGeneral(value_type* data, size_type size)
|
||||||
|
: m_data(data)
|
||||||
|
, m_size(size)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template<bool SRC_CONST>
|
||||||
|
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
template<bool SRC_CONST>
|
||||||
|
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
template<typename T>
|
||||||
|
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool SRC_CONST>
|
||||||
|
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template<bool SRC_CONST>
|
||||||
|
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
other.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
|
||||||
|
{
|
||||||
|
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
S& as() const requires(!CONST || is_const_v<S>)
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
ASSERT(m_size >= sizeof(S));
|
||||||
|
return *reinterpret_cast<S*>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
Span<S> as_span() const requires(!CONST || is_const_v<S>)
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
ASSERT(m_size >= offset);
|
||||||
|
if (length == size_type(-1))
|
||||||
|
length = m_size - offset;
|
||||||
|
ASSERT(m_size >= offset + length);
|
||||||
|
return ByteSpanGeneral(m_data + offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator[](size_type offset) const
|
||||||
|
{
|
||||||
|
ASSERT(offset < m_size);
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type* data() const { return m_data; }
|
||||||
|
|
||||||
|
bool empty() const { return m_size == 0; }
|
||||||
|
size_type size() const { return m_size; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_data = nullptr;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type* m_data { nullptr };
|
||||||
|
size_type m_size { 0 };
|
||||||
|
|
||||||
|
friend class ByteSpanGeneral<!CONST>;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ByteSpan = ByteSpanGeneral<false>;
|
||||||
|
using ConstByteSpan = ByteSpanGeneral<true>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
class CircularQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CircularQueue() = default;
|
||||||
|
~CircularQueue();
|
||||||
|
|
||||||
|
void push(const T&);
|
||||||
|
void push(T&&);
|
||||||
|
template<typename... Args>
|
||||||
|
void emplace(Args&&... args) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
void pop();
|
||||||
|
|
||||||
|
const T& front() const;
|
||||||
|
T& front();
|
||||||
|
|
||||||
|
const T& back() const;
|
||||||
|
T& back();
|
||||||
|
|
||||||
|
const T& operator[](size_t index) const;
|
||||||
|
T& operator[](size_t index);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
size_type size() const { return m_size; }
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
bool full() const { return size() == capacity(); }
|
||||||
|
|
||||||
|
static constexpr size_type capacity() { return S; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* element_at(size_type);
|
||||||
|
const T* element_at(size_type) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alignas(T) uint8_t m_storage[sizeof(T) * capacity()];
|
||||||
|
size_type m_first { 0 };
|
||||||
|
size_type m_size { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
CircularQueue<T, S>::~CircularQueue()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::push(const T& value)
|
||||||
|
{
|
||||||
|
emplace(BAN::move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::push(T&& value)
|
||||||
|
{
|
||||||
|
emplace(BAN::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
template<typename... Args>
|
||||||
|
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
ASSERT(!full());
|
||||||
|
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
|
||||||
|
m_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::pop()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
element_at(m_first)->~T();
|
||||||
|
m_first = (m_first + 1) % capacity();
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
const T& CircularQueue<T, S>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *element_at(m_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T& CircularQueue<T, S>::front()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *element_at(m_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
const T& CircularQueue<T, S>::back() const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *element_at((m_first + m_size - 1) % capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T& CircularQueue<T, S>::back()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *element_at((m_first + m_size - 1) % capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
const T& CircularQueue<T, S>::operator[](size_t index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return *element_at((m_first + index) % capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T& CircularQueue<T, S>::operator[](size_t index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return *element_at((m_first + index) % capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::clear()
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
element_at((m_first + i) % capacity())->~T();
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
const T* CircularQueue<T, S>::element_at(size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < capacity());
|
||||||
|
return (const T*)(m_storage + index * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T* CircularQueue<T, S>::element_at(size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < capacity());
|
||||||
|
return (T*)(m_storage + index * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if __is_kernel
|
||||||
|
|
||||||
|
#include <kernel/Debug.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define __debug_putchar [](int c) { putc_unlocked(c, stddbg); }
|
||||||
|
|
||||||
|
#define dprintln(...) \
|
||||||
|
do { \
|
||||||
|
flockfile(stddbg); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar,"\n"); \
|
||||||
|
fflush(stddbg); \
|
||||||
|
funlockfile(stddbg); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define dwarnln(...) \
|
||||||
|
do { \
|
||||||
|
flockfile(stddbg); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
|
||||||
|
fflush(stddbg); \
|
||||||
|
funlockfile(stddbg); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define derrorln(...) \
|
||||||
|
do { \
|
||||||
|
flockfile(stddbg); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
|
||||||
|
fflush(stddbg); \
|
||||||
|
funlockfile(stddbg); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define dprintln_if(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if constexpr(cond) \
|
||||||
|
dprintln(__VA_ARGS__); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define dwarnln_if(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if constexpr(cond) \
|
||||||
|
dwarnln(__VA_ARGS__); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define derrorln_if(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if constexpr(cond) \
|
||||||
|
derrorln(__VA_ARGS__); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T swap_endianness(T value)
|
||||||
|
{
|
||||||
|
if constexpr(sizeof(T) == 1)
|
||||||
|
return value;
|
||||||
|
if constexpr(sizeof(T) == 2)
|
||||||
|
return (((value >> 8) & 0xFF) << 0)
|
||||||
|
| (((value >> 0) & 0xFF) << 8);
|
||||||
|
if constexpr(sizeof(T) == 4)
|
||||||
|
return (((value >> 24) & 0xFF) << 0)
|
||||||
|
| (((value >> 16) & 0xFF) << 8)
|
||||||
|
| (((value >> 8) & 0xFF) << 16)
|
||||||
|
| (((value >> 0) & 0xFF) << 24);
|
||||||
|
if constexpr(sizeof(T) == 8)
|
||||||
|
return (((value >> 56) & 0xFF) << 0)
|
||||||
|
| (((value >> 48) & 0xFF) << 8)
|
||||||
|
| (((value >> 40) & 0xFF) << 16)
|
||||||
|
| (((value >> 32) & 0xFF) << 24)
|
||||||
|
| (((value >> 24) & 0xFF) << 32)
|
||||||
|
| (((value >> 16) & 0xFF) << 40)
|
||||||
|
| (((value >> 8) & 0xFF) << 48)
|
||||||
|
| (((value >> 0) & 0xFF) << 56);
|
||||||
|
T result { 0 };
|
||||||
|
for (size_t i = 0; i < sizeof(T); i++)
|
||||||
|
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T host_to_little_endian(T value)
|
||||||
|
{
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
return swap_endianness(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T little_endian_to_host(T value)
|
||||||
|
{
|
||||||
|
return host_to_little_endian(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T host_to_big_endian(T value)
|
||||||
|
{
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
return swap_endianness(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T big_endian_to_host(T value)
|
||||||
|
{
|
||||||
|
return host_to_big_endian(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
struct LittleEndian
|
||||||
|
{
|
||||||
|
constexpr LittleEndian()
|
||||||
|
: raw(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr LittleEndian(T value)
|
||||||
|
: raw(host_to_little_endian(value))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr operator T() const
|
||||||
|
{
|
||||||
|
return host_to_little_endian(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
struct BigEndian
|
||||||
|
{
|
||||||
|
constexpr BigEndian()
|
||||||
|
: raw(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr BigEndian(T value)
|
||||||
|
: raw(host_to_big_endian(value))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr operator T() const
|
||||||
|
{
|
||||||
|
return host_to_big_endian(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
using NetworkEndian = BigEndian<T>;
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T host_to_network_endian(T value)
|
||||||
|
{
|
||||||
|
return host_to_big_endian(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T network_endian_to_host(T value)
|
||||||
|
{
|
||||||
|
return big_endian_to_host(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
#include <BAN/Variant.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __is_kernel
|
||||||
|
#include <kernel/Panic.h>
|
||||||
|
#include <kernel/Errors.h>
|
||||||
|
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
||||||
|
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
|
||||||
|
#else
|
||||||
|
#include <BAN/Debug.h>
|
||||||
|
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } e.release_value(); })
|
||||||
|
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } &e.release_value(); })
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TRY(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||||
|
#define TRY_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); &e.release_value(); })
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
class Error
|
||||||
|
{
|
||||||
|
#ifdef __is_kernel
|
||||||
|
private:
|
||||||
|
static constexpr uint64_t kernel_error_mask = uint64_t(1) << 63;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef __is_kernel
|
||||||
|
static Error from_error_code(Kernel::ErrorCode error)
|
||||||
|
{
|
||||||
|
return Error((uint64_t)error | kernel_error_mask);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template<size_t N>
|
||||||
|
consteval static Error from_literal(const char (&message)[N])
|
||||||
|
{
|
||||||
|
return Error(message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Error from_errno(int error)
|
||||||
|
{
|
||||||
|
return Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __is_kernel
|
||||||
|
Kernel::ErrorCode kernel_error() const
|
||||||
|
{
|
||||||
|
return (Kernel::ErrorCode)(m_error_code & ~kernel_error_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_kernel_error() const
|
||||||
|
{
|
||||||
|
return m_error_code & kernel_error_mask;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr uint64_t get_error_code() const { return m_error_code; }
|
||||||
|
const char* get_message() const
|
||||||
|
{
|
||||||
|
#ifdef __is_kernel
|
||||||
|
if (m_error_code & kernel_error_mask)
|
||||||
|
return Kernel::error_string(kernel_error());
|
||||||
|
#else
|
||||||
|
if (m_message)
|
||||||
|
return m_message;
|
||||||
|
#endif
|
||||||
|
if (auto* desc = strerrordesc_np(m_error_code))
|
||||||
|
return desc;
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr Error(uint64_t error)
|
||||||
|
: m_error_code(error)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#ifndef __is_kernel
|
||||||
|
constexpr Error(const char* message)
|
||||||
|
: m_message(message)
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t m_error_code { 0 };
|
||||||
|
|
||||||
|
#ifndef __is_kernel
|
||||||
|
const char* m_message { nullptr };
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class [[nodiscard]] ErrorOr
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(ErrorOr);
|
||||||
|
public:
|
||||||
|
ErrorOr(const T& value)
|
||||||
|
: m_data(value)
|
||||||
|
{}
|
||||||
|
ErrorOr(T&& value)
|
||||||
|
: m_data(move(value))
|
||||||
|
{}
|
||||||
|
ErrorOr(const Error& error)
|
||||||
|
: m_data(error)
|
||||||
|
{}
|
||||||
|
ErrorOr(Error&& error)
|
||||||
|
: m_data(move(error))
|
||||||
|
{}
|
||||||
|
ErrorOr(ErrorOr&& other)
|
||||||
|
: m_data(move(other.m_data))
|
||||||
|
{}
|
||||||
|
ErrorOr& operator=(ErrorOr&& other)
|
||||||
|
{
|
||||||
|
m_data = move(other.m_data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_error() const { return m_data.template has<Error>(); }
|
||||||
|
const Error& error() const { return m_data.template get<Error>(); }
|
||||||
|
Error& error() { return m_data.template get<Error>(); }
|
||||||
|
const T& value() const { return m_data.template get<T>(); }
|
||||||
|
T& value() { return m_data.template get<T>(); }
|
||||||
|
|
||||||
|
Error release_error() { return move(error()); m_data.clear(); }
|
||||||
|
T release_value() { return move(value()); m_data.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Variant<Error, T> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<lvalue_reference T>
|
||||||
|
class [[nodiscard]] ErrorOr<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorOr(T value)
|
||||||
|
{
|
||||||
|
m_data.template set<T>(value);
|
||||||
|
}
|
||||||
|
ErrorOr(Error&& error)
|
||||||
|
: m_data(move(error))
|
||||||
|
{ }
|
||||||
|
ErrorOr(const Error& error)
|
||||||
|
: m_data(error)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool is_error() const { return m_data.template has<Error>(); }
|
||||||
|
Error& error() { return m_data.template get<Error>(); }
|
||||||
|
const Error& error() const { return m_data.template get<Error>(); }
|
||||||
|
T value() { return m_data.template get<T>(); }
|
||||||
|
|
||||||
|
Error release_error() { return move(error()); m_data.clear(); }
|
||||||
|
T release_value() { return value(); m_data.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Variant<Error, T> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class [[nodiscard]] ErrorOr<void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorOr() {}
|
||||||
|
ErrorOr(const Error& error) : m_data(error), m_has_error(true) {}
|
||||||
|
ErrorOr(Error&& error) : m_data(move(error)), m_has_error(true) {}
|
||||||
|
|
||||||
|
bool is_error() const { return m_has_error; }
|
||||||
|
Error& error() { return m_data; }
|
||||||
|
const Error& error() const { return m_data; }
|
||||||
|
void value() { }
|
||||||
|
|
||||||
|
Error release_error() { return move(m_data); }
|
||||||
|
void release_value() { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Error m_data { Error::from_errno(0) };
|
||||||
|
bool m_has_error { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const Error& error, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
print_argument(putc, error.get_message(), format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
struct ValueFormat;
|
||||||
|
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
(print_argument(putc, BAN::forward<Args>(args), format), ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F, typename T>
|
||||||
|
inline void print_argument(F putc, T value, const ValueFormat& format);
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template<typename F, typename T>
|
||||||
|
inline size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
IMPLEMENTATION
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ValueFormat
|
||||||
|
{
|
||||||
|
int base = 10;
|
||||||
|
int percision = 3;
|
||||||
|
int fill = 0;
|
||||||
|
char fill_char = '0';
|
||||||
|
bool upper = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline void print(F putc, const char* format)
|
||||||
|
{
|
||||||
|
while (*format)
|
||||||
|
{
|
||||||
|
putc(*format);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...>
|
||||||
|
inline void print(F putc, const char* format, Arg&& arg, Args&&... args)
|
||||||
|
{
|
||||||
|
while (*format && *format != '{')
|
||||||
|
{
|
||||||
|
putc(*format);
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == '{')
|
||||||
|
{
|
||||||
|
size_t arg_len = detail::parse_format_and_print_argument(putc, format, forward<Arg>(arg));
|
||||||
|
if (arg_len == size_t(-1))
|
||||||
|
return print(putc, format);
|
||||||
|
print(putc, format + arg_len, forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
inline void println(F putc, const char* format, Args&&... args)
|
||||||
|
{
|
||||||
|
print(putc, format, args...);
|
||||||
|
putc('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F, typename Arg>
|
||||||
|
inline size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
|
||||||
|
{
|
||||||
|
ValueFormat value_format;
|
||||||
|
|
||||||
|
if (format[0] != '{')
|
||||||
|
return size_t(-1);
|
||||||
|
|
||||||
|
size_t i = 1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!format[i] || format[i] == '}')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (format[i] == ' ')
|
||||||
|
{
|
||||||
|
value_format.fill_char = ' ';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('0' <= format[i] && format[i] <= '9')
|
||||||
|
{
|
||||||
|
int fill = 0;
|
||||||
|
while ('0' <= format[i] && format[i] <= '9')
|
||||||
|
{
|
||||||
|
fill = (fill * 10) + (format[i] - '0');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
value_format.fill = fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format[i])
|
||||||
|
{
|
||||||
|
case 'b': value_format.base = 2; value_format.upper = false; i++; break;
|
||||||
|
case 'B': value_format.base = 2; value_format.upper = true; i++; break;
|
||||||
|
case 'o': value_format.base = 8; value_format.upper = false; i++; break;
|
||||||
|
case 'O': value_format.base = 8; value_format.upper = true; i++; break;
|
||||||
|
case 'd': value_format.base = 10; value_format.upper = false; i++; break;
|
||||||
|
case 'D': value_format.base = 10; value_format.upper = true; i++; break;
|
||||||
|
case 'h': value_format.base = 16; value_format.upper = false; i++; break;
|
||||||
|
case 'H': value_format.base = 16; value_format.upper = true; i++; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!format[i] || format[i] == '}')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (format[i] == '.')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
int percision = 0;
|
||||||
|
while ('0' <= format[i] && format[i] <= '9')
|
||||||
|
{
|
||||||
|
percision = (percision * 10) + (format[i] - '0');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
value_format.percision = percision;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
if (format[i] != '}')
|
||||||
|
return size_t(-1);
|
||||||
|
|
||||||
|
print_argument(putc, forward<Arg>(argument), value_format);
|
||||||
|
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char value_to_base_char(uint8_t value, int base, bool upper)
|
||||||
|
{
|
||||||
|
if (base <= 10)
|
||||||
|
return value + '0';
|
||||||
|
if (base <= 36)
|
||||||
|
{
|
||||||
|
if (value < 10)
|
||||||
|
return value + '0';
|
||||||
|
return value + (upper ? 'A' : 'a') - 10;
|
||||||
|
}
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename T>
|
||||||
|
inline void print_integer(F putc, T value, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < format.fill - 1; i++)
|
||||||
|
putc(format.fill_char);
|
||||||
|
putc('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sign = false;
|
||||||
|
|
||||||
|
// Fits signed 64-bit binary number and null
|
||||||
|
char buffer[66];
|
||||||
|
char* ptr = buffer + sizeof(buffer);
|
||||||
|
*(--ptr) = '\0';
|
||||||
|
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
sign = true;
|
||||||
|
T digit = (format.base - (value % format.base)) % format.base;
|
||||||
|
*(--ptr) = value_to_base_char(digit, format.base, format.upper);
|
||||||
|
value = -(value / format.base);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (value)
|
||||||
|
{
|
||||||
|
*(--ptr) = value_to_base_char(value % format.base, format.base, format.upper);
|
||||||
|
value /= format.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ptr >= buffer + sizeof(buffer) - format.fill)
|
||||||
|
*(--ptr) = format.fill_char;
|
||||||
|
|
||||||
|
if (sign)
|
||||||
|
*(--ptr) = '-';
|
||||||
|
|
||||||
|
print(putc, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename T>
|
||||||
|
inline void print_floating(F putc, T value, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
putc('-');
|
||||||
|
return print_floating(putc, -value, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t int_part = (int64_t)value;
|
||||||
|
T frac_part = value - (T)int_part;
|
||||||
|
|
||||||
|
print_integer(putc, int_part, format);
|
||||||
|
|
||||||
|
if (format.percision > 0)
|
||||||
|
putc('.');
|
||||||
|
|
||||||
|
for (int i = 0; i < format.percision; i++)
|
||||||
|
{
|
||||||
|
frac_part *= format.base;
|
||||||
|
if (i == format.percision - 1)
|
||||||
|
frac_part += 0.5;
|
||||||
|
|
||||||
|
putc(value_to_base_char((uint8_t)frac_part % format.base, format.base, format.upper));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline void print_pointer(F putc, void* ptr, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
uintptr_t value = (uintptr_t)ptr;
|
||||||
|
print(putc, "0x");
|
||||||
|
for (int i = sizeof(void*) * 8 - 4; i >= 0; i -= 4)
|
||||||
|
putc(value_to_base_char((value >> i) & 0xF, 16, format.upper));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
TEMPLATE SPECIALIZATIONS
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename F, integral T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||||
|
template<typename F, floating_point T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
|
||||||
|
template<typename F, pointer T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||||
|
|
||||||
|
template<typename F> inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
|
||||||
|
template<typename F> inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
|
||||||
|
template<typename F> inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
|
||||||
|
template<typename F> inline void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template<typename, size_t> class Array;
|
||||||
|
template<typename> class ErrorOr;
|
||||||
|
template<typename> class Function;
|
||||||
|
template<typename> class Queue;
|
||||||
|
class String;
|
||||||
|
class StringView;
|
||||||
|
template<typename> class Vector;
|
||||||
|
template<typename> class LinkedList;
|
||||||
|
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
class Function;
|
||||||
|
template<typename Ret, typename... Args>
|
||||||
|
class Function<Ret(Args...)>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Function() = default;
|
||||||
|
Function(Ret(*function)(Args...))
|
||||||
|
{
|
||||||
|
static_assert(sizeof(CallablePointer) <= m_size);
|
||||||
|
new (m_storage) CallablePointer(function);
|
||||||
|
}
|
||||||
|
template<typename Own>
|
||||||
|
Function(Ret(Own::*function)(Args...), Own& owner)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(CallableMember<Own>) <= m_size);
|
||||||
|
new (m_storage) CallableMember<Own>(function, owner);
|
||||||
|
}
|
||||||
|
template<typename Own>
|
||||||
|
Function(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
|
||||||
|
new (m_storage) CallableMemberConst<Own>(function, owner);
|
||||||
|
}
|
||||||
|
template<typename Lambda>
|
||||||
|
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; }
|
||||||
|
{
|
||||||
|
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
||||||
|
new (m_storage) CallableLambda<Lambda>(lambda);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Function()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ret operator()(Args... args) const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
return reinterpret_cast<const CallableBase*>(m_storage)->call(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_size; i++)
|
||||||
|
if (m_storage[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (*this)
|
||||||
|
reinterpret_cast<CallableBase*>(m_storage)->~CallableBase();
|
||||||
|
memset(m_storage, 0, m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t size() { return m_size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct CallableBase
|
||||||
|
{
|
||||||
|
virtual ~CallableBase() {}
|
||||||
|
virtual Ret call(Args...) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallablePointer : public CallableBase
|
||||||
|
{
|
||||||
|
CallablePointer(Ret(*function)(Args...))
|
||||||
|
: m_function(function)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual Ret call(Args... args) const override
|
||||||
|
{
|
||||||
|
return m_function(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ret(*m_function)(Args...) = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Own>
|
||||||
|
struct CallableMember : public CallableBase
|
||||||
|
{
|
||||||
|
CallableMember(Ret(Own::*function)(Args...), Own& owner)
|
||||||
|
: m_owner(owner)
|
||||||
|
, m_function(function)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual Ret call(Args... args) const override
|
||||||
|
{
|
||||||
|
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Own& m_owner;
|
||||||
|
Ret(Own::*m_function)(Args...) = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Own>
|
||||||
|
struct CallableMemberConst : public CallableBase
|
||||||
|
{
|
||||||
|
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||||
|
: m_owner(owner)
|
||||||
|
, m_function(function)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual Ret call(Args... args) const override
|
||||||
|
{
|
||||||
|
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Own& m_owner;
|
||||||
|
Ret(Own::*m_function)(Args...) const = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Lambda>
|
||||||
|
struct CallableLambda : public CallableBase
|
||||||
|
{
|
||||||
|
CallableLambda(Lambda lambda)
|
||||||
|
: m_lambda(lambda)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual Ret call(Args... args) const override
|
||||||
|
{
|
||||||
|
return m_lambda(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lambda m_lambda;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t m_size = sizeof(void*) * 8;
|
||||||
|
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/String.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
struct GUID
|
||||||
|
{
|
||||||
|
uint32_t component1 { 0 };
|
||||||
|
uint16_t component2 { 0 };
|
||||||
|
uint16_t component3 { 0 };
|
||||||
|
uint8_t component45[8] { };
|
||||||
|
|
||||||
|
bool operator==(const GUID& other) const
|
||||||
|
{
|
||||||
|
return memcmp(this, &other, sizeof(GUID)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::String> to_string() const
|
||||||
|
{
|
||||||
|
char buffer[37];
|
||||||
|
char* ptr = buffer;
|
||||||
|
|
||||||
|
const auto append_hex_nibble =
|
||||||
|
[&ptr](uint8_t nibble)
|
||||||
|
{
|
||||||
|
if (nibble < 10)
|
||||||
|
*ptr++ = '0' + nibble;
|
||||||
|
else
|
||||||
|
*ptr++ = 'A' + nibble - 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto append_hex_byte =
|
||||||
|
[&append_hex_nibble](uint8_t byte)
|
||||||
|
{
|
||||||
|
append_hex_nibble(byte >> 4);
|
||||||
|
append_hex_nibble(byte & 0xF);
|
||||||
|
};
|
||||||
|
|
||||||
|
append_hex_byte((component1 >> 24) & 0xFF);
|
||||||
|
append_hex_byte((component1 >> 16) & 0xFF);
|
||||||
|
append_hex_byte((component1 >> 8) & 0xFF);
|
||||||
|
append_hex_byte((component1 >> 0) & 0xFF);
|
||||||
|
*ptr++ = '-';
|
||||||
|
append_hex_byte((component2 >> 8) & 0xFF);
|
||||||
|
append_hex_byte((component2 >> 0) & 0xFF);
|
||||||
|
*ptr++ = '-';
|
||||||
|
append_hex_byte((component3 >> 8) & 0xFF);
|
||||||
|
append_hex_byte((component3 >> 0) & 0xFF);
|
||||||
|
*ptr++ = '-';
|
||||||
|
append_hex_byte(component45[0]);
|
||||||
|
append_hex_byte(component45[1]);
|
||||||
|
*ptr++ = '-';
|
||||||
|
append_hex_byte(component45[2]);
|
||||||
|
append_hex_byte(component45[3]);
|
||||||
|
append_hex_byte(component45[4]);
|
||||||
|
append_hex_byte(component45[5]);
|
||||||
|
append_hex_byte(component45[6]);
|
||||||
|
append_hex_byte(component45[7]);
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
BAN::String guid;
|
||||||
|
TRY(guid.append(buffer));
|
||||||
|
return BAN::move(guid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(GUID) == 16);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct hash;
|
||||||
|
|
||||||
|
using hash_t = uint32_t;
|
||||||
|
|
||||||
|
inline constexpr hash_t u32_hash(uint32_t val)
|
||||||
|
{
|
||||||
|
val = ((val >> 16) ^ val) * 0x119de1f3;
|
||||||
|
val = ((val >> 16) ^ val) * 0x119de1f3;
|
||||||
|
val = ((val >> 16) ^ val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr hash_t u64_hash(uint64_t val)
|
||||||
|
{
|
||||||
|
hash_t low = u32_hash(val);
|
||||||
|
hash_t high = u32_hash(val >> 32);
|
||||||
|
return low ^ high;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
struct hash<T>
|
||||||
|
{
|
||||||
|
constexpr hash_t operator()(T val) const
|
||||||
|
{
|
||||||
|
if constexpr(sizeof(val) <= sizeof(uint32_t))
|
||||||
|
return u32_hash(val);
|
||||||
|
return u64_hash(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<pointer T>
|
||||||
|
struct hash<T>
|
||||||
|
{
|
||||||
|
constexpr hash_t operator()(T val) const
|
||||||
|
{
|
||||||
|
return hash<uintptr_t>()((uintptr_t)val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,319 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||||
|
class HashMap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
template<typename... Args>
|
||||||
|
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
: key(key)
|
||||||
|
, value(forward<Args>(args)...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
: key(BAN::move(key))
|
||||||
|
, value(forward<Args>(args)...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Key key;
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashMap() = default;
|
||||||
|
HashMap(const HashMap<Key, T, HASH>&);
|
||||||
|
HashMap(HashMap<Key, T, HASH>&&);
|
||||||
|
~HashMap();
|
||||||
|
|
||||||
|
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
||||||
|
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
||||||
|
|
||||||
|
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
|
||||||
|
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
|
||||||
|
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
|
||||||
|
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
|
||||||
|
|
||||||
|
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
|
||||||
|
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
|
||||||
|
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
|
||||||
|
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{ return emplace(Key(key), forward<Args>(args)...); }
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
|
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
|
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
|
||||||
|
|
||||||
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
|
void remove(const Key&);
|
||||||
|
void remove(iterator it);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
T& operator[](const Key&);
|
||||||
|
const T& operator[](const Key&) const;
|
||||||
|
|
||||||
|
iterator find(const Key& key);
|
||||||
|
const_iterator find(const Key& key) const;
|
||||||
|
bool contains(const Key&) const;
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
size_type size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> rebucket(size_type);
|
||||||
|
LinkedList<Entry>& get_bucket(const Key&);
|
||||||
|
const LinkedList<Entry>& get_bucket(const Key&) const;
|
||||||
|
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
|
||||||
|
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<LinkedList<Entry>> m_buckets;
|
||||||
|
size_type m_size = 0;
|
||||||
|
|
||||||
|
friend iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
|
||||||
|
{
|
||||||
|
*this = move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
HashMap<Key, T, HASH>::~HashMap()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_buckets = other.m_buckets;
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_buckets = move(other.m_buckets);
|
||||||
|
m_size = other.m_size;
|
||||||
|
other.m_size = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
ASSERT(!contains(key));
|
||||||
|
TRY(rebucket(m_size + 1));
|
||||||
|
|
||||||
|
auto bucket_it = get_bucket_iterator(key);
|
||||||
|
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
|
||||||
|
m_size++;
|
||||||
|
|
||||||
|
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
return emplace(move(key), forward<Args>(args)...);
|
||||||
|
|
||||||
|
auto bucket_it = get_bucket_iterator(key);
|
||||||
|
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++)
|
||||||
|
{
|
||||||
|
if (entry_it->key != key)
|
||||||
|
continue;
|
||||||
|
entry_it->value = T(forward<Args>(args)...);
|
||||||
|
return iterator(m_buckets.end(), bucket_it, entry_it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return emplace(move(key), forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
|
||||||
|
{
|
||||||
|
TRY(rebucket(size));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
void HashMap<Key, T, HASH>::remove(const Key& key)
|
||||||
|
{
|
||||||
|
auto it = find(key);
|
||||||
|
if (it != end())
|
||||||
|
remove(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
void HashMap<Key, T, HASH>::remove(iterator it)
|
||||||
|
{
|
||||||
|
it.outer_current()->remove(it.inner_current());
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
void HashMap<Key, T, HASH>::clear()
|
||||||
|
{
|
||||||
|
m_buckets.clear();
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
T& HashMap<Key, T, HASH>::operator[](const Key& key)
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
auto& bucket = get_bucket(key);
|
||||||
|
for (Entry& entry : bucket)
|
||||||
|
if (entry.key == key)
|
||||||
|
return entry.value;
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
const T& HashMap<Key, T, HASH>::operator[](const Key& key) const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
const auto& bucket = get_bucket(key);
|
||||||
|
for (const Entry& entry : bucket)
|
||||||
|
if (entry.key == key)
|
||||||
|
return entry.value;
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
return end();
|
||||||
|
auto bucket_it = get_bucket_iterator(key);
|
||||||
|
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||||
|
if (it->key == key)
|
||||||
|
return iterator(m_buckets.end(), bucket_it, it);
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
return end();
|
||||||
|
auto bucket_it = get_bucket_iterator(key);
|
||||||
|
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||||
|
if (it->key == key)
|
||||||
|
return const_iterator(m_buckets.end(), bucket_it, it);
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
||||||
|
{
|
||||||
|
return find(key) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
bool HashMap<Key, T, HASH>::empty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
|
||||||
|
{
|
||||||
|
if (m_buckets.size() >= bucket_count)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
|
Vector<LinkedList<Entry>> new_buckets;
|
||||||
|
TRY(new_buckets.resize(new_bucket_count));
|
||||||
|
|
||||||
|
for (auto& bucket : m_buckets)
|
||||||
|
{
|
||||||
|
for (auto it = bucket.begin(); it != bucket.end();)
|
||||||
|
{
|
||||||
|
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
|
||||||
|
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buckets = move(new_buckets);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
||||||
|
{
|
||||||
|
return *get_bucket_iterator(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
||||||
|
{
|
||||||
|
return *get_bucket_iterator(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
|
||||||
|
{
|
||||||
|
ASSERT(!m_buckets.empty());
|
||||||
|
auto index = HASH()(key) % m_buckets.size();
|
||||||
|
return next(m_buckets.begin(), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename HASH>
|
||||||
|
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
|
||||||
|
{
|
||||||
|
ASSERT(!m_buckets.empty());
|
||||||
|
auto index = HASH()(key) % m_buckets.size();
|
||||||
|
return next(m_buckets.begin(), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, typename HASH = hash<T>>
|
||||||
|
class HashSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashSet() = default;
|
||||||
|
HashSet(const HashSet&);
|
||||||
|
HashSet(HashSet&&);
|
||||||
|
|
||||||
|
HashSet& operator=(const HashSet&);
|
||||||
|
HashSet& operator=(HashSet&&);
|
||||||
|
|
||||||
|
ErrorOr<void> insert(const T&);
|
||||||
|
ErrorOr<void> insert(T&&);
|
||||||
|
void remove(const T&);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
|
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
|
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
|
||||||
|
|
||||||
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
size_type size() const;
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> rebucket(size_type);
|
||||||
|
LinkedList<T>& get_bucket(const T&);
|
||||||
|
const LinkedList<T>& get_bucket(const T&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<LinkedList<T>> m_buckets;
|
||||||
|
size_type m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
HashSet<T, HASH>::HashSet(const HashSet& other)
|
||||||
|
: m_buckets(other.m_buckets)
|
||||||
|
, m_size(other.m_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
HashSet<T, HASH>::HashSet(HashSet&& other)
|
||||||
|
: m_buckets(move(other.m_buckets))
|
||||||
|
, m_size(other.m_size)
|
||||||
|
{
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_buckets = other.m_buckets;
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_buckets = move(other.m_buckets);
|
||||||
|
m_size = other.m_size;
|
||||||
|
other.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
|
||||||
|
{
|
||||||
|
return insert(move(T(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
|
||||||
|
{
|
||||||
|
if (!empty() && get_bucket(key).contains(key))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
TRY(rebucket(m_size + 1));
|
||||||
|
TRY(get_bucket(key).push_back(move(key)));
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
void HashSet<T, HASH>::remove(const T& key)
|
||||||
|
{
|
||||||
|
if (empty()) return;
|
||||||
|
auto& bucket = get_bucket(key);
|
||||||
|
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||||
|
{
|
||||||
|
if (*it == key)
|
||||||
|
{
|
||||||
|
bucket.remove(it);
|
||||||
|
m_size--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
void HashSet<T, HASH>::clear()
|
||||||
|
{
|
||||||
|
m_buckets.clear();
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
|
||||||
|
{
|
||||||
|
TRY(rebucket(size));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
bool HashSet<T, HASH>::contains(const T& key) const
|
||||||
|
{
|
||||||
|
if (empty()) return false;
|
||||||
|
return get_bucket(key).contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
bool HashSet<T, HASH>::empty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
|
||||||
|
{
|
||||||
|
if (m_buckets.size() >= bucket_count)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
|
Vector<LinkedList<T>> new_buckets;
|
||||||
|
if (new_buckets.resize(new_bucket_count).is_error())
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
for (auto& bucket : m_buckets)
|
||||||
|
{
|
||||||
|
for (auto it = bucket.begin(); it != bucket.end();)
|
||||||
|
{
|
||||||
|
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
|
||||||
|
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buckets = move(new_buckets);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||||
|
{
|
||||||
|
ASSERT(!m_buckets.empty());
|
||||||
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
|
return m_buckets[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename HASH>
|
||||||
|
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||||
|
{
|
||||||
|
ASSERT(!m_buckets.empty());
|
||||||
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
|
return m_buckets[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Swap.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
void heapify_up(It begin, size_t index, Comp comp)
|
||||||
|
{
|
||||||
|
size_t parent = (index - 1) / 2;
|
||||||
|
while (parent < index)
|
||||||
|
{
|
||||||
|
if (comp(*(begin + index), *(begin + parent)))
|
||||||
|
break;
|
||||||
|
swap(*(begin + parent), *(begin + index));
|
||||||
|
index = parent;
|
||||||
|
parent = (index - 1) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
void heapify_down(It begin, size_t index, size_t len, Comp comp)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const size_t lchild = 2 * index + 1;
|
||||||
|
const size_t rchild = 2 * index + 2;
|
||||||
|
|
||||||
|
size_t child = 0;
|
||||||
|
if (lchild < len && !comp(*(begin + lchild), *(begin + index)))
|
||||||
|
{
|
||||||
|
if (rchild < len && !comp(*(begin + rchild), *(begin + lchild)))
|
||||||
|
child = rchild;
|
||||||
|
else
|
||||||
|
child = lchild;
|
||||||
|
}
|
||||||
|
else if (rchild < len && !comp(*(begin + rchild), *(begin + index)))
|
||||||
|
child = rchild;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
swap(*(begin + child), *(begin + index));
|
||||||
|
index = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void make_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
if (len <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t index = (len - 2) / 2;
|
||||||
|
while (index < len)
|
||||||
|
detail::heapify_down(begin, index--, len, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void push_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
detail::heapify_up(begin, len - 1, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void pop_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
swap(*begin, *(begin + len - 1));
|
||||||
|
detail::heapify_down(begin, 0, len - 1, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void sort_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
while (begin != end)
|
||||||
|
pop_heap(begin, end--, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Endianness.h>
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IPv4Address
|
||||||
|
{
|
||||||
|
constexpr IPv4Address(uint32_t u32_address)
|
||||||
|
{
|
||||||
|
raw = u32_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IPv4Address(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4)
|
||||||
|
{
|
||||||
|
octets[0] = oct1;
|
||||||
|
octets[1] = oct2;
|
||||||
|
octets[2] = oct3;
|
||||||
|
octets[3] = oct4;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const IPv4Address& other) const
|
||||||
|
{
|
||||||
|
return raw == other.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IPv4Address mask(const IPv4Address& other) const
|
||||||
|
{
|
||||||
|
return IPv4Address(raw & other.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t octets[4];
|
||||||
|
uint32_t raw;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IPv4Address) == 4);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<IPv4Address>
|
||||||
|
{
|
||||||
|
constexpr hash_t operator()(IPv4Address ipv4) const
|
||||||
|
{
|
||||||
|
return hash<uint32_t>()(ipv4.raw);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const IPv4Address& ipv4, const ValueFormat&)
|
||||||
|
{
|
||||||
|
ValueFormat format {
|
||||||
|
.base = 10,
|
||||||
|
.percision = 0,
|
||||||
|
.fill = 0,
|
||||||
|
.upper = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
print_argument(putc, ipv4.octets[0], format);
|
||||||
|
for (size_t i = 1; i < 4; i++)
|
||||||
|
{
|
||||||
|
putc('.');
|
||||||
|
print_argument(putc, ipv4.octets[i], format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Iteration
|
||||||
|
{
|
||||||
|
Continue,
|
||||||
|
Break
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,330 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
constexpr It next(It it, size_t count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
++it;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
requires requires(It it, size_t n) { requires is_same_v<decltype(it + n), It>; }
|
||||||
|
constexpr It next(It it, size_t count)
|
||||||
|
{
|
||||||
|
return it + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
constexpr It prev(It it, size_t count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
--it;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
requires requires(It it, size_t n) { requires is_same_v<decltype(it - n), It>; }
|
||||||
|
constexpr It prev(It it, size_t count)
|
||||||
|
{
|
||||||
|
return it - count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
constexpr size_t distance(It it1, It it2)
|
||||||
|
{
|
||||||
|
size_t dist = 0;
|
||||||
|
while (it1 != it2)
|
||||||
|
{
|
||||||
|
++it1;
|
||||||
|
++dist;
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
requires requires(It it1, It it2) { requires is_integral_v<decltype(it2 - it1)>; }
|
||||||
|
constexpr size_t distance(It it1, It it2)
|
||||||
|
{
|
||||||
|
return it2 - it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename Container, bool CONST>
|
||||||
|
class IteratorSimpleGeneral
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr IteratorSimpleGeneral() = default;
|
||||||
|
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||||
|
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||||
|
: m_pointer(other.m_pointer)
|
||||||
|
, m_valid(other.m_valid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T& operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral& operator++()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
++m_pointer;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorSimpleGeneral operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral& operator--()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
--m_pointer;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorSimpleGeneral operator--(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
--(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t operator-(const IteratorSimpleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this && other);
|
||||||
|
return m_pointer - other.m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral operator+(size_t offset) const
|
||||||
|
{
|
||||||
|
return IteratorSimpleGeneral(m_pointer + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral operator-(size_t offset) const
|
||||||
|
{
|
||||||
|
return IteratorSimpleGeneral(m_pointer - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator<(const IteratorSimpleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
return m_pointer < other.m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const IteratorSimpleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
return m_pointer == other.m_pointer;
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(const IteratorSimpleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||||
|
: m_pointer(pointer)
|
||||||
|
, m_valid(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
maybe_const_t<CONST, T>* m_pointer = nullptr;
|
||||||
|
bool m_valid = false;
|
||||||
|
|
||||||
|
friend IteratorSimpleGeneral<T, Container, !CONST>;
|
||||||
|
friend Container;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container, bool CONST>
|
||||||
|
class IteratorDoubleGeneral
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Inner = InnerContainer<T>;
|
||||||
|
using Outer = OuterContainer<Inner>;
|
||||||
|
|
||||||
|
using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>;
|
||||||
|
using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>;
|
||||||
|
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr IteratorDoubleGeneral() = default;
|
||||||
|
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||||
|
constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
|
||||||
|
: m_outer_end(other.m_outer_end)
|
||||||
|
, m_outer_current(other.m_outer_current)
|
||||||
|
, m_inner_current(other.m_inner_current)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T& operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator*();
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator*();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator->();
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorDoubleGeneral& operator++()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
m_inner_current++;
|
||||||
|
find_valid_or_end();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorDoubleGeneral operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const IteratorDoubleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this && other);
|
||||||
|
if (m_outer_end != other.m_outer_end)
|
||||||
|
return false;
|
||||||
|
if (m_outer_current != other.m_outer_current)
|
||||||
|
return false;
|
||||||
|
if (m_outer_current == m_outer_end)
|
||||||
|
return true;
|
||||||
|
ASSERT(m_inner_current && other.m_inner_current);
|
||||||
|
return m_inner_current == other.m_inner_current;
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(const IteratorDoubleGeneral& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const
|
||||||
|
{
|
||||||
|
return !!m_outer_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||||
|
: m_outer_end(outer_end)
|
||||||
|
, m_outer_current(outer_current)
|
||||||
|
{
|
||||||
|
if (outer_current != outer_end)
|
||||||
|
{
|
||||||
|
m_inner_current = m_outer_current->begin();
|
||||||
|
find_valid_or_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current, const InnerIterator& inner_current)
|
||||||
|
: m_outer_end(outer_end)
|
||||||
|
, m_outer_current(outer_current)
|
||||||
|
, m_inner_current(inner_current)
|
||||||
|
{
|
||||||
|
find_valid_or_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void find_valid_or_end()
|
||||||
|
{
|
||||||
|
while (m_inner_current == m_outer_current->end())
|
||||||
|
{
|
||||||
|
m_outer_current++;
|
||||||
|
if (m_outer_current == m_outer_end)
|
||||||
|
break;
|
||||||
|
m_inner_current = m_outer_current->begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr OuterIterator outer_current() { return m_outer_current; }
|
||||||
|
constexpr InnerIterator inner_current() { return m_inner_current; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
OuterIterator m_outer_end;
|
||||||
|
OuterIterator m_outer_current;
|
||||||
|
InnerIterator m_inner_current;
|
||||||
|
|
||||||
|
friend class IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, !CONST>;
|
||||||
|
friend Container;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename Container>
|
||||||
|
using IteratorSimple = IteratorSimpleGeneral<T, Container, false>;
|
||||||
|
|
||||||
|
template<typename T, typename Container>
|
||||||
|
using ConstIteratorSimple = IteratorSimpleGeneral<T, Container, true>;
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||||
|
using IteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, false>;
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||||
|
using ConstIteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, true>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class numeric_limits
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
numeric_limits() = delete;
|
||||||
|
|
||||||
|
static inline constexpr T max()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, char>)
|
||||||
|
return __SCHAR_MAX__;
|
||||||
|
if constexpr(is_same_v<T, signed char>)
|
||||||
|
return __SCHAR_MAX__;
|
||||||
|
if constexpr(is_same_v<T, unsigned char>)
|
||||||
|
return (T)__SCHAR_MAX__ * 2 + 1;
|
||||||
|
|
||||||
|
if constexpr(is_same_v<T, short>)
|
||||||
|
return __SHRT_MAX__;
|
||||||
|
if constexpr(is_same_v<T, int>)
|
||||||
|
return __INT_MAX__;
|
||||||
|
if constexpr(is_same_v<T, long>)
|
||||||
|
return __LONG_MAX__;
|
||||||
|
if constexpr(is_same_v<T, long long>)
|
||||||
|
return __LONG_LONG_MAX__;
|
||||||
|
|
||||||
|
if constexpr(is_same_v<T, unsigned short>)
|
||||||
|
return (T)__SHRT_MAX__ * 2 + 1;
|
||||||
|
if constexpr(is_same_v<T, unsigned int>)
|
||||||
|
return (T)__INT_MAX__ * 2 + 1;
|
||||||
|
if constexpr(is_same_v<T, unsigned long>)
|
||||||
|
return (T)__LONG_MAX__ * 2 + 1;
|
||||||
|
if constexpr(is_same_v<T, unsigned long long>)
|
||||||
|
return (T)__LONG_LONG_MAX__ * 2 + 1;
|
||||||
|
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MAX__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MAX__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MAX__;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr T min()
|
||||||
|
{
|
||||||
|
if constexpr(is_signed_v<T> && is_integral_v<T>)
|
||||||
|
return -max() - 1;
|
||||||
|
|
||||||
|
if constexpr(is_unsigned_v<T> && is_integral_v<T>)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MIN__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MIN__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MIN__;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr bool has_infinity()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_HAS_INFINITY__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_HAS_INFINITY__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_HAS_INFINITY__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr T infinity() requires(has_infinity())
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __builtin_inff();
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __builtin_inf();
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __builtin_infl();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr bool has_quiet_NaN()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_HAS_QUIET_NAN__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_HAS_QUIET_NAN__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_HAS_QUIET_NAN__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr T quiet_NaN() requires(has_quiet_NaN())
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __builtin_nanf("");
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __builtin_nan("");
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __builtin_nanl("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr int max_exponent2()
|
||||||
|
{
|
||||||
|
static_assert(__FLT_RADIX__ == 2);
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MAX_EXP__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MAX_EXP__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MAX_EXP__;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr int max_exponent10()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MAX_10_EXP__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MAX_10_EXP__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MAX_10_EXP__;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr int min_exponent2()
|
||||||
|
{
|
||||||
|
static_assert(__FLT_RADIX__ == 2);
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MIN_EXP__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MIN_EXP__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MIN_EXP__;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr int min_exponent10()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __FLT_MIN_10_EXP__;
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __DBL_MIN_10_EXP__;
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __LDBL_MIN_10_EXP__;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,426 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
class LinkedListIterator;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class LinkedList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = LinkedListIterator<T, false>;
|
||||||
|
using const_iterator = LinkedListIterator<T, true>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LinkedList() = default;
|
||||||
|
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
|
||||||
|
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
||||||
|
~LinkedList() { clear(); }
|
||||||
|
|
||||||
|
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
|
||||||
|
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||||
|
|
||||||
|
ErrorOr<void> push_back(const T&);
|
||||||
|
ErrorOr<void> push_back(T&&);
|
||||||
|
ErrorOr<void> insert(iterator, const T&);
|
||||||
|
ErrorOr<void> insert(iterator, T&&);
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
void pop_back();
|
||||||
|
iterator remove(iterator);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
iterator move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter);
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_data, empty()); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_data, empty()); }
|
||||||
|
iterator end() { return iterator(m_last, true); }
|
||||||
|
const_iterator end() const { return const_iterator(m_last, true); }
|
||||||
|
|
||||||
|
const T& back() const;
|
||||||
|
T& back();
|
||||||
|
const T& front() const;
|
||||||
|
T& front();
|
||||||
|
|
||||||
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
size_type size() const;
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
Node* next;
|
||||||
|
Node* prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<Node*> allocate_node(Args&&...) const;
|
||||||
|
|
||||||
|
Node* remove_node(iterator);
|
||||||
|
void insert_node(iterator, Node*);
|
||||||
|
|
||||||
|
Node* m_data = nullptr;
|
||||||
|
Node* m_last = nullptr;
|
||||||
|
size_type m_size = 0;
|
||||||
|
|
||||||
|
friend class LinkedListIterator<T, true>;
|
||||||
|
friend class LinkedListIterator<T, false>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
class LinkedListIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using data_type = maybe_const_t<CONST, typename LinkedList<T>::Node>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LinkedListIterator() = default;
|
||||||
|
template<bool C>
|
||||||
|
LinkedListIterator(const LinkedListIterator<T, C>&, enable_if_t<C == CONST || !C>* = 0);
|
||||||
|
|
||||||
|
LinkedListIterator<T, CONST>& operator++();
|
||||||
|
LinkedListIterator<T, CONST>& operator--();
|
||||||
|
LinkedListIterator<T, CONST> operator++(int);
|
||||||
|
LinkedListIterator<T, CONST> operator--(int);
|
||||||
|
|
||||||
|
template<bool ENABLE = !CONST>
|
||||||
|
enable_if_t<ENABLE, T&> operator*();
|
||||||
|
const T& operator*() const;
|
||||||
|
|
||||||
|
template<bool ENABLE = !CONST>
|
||||||
|
enable_if_t<ENABLE, T*> operator->();
|
||||||
|
const T* operator->() const;
|
||||||
|
|
||||||
|
bool operator==(const LinkedListIterator<T, CONST>&) const;
|
||||||
|
bool operator!=(const LinkedListIterator<T, CONST>&) const;
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LinkedListIterator(data_type*, bool);
|
||||||
|
|
||||||
|
private:
|
||||||
|
data_type* m_current = nullptr;
|
||||||
|
bool m_past_end = false;
|
||||||
|
|
||||||
|
friend class LinkedList<T>;
|
||||||
|
friend class LinkedListIterator<T, !CONST>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
for (const T& elem : other)
|
||||||
|
MUST(push_back(elem));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
LinkedList<T>& LinkedList<T>::operator=(LinkedList<T>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_last = other.m_last;
|
||||||
|
m_size = other.m_size;
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_last = nullptr;
|
||||||
|
other.m_size = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
LinkedList<T>::Node* LinkedList<T>::remove_node(iterator iter)
|
||||||
|
{
|
||||||
|
ASSERT(!empty() && iter);
|
||||||
|
Node* node = iter.m_current;
|
||||||
|
Node* prev = node->prev;
|
||||||
|
Node* next = node->next;
|
||||||
|
(prev ? prev->next : m_data) = next;
|
||||||
|
(next ? next->prev : m_last) = prev;
|
||||||
|
m_size--;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void LinkedList<T>::insert_node(iterator iter, Node* node)
|
||||||
|
{
|
||||||
|
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||||
|
Node* prev = next ? next->prev : m_last;
|
||||||
|
node->next = next;
|
||||||
|
node->prev = prev;
|
||||||
|
(prev ? prev->next : m_data) = node;
|
||||||
|
(next ? next->prev : m_last) = node;
|
||||||
|
m_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> LinkedList<T>::push_back(const T& value)
|
||||||
|
{
|
||||||
|
return push_back(move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> LinkedList<T>::push_back(T&& value)
|
||||||
|
{
|
||||||
|
return insert(end(), move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> LinkedList<T>::insert(iterator iter, const T& value)
|
||||||
|
{
|
||||||
|
return insert(iter, move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
|
||||||
|
{
|
||||||
|
Node* new_node = TRY(allocate_node(move(value)));
|
||||||
|
insert_node(iter, new_node);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
return emplace(end(), forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
|
||||||
|
insert_node(iter, new_node);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void LinkedList<T>::pop_back()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
remove(iterator(m_last, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||||
|
{
|
||||||
|
ASSERT(!empty() && iter);
|
||||||
|
Node* node = remove_node(iter);
|
||||||
|
Node* next = node->next;
|
||||||
|
node->value.~T();
|
||||||
|
BAN::deallocator(node);
|
||||||
|
return next ? iterator(next, false) : iterator(m_last, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void LinkedList<T>::clear()
|
||||||
|
{
|
||||||
|
Node* ptr = m_data;
|
||||||
|
while (ptr)
|
||||||
|
{
|
||||||
|
Node* next = ptr->next;
|
||||||
|
ptr->value.~T();
|
||||||
|
BAN::deallocator(ptr);
|
||||||
|
ptr = next;
|
||||||
|
}
|
||||||
|
m_data = nullptr;
|
||||||
|
m_last = nullptr;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
LinkedList<T>::iterator LinkedList<T>::move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter)
|
||||||
|
{
|
||||||
|
ASSERT(!empty() && src_iter);
|
||||||
|
Node* node = remove_node(src_iter);
|
||||||
|
iterator ret = node->next ? iterator(node->next, false) : iterator(m_last, true);
|
||||||
|
dest_list.insert_node(dest_iter, node);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& LinkedList<T>::back() const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *const_iterator(m_last, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& LinkedList<T>::back()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *iterator(m_last, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& LinkedList<T>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *const_iterator(m_data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& LinkedList<T>::front()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *iterator(m_data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool LinkedList<T>::contains(const T& value) const
|
||||||
|
{
|
||||||
|
if (empty()) return false;
|
||||||
|
for (Node* node = m_data;; node = node->next)
|
||||||
|
{
|
||||||
|
if (node->value == value)
|
||||||
|
return true;
|
||||||
|
if (node == m_last)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename LinkedList<T>::size_type LinkedList<T>::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool LinkedList<T>::empty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
|
||||||
|
{
|
||||||
|
Node* node = (Node*)BAN::allocator(sizeof(Node));
|
||||||
|
if (node == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
new (&node->value) T(forward<Args>(args)...);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
template<bool C>
|
||||||
|
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
||||||
|
: m_current(other.m_current)
|
||||||
|
, m_past_end(other.m_past_end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST>::LinkedListIterator(data_type* node, bool past_end)
|
||||||
|
: m_current(node)
|
||||||
|
, m_past_end(past_end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST>& LinkedListIterator<T, CONST>::operator++()
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
ASSERT(m_current->next || !m_past_end);
|
||||||
|
if (m_current->next)
|
||||||
|
m_current = m_current->next;
|
||||||
|
else
|
||||||
|
m_past_end = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST>& LinkedListIterator<T, CONST>::operator--()
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
ASSERT(m_current->prev || m_past_end);
|
||||||
|
if (m_past_end)
|
||||||
|
m_past_end = false;
|
||||||
|
else
|
||||||
|
m_current = m_current->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST> LinkedListIterator<T, CONST>::operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST> LinkedListIterator<T, CONST>::operator--(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
--(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
template<bool ENABLE>
|
||||||
|
enable_if_t<ENABLE, T&> LinkedListIterator<T, CONST>::operator*()
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
return m_current->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
const T& LinkedListIterator<T, CONST>::operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
return m_current->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
template<bool ENABLE>
|
||||||
|
enable_if_t<ENABLE, T*> LinkedListIterator<T, CONST>::operator->()
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
return &m_current->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
const T* LinkedListIterator<T, CONST>::operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(m_current);
|
||||||
|
return &m_current->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
|
||||||
|
{
|
||||||
|
if (m_current != other.m_current)
|
||||||
|
return false;
|
||||||
|
return m_past_end == other.m_past_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
bool LinkedListIterator<T, CONST>::operator!=(const LinkedListIterator<T, CONST>& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, bool CONST>
|
||||||
|
LinkedListIterator<T, CONST>::operator bool() const
|
||||||
|
{
|
||||||
|
return m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
struct MACAddress
|
||||||
|
{
|
||||||
|
uint8_t address[6];
|
||||||
|
|
||||||
|
constexpr bool operator==(const MACAddress& other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
address[0] == other.address[0] &&
|
||||||
|
address[1] == other.address[1] &&
|
||||||
|
address[2] == other.address[2] &&
|
||||||
|
address[3] == other.address[3] &&
|
||||||
|
address[4] == other.address[4] &&
|
||||||
|
address[5] == other.address[5];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const MACAddress& mac, const ValueFormat&)
|
||||||
|
{
|
||||||
|
ValueFormat format {
|
||||||
|
.base = 16,
|
||||||
|
.percision = 0,
|
||||||
|
.fill = 2,
|
||||||
|
.upper = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
print_argument(putc, mac.address[0], format);
|
||||||
|
for (size_t i = 1; i < 6; i++)
|
||||||
|
{
|
||||||
|
putc(':');
|
||||||
|
print_argument(putc, mac.address[i], format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,443 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Limits.h>
|
||||||
|
#include <BAN/Numbers.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
namespace BAN::Math
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T abs(T x)
|
||||||
|
{
|
||||||
|
return x < 0 ? -x : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T min(T a, T b)
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T max(T a, T b)
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T clamp(T x, T min, T max)
|
||||||
|
{
|
||||||
|
return x < min ? min : x > max ? max : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
inline constexpr T gcd(T a, T b)
|
||||||
|
{
|
||||||
|
T t;
|
||||||
|
while (b)
|
||||||
|
{
|
||||||
|
t = b;
|
||||||
|
b = a % b;
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
inline constexpr T lcm(T a, T b)
|
||||||
|
{
|
||||||
|
return a / gcd(a, b) * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
inline constexpr T div_round_up(T a, T b)
|
||||||
|
{
|
||||||
|
return (a + b - 1) / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
inline constexpr bool is_power_of_two(T x)
|
||||||
|
{
|
||||||
|
if (x == 0)
|
||||||
|
return false;
|
||||||
|
return (x & (x - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<BAN::integral T>
|
||||||
|
static constexpr bool will_multiplication_overflow(T a, T b)
|
||||||
|
{
|
||||||
|
if (a == 0 || b == 0)
|
||||||
|
return false;
|
||||||
|
if ((a > 0) == (b > 0))
|
||||||
|
return a > BAN::numeric_limits<T>::max() / b;
|
||||||
|
else
|
||||||
|
return a < BAN::numeric_limits<T>::min() / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<BAN::integral T>
|
||||||
|
static constexpr bool will_addition_overflow(T a, T b)
|
||||||
|
{
|
||||||
|
if (a > 0 && b > 0)
|
||||||
|
return a > BAN::numeric_limits<T>::max() - b;
|
||||||
|
if (a < 0 && b < 0)
|
||||||
|
return a < BAN::numeric_limits<T>::min() - b;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
|
||||||
|
inline constexpr T ilog2(T x)
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, unsigned int>)
|
||||||
|
return sizeof(T) * 8 - __builtin_clz(x) - 1;
|
||||||
|
if constexpr(is_same_v<T, unsigned long>)
|
||||||
|
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
|
||||||
|
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T floor(T x)
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __builtin_floorf(x);
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __builtin_floor(x);
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __builtin_floorl(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T ceil(T x)
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __builtin_ceilf(x);
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __builtin_ceil(x);
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __builtin_ceill(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T round(T x)
|
||||||
|
{
|
||||||
|
if (x == (T)0.0)
|
||||||
|
return x;
|
||||||
|
if (x > (T)0.0)
|
||||||
|
return floor<T>(x + (T)0.5);
|
||||||
|
return ceil<T>(x - (T)0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T trunc(T x)
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, float>)
|
||||||
|
return __builtin_truncf(x);
|
||||||
|
if constexpr(is_same_v<T, double>)
|
||||||
|
return __builtin_trunc(x);
|
||||||
|
if constexpr(is_same_v<T, long double>)
|
||||||
|
return __builtin_truncl(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T rint(T x)
|
||||||
|
{
|
||||||
|
asm("frndint" : "+t"(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T fmod(T a, T b)
|
||||||
|
{
|
||||||
|
asm(
|
||||||
|
"1:"
|
||||||
|
"fprem;"
|
||||||
|
"fnstsw %%ax;"
|
||||||
|
"testb $4, %%ah;"
|
||||||
|
"jne 1b;"
|
||||||
|
: "+t"(a)
|
||||||
|
: "u"(b)
|
||||||
|
: "ax"
|
||||||
|
);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
static T modf(T x, T* iptr)
|
||||||
|
{
|
||||||
|
const T frac = BAN::Math::fmod<T>(x, 1);
|
||||||
|
*iptr = x - frac;
|
||||||
|
return frac;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T frexp(T num, int* exp)
|
||||||
|
{
|
||||||
|
if (num == 0.0)
|
||||||
|
{
|
||||||
|
*exp = 0;
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T _exp;
|
||||||
|
asm("fxtract" : "+t"(num), "=u"(_exp));
|
||||||
|
*exp = (int)_exp + 1;
|
||||||
|
return num / (T)2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T copysign(T x, T y)
|
||||||
|
{
|
||||||
|
if ((x < (T)0.0) != (y < (T)0.0))
|
||||||
|
x = -x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T fyl2x(T x, T y)
|
||||||
|
{
|
||||||
|
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T log(T x)
|
||||||
|
{
|
||||||
|
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T log2(T x)
|
||||||
|
{
|
||||||
|
return detail::fyl2x<T>(x, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T log10(T x)
|
||||||
|
{
|
||||||
|
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T logb(T x)
|
||||||
|
{
|
||||||
|
static_assert(FLT_RADIX == 2);
|
||||||
|
return log2<T>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T exp2(T x)
|
||||||
|
{
|
||||||
|
if (abs(x) <= (T)1.0)
|
||||||
|
{
|
||||||
|
asm("f2xm1" : "+t"(x));
|
||||||
|
return x + (T)1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
asm(
|
||||||
|
"fld1;"
|
||||||
|
"fld %%st(1);"
|
||||||
|
"fprem;"
|
||||||
|
"f2xm1;"
|
||||||
|
"faddp;"
|
||||||
|
"fscale;"
|
||||||
|
"fstp %%st(1);"
|
||||||
|
: "+t"(x)
|
||||||
|
);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T exp(T x)
|
||||||
|
{
|
||||||
|
return exp2<T>(x * numbers::log2e_v<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T pow(T x, T y)
|
||||||
|
{
|
||||||
|
asm(
|
||||||
|
"fyl2x;"
|
||||||
|
"fld1;"
|
||||||
|
"fld %%st(1);"
|
||||||
|
"fprem;"
|
||||||
|
"f2xm1;"
|
||||||
|
"faddp;"
|
||||||
|
"fscale;"
|
||||||
|
: "+t"(x), "+u"(y)
|
||||||
|
);
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T scalbn(T x, int n)
|
||||||
|
{
|
||||||
|
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T ldexp(T x, int y)
|
||||||
|
{
|
||||||
|
const bool exp_sign = y < 0;
|
||||||
|
if (exp_sign)
|
||||||
|
y = -y;
|
||||||
|
|
||||||
|
T exp = (T)1.0;
|
||||||
|
T mult = (T)2.0;
|
||||||
|
while (y)
|
||||||
|
{
|
||||||
|
if (y & 1)
|
||||||
|
exp *= mult;
|
||||||
|
mult *= mult;
|
||||||
|
y >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exp_sign)
|
||||||
|
exp = (T)1.0 / exp;
|
||||||
|
|
||||||
|
return x * exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T sqrt(T x)
|
||||||
|
{
|
||||||
|
asm("fsqrt" : "+t"(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T cbrt(T value)
|
||||||
|
{
|
||||||
|
if (value == 0.0)
|
||||||
|
return 0.0;
|
||||||
|
return pow<T>(value, 1.0 / 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T sin(T x)
|
||||||
|
{
|
||||||
|
asm("fsin" : "+t"(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T cos(T x)
|
||||||
|
{
|
||||||
|
asm("fcos" : "+t"(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr void sincos(T x, T& sin, T& cos)
|
||||||
|
{
|
||||||
|
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T tan(T x)
|
||||||
|
{
|
||||||
|
T one, ret;
|
||||||
|
asm(
|
||||||
|
"fptan"
|
||||||
|
: "=t"(one), "=u"(ret)
|
||||||
|
: "0"(x)
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T atan2(T y, T x)
|
||||||
|
{
|
||||||
|
asm(
|
||||||
|
"fpatan"
|
||||||
|
: "+t"(x)
|
||||||
|
: "u"(y)
|
||||||
|
: "st(1)"
|
||||||
|
);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T atan(T x)
|
||||||
|
{
|
||||||
|
return atan2<T>(x, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T asin(T x)
|
||||||
|
{
|
||||||
|
if (x == (T)0.0)
|
||||||
|
return (T)0.0;
|
||||||
|
if (x == (T)1.0)
|
||||||
|
return numbers::pi_v<T> / (T)2.0;
|
||||||
|
if (x == (T)-1.0)
|
||||||
|
return -numbers::pi_v<T> / (T)2.0;
|
||||||
|
return (T)2.0 * atan<T>(x / (T(1.0) + sqrt<T>((T)1.0 - x * x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T acos(T x)
|
||||||
|
{
|
||||||
|
if (x == (T)0.0)
|
||||||
|
return numbers::pi_v<T> / (T)2.0;
|
||||||
|
if (x == (T)1.0)
|
||||||
|
return (T)0.0;
|
||||||
|
if (x == (T)-1.0)
|
||||||
|
return numbers::pi_v<T>;
|
||||||
|
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T sinh(T x)
|
||||||
|
{
|
||||||
|
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T cosh(T x)
|
||||||
|
{
|
||||||
|
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T tanh(T x)
|
||||||
|
{
|
||||||
|
const T exp_px = exp<T>(x);
|
||||||
|
const T exp_nx = exp<T>(-x);
|
||||||
|
return (exp_px - exp_nx) / (exp_px + exp_nx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T asinh(T x)
|
||||||
|
{
|
||||||
|
return log<T>(x + sqrt<T>(x * x + (T)1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T acosh(T x)
|
||||||
|
{
|
||||||
|
return log<T>(x + sqrt<T>(x * x - (T)1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T atanh(T x)
|
||||||
|
{
|
||||||
|
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T hypot(T x, T y)
|
||||||
|
{
|
||||||
|
return sqrt<T>(x * x + y * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr remove_reference_t<T>&& move(T&& arg)
|
||||||
|
{
|
||||||
|
return static_cast<remove_reference_t<T>&&>(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T&& forward(remove_reference_t<T>& arg)
|
||||||
|
{
|
||||||
|
return static_cast<T&&>(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T&& forward(remove_reference_t<T>&& arg)
|
||||||
|
{
|
||||||
|
static_assert(!is_lvalue_reference_v<T>);
|
||||||
|
return static_cast<T&&>(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__is_kernel)
|
||||||
|
#include <kernel/Memory/kmalloc.h>
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
#if defined(__is_kernel)
|
||||||
|
static constexpr void*(&allocator)(size_t) = kmalloc;
|
||||||
|
static constexpr void(&deallocator)(void*) = kfree;
|
||||||
|
#else
|
||||||
|
static constexpr void*(&allocator)(size_t) = malloc;
|
||||||
|
static constexpr void(&deallocator)(void*) = free;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define BAN_NON_COPYABLE(class) \
|
||||||
|
private: \
|
||||||
|
class(const class&) = delete; \
|
||||||
|
class& operator=(const class&) = delete
|
||||||
|
|
||||||
|
#define BAN_NON_MOVABLE(class) \
|
||||||
|
private: \
|
||||||
|
class(class&&) = delete; \
|
||||||
|
class& operator=(class&&) = delete
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
namespace BAN::numbers
|
||||||
|
{
|
||||||
|
|
||||||
|
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
|
||||||
|
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
|
||||||
|
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
|
||||||
|
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
|
||||||
|
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
|
||||||
|
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
|
||||||
|
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
|
||||||
|
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
|
||||||
|
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
|
||||||
|
|
||||||
|
inline constexpr double e = e_v<double>;
|
||||||
|
inline constexpr double log2e = log2e_v<double>;
|
||||||
|
inline constexpr double lge = lge_v<double>;
|
||||||
|
inline constexpr double lg2 = lge_v<double>;
|
||||||
|
inline constexpr double ln2 = ln2_v<double>;
|
||||||
|
inline constexpr double ln10 = ln10_v<double>;
|
||||||
|
inline constexpr double pi = pi_v<double>;
|
||||||
|
inline constexpr double sqrt2 = sqrt2_v<double>;
|
||||||
|
inline constexpr double sqrt3 = sqrt3_v<double>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Optional
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr Optional();
|
||||||
|
constexpr Optional(Optional&&);
|
||||||
|
constexpr Optional(const Optional&);
|
||||||
|
constexpr Optional(const T&);
|
||||||
|
constexpr Optional(T&&);
|
||||||
|
|
||||||
|
~Optional();
|
||||||
|
|
||||||
|
constexpr Optional& operator=(Optional&&);
|
||||||
|
constexpr Optional& operator=(const Optional&);
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
constexpr T* operator->();
|
||||||
|
constexpr const T* operator->() const;
|
||||||
|
|
||||||
|
constexpr T& operator*();
|
||||||
|
constexpr const T& operator*() const;
|
||||||
|
|
||||||
|
constexpr bool has_value() const;
|
||||||
|
|
||||||
|
constexpr T release_value();
|
||||||
|
constexpr T& value();
|
||||||
|
constexpr const T& value() const;
|
||||||
|
constexpr T& value_or(T&);
|
||||||
|
constexpr const T& value_or(const T&) const;
|
||||||
|
|
||||||
|
constexpr void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
alignas(T) uint8_t m_storage[sizeof(T)] {};
|
||||||
|
bool m_has_value { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional()
|
||||||
|
: m_has_value(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional(Optional<T>&& other)
|
||||||
|
: m_has_value(other.has_value())
|
||||||
|
{
|
||||||
|
if (other.has_value())
|
||||||
|
new (m_storage) T(move(other.release_value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional(const Optional<T>& other)
|
||||||
|
: m_has_value(other.has_value())
|
||||||
|
{
|
||||||
|
if (other.has_value())
|
||||||
|
new (m_storage) T(other.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional(const T& value)
|
||||||
|
: m_has_value(true)
|
||||||
|
{
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional(T&& value)
|
||||||
|
: m_has_value(true)
|
||||||
|
{
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Optional<T>::~Optional()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = other.has_value();
|
||||||
|
if (other.has_value())
|
||||||
|
new (m_storage) T(move(other.release_value()));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = other.has_value();
|
||||||
|
if (other.has_value())
|
||||||
|
new (m_storage) T(other.value());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = true;
|
||||||
|
new (m_storage) T(forward<Args>(args)...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T* Optional<T>::operator->()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T* Optional<T>::operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T& Optional<T>::operator*()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& Optional<T>::operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool Optional<T>::has_value() const
|
||||||
|
{
|
||||||
|
return m_has_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T Optional<T>::release_value()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
T released_value = move(value());
|
||||||
|
value().~T();
|
||||||
|
m_has_value = false;
|
||||||
|
return move(released_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T& Optional<T>::value()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return *reinterpret_cast<T*>(&m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& Optional<T>::value() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return *reinterpret_cast<const T*>(&m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T& Optional<T>::value_or(T& empty)
|
||||||
|
{
|
||||||
|
if (!has_value())
|
||||||
|
return empty;
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& Optional<T>::value_or(const T& empty) const
|
||||||
|
{
|
||||||
|
if (!has_value())
|
||||||
|
return empty;
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr void Optional<T>::clear()
|
||||||
|
{
|
||||||
|
if (m_has_value)
|
||||||
|
value().~T();
|
||||||
|
m_has_value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if __has_include(<new>)
|
||||||
|
#include <new>
|
||||||
|
#else
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
inline void* operator new(size_t, void* addr) { return addr; }
|
||||||
|
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BAN/Errors.h"
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
#include <BAN/Heap.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, typename Comp = less<T>>
|
||||||
|
class PriorityQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PriorityQueue() = default;
|
||||||
|
PriorityQueue(Comp comp)
|
||||||
|
: m_comp(comp)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ErrorOr<void> push(const T& value)
|
||||||
|
{
|
||||||
|
TRY(m_data.push_back(value));
|
||||||
|
push_heap(m_data.begin(), m_data.end());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> push(T&& value)
|
||||||
|
{
|
||||||
|
TRY(m_data.push_back(move(value)));
|
||||||
|
push_heap(m_data.begin(), m_data.end());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
TRY(m_data.emplace_back(forward<Args>(args)...));
|
||||||
|
push_heap(m_data.begin(), m_data.end());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
pop_heap(m_data.begin(), m_data.end());
|
||||||
|
m_data.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> reserve(Vector<T>::size_type size)
|
||||||
|
{
|
||||||
|
return m_data.reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& top() { return m_data.front(); }
|
||||||
|
const T& top() const { return m_data.front(); }
|
||||||
|
|
||||||
|
bool empty() const { return m_data.empty(); }
|
||||||
|
Vector<T>::size_type size() const { return m_data.size(); }
|
||||||
|
Vector<T>::size_type capacity() const { return m_data.capacity(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Comp m_comp;
|
||||||
|
Vector<T> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Queue>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Queue>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Queue() = default;
|
||||||
|
Queue(Queue<T>&&);
|
||||||
|
Queue(const Queue<T>&);
|
||||||
|
~Queue();
|
||||||
|
|
||||||
|
Queue<T>& operator=(Queue<T>&&);
|
||||||
|
Queue<T>& operator=(const Queue<T>&);
|
||||||
|
|
||||||
|
ErrorOr<void> push(T&&);
|
||||||
|
ErrorOr<void> push(const T&);
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
|
||||||
|
ErrorOr<void> reserve(size_type);
|
||||||
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_data); }
|
||||||
|
iterator end() { return iterator(m_data + m_size); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
|
void pop();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
size_type capacity() const;
|
||||||
|
size_type size() const;
|
||||||
|
|
||||||
|
const T& front() const;
|
||||||
|
T& front();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> ensure_capacity(size_type size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_data = nullptr;
|
||||||
|
size_type m_capacity = 0;
|
||||||
|
size_type m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Queue<T>::Queue(Queue<T>&& other)
|
||||||
|
{
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_capacity = other.m_capacity;
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_capacity = 0;
|
||||||
|
other.m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Queue<T>::Queue(const Queue<T>& other)
|
||||||
|
{
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
for (size_type i = 0; i < other.size(); i++)
|
||||||
|
new (m_data + i) T(other.m_data[i]);
|
||||||
|
m_size = other.m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Queue<T>::~Queue()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Queue<T>& Queue<T>::operator=(Queue<T>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_capacity = other.m_capacity;
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_capacity = 0;
|
||||||
|
other.m_size = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Queue<T>& Queue<T>::operator=(const Queue<T>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
for (size_type i = 0; i < other.size(); i++)
|
||||||
|
new (m_data + i) T(other.m_data[i]);
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Queue<T>::push(T&& value)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
new (m_data + m_size) T(move(value));
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Queue<T>::push(const T& value)
|
||||||
|
{
|
||||||
|
return push(move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
new (m_data + m_size) T(forward<Args>(args)...);
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Queue<T>::reserve(size_type size)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(size));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Queue<T>::shrink_to_fit()
|
||||||
|
{
|
||||||
|
size_type temp = m_capacity;
|
||||||
|
m_capacity = 0;
|
||||||
|
auto error_or = ensure_capacity(m_size);
|
||||||
|
if (error_or.is_error())
|
||||||
|
{
|
||||||
|
m_capacity = temp;
|
||||||
|
return error_or;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Queue<T>::pop()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
for (size_type i = 0; i < m_size - 1; i++)
|
||||||
|
m_data[i] = move(m_data[i + 1]);
|
||||||
|
m_data[m_size - 1].~T();
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Queue<T>::clear()
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
m_data[i].~T();
|
||||||
|
BAN::deallocator(m_data);
|
||||||
|
m_data = nullptr;
|
||||||
|
m_capacity = 0;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool Queue<T>::empty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename Queue<T>::size_type Queue<T>::capacity() const
|
||||||
|
{
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename Queue<T>::size_type Queue<T>::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& Queue<T>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& Queue<T>::front()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Queue<T>::ensure_capacity(size_type size)
|
||||||
|
{
|
||||||
|
if (m_capacity > size)
|
||||||
|
return {};
|
||||||
|
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||||
|
T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
|
||||||
|
if (new_data == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
new (new_data + i) T(move(m_data[i]));
|
||||||
|
m_data[i].~T();
|
||||||
|
}
|
||||||
|
BAN::deallocator(m_data);
|
||||||
|
m_data = new_data;
|
||||||
|
m_capacity = new_cap;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Atomic.h>
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class RefCounted
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(RefCounted);
|
||||||
|
BAN_NON_MOVABLE(RefCounted);
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t ref_count() const
|
||||||
|
{
|
||||||
|
return m_ref_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ref() const
|
||||||
|
{
|
||||||
|
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed);
|
||||||
|
ASSERT(old > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_ref() const
|
||||||
|
{
|
||||||
|
uint32_t expected = m_ref_count.load(MemoryOrder::memory_order_relaxed);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (expected == 0)
|
||||||
|
return false;
|
||||||
|
if (m_ref_count.compare_exchange(expected, expected + 1, MemoryOrder::memory_order_acquire))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unref() const
|
||||||
|
{
|
||||||
|
uint32_t old = m_ref_count.fetch_sub(1);
|
||||||
|
ASSERT(old > 0);
|
||||||
|
if (old == 1)
|
||||||
|
delete static_cast<const T*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RefCounted() = default;
|
||||||
|
virtual ~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Atomic<uint32_t> m_ref_count = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class RefPtr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RefPtr() = default;
|
||||||
|
RefPtr(T* pointer)
|
||||||
|
{
|
||||||
|
m_pointer = pointer;
|
||||||
|
if (m_pointer)
|
||||||
|
m_pointer->ref();
|
||||||
|
}
|
||||||
|
~RefPtr() { clear(); }
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
static RefPtr adopt(U* pointer)
|
||||||
|
{
|
||||||
|
RefPtr ptr;
|
||||||
|
ptr.m_pointer = pointer;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
|
||||||
|
template<typename... Args>
|
||||||
|
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||||
|
{
|
||||||
|
T* pointer = new T(forward<Args>(args)...);
|
||||||
|
if (pointer == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
return adopt(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr(const RefPtr& other) { *this = other; }
|
||||||
|
RefPtr(RefPtr&& other) { *this = move(other); }
|
||||||
|
template<typename U>
|
||||||
|
RefPtr(const RefPtr<U>& other) { *this = other; }
|
||||||
|
template<typename U>
|
||||||
|
RefPtr(RefPtr<U>&& other) { *this = move(other); }
|
||||||
|
|
||||||
|
RefPtr& operator=(const RefPtr& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
if (m_pointer)
|
||||||
|
m_pointer->ref();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr& operator=(RefPtr&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
RefPtr& operator=(const RefPtr<U>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
if (m_pointer)
|
||||||
|
m_pointer->ref();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
RefPtr& operator=(RefPtr<U>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
||||||
|
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
||||||
|
|
||||||
|
T& operator*() { return *ptr(); }
|
||||||
|
const T& operator*() const { return *ptr(); }
|
||||||
|
|
||||||
|
T* operator->() { return ptr(); }
|
||||||
|
const T* operator->() const { return ptr(); }
|
||||||
|
|
||||||
|
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
|
||||||
|
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
|
||||||
|
|
||||||
|
bool empty() const { return m_pointer == nullptr; }
|
||||||
|
explicit operator bool() const { return m_pointer; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_pointer)
|
||||||
|
m_pointer->unref();
|
||||||
|
m_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_pointer = nullptr;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
friend class RefPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Function.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
class ScopeGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopeGuard(const BAN::Function<void()>& func)
|
||||||
|
: m_func(func)
|
||||||
|
{ }
|
||||||
|
~ScopeGuard()
|
||||||
|
{
|
||||||
|
if (m_enabled)
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
BAN::Function<void()> m_func;
|
||||||
|
bool m_enabled { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Heap.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Swap.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace BAN::sort
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void exchange_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
for (It lhs = begin; lhs != end; ++lhs)
|
||||||
|
for (It rhs = next(lhs, 1); rhs != end; ++rhs)
|
||||||
|
if (!comp(*lhs, *rhs))
|
||||||
|
swap(*lhs, *rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
It partition(It begin, It end, Comp comp)
|
||||||
|
{
|
||||||
|
It pivot = prev(end, 1);
|
||||||
|
|
||||||
|
It it1 = begin;
|
||||||
|
for (It it2 = begin; it2 != pivot; ++it2)
|
||||||
|
{
|
||||||
|
if (comp(*it2, *pivot))
|
||||||
|
{
|
||||||
|
swap(*it1, *it2);
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
swap(*it1, *pivot);
|
||||||
|
|
||||||
|
return it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void quick_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
if (distance(begin, end) <= 1)
|
||||||
|
return;
|
||||||
|
It mid = detail::partition(begin, end, comp);
|
||||||
|
quick_sort(begin, mid, comp);
|
||||||
|
quick_sort(++mid, end, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void insertion_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
if (distance(begin, end) <= 1)
|
||||||
|
return;
|
||||||
|
for (It it1 = next(begin, 1); it1 != end; ++it1)
|
||||||
|
{
|
||||||
|
auto x = move(*it1);
|
||||||
|
It it2 = it1;
|
||||||
|
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
|
||||||
|
*it2 = move(*prev(it2, 1));
|
||||||
|
*it2 = move(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void heap_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
make_heap(begin, end, comp);
|
||||||
|
sort_heap(begin, end, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
void intro_sort_impl(It begin, It end, size_t max_depth, Comp comp)
|
||||||
|
{
|
||||||
|
if (distance(begin, end) <= 16)
|
||||||
|
return insertion_sort(begin, end, comp);
|
||||||
|
if (max_depth == 0)
|
||||||
|
return heap_sort(begin, end, comp);
|
||||||
|
It mid = detail::partition(begin, end, comp);
|
||||||
|
intro_sort_impl(begin, mid, max_depth - 1, comp);
|
||||||
|
intro_sort_impl(++mid, end, max_depth - 1, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void intro_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
if (len <= 1)
|
||||||
|
return;
|
||||||
|
detail::intro_sort_impl(begin, end, 2 * Math::ilog2(len), comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<unsigned_integral T>
|
||||||
|
consteval T lsb_index(T value)
|
||||||
|
{
|
||||||
|
for (T result = 0;; result++)
|
||||||
|
if (value & (1 << result))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, size_t radix = 256>
|
||||||
|
requires is_unsigned_v<it_value_type_t<It>> && (radix > 0 && (radix & (radix - 1)) == 0)
|
||||||
|
BAN::ErrorOr<void> radix_sort(It begin, It end)
|
||||||
|
{
|
||||||
|
using value_type = it_value_type_t<It>;
|
||||||
|
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
if (len <= 1)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
Vector<value_type> temp;
|
||||||
|
TRY(temp.resize(len));
|
||||||
|
|
||||||
|
Vector<size_t> counts;
|
||||||
|
TRY(counts.resize(radix));
|
||||||
|
|
||||||
|
constexpr size_t mask = radix - 1;
|
||||||
|
constexpr size_t shift = detail::lsb_index(radix);
|
||||||
|
|
||||||
|
for (size_t s = 0; s < sizeof(value_type) * 8; s += shift)
|
||||||
|
{
|
||||||
|
for (auto& cnt : counts)
|
||||||
|
cnt = 0;
|
||||||
|
for (It it = begin; it != end; ++it)
|
||||||
|
counts[(*it >> s) & mask]++;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < radix - 1; i++)
|
||||||
|
counts[i + 1] += counts[i];
|
||||||
|
|
||||||
|
for (It it = end; it != begin;)
|
||||||
|
{
|
||||||
|
--it;
|
||||||
|
temp[--counts[(*it >> s) & mask]] = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < temp.size(); j++)
|
||||||
|
*next(begin, j) = temp[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||||
|
void sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
return intro_sort(begin, end, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Span
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorSimple<value_type, Span>;
|
||||||
|
using const_iterator = ConstIteratorSimple<value_type, Span>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename S>
|
||||||
|
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Span() = default;
|
||||||
|
Span(value_type* data, size_type size)
|
||||||
|
: m_data(data)
|
||||||
|
, m_size(size)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
Span(const Span<S>& other) requires can_init_from_v<S>
|
||||||
|
: m_data(other.m_data)
|
||||||
|
, m_size(other.m_size)
|
||||||
|
{ }
|
||||||
|
template<typename S>
|
||||||
|
Span(Span<S>&& other) requires can_init_from_v<S>
|
||||||
|
: m_data(other.m_data)
|
||||||
|
, m_size(other.m_size)
|
||||||
|
{
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
|
||||||
|
{
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template<typename S>
|
||||||
|
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
|
||||||
|
{
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_data); }
|
||||||
|
iterator end() { return iterator(m_data + m_size); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
|
value_type& operator[](size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type* data() const
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const { return m_size == 0; }
|
||||||
|
size_type size() const { return m_size; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_data = nullptr;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span slice(size_type start, size_type length = ~size_type(0)) const
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
ASSERT(start <= m_size);
|
||||||
|
if (length == ~size_type(0))
|
||||||
|
length = m_size - start;
|
||||||
|
ASSERT(m_size - start >= length);
|
||||||
|
return Span(m_data + start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const value_type> as_const() const { return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type* m_data = nullptr;
|
||||||
|
size_type m_size = 0;
|
||||||
|
|
||||||
|
friend class Span<const value_type>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,361 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
class String
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorSimple<char, String>;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, String>;
|
||||||
|
static constexpr size_type sso_capacity = 15;
|
||||||
|
|
||||||
|
public:
|
||||||
|
String() {}
|
||||||
|
String(const String& other) { *this = other; }
|
||||||
|
String(String&& other) { *this = move(other); }
|
||||||
|
String(StringView other) { *this = other; }
|
||||||
|
~String() { clear(); }
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static BAN::ErrorOr<String> formatted(const char* format, Args&&... args)
|
||||||
|
{
|
||||||
|
size_type length = 0;
|
||||||
|
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
|
||||||
|
|
||||||
|
String result;
|
||||||
|
TRY(result.reserve(length));
|
||||||
|
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String& operator=(const String& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
memcpy(data(), other.data(), other.size() + 1);
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String& operator=(String&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (other.has_sso())
|
||||||
|
memcpy(data(), other.data(), other.size() + 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_storage.general_storage = other.m_storage.general_storage;
|
||||||
|
m_has_sso = false;
|
||||||
|
}
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_size = 0;
|
||||||
|
other.m_storage.sso_storage = SSOStorage();
|
||||||
|
other.m_has_sso = true;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String& operator=(StringView other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
memcpy(data(), other.data(), other.size());
|
||||||
|
m_size = other.size();
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> push_back(char c)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
data()[m_size] = c;
|
||||||
|
m_size++;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> insert(char c, size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index <= m_size);
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
memmove(data() + index + 1, data() + index, m_size - index);
|
||||||
|
data()[index] = c;
|
||||||
|
m_size++;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> insert(StringView str, size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index <= m_size);
|
||||||
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
|
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||||
|
memcpy(data() + index, str.data(), str.size());
|
||||||
|
m_size += str.size();
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> append(StringView str)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
|
memcpy(data() + m_size, str.data(), str.size());
|
||||||
|
m_size += str.size();
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
m_size--;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
memmove(data() + index, data() + index + 1, m_size - index);
|
||||||
|
m_size--;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (!has_sso())
|
||||||
|
{
|
||||||
|
deallocator(m_storage.general_storage.data);
|
||||||
|
m_storage.sso_storage = SSOStorage();
|
||||||
|
m_has_sso = true;
|
||||||
|
}
|
||||||
|
m_size = 0;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const { return const_iterator(data()); }
|
||||||
|
iterator begin() { return iterator(data()); }
|
||||||
|
const_iterator end() const { return const_iterator(data() + size()); }
|
||||||
|
iterator end() { return iterator(data() + size()); }
|
||||||
|
|
||||||
|
char front() const { ASSERT(m_size > 0); return data()[0]; }
|
||||||
|
char& front() { ASSERT(m_size > 0); return data()[0]; }
|
||||||
|
|
||||||
|
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
|
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
|
|
||||||
|
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||||
|
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||||
|
|
||||||
|
bool operator==(const String& str) const
|
||||||
|
{
|
||||||
|
if (size() != str.size())
|
||||||
|
return false;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (data()[i] != str.data()[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(StringView str) const
|
||||||
|
{
|
||||||
|
if (size() != str.size())
|
||||||
|
return false;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (data()[i] != str.data()[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const char* cstr) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (data()[i] != cstr[i])
|
||||||
|
return false;
|
||||||
|
if (cstr[size()] != '\0')
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> resize(size_type new_size, char init_c = '\0')
|
||||||
|
{
|
||||||
|
if (m_size == new_size)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// expanding
|
||||||
|
if (m_size < new_size)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(new_size));
|
||||||
|
memset(data() + m_size, init_c, new_size - m_size);
|
||||||
|
m_size = new_size;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_size = new_size;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> reserve(size_type new_size)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(new_size));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> shrink_to_fit()
|
||||||
|
{
|
||||||
|
if (has_sso())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (fits_in_sso())
|
||||||
|
{
|
||||||
|
char* data = m_storage.general_storage.data;
|
||||||
|
m_storage.sso_storage = SSOStorage();
|
||||||
|
m_has_sso = true;
|
||||||
|
memcpy(this->data(), data, m_size + 1);
|
||||||
|
deallocator(data);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneralStorage& storage = m_storage.general_storage;
|
||||||
|
if (storage.capacity == m_size)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char* new_data = (char*)allocator(m_size + 1);
|
||||||
|
if (new_data == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
memcpy(new_data, storage.data, m_size);
|
||||||
|
deallocator(storage.data);
|
||||||
|
|
||||||
|
storage.capacity = m_size;
|
||||||
|
storage.data = new_data;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
StringView sv() const { return StringView(data(), size()); }
|
||||||
|
|
||||||
|
bool empty() const { return m_size == 0; }
|
||||||
|
size_type size() const { return m_size; }
|
||||||
|
|
||||||
|
size_type capacity() const
|
||||||
|
{
|
||||||
|
if (has_sso())
|
||||||
|
return sso_capacity;
|
||||||
|
return m_storage.general_storage.capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data()
|
||||||
|
{
|
||||||
|
if (has_sso())
|
||||||
|
return m_storage.sso_storage.data;
|
||||||
|
return m_storage.general_storage.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* data() const
|
||||||
|
{
|
||||||
|
if (has_sso())
|
||||||
|
return m_storage.sso_storage.data;
|
||||||
|
return m_storage.general_storage.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> ensure_capacity(size_type new_size)
|
||||||
|
{
|
||||||
|
if (m_size >= new_size)
|
||||||
|
return {};
|
||||||
|
if (has_sso() && fits_in_sso(new_size))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char* new_data = (char*)allocator(new_size + 1);
|
||||||
|
if (new_data == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
if (m_size)
|
||||||
|
memcpy(new_data, data(), m_size + 1);
|
||||||
|
|
||||||
|
if (has_sso())
|
||||||
|
{
|
||||||
|
m_storage.general_storage = GeneralStorage();
|
||||||
|
m_has_sso = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
deallocator(m_storage.general_storage.data);
|
||||||
|
|
||||||
|
auto& storage = m_storage.general_storage;
|
||||||
|
storage.capacity = new_size;
|
||||||
|
storage.data = new_data;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_sso() const { return m_has_sso; }
|
||||||
|
|
||||||
|
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||||
|
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct SSOStorage
|
||||||
|
{
|
||||||
|
char data[sso_capacity + 1] {};
|
||||||
|
};
|
||||||
|
struct GeneralStorage
|
||||||
|
{
|
||||||
|
size_type capacity { 0 };
|
||||||
|
char* data { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
SSOStorage sso_storage;
|
||||||
|
GeneralStorage general_storage;
|
||||||
|
} m_storage { .sso_storage = SSOStorage() };
|
||||||
|
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
|
||||||
|
size_type m_has_sso : 1 { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<String>
|
||||||
|
{
|
||||||
|
hash_t operator()(const String& string) const
|
||||||
|
{
|
||||||
|
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
|
||||||
|
constexpr hash_t FNV_prime = 0x01000193;
|
||||||
|
|
||||||
|
hash_t hash = FNV_offset_basis;
|
||||||
|
for (String::size_type i = 0; i < string.size(); i++)
|
||||||
|
{
|
||||||
|
hash *= FNV_prime;
|
||||||
|
hash ^= (uint8_t)string[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const String& string, const ValueFormat&)
|
||||||
|
{
|
||||||
|
for (String::size_type i = 0; i < string.size(); i++)
|
||||||
|
putc(string[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
class StringView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr StringView() {}
|
||||||
|
constexpr StringView(const char* string, size_type len = -1)
|
||||||
|
{
|
||||||
|
if (len == size_type(-1))
|
||||||
|
for (len = 0; string[len];)
|
||||||
|
len++;
|
||||||
|
m_data = string;
|
||||||
|
m_size = len;
|
||||||
|
}
|
||||||
|
StringView(const String&);
|
||||||
|
|
||||||
|
constexpr const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
constexpr const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
|
constexpr char operator[](size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(StringView other) const
|
||||||
|
{
|
||||||
|
if (m_size != other.m_size)
|
||||||
|
return false;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] != other.m_data[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const char* other) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] != other[i])
|
||||||
|
return false;
|
||||||
|
return other[m_size] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr StringView substring(size_type index, size_type len = -1) const
|
||||||
|
{
|
||||||
|
ASSERT(index <= m_size);
|
||||||
|
if (len == size_type(-1))
|
||||||
|
len = m_size - index;
|
||||||
|
ASSERT(len <= m_size - index); // weird order to avoid overflow
|
||||||
|
StringView result;
|
||||||
|
result.m_data = m_data + index;
|
||||||
|
result.m_size = len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Vector<StringView>> split(char delim, bool allow_empties = false) const
|
||||||
|
{
|
||||||
|
size_type count = 0;
|
||||||
|
{
|
||||||
|
size_type start = 0;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
if (m_data[i] == delim)
|
||||||
|
{
|
||||||
|
if (allow_empties || start != i)
|
||||||
|
count++;
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start != m_size)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<StringView> result;
|
||||||
|
TRY(result.reserve(count));
|
||||||
|
|
||||||
|
size_type start = 0;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
if (m_data[i] == delim)
|
||||||
|
{
|
||||||
|
if (allow_empties || start != i)
|
||||||
|
TRY(result.push_back(this->substring(start, i - start)));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start < m_size || (start == m_size && allow_empties))
|
||||||
|
TRY(result.push_back(this->substring(start)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool allow_empties = false) const
|
||||||
|
{
|
||||||
|
size_type count = 0;
|
||||||
|
{
|
||||||
|
size_type start = 0;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
if (comp(m_data[i]))
|
||||||
|
{
|
||||||
|
if (allow_empties || start != i)
|
||||||
|
count++;
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start != m_size)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<StringView> result;
|
||||||
|
TRY(result.reserve(count));
|
||||||
|
|
||||||
|
size_type start = 0;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
if (comp(m_data[i]))
|
||||||
|
{
|
||||||
|
if (allow_empties || start != i)
|
||||||
|
TRY(result.push_back(this->substring(start, i - start)));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start < m_size || (start == m_size && allow_empties))
|
||||||
|
TRY(result.push_back(this->substring(start)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char back() const
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[m_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char front() const
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<size_type> find(char ch) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] == ch)
|
||||||
|
return i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<size_type> find(bool(*comp)(char)) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (comp(m_data[i]))
|
||||||
|
return i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<size_type> rfind(char ch) const
|
||||||
|
{
|
||||||
|
for (size_type i = m_size; i > 0; i--)
|
||||||
|
if (m_data[i - 1] == ch)
|
||||||
|
return i - 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<size_type> rfind(bool(*comp)(char)) const
|
||||||
|
{
|
||||||
|
for (size_type i = m_size; i > 0; i--)
|
||||||
|
if (comp(m_data[i - 1]))
|
||||||
|
return i - 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool starts_with(BAN::StringView target) const
|
||||||
|
{
|
||||||
|
if (target.size() > m_size)
|
||||||
|
return false;
|
||||||
|
for (size_type i = 0; i < target.size(); i++)
|
||||||
|
if (m_data[i] != target[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool contains(char ch) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] == ch)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_type count(char ch) const
|
||||||
|
{
|
||||||
|
size_type result = 0;
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] == ch)
|
||||||
|
result++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const { return m_size == 0; }
|
||||||
|
constexpr size_type size() const { return m_size; }
|
||||||
|
constexpr const char* data() const { return m_data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* m_data = nullptr;
|
||||||
|
size_type m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<StringView>
|
||||||
|
{
|
||||||
|
hash_t operator()(StringView string) const
|
||||||
|
{
|
||||||
|
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
|
||||||
|
constexpr hash_t FNV_prime = 0x01000193;
|
||||||
|
|
||||||
|
hash_t hash = FNV_offset_basis;
|
||||||
|
for (StringView::size_type i = 0; i < string.size(); i++)
|
||||||
|
{
|
||||||
|
hash *= FNV_prime;
|
||||||
|
hash ^= (uint8_t)string[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const StringView& sv, const ValueFormat&)
|
||||||
|
{
|
||||||
|
for (StringView::size_type i = 0; i < sv.size(); i++)
|
||||||
|
putc(sv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void swap(T& lhs, T& rhs)
|
||||||
|
{
|
||||||
|
T tmp = move(lhs);
|
||||||
|
lhs = move(rhs);
|
||||||
|
rhs = move(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Time
|
||||||
|
{
|
||||||
|
uint32_t year;
|
||||||
|
uint8_t month;
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
uint8_t week_day;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t to_unix_time(const BAN::Time&);
|
||||||
|
BAN::Time from_unix_time(uint64_t);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void print_argument(F putc, const Time& time, const ValueFormat&)
|
||||||
|
{
|
||||||
|
constexpr const char* week_days[] { "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||||
|
constexpr const char* months[] { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||||
|
print(putc, "{} {} {} {2}:{2}:{2} GMT+0 {4}", week_days[time.week_day], months[time.month], time.day, time.hour, time.minute, time.second, time.year);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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<typename T> struct remove_volatile { using type = T; };
|
||||||
|
template<typename T> struct remove_volatile<volatile T> { using type = T; };
|
||||||
|
template<typename T> using remove_volatile_t = typename remove_volatile<T>::type;
|
||||||
|
|
||||||
|
template<typename T> struct remove_cv { using type = remove_volatile_t<remove_const_t<T>>; };
|
||||||
|
template<typename T> using remove_cv_t = typename remove_cv<T>::type;
|
||||||
|
|
||||||
|
template<typename T> struct remove_const_and_reference { using type = remove_const_t<remove_reference_t<T>>; };
|
||||||
|
template<typename T> using remove_const_and_reference_t = typename remove_const_and_reference<T>::type;
|
||||||
|
|
||||||
|
template<bool B, typename T = void> struct enable_if {};
|
||||||
|
template<typename T> struct enable_if<true, T> { using type = T; };
|
||||||
|
template<bool B, typename T = void> using enable_if_t = typename enable_if<B, T>::type;
|
||||||
|
|
||||||
|
template<bool B, typename T> struct maybe_const { using type = T; };
|
||||||
|
template<typename T> struct maybe_const<true, T> { using type = const T; };
|
||||||
|
template<bool B, typename T> using maybe_const_t = typename maybe_const<B, T>::type;
|
||||||
|
|
||||||
|
template<bool B, typename T1, typename T2> struct either_or { using type = T2; };
|
||||||
|
template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; };
|
||||||
|
template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type;
|
||||||
|
|
||||||
|
template<typename T, T V> struct integral_constant { static constexpr T value = V; };
|
||||||
|
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value;
|
||||||
|
using true_type = integral_constant<bool, true>;
|
||||||
|
using false_type = integral_constant<bool, false>;
|
||||||
|
|
||||||
|
template<typename T, typename S> struct is_same : false_type {};
|
||||||
|
template<typename T> struct is_same<T, T> : true_type {};
|
||||||
|
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
|
||||||
|
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
|
||||||
|
|
||||||
|
template<typename T> struct is_lvalue_reference : false_type {};
|
||||||
|
template<typename T> struct is_lvalue_reference<T&> : true_type {};
|
||||||
|
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
|
||||||
|
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
|
||||||
|
|
||||||
|
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
|
||||||
|
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
|
||||||
|
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
|
||||||
|
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
|
||||||
|
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
|
||||||
|
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||||
|
template<typename T> concept integral = is_integral_v<T>;
|
||||||
|
|
||||||
|
template<typename T> struct is_floating_point : false_type {};
|
||||||
|
template<> struct is_floating_point<float> : true_type {};
|
||||||
|
template<> struct is_floating_point<double> : true_type {};
|
||||||
|
template<> struct is_floating_point<long double> : true_type {};
|
||||||
|
template<typename T> inline constexpr bool is_floating_point_v = is_floating_point<T>::value;
|
||||||
|
template<typename T> concept floating_point = is_floating_point_v<T>;
|
||||||
|
|
||||||
|
template<typename T> struct is_pointer : false_type {};
|
||||||
|
template<typename T> struct is_pointer<T*> : true_type {};
|
||||||
|
template<typename T> struct is_pointer<T* const> : true_type {};
|
||||||
|
template<typename T> struct is_pointer<T* volatile> : true_type {};
|
||||||
|
template<typename T> struct is_pointer<T* const volatile> : true_type {};
|
||||||
|
template<typename T> inline constexpr bool is_pointer_v = is_pointer<T>::value;
|
||||||
|
template<typename T> concept pointer = is_pointer_v<T>;
|
||||||
|
|
||||||
|
template<typename T> struct is_const : false_type {};
|
||||||
|
template<typename T> struct is_const<const T> : true_type {};
|
||||||
|
template<typename T> inline constexpr bool is_const_v = is_const<T>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
|
||||||
|
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
|
||||||
|
|
||||||
|
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
|
||||||
|
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
|
||||||
|
|
||||||
|
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
|
||||||
|
template<typename T> inline constexpr bool is_pod_v = is_pod<T>::value;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
|
||||||
|
template<typename T> struct is_signed<T, false> : false_type {};
|
||||||
|
|
||||||
|
template<typename T, bool = is_arithmetic_v<T>> struct is_unsigned { static constexpr bool value = T(0) < T(-1); };
|
||||||
|
template<typename T> struct is_unsigned<T, false> : false_type {};
|
||||||
|
}
|
||||||
|
template<typename T> struct is_signed : detail::is_signed<T> {};
|
||||||
|
template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value;
|
||||||
|
template<typename T> concept signed_integral = is_signed_v<T> && is_integral_v<T>;
|
||||||
|
|
||||||
|
template<typename T> struct is_unsigned : detail::is_unsigned<T> {};
|
||||||
|
template<typename T> inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
|
||||||
|
template<typename T> concept unsigned_integral = is_unsigned_v<T> && is_integral_v<T>;
|
||||||
|
|
||||||
|
#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \
|
||||||
|
template<> struct make_unsigned<__type> { using type = unsigned __type; }; \
|
||||||
|
template<> struct make_unsigned<const __type> { using type = unsigned const __type; }; \
|
||||||
|
template<> struct make_unsigned<volatile __type> { using type = unsigned volatile __type; }; \
|
||||||
|
template<> struct make_unsigned<const volatile __type> { using type = unsigned const volatile __type; };
|
||||||
|
|
||||||
|
template<typename T> requires is_arithmetic_v<T> struct make_unsigned { using type = T; };
|
||||||
|
__BAN_TRAITS_MAKE_UNSIGNED_CV(char)
|
||||||
|
__BAN_TRAITS_MAKE_UNSIGNED_CV(short)
|
||||||
|
__BAN_TRAITS_MAKE_UNSIGNED_CV(int)
|
||||||
|
__BAN_TRAITS_MAKE_UNSIGNED_CV(long)
|
||||||
|
__BAN_TRAITS_MAKE_UNSIGNED_CV(long long)
|
||||||
|
template<typename T> using make_unsigned_t = typename make_unsigned<T>::type;
|
||||||
|
#undef __BAN_TRAITS_MAKE_UNSIGNED_CV
|
||||||
|
|
||||||
|
#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \
|
||||||
|
template<> struct make_signed<unsigned __type> { using type = __type; }; \
|
||||||
|
template<> struct make_signed<unsigned const __type> { using type = const __type; }; \
|
||||||
|
template<> struct make_signed<unsigned volatile __type> { using type = volatile __type; }; \
|
||||||
|
template<> struct make_signed<unsigned const volatile __type> { using type = const volatile __type; };
|
||||||
|
|
||||||
|
template<typename T> requires is_arithmetic_v<T> struct make_signed { using type = T; };
|
||||||
|
__BAN_TRAITS_MAKE_SIGNED_CV(char)
|
||||||
|
__BAN_TRAITS_MAKE_SIGNED_CV(short)
|
||||||
|
__BAN_TRAITS_MAKE_SIGNED_CV(int)
|
||||||
|
__BAN_TRAITS_MAKE_SIGNED_CV(long)
|
||||||
|
__BAN_TRAITS_MAKE_SIGNED_CV(long long)
|
||||||
|
template<typename T> using make_signed_t = typename make_signed<T>::type;
|
||||||
|
#undef __BAN_TRAITS_MAKE_SIGNED_CV
|
||||||
|
|
||||||
|
template<typename T> struct it_value_type { using value_type = T::value_type; };
|
||||||
|
template<typename T> struct it_value_type<T*> { using value_type = T; };
|
||||||
|
template<typename T> using it_value_type_t = typename it_value_type<T>::value_type;
|
||||||
|
|
||||||
|
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
|
||||||
|
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };
|
||||||
|
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN::UTF8
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr uint32_t invalid = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
constexpr uint32_t byte_length(uint8_t first_byte)
|
||||||
|
{
|
||||||
|
if ((first_byte & 0x80) == 0x00)
|
||||||
|
return 1;
|
||||||
|
if ((first_byte & 0xE0) == 0xC0)
|
||||||
|
return 2;
|
||||||
|
if ((first_byte & 0xF0) == 0xE0)
|
||||||
|
return 3;
|
||||||
|
if ((first_byte & 0xF8) == 0xF0)
|
||||||
|
return 4;
|
||||||
|
return UTF8::invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires (sizeof(T) == 1)
|
||||||
|
constexpr uint32_t to_codepoint(const T* bytes)
|
||||||
|
{
|
||||||
|
uint32_t length = byte_length(bytes[0]);
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i < length; i++)
|
||||||
|
if (((uint8_t)bytes[i] & 0xC0) != 0x80)
|
||||||
|
return UTF8::invalid;
|
||||||
|
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid : (uint8_t)bytes[0];
|
||||||
|
case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) << 6) | ((uint8_t)bytes[1] & 0x3F);
|
||||||
|
case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) << 6) | ((uint8_t)bytes[2] & 0x3F);
|
||||||
|
case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UTF8::invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool from_codepoints(const T* codepoints, size_t count, char* out)
|
||||||
|
{
|
||||||
|
uint8_t* ptr = (uint8_t*)out;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (codepoints[i] < 0x80)
|
||||||
|
{
|
||||||
|
*ptr++ = codepoints[i];
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x800)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xC0 | ((codepoints[i] >> 6) & 0x1F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x10000)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xE0 | ((codepoints[i] >> 12) & 0x0F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x110000)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xF0 | ((codepoints[i] >> 18) & 0x07);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 12) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class UniqPtr
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(UniqPtr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
UniqPtr() = default;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
UniqPtr(UniqPtr<U>&& other)
|
||||||
|
{
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UniqPtr()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static UniqPtr adopt(T* pointer)
|
||||||
|
{
|
||||||
|
UniqPtr uniq;
|
||||||
|
uniq.m_pointer = pointer;
|
||||||
|
return uniq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
|
||||||
|
template<typename... Args>
|
||||||
|
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||||
|
{
|
||||||
|
UniqPtr uniq;
|
||||||
|
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
|
||||||
|
if (uniq.m_pointer == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
return uniq;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
UniqPtr& operator=(UniqPtr<U>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*()
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() { return m_pointer; }
|
||||||
|
const T* ptr() const { return m_pointer; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_pointer)
|
||||||
|
delete m_pointer;
|
||||||
|
m_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const { return m_pointer != nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_pointer = nullptr;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
friend class UniqPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,317 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t size_ref_as_ptr() { return is_lvalue_reference_v<T> ? sizeof(remove_reference_t<T>*) : sizeof(T); }
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t align_ref_as_ptr() { return is_lvalue_reference_v<T> ? alignof(remove_reference_t<T>*) : alignof(T); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T>(); }
|
||||||
|
template<typename T0, typename T1, typename... Ts>
|
||||||
|
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T0>() > size_ref_as_ptr<T1>() ? max_size_ref_as_ptr<T0, Ts...>() : max_size_ref_as_ptr<T1, Ts...>(); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T>(); }
|
||||||
|
template<typename T0, typename T1, typename... Ts>
|
||||||
|
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T0>() > align_ref_as_ptr<T1>() ? max_align_ref_as_ptr<T0, Ts...>() : max_align_ref_as_ptr<T1, Ts...>(); }
|
||||||
|
|
||||||
|
template<typename T, typename T0, typename... Ts>
|
||||||
|
constexpr size_t index()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, T0>)
|
||||||
|
return 0;
|
||||||
|
else if constexpr(sizeof...(Ts) == 0)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return index<T, Ts...>() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void destruct(size_t index, uint8_t* data)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
reinterpret_cast<T*>(data)->~T();
|
||||||
|
}
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
destruct<Ts...>(index - 1, data);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void move_construct(size_t index, uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
new (target) T(move(*reinterpret_cast<T*>(source)));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
move_construct<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void copy_construct(size_t index, const uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
new (target) T(*reinterpret_cast<const T*>(source));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
copy_construct<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void move_assign(size_t index, uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
*reinterpret_cast<T*>(target) = move(*reinterpret_cast<T*>(source));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
move_assign<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void copy_assign(size_t index, const uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
*reinterpret_cast<T*>(target) = *reinterpret_cast<const T*>(source);
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
copy_assign<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
requires (!is_const_v<Ts> && ...)
|
||||||
|
class Variant
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
static constexpr bool can_have() { return detail::index<T, Ts...>() != invalid_index(); }
|
||||||
|
static constexpr size_t invalid_index() { return sizeof...(Ts); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
Variant() = default;
|
||||||
|
|
||||||
|
Variant(Variant&& other)
|
||||||
|
: m_index(other.m_index)
|
||||||
|
{
|
||||||
|
if (!other.has_value())
|
||||||
|
return;
|
||||||
|
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant(const Variant& other)
|
||||||
|
: m_index(other.m_index)
|
||||||
|
{
|
||||||
|
if (!other.has_value())
|
||||||
|
return;
|
||||||
|
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Variant(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
: m_index(detail::index<T, Ts...>())
|
||||||
|
{
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Variant(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
: m_index(detail::index<T, Ts...>())
|
||||||
|
{
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Variant()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant& operator=(Variant&& other)
|
||||||
|
{
|
||||||
|
if (m_index == other.m_index)
|
||||||
|
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
m_index = other.m_index;
|
||||||
|
}
|
||||||
|
other.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant& operator=(const Variant& other)
|
||||||
|
{
|
||||||
|
if (m_index == other.m_index)
|
||||||
|
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
m_index = other.m_index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Variant& operator=(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||||
|
get<T>() = move(value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
m_index = index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Variant& operator=(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||||
|
get<T>() = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
new (m_storage) T(value);
|
||||||
|
m_index = index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool has() const requires (can_have<T>())
|
||||||
|
{
|
||||||
|
return m_index == detail::index<T, Ts...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
new (m_storage) T(BAN::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (has<T>())
|
||||||
|
get<T>() = move(value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (has<T>())
|
||||||
|
get<T>() = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(T value) requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
*reinterpret_cast<remove_reference_t<T>**>(m_storage) = &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& get() requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return *reinterpret_cast<T*>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& get() const requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return *reinterpret_cast<const T*>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T get() requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return **reinterpret_cast<remove_reference_t<T>**>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T get() const requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_value() const
|
||||||
|
{
|
||||||
|
return m_index != invalid_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_index != invalid_index())
|
||||||
|
{
|
||||||
|
detail::destruct<Ts...>(m_index, m_storage);
|
||||||
|
m_index = invalid_index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
alignas(detail::max_align_ref_as_ptr<Ts...>()) uint8_t m_storage[detail::max_size_ref_as_ptr<Ts...>()] {};
|
||||||
|
size_t m_index { invalid_index() };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,418 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
#include <BAN/Swap.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
// T must be move assignable, move constructable (and copy constructable for some functions)
|
||||||
|
template<typename T>
|
||||||
|
class Vector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Vector>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Vector>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Vector() = default;
|
||||||
|
Vector(Vector<T>&&);
|
||||||
|
Vector(const Vector<T>&);
|
||||||
|
Vector(size_type, const T& = T());
|
||||||
|
~Vector();
|
||||||
|
|
||||||
|
Vector<T>& operator=(Vector<T>&&);
|
||||||
|
Vector<T>& operator=(const Vector<T>&);
|
||||||
|
|
||||||
|
ErrorOr<void> push_back(T&&);
|
||||||
|
ErrorOr<void> push_back(const T&);
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>;
|
||||||
|
ErrorOr<void> insert(size_type, T&&);
|
||||||
|
ErrorOr<void> insert(size_type, const T&);
|
||||||
|
|
||||||
|
iterator begin() { return iterator(m_data); }
|
||||||
|
iterator end() { return iterator(m_data + m_size); }
|
||||||
|
const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
|
void pop_back();
|
||||||
|
void remove(size_type);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
T* data() { return m_data; }
|
||||||
|
const T* data() const { return m_data; }
|
||||||
|
|
||||||
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
Span<T> span() { return Span(m_data, m_size); }
|
||||||
|
Span<const T> span() const { return Span(m_data, m_size); }
|
||||||
|
|
||||||
|
const T& operator[](size_type) const;
|
||||||
|
T& operator[](size_type);
|
||||||
|
|
||||||
|
const T& back() const;
|
||||||
|
T& back();
|
||||||
|
const T& front() const;
|
||||||
|
T& front();
|
||||||
|
|
||||||
|
void reverse();
|
||||||
|
|
||||||
|
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
|
||||||
|
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
|
||||||
|
ErrorOr<void> reserve(size_type);
|
||||||
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
size_type size() const;
|
||||||
|
size_type capacity() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> ensure_capacity(size_type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_data = nullptr;
|
||||||
|
size_type m_capacity = 0;
|
||||||
|
size_type m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>::Vector(Vector<T>&& other)
|
||||||
|
{
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_capacity = other.m_capacity;
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_capacity = 0;
|
||||||
|
other.m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>::Vector(const Vector<T>& other)
|
||||||
|
{
|
||||||
|
MUST(ensure_capacity(other.m_size));
|
||||||
|
for (size_type i = 0; i < other.m_size; i++)
|
||||||
|
new (m_data + i) T(other.m_data[i]);
|
||||||
|
m_size = other.m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>::Vector(size_type size, const T& value)
|
||||||
|
{
|
||||||
|
MUST(ensure_capacity(size));
|
||||||
|
for (size_type i = 0; i < size; i++)
|
||||||
|
new (m_data + i) T(value);
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>::~Vector()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>& Vector<T>::operator=(Vector<T>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_capacity = other.m_capacity;
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_capacity = 0;
|
||||||
|
other.m_size = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
||||||
|
{
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++)
|
||||||
|
m_data[i] = other.m_data[i];
|
||||||
|
for (size_type i = size(); i < other.size(); i++)
|
||||||
|
new (m_data + i) T(other[i]);
|
||||||
|
for (size_type i = other.size(); i < size(); i++)
|
||||||
|
m_data[i].~T();
|
||||||
|
m_size = other.m_size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::push_back(T&& value)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
new (m_data + m_size) T(move(value));
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::push_back(const T& value)
|
||||||
|
{
|
||||||
|
return push_back(move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
new (m_data + m_size) T(forward<Args>(args)...);
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
|
{
|
||||||
|
ASSERT(index <= m_size);
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
if (index < m_size)
|
||||||
|
{
|
||||||
|
new (m_data + m_size) T(move(m_data[m_size - 1]));
|
||||||
|
for (size_type i = m_size - 1; i > index; i--)
|
||||||
|
m_data[i] = move(m_data[i - 1]);
|
||||||
|
m_data[index] = move(T(forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new (m_data + m_size) T(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::insert(size_type index, T&& value)
|
||||||
|
{
|
||||||
|
ASSERT(index <= m_size);
|
||||||
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
if (index < m_size)
|
||||||
|
{
|
||||||
|
new (m_data + m_size) T(move(m_data[m_size - 1]));
|
||||||
|
for (size_type i = m_size - 1; i > index; i--)
|
||||||
|
m_data[i] = move(m_data[i - 1]);
|
||||||
|
m_data[index] = move(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new (m_data + m_size) T(move(value));
|
||||||
|
}
|
||||||
|
m_size++;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
|
||||||
|
{
|
||||||
|
return insert(index, move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Vector<T>::pop_back()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
m_data[m_size - 1].~T();
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Vector<T>::remove(size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
for (size_type i = index; i < m_size - 1; i++)
|
||||||
|
m_data[i] = move(m_data[i + 1]);
|
||||||
|
m_data[m_size - 1].~T();
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Vector<T>::clear()
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
m_data[i].~T();
|
||||||
|
BAN::deallocator(m_data);
|
||||||
|
m_data = nullptr;
|
||||||
|
m_capacity = 0;
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool Vector<T>::contains(const T& other) const
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] == other)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& Vector<T>::operator[](size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& Vector<T>::operator[](size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& Vector<T>::back() const
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[m_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& Vector<T>::back()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[m_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& Vector<T>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& Vector<T>::front()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
return m_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Vector<T>::reverse()
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size / 2; i++)
|
||||||
|
BAN::swap(m_data[i], m_data[m_size - i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(size));
|
||||||
|
if (size < m_size)
|
||||||
|
for (size_type i = size; i < m_size; i++)
|
||||||
|
m_data[i].~T();
|
||||||
|
if (size > m_size)
|
||||||
|
for (size_type i = m_size; i < size; i++)
|
||||||
|
new (m_data + i) T();
|
||||||
|
m_size = size;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(size));
|
||||||
|
if (size < m_size)
|
||||||
|
for (size_type i = size; i < m_size; i++)
|
||||||
|
m_data[i].~T();
|
||||||
|
if (size > m_size)
|
||||||
|
for (size_type i = m_size; i < size; i++)
|
||||||
|
new (m_data + i) T(value);
|
||||||
|
m_size = size;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::reserve(size_type size)
|
||||||
|
{
|
||||||
|
TRY(ensure_capacity(size));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::shrink_to_fit()
|
||||||
|
{
|
||||||
|
size_type temp = m_capacity;
|
||||||
|
m_capacity = 0;
|
||||||
|
auto error_or = ensure_capacity(m_size);
|
||||||
|
if (error_or.is_error())
|
||||||
|
{
|
||||||
|
m_capacity = temp;
|
||||||
|
return error_or;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool Vector<T>::empty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename Vector<T>::size_type Vector<T>::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename Vector<T>::size_type Vector<T>::capacity() const
|
||||||
|
{
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::ensure_capacity(size_type size)
|
||||||
|
{
|
||||||
|
if (m_capacity >= size)
|
||||||
|
return {};
|
||||||
|
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||||
|
T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
|
||||||
|
if (new_data == nullptr)
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
{
|
||||||
|
new (new_data + i) T(move(m_data[i]));
|
||||||
|
m_data[i].~T();
|
||||||
|
}
|
||||||
|
BAN::deallocator(m_data);
|
||||||
|
m_data = new_data;
|
||||||
|
m_capacity = new_cap;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BAN::Formatter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename F, typename T>
|
||||||
|
void print_argument(F putc, const Vector<T>& vector, const ValueFormat& format)
|
||||||
|
{
|
||||||
|
putc('[');
|
||||||
|
for (typename Vector<T>::size_type i = 0; i < vector.size(); i++)
|
||||||
|
{
|
||||||
|
if (i != 0) putc(',');
|
||||||
|
print_argument(putc, vector[i], format);
|
||||||
|
}
|
||||||
|
putc(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/RefPtr.h>
|
||||||
|
|
||||||
|
#if __is_kernel
|
||||||
|
#include <kernel/Lock/SpinLock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Weakable;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class WeakPtr;
|
||||||
|
|
||||||
|
// FIXME: Write this without using locks...
|
||||||
|
template<typename T>
|
||||||
|
class WeakLink : public RefCounted<WeakLink<T>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RefPtr<T> try_lock() const
|
||||||
|
{
|
||||||
|
#if __is_kernel
|
||||||
|
Kernel::SpinLockGuard _(m_weak_lock);
|
||||||
|
#endif
|
||||||
|
if (m_ptr && m_ptr->try_ref())
|
||||||
|
return RefPtr<T>::adopt(m_ptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
bool valid() const { return m_ptr; }
|
||||||
|
void invalidate()
|
||||||
|
{
|
||||||
|
#if __is_kernel
|
||||||
|
Kernel::SpinLockGuard _(m_weak_lock);
|
||||||
|
#endif
|
||||||
|
m_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WeakLink(T* ptr) : m_ptr(ptr) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_ptr;
|
||||||
|
#if __is_kernel
|
||||||
|
mutable Kernel::SpinLock m_weak_lock;
|
||||||
|
#endif
|
||||||
|
friend class RefPtr<WeakLink<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Weakable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Weakable()
|
||||||
|
{
|
||||||
|
if (m_link)
|
||||||
|
m_link->invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<WeakPtr<T>> get_weak_ptr() const
|
||||||
|
{
|
||||||
|
if (!m_link)
|
||||||
|
m_link = TRY(RefPtr<WeakLink<T>>::create((T*)this));
|
||||||
|
return WeakPtr<T>(m_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable RefPtr<WeakLink<T>> m_link;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class WeakPtr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WeakPtr() = default;
|
||||||
|
WeakPtr(WeakPtr&& other) { *this = move(other); }
|
||||||
|
WeakPtr(const WeakPtr& other) { *this = other; }
|
||||||
|
WeakPtr(const RefPtr<T>& other) { *this = other; }
|
||||||
|
|
||||||
|
WeakPtr& operator=(WeakPtr&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_link = move(other.m_link);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WeakPtr& operator=(const WeakPtr& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_link = other.m_link;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WeakPtr& operator=(const RefPtr<T>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
if (other)
|
||||||
|
m_link = MUST(other->get_weak_ptr()).move_link();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<T> lock() const
|
||||||
|
{
|
||||||
|
if (m_link)
|
||||||
|
return m_link->try_lock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() { m_link.clear(); }
|
||||||
|
|
||||||
|
bool valid() const { return m_link && m_link->valid(); }
|
||||||
|
|
||||||
|
explicit operator bool() const { return valid(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
WeakPtr(const RefPtr<WeakLink<T>>& link)
|
||||||
|
: m_link(link)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
RefPtr<WeakLink<T>>&& move_link() { return move(m_link); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<WeakLink<T>> m_link;
|
||||||
|
|
||||||
|
friend class Weakable<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
project(banan-os CXX C ASM)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
function(banan_include_headers target library)
|
||||||
|
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(banan_link_library target library)
|
||||||
|
target_link_libraries(${target} PUBLIC ${library})
|
||||||
|
banan_include_headers(${target} ${library})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(banan_install_headers target)
|
||||||
|
file(GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/include *.h)
|
||||||
|
foreach(header ${headers})
|
||||||
|
get_filename_component(subdirectory ${header} DIRECTORY)
|
||||||
|
install(FILES include/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory})
|
||||||
|
endforeach()
|
||||||
|
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
add_compile_definitions(-Dstddbg=stdout)
|
||||||
|
add_compile_options(-g)
|
||||||
|
|
||||||
|
add_subdirectory(BAN)
|
||||||
|
add_subdirectory(LibClipboard)
|
||||||
|
add_subdirectory(LibDEFLATE)
|
||||||
|
add_subdirectory(LibFont)
|
||||||
|
add_subdirectory(LibGUI)
|
||||||
|
add_subdirectory(LibImage)
|
||||||
|
add_subdirectory(LibInput)
|
||||||
|
add_subdirectory(Terminal)
|
||||||
|
add_subdirectory(ProgramLauncher)
|
||||||
|
add_subdirectory(WindowServer)
|
||||||
|
add_subdirectory(xbanan)
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
set(LIBCLIPBOARD_SOURCES
|
||||||
|
Clipboard.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(libclipboard ${LIBCLIPBOARD_SOURCES})
|
||||||
|
banan_link_library(libclipboard ban)
|
||||||
|
|
||||||
|
banan_install_headers(libclipboard)
|
||||||
|
install(TARGETS libclipboard OPTIONAL)
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
#include <LibClipboard/Clipboard.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace LibClipboard
|
||||||
|
{
|
||||||
|
|
||||||
|
static int s_server_fd = -1;
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> send_credentials(int fd)
|
||||||
|
{
|
||||||
|
char dummy = '\0';
|
||||||
|
iovec iovec {
|
||||||
|
.iov_base = &dummy,
|
||||||
|
.iov_len = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t control_size = CMSG_LEN(sizeof(ucred));
|
||||||
|
uint8_t control_buffer[control_size];
|
||||||
|
|
||||||
|
cmsghdr* control = reinterpret_cast<cmsghdr*>(control_buffer);
|
||||||
|
|
||||||
|
*control = {
|
||||||
|
.cmsg_len = control_size,
|
||||||
|
.cmsg_level = SOL_SOCKET,
|
||||||
|
.cmsg_type = SCM_CREDENTIALS,
|
||||||
|
};
|
||||||
|
|
||||||
|
*reinterpret_cast<ucred*>(CMSG_DATA(control)) = {
|
||||||
|
.pid = getpid(),
|
||||||
|
.uid = getuid(),
|
||||||
|
.gid = getgid(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const msghdr message {
|
||||||
|
.msg_name = nullptr,
|
||||||
|
.msg_namelen = 0,
|
||||||
|
.msg_iov = &iovec,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = control,
|
||||||
|
.msg_controllen = control_size,
|
||||||
|
.msg_flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sendmsg(fd, &message, 0) < 0)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> ensure_connected()
|
||||||
|
{
|
||||||
|
if (s_server_fd != -1)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sock == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
|
||||||
|
sockaddr_un server_addr;
|
||||||
|
server_addr.sun_family = AF_UNIX;
|
||||||
|
strcpy(server_addr.sun_path, s_clipboard_server_socket.data());
|
||||||
|
|
||||||
|
if (connect(sock, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == -1)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto ret = send_credentials(sock); ret.is_error())
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_server_fd = sock;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> recv_sized(void* data, size_t size)
|
||||||
|
{
|
||||||
|
ASSERT(s_server_fd != -1);
|
||||||
|
|
||||||
|
uint8_t* u8_data = static_cast<uint8_t*>(data);
|
||||||
|
|
||||||
|
size_t total_recv = 0;
|
||||||
|
while (total_recv < size)
|
||||||
|
{
|
||||||
|
const ssize_t nrecv = recv(s_server_fd, u8_data + total_recv, size - total_recv, 0);
|
||||||
|
if (nrecv <= 0)
|
||||||
|
{
|
||||||
|
const int error = nrecv ? errno : ECONNRESET;
|
||||||
|
close(s_server_fd);
|
||||||
|
s_server_fd = -1;
|
||||||
|
return BAN::Error::from_errno(error);
|
||||||
|
}
|
||||||
|
total_recv += nrecv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> send_sized(const void* data, size_t size)
|
||||||
|
{
|
||||||
|
ASSERT(s_server_fd != -1);
|
||||||
|
|
||||||
|
const uint8_t* u8_data = static_cast<const uint8_t*>(data);
|
||||||
|
|
||||||
|
size_t total_sent = 0;
|
||||||
|
while (total_sent < size)
|
||||||
|
{
|
||||||
|
const ssize_t nsend = send(s_server_fd, u8_data + total_sent, size - total_sent, 0);
|
||||||
|
if (nsend <= 0)
|
||||||
|
{
|
||||||
|
const int error = nsend ? errno : ECONNRESET;
|
||||||
|
close(s_server_fd);
|
||||||
|
s_server_fd = -1;
|
||||||
|
return BAN::Error::from_errno(error);
|
||||||
|
}
|
||||||
|
total_sent += nsend;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Clipboard::Info> Clipboard::get_clipboard()
|
||||||
|
{
|
||||||
|
TRY(ensure_connected());
|
||||||
|
|
||||||
|
{
|
||||||
|
DataType type = DataType::__get;
|
||||||
|
TRY(send_sized(&type, sizeof(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Info info;
|
||||||
|
TRY(recv_sized(&info.type, sizeof(info.type)));
|
||||||
|
|
||||||
|
switch (info.type)
|
||||||
|
{
|
||||||
|
case DataType::__get:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
case DataType::None:
|
||||||
|
break;
|
||||||
|
case DataType::Text:
|
||||||
|
size_t data_size;
|
||||||
|
TRY(recv_sized(&data_size, sizeof(data_size)));
|
||||||
|
|
||||||
|
TRY(info.data.resize(data_size));
|
||||||
|
TRY(recv_sized(info.data.data(), data_size));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Clipboard::set_clipboard(DataType type, BAN::Span<const uint8_t> data)
|
||||||
|
{
|
||||||
|
ASSERT(type != DataType::__get);
|
||||||
|
|
||||||
|
TRY(ensure_connected());
|
||||||
|
|
||||||
|
TRY(send_sized(&type, sizeof(type)));
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DataType::__get:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
case DataType::None:
|
||||||
|
break;
|
||||||
|
case DataType::Text:
|
||||||
|
const size_t size = data.size();
|
||||||
|
TRY(send_sized(&size, sizeof(size)));
|
||||||
|
TRY(send_sized(data.data(), size));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::String> Clipboard::get_clipboard_text()
|
||||||
|
{
|
||||||
|
auto info = TRY(get_clipboard());
|
||||||
|
if (info.type != DataType::Text)
|
||||||
|
return BAN::String {};
|
||||||
|
|
||||||
|
BAN::String string;
|
||||||
|
TRY(string.resize(info.data.size()));
|
||||||
|
memcpy(string.data(), info.data.data(), info.data.size());
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Clipboard::set_clipboard_text(BAN::StringView string)
|
||||||
|
{
|
||||||
|
return set_clipboard(DataType::Text, { reinterpret_cast<const uint8_t*>(string.data()), string.size() });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
#include <BAN/String.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace LibClipboard
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr BAN::StringView s_clipboard_server_socket = "/tmp/clipboard-server.socket"_sv;
|
||||||
|
|
||||||
|
class Clipboard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class DataType : uint32_t
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Text,
|
||||||
|
__get = UINT32_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Info
|
||||||
|
{
|
||||||
|
DataType type = DataType::None;
|
||||||
|
BAN::Vector<uint8_t> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<Info> get_clipboard();
|
||||||
|
static BAN::ErrorOr<void> set_clipboard(DataType type, BAN::Span<const uint8_t> data);
|
||||||
|
|
||||||
|
static BAN::ErrorOr<BAN::String> get_clipboard_text();
|
||||||
|
static BAN::ErrorOr<void> set_clipboard_text(BAN::StringView string);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
set(LIBDEFLATE_SOURCES
|
||||||
|
Compressor.cpp
|
||||||
|
Decompressor.cpp
|
||||||
|
HuffmanTree.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(libdeflate ${LIBDEFLATE_SOURCES})
|
||||||
|
banan_link_library(libdeflate ban)
|
||||||
|
|
||||||
|
banan_install_headers(libdeflate)
|
||||||
|
install(TARGETS libdeflate OPTIONAL)
|
||||||
|
|
@ -0,0 +1,620 @@
|
||||||
|
#include <LibDEFLATE/Compressor.h>
|
||||||
|
#include <LibDEFLATE/Utils.h>
|
||||||
|
|
||||||
|
#include <BAN/Array.h>
|
||||||
|
#include <BAN/Heap.h>
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/Sort.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr size_t s_max_length = 258;
|
||||||
|
constexpr size_t s_max_distance = 32768;
|
||||||
|
|
||||||
|
constexpr size_t s_max_symbols = 288;
|
||||||
|
constexpr uint8_t s_max_bits = 15;
|
||||||
|
|
||||||
|
struct Leaf
|
||||||
|
{
|
||||||
|
uint16_t code;
|
||||||
|
uint8_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> create_huffman_tree(BAN::Span<const size_t> freq, BAN::Span<Leaf> output)
|
||||||
|
{
|
||||||
|
ASSERT(freq.size() <= s_max_symbols);
|
||||||
|
ASSERT(freq.size() == output.size());
|
||||||
|
|
||||||
|
struct node_t
|
||||||
|
{
|
||||||
|
size_t symbol;
|
||||||
|
size_t freq;
|
||||||
|
node_t* left;
|
||||||
|
node_t* right;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LIBDEFLATE_AVOID_STACK
|
||||||
|
BAN::Vector<node_t*> nodes;
|
||||||
|
TRY(nodes.resize(s_max_symbols));
|
||||||
|
#else
|
||||||
|
BAN::Array<node_t*, s_max_symbols> nodes;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t node_count = 0;
|
||||||
|
for (size_t sym = 0; sym < freq.size(); sym++)
|
||||||
|
{
|
||||||
|
if (freq[sym] == 0)
|
||||||
|
continue;
|
||||||
|
nodes[node_count] = static_cast<node_t*>(BAN::allocator(sizeof(node_t)));
|
||||||
|
if (nodes[node_count] == nullptr)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < node_count; j++)
|
||||||
|
BAN::deallocator(nodes[j]);
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
*nodes[node_count++] = {
|
||||||
|
.symbol = sym,
|
||||||
|
.freq = freq[sym],
|
||||||
|
.left = nullptr,
|
||||||
|
.right = nullptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& symbol : output)
|
||||||
|
symbol = { .code = 0, .length = 0 };
|
||||||
|
|
||||||
|
if (node_count == 0)
|
||||||
|
{
|
||||||
|
output[0] = { .code = 0, .length = 1 };
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void (*free_tree)(node_t*) =
|
||||||
|
[](node_t* root) -> void {
|
||||||
|
if (root == nullptr)
|
||||||
|
return;
|
||||||
|
free_tree(root->left);
|
||||||
|
free_tree(root->right);
|
||||||
|
BAN::deallocator(root);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto comp =
|
||||||
|
[](const node_t* a, const node_t* b) -> bool {
|
||||||
|
if (a->freq != b->freq)
|
||||||
|
return a->freq > b->freq;
|
||||||
|
return a->symbol > b->symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto end_it = nodes.begin() + node_count;
|
||||||
|
BAN::make_heap(nodes.begin(), end_it, comp);
|
||||||
|
|
||||||
|
while (nodes.begin() + 1 != end_it)
|
||||||
|
{
|
||||||
|
node_t* parent = static_cast<node_t*>(BAN::allocator(sizeof(node_t)));
|
||||||
|
if (parent == nullptr)
|
||||||
|
{
|
||||||
|
for (auto it = nodes.begin(); it != end_it; it++)
|
||||||
|
free_tree(*it);
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* node1 = nodes.front();
|
||||||
|
BAN::pop_heap(nodes.begin(), end_it--, comp);
|
||||||
|
|
||||||
|
node_t* node2 = nodes.front();
|
||||||
|
BAN::pop_heap(nodes.begin(), end_it--, comp);
|
||||||
|
|
||||||
|
*parent = {
|
||||||
|
.symbol = 0,
|
||||||
|
.freq = node1->freq + node2->freq,
|
||||||
|
.left = node1,
|
||||||
|
.right = node2,
|
||||||
|
};
|
||||||
|
|
||||||
|
*end_it++ = parent;
|
||||||
|
BAN::push_heap(nodes.begin(), end_it, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t (*gather_lengths)(const node_t*, BAN::Span<Leaf>, uint16_t) =
|
||||||
|
[](const node_t* node, BAN::Span<Leaf> symbols, uint16_t depth) -> uint16_t {
|
||||||
|
if (node == nullptr)
|
||||||
|
return 0;
|
||||||
|
uint16_t count = (depth > s_max_bits);
|
||||||
|
if (node->left == nullptr && node->right == nullptr)
|
||||||
|
symbols[node->symbol].length = BAN::Math::min<uint16_t>(depth, s_max_bits);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count += gather_lengths(node->left, symbols, depth + 1);
|
||||||
|
count += gather_lengths(node->right, symbols, depth + 1);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto too_long_count = gather_lengths(nodes[0], output, 0);
|
||||||
|
free_tree(nodes[0]);
|
||||||
|
|
||||||
|
uint16_t bl_count[s_max_bits + 1] {};
|
||||||
|
for (size_t sym = 0; sym < freq.size(); sym++)
|
||||||
|
if (const uint8_t len = output[sym].length)
|
||||||
|
bl_count[len]++;
|
||||||
|
|
||||||
|
if (too_long_count > 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < too_long_count / 2; i++)
|
||||||
|
{
|
||||||
|
uint16_t bits = s_max_bits - 1;
|
||||||
|
while (bl_count[bits] == 0)
|
||||||
|
bits--;
|
||||||
|
bl_count[bits + 0]--;
|
||||||
|
bl_count[bits + 1] += 2;
|
||||||
|
bl_count[s_max_bits]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SymFreq
|
||||||
|
{
|
||||||
|
size_t symbol;
|
||||||
|
size_t freq;
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::Vector<SymFreq> sym_freq;
|
||||||
|
for (size_t sym = 0; sym < output.size(); sym++)
|
||||||
|
if (freq[sym] != 0)
|
||||||
|
TRY(sym_freq.push_back({ .symbol = sym, .freq = freq[sym] }));
|
||||||
|
|
||||||
|
BAN::sort::sort(sym_freq.begin(), sym_freq.end(),
|
||||||
|
[](auto a, auto b) { return a.freq < b.freq; }
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (uint16_t bits = s_max_bits; bits > 0; bits--)
|
||||||
|
for (size_t i = 0; i < bl_count[bits]; i++)
|
||||||
|
output[sym_freq[index++].symbol].length = bits;
|
||||||
|
ASSERT(index == sym_freq.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t next_code[s_max_bits + 1] {};
|
||||||
|
uint16_t code = 0;
|
||||||
|
for (uint8_t bits = 1; bits <= s_max_bits; bits++)
|
||||||
|
{
|
||||||
|
code = (code + bl_count[bits - 1]) << 1;
|
||||||
|
next_code[bits] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t sym = 0; sym < freq.size(); sym++)
|
||||||
|
if (const uint16_t len = output[sym].length)
|
||||||
|
output[sym].code = next_code[len]++;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Encoding
|
||||||
|
{
|
||||||
|
uint16_t symbol;
|
||||||
|
uint16_t extra_data { 0 };
|
||||||
|
uint8_t extra_len { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr Encoding get_len_encoding(uint16_t length)
|
||||||
|
{
|
||||||
|
ASSERT(3 <= length && length <= s_max_length);
|
||||||
|
|
||||||
|
constexpr uint16_t base[] {
|
||||||
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||||
|
};
|
||||||
|
constexpr uint8_t extra_bits[] {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||||
|
};
|
||||||
|
constexpr size_t count = sizeof(base) / sizeof(*base);
|
||||||
|
|
||||||
|
for (size_t i = 0;; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 < count && length >= base[i + 1])
|
||||||
|
continue;
|
||||||
|
return {
|
||||||
|
.symbol = static_cast<uint16_t>(257 + i),
|
||||||
|
.extra_data = static_cast<uint16_t>(length - base[i]),
|
||||||
|
.extra_len = extra_bits[i],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Encoding get_dist_encoding(uint16_t distance)
|
||||||
|
{
|
||||||
|
ASSERT(1 <= distance && distance <= s_max_distance);
|
||||||
|
|
||||||
|
constexpr uint16_t base[] {
|
||||||
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
|
||||||
|
};
|
||||||
|
constexpr uint8_t extra_bits[] {
|
||||||
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||||
|
};
|
||||||
|
constexpr size_t count = sizeof(base) / sizeof(*base);
|
||||||
|
|
||||||
|
for (size_t i = 0;; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 < count && distance >= base[i + 1])
|
||||||
|
continue;
|
||||||
|
return {
|
||||||
|
.symbol = static_cast<uint16_t>(i),
|
||||||
|
.extra_data = static_cast<uint16_t>(distance - base[i]),
|
||||||
|
.extra_len = extra_bits[i],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_frequencies(BAN::Span<const Compressor::LZ77Entry> entries, BAN::Span<size_t> lit_len_freq, BAN::Span<size_t> dist_freq)
|
||||||
|
{
|
||||||
|
ASSERT(lit_len_freq.size() == 286);
|
||||||
|
ASSERT(dist_freq.size() == 30);
|
||||||
|
|
||||||
|
for (auto entry : entries)
|
||||||
|
{
|
||||||
|
switch (entry.type)
|
||||||
|
{
|
||||||
|
case Compressor::LZ77Entry::Type::Literal:
|
||||||
|
lit_len_freq[entry.as.literal]++;
|
||||||
|
break;
|
||||||
|
case Compressor::LZ77Entry::Type::DistLength:
|
||||||
|
lit_len_freq[get_len_encoding(entry.as.dist_length.length).symbol]++;
|
||||||
|
dist_freq[get_dist_encoding(entry.as.dist_length.distance).symbol]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lit_len_freq[256]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CodeLengthInfo
|
||||||
|
{
|
||||||
|
uint16_t hlit;
|
||||||
|
uint8_t hdist;
|
||||||
|
uint8_t hclen;
|
||||||
|
BAN::Vector<Encoding> encoding;
|
||||||
|
BAN::Array<uint8_t, 19> code_length;
|
||||||
|
BAN::Array<Leaf, 19> code_length_tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BAN::ErrorOr<CodeLengthInfo> build_code_length_info(BAN::Span<const Leaf> lit_len_tree, BAN::Span<const Leaf> dist_tree)
|
||||||
|
{
|
||||||
|
CodeLengthInfo result;
|
||||||
|
|
||||||
|
const auto append_tree =
|
||||||
|
[&result](BAN::Span<const Leaf>& tree) -> BAN::ErrorOr<void>
|
||||||
|
{
|
||||||
|
while (!tree.empty() && tree[tree.size() - 1].length == 0)
|
||||||
|
tree = tree.slice(0, tree.size() - 1);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < tree.size();)
|
||||||
|
{
|
||||||
|
size_t count = 1;
|
||||||
|
while (i + count < tree.size() && tree[i].length == tree[i + count].length)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (tree[i].length == 0)
|
||||||
|
{
|
||||||
|
if (count > 138)
|
||||||
|
count = 138;
|
||||||
|
|
||||||
|
if (count < 3)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < count; j++)
|
||||||
|
TRY(result.encoding.push_back({ .symbol = 0 }));
|
||||||
|
}
|
||||||
|
else if (count < 11)
|
||||||
|
{
|
||||||
|
TRY(result.encoding.push_back({
|
||||||
|
.symbol = 17,
|
||||||
|
.extra_data = static_cast<uint8_t>(count - 3),
|
||||||
|
.extra_len = 3,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(result.encoding.push_back({
|
||||||
|
.symbol = 18,
|
||||||
|
.extra_data = static_cast<uint8_t>(count - 11),
|
||||||
|
.extra_len = 7,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (count >= 3 && !result.encoding.empty() && result.encoding.back().symbol == tree[i].length)
|
||||||
|
{
|
||||||
|
if (count > 6)
|
||||||
|
count = 6;
|
||||||
|
TRY(result.encoding.push_back({
|
||||||
|
.symbol = 16,
|
||||||
|
.extra_data = static_cast<uint8_t>(count - 3),
|
||||||
|
.extra_len = 2,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = 1;
|
||||||
|
TRY(result.encoding.push_back({ .symbol = tree[i].length }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
TRY(append_tree(lit_len_tree));
|
||||||
|
result.hlit = lit_len_tree.size();
|
||||||
|
|
||||||
|
TRY(append_tree(dist_tree));
|
||||||
|
result.hdist = dist_tree.size();
|
||||||
|
|
||||||
|
BAN::Array<size_t, 19> code_len_freq(0);
|
||||||
|
for (auto entry : result.encoding)
|
||||||
|
code_len_freq[entry.symbol]++;
|
||||||
|
TRY(create_huffman_tree(code_len_freq.span(), result.code_length_tree.span()));
|
||||||
|
|
||||||
|
constexpr uint8_t code_length_order[] {
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < result.code_length_tree.size(); i++)
|
||||||
|
result.code_length[i] = result.code_length_tree[code_length_order[i]].length;
|
||||||
|
result.hclen = 19;
|
||||||
|
while (result.hclen > 4 && result.code_length[result.hclen - 1] == 0)
|
||||||
|
result.hclen--;
|
||||||
|
|
||||||
|
return BAN::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Compressor::get_hash_key(BAN::ConstByteSpan needle) const
|
||||||
|
{
|
||||||
|
ASSERT(needle.size() >= 3);
|
||||||
|
return (needle[2] << 16) | (needle[1] << 8) | needle[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Compressor::update_hash_chain(size_t count)
|
||||||
|
{
|
||||||
|
if (m_hash_chain.size() >= s_max_distance * 2)
|
||||||
|
{
|
||||||
|
const uint8_t* current = m_data.data() + m_hash_chain_index + count;
|
||||||
|
for (auto& [_, chain] : m_hash_chain)
|
||||||
|
{
|
||||||
|
for (auto it = chain.begin(); it != chain.end(); it++)
|
||||||
|
{
|
||||||
|
const size_t distance = current - it->data();
|
||||||
|
if (distance < s_max_distance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (it != chain.end())
|
||||||
|
it = chain.remove(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
auto slice = m_data.slice(m_hash_chain_index + i);
|
||||||
|
if (slice.size() < 3)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const uint32_t key = get_hash_key(slice);
|
||||||
|
|
||||||
|
auto it = m_hash_chain.find(key);
|
||||||
|
if (it != m_hash_chain.end())
|
||||||
|
TRY(it->value.insert(it->value.begin(), slice));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HashChain new_chain;
|
||||||
|
TRY(new_chain.push_back(slice));
|
||||||
|
TRY(m_hash_chain.insert(key, BAN::move(new_chain)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hash_chain_index += count;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Compressor::LZ77Entry> Compressor::find_longest_match(BAN::ConstByteSpan needle) const
|
||||||
|
{
|
||||||
|
LZ77Entry result = {
|
||||||
|
.type = LZ77Entry::Type::Literal,
|
||||||
|
.as = { .literal = needle[0] }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (needle.size() < 3)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
const uint32_t key = get_hash_key(needle);
|
||||||
|
|
||||||
|
auto it = m_hash_chain.find(key);
|
||||||
|
if (it == m_hash_chain.end())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
auto& chain = it->value;
|
||||||
|
for (const auto node : chain)
|
||||||
|
{
|
||||||
|
const size_t distance = needle.data() - node.data();
|
||||||
|
if (distance > s_max_distance)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size_t length = 3;
|
||||||
|
const size_t max_length = BAN::Math::min(needle.size(), s_max_length);
|
||||||
|
while (length < max_length && needle[length] == node[length])
|
||||||
|
length++;
|
||||||
|
|
||||||
|
if (result.type != LZ77Entry::Type::DistLength || length > result.as.dist_length.length)
|
||||||
|
{
|
||||||
|
result = LZ77Entry {
|
||||||
|
.type = LZ77Entry::Type::DistLength,
|
||||||
|
.as = {
|
||||||
|
.dist_length = {
|
||||||
|
.length = static_cast<uint16_t>(length),
|
||||||
|
.distance = static_cast<uint16_t>(distance),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<Compressor::LZ77Entry>> Compressor::lz77_compress(BAN::ConstByteSpan data)
|
||||||
|
{
|
||||||
|
BAN::Vector<LZ77Entry> result;
|
||||||
|
|
||||||
|
size_t advance = 0;
|
||||||
|
for (size_t i = 0; i < data.size(); i += advance)
|
||||||
|
{
|
||||||
|
TRY(update_hash_chain(advance));
|
||||||
|
|
||||||
|
auto match = TRY(find_longest_match(data.slice(i)));
|
||||||
|
if (match.type == LZ77Entry::Type::Literal)
|
||||||
|
{
|
||||||
|
TRY(result.push_back(match));
|
||||||
|
advance = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(match.type == LZ77Entry::Type::DistLength);
|
||||||
|
|
||||||
|
auto lazy_match = TRY(find_longest_match(data.slice(i + 1)));
|
||||||
|
if (lazy_match.type == LZ77Entry::Type::DistLength && lazy_match.as.dist_length.length > match.as.dist_length.length)
|
||||||
|
{
|
||||||
|
TRY(result.push_back({ .type = LZ77Entry::Type::Literal, .as = { .literal = data[i] }}));
|
||||||
|
TRY(result.push_back(lazy_match));
|
||||||
|
advance = 1 + lazy_match.as.dist_length.length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(result.push_back(match));
|
||||||
|
advance = match.as.dist_length.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Compressor::compress_block(BAN::ConstByteSpan data, bool final)
|
||||||
|
{
|
||||||
|
// FIXME: use fixed trees or uncompressed blocks
|
||||||
|
|
||||||
|
auto lz77_entries = TRY(lz77_compress(data));
|
||||||
|
|
||||||
|
#if LIBDEFLATE_AVOID_STACK
|
||||||
|
BAN::Vector<size_t> lit_len_freq, dist_freq;
|
||||||
|
TRY(lit_len_freq.resize(286, 0));
|
||||||
|
TRY(dist_freq.resize(30, 0));
|
||||||
|
#else
|
||||||
|
BAN::Array<size_t, 286> lit_len_freq(0);
|
||||||
|
BAN::Array<size_t, 30> dist_freq(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
get_frequencies(lz77_entries.span(), lit_len_freq.span(), dist_freq.span());
|
||||||
|
|
||||||
|
#if LIBDEFLATE_AVOID_STACK
|
||||||
|
BAN::Vector<Leaf> lit_len_tree, dist_tree;
|
||||||
|
TRY(lit_len_tree.resize(286));
|
||||||
|
TRY(dist_tree.resize(30));
|
||||||
|
#else
|
||||||
|
BAN::Array<Leaf, 286> lit_len_tree;
|
||||||
|
BAN::Array<Leaf, 30> dist_tree;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TRY(create_huffman_tree(lit_len_freq.span(), lit_len_tree.span()));
|
||||||
|
TRY(create_huffman_tree(dist_freq.span(), dist_tree.span()));
|
||||||
|
|
||||||
|
auto info = TRY(build_code_length_info(lit_len_tree.span(), dist_tree.span()));
|
||||||
|
|
||||||
|
TRY(m_stream.write_bits(final, 1));
|
||||||
|
TRY(m_stream.write_bits(2, 2));
|
||||||
|
|
||||||
|
TRY(m_stream.write_bits(info.hlit - 257, 5));
|
||||||
|
TRY(m_stream.write_bits(info.hdist - 1, 5));
|
||||||
|
TRY(m_stream.write_bits(info.hclen - 4, 4));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < info.hclen; i++)
|
||||||
|
TRY(m_stream.write_bits(info.code_length[i], 3));
|
||||||
|
|
||||||
|
for (const auto entry : info.encoding)
|
||||||
|
{
|
||||||
|
const auto symbol = info.code_length_tree[entry.symbol];
|
||||||
|
TRY(m_stream.write_bits(reverse_bits(symbol.code, symbol.length), symbol.length));
|
||||||
|
TRY(m_stream.write_bits(entry.extra_data, entry.extra_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto entry : lz77_entries)
|
||||||
|
{
|
||||||
|
switch (entry.type)
|
||||||
|
{
|
||||||
|
case LZ77Entry::Type::Literal:
|
||||||
|
{
|
||||||
|
const auto symbol = lit_len_tree[entry.as.literal];
|
||||||
|
TRY(m_stream.write_bits(reverse_bits(symbol.code, symbol.length), symbol.length));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LZ77Entry::Type::DistLength:
|
||||||
|
{
|
||||||
|
const auto len_encoding = get_len_encoding(entry.as.dist_length.length);
|
||||||
|
const auto len_code = lit_len_tree[len_encoding.symbol];
|
||||||
|
TRY(m_stream.write_bits(reverse_bits(len_code.code, len_code.length), len_code.length));
|
||||||
|
TRY(m_stream.write_bits(len_encoding.extra_data, len_encoding.extra_len));
|
||||||
|
|
||||||
|
const auto dist_encoding = get_dist_encoding(entry.as.dist_length.distance);
|
||||||
|
const auto dist_code = dist_tree[dist_encoding.symbol];
|
||||||
|
TRY(m_stream.write_bits(reverse_bits(dist_code.code, dist_code.length), dist_code.length));
|
||||||
|
TRY(m_stream.write_bits(dist_encoding.extra_data, dist_encoding.extra_len));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto end_code = lit_len_tree[256];
|
||||||
|
TRY(m_stream.write_bits(reverse_bits(end_code.code, end_code.length), end_code.length));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> Compressor::compress()
|
||||||
|
{
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case StreamType::Raw:
|
||||||
|
break;
|
||||||
|
case StreamType::Zlib:
|
||||||
|
TRY(m_stream.write_bits(0x78, 8)); // deflate with 32k window
|
||||||
|
TRY(m_stream.write_bits(0x9C, 8)); // default compression
|
||||||
|
checksum = calculate_adler32(m_data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t max_block_size = 16 * 1024;
|
||||||
|
while (!m_data.empty())
|
||||||
|
{
|
||||||
|
const size_t block_size = BAN::Math::min<size_t>(m_data.size(), max_block_size);
|
||||||
|
TRY(compress_block(m_data.slice(0, block_size), block_size == m_data.size()));
|
||||||
|
m_data = m_data.slice(block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(m_stream.pad_to_byte_boundary());
|
||||||
|
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case StreamType::Raw:
|
||||||
|
break;
|
||||||
|
case StreamType::Zlib:
|
||||||
|
TRY(m_stream.write_bits(checksum >> 24, 8));
|
||||||
|
TRY(m_stream.write_bits(checksum >> 16, 8));
|
||||||
|
TRY(m_stream.write_bits(checksum >> 8, 8));
|
||||||
|
TRY(m_stream.write_bits(checksum >> 0, 8));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_stream.take_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
#include <LibDEFLATE/Decompressor.h>
|
||||||
|
#include <LibDEFLATE/Utils.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
union ZLibHeader
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t cm : 4;
|
||||||
|
uint8_t cinfo : 4;
|
||||||
|
uint8_t fcheck : 5;
|
||||||
|
uint8_t fdict : 1;
|
||||||
|
uint8_t flevel : 2;
|
||||||
|
};
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t raw1;
|
||||||
|
uint8_t raw2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::ErrorOr<uint16_t> Decompressor::read_symbol(const HuffmanTree& tree)
|
||||||
|
{
|
||||||
|
const uint8_t instant_bits = tree.instant_bits();
|
||||||
|
|
||||||
|
uint16_t code = reverse_bits(TRY(m_stream.peek_bits(instant_bits)), instant_bits);
|
||||||
|
if (auto symbol = tree.get_symbol_instant(code); symbol.has_value())
|
||||||
|
{
|
||||||
|
MUST(m_stream.take_bits(symbol->len));
|
||||||
|
return symbol->symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
MUST(m_stream.take_bits(instant_bits));
|
||||||
|
|
||||||
|
uint8_t len = instant_bits;
|
||||||
|
while (len < tree.max_bits())
|
||||||
|
{
|
||||||
|
code = (code << 1) | TRY(m_stream.take_bits(1));
|
||||||
|
len++;
|
||||||
|
if (auto symbol = tree.get_symbol(code, len); symbol.has_value())
|
||||||
|
return symbol.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::inflate_block(const HuffmanTree& length_tree, const HuffmanTree& distance_tree)
|
||||||
|
{
|
||||||
|
uint16_t symbol;
|
||||||
|
while ((symbol = TRY(read_symbol(length_tree))) != 256)
|
||||||
|
{
|
||||||
|
if (symbol < 256)
|
||||||
|
{
|
||||||
|
TRY(m_output.push_back(symbol));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t length_base[] {
|
||||||
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||||
|
};
|
||||||
|
constexpr uint8_t length_extra_bits[] {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint16_t distance_base[] {
|
||||||
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
|
||||||
|
};
|
||||||
|
constexpr uint8_t distance_extra_bits[] {
|
||||||
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||||
|
};
|
||||||
|
|
||||||
|
if (symbol > 285)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
symbol -= 257;
|
||||||
|
|
||||||
|
const uint16_t length = length_base[symbol] + TRY(m_stream.take_bits(length_extra_bits[symbol]));
|
||||||
|
|
||||||
|
uint16_t distance_code;
|
||||||
|
if (distance_tree.empty())
|
||||||
|
distance_code = reverse_bits(TRY(m_stream.take_bits(5)), 5);
|
||||||
|
else
|
||||||
|
distance_code = TRY(read_symbol(distance_tree));
|
||||||
|
if (distance_code > 29)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
const uint16_t distance = distance_base[distance_code] + TRY(m_stream.take_bits(distance_extra_bits[distance_code]));
|
||||||
|
|
||||||
|
const size_t orig_size = m_output.size();
|
||||||
|
const size_t offset = orig_size - distance;
|
||||||
|
TRY(m_output.resize(orig_size + length));
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
m_output[orig_size + i] = m_output[offset + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::handle_header()
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case StreamType::Raw:
|
||||||
|
return {};
|
||||||
|
case StreamType::Zlib:
|
||||||
|
{
|
||||||
|
ZLibHeader header;
|
||||||
|
header.raw1 = TRY(m_stream.take_bits(8));
|
||||||
|
header.raw2 = TRY(m_stream.take_bits(8));
|
||||||
|
|
||||||
|
if (((header.raw1 << 8) | header.raw2) % 31)
|
||||||
|
{
|
||||||
|
dwarnln("zlib header checksum failed");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.cm != 8)
|
||||||
|
{
|
||||||
|
dwarnln("zlib does not use DEFLATE");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.fdict)
|
||||||
|
{
|
||||||
|
TRY(m_stream.take_bits(16));
|
||||||
|
TRY(m_stream.take_bits(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::handle_footer()
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case StreamType::Raw:
|
||||||
|
return {};
|
||||||
|
case StreamType::Zlib:
|
||||||
|
{
|
||||||
|
m_stream.skip_to_byte_boundary();
|
||||||
|
|
||||||
|
uint32_t adler32 = 0;
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
adler32 = (adler32 << 8) | TRY(m_stream.take_bits(8));
|
||||||
|
|
||||||
|
if (adler32 != calculate_adler32(m_output.span()))
|
||||||
|
{
|
||||||
|
dwarnln("zlib final adler32 checksum failed");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::decompress_type0()
|
||||||
|
{
|
||||||
|
m_stream.skip_to_byte_boundary();
|
||||||
|
const uint16_t len = TRY(m_stream.take_bits(16));
|
||||||
|
const uint16_t nlen = TRY(m_stream.take_bits(16));
|
||||||
|
if (len != 0xFFFF - nlen)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
const size_t orig_size = m_output.size();
|
||||||
|
TRY(m_output.resize(orig_size + len));
|
||||||
|
TRY(m_stream.take_byte_aligned(&m_output[orig_size], len));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::decompress_type1()
|
||||||
|
{
|
||||||
|
if (!m_fixed_tree.has_value())
|
||||||
|
m_fixed_tree = TRY(HuffmanTree::fixed_tree());
|
||||||
|
TRY(inflate_block(m_fixed_tree.value(), {}));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Decompressor::decompress_type2()
|
||||||
|
{
|
||||||
|
constexpr uint8_t code_length_order[] {
|
||||||
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16_t hlit = TRY(m_stream.take_bits(5)) + 257;
|
||||||
|
const uint8_t hdist = TRY(m_stream.take_bits(5)) + 1;
|
||||||
|
const uint8_t hclen = TRY(m_stream.take_bits(4)) + 4;
|
||||||
|
|
||||||
|
uint8_t code_lengths[19] {};
|
||||||
|
for (size_t i = 0; i < hclen; i++)
|
||||||
|
code_lengths[code_length_order[i]] = TRY(m_stream.take_bits(3));
|
||||||
|
const auto code_length_tree = TRY(HuffmanTree::create({ code_lengths, 19 }));
|
||||||
|
|
||||||
|
uint8_t bit_lengths[286 + 32] {};
|
||||||
|
size_t bit_lengths_len = 0;
|
||||||
|
|
||||||
|
uint16_t last_symbol = 0;
|
||||||
|
while (bit_lengths_len < hlit + hdist)
|
||||||
|
{
|
||||||
|
uint16_t symbol = TRY(read_symbol(code_length_tree));
|
||||||
|
if (symbol > 18)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
uint8_t count;
|
||||||
|
if (symbol <= 15)
|
||||||
|
{
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
else if (symbol == 16)
|
||||||
|
{
|
||||||
|
symbol = last_symbol;
|
||||||
|
count = TRY(m_stream.take_bits(2)) + 3;
|
||||||
|
}
|
||||||
|
else if (symbol == 17)
|
||||||
|
{
|
||||||
|
symbol = 0;
|
||||||
|
count = TRY(m_stream.take_bits(3)) + 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
symbol = 0;
|
||||||
|
count = TRY(m_stream.take_bits(7)) + 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(bit_lengths_len + count <= hlit + hdist);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < count; i++)
|
||||||
|
bit_lengths[bit_lengths_len++] = symbol;
|
||||||
|
last_symbol = symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(inflate_block(
|
||||||
|
TRY(HuffmanTree::create({ bit_lengths, hlit })),
|
||||||
|
TRY(HuffmanTree::create({ bit_lengths + hlit, hdist }))
|
||||||
|
));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> Decompressor::decompress()
|
||||||
|
{
|
||||||
|
TRY(handle_header());
|
||||||
|
|
||||||
|
bool bfinal = false;
|
||||||
|
while (!bfinal)
|
||||||
|
{
|
||||||
|
bfinal = TRY(m_stream.take_bits(1));
|
||||||
|
switch (TRY(m_stream.take_bits(2)))
|
||||||
|
{
|
||||||
|
case 0b00:
|
||||||
|
TRY(decompress_type0());
|
||||||
|
break;
|
||||||
|
case 0b01:
|
||||||
|
TRY(decompress_type1());
|
||||||
|
break;
|
||||||
|
case 0b10:
|
||||||
|
TRY(decompress_type2());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(handle_footer());
|
||||||
|
|
||||||
|
return BAN::move(m_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
#include <LibDEFLATE/HuffmanTree.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
HuffmanTree& HuffmanTree::operator=(HuffmanTree&& other)
|
||||||
|
{
|
||||||
|
m_instant_bits = other.m_instant_bits;
|
||||||
|
m_min_bits = other.m_min_bits;
|
||||||
|
m_max_bits = other.m_max_bits;
|
||||||
|
|
||||||
|
m_instant = BAN::move(other.m_instant);
|
||||||
|
m_min_code = BAN::move(other.m_min_code);
|
||||||
|
m_slow_table = BAN::move(other.m_slow_table);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<HuffmanTree> HuffmanTree::create(BAN::Span<const uint8_t> bit_lengths)
|
||||||
|
{
|
||||||
|
HuffmanTree result;
|
||||||
|
TRY(result.initialize(bit_lengths));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> HuffmanTree::initialize(BAN::Span<const uint8_t> bit_lengths)
|
||||||
|
{
|
||||||
|
m_max_bits = 0;
|
||||||
|
m_min_bits = MAX_BITS;
|
||||||
|
|
||||||
|
uint16_t max_sym = 0;
|
||||||
|
uint16_t bl_count[MAX_BITS + 1] {};
|
||||||
|
for (size_t sym = 0; sym < bit_lengths.size(); sym++)
|
||||||
|
{
|
||||||
|
if (bit_lengths[sym] == 0)
|
||||||
|
continue;
|
||||||
|
m_max_bits = BAN::Math::max(bit_lengths[sym], m_max_bits);
|
||||||
|
m_min_bits = BAN::Math::min(bit_lengths[sym], m_min_bits);
|
||||||
|
bl_count[bit_lengths[sym]]++;
|
||||||
|
max_sym = sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t next_code[MAX_BITS + 1] {};
|
||||||
|
|
||||||
|
uint16_t code = 0;
|
||||||
|
for (uint8_t bits = 1; bits <= MAX_BITS; bits++)
|
||||||
|
{
|
||||||
|
code = (code + bl_count[bits - 1]) << 1;
|
||||||
|
next_code[bits] = code;
|
||||||
|
m_min_code[bits] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Vector<Leaf> tree;
|
||||||
|
TRY(tree.resize(max_sym + 1, { .code = 0, .len = 0 }));
|
||||||
|
for (uint16_t sym = 0; sym <= max_sym; sym++)
|
||||||
|
{
|
||||||
|
tree[sym].len = bit_lengths[sym];
|
||||||
|
if (const uint8_t len = tree[sym].len)
|
||||||
|
tree[sym].code = next_code[len]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(build_instant_table(tree.span()));
|
||||||
|
TRY(build_slow_table(tree.span()));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> HuffmanTree::build_instant_table(BAN::Span<const Leaf> tree)
|
||||||
|
{
|
||||||
|
m_instant_bits = BAN::Math::min<uint8_t>(9, m_max_bits);
|
||||||
|
TRY(m_instant.resize(1 << m_instant_bits, {}));
|
||||||
|
|
||||||
|
for (uint16_t sym = 0; sym < tree.size(); sym++)
|
||||||
|
{
|
||||||
|
if (tree[sym].len == 0 || tree[sym].len > m_instant_bits)
|
||||||
|
continue;
|
||||||
|
const uint16_t code = tree[sym].code;
|
||||||
|
const uint16_t shift = m_instant_bits - tree[sym].len;
|
||||||
|
for (uint16_t j = code << shift; j < (code + 1) << shift; j++)
|
||||||
|
m_instant[j] = { sym, tree[sym].len };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> HuffmanTree::build_slow_table(BAN::Span<const Leaf> tree)
|
||||||
|
{
|
||||||
|
TRY(m_slow_table.resize(MAX_BITS + 1));
|
||||||
|
for (uint16_t sym = 0; sym < tree.size(); sym++)
|
||||||
|
{
|
||||||
|
const auto leaf = tree[sym];
|
||||||
|
if (leaf.len == 0)
|
||||||
|
continue;
|
||||||
|
const size_t offset = leaf.code - m_min_code[leaf.len];
|
||||||
|
if (offset >= m_slow_table[leaf.len].size())
|
||||||
|
TRY(m_slow_table[leaf.len].resize(offset + 1));
|
||||||
|
m_slow_table[leaf.len][offset] = sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BAN::ErrorOr<HuffmanTree> HuffmanTree::fixed_tree()
|
||||||
|
{
|
||||||
|
struct BitLengths
|
||||||
|
{
|
||||||
|
consteval BitLengths()
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for (; i <= 143; i++) values[i] = 8;
|
||||||
|
for (; i <= 255; i++) values[i] = 9;
|
||||||
|
for (; i <= 279; i++) values[i] = 7;
|
||||||
|
for (; i <= 287; i++) values[i] = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Array<uint8_t, 288> values;
|
||||||
|
};
|
||||||
|
static constexpr BitLengths bit_lengths;
|
||||||
|
return TRY(HuffmanTree::create(bit_lengths.values.span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<HuffmanTree::Instant> HuffmanTree::get_symbol_instant(uint16_t code) const
|
||||||
|
{
|
||||||
|
ASSERT(code < m_instant.size());
|
||||||
|
if (const auto entry = m_instant[code]; entry.len)
|
||||||
|
return entry;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<uint16_t> HuffmanTree::get_symbol(uint16_t code, uint8_t len) const
|
||||||
|
{
|
||||||
|
ASSERT(len <= m_max_bits);
|
||||||
|
const auto& symbols = m_slow_table[len];
|
||||||
|
const size_t offset = code - m_min_code[len];
|
||||||
|
if (symbols.size() <= offset)
|
||||||
|
return {};
|
||||||
|
return symbols[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
class BitInputStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BitInputStream(BAN::ConstByteSpan data)
|
||||||
|
: m_data(data)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<uint16_t> peek_bits(size_t count)
|
||||||
|
{
|
||||||
|
ASSERT(count <= 16);
|
||||||
|
|
||||||
|
while (m_bit_buffer_len < count)
|
||||||
|
{
|
||||||
|
if (m_data.empty())
|
||||||
|
return BAN::Error::from_errno(ENOBUFS);
|
||||||
|
m_bit_buffer |= m_data[0] << m_bit_buffer_len;
|
||||||
|
m_bit_buffer_len += 8;
|
||||||
|
m_data = m_data.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_bit_buffer & ((1 << count) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<uint16_t> take_bits(size_t count)
|
||||||
|
{
|
||||||
|
const uint16_t result = TRY(peek_bits(count));
|
||||||
|
m_bit_buffer >>= count;
|
||||||
|
m_bit_buffer_len -= count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> take_byte_aligned(uint8_t* output, size_t bytes)
|
||||||
|
{
|
||||||
|
ASSERT(m_bit_buffer % 8 == 0);
|
||||||
|
|
||||||
|
while (m_bit_buffer_len && bytes)
|
||||||
|
{
|
||||||
|
*output++ = m_bit_buffer;
|
||||||
|
m_bit_buffer >>= 8;
|
||||||
|
m_bit_buffer_len -= 8;
|
||||||
|
bytes--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes > m_data.size())
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
memcpy(output, m_data.data(), bytes);
|
||||||
|
m_data = m_data.slice(bytes);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_to_byte_boundary()
|
||||||
|
{
|
||||||
|
const size_t bits_to_remove = m_bit_buffer_len % 8;
|
||||||
|
m_bit_buffer >>= bits_to_remove;
|
||||||
|
m_bit_buffer_len -= bits_to_remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::ConstByteSpan m_data;
|
||||||
|
uint32_t m_bit_buffer { 0 };
|
||||||
|
uint8_t m_bit_buffer_len { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitOutputStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BAN::ErrorOr<void> write_bits(uint16_t value, size_t count)
|
||||||
|
{
|
||||||
|
ASSERT(m_bit_buffer_len < 8);
|
||||||
|
ASSERT(count <= 16);
|
||||||
|
|
||||||
|
const uint16_t mask = (1 << count) - 1;
|
||||||
|
m_bit_buffer |= (value & mask) << m_bit_buffer_len;
|
||||||
|
m_bit_buffer_len += count;
|
||||||
|
|
||||||
|
while (m_bit_buffer_len >= 8)
|
||||||
|
{
|
||||||
|
TRY(m_data.push_back(m_bit_buffer));
|
||||||
|
m_bit_buffer >>= 8;
|
||||||
|
m_bit_buffer_len -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> pad_to_byte_boundary()
|
||||||
|
{
|
||||||
|
ASSERT(m_bit_buffer_len < 8);
|
||||||
|
if (m_bit_buffer_len == 0)
|
||||||
|
return {};
|
||||||
|
TRY(m_data.push_back(m_bit_buffer));
|
||||||
|
m_bit_buffer = 0;
|
||||||
|
m_bit_buffer_len = 0;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Vector<uint8_t> take_buffer()
|
||||||
|
{
|
||||||
|
ASSERT(m_bit_buffer_len == 0);
|
||||||
|
return BAN::move(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::Vector<uint8_t> m_data;
|
||||||
|
uint32_t m_bit_buffer { 0 };
|
||||||
|
uint8_t m_bit_buffer_len { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
#include <BAN/HashMap.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
#include <LibDEFLATE/BitStream.h>
|
||||||
|
#include <LibDEFLATE/StreamType.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
class Compressor
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(Compressor);
|
||||||
|
BAN_NON_MOVABLE(Compressor);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using HashChain = BAN::LinkedList<BAN::ConstByteSpan>;
|
||||||
|
|
||||||
|
struct LZ77Entry
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Literal,
|
||||||
|
DistLength,
|
||||||
|
} type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t literal;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint16_t length;
|
||||||
|
uint16_t distance;
|
||||||
|
} dist_length;
|
||||||
|
} as;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Compressor(BAN::ConstByteSpan data, StreamType type)
|
||||||
|
: m_type(type)
|
||||||
|
, m_data(data)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> compress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::ErrorOr<void> compress_block(BAN::ConstByteSpan, bool final);
|
||||||
|
|
||||||
|
uint32_t get_hash_key(BAN::ConstByteSpan needle) const;
|
||||||
|
BAN::ErrorOr<void> update_hash_chain(size_t count);
|
||||||
|
|
||||||
|
BAN::ErrorOr<LZ77Entry> find_longest_match(BAN::ConstByteSpan needle) const;
|
||||||
|
BAN::ErrorOr<BAN::Vector<LZ77Entry>> lz77_compress(BAN::ConstByteSpan data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const StreamType m_type;
|
||||||
|
BAN::ConstByteSpan m_data;
|
||||||
|
BitOutputStream m_stream;
|
||||||
|
|
||||||
|
size_t m_hash_chain_index { 0 };
|
||||||
|
BAN::HashMap<uint32_t, HashChain> m_hash_chain;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
#include <LibDEFLATE/BitStream.h>
|
||||||
|
#include <LibDEFLATE/HuffmanTree.h>
|
||||||
|
#include <LibDEFLATE/StreamType.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
class Decompressor
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(Decompressor);
|
||||||
|
BAN_NON_MOVABLE(Decompressor);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Decompressor(BAN::ConstByteSpan data, StreamType type)
|
||||||
|
: m_type(type)
|
||||||
|
, m_stream(data)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> decompress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::ErrorOr<uint16_t> read_symbol(const HuffmanTree& tree);
|
||||||
|
BAN::ErrorOr<void> inflate_block(const HuffmanTree& length_tree, const HuffmanTree& distance_tree);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> decompress_type0();
|
||||||
|
BAN::ErrorOr<void> decompress_type1();
|
||||||
|
BAN::ErrorOr<void> decompress_type2();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> handle_header();
|
||||||
|
BAN::ErrorOr<void> handle_footer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const StreamType m_type;
|
||||||
|
BitInputStream m_stream;
|
||||||
|
BAN::Vector<uint8_t> m_output;
|
||||||
|
BAN::Optional<HuffmanTree> m_fixed_tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Array.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
class HuffmanTree
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(HuffmanTree);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MAX_BITS = 15;
|
||||||
|
|
||||||
|
struct Leaf
|
||||||
|
{
|
||||||
|
uint16_t code;
|
||||||
|
uint8_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instant
|
||||||
|
{
|
||||||
|
uint16_t symbol;
|
||||||
|
uint8_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
HuffmanTree() {}
|
||||||
|
HuffmanTree(HuffmanTree&& other) { *this = BAN::move(other); }
|
||||||
|
HuffmanTree& operator=(HuffmanTree&& other);
|
||||||
|
|
||||||
|
static BAN::ErrorOr<HuffmanTree> create(BAN::Span<const uint8_t> bit_lengths);
|
||||||
|
|
||||||
|
static BAN::ErrorOr<HuffmanTree> fixed_tree();
|
||||||
|
BAN::Optional<Instant> get_symbol_instant(uint16_t code) const;
|
||||||
|
|
||||||
|
BAN::Optional<uint16_t> get_symbol(uint16_t code, uint8_t len) const;
|
||||||
|
|
||||||
|
uint8_t instant_bits() const { return m_instant_bits; }
|
||||||
|
uint8_t min_bits() const { return m_min_bits; }
|
||||||
|
uint8_t max_bits() const { return m_max_bits; }
|
||||||
|
bool empty() const { return m_min_bits == 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::ErrorOr<void> initialize(BAN::Span<const uint8_t> bit_lengths);
|
||||||
|
BAN::ErrorOr<void> build_instant_table(BAN::Span<const Leaf> tree);
|
||||||
|
BAN::ErrorOr<void> build_slow_table(BAN::Span<const Leaf> tree);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t m_instant_bits { 0 };
|
||||||
|
uint8_t m_min_bits { 0 };
|
||||||
|
uint8_t m_max_bits { 0 };
|
||||||
|
|
||||||
|
BAN::Vector<Instant> m_instant;
|
||||||
|
BAN::Array<uint16_t, MAX_BITS + 1> m_min_code;
|
||||||
|
BAN::Vector<BAN::Vector<uint16_t>> m_slow_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class StreamType
|
||||||
|
{
|
||||||
|
Raw,
|
||||||
|
Zlib,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
|
||||||
|
namespace LibDEFLATE
|
||||||
|
{
|
||||||
|
|
||||||
|
inline uint32_t calculate_adler32(BAN::ConstByteSpan data)
|
||||||
|
{
|
||||||
|
uint32_t s1 = 1;
|
||||||
|
uint32_t s2 = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
s1 = (s1 + data[i]) % 65521;
|
||||||
|
s2 = (s2 + s1) % 65521;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (s2 << 16) | s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint16_t reverse_bits(uint16_t value, size_t count)
|
||||||
|
{
|
||||||
|
uint16_t reverse = 0;
|
||||||
|
for (uint8_t bit = 0; bit < count; bit++)
|
||||||
|
reverse |= ((value >> bit) & 1) << (count - bit - 1);
|
||||||
|
return reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
set(LIBGUI_SOURCES
|
||||||
|
Font.cpp
|
||||||
|
PSF.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(libfont ${LIBGUI_SOURCES})
|
||||||
|
banan_link_library(libfont ban)
|
||||||
|
|
||||||
|
banan_install_headers(libfont)
|
||||||
|
install(TARGETS libfont OPTIONAL)
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibFont/PSF.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace LibFont
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<Font> Font::load(BAN::StringView path)
|
||||||
|
{
|
||||||
|
BAN::Vector<uint8_t> file_data;
|
||||||
|
|
||||||
|
char path_buffer[PATH_MAX];
|
||||||
|
strncpy(path_buffer, path.data(), path.size());
|
||||||
|
path_buffer[path.size()] = '\0';
|
||||||
|
|
||||||
|
int fd = open(path_buffer, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
BAN::ScopeGuard file_closer([fd] { close(fd); });
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
TRY(file_data.resize(st.st_size));
|
||||||
|
|
||||||
|
ssize_t total_read = 0;
|
||||||
|
while (total_read < st.st_size)
|
||||||
|
{
|
||||||
|
ssize_t nread = read(fd, file_data.data() + total_read, st.st_size - total_read);
|
||||||
|
if (nread == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
total_read += nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
return load(BAN::ConstByteSpan(file_data.span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Font> Font::load(BAN::ConstByteSpan font_data)
|
||||||
|
{
|
||||||
|
if (is_psf1(font_data))
|
||||||
|
return TRY(parse_psf1(font_data));
|
||||||
|
if (is_psf2(font_data))
|
||||||
|
return TRY(parse_psf2(font_data));
|
||||||
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
#include <BAN/Debug.h>
|
||||||
|
#include <BAN/Endianness.h>
|
||||||
|
#include <BAN/UTF8.h>
|
||||||
|
|
||||||
|
#include <LibFont/PSF.h>
|
||||||
|
|
||||||
|
#define PSF1_MAGIC0 0x36
|
||||||
|
#define PSF1_MAGIC1 0x04
|
||||||
|
#define PSF1_MODE512 0x01
|
||||||
|
#define PSF1_MODEHASTAB 0x02
|
||||||
|
#define PSF1_MODEHASSEQ 0x04
|
||||||
|
#define PSF1_STARTSEQ 0xFFFE
|
||||||
|
#define PSF1_SEPARATOR 0xFFFF
|
||||||
|
|
||||||
|
#define PSF2_MAGIC0 0x72
|
||||||
|
#define PSF2_MAGIC1 0xB5
|
||||||
|
#define PSF2_MAGIC2 0x4A
|
||||||
|
#define PSF2_MAGIC3 0x86
|
||||||
|
#define PSF2_HAS_UNICODE_TABLE 0x01
|
||||||
|
#define PSF2_STARTSEQ 0xFE
|
||||||
|
#define PSF2_SEPARATOR 0xFF
|
||||||
|
|
||||||
|
namespace LibFont
|
||||||
|
{
|
||||||
|
|
||||||
|
bool is_psf1(BAN::ConstByteSpan font_data)
|
||||||
|
{
|
||||||
|
if (font_data.size() < 2)
|
||||||
|
return false;
|
||||||
|
return font_data[0] == PSF1_MAGIC0 && font_data[1] == PSF1_MAGIC1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Font> parse_psf1(BAN::ConstByteSpan font_data)
|
||||||
|
{
|
||||||
|
struct PSF1Header
|
||||||
|
{
|
||||||
|
uint8_t magic[2];
|
||||||
|
uint8_t mode;
|
||||||
|
uint8_t char_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (font_data.size() < sizeof(PSF1Header))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
const auto& header = font_data.as<const PSF1Header>();
|
||||||
|
|
||||||
|
uint32_t glyph_count = header.mode & PSF1_MODE512 ? 512 : 256;
|
||||||
|
uint32_t glyph_size = header.char_size;
|
||||||
|
uint32_t glyph_data_size = glyph_size * glyph_count;
|
||||||
|
|
||||||
|
if (font_data.size() < sizeof(PSF1Header) + glyph_data_size)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
BAN::Vector<uint8_t> glyph_data;
|
||||||
|
TRY(glyph_data.resize(glyph_data_size));
|
||||||
|
memcpy(glyph_data.data(), font_data.data() + sizeof(PSF1Header), glyph_data_size);
|
||||||
|
|
||||||
|
BAN::HashMap<uint32_t, uint32_t> glyph_offsets;
|
||||||
|
TRY(glyph_offsets.reserve(glyph_count));
|
||||||
|
|
||||||
|
bool codepoint_redef = false;
|
||||||
|
bool codepoint_sequence = false;
|
||||||
|
|
||||||
|
if (header.mode & (PSF1_MODEHASTAB | PSF1_MODEHASSEQ))
|
||||||
|
{
|
||||||
|
uint32_t current_index = sizeof(PSF1Header) + glyph_data_size;
|
||||||
|
|
||||||
|
uint32_t glyph_index = 0;
|
||||||
|
while (current_index < font_data.size())
|
||||||
|
{
|
||||||
|
uint16_t lo = font_data[current_index];
|
||||||
|
uint16_t hi = font_data[current_index + 1];
|
||||||
|
uint16_t codepoint = (hi << 8) | lo;
|
||||||
|
|
||||||
|
if (codepoint == PSF1_STARTSEQ)
|
||||||
|
{
|
||||||
|
codepoint_sequence = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (codepoint == PSF1_SEPARATOR)
|
||||||
|
{
|
||||||
|
glyph_index++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (glyph_offsets.contains(codepoint))
|
||||||
|
codepoint_redef = true;
|
||||||
|
else
|
||||||
|
TRY(glyph_offsets.insert(codepoint, glyph_index * glyph_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_index += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < glyph_count; i++)
|
||||||
|
TRY(glyph_offsets.insert(i, i * glyph_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codepoint_redef)
|
||||||
|
dwarnln("Font contains multiple definitions for same codepoint(s)");
|
||||||
|
if (codepoint_sequence)
|
||||||
|
dwarnln("Font contains codepoint sequences (not supported)");
|
||||||
|
|
||||||
|
return Font(BAN::move(glyph_offsets), BAN::move(glyph_data), 8, header.char_size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_psf2(BAN::ConstByteSpan font_data)
|
||||||
|
{
|
||||||
|
if (font_data.size() < 4)
|
||||||
|
return false;
|
||||||
|
return font_data[0] == PSF2_MAGIC0 && font_data[1] == PSF2_MAGIC1 && font_data[2] == PSF2_MAGIC2 && font_data[3] == PSF2_MAGIC3;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Font> parse_psf2(BAN::ConstByteSpan font_data)
|
||||||
|
{
|
||||||
|
struct PSF2Header
|
||||||
|
{
|
||||||
|
uint8_t magic[4];
|
||||||
|
BAN::LittleEndian<uint32_t> version;
|
||||||
|
BAN::LittleEndian<uint32_t> header_size;
|
||||||
|
BAN::LittleEndian<uint32_t> flags;
|
||||||
|
BAN::LittleEndian<uint32_t> glyph_count;
|
||||||
|
BAN::LittleEndian<uint32_t> glyph_size;
|
||||||
|
BAN::LittleEndian<uint32_t> height;
|
||||||
|
BAN::LittleEndian<uint32_t> width;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (font_data.size() < sizeof(PSF2Header))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
const auto& header = font_data.as<const PSF2Header>();
|
||||||
|
|
||||||
|
uint32_t glyph_data_size = header.glyph_count * header.glyph_size;
|
||||||
|
|
||||||
|
if (font_data.size() < glyph_data_size + header.header_size)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
BAN::Vector<uint8_t> glyph_data;
|
||||||
|
TRY(glyph_data.resize(glyph_data_size));
|
||||||
|
memcpy(glyph_data.data(), font_data.data() + header.header_size, glyph_data_size);
|
||||||
|
|
||||||
|
BAN::HashMap<uint32_t, uint32_t> glyph_offsets;
|
||||||
|
TRY(glyph_offsets.reserve(400));
|
||||||
|
|
||||||
|
bool invalid_utf = false;
|
||||||
|
bool codepoint_redef = false;
|
||||||
|
bool codepoint_sequence = false;
|
||||||
|
|
||||||
|
uint8_t bytes[4] {};
|
||||||
|
uint32_t byte_index = 0;
|
||||||
|
if (header.flags & PSF2_HAS_UNICODE_TABLE)
|
||||||
|
{
|
||||||
|
uint32_t glyph_index = 0;
|
||||||
|
for (uint32_t i = glyph_data_size + header.header_size; i < font_data.size(); i++)
|
||||||
|
{
|
||||||
|
uint8_t byte = font_data[i];
|
||||||
|
|
||||||
|
if (byte == PSF2_STARTSEQ)
|
||||||
|
{
|
||||||
|
codepoint_sequence = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (byte == PSF2_SEPARATOR)
|
||||||
|
{
|
||||||
|
if (byte_index)
|
||||||
|
{
|
||||||
|
invalid_utf = true;
|
||||||
|
byte_index = 0;
|
||||||
|
}
|
||||||
|
glyph_index++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT(byte_index < 4);
|
||||||
|
bytes[byte_index++] = byte;
|
||||||
|
|
||||||
|
uint32_t len = BAN::UTF8::byte_length(bytes[0]);
|
||||||
|
|
||||||
|
if (len == BAN::UTF8::invalid)
|
||||||
|
{
|
||||||
|
invalid_utf = true;
|
||||||
|
byte_index = 0;
|
||||||
|
}
|
||||||
|
else if (len == byte_index)
|
||||||
|
{
|
||||||
|
uint32_t codepoint = BAN::UTF8::to_codepoint(bytes);
|
||||||
|
if (codepoint == BAN::UTF8::invalid)
|
||||||
|
invalid_utf = true;
|
||||||
|
else if (glyph_offsets.contains(codepoint))
|
||||||
|
codepoint_redef = true;
|
||||||
|
else
|
||||||
|
TRY(glyph_offsets.insert(codepoint, glyph_index * header.glyph_size));
|
||||||
|
byte_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < header.glyph_count; i++)
|
||||||
|
TRY(glyph_offsets.insert(i, i * header.glyph_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalid_utf)
|
||||||
|
dwarnln("Font contains invalid UTF-8 codepoint(s)");
|
||||||
|
if (codepoint_redef)
|
||||||
|
dwarnln("Font contains multiple definitions for same codepoint(s)");
|
||||||
|
if (codepoint_sequence)
|
||||||
|
dwarnln("Font contains codepoint sequences (not supported)");
|
||||||
|
|
||||||
|
return Font(BAN::move(glyph_offsets), BAN::move(glyph_data), header.width, header.height, header.glyph_size / header.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
|
||||||
|
namespace LibFont
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
#include <BAN/HashMap.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
|
namespace LibFont
|
||||||
|
{
|
||||||
|
|
||||||
|
class Font
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Font() = default;
|
||||||
|
Font(BAN::HashMap<uint32_t, uint32_t>&& glyph_offsets, BAN::Vector<uint8_t>&& glyph_data, uint32_t width, uint32_t height, uint32_t pitch)
|
||||||
|
: m_glyph_offsets(BAN::move(glyph_offsets))
|
||||||
|
, m_glyph_data(BAN::move(glyph_data))
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
, m_pitch(pitch)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
static BAN::ErrorOr<Font> load(BAN::StringView path);
|
||||||
|
static BAN::ErrorOr<Font> load(BAN::ConstByteSpan font_data);
|
||||||
|
#if __is_kernel
|
||||||
|
static BAN::ErrorOr<Font> prefs();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t width() const { return m_width; }
|
||||||
|
uint32_t height() const { return m_height; }
|
||||||
|
uint32_t pitch() const { return m_pitch; }
|
||||||
|
|
||||||
|
bool has_glyph(uint32_t codepoint) const { return glyph(codepoint) != nullptr; }
|
||||||
|
const uint8_t* glyph(uint32_t codepoint) const
|
||||||
|
{
|
||||||
|
auto it = m_glyph_offsets.find(codepoint);
|
||||||
|
if (it == m_glyph_offsets.end())
|
||||||
|
return nullptr;
|
||||||
|
return m_glyph_data.data() + it->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::HashMap<uint32_t, uint32_t> m_glyph_offsets;
|
||||||
|
BAN::Vector<uint8_t> m_glyph_data;
|
||||||
|
uint32_t m_width = 0;
|
||||||
|
uint32_t m_height = 0;
|
||||||
|
uint32_t m_pitch = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
|
||||||
|
namespace LibFont
|
||||||
|
{
|
||||||
|
|
||||||
|
bool is_psf1(BAN::ConstByteSpan);
|
||||||
|
BAN::ErrorOr<Font> parse_psf1(BAN::ConstByteSpan);
|
||||||
|
|
||||||
|
bool is_psf2(BAN::ConstByteSpan);
|
||||||
|
BAN::ErrorOr<Font> parse_psf2(BAN::ConstByteSpan);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
set(LIBGUI_SOURCES
|
||||||
|
MessageBox.cpp
|
||||||
|
Texture.cpp
|
||||||
|
Widget/Button.cpp
|
||||||
|
Widget/Grid.cpp
|
||||||
|
Widget/Label.cpp
|
||||||
|
Widget/RoundedWidget.cpp
|
||||||
|
Widget/TextArea.cpp
|
||||||
|
Widget/Widget.cpp
|
||||||
|
Window.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(libgui ${LIBGUI_SOURCES})
|
||||||
|
banan_link_library(libgui ban)
|
||||||
|
banan_link_library(libgui libfont)
|
||||||
|
banan_link_library(libgui libinput)
|
||||||
|
|
||||||
|
banan_install_headers(libgui)
|
||||||
|
install(TARGETS libgui OPTIONAL)
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include <LibGUI/MessageBox.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
#include <LibGUI/Widget/Button.h>
|
||||||
|
#include <LibGUI/Widget/Grid.h>
|
||||||
|
#include <LibGUI/Widget/TextArea.h>
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> MessageBox::create(BAN::StringView message, BAN::StringView title)
|
||||||
|
{
|
||||||
|
BAN::StringView ok_button = "OK";
|
||||||
|
TRY(create(message, title, { &ok_button, 1 }));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<size_t> MessageBox::create(BAN::StringView message, BAN::StringView title, BAN::Span<BAN::StringView> buttons)
|
||||||
|
{
|
||||||
|
if (buttons.empty())
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
const uint32_t window_width = 300;
|
||||||
|
|
||||||
|
auto root_widget = TRY(Widget::Widget::create({}, 0xFFFFFF, { 0, 0, window_width, 0 }));
|
||||||
|
|
||||||
|
auto text_area = TRY(Widget::TextArea::create(root_widget, message, { 0, 0, window_width, 0}));
|
||||||
|
text_area->style().border_width = 0;
|
||||||
|
text_area->style().color_normal = Widget::Widget::color_invisible;
|
||||||
|
text_area->style().corner_radius = 0;
|
||||||
|
TRY(text_area->set_relative_geometry({ 0.0, 0.0, 1.0, 0.8 }));
|
||||||
|
text_area->show();
|
||||||
|
|
||||||
|
bool waiting = true;
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
auto button_area = TRY(Widget::Grid::create(root_widget, buttons.size(), 1));
|
||||||
|
for (size_t i = 0; i < buttons.size(); i++)
|
||||||
|
{
|
||||||
|
auto button = TRY(Widget::Button::create(button_area, buttons[i]));
|
||||||
|
TRY(button_area->set_widget_position(button, i, 1, 0, 1));
|
||||||
|
button->set_click_callback([&result, &waiting, i] { result = i; waiting = false; });
|
||||||
|
button->show();
|
||||||
|
}
|
||||||
|
TRY(button_area->set_relative_geometry({ 0.0, 0.8, 1.0, 0.2 }));
|
||||||
|
button_area->show();
|
||||||
|
|
||||||
|
const uint32_t button_height = 20;
|
||||||
|
const uint32_t window_height = text_area->get_required_height() + button_height;
|
||||||
|
|
||||||
|
auto attributes = Window::default_attributes;
|
||||||
|
attributes.resizable = true;
|
||||||
|
|
||||||
|
auto window = TRY(Window::create(window_width, window_height, title, attributes));
|
||||||
|
TRY(window->set_root_widget(root_widget));
|
||||||
|
window->set_close_window_event_callback([&waiting] { waiting = false; });
|
||||||
|
|
||||||
|
while (waiting)
|
||||||
|
{
|
||||||
|
window->wait_events();
|
||||||
|
window->poll_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
#include <LibGUI/Texture.h>
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<Texture> Texture::create(uint32_t width, uint32_t height, uint32_t color)
|
||||||
|
{
|
||||||
|
if (BAN::Math::will_addition_overflow(width, height))
|
||||||
|
return BAN::Error::from_errno(EOVERFLOW);
|
||||||
|
|
||||||
|
BAN::Vector<uint32_t> pixels;
|
||||||
|
TRY(pixels.resize(width * height, color));
|
||||||
|
return Texture(BAN::move(pixels), width, height, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Texture::resize(uint32_t new_width, uint32_t new_height)
|
||||||
|
{
|
||||||
|
if (BAN::Math::will_addition_overflow(new_width, new_height))
|
||||||
|
return BAN::Error::from_errno(EOVERFLOW);
|
||||||
|
|
||||||
|
BAN::Vector<uint32_t> pixels;
|
||||||
|
TRY(pixels.resize(new_width * new_height, m_bg_color));
|
||||||
|
|
||||||
|
const uint32_t max_x = BAN::Math::min(new_width, m_width);
|
||||||
|
const uint32_t max_y = BAN::Math::min(new_height, m_height);
|
||||||
|
for (uint32_t y = 0; y < max_y; y++)
|
||||||
|
for (uint32_t x = 0; x < max_x; x++)
|
||||||
|
pixels[y * new_width + x] = m_pixels[y * m_width + x];
|
||||||
|
|
||||||
|
m_width = new_width;
|
||||||
|
m_height = new_height;
|
||||||
|
m_pixels = BAN::move(pixels);
|
||||||
|
|
||||||
|
if (m_has_set_clip)
|
||||||
|
set_clip_area(m_clip_x, m_clip_y, m_clip_w, m_clip_h);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_clip_w = new_width;
|
||||||
|
m_clip_h = new_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
m_clip_x = 0;
|
||||||
|
m_clip_y = 0;
|
||||||
|
m_clip_w = this->width();
|
||||||
|
m_clip_h = this->height();
|
||||||
|
|
||||||
|
|
||||||
|
if (!clamp_to_texture(x, y, width, height))
|
||||||
|
{
|
||||||
|
m_clip_h = 0;
|
||||||
|
m_clip_w = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_clip_x = x;
|
||||||
|
m_clip_y = y;
|
||||||
|
m_clip_w = width;
|
||||||
|
m_clip_h = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_has_set_clip = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color)
|
||||||
|
{
|
||||||
|
if (!clamp_to_texture(x, y, width, height))
|
||||||
|
return;
|
||||||
|
for (uint32_t y_off = 0; y_off < height; y_off++)
|
||||||
|
for (uint32_t x_off = 0; x_off < width; x_off++)
|
||||||
|
set_pixel(x + x_off, y + y_off, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x, uint32_t sub_y, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
int32_t src_x = sub_x, src_y = sub_y;
|
||||||
|
if (!clamp_to_texture(x, y, src_x, src_y, width, height, texture))
|
||||||
|
return;
|
||||||
|
sub_x = src_x;
|
||||||
|
sub_y = src_y;
|
||||||
|
|
||||||
|
for (uint32_t y_off = 0; y_off < height; y_off++)
|
||||||
|
for (uint32_t x_off = 0; x_off < width; x_off++)
|
||||||
|
if (const uint32_t color = texture.get_pixel(sub_x + x_off, sub_y + y_off); color != color_invisible)
|
||||||
|
set_pixel(x + x_off, y + y_off, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
|
||||||
|
{
|
||||||
|
if (tl_y + (int32_t)font.height() < 0 || tl_y >= (int32_t)height())
|
||||||
|
return;
|
||||||
|
if (tl_x + (int32_t)font.width() < 0 || tl_x >= (int32_t)width())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto glyph = font.glyph(codepoint);
|
||||||
|
if (glyph == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int32_t off_y = 0; off_y < (int32_t)font.height(); off_y++)
|
||||||
|
{
|
||||||
|
if (tl_y + off_y < 0)
|
||||||
|
continue;
|
||||||
|
uint32_t abs_y = tl_y + off_y;
|
||||||
|
if (abs_y >= height())
|
||||||
|
break;
|
||||||
|
for (int32_t off_x = 0; off_x < (int32_t)font.width(); off_x++)
|
||||||
|
{
|
||||||
|
if (tl_x + off_x < 0)
|
||||||
|
continue;
|
||||||
|
uint32_t abs_x = tl_x + off_x;
|
||||||
|
if (abs_x >= width())
|
||||||
|
break;
|
||||||
|
const uint8_t bitmask = 1 << (font.width() - off_x - 1);
|
||||||
|
if (glyph[off_y * font.pitch()] & bitmask)
|
||||||
|
set_pixel(abs_x, abs_y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::draw_text(BAN::StringView text, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < text.size(); i++)
|
||||||
|
draw_character(text[i], font, tl_x + (int32_t)(i * font.width()), tl_y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::shift_vertical(int32_t amount)
|
||||||
|
{
|
||||||
|
const uint32_t amount_abs = BAN::Math::abs(amount);
|
||||||
|
if (amount_abs == 0 || amount_abs >= height())
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t* dst = (amount > 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
|
||||||
|
uint32_t* src = (amount < 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
|
||||||
|
memmove(dst, src, width() * (height() - amount_abs) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t uamount)
|
||||||
|
{
|
||||||
|
int32_t amount = uamount;
|
||||||
|
if (dst_y < 0)
|
||||||
|
{
|
||||||
|
amount -= -dst_y;
|
||||||
|
src_y += -dst_y;
|
||||||
|
dst_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
amount = BAN::Math::min<int32_t>(amount, height() - dst_y);
|
||||||
|
if (amount <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int32_t copy_src_y = BAN::Math::clamp<int32_t>(src_y, 0, height());
|
||||||
|
const int32_t copy_amount = BAN::Math::clamp<int32_t>(src_y + amount, 0, height()) - copy_src_y;
|
||||||
|
if (copy_amount > 0)
|
||||||
|
{
|
||||||
|
memmove(
|
||||||
|
&m_pixels[width() * (dst_y + (copy_src_y - src_y))],
|
||||||
|
&m_pixels[width() * copy_src_y],
|
||||||
|
copy_amount * width() * 4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
if (!clamp_to_texture(dst_x, dst_y, src_x, src_y, width, height, *this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool copy_dir = dst_y < src_y;
|
||||||
|
for (uint32_t i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
const uint32_t y_off = copy_dir ? i : height - i - 1;
|
||||||
|
memmove(
|
||||||
|
&m_pixels[(dst_y + y_off) * this->width() + dst_x],
|
||||||
|
&m_pixels[(src_y + y_off) * this->width() + src_x],
|
||||||
|
width * 4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::clamp_to_texture(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
|
||||||
|
{
|
||||||
|
const int32_t min_x = BAN::Math::max<int32_t>(signed_x, m_clip_x);
|
||||||
|
const int32_t min_y = BAN::Math::max<int32_t>(signed_y, m_clip_y);
|
||||||
|
const int32_t max_x = BAN::Math::min<int32_t>(signed_x + (int32_t)width, m_clip_x + m_clip_w);
|
||||||
|
const int32_t max_y = BAN::Math::min<int32_t>(signed_y + (int32_t)height, m_clip_y + m_clip_h);
|
||||||
|
|
||||||
|
if (min_x >= max_x)
|
||||||
|
return false;
|
||||||
|
if (min_y >= max_y)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
signed_x = min_x;
|
||||||
|
signed_y = min_y;
|
||||||
|
width = max_x - min_x;
|
||||||
|
height = max_y - min_y;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Texture::clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture& texture) const
|
||||||
|
{
|
||||||
|
if (width > texture.width())
|
||||||
|
width = texture.width();
|
||||||
|
if (height > texture.height())
|
||||||
|
height = texture.height();
|
||||||
|
|
||||||
|
if (dst_x >= static_cast<int32_t>(m_clip_x + m_clip_w))
|
||||||
|
return false;
|
||||||
|
if (dst_y >= static_cast<int32_t>(m_clip_y + m_clip_h))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (src_x >= static_cast<int32_t>(texture.width()))
|
||||||
|
return false;
|
||||||
|
if (src_y >= static_cast<int32_t>(texture.height()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (dst_x + static_cast<int32_t>(width) > static_cast<int32_t>(m_clip_x + m_clip_w))
|
||||||
|
width = m_clip_x + m_clip_w - dst_x;
|
||||||
|
if (src_x + static_cast<int32_t>(width) > static_cast<int32_t>(texture.width()))
|
||||||
|
width = texture.width() - src_x;
|
||||||
|
|
||||||
|
if (dst_y + static_cast<int32_t>(height) > static_cast<int32_t>(m_clip_y + m_clip_h))
|
||||||
|
height = m_clip_y + m_clip_h - dst_y;
|
||||||
|
if (src_y + static_cast<int32_t>(height) > static_cast<int32_t>(texture.height()))
|
||||||
|
height = texture.height() - src_y;
|
||||||
|
|
||||||
|
int32_t off_x = 0;
|
||||||
|
if (dst_x < static_cast<int32_t>(m_clip_x))
|
||||||
|
off_x = m_clip_x - dst_x;
|
||||||
|
if (src_x + off_x < 0)
|
||||||
|
off_x = -src_x;
|
||||||
|
if (off_x >= static_cast<int32_t>(width))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int32_t off_y = 0;
|
||||||
|
if (dst_y < static_cast<int32_t>(m_clip_y))
|
||||||
|
off_y = m_clip_y - dst_y;
|
||||||
|
if (src_y + off_y < 0)
|
||||||
|
off_y = -src_y;
|
||||||
|
if (off_y >= static_cast<int32_t>(height))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dst_x += off_x;
|
||||||
|
src_x += off_x;
|
||||||
|
dst_y += off_y;
|
||||||
|
src_y += off_y;
|
||||||
|
|
||||||
|
width -= off_x;
|
||||||
|
height -= off_y;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/Button.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Button>> Button::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* button_ptr = new Button(parent, geometry);
|
||||||
|
if (button_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto button = BAN::RefPtr<Button>::adopt(button_ptr);
|
||||||
|
TRY(button->initialize(color_invisible));
|
||||||
|
TRY(button->m_text.append(text));
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Button::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::update_impl()
|
||||||
|
{
|
||||||
|
const bool hover_color = is_hovered() && !is_child_hovered();
|
||||||
|
if (hover_color != m_hover_state)
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::show_impl()
|
||||||
|
{
|
||||||
|
m_hover_state = is_hovered() && !is_child_hovered();
|
||||||
|
|
||||||
|
const auto& font = default_font();
|
||||||
|
const int32_t text_h = font.height();
|
||||||
|
const int32_t text_w = font.width() * m_text.size();
|
||||||
|
|
||||||
|
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
|
||||||
|
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
|
||||||
|
|
||||||
|
m_texture.fill(m_hover_state ? m_style.color_hovered : m_style.color_normal);
|
||||||
|
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Button::on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t event)
|
||||||
|
{
|
||||||
|
if (event.pressed && event.button == LibInput::MouseButton::Left && m_click_callback)
|
||||||
|
m_click_callback();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <LibGUI/Widget/Grid.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Grid>> Grid::create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* grid_ptr = new Grid(parent, geometry, cols, rows);
|
||||||
|
if (grid_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto grid = BAN::RefPtr<Grid>::adopt(grid_ptr);
|
||||||
|
TRY(grid->initialize(color));
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::Rectangle Grid::grid_element_area(const GridElement& element) const
|
||||||
|
{
|
||||||
|
const uint32_t this_x = element.col * width() / m_cols;
|
||||||
|
const uint32_t this_y = element.row * height() / m_rows;
|
||||||
|
|
||||||
|
const uint32_t next_x = (element.col + element.col_span) * width() / m_cols;
|
||||||
|
const uint32_t next_y = (element.row + element.row_span) * height() / m_rows;
|
||||||
|
|
||||||
|
return Widget::Rectangle {
|
||||||
|
.x = static_cast<int32_t>(this_x),
|
||||||
|
.y = static_cast<int32_t>(this_y),
|
||||||
|
.w = next_x - this_x,
|
||||||
|
.h = next_y - this_y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Grid::update_geometry_impl()
|
||||||
|
{
|
||||||
|
for (auto& grid_element : m_grid_elements)
|
||||||
|
TRY(grid_element.widget->set_fixed_geometry(grid_element_area(grid_element)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Grid::set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span)
|
||||||
|
{
|
||||||
|
if (col_span == 0 || row_span == 0)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
if (col + col_span > m_cols)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
if (row + row_span > m_rows)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
ASSERT(widget->parent() == this);
|
||||||
|
TRY(m_grid_elements.emplace_back(widget, col, col_span, row, row_span));
|
||||||
|
TRY(widget->set_fixed_geometry(grid_element_area(m_grid_elements.back())));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/Label.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Label>> Label::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* label_ptr = new Label(parent, geometry);
|
||||||
|
if (label_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto label = BAN::RefPtr<Label>::adopt(label_ptr);
|
||||||
|
TRY(label->initialize(color_invisible));
|
||||||
|
TRY(label->m_text.append(text));
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Label::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Label::show_impl()
|
||||||
|
{
|
||||||
|
const auto& font = default_font();
|
||||||
|
const int32_t text_h = font.height();
|
||||||
|
const int32_t text_w = font.width() * m_text.size();
|
||||||
|
|
||||||
|
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
|
||||||
|
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
|
||||||
|
|
||||||
|
m_texture.fill(m_style.color_normal);
|
||||||
|
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
bool RoundedWidget::contains(Point point) const
|
||||||
|
{
|
||||||
|
if (!Widget::contains(point))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto is_outside_corner =
|
||||||
|
[this]<uint8_t quadrant>(Point point) -> bool
|
||||||
|
{
|
||||||
|
const auto radius = m_style.corner_radius;
|
||||||
|
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
|
||||||
|
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
|
||||||
|
|
||||||
|
if (point.x < static_cast<int32_t>(base_x) || point.x > static_cast<int32_t>(base_x + radius))
|
||||||
|
return false;
|
||||||
|
if (point.y < static_cast<int32_t>(base_y) || point.y > static_cast<int32_t>(base_y + radius))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uint32_t x_off = point.x - base_x;
|
||||||
|
const uint32_t y_off = point.y - base_y;
|
||||||
|
|
||||||
|
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
|
||||||
|
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
|
||||||
|
const uint32_t distance = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
return distance >= radius * radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_outside_corner.operator()<0>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<1>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<2>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<3>(point))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoundedWidget::show_impl()
|
||||||
|
{
|
||||||
|
if (m_style.border_width)
|
||||||
|
{
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
m_texture.width(),
|
||||||
|
m_style.border_width,
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
m_style.border_width,
|
||||||
|
m_texture.height(),
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
m_texture.height() - m_style.border_width,
|
||||||
|
m_texture.width(),
|
||||||
|
m_style.border_width,
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
m_texture.width() - m_style.border_width,
|
||||||
|
0,
|
||||||
|
m_style.border_width,
|
||||||
|
m_texture.height(),
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_style.corner_radius)
|
||||||
|
{
|
||||||
|
const auto draw_corner =
|
||||||
|
[this]<uint8_t quadrant>()
|
||||||
|
{
|
||||||
|
const auto radius = m_style.corner_radius;
|
||||||
|
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
|
||||||
|
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
|
||||||
|
const uint32_t distance_max = radius * radius;
|
||||||
|
const uint32_t distance_min = (radius - m_style.border_width) * (radius - m_style.border_width);
|
||||||
|
|
||||||
|
for (uint32_t y_off = 0; y_off <= radius; y_off++)
|
||||||
|
{
|
||||||
|
for (uint32_t x_off = 0; x_off <= radius; x_off++)
|
||||||
|
{
|
||||||
|
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
|
||||||
|
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
|
||||||
|
const uint32_t distance = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
if (base_x + x_off >= m_texture.width())
|
||||||
|
continue;
|
||||||
|
if (base_y + y_off >= m_texture.height())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (distance > distance_max)
|
||||||
|
m_texture.set_pixel(base_x + x_off, base_y + y_off, color_invisible);
|
||||||
|
else if (distance >= distance_min)
|
||||||
|
m_texture.set_pixel(base_x + x_off, base_y + y_off, m_style.color_border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_corner.operator()<0>();
|
||||||
|
draw_corner.operator()<1>();
|
||||||
|
draw_corner.operator()<2>();
|
||||||
|
draw_corner.operator()<3>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/TextArea.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<TextArea>> TextArea::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* text_area_ptr = new TextArea(parent, geometry);
|
||||||
|
if (text_area_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto text_area = BAN::RefPtr<TextArea>::adopt(text_area_ptr);
|
||||||
|
TRY(text_area->initialize(color_invisible));
|
||||||
|
TRY(text_area->set_text(text));
|
||||||
|
return text_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
TRY(wrap_text());
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TextArea::get_required_height() const
|
||||||
|
{
|
||||||
|
auto& font = default_font();
|
||||||
|
return m_wrapped_text.size() * font.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::wrap_text()
|
||||||
|
{
|
||||||
|
m_wrapped_text.clear();
|
||||||
|
|
||||||
|
if (width() == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto& font = default_font();
|
||||||
|
|
||||||
|
const uint32_t total_columns = width() / font.width();
|
||||||
|
ASSERT(total_columns != 0);
|
||||||
|
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_text.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_text[i] == '\n')
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
else if (isspace(m_text[i]) && m_wrapped_text.back().size() == 0)
|
||||||
|
;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(m_wrapped_text.back().push_back(m_text[i]));
|
||||||
|
|
||||||
|
if (i + 1 < m_text.size() && isspace(m_text[i]) && !isspace(m_text[i + 1]))
|
||||||
|
{
|
||||||
|
size_t word_len = 0;
|
||||||
|
for (size_t j = i + 1; j < m_text.size() && !isspace(m_text[j]); j++)
|
||||||
|
word_len++;
|
||||||
|
if (word_len <= total_columns && m_wrapped_text.back().size() + word_len > total_columns)
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_wrapped_text.back().size() >= total_columns)
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::update_geometry_impl()
|
||||||
|
{
|
||||||
|
TRY(wrap_text());
|
||||||
|
return Widget::update_geometry_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextArea::show_impl()
|
||||||
|
{
|
||||||
|
const auto& font = default_font();
|
||||||
|
|
||||||
|
m_texture.fill(m_style.color_normal);
|
||||||
|
for (int32_t row = 0; row < static_cast<int32_t>(m_wrapped_text.size()); row++)
|
||||||
|
m_texture.draw_text(m_wrapped_text[row].sv(), font, 0, row * font.height(), m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
static BAN::Optional<LibFont::Font> s_default_font;
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Widget::set_default_font(BAN::StringView path)
|
||||||
|
{
|
||||||
|
s_default_font = TRY(LibFont::Font::load(path));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const LibFont::Font& Widget::default_font()
|
||||||
|
{
|
||||||
|
if (!s_default_font.has_value())
|
||||||
|
MUST(set_default_font("/usr/share/fonts/lat0-16.psfu"_sv));
|
||||||
|
return s_default_font.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Widget>> Widget::create(BAN::RefPtr<Widget> parent, uint32_t color, Rectangle area)
|
||||||
|
{
|
||||||
|
auto* widget_ptr = new Widget(parent, area);
|
||||||
|
if (widget_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto widget = BAN::RefPtr<Widget>::adopt(widget_ptr);
|
||||||
|
TRY(widget->initialize(color));
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Widget::initialize(uint32_t color)
|
||||||
|
{
|
||||||
|
m_texture = TRY(Texture::create(width(), height(), color));
|
||||||
|
if (m_parent)
|
||||||
|
TRY(m_parent->m_children.push_back(this));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::is_child_hovered() const
|
||||||
|
{
|
||||||
|
for (auto child : m_children)
|
||||||
|
if (child->m_hovered || child->is_child_hovered())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Widget::set_fixed_geometry(Rectangle area)
|
||||||
|
{
|
||||||
|
TRY(m_texture.resize(area.w, area.h));
|
||||||
|
m_fixed_area = area;
|
||||||
|
m_relative_area.clear();
|
||||||
|
|
||||||
|
TRY(update_geometry_impl());
|
||||||
|
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Widget::set_relative_geometry(FloatRectangle area)
|
||||||
|
{
|
||||||
|
if (area.w < 0.0f || area.h < 0.0f)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
ASSERT(m_parent);
|
||||||
|
|
||||||
|
TRY(set_fixed_geometry({
|
||||||
|
.x = static_cast<int32_t>(area.x * m_parent->width()),
|
||||||
|
.y = static_cast<int32_t>(area.y * m_parent->height()),
|
||||||
|
.w = static_cast<uint32_t>(area.w * m_parent->width()),
|
||||||
|
.h = static_cast<uint32_t>(area.h * m_parent->height()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
m_relative_area = area;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Widget::update_geometry_impl()
|
||||||
|
{
|
||||||
|
for (auto child : m_children)
|
||||||
|
{
|
||||||
|
if (!child->m_relative_area.has_value())
|
||||||
|
continue;
|
||||||
|
TRY(child->set_relative_geometry(child->m_relative_area.value()));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::show()
|
||||||
|
{
|
||||||
|
m_shown = true;
|
||||||
|
|
||||||
|
show_impl();
|
||||||
|
m_changed = true;
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
if (child->is_shown())
|
||||||
|
child->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::hide()
|
||||||
|
{
|
||||||
|
if (!is_shown())
|
||||||
|
return;
|
||||||
|
m_shown = false;
|
||||||
|
|
||||||
|
auto root = m_parent;
|
||||||
|
while (root && root->m_parent)
|
||||||
|
root = root->m_parent;
|
||||||
|
if (root)
|
||||||
|
root->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::before_mouse_move()
|
||||||
|
{
|
||||||
|
if (!is_shown())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_old_hovered = m_hovered;
|
||||||
|
m_hovered = false;
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
child->before_mouse_move();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::after_mouse_move()
|
||||||
|
{
|
||||||
|
if (!is_shown())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_old_hovered != m_hovered)
|
||||||
|
on_hover_change_impl(m_hovered);
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
child->after_mouse_move();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t event)
|
||||||
|
{
|
||||||
|
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
|
||||||
|
return false;
|
||||||
|
if (!is_shown())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_hovered = contains({ event.x, event.y });
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
{
|
||||||
|
auto rel_event = event;
|
||||||
|
rel_event.x -= child->m_fixed_area.x;
|
||||||
|
rel_event.y -= child->m_fixed_area.y;
|
||||||
|
if (child->on_mouse_move(rel_event))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_hovered)
|
||||||
|
return false;
|
||||||
|
return on_mouse_move_impl(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t event)
|
||||||
|
{
|
||||||
|
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
|
||||||
|
return false;
|
||||||
|
if (!is_shown())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
{
|
||||||
|
auto rel_event = event;
|
||||||
|
rel_event.x -= child->m_fixed_area.x;
|
||||||
|
rel_event.y -= child->m_fixed_area.y;
|
||||||
|
if (child->on_mouse_button(rel_event))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contains({ event.x, event.y }))
|
||||||
|
return false;
|
||||||
|
return on_mouse_button_impl(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::Rectangle Widget::render(Texture& output, Point parent_position, Rectangle out_area)
|
||||||
|
{
|
||||||
|
if (!is_shown())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
update_impl();
|
||||||
|
|
||||||
|
Rectangle invalidated {};
|
||||||
|
if (m_changed)
|
||||||
|
{
|
||||||
|
auto where_i_would_be = Rectangle {
|
||||||
|
.x = parent_position.x + m_fixed_area.x,
|
||||||
|
.y = parent_position.y + m_fixed_area.y,
|
||||||
|
.w = m_fixed_area.w,
|
||||||
|
.h = m_fixed_area.h,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto where_to_draw = out_area.overlap(where_i_would_be);
|
||||||
|
|
||||||
|
if (where_to_draw.w && where_to_draw.h)
|
||||||
|
{
|
||||||
|
output.copy_texture(
|
||||||
|
m_texture,
|
||||||
|
where_to_draw.x,
|
||||||
|
where_to_draw.y,
|
||||||
|
where_to_draw.x - where_i_would_be.x,
|
||||||
|
where_to_draw.y - where_i_would_be.y,
|
||||||
|
where_to_draw.w,
|
||||||
|
where_to_draw.h
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidated = where_to_draw;
|
||||||
|
m_changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_area = out_area.overlap({
|
||||||
|
.x = parent_position.x + m_fixed_area.x,
|
||||||
|
.y = parent_position.y + m_fixed_area.y,
|
||||||
|
.w = m_fixed_area.w,
|
||||||
|
.h = m_fixed_area.h,
|
||||||
|
});
|
||||||
|
|
||||||
|
parent_position = {
|
||||||
|
.x = parent_position.x + m_fixed_area.x,
|
||||||
|
.y = parent_position.y + m_fixed_area.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto child : m_children)
|
||||||
|
{
|
||||||
|
invalidated = invalidated.bounding_box(
|
||||||
|
child->render(output, parent_position, out_area)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,393 @@
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
struct ReceivePacket
|
||||||
|
{
|
||||||
|
PacketType type;
|
||||||
|
BAN::Vector<uint8_t> data_with_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BAN::ErrorOr<ReceivePacket> recv_packet(int socket)
|
||||||
|
{
|
||||||
|
uint32_t packet_size;
|
||||||
|
|
||||||
|
{
|
||||||
|
const ssize_t nrecv = recv(socket, &packet_size, sizeof(uint32_t), 0);
|
||||||
|
if (nrecv < 0)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
if (nrecv == 0)
|
||||||
|
return BAN::Error::from_errno(ECONNRESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet_size < sizeof(uint32_t))
|
||||||
|
return BAN::Error::from_literal("invalid packet, does not fit packet id");
|
||||||
|
|
||||||
|
BAN::Vector<uint8_t> packet_data;
|
||||||
|
TRY(packet_data.resize(packet_size));
|
||||||
|
|
||||||
|
size_t total_recv = 0;
|
||||||
|
while (total_recv < packet_size)
|
||||||
|
{
|
||||||
|
const ssize_t nrecv = recv(socket, packet_data.data() + total_recv, packet_size - total_recv, 0);
|
||||||
|
if (nrecv < 0)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
if (nrecv == 0)
|
||||||
|
return BAN::Error::from_errno(ECONNRESET);
|
||||||
|
total_recv += nrecv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReceivePacket {
|
||||||
|
*reinterpret_cast<PacketType*>(packet_data.data()),
|
||||||
|
packet_data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::~Window()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::UniqPtr<Window>> Window::create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes)
|
||||||
|
{
|
||||||
|
int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (server_fd == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
BAN::ScopeGuard server_closer([server_fd] { close(server_fd); });
|
||||||
|
|
||||||
|
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
if (epoll_fd == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
BAN::ScopeGuard epoll_closer([epoll_fd] { close(epoll_fd); });
|
||||||
|
|
||||||
|
epoll_event epoll_event {
|
||||||
|
.events = EPOLLIN,
|
||||||
|
.data = { .fd = server_fd },
|
||||||
|
};
|
||||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &epoll_event) == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
|
||||||
|
if (fcntl(server_fd, F_SETFD, fcntl(server_fd, F_GETFD) | FD_CLOEXEC) == -1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
|
||||||
|
timespec start_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
sockaddr_un server_address;
|
||||||
|
server_address.sun_family = AF_UNIX;
|
||||||
|
strcpy(server_address.sun_path, s_window_server_socket.data());
|
||||||
|
if (connect(server_fd, (sockaddr*)&server_address, sizeof(server_address)) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
timespec current_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
||||||
|
time_t duration_s = (current_time.tv_sec - start_time.tv_sec) + (current_time.tv_nsec >= start_time.tv_nsec);
|
||||||
|
if (duration_s > 1)
|
||||||
|
return BAN::Error::from_errno(ETIMEDOUT);
|
||||||
|
|
||||||
|
timespec sleep_time;
|
||||||
|
sleep_time.tv_sec = 0;
|
||||||
|
sleep_time.tv_nsec = 1'000'000;
|
||||||
|
nanosleep(&sleep_time, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowPacket::WindowCreate create_packet;
|
||||||
|
create_packet.width = width;
|
||||||
|
create_packet.height = height;
|
||||||
|
create_packet.attributes = attributes;
|
||||||
|
TRY(create_packet.title.append(title));
|
||||||
|
TRY(create_packet.send_serialized(server_fd));
|
||||||
|
|
||||||
|
auto window = TRY(BAN::UniqPtr<Window>::create(server_fd, epoll_fd, attributes));
|
||||||
|
|
||||||
|
bool resized = false;
|
||||||
|
window->set_resize_window_event_callback([&]() { resized = true; });
|
||||||
|
while (!resized)
|
||||||
|
window->poll_events();
|
||||||
|
window->set_resize_window_event_callback({});
|
||||||
|
|
||||||
|
server_closer.disable();
|
||||||
|
epoll_closer.disable();
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Window::set_root_widget(BAN::RefPtr<Widget::Widget> widget)
|
||||||
|
{
|
||||||
|
TRY(widget->set_fixed_geometry({ 0, 0, m_width, m_height }));
|
||||||
|
m_root_widget = widget;
|
||||||
|
m_root_widget->show();
|
||||||
|
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
|
||||||
|
if (invalidated.w && invalidated.h)
|
||||||
|
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
if (!m_texture.clamp_to_texture(x, y, width, height))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < height; i++)
|
||||||
|
memcpy(&m_framebuffer_smo[(y + i) * m_width + x], &m_texture.pixels()[(y + i) * m_width + x], width * sizeof(uint32_t));
|
||||||
|
|
||||||
|
WindowPacket::WindowInvalidate packet;
|
||||||
|
packet.x = x;
|
||||||
|
packet.y = y;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_mouse_relative(bool enabled)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetMouseRelative packet;
|
||||||
|
packet.enabled = enabled;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_fullscreen(bool fullscreen)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetFullscreen packet;
|
||||||
|
packet.fullscreen = fullscreen;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_title(BAN::StringView title)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetTitle packet;
|
||||||
|
MUST(packet.title.append(title));
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_position(int32_t x, int32_t y)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetPosition packet;
|
||||||
|
packet.x = x;
|
||||||
|
packet.y = y;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_cursor_visible(bool visible)
|
||||||
|
{
|
||||||
|
auto attributes = m_attributes;
|
||||||
|
if (attributes.cursor_visible == visible)
|
||||||
|
return;
|
||||||
|
attributes.cursor_visible = visible;
|
||||||
|
set_attributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_cursor(uint32_t width, uint32_t height, BAN::Span<uint32_t> pixels)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetCursor packet;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
MUST(packet.pixels.resize(pixels.size()));
|
||||||
|
for (size_t i = 0; i < packet.pixels.size(); i++)
|
||||||
|
packet.pixels[i] = pixels[i];
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_min_size(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetMinSize packet;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_max_size(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetMaxSize packet;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_attributes(Attributes attributes)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetAttributes packet;
|
||||||
|
packet.attributes = attributes;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
|
||||||
|
m_attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::request_resize(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetSize packet;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::on_socket_error(BAN::StringView function)
|
||||||
|
{
|
||||||
|
if (m_handling_socket_error)
|
||||||
|
return;
|
||||||
|
m_handling_socket_error = true;
|
||||||
|
|
||||||
|
dprintln("Socket error while running Window::{}", function);
|
||||||
|
|
||||||
|
if (!m_socket_error_callback)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
m_socket_error_callback();
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::cleanup()
|
||||||
|
{
|
||||||
|
shmdt(m_framebuffer_smo);
|
||||||
|
close(m_server_fd);
|
||||||
|
close(m_epoll_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Window::handle_resize_event(const EventPacket::ResizeWindowEvent& event)
|
||||||
|
{
|
||||||
|
if (m_framebuffer_smo)
|
||||||
|
shmdt(m_framebuffer_smo);
|
||||||
|
m_framebuffer_smo = nullptr;
|
||||||
|
|
||||||
|
TRY(m_texture.resize(event.width, event.height));
|
||||||
|
|
||||||
|
if (m_root_widget)
|
||||||
|
TRY(m_root_widget->set_fixed_geometry({ 0, 0, event.width, event.height }));
|
||||||
|
|
||||||
|
void* framebuffer_addr = shmat(event.smo_key, nullptr, 0);
|
||||||
|
if (framebuffer_addr == (void*)-1)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
|
||||||
|
m_framebuffer_smo = static_cast<uint32_t*>(framebuffer_addr);
|
||||||
|
m_width = event.width;
|
||||||
|
m_height = event.height;
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::wait_events()
|
||||||
|
{
|
||||||
|
epoll_event dummy;
|
||||||
|
epoll_wait(m_epoll_fd, &dummy, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::poll_events()
|
||||||
|
{
|
||||||
|
#define TRY_OR_BREAK(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) break; e.release_value(); })
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
epoll_event event;
|
||||||
|
if (epoll_wait(m_epoll_fd, &event, 1, 0) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto packet_or_error = recv_packet(m_server_fd);
|
||||||
|
if (packet_or_error.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
|
||||||
|
const auto [packet_type, packet_data] = packet_or_error.release_value();
|
||||||
|
switch (packet_type)
|
||||||
|
{
|
||||||
|
case PacketType::DestroyWindowEvent:
|
||||||
|
exit(1);
|
||||||
|
case PacketType::CloseWindowEvent:
|
||||||
|
if (m_close_window_event_callback)
|
||||||
|
m_close_window_event_callback();
|
||||||
|
else
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case PacketType::ResizeWindowEvent:
|
||||||
|
{
|
||||||
|
MUST(handle_resize_event(TRY_OR_BREAK(EventPacket::ResizeWindowEvent::deserialize(packet_data.span()))));
|
||||||
|
if (m_resize_window_event_callback)
|
||||||
|
m_resize_window_event_callback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PacketType::WindowShownEvent:
|
||||||
|
if (m_window_shown_event_callback)
|
||||||
|
m_window_shown_event_callback(TRY_OR_BREAK(EventPacket::WindowShownEvent::deserialize(packet_data.span())).event);
|
||||||
|
break;
|
||||||
|
case PacketType::WindowFocusEvent:
|
||||||
|
if (m_window_focus_event_callback)
|
||||||
|
m_window_focus_event_callback(TRY_OR_BREAK(EventPacket::WindowFocusEvent::deserialize(packet_data.span())).event);
|
||||||
|
break;
|
||||||
|
case PacketType::KeyEvent:
|
||||||
|
if (m_key_event_callback)
|
||||||
|
m_key_event_callback(TRY_OR_BREAK(EventPacket::KeyEvent::deserialize(packet_data.span())).event);
|
||||||
|
break;
|
||||||
|
case PacketType::MouseButtonEvent:
|
||||||
|
{
|
||||||
|
auto event = TRY_OR_BREAK(EventPacket::MouseButtonEvent::deserialize(packet_data.span())).event;
|
||||||
|
if (m_mouse_button_event_callback)
|
||||||
|
m_mouse_button_event_callback(event);
|
||||||
|
if (m_root_widget)
|
||||||
|
m_root_widget->on_mouse_button(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PacketType::MouseMoveEvent:
|
||||||
|
{
|
||||||
|
auto event = TRY_OR_BREAK(EventPacket::MouseMoveEvent::deserialize(packet_data.span())).event;
|
||||||
|
if (m_mouse_move_event_callback)
|
||||||
|
m_mouse_move_event_callback(event);
|
||||||
|
if (m_root_widget)
|
||||||
|
{
|
||||||
|
m_root_widget->before_mouse_move();
|
||||||
|
m_root_widget->on_mouse_move(event);
|
||||||
|
m_root_widget->after_mouse_move();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PacketType::MouseScrollEvent:
|
||||||
|
if (m_mouse_scroll_event_callback)
|
||||||
|
m_mouse_scroll_event_callback(TRY_OR_BREAK(EventPacket::MouseScrollEvent::deserialize(packet_data.span())).event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef TRY_OR_BREAK
|
||||||
|
|
||||||
|
if (m_root_widget)
|
||||||
|
{
|
||||||
|
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
|
||||||
|
if (invalidated.w && invalidated.h)
|
||||||
|
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
class MessageBox
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<void> create(BAN::StringView message, BAN::StringView title);
|
||||||
|
static BAN::ErrorOr<size_t> create(BAN::StringView message, BAN::StringView title, BAN::Span<BAN::StringView> buttons);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,381 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/String.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
|
|
||||||
|
#include <LibInput/KeyEvent.h>
|
||||||
|
#include <LibInput/MouseEvent.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#define FOR_EACH_0(macro)
|
||||||
|
#define FOR_EACH_2(macro, type, name) macro(type, name)
|
||||||
|
#define FOR_EACH_4(macro, type, name, ...) macro(type, name) FOR_EACH_2(macro, __VA_ARGS__)
|
||||||
|
#define FOR_EACH_6(macro, type, name, ...) macro(type, name) FOR_EACH_4(macro, __VA_ARGS__)
|
||||||
|
#define FOR_EACH_8(macro, type, name, ...) macro(type, name) FOR_EACH_6(macro, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define CONCATENATE_2(arg1, arg2) arg1 ## arg2
|
||||||
|
#define CONCATENATE_1(arg1, arg2) CONCATENATE_2(arg1, arg2)
|
||||||
|
#define CONCATENATE(arg1, arg2) CONCATENATE_1(arg1, arg2)
|
||||||
|
|
||||||
|
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__ __VA_OPT__(,) FOR_EACH_RSEQ_N())
|
||||||
|
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
|
||||||
|
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
|
||||||
|
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||||
|
|
||||||
|
#define FOR_EACH_(N, what, ...) CONCATENATE(FOR_EACH_, N)(what __VA_OPT__(,) __VA_ARGS__)
|
||||||
|
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what __VA_OPT__(,) __VA_ARGS__)
|
||||||
|
|
||||||
|
#define FIELD_DECL(type, name) type name;
|
||||||
|
#define ADD_SERIALIZED_SIZE(type, name) serialized_size += Serialize::serialized_size_impl<type>(this->name);
|
||||||
|
#define SEND_SERIALIZED(type, name) TRY(Serialize::send_serialized_impl<type>(socket, this->name));
|
||||||
|
#define DESERIALIZE(type, name) value.name = TRY(Serialize::deserialize_impl<type>(buffer));
|
||||||
|
|
||||||
|
#define DEFINE_PACKET_EXTRA(name, extra, ...) \
|
||||||
|
struct name \
|
||||||
|
{ \
|
||||||
|
static constexpr PacketType type = PacketType::name; \
|
||||||
|
static constexpr uint32_t type_u32 = static_cast<uint32_t>(type); \
|
||||||
|
\
|
||||||
|
extra; \
|
||||||
|
\
|
||||||
|
FOR_EACH(FIELD_DECL, __VA_ARGS__) \
|
||||||
|
\
|
||||||
|
size_t serialized_size() \
|
||||||
|
{ \
|
||||||
|
size_t serialized_size = Serialize::serialized_size_impl<uint32_t>(type_u32); \
|
||||||
|
FOR_EACH(ADD_SERIALIZED_SIZE, __VA_ARGS__) \
|
||||||
|
return serialized_size; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
BAN::ErrorOr<void> send_serialized(int socket) \
|
||||||
|
{ \
|
||||||
|
const uint32_t serialized_size = this->serialized_size(); \
|
||||||
|
TRY(Serialize::send_serialized_impl<uint32_t>(socket, serialized_size)); \
|
||||||
|
TRY(Serialize::send_serialized_impl<uint32_t>(socket, type_u32)); \
|
||||||
|
FOR_EACH(SEND_SERIALIZED, __VA_ARGS__) \
|
||||||
|
return {}; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static BAN::ErrorOr<name> deserialize(BAN::ConstByteSpan buffer) \
|
||||||
|
{ \
|
||||||
|
const uint32_t type_u32 = TRY(Serialize::deserialize_impl<uint32_t>(buffer)); \
|
||||||
|
if (type_u32 != name::type_u32) \
|
||||||
|
return BAN::Error::from_errno(EINVAL); \
|
||||||
|
name value; \
|
||||||
|
FOR_EACH(DESERIALIZE, __VA_ARGS__) \
|
||||||
|
return value; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_PACKET(name, ...) DEFINE_PACKET_EXTRA(name, , __VA_ARGS__)
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
concept Vector = requires {
|
||||||
|
requires BAN::same_as<T, BAN::Vector<typename T::value_type>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr BAN::StringView s_window_server_socket = "/tmp/window-server.socket"_sv;
|
||||||
|
|
||||||
|
namespace Serialize
|
||||||
|
{
|
||||||
|
|
||||||
|
inline BAN::ErrorOr<void> send_raw_data(int socket, BAN::ConstByteSpan data)
|
||||||
|
{
|
||||||
|
size_t send_done = 0;
|
||||||
|
while (send_done < data.size())
|
||||||
|
{
|
||||||
|
const ssize_t nsend = ::send(socket, data.data() + send_done, data.size() - send_done, 0);
|
||||||
|
if (nsend < 0)
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
if (nsend == 0)
|
||||||
|
return BAN::Error::from_errno(ECONNRESET);
|
||||||
|
send_done += nsend;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_pod_v<T>
|
||||||
|
inline size_t serialized_size_impl(const T&)
|
||||||
|
{
|
||||||
|
return sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_pod_v<T>
|
||||||
|
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& value)
|
||||||
|
{
|
||||||
|
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_pod_v<T>
|
||||||
|
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
|
||||||
|
{
|
||||||
|
if (buffer.size() < sizeof(T))
|
||||||
|
return BAN::Error::from_errno(ENOBUFS);
|
||||||
|
const T value = buffer.as<const T>();
|
||||||
|
buffer = buffer.slice(sizeof(T));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_same_v<T, BAN::String>
|
||||||
|
inline size_t serialized_size_impl(const T& value)
|
||||||
|
{
|
||||||
|
return sizeof(uint32_t) + value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_same_v<T, BAN::String>
|
||||||
|
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& value)
|
||||||
|
{
|
||||||
|
const uint32_t value_size = value.size();
|
||||||
|
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value_size)));
|
||||||
|
auto* u8_data = reinterpret_cast<const uint8_t*>(value.data());
|
||||||
|
TRY(send_raw_data(socket, BAN::ConstByteSpan(u8_data, value.size())));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires BAN::is_same_v<T, BAN::String>
|
||||||
|
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
|
||||||
|
{
|
||||||
|
if (buffer.size() < sizeof(uint32_t))
|
||||||
|
return BAN::Error::from_errno(ENOBUFS);
|
||||||
|
const uint32_t string_len = buffer.as<const uint32_t>();
|
||||||
|
buffer = buffer.slice(sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (buffer.size() < string_len)
|
||||||
|
return BAN::Error::from_errno(ENOBUFS);
|
||||||
|
|
||||||
|
BAN::String string;
|
||||||
|
TRY(string.resize(string_len));
|
||||||
|
memcpy(string.data(), buffer.data(), string_len);
|
||||||
|
buffer = buffer.slice(string_len);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<detail::Vector T>
|
||||||
|
inline size_t serialized_size_impl(const T& vector)
|
||||||
|
{
|
||||||
|
size_t result = sizeof(uint32_t);
|
||||||
|
for (const auto& element : vector)
|
||||||
|
result += serialized_size_impl(element);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<detail::Vector T>
|
||||||
|
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& vector)
|
||||||
|
{
|
||||||
|
const uint32_t value_size = vector.size();
|
||||||
|
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value_size)));
|
||||||
|
for (const auto& element : vector)
|
||||||
|
TRY(send_serialized_impl(socket, element));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<detail::Vector T>
|
||||||
|
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
|
||||||
|
{
|
||||||
|
if (buffer.size() < sizeof(uint32_t))
|
||||||
|
return BAN::Error::from_errno(ENOBUFS);
|
||||||
|
const uint32_t vector_size = buffer.as<const uint32_t>();
|
||||||
|
buffer = buffer.slice(sizeof(uint32_t));
|
||||||
|
|
||||||
|
T vector;
|
||||||
|
TRY(vector.resize(vector_size));
|
||||||
|
for (auto& element : vector)
|
||||||
|
element = TRY(deserialize_impl<typename T::value_type>(buffer));
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PacketType : uint32_t
|
||||||
|
{
|
||||||
|
WindowCreate,
|
||||||
|
WindowCreateResponse,
|
||||||
|
WindowInvalidate,
|
||||||
|
WindowSetPosition,
|
||||||
|
WindowSetAttributes,
|
||||||
|
WindowSetMouseRelative,
|
||||||
|
WindowSetSize,
|
||||||
|
WindowSetMinSize,
|
||||||
|
WindowSetMaxSize,
|
||||||
|
WindowSetFullscreen,
|
||||||
|
WindowSetTitle,
|
||||||
|
WindowSetCursor,
|
||||||
|
|
||||||
|
DestroyWindowEvent,
|
||||||
|
CloseWindowEvent,
|
||||||
|
ResizeWindowEvent,
|
||||||
|
WindowShownEvent,
|
||||||
|
WindowFocusEvent,
|
||||||
|
KeyEvent,
|
||||||
|
MouseButtonEvent,
|
||||||
|
MouseMoveEvent,
|
||||||
|
MouseScrollEvent,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace WindowPacket
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Attributes
|
||||||
|
{
|
||||||
|
bool title_bar;
|
||||||
|
bool movable;
|
||||||
|
bool focusable;
|
||||||
|
bool rounded_corners;
|
||||||
|
bool alpha_channel;
|
||||||
|
bool resizable;
|
||||||
|
bool shown;
|
||||||
|
bool cursor_visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowCreate,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height,
|
||||||
|
Attributes, attributes,
|
||||||
|
BAN::String, title
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowInvalidate,
|
||||||
|
uint32_t, x,
|
||||||
|
uint32_t, y,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetPosition,
|
||||||
|
int32_t, x,
|
||||||
|
int32_t, y
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetAttributes,
|
||||||
|
Attributes, attributes
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetMouseRelative,
|
||||||
|
bool, enabled
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetSize,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetMinSize,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetMaxSize,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetFullscreen,
|
||||||
|
bool, fullscreen
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetTitle,
|
||||||
|
BAN::String, title
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
WindowSetCursor,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height,
|
||||||
|
BAN::Vector<uint32_t>, pixels
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace EventPacket
|
||||||
|
{
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
DestroyWindowEvent
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
CloseWindowEvent
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET(
|
||||||
|
ResizeWindowEvent,
|
||||||
|
uint32_t, width,
|
||||||
|
uint32_t, height,
|
||||||
|
long, smo_key
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
WindowShownEvent,
|
||||||
|
struct event_t {
|
||||||
|
bool shown;
|
||||||
|
},
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
WindowFocusEvent,
|
||||||
|
struct event_t {
|
||||||
|
bool focused;
|
||||||
|
},
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
KeyEvent,
|
||||||
|
using event_t = LibInput::KeyEvent,
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
MouseButtonEvent,
|
||||||
|
struct event_t {
|
||||||
|
LibInput::MouseButton button;
|
||||||
|
bool pressed;
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
},
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
MouseMoveEvent,
|
||||||
|
struct event_t {
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
},
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_PACKET_EXTRA(
|
||||||
|
MouseScrollEvent,
|
||||||
|
struct event_t {
|
||||||
|
int32_t scroll;
|
||||||
|
},
|
||||||
|
event_t, event
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace LibFont { class Font; }
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
class Texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t color_invisible = 0x69000000;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<Texture> create(uint32_t width, uint32_t height, uint32_t color);
|
||||||
|
Texture() = default;
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
void set_pixel(uint32_t x, uint32_t y, uint32_t color)
|
||||||
|
{
|
||||||
|
ASSERT(x < m_width);
|
||||||
|
ASSERT(y < m_height);
|
||||||
|
if (x < m_clip_x || x >= m_clip_x + m_clip_w)
|
||||||
|
return;
|
||||||
|
if (y < m_clip_y || y >= m_clip_y + m_clip_h)
|
||||||
|
return;
|
||||||
|
m_pixels[y * m_width + x] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_pixel(uint32_t x, uint32_t y) const
|
||||||
|
{
|
||||||
|
ASSERT(x < m_width);
|
||||||
|
ASSERT(y < m_height);
|
||||||
|
return m_pixels[y * m_width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Span<uint32_t> pixels() { return m_pixels.span(); }
|
||||||
|
BAN::Span<const uint32_t> pixels() const { return m_pixels.span(); }
|
||||||
|
|
||||||
|
void set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
void fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color);
|
||||||
|
void fill(uint32_t color) { return fill_rect(0, 0, width(), height(), color); }
|
||||||
|
|
||||||
|
void clear_rect(int32_t x, int32_t y, uint32_t width, uint32_t height) { fill_rect(x, y, width, height, m_bg_color); }
|
||||||
|
void clear() { return clear_rect(0, 0, width(), height()); }
|
||||||
|
|
||||||
|
void copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x = 0, uint32_t sub_y = 0, uint32_t width = -1, uint32_t height = -1);
|
||||||
|
|
||||||
|
void draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
|
||||||
|
void draw_text(BAN::StringView text, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
|
||||||
|
|
||||||
|
// shift whole vertically by amount pixels, sign determines the direction
|
||||||
|
void shift_vertical(int32_t amount);
|
||||||
|
|
||||||
|
// copy horizontal slice [src_y, src_y + amount[ to [dst_y, dst_y + amount[
|
||||||
|
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount);
|
||||||
|
|
||||||
|
// copy rect (src_x, src_y, width, height) to (dst_x, dst_y, width, height)
|
||||||
|
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
uint32_t width() const { return m_width; }
|
||||||
|
uint32_t height() const { return m_height; }
|
||||||
|
|
||||||
|
// used on resize to fill empty space
|
||||||
|
void set_bg_color(uint32_t bg_color) { m_bg_color = bg_color; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Texture(BAN::Vector<uint32_t>&& pixels, uint32_t width, uint32_t height, uint32_t color)
|
||||||
|
: m_pixels(BAN::move(pixels))
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
, m_bg_color(color)
|
||||||
|
, m_clip_x(0)
|
||||||
|
, m_clip_y(0)
|
||||||
|
, m_clip_w(width)
|
||||||
|
, m_clip_h(height)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool clamp_to_texture(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const;
|
||||||
|
bool clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::Vector<uint32_t> m_pixels;
|
||||||
|
uint32_t m_width { 0 };
|
||||||
|
uint32_t m_height { 0 };
|
||||||
|
uint32_t m_bg_color { 0xFFFFFFFF };
|
||||||
|
|
||||||
|
uint32_t m_clip_x { 0 };
|
||||||
|
uint32_t m_clip_y { 0 };
|
||||||
|
uint32_t m_clip_w { 0 };
|
||||||
|
uint32_t m_clip_h { 0 };
|
||||||
|
bool m_has_set_clip { false };
|
||||||
|
|
||||||
|
friend class Window;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Function.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Button : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_hovered(0x808080)
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_hovered;
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Button>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
void set_click_callback(BAN::Function<void()> callback) { m_click_callback = callback; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Button(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void update_impl() override;
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
bool m_hover_state { false };
|
||||||
|
BAN::String m_text;
|
||||||
|
|
||||||
|
BAN::Function<void()> m_click_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Grid : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Grid>> create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color = color_invisible, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Grid(BAN::RefPtr<Widget> parent, Rectangle geometry, uint32_t cols, uint32_t rows)
|
||||||
|
: Widget(parent, geometry)
|
||||||
|
, m_cols(cols)
|
||||||
|
, m_rows(rows)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> update_geometry_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GridElement
|
||||||
|
{
|
||||||
|
BAN::RefPtr<Widget> widget;
|
||||||
|
uint32_t col;
|
||||||
|
uint32_t col_span;
|
||||||
|
uint32_t row;
|
||||||
|
uint32_t row_span;
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle grid_element_area(const GridElement& element) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint32_t m_cols;
|
||||||
|
const uint32_t m_rows;
|
||||||
|
BAN::Vector<GridElement> m_grid_elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Label : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Label>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::StringView text() const { return m_text; }
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Label(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
BAN::String m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class RoundedWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style
|
||||||
|
{
|
||||||
|
Style(uint32_t color_normal = 0xA0A0A0, uint32_t border_width = 1, uint32_t color_border = 0x000000, uint32_t corner_radius = 5)
|
||||||
|
: color_normal(color_normal)
|
||||||
|
, border_width(border_width)
|
||||||
|
, color_border(color_border)
|
||||||
|
, corner_radius(corner_radius)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_normal;
|
||||||
|
uint32_t border_width;
|
||||||
|
uint32_t color_border;
|
||||||
|
uint32_t corner_radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RoundedWidget(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: Widget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool contains(Point point) const override;
|
||||||
|
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class TextArea : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<TextArea>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::StringView text() const { return m_text; }
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
uint32_t get_required_height() const;
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TextArea(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> wrap_text();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> update_geometry_impl() override;
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
BAN::String m_text;
|
||||||
|
BAN::Vector<BAN::String> m_wrapped_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/RefPtr.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Texture.h>
|
||||||
|
#include <LibGUI/Packet.h>
|
||||||
|
|
||||||
|
namespace LibGUI { class Window; }
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Widget : public BAN::RefCounted<Widget>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t color_invisible = Texture::color_invisible;
|
||||||
|
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FloatRectangle
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
float w, h;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rectangle
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
uint32_t w, h;
|
||||||
|
|
||||||
|
struct Bounds
|
||||||
|
{
|
||||||
|
int32_t min_x, min_y;
|
||||||
|
int32_t max_x, max_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool contains(Point point) const
|
||||||
|
{
|
||||||
|
if (point.x < x || point.x >= x + static_cast<int32_t>(w))
|
||||||
|
return false;
|
||||||
|
if (point.y < y || point.y >= y + static_cast<int32_t>(h))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bounds bounds(Rectangle other) const
|
||||||
|
{
|
||||||
|
return Bounds {
|
||||||
|
.min_x = BAN::Math::max(x, other.x),
|
||||||
|
.min_y = BAN::Math::max(y, other.y),
|
||||||
|
.max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w)),
|
||||||
|
.max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle overlap(Rectangle other) const
|
||||||
|
{
|
||||||
|
const auto min_x = BAN::Math::max(x, other.x);
|
||||||
|
const auto min_y = BAN::Math::max(y, other.y);
|
||||||
|
const auto max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
|
||||||
|
const auto max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
|
||||||
|
if (min_x >= max_x || min_y >= max_y)
|
||||||
|
return {};
|
||||||
|
return Rectangle {
|
||||||
|
.x = min_x,
|
||||||
|
.y = min_y,
|
||||||
|
.w = static_cast<uint32_t>(max_x - min_x),
|
||||||
|
.h = static_cast<uint32_t>(max_y - min_y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle bounding_box(Rectangle other) const
|
||||||
|
{
|
||||||
|
if (w == 0 || h == 0)
|
||||||
|
return other;
|
||||||
|
if (other.w == 0 || other.h == 0)
|
||||||
|
return *this;
|
||||||
|
const auto min_x = BAN::Math::min(x, other.x);
|
||||||
|
const auto min_y = BAN::Math::min(y, other.y);
|
||||||
|
const auto max_x = BAN::Math::max(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
|
||||||
|
const auto max_y = BAN::Math::max(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
|
||||||
|
return Rectangle {
|
||||||
|
.x = min_x,
|
||||||
|
.y = min_y,
|
||||||
|
.w = static_cast<uint32_t>(max_x - min_x),
|
||||||
|
.h = static_cast<uint32_t>(max_y - min_y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Widget>> create(BAN::RefPtr<Widget> parent, uint32_t color = color_invisible, Rectangle geometry = {});
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> set_default_font(BAN::StringView path);
|
||||||
|
static const LibFont::Font& default_font();
|
||||||
|
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_fixed_geometry(Rectangle);
|
||||||
|
BAN::ErrorOr<void> set_relative_geometry(FloatRectangle);
|
||||||
|
|
||||||
|
BAN::RefPtr<Widget> parent() { return m_parent; }
|
||||||
|
|
||||||
|
uint32_t width() const { return m_fixed_area.w; }
|
||||||
|
uint32_t height() const { return m_fixed_area.h; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void before_mouse_move();
|
||||||
|
void after_mouse_move();
|
||||||
|
bool on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t);
|
||||||
|
|
||||||
|
bool on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Widget(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: m_parent(parent)
|
||||||
|
, m_fixed_area(area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize(uint32_t color);
|
||||||
|
|
||||||
|
virtual bool contains(Point point) const { return Rectangle { 0, 0, width(), height() }.contains(point); }
|
||||||
|
|
||||||
|
bool is_hovered() const { return m_hovered; }
|
||||||
|
bool is_child_hovered() const;
|
||||||
|
|
||||||
|
bool is_shown() const { return m_shown; }
|
||||||
|
|
||||||
|
Rectangle render(Texture& output, Point parent_position, Rectangle out_area);
|
||||||
|
|
||||||
|
virtual void update_impl() {}
|
||||||
|
virtual void show_impl() {}
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<void> update_geometry_impl();
|
||||||
|
|
||||||
|
virtual void on_hover_change_impl(bool hovered) { (void)hovered; }
|
||||||
|
virtual bool on_mouse_move_impl(LibGUI::EventPacket::MouseMoveEvent::event_t) { return true; }
|
||||||
|
virtual bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) { return true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Texture m_texture;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::RefPtr<Widget> m_parent;
|
||||||
|
BAN::Vector<BAN::RefPtr<Widget>> m_children;
|
||||||
|
bool m_shown { false };
|
||||||
|
|
||||||
|
Rectangle m_fixed_area;
|
||||||
|
BAN::Optional<FloatRectangle> m_relative_area;
|
||||||
|
|
||||||
|
bool m_changed { false };
|
||||||
|
|
||||||
|
bool m_hovered { false };
|
||||||
|
bool m_old_hovered { false };
|
||||||
|
|
||||||
|
friend class LibGUI::Window;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Function.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <BAN/UniqPtr.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Packet.h>
|
||||||
|
#include <LibGUI/Texture.h>
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibFont { class Font; }
|
||||||
|
|
||||||
|
namespace LibGUI
|
||||||
|
{
|
||||||
|
|
||||||
|
class Window
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Attributes = WindowPacket::Attributes;
|
||||||
|
|
||||||
|
static constexpr Attributes default_attributes = {
|
||||||
|
.title_bar = true,
|
||||||
|
.movable = true,
|
||||||
|
.focusable = true,
|
||||||
|
.rounded_corners = true,
|
||||||
|
.alpha_channel = false,
|
||||||
|
.resizable = false,
|
||||||
|
.shown = true,
|
||||||
|
.cursor_visible = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
~Window();
|
||||||
|
|
||||||
|
static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes = default_attributes);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_root_widget(BAN::RefPtr<Widget::Widget> widget);
|
||||||
|
BAN::RefPtr<Widget::Widget> root_widget() { return m_root_widget; }
|
||||||
|
|
||||||
|
Texture& texture() { return m_texture; }
|
||||||
|
const Texture& texture() const { return m_texture; }
|
||||||
|
|
||||||
|
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||||
|
void invalidate() { return invalidate(0, 0, width(), height()); }
|
||||||
|
|
||||||
|
void set_mouse_relative(bool enabled);
|
||||||
|
void set_fullscreen(bool fullscreen);
|
||||||
|
void set_title(BAN::StringView title);
|
||||||
|
|
||||||
|
void set_position(int32_t x, int32_t y);
|
||||||
|
|
||||||
|
void set_cursor_visible(bool visible);
|
||||||
|
void set_cursor(uint32_t width, uint32_t height, BAN::Span<uint32_t> pixels);
|
||||||
|
|
||||||
|
Attributes get_attributes() const { return m_attributes; }
|
||||||
|
void set_attributes(Attributes attributes);
|
||||||
|
|
||||||
|
void set_min_size(uint32_t width, uint32_t height);
|
||||||
|
void set_max_size(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
// send resize request to window server
|
||||||
|
// actual resize is only done after resize callback is called
|
||||||
|
void request_resize(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
uint32_t width() const { return m_width; }
|
||||||
|
uint32_t height() const { return m_height; }
|
||||||
|
|
||||||
|
void wait_events();
|
||||||
|
void poll_events();
|
||||||
|
|
||||||
|
void set_socket_error_callback(BAN::Function<void()> callback) { m_socket_error_callback = callback; }
|
||||||
|
void set_close_window_event_callback(BAN::Function<void()> callback) { m_close_window_event_callback = callback; }
|
||||||
|
void set_resize_window_event_callback(BAN::Function<void()> callback) { m_resize_window_event_callback = callback; }
|
||||||
|
void set_key_event_callback(BAN::Function<void(EventPacket::KeyEvent::event_t)> callback) { m_key_event_callback = callback; }
|
||||||
|
void set_mouse_button_event_callback(BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> callback) { m_mouse_button_event_callback = callback; }
|
||||||
|
void set_mouse_move_event_callback(BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> callback) { m_mouse_move_event_callback = callback; }
|
||||||
|
void set_mouse_scroll_event_callback(BAN::Function<void(EventPacket::MouseScrollEvent::event_t)> callback) { m_mouse_scroll_event_callback = callback; }
|
||||||
|
void set_window_shown_event_callback(BAN::Function<void(EventPacket::WindowShownEvent::event_t)> callback) { m_window_shown_event_callback = callback; }
|
||||||
|
void set_window_focus_event_callback(BAN::Function<void(EventPacket::WindowFocusEvent::event_t)> callback) { m_window_focus_event_callback = callback; }
|
||||||
|
|
||||||
|
int server_fd() const { return m_server_fd; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Window(int server_fd, int epoll_fd, Attributes attributes)
|
||||||
|
: m_server_fd(server_fd)
|
||||||
|
, m_epoll_fd(epoll_fd)
|
||||||
|
, m_attributes(attributes)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void on_socket_error(BAN::StringView function);
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> handle_resize_event(const EventPacket::ResizeWindowEvent&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int m_server_fd;
|
||||||
|
const int m_epoll_fd;
|
||||||
|
|
||||||
|
bool m_handling_socket_error { false };
|
||||||
|
|
||||||
|
Attributes m_attributes;
|
||||||
|
|
||||||
|
uint32_t* m_framebuffer_smo { nullptr };
|
||||||
|
uint32_t m_width { 0 };
|
||||||
|
uint32_t m_height { 0 };
|
||||||
|
|
||||||
|
Texture m_texture;
|
||||||
|
BAN::RefPtr<Widget::Widget> m_root_widget;
|
||||||
|
|
||||||
|
BAN::Function<void()> m_socket_error_callback;
|
||||||
|
BAN::Function<void()> m_close_window_event_callback;
|
||||||
|
BAN::Function<void()> m_resize_window_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::WindowShownEvent::event_t)> m_window_shown_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::WindowFocusEvent::event_t)> m_window_focus_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::KeyEvent::event_t)> m_key_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> m_mouse_button_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> m_mouse_move_event_callback;
|
||||||
|
BAN::Function<void(EventPacket::MouseScrollEvent::event_t)> m_mouse_scroll_event_callback;
|
||||||
|
|
||||||
|
friend class BAN::UniqPtr<Window>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
set(LIBIMAGE_SOURCES
|
||||||
|
Image.cpp
|
||||||
|
Netbpm.cpp
|
||||||
|
PNG.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(libimage ${LIBIMAGE_SOURCES})
|
||||||
|
banan_link_library(libimage ban)
|
||||||
|
banan_link_library(libimage libdeflate)
|
||||||
|
|
||||||
|
banan_install_headers(libimage)
|
||||||
|
install(TARGETS libimage OPTIONAL)
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <BAN/String.h>
|
||||||
|
|
||||||
|
#include <LibImage/Image.h>
|
||||||
|
#include <LibImage/Netbpm.h>
|
||||||
|
#include <LibImage/PNG.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
namespace LibImage
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::UniqPtr<Image>> Image::load_from_file(BAN::StringView path)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
if (path.data()[path.size()] == '\0')
|
||||||
|
{
|
||||||
|
fd = open(path.data(), O_RDONLY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BAN::String path_str;
|
||||||
|
TRY(path_str.append(path));
|
||||||
|
fd = open(path_str.data(), O_RDONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
fprintf(stddbg, "open: %s\n", strerror(errno));
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ScopeGuard guard_file_close([fd] { close(fd); });
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) == -1)
|
||||||
|
{
|
||||||
|
fprintf(stddbg, "fstat: %s\n", strerror(errno));
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* addr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
if (addr == MAP_FAILED)
|
||||||
|
{
|
||||||
|
fprintf(stddbg, "mmap: %s\n", strerror(errno));
|
||||||
|
return BAN::Error::from_errno(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ScopeGuard guard_munmap([&] { munmap(addr, st.st_size); });
|
||||||
|
|
||||||
|
auto image_data_span = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(addr), st.st_size);
|
||||||
|
|
||||||
|
if (probe_netbpm(image_data_span))
|
||||||
|
return TRY(load_netbpm(image_data_span));
|
||||||
|
|
||||||
|
if (probe_png(image_data_span))
|
||||||
|
return TRY(load_png(image_data_span));
|
||||||
|
|
||||||
|
fprintf(stderr, "unrecognized image format\n");
|
||||||
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FloatingColor
|
||||||
|
{
|
||||||
|
__m128 vals;
|
||||||
|
|
||||||
|
FloatingColor() {}
|
||||||
|
FloatingColor(float b, float g, float r, float a)
|
||||||
|
: vals { b, g, r, a }
|
||||||
|
{}
|
||||||
|
FloatingColor(Image::Color c)
|
||||||
|
: FloatingColor(c.b, c.g, c.r, c.a)
|
||||||
|
{}
|
||||||
|
FloatingColor operator*(float value) const
|
||||||
|
{
|
||||||
|
FloatingColor color;
|
||||||
|
color.vals = _mm_mul_ps(vals, _mm_set1_ps(value));
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
FloatingColor operator+(FloatingColor other) const
|
||||||
|
{
|
||||||
|
FloatingColor color;
|
||||||
|
color.vals = _mm_add_ps(this->vals, other.vals);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
Image::Color as_color() const
|
||||||
|
{
|
||||||
|
__m128i int32 = _mm_cvttps_epi32(this->vals);
|
||||||
|
__m128i int16 = _mm_packs_epi32(int32, _mm_setzero_si128());
|
||||||
|
__m128i int8 = _mm_packus_epi16(int16, _mm_setzero_si128());
|
||||||
|
|
||||||
|
const uint32_t temp = _mm_cvtsi128_si32(int8);
|
||||||
|
return Image::Color {
|
||||||
|
.b = reinterpret_cast<const uint8_t*>(&temp)[0],
|
||||||
|
.g = reinterpret_cast<const uint8_t*>(&temp)[1],
|
||||||
|
.r = reinterpret_cast<const uint8_t*>(&temp)[2],
|
||||||
|
.a = reinterpret_cast<const uint8_t*>(&temp)[3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::UniqPtr<Image>> Image::resize(uint64_t new_width, uint64_t new_height, ResizeAlgorithm algorithm)
|
||||||
|
{
|
||||||
|
if (!validate_size(new_width, new_height))
|
||||||
|
return BAN::Error::from_errno(EOVERFLOW);
|
||||||
|
|
||||||
|
const float ratio_x = static_cast<float>(width()) / new_width;
|
||||||
|
const float ratio_y = static_cast<float>(height()) / new_height;
|
||||||
|
|
||||||
|
const auto get_clamped_color =
|
||||||
|
[this](int64_t x, int64_t y)
|
||||||
|
{
|
||||||
|
x = BAN::Math::clamp<int64_t>(x, 0, width() - 1);
|
||||||
|
y = BAN::Math::clamp<int64_t>(y, 0, height() - 1);
|
||||||
|
return get_color(x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (algorithm)
|
||||||
|
{
|
||||||
|
case ResizeAlgorithm::Nearest:
|
||||||
|
{
|
||||||
|
BAN::Vector<Color> nearest_bitmap;
|
||||||
|
TRY(nearest_bitmap.resize(new_width * new_height));
|
||||||
|
for (uint64_t y = 0; y < new_height; y++)
|
||||||
|
for (uint64_t x = 0; x < new_width; x++)
|
||||||
|
nearest_bitmap[y * new_width + x] = get_clamped_color(x * ratio_x, y * ratio_y);
|
||||||
|
return TRY(BAN::UniqPtr<Image>::create(new_width, new_height, BAN::move(nearest_bitmap)));
|
||||||
|
}
|
||||||
|
case ResizeAlgorithm::Linear:
|
||||||
|
{
|
||||||
|
BAN::Vector<Color> bilinear_bitmap;
|
||||||
|
TRY(bilinear_bitmap.resize(new_width * new_height));
|
||||||
|
|
||||||
|
const uint64_t temp_w = width() + 1;
|
||||||
|
const uint64_t temp_h = height() + 1;
|
||||||
|
|
||||||
|
BAN::Vector<FloatingColor> floating_bitmap;
|
||||||
|
TRY(floating_bitmap.resize(temp_w * temp_h));
|
||||||
|
for (uint64_t y = 0; y < temp_h; y++)
|
||||||
|
for (uint64_t x = 0; x < temp_w; x++)
|
||||||
|
floating_bitmap[y * temp_w + x] = get_clamped_color(x, y);
|
||||||
|
|
||||||
|
for (uint64_t y = 0; y < new_height; y++)
|
||||||
|
{
|
||||||
|
for (uint64_t x = 0; x < new_width; x++)
|
||||||
|
{
|
||||||
|
const float src_x = x * ratio_x;
|
||||||
|
const float src_y = y * ratio_y;
|
||||||
|
|
||||||
|
const float weight_x = BAN::Math::fmod(src_x, 1.0f);
|
||||||
|
const float weight_y = BAN::Math::fmod(src_y, 1.0f);
|
||||||
|
|
||||||
|
const uint64_t src_x_u64 = BAN::Math::clamp<uint64_t>(src_x, 0, width() - 1);
|
||||||
|
const uint64_t src_y_u64 = BAN::Math::clamp<uint64_t>(src_y, 0, height() - 1);
|
||||||
|
|
||||||
|
const auto tl = floating_bitmap[(src_y_u64 + 0) * temp_w + (src_x_u64 + 0)];
|
||||||
|
const auto tr = floating_bitmap[(src_y_u64 + 0) * temp_w + (src_x_u64 + 1)];
|
||||||
|
const auto bl = floating_bitmap[(src_y_u64 + 1) * temp_w + (src_x_u64 + 0)];
|
||||||
|
const auto br = floating_bitmap[(src_y_u64 + 1) * temp_w + (src_x_u64 + 1)];
|
||||||
|
|
||||||
|
const auto avg_t = tl * (1.0f - weight_x) + tr * weight_x;
|
||||||
|
const auto avg_b = bl * (1.0f - weight_x) + br * weight_x;
|
||||||
|
const auto avg = avg_t * (1.0f - weight_y) + avg_b * weight_y;
|
||||||
|
|
||||||
|
bilinear_bitmap[y * new_width + x] = avg.as_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRY(BAN::UniqPtr<Image>::create(new_width, new_height, BAN::move(bilinear_bitmap)));
|
||||||
|
}
|
||||||
|
case ResizeAlgorithm::Cubic:
|
||||||
|
{
|
||||||
|
BAN::Vector<Color> bicubic_bitmap;
|
||||||
|
TRY(bicubic_bitmap.resize(new_width * new_height, {}));
|
||||||
|
|
||||||
|
constexpr auto cubic_interpolate =
|
||||||
|
[](const FloatingColor p[4], float weight) -> FloatingColor
|
||||||
|
{
|
||||||
|
const auto a = (p[0] * -0.5) + (p[1] * 1.5) + (p[2] * -1.5) + (p[3] * 0.5);
|
||||||
|
const auto b = p[0] + (p[1] * -2.5) + (p[2] * 2.0) + (p[3] * -0.5);
|
||||||
|
const auto c = (p[0] * -0.5) + (p[2] * 0.5);
|
||||||
|
const auto d = p[1];
|
||||||
|
return ((a * weight + b) * weight + c) * weight + d;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint64_t temp_w = width() + 3;
|
||||||
|
const uint64_t temp_h = height() + 3;
|
||||||
|
|
||||||
|
BAN::Vector<FloatingColor> floating_bitmap;
|
||||||
|
TRY(floating_bitmap.resize(temp_w * temp_h, {}));
|
||||||
|
for (uint64_t y = 0; y < temp_h; y++)
|
||||||
|
for (uint64_t x = 0; x < temp_w; x++)
|
||||||
|
floating_bitmap[y * temp_w + x] = get_clamped_color(
|
||||||
|
static_cast<int64_t>(x) - 1,
|
||||||
|
static_cast<int64_t>(y) - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
for (uint64_t y = 0; y < new_height; y++)
|
||||||
|
{
|
||||||
|
for (uint64_t x = 0; x < new_width; x++)
|
||||||
|
{
|
||||||
|
const float src_x = x * ratio_x;
|
||||||
|
const float src_y = y * ratio_y;
|
||||||
|
|
||||||
|
const float weight_x = BAN::Math::fmod(src_x, 1.0f);
|
||||||
|
const float weight_y = BAN::Math::fmod(src_y, 1.0f);
|
||||||
|
|
||||||
|
const uint64_t src_x_u64 = BAN::Math::clamp<uint64_t>(src_x, 0, width() - 1) + 1;
|
||||||
|
const uint64_t src_y_u64 = BAN::Math::clamp<uint64_t>(src_y, 0, height() - 1) + 1;
|
||||||
|
|
||||||
|
FloatingColor values[4];
|
||||||
|
for (int64_t m = -1; m <= 2; m++)
|
||||||
|
{
|
||||||
|
const FloatingColor p[4] {
|
||||||
|
floating_bitmap[(src_y_u64 + m) * temp_w + (src_x_u64 - 1)],
|
||||||
|
floating_bitmap[(src_y_u64 + m) * temp_w + (src_x_u64 + 0)],
|
||||||
|
floating_bitmap[(src_y_u64 + m) * temp_w + (src_x_u64 + 1)],
|
||||||
|
floating_bitmap[(src_y_u64 + m) * temp_w + (src_x_u64 + 2)],
|
||||||
|
};
|
||||||
|
values[m + 1] = cubic_interpolate(p, weight_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bicubic_bitmap[y * new_width + x] = cubic_interpolate(values, weight_y).as_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRY(BAN::UniqPtr<Image>::create(new_width, new_height, BAN::move(bicubic_bitmap)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue