From 219734a813eabb60d0e01821289d320d6ad4b1fd Mon Sep 17 00:00:00 2001 From: Oskari Alaranta Date: Sat, 7 Feb 2026 18:32:40 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 + .vscode/settings.json | 9 + BAN/BAN/Assert.cpp | 22 + BAN/BAN/Assert.cpp.o | Bin 0 -> 13464 bytes BAN/BAN/New.cpp | 9 + BAN/BAN/New.cpp.o | Bin 0 -> 8400 bytes BAN/BAN/StringView.cpp | 11 + BAN/BAN/StringView.cpp.o | Bin 0 -> 24912 bytes BAN/BAN/Time.cpp | 71 + BAN/BAN/Time.cpp.o | Bin 0 -> 9488 bytes BAN/CMakeLists.txt | 16 + BAN/include/BAN/Array.h | 104 + BAN/include/BAN/Assert.h | 14 + BAN/include/BAN/Atomic.h | 99 + BAN/include/BAN/Bitcast.h | 12 + BAN/include/BAN/Bitset.h | 27 + BAN/include/BAN/ByteSpan.h | 124 + BAN/include/BAN/CircularQueue.h | 158 + BAN/include/BAN/Debug.h | 61 + BAN/include/BAN/Endianness.h | 125 + BAN/include/BAN/Errors.h | 194 ++ BAN/include/BAN/Formatter.h | 257 ++ BAN/include/BAN/ForwardList.h | 20 + BAN/include/BAN/Function.h | 148 + BAN/include/BAN/GUID.h | 73 + BAN/include/BAN/Hash.h | 50 + BAN/include/BAN/HashMap.h | 319 ++ BAN/include/BAN/HashSet.h | 199 ++ BAN/include/BAN/Heap.h | 89 + BAN/include/BAN/IPv4.h | 75 + BAN/include/BAN/Iteration.h | 12 + BAN/include/BAN/Iterators.h | 330 ++ BAN/include/BAN/Limits.h | 156 + BAN/include/BAN/LinkedList.h | 426 +++ BAN/include/BAN/MAC.h | 47 + BAN/include/BAN/Math.h | 443 +++ BAN/include/BAN/Move.h | 29 + BAN/include/BAN/New.h | 18 + BAN/include/BAN/NoCopyMove.h | 11 + BAN/include/BAN/Numbers.h | 28 + BAN/include/BAN/Optional.h | 204 ++ BAN/include/BAN/PlacementNew.h | 10 + BAN/include/BAN/PriorityQueue.h | 64 + BAN/include/BAN/Queue.h | 236 ++ BAN/include/BAN/RefPtr.h | 161 + BAN/include/BAN/ScopeGuard.h | 28 + BAN/include/BAN/Sort.h | 165 + BAN/include/BAN/Span.h | 104 + BAN/include/BAN/String.h | 361 +++ BAN/include/BAN/StringView.h | 255 ++ BAN/include/BAN/Swap.h | 16 + BAN/include/BAN/Time.h | 37 + BAN/include/BAN/Traits.h | 150 + BAN/include/BAN/UTF8.h | 84 + BAN/include/BAN/UniqPtr.h | 99 + BAN/include/BAN/Variant.h | 317 ++ BAN/include/BAN/Vector.h | 418 +++ BAN/include/BAN/WeakPtr.h | 128 + CMakeLists.txt | 39 + LibClipboard/CMakeLists.txt | 9 + LibClipboard/Clipboard.cpp | 201 ++ LibClipboard/include/LibClipboard/Clipboard.h | 37 + LibDEFLATE/CMakeLists.txt | 11 + LibDEFLATE/Compressor.cpp | 620 ++++ LibDEFLATE/Decompressor.cpp | 277 ++ LibDEFLATE/HuffmanTree.cpp | 141 + LibDEFLATE/include/LibDEFLATE/BitStream.h | 118 + LibDEFLATE/include/LibDEFLATE/Compressor.h | 67 + LibDEFLATE/include/LibDEFLATE/Decompressor.h | 46 + LibDEFLATE/include/LibDEFLATE/HuffmanTree.h | 61 + LibDEFLATE/include/LibDEFLATE/StreamType.h | 12 + LibDEFLATE/include/LibDEFLATE/Utils.h | 30 + LibFont/CMakeLists.txt | 10 + LibFont/Font.cpp | 53 + LibFont/PSF.cpp | 214 ++ LibFont/TTF.cpp | 8 + LibFont/include/LibFont/Font.h | 50 + LibFont/include/LibFont/PSF.h | 14 + LibGUI/CMakeLists.txt | 19 + LibGUI/MessageBox.cpp | 67 + LibGUI/Texture.cpp | 258 ++ LibGUI/Widget/Button.cpp | 60 + LibGUI/Widget/Grid.cpp | 53 + LibGUI/Widget/Label.cpp | 44 + LibGUI/Widget/RoundedWidget.cpp | 117 + LibGUI/Widget/TextArea.cpp | 96 + LibGUI/Widget/Widget.cpp | 241 ++ LibGUI/Window.cpp | 393 +++ LibGUI/include/LibGUI/MessageBox.h | 18 + LibGUI/include/LibGUI/Packet.h | 381 +++ LibGUI/include/LibGUI/Texture.h | 103 + LibGUI/include/LibGUI/Widget/Button.h | 54 + LibGUI/include/LibGUI/Widget/Grid.h | 42 + LibGUI/include/LibGUI/Widget/Label.h | 44 + LibGUI/include/LibGUI/Widget/RoundedWidget.h | 42 + LibGUI/include/LibGUI/Widget/TextArea.h | 50 + LibGUI/include/LibGUI/Widget/Widget.h | 163 + LibGUI/include/LibGUI/Window.h | 123 + LibImage/CMakeLists.txt | 12 + LibImage/Image.cpp | 240 ++ LibImage/Netbpm.cpp | 140 + LibImage/PNG.cpp | 506 +++ LibImage/include/LibImage/Image.h | 91 + LibImage/include/LibImage/Netbpm.h | 11 + LibImage/include/LibImage/PNG.h | 11 + LibInput/CMakeLists.txt | 10 + LibInput/KeyEvent.cpp | 120 + LibInput/KeyboardLayout.cpp | 464 +++ LibInput/include/LibInput/Joystick.h | 60 + LibInput/include/LibInput/KeyEvent.h | 107 + LibInput/include/LibInput/KeyboardLayout.h | 30 + LibInput/include/LibInput/MouseEvent.h | 61 + ProgramLauncher/CMakeLists.txt | 11 + ProgramLauncher/main.cpp | 344 ++ Terminal/CMakeLists.txt | 13 + Terminal/Terminal.cpp | 1238 +++++++ Terminal/Terminal.h | 128 + Terminal/main.cpp | 7 + WindowServer/CMakeLists.txt | 17 + WindowServer/Cursor.h | 35 + WindowServer/Framebuffer.cpp | 61 + WindowServer/Framebuffer.h | 15 + WindowServer/Utils.h | 111 + WindowServer/Window.cpp | 106 + WindowServer/Window.h | 119 + WindowServer/WindowServer.cpp | 1403 ++++++++ WindowServer/WindowServer.h | 114 + WindowServer/main.cpp | 610 ++++ foobar.py | 33 + lat0-16.psfu | Bin 0 -> 4949 bytes qwerty.keymap | 93 + us.keymap | 33 + xbanan/CMakeLists.txt | 9 + xbanan/main.cpp | 2883 +++++++++++++++++ 134 files changed, 20257 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 BAN/BAN/Assert.cpp create mode 100644 BAN/BAN/Assert.cpp.o create mode 100644 BAN/BAN/New.cpp create mode 100644 BAN/BAN/New.cpp.o create mode 100644 BAN/BAN/StringView.cpp create mode 100644 BAN/BAN/StringView.cpp.o create mode 100644 BAN/BAN/Time.cpp create mode 100644 BAN/BAN/Time.cpp.o create mode 100644 BAN/CMakeLists.txt create mode 100644 BAN/include/BAN/Array.h create mode 100644 BAN/include/BAN/Assert.h create mode 100644 BAN/include/BAN/Atomic.h create mode 100644 BAN/include/BAN/Bitcast.h create mode 100644 BAN/include/BAN/Bitset.h create mode 100644 BAN/include/BAN/ByteSpan.h create mode 100644 BAN/include/BAN/CircularQueue.h create mode 100644 BAN/include/BAN/Debug.h create mode 100644 BAN/include/BAN/Endianness.h create mode 100644 BAN/include/BAN/Errors.h create mode 100644 BAN/include/BAN/Formatter.h create mode 100644 BAN/include/BAN/ForwardList.h create mode 100644 BAN/include/BAN/Function.h create mode 100644 BAN/include/BAN/GUID.h create mode 100644 BAN/include/BAN/Hash.h create mode 100644 BAN/include/BAN/HashMap.h create mode 100644 BAN/include/BAN/HashSet.h create mode 100644 BAN/include/BAN/Heap.h create mode 100644 BAN/include/BAN/IPv4.h create mode 100644 BAN/include/BAN/Iteration.h create mode 100644 BAN/include/BAN/Iterators.h create mode 100644 BAN/include/BAN/Limits.h create mode 100644 BAN/include/BAN/LinkedList.h create mode 100644 BAN/include/BAN/MAC.h create mode 100644 BAN/include/BAN/Math.h create mode 100644 BAN/include/BAN/Move.h create mode 100644 BAN/include/BAN/New.h create mode 100644 BAN/include/BAN/NoCopyMove.h create mode 100644 BAN/include/BAN/Numbers.h create mode 100644 BAN/include/BAN/Optional.h create mode 100644 BAN/include/BAN/PlacementNew.h create mode 100644 BAN/include/BAN/PriorityQueue.h create mode 100644 BAN/include/BAN/Queue.h create mode 100644 BAN/include/BAN/RefPtr.h create mode 100644 BAN/include/BAN/ScopeGuard.h create mode 100644 BAN/include/BAN/Sort.h create mode 100644 BAN/include/BAN/Span.h create mode 100644 BAN/include/BAN/String.h create mode 100644 BAN/include/BAN/StringView.h create mode 100644 BAN/include/BAN/Swap.h create mode 100644 BAN/include/BAN/Time.h create mode 100644 BAN/include/BAN/Traits.h create mode 100644 BAN/include/BAN/UTF8.h create mode 100644 BAN/include/BAN/UniqPtr.h create mode 100644 BAN/include/BAN/Variant.h create mode 100644 BAN/include/BAN/Vector.h create mode 100644 BAN/include/BAN/WeakPtr.h create mode 100644 CMakeLists.txt create mode 100644 LibClipboard/CMakeLists.txt create mode 100644 LibClipboard/Clipboard.cpp create mode 100644 LibClipboard/include/LibClipboard/Clipboard.h create mode 100644 LibDEFLATE/CMakeLists.txt create mode 100644 LibDEFLATE/Compressor.cpp create mode 100644 LibDEFLATE/Decompressor.cpp create mode 100644 LibDEFLATE/HuffmanTree.cpp create mode 100644 LibDEFLATE/include/LibDEFLATE/BitStream.h create mode 100644 LibDEFLATE/include/LibDEFLATE/Compressor.h create mode 100644 LibDEFLATE/include/LibDEFLATE/Decompressor.h create mode 100644 LibDEFLATE/include/LibDEFLATE/HuffmanTree.h create mode 100644 LibDEFLATE/include/LibDEFLATE/StreamType.h create mode 100644 LibDEFLATE/include/LibDEFLATE/Utils.h create mode 100644 LibFont/CMakeLists.txt create mode 100644 LibFont/Font.cpp create mode 100644 LibFont/PSF.cpp create mode 100644 LibFont/TTF.cpp create mode 100644 LibFont/include/LibFont/Font.h create mode 100644 LibFont/include/LibFont/PSF.h create mode 100644 LibGUI/CMakeLists.txt create mode 100644 LibGUI/MessageBox.cpp create mode 100644 LibGUI/Texture.cpp create mode 100644 LibGUI/Widget/Button.cpp create mode 100644 LibGUI/Widget/Grid.cpp create mode 100644 LibGUI/Widget/Label.cpp create mode 100644 LibGUI/Widget/RoundedWidget.cpp create mode 100644 LibGUI/Widget/TextArea.cpp create mode 100644 LibGUI/Widget/Widget.cpp create mode 100644 LibGUI/Window.cpp create mode 100644 LibGUI/include/LibGUI/MessageBox.h create mode 100644 LibGUI/include/LibGUI/Packet.h create mode 100644 LibGUI/include/LibGUI/Texture.h create mode 100644 LibGUI/include/LibGUI/Widget/Button.h create mode 100644 LibGUI/include/LibGUI/Widget/Grid.h create mode 100644 LibGUI/include/LibGUI/Widget/Label.h create mode 100644 LibGUI/include/LibGUI/Widget/RoundedWidget.h create mode 100644 LibGUI/include/LibGUI/Widget/TextArea.h create mode 100644 LibGUI/include/LibGUI/Widget/Widget.h create mode 100644 LibGUI/include/LibGUI/Window.h create mode 100644 LibImage/CMakeLists.txt create mode 100644 LibImage/Image.cpp create mode 100644 LibImage/Netbpm.cpp create mode 100644 LibImage/PNG.cpp create mode 100644 LibImage/include/LibImage/Image.h create mode 100644 LibImage/include/LibImage/Netbpm.h create mode 100644 LibImage/include/LibImage/PNG.h create mode 100644 LibInput/CMakeLists.txt create mode 100644 LibInput/KeyEvent.cpp create mode 100644 LibInput/KeyboardLayout.cpp create mode 100644 LibInput/include/LibInput/Joystick.h create mode 100644 LibInput/include/LibInput/KeyEvent.h create mode 100644 LibInput/include/LibInput/KeyboardLayout.h create mode 100644 LibInput/include/LibInput/MouseEvent.h create mode 100644 ProgramLauncher/CMakeLists.txt create mode 100644 ProgramLauncher/main.cpp create mode 100644 Terminal/CMakeLists.txt create mode 100644 Terminal/Terminal.cpp create mode 100644 Terminal/Terminal.h create mode 100644 Terminal/main.cpp create mode 100644 WindowServer/CMakeLists.txt create mode 100644 WindowServer/Cursor.h create mode 100644 WindowServer/Framebuffer.cpp create mode 100644 WindowServer/Framebuffer.h create mode 100644 WindowServer/Utils.h create mode 100644 WindowServer/Window.cpp create mode 100644 WindowServer/Window.h create mode 100644 WindowServer/WindowServer.cpp create mode 100644 WindowServer/WindowServer.h create mode 100644 WindowServer/main.cpp create mode 100644 foobar.py create mode 100644 lat0-16.psfu create mode 100644 qwerty.keymap create mode 100644 us.keymap create mode 100644 xbanan/CMakeLists.txt create mode 100644 xbanan/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4fb4fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.cache/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d677109 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "clangd.arguments": [ + "--compile-commands-dir=${workspaceFolder}/build", + "-header-insertion=never" + ], + "search.exclude": { + ".cache/**": true + } +} diff --git a/BAN/BAN/Assert.cpp b/BAN/BAN/Assert.cpp new file mode 100644 index 0000000..6bc462a --- /dev/null +++ b/BAN/BAN/Assert.cpp @@ -0,0 +1,22 @@ +#include + +#if __is_kernel + +#include + +[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg) +{ + Kernel::panic_impl(location, msg); +} + +#else + +#include + +[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg) +{ + derrorln("{}: {}", location, msg); + __builtin_trap(); +} + +#endif diff --git a/BAN/BAN/Assert.cpp.o b/BAN/BAN/Assert.cpp.o new file mode 100644 index 0000000000000000000000000000000000000000..9b01a13e168ec3374d0afa85f232c428186b5f99 GIT binary patch literal 13464 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 literal 0 HcmV?d00001 diff --git a/BAN/BAN/New.cpp b/BAN/BAN/New.cpp new file mode 100644 index 0000000..51d3577 --- /dev/null +++ b/BAN/BAN/New.cpp @@ -0,0 +1,9 @@ +#include + +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); } diff --git a/BAN/BAN/New.cpp.o b/BAN/BAN/New.cpp.o new file mode 100644 index 0000000000000000000000000000000000000000..00ca23b36d3d6e784b0f70c8ab0ee515cd23ea4c GIT binary patch literal 8400 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 literal 0 HcmV?d00001 diff --git a/BAN/BAN/StringView.cpp b/BAN/BAN/StringView.cpp new file mode 100644 index 0000000..5a2eea0 --- /dev/null +++ b/BAN/BAN/StringView.cpp @@ -0,0 +1,11 @@ +#include +#include + +namespace BAN +{ + + StringView::StringView(const String& other) + : StringView(other.data(), other.size()) + { } + +} diff --git a/BAN/BAN/StringView.cpp.o b/BAN/BAN/StringView.cpp.o new file mode 100644 index 0000000000000000000000000000000000000000..149f34f6b6eca2bce9105c3c54b88cde2dcfe1ce GIT binary patch literal 24912 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 literal 0 HcmV?d00001 diff --git a/BAN/BAN/Time.cpp b/BAN/BAN/Time.cpp new file mode 100644 index 0000000..73237a8 --- /dev/null +++ b/BAN/BAN/Time.cpp @@ -0,0 +1,71 @@ +#include + +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; + } + +} diff --git a/BAN/BAN/Time.cpp.o b/BAN/BAN/Time.cpp.o new file mode 100644 index 0000000000000000000000000000000000000000..d477b5480017d0ce887bb76a60ca43c8213cd160 GIT binary patch literal 9488 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 bohTdsOP{| +#include + +#include + +namespace BAN +{ + + template + class Array + { + public: + using size_type = decltype(S); + using value_type = T; + using iterator = IteratorSimple; + using const_iterator = ConstIteratorSimple; + + 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 span() { return Span(m_data, size()); } + Span 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 + constexpr Array::Array(const T& value) + { + for (size_type i = 0; i < S; i++) + m_data[i] = value; + } + + template + constexpr const T& Array::operator[](size_type index) const + { + ASSERT(index < S); + return m_data[index]; + } + + template + constexpr T& Array::operator[](size_type index) + { + ASSERT(index < S); + return m_data[index]; + } + + template + constexpr const T& Array::back() const + { + ASSERT(S != 0); + return m_data[S - 1]; + } + + template + constexpr T& Array::back() + { + ASSERT(S != 0); + return m_data[S - 1]; + } + + template + constexpr const T& Array::front() const + { + ASSERT(S != 0); + return m_data[0]; + } + + template + constexpr T& Array::front() + { + ASSERT(S != 0); + return m_data[0]; + } + + template + constexpr typename Array::size_type Array::size() const + { + return S; + } + +} diff --git a/BAN/include/BAN/Assert.h b/BAN/include/BAN/Assert.h new file mode 100644 index 0000000..9877722 --- /dev/null +++ b/BAN/include/BAN/Assert.h @@ -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); diff --git a/BAN/include/BAN/Atomic.h b/BAN/include/BAN/Atomic.h new file mode 100644 index 0000000..a99376f --- /dev/null +++ b/BAN/include/BAN/Atomic.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +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 concept atomic_c = is_integral_v || is_pointer_v; + template concept atomic_lockfree_c = (is_integral_v || is_pointer_v) && __atomic_always_lock_free(sizeof(T), 0); + + template + inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); } + template + inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); } + + template + 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 + 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 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 + 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; + }; + +} diff --git a/BAN/include/BAN/Bitcast.h b/BAN/include/BAN/Bitcast.h new file mode 100644 index 0000000..e025a91 --- /dev/null +++ b/BAN/include/BAN/Bitcast.h @@ -0,0 +1,12 @@ +#pragma once + +namespace BAN +{ + + template + constexpr To bit_cast(const From& from) + { + return __builtin_bit_cast(To, from); + } + +} diff --git a/BAN/include/BAN/Bitset.h b/BAN/include/BAN/Bitset.h new file mode 100644 index 0000000..201a80e --- /dev/null +++ b/BAN/include/BAN/Bitset.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +namespace BAN +{ + + template + class Bitset + { + public: + using internal_type = uint64_t; + + public: + + private: + BAN::Array m_bits; + }; + + void foo() + { + sizeof(Bitset<65>); + }; + +} diff --git a/BAN/include/BAN/ByteSpan.h b/BAN/include/BAN/ByteSpan.h new file mode 100644 index 0000000..faea6fa --- /dev/null +++ b/BAN/include/BAN/ByteSpan.h @@ -0,0 +1,124 @@ +#pragma once + +#include + +#include + +namespace BAN +{ + + template + class ByteSpanGeneral + { + public: + using value_type = maybe_const_t; + using size_type = size_t; + + public: + ByteSpanGeneral() = default; + ByteSpanGeneral(value_type* data, size_type size) + : m_data(data) + , m_size(size) + { } + + template + ByteSpanGeneral(const ByteSpanGeneral& other) requires(CONST || !SRC_CONST) + : m_data(other.data()) + , m_size(other.size()) + { } + template + ByteSpanGeneral(ByteSpanGeneral&& other) requires(CONST || !SRC_CONST) + : m_data(other.data()) + , m_size(other.size()) + { + other.clear(); + } + + template + ByteSpanGeneral(const Span& other) requires(is_same_v || (is_same_v && CONST)) + : m_data(other.data()) + , m_size(other.size()) + { } + template + ByteSpanGeneral(Span&& other) requires(is_same_v || (is_same_v && CONST)) + : m_data(other.data()) + , m_size(other.size()) + { + other.clear(); + } + + template + ByteSpanGeneral& operator=(const ByteSpanGeneral& other) requires(CONST || !SRC_CONST) + { + m_data = other.data(); + m_size = other.size(); + return *this; + } + template + ByteSpanGeneral& operator=(ByteSpanGeneral&& other) requires(CONST || !SRC_CONST) + { + m_data = other.data(); + m_size = other.size(); + other.clear(); + return *this; + } + + template + static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v) + { + return ByteSpanGeneral(reinterpret_cast(&value), sizeof(S)); + } + + template + S& as() const requires(!CONST || is_const_v) + { + ASSERT(m_data); + ASSERT(m_size >= sizeof(S)); + return *reinterpret_cast(m_data); + } + + template + Span as_span() const requires(!CONST || is_const_v) + { + ASSERT(m_data); + return Span(reinterpret_cast(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; + }; + + using ByteSpan = ByteSpanGeneral; + using ConstByteSpan = ByteSpanGeneral; + +} diff --git a/BAN/include/BAN/CircularQueue.h b/BAN/include/BAN/CircularQueue.h new file mode 100644 index 0000000..9400d81 --- /dev/null +++ b/BAN/include/BAN/CircularQueue.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace BAN +{ + + template + class CircularQueue + { + public: + using size_type = size_t; + using value_type = T; + + public: + CircularQueue() = default; + ~CircularQueue(); + + void push(const T&); + void push(T&&); + template + void emplace(Args&&... args) requires is_constructible_v; + + 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 + CircularQueue::~CircularQueue() + { + clear(); + } + + template + void CircularQueue::push(const T& value) + { + emplace(BAN::move(T(value))); + } + + template + void CircularQueue::push(T&& value) + { + emplace(BAN::move(value)); + } + + template + template + void CircularQueue::emplace(Args&&... args) requires is_constructible_v + { + ASSERT(!full()); + new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward(args)...); + m_size++; + } + + template + void CircularQueue::pop() + { + ASSERT(!empty()); + element_at(m_first)->~T(); + m_first = (m_first + 1) % capacity(); + m_size--; + } + + template + const T& CircularQueue::front() const + { + ASSERT(!empty()); + return *element_at(m_first); + } + + template + T& CircularQueue::front() + { + ASSERT(!empty()); + return *element_at(m_first); + } + + template + const T& CircularQueue::back() const + { + ASSERT(!empty()); + return *element_at((m_first + m_size - 1) % capacity()); + } + + template + T& CircularQueue::back() + { + ASSERT(!empty()); + return *element_at((m_first + m_size - 1) % capacity()); + } + + template + const T& CircularQueue::operator[](size_t index) const + { + ASSERT(index < m_size); + return *element_at((m_first + index) % capacity()); + } + + template + T& CircularQueue::operator[](size_t index) + { + ASSERT(index < m_size); + return *element_at((m_first + index) % capacity()); + } + + template + void CircularQueue::clear() + { + for (size_type i = 0; i < m_size; i++) + element_at((m_first + i) % capacity())->~T(); + m_size = 0; + } + + template + const T* CircularQueue::element_at(size_type index) const + { + ASSERT(index < capacity()); + return (const T*)(m_storage + index * sizeof(T)); + } + + template + T* CircularQueue::element_at(size_type index) + { + ASSERT(index < capacity()); + return (T*)(m_storage + index * sizeof(T)); + } + +} diff --git a/BAN/include/BAN/Debug.h b/BAN/include/BAN/Debug.h new file mode 100644 index 0000000..bb5c925 --- /dev/null +++ b/BAN/include/BAN/Debug.h @@ -0,0 +1,61 @@ +#pragma once + +#if __is_kernel + +#include + +#else + +#include +#include + +#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 diff --git a/BAN/include/BAN/Endianness.h b/BAN/include/BAN/Endianness.h new file mode 100644 index 0000000..e1ccc09 --- /dev/null +++ b/BAN/include/BAN/Endianness.h @@ -0,0 +1,125 @@ +#pragma once + +#include + +#include + +namespace BAN +{ + + template + 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 + constexpr T host_to_little_endian(T value) + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return value; +#else + return swap_endianness(value); +#endif + } + + template + constexpr T little_endian_to_host(T value) + { + return host_to_little_endian(value); + } + + template + constexpr T host_to_big_endian(T value) + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return value; +#else + return swap_endianness(value); +#endif + } + + template + constexpr T big_endian_to_host(T value) + { + return host_to_big_endian(value); + } + + template + 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 + 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 + using NetworkEndian = BigEndian; + + template + constexpr T host_to_network_endian(T value) + { + return host_to_big_endian(value); + } + + template + constexpr T network_endian_to_host(T value) + { + return big_endian_to_host(value); + } + +} diff --git a/BAN/include/BAN/Errors.h b/BAN/include/BAN/Errors.h new file mode 100644 index 0000000..dc9e9fe --- /dev/null +++ b/BAN/include/BAN/Errors.h @@ -0,0 +1,194 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#ifdef __is_kernel + #include + #include + #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 + #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 + 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 + 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(); } + const Error& error() const { return m_data.template get(); } + Error& error() { return m_data.template get(); } + const T& value() const { return m_data.template get(); } + T& value() { return m_data.template get(); } + + Error release_error() { return move(error()); m_data.clear(); } + T release_value() { return move(value()); m_data.clear(); } + + private: + Variant m_data; + }; + + template + class [[nodiscard]] ErrorOr + { + public: + ErrorOr(T value) + { + m_data.template set(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() { return m_data.template get(); } + const Error& error() const { return m_data.template get(); } + T value() { return m_data.template get(); } + + Error release_error() { return move(error()); m_data.clear(); } + T release_value() { return value(); m_data.clear(); } + + private: + Variant m_data; + }; + + template<> + class [[nodiscard]] ErrorOr + { + 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 + void print_argument(F putc, const Error& error, const ValueFormat& format) + { + print_argument(putc, error.get_message(), format); + } +} diff --git a/BAN/include/BAN/Formatter.h b/BAN/include/BAN/Formatter.h new file mode 100644 index 0000000..4f36fa2 --- /dev/null +++ b/BAN/include/BAN/Formatter.h @@ -0,0 +1,257 @@ +#pragma once + +#include + +#include +#include + +namespace BAN::Formatter +{ + + struct ValueFormat; + + template + concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format) + { + (print_argument(putc, BAN::forward(args), format), ...); + }; + + template + inline void print_argument(F putc, T value, const ValueFormat& format); + + namespace detail + { + template + 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 + inline void print(F putc, const char* format) + { + while (*format) + { + putc(*format); + format++; + } + } + + template requires PrintableArguments + 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)); + if (arg_len == size_t(-1)) + return print(putc, format); + print(putc, format + arg_len, forward(args)...); + } + } + + template + inline void println(F putc, const char* format, Args&&... args) + { + print(putc, format, args...); + putc('\n'); + } + + namespace detail + { + + template + 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(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 + 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 + 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 + 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 inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); } + template inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); } + template inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); } + + template inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); } + template inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); } + template inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); } + template inline void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); } + +} diff --git a/BAN/include/BAN/ForwardList.h b/BAN/include/BAN/ForwardList.h new file mode 100644 index 0000000..6d1f0d5 --- /dev/null +++ b/BAN/include/BAN/ForwardList.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace BAN +{ + + + template class Array; + template class ErrorOr; + template class Function; + template class Queue; + class String; + class StringView; + template class Vector; + template class LinkedList; + template requires (!is_const_v && ...) class Variant; + +} diff --git a/BAN/include/BAN/Function.h b/BAN/include/BAN/Function.h new file mode 100644 index 0000000..9406872 --- /dev/null +++ b/BAN/include/BAN/Function.h @@ -0,0 +1,148 @@ +#pragma once + +#include +#include +#include + +namespace BAN +{ + + template + class Function; + template + class Function + { + public: + Function() = default; + Function(Ret(*function)(Args...)) + { + static_assert(sizeof(CallablePointer) <= m_size); + new (m_storage) CallablePointer(function); + } + template + Function(Ret(Own::*function)(Args...), Own& owner) + { + static_assert(sizeof(CallableMember) <= m_size); + new (m_storage) CallableMember(function, owner); + } + template + Function(Ret(Own::*function)(Args...) const, const Own& owner) + { + static_assert(sizeof(CallableMemberConst) <= m_size); + new (m_storage) CallableMemberConst(function, owner); + } + template + Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward(args)...) } -> BAN::same_as; } + { + static_assert(sizeof(CallableLambda) <= m_size); + new (m_storage) CallableLambda(lambda); + } + + ~Function() + { + clear(); + } + + Ret operator()(Args... args) const + { + ASSERT(*this); + return reinterpret_cast(m_storage)->call(forward(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(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)...); + } + + private: + Ret(*m_function)(Args...) = nullptr; + }; + + template + 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)...); + } + + private: + Own& m_owner; + Ret(Own::*m_function)(Args...) = nullptr; + }; + + template + 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)...); + } + + private: + const Own& m_owner; + Ret(Own::*m_function)(Args...) const = nullptr; + }; + + template + struct CallableLambda : public CallableBase + { + CallableLambda(Lambda lambda) + : m_lambda(lambda) + { } + + virtual Ret call(Args... args) const override + { + return m_lambda(forward(args)...); + } + + private: + Lambda m_lambda; + }; + + private: + static constexpr size_t m_size = sizeof(void*) * 8; + alignas(CallableBase) uint8_t m_storage[m_size] { 0 }; + }; + +} diff --git a/BAN/include/BAN/GUID.h b/BAN/include/BAN/GUID.h new file mode 100644 index 0000000..2336ee7 --- /dev/null +++ b/BAN/include/BAN/GUID.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include + +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 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); + +} diff --git a/BAN/include/BAN/Hash.h b/BAN/include/BAN/Hash.h new file mode 100644 index 0000000..035e3be --- /dev/null +++ b/BAN/include/BAN/Hash.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +namespace BAN +{ + + template + 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 + struct hash + { + constexpr hash_t operator()(T val) const + { + if constexpr(sizeof(val) <= sizeof(uint32_t)) + return u32_hash(val); + return u64_hash(val); + } + }; + + template + struct hash + { + constexpr hash_t operator()(T val) const + { + return hash()((uintptr_t)val); + } + }; + +} diff --git a/BAN/include/BAN/HashMap.h b/BAN/include/BAN/HashMap.h new file mode 100644 index 0000000..ef2489d --- /dev/null +++ b/BAN/include/BAN/HashMap.h @@ -0,0 +1,319 @@ +#pragma once + +#include +#include +#include + +namespace BAN +{ + + template> + class HashMap + { + public: + struct Entry + { + template + Entry(const Key& key, Args&&... args) requires is_constructible_v + : key(key) + , value(forward(args)...) + {} + + template + Entry(Key&& key, Args&&... args) requires is_constructible_v + : key(BAN::move(key)) + , value(forward(args)...) + {} + + Key key; + T value; + }; + + public: + using size_type = size_t; + using key_type = Key; + using value_type = T; + using iterator = IteratorDouble; + using const_iterator = ConstIteratorDouble; + + public: + HashMap() = default; + HashMap(const HashMap&); + HashMap(HashMap&&); + ~HashMap(); + + HashMap& operator=(const HashMap&); + HashMap& operator=(HashMap&&); + + ErrorOr insert(const Key& key, const T& value) { return emplace(key, value); } + ErrorOr insert(const Key& key, T&& value) { return emplace(key, move(value)); } + ErrorOr insert(Key&& key, const T& value) { return emplace(move(key), value); } + ErrorOr insert(Key&& key, T&& value) { return emplace(move(key), move(value)); } + + ErrorOr insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); } + ErrorOr insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); } + ErrorOr insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); } + ErrorOr insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); } + + template + ErrorOr emplace(const Key& key, Args&&... args) requires is_constructible_v + { return emplace(Key(key), forward(args)...); } + template + ErrorOr emplace(Key&&, Args&&...) requires is_constructible_v; + + template + ErrorOr emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v + { return emplace_or_assign(Key(key), forward(args)...); } + template + ErrorOr emplace_or_assign(Key&&, Args&&...) requires is_constructible_v; + + 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 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 rebucket(size_type); + LinkedList& get_bucket(const Key&); + const LinkedList& get_bucket(const Key&) const; + Vector>::iterator get_bucket_iterator(const Key&); + Vector>::const_iterator get_bucket_iterator(const Key&) const; + + private: + Vector> m_buckets; + size_type m_size = 0; + + friend iterator; + }; + + template + HashMap::HashMap(const HashMap& other) + { + *this = other; + } + + template + HashMap::HashMap(HashMap&& other) + { + *this = move(other); + } + + template + HashMap::~HashMap() + { + clear(); + } + + template + HashMap& HashMap::operator=(const HashMap& other) + { + clear(); + m_buckets = other.m_buckets; + m_size = other.m_size; + return *this; + } + + template + HashMap& HashMap::operator=(HashMap&& other) + { + clear(); + m_buckets = move(other.m_buckets); + m_size = other.m_size; + other.m_size = 0; + return *this; + } + + template + template + ErrorOr::iterator> HashMap::emplace(Key&& key, Args&&... args) requires is_constructible_v + { + ASSERT(!contains(key)); + TRY(rebucket(m_size + 1)); + + auto bucket_it = get_bucket_iterator(key); + TRY(bucket_it->emplace_back(move(key), forward(args)...)); + m_size++; + + return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1)); + } + + template + template + ErrorOr::iterator> HashMap::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v + { + if (empty()) + return emplace(move(key), forward(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)...); + return iterator(m_buckets.end(), bucket_it, entry_it); + } + + return emplace(move(key), forward(args)...); + } + + template + ErrorOr HashMap::reserve(size_type size) + { + TRY(rebucket(size)); + return {}; + } + + template + void HashMap::remove(const Key& key) + { + auto it = find(key); + if (it != end()) + remove(it); + } + + template + void HashMap::remove(iterator it) + { + it.outer_current()->remove(it.inner_current()); + m_size--; + } + + template + void HashMap::clear() + { + m_buckets.clear(); + m_size = 0; + } + + template + T& HashMap::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 + const T& HashMap::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 HashMap::iterator HashMap::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 HashMap::const_iterator HashMap::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 + bool HashMap::contains(const Key& key) const + { + return find(key) != end(); + } + + template + bool HashMap::empty() const + { + return m_size == 0; + } + + template + typename HashMap::size_type HashMap::size() const + { + return m_size; + } + + template + ErrorOr HashMap::rebucket(size_type bucket_count) + { + if (m_buckets.size() >= bucket_count) + return {}; + + size_type new_bucket_count = BAN::Math::max(bucket_count, m_buckets.size() * 2); + Vector> 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 + LinkedList::Entry>& HashMap::get_bucket(const Key& key) + { + return *get_bucket_iterator(key); + } + + template + const LinkedList::Entry>& HashMap::get_bucket(const Key& key) const + { + return *get_bucket_iterator(key); + } + + template + Vector::Entry>>::iterator HashMap::get_bucket_iterator(const Key& key) + { + ASSERT(!m_buckets.empty()); + auto index = HASH()(key) % m_buckets.size(); + return next(m_buckets.begin(), index); + } + + template + Vector::Entry>>::const_iterator HashMap::get_bucket_iterator(const Key& key) const + { + ASSERT(!m_buckets.empty()); + auto index = HASH()(key) % m_buckets.size(); + return next(m_buckets.begin(), index); + } + +} diff --git a/BAN/include/BAN/HashSet.h b/BAN/include/BAN/HashSet.h new file mode 100644 index 0000000..d8a5305 --- /dev/null +++ b/BAN/include/BAN/HashSet.h @@ -0,0 +1,199 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace BAN +{ + + template> + class HashSet + { + public: + using value_type = T; + using size_type = size_t; + using iterator = IteratorDouble; + using const_iterator = ConstIteratorDouble; + + public: + HashSet() = default; + HashSet(const HashSet&); + HashSet(HashSet&&); + + HashSet& operator=(const HashSet&); + HashSet& operator=(HashSet&&); + + ErrorOr insert(const T&); + ErrorOr insert(T&&); + void remove(const T&); + void clear(); + + ErrorOr 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 rebucket(size_type); + LinkedList& get_bucket(const T&); + const LinkedList& get_bucket(const T&) const; + + private: + Vector> m_buckets; + size_type m_size = 0; + }; + + template + HashSet::HashSet(const HashSet& other) + : m_buckets(other.m_buckets) + , m_size(other.m_size) + { + } + + template + HashSet::HashSet(HashSet&& other) + : m_buckets(move(other.m_buckets)) + , m_size(other.m_size) + { + other.clear(); + } + + template + HashSet& HashSet::operator=(const HashSet& other) + { + clear(); + m_buckets = other.m_buckets; + m_size = other.m_size; + return *this; + } + + template + HashSet& HashSet::operator=(HashSet&& other) + { + clear(); + m_buckets = move(other.m_buckets); + m_size = other.m_size; + other.clear(); + return *this; + } + + template + ErrorOr HashSet::insert(const T& key) + { + return insert(move(T(key))); + } + + template + ErrorOr HashSet::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 + void HashSet::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 + void HashSet::clear() + { + m_buckets.clear(); + m_size = 0; + } + + template + ErrorOr HashSet::reserve(size_type size) + { + TRY(rebucket(size)); + return {}; + } + + template + bool HashSet::contains(const T& key) const + { + if (empty()) return false; + return get_bucket(key).contains(key); + } + + template + typename HashSet::size_type HashSet::size() const + { + return m_size; + } + + template + bool HashSet::empty() const + { + return m_size == 0; + } + + template + ErrorOr HashSet::rebucket(size_type bucket_count) + { + if (m_buckets.size() >= bucket_count) + return {}; + + size_type new_bucket_count = Math::max(bucket_count, m_buckets.size() * 2); + Vector> 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 + LinkedList& HashSet::get_bucket(const T& key) + { + ASSERT(!m_buckets.empty()); + size_type index = HASH()(key) % m_buckets.size(); + return m_buckets[index]; + } + + template + const LinkedList& HashSet::get_bucket(const T& key) const + { + ASSERT(!m_buckets.empty()); + size_type index = HASH()(key) % m_buckets.size(); + return m_buckets[index]; + } + +} diff --git a/BAN/include/BAN/Heap.h b/BAN/include/BAN/Heap.h new file mode 100644 index 0000000..28f6fb8 --- /dev/null +++ b/BAN/include/BAN/Heap.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include + +namespace BAN +{ + + namespace detail + { + + template + 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 + 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>> + 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>> + void push_heap(It begin, It end, Comp comp = {}) + { + const size_t len = distance(begin, end); + detail::heapify_up(begin, len - 1, comp); + } + + template>> + 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>> + void sort_heap(It begin, It end, Comp comp = {}) + { + while (begin != end) + pop_heap(begin, end--, comp); + } + +} diff --git a/BAN/include/BAN/IPv4.h b/BAN/include/BAN/IPv4.h new file mode 100644 index 0000000..325f0c4 --- /dev/null +++ b/BAN/include/BAN/IPv4.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +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 + { + constexpr hash_t operator()(IPv4Address ipv4) const + { + return hash()(ipv4.raw); + } + }; + +} + +namespace BAN::Formatter +{ + + template + 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); + } + } + +} diff --git a/BAN/include/BAN/Iteration.h b/BAN/include/BAN/Iteration.h new file mode 100644 index 0000000..f90385a --- /dev/null +++ b/BAN/include/BAN/Iteration.h @@ -0,0 +1,12 @@ +#pragma once + +namespace BAN +{ + + enum class Iteration + { + Continue, + Break + }; + +} diff --git a/BAN/include/BAN/Iterators.h b/BAN/include/BAN/Iterators.h new file mode 100644 index 0000000..782b59e --- /dev/null +++ b/BAN/include/BAN/Iterators.h @@ -0,0 +1,330 @@ +#pragma once + +#include +#include + +#include + +namespace BAN +{ + + template + constexpr It next(It it, size_t count) + { + for (size_t i = 0; i < count; i++) + ++it; + return it; + } + + template + requires requires(It it, size_t n) { requires is_same_v; } + constexpr It next(It it, size_t count) + { + return it + count; + } + + template + constexpr It prev(It it, size_t count) + { + for (size_t i = 0; i < count; i++) + --it; + return it; + } + + template + requires requires(It it, size_t n) { requires is_same_v; } + constexpr It prev(It it, size_t count) + { + return it - count; + } + + template + constexpr size_t distance(It it1, It it2) + { + size_t dist = 0; + while (it1 != it2) + { + ++it1; + ++dist; + } + return dist; + } + + template + requires requires(It it1, It it2) { requires is_integral_v; } + constexpr size_t distance(It it1, It it2) + { + return it2 - it1; + } + + template + class IteratorSimpleGeneral + { + public: + using value_type = T; + + public: + constexpr IteratorSimpleGeneral() = default; + template> + constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral& other) + : m_pointer(other.m_pointer) + , m_valid(other.m_valid) + { + } + + constexpr const T& operator*() const + { + ASSERT(m_pointer); + return *m_pointer; + } + template + constexpr enable_if_t operator*() + { + ASSERT(*this); + ASSERT(m_pointer); + return *m_pointer; + } + + constexpr const T* operator->() const + { + ASSERT(*this); + ASSERT(m_pointer); + return m_pointer; + } + template + constexpr enable_if_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* pointer) + : m_pointer(pointer) + , m_valid(true) + { + } + + private: + maybe_const_t* m_pointer = nullptr; + bool m_valid = false; + + friend IteratorSimpleGeneral; + friend Container; + }; + + template typename OuterContainer, template typename InnerContainer, typename Container, bool CONST> + class IteratorDoubleGeneral + { + public: + using Inner = InnerContainer; + using Outer = OuterContainer; + + using InnerIterator = either_or_t; + using OuterIterator = either_or_t; + + using value_type = T; + + public: + constexpr IteratorDoubleGeneral() = default; + template> + constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral& 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 + constexpr enable_if_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 + constexpr enable_if_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; + friend Container; + }; + + template + using IteratorSimple = IteratorSimpleGeneral; + + template + using ConstIteratorSimple = IteratorSimpleGeneral; + + template typename OuterContainer, template typename InnerContainer, typename Container> + using IteratorDouble = IteratorDoubleGeneral; + + template typename OuterContainer, template typename InnerContainer, typename Container> + using ConstIteratorDouble = IteratorDoubleGeneral; + +} diff --git a/BAN/include/BAN/Limits.h b/BAN/include/BAN/Limits.h new file mode 100644 index 0000000..5efc694 --- /dev/null +++ b/BAN/include/BAN/Limits.h @@ -0,0 +1,156 @@ +#pragma once + +#include + +#include + +namespace BAN +{ + + template + class numeric_limits + { + public: + numeric_limits() = delete; + + static inline constexpr T max() + { + if constexpr(is_same_v) + return __SCHAR_MAX__; + if constexpr(is_same_v) + return __SCHAR_MAX__; + if constexpr(is_same_v) + return (T)__SCHAR_MAX__ * 2 + 1; + + if constexpr(is_same_v) + return __SHRT_MAX__; + if constexpr(is_same_v) + return __INT_MAX__; + if constexpr(is_same_v) + return __LONG_MAX__; + if constexpr(is_same_v) + return __LONG_LONG_MAX__; + + if constexpr(is_same_v) + return (T)__SHRT_MAX__ * 2 + 1; + if constexpr(is_same_v) + return (T)__INT_MAX__ * 2 + 1; + if constexpr(is_same_v) + return (T)__LONG_MAX__ * 2 + 1; + if constexpr(is_same_v) + return (T)__LONG_LONG_MAX__ * 2 + 1; + + if constexpr(is_same_v) + return __FLT_MAX__; + if constexpr(is_same_v) + return __DBL_MAX__; + if constexpr(is_same_v) + return __LDBL_MAX__; + } + + static inline constexpr T min() + { + if constexpr(is_signed_v && is_integral_v) + return -max() - 1; + + if constexpr(is_unsigned_v && is_integral_v) + return 0; + + if constexpr(is_same_v) + return __FLT_MIN__; + if constexpr(is_same_v) + return __DBL_MIN__; + if constexpr(is_same_v) + return __LDBL_MIN__; + } + + static inline constexpr bool has_infinity() + { + if constexpr(is_same_v) + return __FLT_HAS_INFINITY__; + if constexpr(is_same_v) + return __DBL_HAS_INFINITY__; + if constexpr(is_same_v) + return __LDBL_HAS_INFINITY__; + return false; + } + + static inline constexpr T infinity() requires(has_infinity()) + { + if constexpr(is_same_v) + return __builtin_inff(); + if constexpr(is_same_v) + return __builtin_inf(); + if constexpr(is_same_v) + return __builtin_infl(); + } + + static inline constexpr bool has_quiet_NaN() + { + if constexpr(is_same_v) + return __FLT_HAS_QUIET_NAN__; + if constexpr(is_same_v) + return __DBL_HAS_QUIET_NAN__; + if constexpr(is_same_v) + return __LDBL_HAS_QUIET_NAN__; + return false; + } + + static inline constexpr T quiet_NaN() requires(has_quiet_NaN()) + { + if constexpr(is_same_v) + return __builtin_nanf(""); + if constexpr(is_same_v) + return __builtin_nan(""); + if constexpr(is_same_v) + return __builtin_nanl(""); + } + + static inline constexpr int max_exponent2() + { + static_assert(__FLT_RADIX__ == 2); + if constexpr(is_same_v) + return __FLT_MAX_EXP__; + if constexpr(is_same_v) + return __DBL_MAX_EXP__; + if constexpr(is_same_v) + return __LDBL_MAX_EXP__; + return 0; + } + + static inline constexpr int max_exponent10() + { + if constexpr(is_same_v) + return __FLT_MAX_10_EXP__; + if constexpr(is_same_v) + return __DBL_MAX_10_EXP__; + if constexpr(is_same_v) + return __LDBL_MAX_10_EXP__; + return 0; + } + + static inline constexpr int min_exponent2() + { + static_assert(__FLT_RADIX__ == 2); + if constexpr(is_same_v) + return __FLT_MIN_EXP__; + if constexpr(is_same_v) + return __DBL_MIN_EXP__; + if constexpr(is_same_v) + return __LDBL_MIN_EXP__; + return 0; + } + + static inline constexpr int min_exponent10() + { + if constexpr(is_same_v) + return __FLT_MIN_10_EXP__; + if constexpr(is_same_v) + return __DBL_MIN_10_EXP__; + if constexpr(is_same_v) + return __LDBL_MIN_10_EXP__; + return 0; + } + }; + +} diff --git a/BAN/include/BAN/LinkedList.h b/BAN/include/BAN/LinkedList.h new file mode 100644 index 0000000..a7b5f3a --- /dev/null +++ b/BAN/include/BAN/LinkedList.h @@ -0,0 +1,426 @@ +#pragma once + +#include +#include +#include +#include + +namespace BAN +{ + + template + class LinkedListIterator; + + template + class LinkedList + { + public: + using size_type = size_t; + using value_type = T; + using iterator = LinkedListIterator; + using const_iterator = LinkedListIterator; + + public: + LinkedList() = default; + LinkedList(const LinkedList& other) requires is_copy_constructible_v { *this = other; } + LinkedList(LinkedList&& other) { *this = move(other); } + ~LinkedList() { clear(); } + + LinkedList& operator=(const LinkedList&) requires is_copy_constructible_v; + LinkedList& operator=(LinkedList&&); + + ErrorOr push_back(const T&); + ErrorOr push_back(T&&); + ErrorOr insert(iterator, const T&); + ErrorOr insert(iterator, T&&); + template + ErrorOr emplace_back(Args&&...) requires is_constructible_v; + template + ErrorOr emplace(iterator, Args&&...) requires is_constructible_v; + + 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 + ErrorOr 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; + friend class LinkedListIterator; + }; + + template + class LinkedListIterator + { + public: + using value_type = T; + using data_type = maybe_const_t::Node>; + + public: + LinkedListIterator() = default; + template + LinkedListIterator(const LinkedListIterator&, enable_if_t* = 0); + + LinkedListIterator& operator++(); + LinkedListIterator& operator--(); + LinkedListIterator operator++(int); + LinkedListIterator operator--(int); + + template + enable_if_t operator*(); + const T& operator*() const; + + template + enable_if_t operator->(); + const T* operator->() const; + + bool operator==(const LinkedListIterator&) const; + bool operator!=(const LinkedListIterator&) const; + operator bool() const; + + private: + LinkedListIterator(data_type*, bool); + + private: + data_type* m_current = nullptr; + bool m_past_end = false; + + friend class LinkedList; + friend class LinkedListIterator; + }; + + template + LinkedList& LinkedList::operator=(const LinkedList& other) requires is_copy_constructible_v + { + clear(); + for (const T& elem : other) + MUST(push_back(elem)); + return *this; + } + + template + LinkedList& LinkedList::operator=(LinkedList&& 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 + LinkedList::Node* LinkedList::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 + void LinkedList::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 + ErrorOr LinkedList::push_back(const T& value) + { + return push_back(move(T(value))); + } + + template + ErrorOr LinkedList::push_back(T&& value) + { + return insert(end(), move(value)); + } + + template + ErrorOr LinkedList::insert(iterator iter, const T& value) + { + return insert(iter, move(T(value))); + } + + template + ErrorOr LinkedList::insert(iterator iter, T&& value) + { + Node* new_node = TRY(allocate_node(move(value))); + insert_node(iter, new_node); + return {}; + } + + template + template + ErrorOr LinkedList::emplace_back(Args&&... args) requires is_constructible_v + { + return emplace(end(), forward(args)...); + } + + template + template + ErrorOr LinkedList::emplace(iterator iter, Args&&... args) requires is_constructible_v + { + Node* new_node = TRY(allocate_node(forward(args)...)); + insert_node(iter, new_node); + return {}; + } + + template + void LinkedList::pop_back() + { + ASSERT(!empty()); + remove(iterator(m_last, false)); + } + + template + LinkedList::iterator LinkedList::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 + void LinkedList::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 + LinkedList::iterator LinkedList::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 + const T& LinkedList::back() const + { + ASSERT(!empty()); + return *const_iterator(m_last, false); + } + + template + T& LinkedList::back() + { + ASSERT(!empty()); + return *iterator(m_last, false); + } + + template + const T& LinkedList::front() const + { + ASSERT(!empty()); + return *const_iterator(m_data, false); + } + + template + T& LinkedList::front() + { + ASSERT(!empty()); + return *iterator(m_data, false); + } + + template + bool LinkedList::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 LinkedList::size_type LinkedList::size() const + { + return m_size; + } + + template + bool LinkedList::empty() const + { + return m_size == 0; + } + + template + template + ErrorOr::Node*> LinkedList::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)...); + return node; + } + + template + template + LinkedListIterator::LinkedListIterator(const LinkedListIterator& other, enable_if_t*) + : m_current(other.m_current) + , m_past_end(other.m_past_end) + { + } + + template + LinkedListIterator::LinkedListIterator(data_type* node, bool past_end) + : m_current(node) + , m_past_end(past_end) + { + } + + template + LinkedListIterator& LinkedListIterator::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 + LinkedListIterator& LinkedListIterator::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 + LinkedListIterator LinkedListIterator::operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + template + LinkedListIterator LinkedListIterator::operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + + template + template + enable_if_t LinkedListIterator::operator*() + { + ASSERT(m_current); + return m_current->value; + } + + template + const T& LinkedListIterator::operator*() const + { + ASSERT(m_current); + return m_current->value; + } + + template + template + enable_if_t LinkedListIterator::operator->() + { + ASSERT(m_current); + return &m_current->value; + } + + template + const T* LinkedListIterator::operator->() const + { + ASSERT(m_current); + return &m_current->value; + } + + template + bool LinkedListIterator::operator==(const LinkedListIterator& other) const + { + if (m_current != other.m_current) + return false; + return m_past_end == other.m_past_end; + } + + template + bool LinkedListIterator::operator!=(const LinkedListIterator& other) const + { + return !(*this == other); + } + + template + LinkedListIterator::operator bool() const + { + return m_current; + } + +} diff --git a/BAN/include/BAN/MAC.h b/BAN/include/BAN/MAC.h new file mode 100644 index 0000000..cd4e408 --- /dev/null +++ b/BAN/include/BAN/MAC.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +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 + 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); + } + } + +} diff --git a/BAN/include/BAN/Math.h b/BAN/include/BAN/Math.h new file mode 100644 index 0000000..0a66347 --- /dev/null +++ b/BAN/include/BAN/Math.h @@ -0,0 +1,443 @@ +#pragma once + +#include +#include +#include + +#include + +namespace BAN::Math +{ + + template + inline constexpr T abs(T x) + { + return x < 0 ? -x : x; + } + + template + inline constexpr T min(T a, T b) + { + return a < b ? a : b; + } + + template + inline constexpr T max(T a, T b) + { + return a > b ? a : b; + } + + template + inline constexpr T clamp(T x, T min, T max) + { + return x < min ? min : x > max ? max : x; + } + + template + inline constexpr T gcd(T a, T b) + { + T t; + while (b) + { + t = b; + b = a % b; + a = t; + } + return a; + } + + template + inline constexpr T lcm(T a, T b) + { + return a / gcd(a, b) * b; + } + + template + inline constexpr T div_round_up(T a, T b) + { + return (a + b - 1) / b; + } + + template + inline constexpr bool is_power_of_two(T x) + { + if (x == 0) + return false; + return (x & (x - 1)) == 0; + } + + template + 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::max() / b; + else + return a < BAN::numeric_limits::min() / b; + } + + template + static constexpr bool will_addition_overflow(T a, T b) + { + if (a > 0 && b > 0) + return a > BAN::numeric_limits::max() - b; + if (a < 0 && b < 0) + return a < BAN::numeric_limits::min() - b; + return false; + } + + template + requires is_same_v || is_same_v || is_same_v + inline constexpr T ilog2(T x) + { + if constexpr(is_same_v) + return sizeof(T) * 8 - __builtin_clz(x) - 1; + if constexpr(is_same_v) + return sizeof(T) * 8 - __builtin_clzl(x) - 1; + return sizeof(T) * 8 - __builtin_clzll(x) - 1; + } + + template + inline constexpr T floor(T x) + { + if constexpr(is_same_v) + return __builtin_floorf(x); + if constexpr(is_same_v) + return __builtin_floor(x); + if constexpr(is_same_v) + return __builtin_floorl(x); + } + + template + inline constexpr T ceil(T x) + { + if constexpr(is_same_v) + return __builtin_ceilf(x); + if constexpr(is_same_v) + return __builtin_ceil(x); + if constexpr(is_same_v) + return __builtin_ceill(x); + } + + template + inline constexpr T round(T x) + { + if (x == (T)0.0) + return x; + if (x > (T)0.0) + return floor(x + (T)0.5); + return ceil(x - (T)0.5); + } + + template + inline constexpr T trunc(T x) + { + if constexpr(is_same_v) + return __builtin_truncf(x); + if constexpr(is_same_v) + return __builtin_trunc(x); + if constexpr(is_same_v) + return __builtin_truncl(x); + } + + template + inline constexpr T rint(T x) + { + asm("frndint" : "+t"(x)); + return x; + } + + template + 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 + static T modf(T x, T* iptr) + { + const T frac = BAN::Math::fmod(x, 1); + *iptr = x - frac; + return frac; + } + + template + 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 + inline constexpr T copysign(T x, T y) + { + if ((x < (T)0.0) != (y < (T)0.0)) + x = -x; + return x; + } + + namespace detail + { + + template + inline constexpr T fyl2x(T x, T y) + { + asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)"); + return x; + } + + } + + template + inline constexpr T log(T x) + { + return detail::fyl2x(x, numbers::ln2_v); + } + + template + inline constexpr T log2(T x) + { + return detail::fyl2x(x, 1.0); + } + + template + inline constexpr T log10(T x) + { + return detail::fyl2x(x, numbers::lg2_v); + } + + template + inline constexpr T logb(T x) + { + static_assert(FLT_RADIX == 2); + return log2(x); + } + + template + 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 + inline constexpr T exp(T x) + { + return exp2(x * numbers::log2e_v); + } + + template + 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 + inline constexpr T scalbn(T x, int n) + { + asm("fscale" : "+t"(x) : "u"(static_cast(n))); + return x; + } + + template + 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 + inline constexpr T sqrt(T x) + { + asm("fsqrt" : "+t"(x)); + return x; + } + + template + inline constexpr T cbrt(T value) + { + if (value == 0.0) + return 0.0; + return pow(value, 1.0 / 3.0); + } + + template + inline constexpr T sin(T x) + { + asm("fsin" : "+t"(x)); + return x; + } + + template + inline constexpr T cos(T x) + { + asm("fcos" : "+t"(x)); + return x; + } + + template + inline constexpr void sincos(T x, T& sin, T& cos) + { + asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x)); + } + + template + inline constexpr T tan(T x) + { + T one, ret; + asm( + "fptan" + : "=t"(one), "=u"(ret) + : "0"(x) + ); + return ret; + } + + template + inline constexpr T atan2(T y, T x) + { + asm( + "fpatan" + : "+t"(x) + : "u"(y) + : "st(1)" + ); + return x; + } + + template + inline constexpr T atan(T x) + { + return atan2(x, 1.0); + } + + template + 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)2.0; + if (x == (T)-1.0) + return -numbers::pi_v / (T)2.0; + return (T)2.0 * atan(x / (T(1.0) + sqrt((T)1.0 - x * x))); + } + + template + inline constexpr T acos(T x) + { + if (x == (T)0.0) + return numbers::pi_v / (T)2.0; + if (x == (T)1.0) + return (T)0.0; + if (x == (T)-1.0) + return numbers::pi_v; + return (T)2.0 * atan(sqrt((T)1.0 - x * x) / ((T)1.0 + x)); + } + + template + inline constexpr T sinh(T x) + { + return (exp(x) - exp(-x)) / (T)2.0; + } + + template + inline constexpr T cosh(T x) + { + return (exp(x) + exp(-x)) / (T)2.0; + } + + template + inline constexpr T tanh(T x) + { + const T exp_px = exp(x); + const T exp_nx = exp(-x); + return (exp_px - exp_nx) / (exp_px + exp_nx); + } + + template + inline constexpr T asinh(T x) + { + return log(x + sqrt(x * x + (T)1.0)); + } + + template + inline constexpr T acosh(T x) + { + return log(x + sqrt(x * x - (T)1.0)); + } + + template + inline constexpr T atanh(T x) + { + return (T)0.5 * log(((T)1.0 + x) / ((T)1.0 - x)); + } + + template + inline constexpr T hypot(T x, T y) + { + return sqrt(x * x + y * y); + } + +} diff --git a/BAN/include/BAN/Move.h b/BAN/include/BAN/Move.h new file mode 100644 index 0000000..8652b2b --- /dev/null +++ b/BAN/include/BAN/Move.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +namespace BAN +{ + + template + constexpr remove_reference_t&& move(T&& arg) + { + return static_cast&&>(arg); + } + + template + constexpr T&& forward(remove_reference_t& arg) + { + return static_cast(arg); + } + + template + constexpr T&& forward(remove_reference_t&& arg) + { + static_assert(!is_lvalue_reference_v); + return static_cast(arg); + } + +} diff --git a/BAN/include/BAN/New.h b/BAN/include/BAN/New.h new file mode 100644 index 0000000..3602fb3 --- /dev/null +++ b/BAN/include/BAN/New.h @@ -0,0 +1,18 @@ +#pragma once + +#if defined(__is_kernel) + #include +#else + #include +#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 +} diff --git a/BAN/include/BAN/NoCopyMove.h b/BAN/include/BAN/NoCopyMove.h new file mode 100644 index 0000000..a60c56e --- /dev/null +++ b/BAN/include/BAN/NoCopyMove.h @@ -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 diff --git a/BAN/include/BAN/Numbers.h b/BAN/include/BAN/Numbers.h new file mode 100644 index 0000000..9389e54 --- /dev/null +++ b/BAN/include/BAN/Numbers.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace BAN::numbers +{ + + template inline constexpr T e_v = 2.71828182845904523536; + template inline constexpr T log2e_v = 1.44269504088896340736; + template inline constexpr T lge_v = 0.43429448190325182765; + template inline constexpr T lg2_v = 0.30102999566398119521; + template inline constexpr T ln2_v = 0.69314718055994530942; + template inline constexpr T ln10_v = 2.30258509299404568402; + template inline constexpr T pi_v = 3.14159265358979323846; + template inline constexpr T sqrt2_v = 1.41421356237309504880; + template inline constexpr T sqrt3_v = 1.73205080756887729353; + + inline constexpr double e = e_v; + inline constexpr double log2e = log2e_v; + inline constexpr double lge = lge_v; + inline constexpr double lg2 = lge_v; + inline constexpr double ln2 = ln2_v; + inline constexpr double ln10 = ln10_v; + inline constexpr double pi = pi_v; + inline constexpr double sqrt2 = sqrt2_v; + inline constexpr double sqrt3 = sqrt3_v; + +} diff --git a/BAN/include/BAN/Optional.h b/BAN/include/BAN/Optional.h new file mode 100644 index 0000000..c593247 --- /dev/null +++ b/BAN/include/BAN/Optional.h @@ -0,0 +1,204 @@ +#pragma once + +#include +#include +#include + +#include + +namespace BAN +{ + + template + 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 + constexpr Optional& emplace(Args&&...) requires is_constructible_v; + + 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 + constexpr Optional::Optional() + : m_has_value(false) + {} + + template + constexpr Optional::Optional(Optional&& other) + : m_has_value(other.has_value()) + { + if (other.has_value()) + new (m_storage) T(move(other.release_value())); + } + + template + constexpr Optional::Optional(const Optional& other) + : m_has_value(other.has_value()) + { + if (other.has_value()) + new (m_storage) T(other.value()); + } + + template + constexpr Optional::Optional(const T& value) + : m_has_value(true) + { + new (m_storage) T(value); + } + + template + constexpr Optional::Optional(T&& value) + : m_has_value(true) + { + new (m_storage) T(move(value)); + } + + template + Optional::~Optional() + { + clear(); + } + + template + constexpr Optional& Optional::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 + constexpr Optional& Optional::operator=(const Optional& other) + { + clear(); + m_has_value = other.has_value(); + if (other.has_value()) + new (m_storage) T(other.value()); + return *this; + } + + template + template + constexpr Optional& Optional::emplace(Args&&... args) requires is_constructible_v + { + clear(); + m_has_value = true; + new (m_storage) T(forward(args)...); + return *this; + } + + template + constexpr T* Optional::operator->() + { + ASSERT(has_value()); + return &value(); + } + + template + constexpr const T* Optional::operator->() const + { + ASSERT(has_value()); + return &value(); + } + + template + constexpr T& Optional::operator*() + { + ASSERT(has_value()); + return value(); + } + + template + constexpr const T& Optional::operator*() const + { + ASSERT(has_value()); + return value(); + } + + template + constexpr bool Optional::has_value() const + { + return m_has_value; + } + + template + constexpr T Optional::release_value() + { + ASSERT(has_value()); + T released_value = move(value()); + value().~T(); + m_has_value = false; + return move(released_value); + } + + template + constexpr T& Optional::value() + { + ASSERT(has_value()); + return *reinterpret_cast(&m_storage); + } + + template + constexpr const T& Optional::value() const + { + ASSERT(has_value()); + return *reinterpret_cast(&m_storage); + } + + template + constexpr T& Optional::value_or(T& empty) + { + if (!has_value()) + return empty; + return value(); + } + + template + constexpr const T& Optional::value_or(const T& empty) const + { + if (!has_value()) + return empty; + return value(); + } + + template + constexpr void Optional::clear() + { + if (m_has_value) + value().~T(); + m_has_value = false; + } + +} diff --git a/BAN/include/BAN/PlacementNew.h b/BAN/include/BAN/PlacementNew.h new file mode 100644 index 0000000..828a8e5 --- /dev/null +++ b/BAN/include/BAN/PlacementNew.h @@ -0,0 +1,10 @@ +#pragma once + +#if __has_include() + #include +#else + #include + + inline void* operator new(size_t, void* addr) { return addr; } + inline void* operator new[](size_t, void* addr) { return addr; } +#endif diff --git a/BAN/include/BAN/PriorityQueue.h b/BAN/include/BAN/PriorityQueue.h new file mode 100644 index 0000000..bee03eb --- /dev/null +++ b/BAN/include/BAN/PriorityQueue.h @@ -0,0 +1,64 @@ +#pragma once + +#include "BAN/Errors.h" +#include +#include + +namespace BAN +{ + + template> + class PriorityQueue + { + public: + PriorityQueue() = default; + PriorityQueue(Comp comp) + : m_comp(comp) + { } + + ErrorOr push(const T& value) + { + TRY(m_data.push_back(value)); + push_heap(m_data.begin(), m_data.end()); + return {}; + } + + ErrorOr push(T&& value) + { + TRY(m_data.push_back(move(value))); + push_heap(m_data.begin(), m_data.end()); + return {}; + } + + template + ErrorOr emplace(Args&&... args) requires is_constructible_v + { + TRY(m_data.emplace_back(forward(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 reserve(Vector::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::size_type size() const { return m_data.size(); } + Vector::size_type capacity() const { return m_data.capacity(); } + + private: + Comp m_comp; + Vector m_data; + }; + +} diff --git a/BAN/include/BAN/Queue.h b/BAN/include/BAN/Queue.h new file mode 100644 index 0000000..88d472e --- /dev/null +++ b/BAN/include/BAN/Queue.h @@ -0,0 +1,236 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace BAN +{ + + template + class Queue + { + public: + using size_type = size_t; + using value_type = T; + using iterator = IteratorSimple; + using const_iterator = ConstIteratorSimple; + + public: + Queue() = default; + Queue(Queue&&); + Queue(const Queue&); + ~Queue(); + + Queue& operator=(Queue&&); + Queue& operator=(const Queue&); + + ErrorOr push(T&&); + ErrorOr push(const T&); + template + ErrorOr emplace(Args&&...) requires is_constructible_v; + + ErrorOr reserve(size_type); + ErrorOr 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 ensure_capacity(size_type size); + + private: + T* m_data = nullptr; + size_type m_capacity = 0; + size_type m_size = 0; + }; + + template + Queue::Queue(Queue&& 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 + Queue::Queue(const Queue& 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 + Queue::~Queue() + { + clear(); + } + + template + Queue& Queue::operator=(Queue&& 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 + Queue& Queue::operator=(const Queue& 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 + ErrorOr Queue::push(T&& value) + { + TRY(ensure_capacity(m_size + 1)); + new (m_data + m_size) T(move(value)); + m_size++; + return {}; + } + + template + ErrorOr Queue::push(const T& value) + { + return push(move(T(value))); + } + + template + template + ErrorOr Queue::emplace(Args&&... args) requires is_constructible_v + { + TRY(ensure_capacity(m_size + 1)); + new (m_data + m_size) T(forward(args)...); + m_size++; + return {}; + } + + template + ErrorOr Queue::reserve(size_type size) + { + TRY(ensure_capacity(size)); + return {}; + } + + template + ErrorOr Queue::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 + void Queue::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 + void Queue::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 + bool Queue::empty() const + { + return m_size == 0; + } + + template + typename Queue::size_type Queue::capacity() const + { + return m_capacity; + } + + template + typename Queue::size_type Queue::size() const + { + return m_size; + } + + template + const T& Queue::front() const + { + ASSERT(m_size > 0); + return m_data[0]; + } + + template + T& Queue::front() + { + ASSERT(m_size > 0); + return m_data[0]; + } + + template + ErrorOr Queue::ensure_capacity(size_type size) + { + if (m_capacity > size) + return {}; + size_type new_cap = BAN::Math::max(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 {}; + } + +} diff --git a/BAN/include/BAN/RefPtr.h b/BAN/include/BAN/RefPtr.h new file mode 100644 index 0000000..9f83ceb --- /dev/null +++ b/BAN/include/BAN/RefPtr.h @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace BAN +{ + + template + 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(this); + } + + protected: + RefCounted() = default; + virtual ~RefCounted() { ASSERT(m_ref_count == 0); } + + private: + mutable Atomic m_ref_count = 1; + }; + + template + class RefPtr + { + public: + RefPtr() = default; + RefPtr(T* pointer) + { + m_pointer = pointer; + if (m_pointer) + m_pointer->ref(); + } + ~RefPtr() { clear(); } + + template + static RefPtr adopt(U* pointer) + { + RefPtr ptr; + ptr.m_pointer = pointer; + return ptr; + } + + // NOTE: don't use is_constructible_v as RefPtr is allowed with friends + template + static ErrorOr create(Args&&... args) requires requires(Args&&... args) { T(forward(args)...); } + { + T* pointer = new T(forward(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 + RefPtr(const RefPtr& other) { *this = other; } + template + RefPtr(RefPtr&& 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 + RefPtr& operator=(const RefPtr& other) + { + clear(); + m_pointer = other.m_pointer; + if (m_pointer) + m_pointer->ref(); + return *this; + } + + template + RefPtr& operator=(RefPtr&& 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 + friend class RefPtr; + }; + +} diff --git a/BAN/include/BAN/ScopeGuard.h b/BAN/include/BAN/ScopeGuard.h new file mode 100644 index 0000000..d514d0e --- /dev/null +++ b/BAN/include/BAN/ScopeGuard.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace BAN +{ + + class ScopeGuard + { + public: + ScopeGuard(const BAN::Function& func) + : m_func(func) + { } + ~ScopeGuard() + { + if (m_enabled) + m_func(); + } + void disable() + { + m_enabled = false; + } + private: + BAN::Function m_func; + bool m_enabled { true }; + }; + +} diff --git a/BAN/include/BAN/Sort.h b/BAN/include/BAN/Sort.h new file mode 100644 index 0000000..4f3a3d7 --- /dev/null +++ b/BAN/include/BAN/Sort.h @@ -0,0 +1,165 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace BAN::sort +{ + + template>> + 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 + 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>> + 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>> + 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>> + void heap_sort(It begin, It end, Comp comp = {}) + { + make_heap(begin, end, comp); + sort_heap(begin, end, comp); + } + + namespace detail + { + + template + 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>> + 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 + consteval T lsb_index(T value) + { + for (T result = 0;; result++) + if (value & (1 << result)) + return result; + } + + } + + template + requires is_unsigned_v> && (radix > 0 && (radix & (radix - 1)) == 0) + BAN::ErrorOr radix_sort(It begin, It end) + { + using value_type = it_value_type_t; + + const size_t len = distance(begin, end); + if (len <= 1) + return {}; + + Vector temp; + TRY(temp.resize(len)); + + Vector 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>> + void sort(It begin, It end, Comp comp = {}) + { + return intro_sort(begin, end, comp); + } + +} diff --git a/BAN/include/BAN/Span.h b/BAN/include/BAN/Span.h new file mode 100644 index 0000000..eac985e --- /dev/null +++ b/BAN/include/BAN/Span.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include + +#include + +namespace BAN +{ + + template + class Span + { + public: + using value_type = T; + using size_type = size_t; + using iterator = IteratorSimple; + using const_iterator = ConstIteratorSimple; + + private: + template + static inline constexpr bool can_init_from_v = is_same_v || is_same_v; + + public: + Span() = default; + Span(value_type* data, size_type size) + : m_data(data) + , m_size(size) + { } + + template + Span(const Span& other) requires can_init_from_v + : m_data(other.m_data) + , m_size(other.m_size) + { } + template + Span(Span&& other) requires can_init_from_v + : m_data(other.m_data) + , m_size(other.m_size) + { + other.clear(); + } + + template + Span& operator=(const Span& other) requires can_init_from_v + { + m_data = other.m_data; + m_size = other.m_size; + return *this; + } + template + Span& operator=(Span&& other) requires can_init_from_v + { + 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 as_const() const { return *this; } + + private: + value_type* m_data = nullptr; + size_type m_size = 0; + + friend class Span; + }; + +} diff --git a/BAN/include/BAN/String.h b/BAN/include/BAN/String.h new file mode 100644 index 0000000..df42d24 --- /dev/null +++ b/BAN/include/BAN/String.h @@ -0,0 +1,361 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace BAN +{ + + class String + { + public: + using size_type = size_t; + using iterator = IteratorSimple; + using const_iterator = ConstIteratorSimple; + 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 + static BAN::ErrorOr formatted(const char* format, Args&&... args) + { + size_type length = 0; + BAN::Formatter::print([&](char) { length++; }, format, BAN::forward(args)...); + + String result; + TRY(result.reserve(length)); + BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward(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 push_back(char c) + { + TRY(ensure_capacity(m_size + 1)); + data()[m_size] = c; + m_size++; + data()[m_size] = '\0'; + return {}; + } + + ErrorOr 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 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 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 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 reserve(size_type new_size) + { + TRY(ensure_capacity(new_size)); + return {}; + } + + ErrorOr 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 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 + { + 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 + void print_argument(F putc, const String& string, const ValueFormat&) + { + for (String::size_type i = 0; i < string.size(); i++) + putc(string[i]); + } + +} diff --git a/BAN/include/BAN/StringView.h b/BAN/include/BAN/StringView.h new file mode 100644 index 0000000..32b1a21 --- /dev/null +++ b/BAN/include/BAN/StringView.h @@ -0,0 +1,255 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace BAN +{ + + class StringView + { + public: + using size_type = size_t; + using const_iterator = ConstIteratorSimple; + + 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> 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 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> 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 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 find(char ch) const + { + for (size_type i = 0; i < m_size; i++) + if (m_data[i] == ch) + return i; + return {}; + } + + BAN::Optional find(bool(*comp)(char)) const + { + for (size_type i = 0; i < m_size; i++) + if (comp(m_data[i])) + return i; + return {}; + } + + BAN::Optional 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 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 + { + 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 + void print_argument(F putc, const StringView& sv, const ValueFormat&) + { + for (StringView::size_type i = 0; i < sv.size(); i++) + putc(sv[i]); + } + +} diff --git a/BAN/include/BAN/Swap.h b/BAN/include/BAN/Swap.h new file mode 100644 index 0000000..8952eb0 --- /dev/null +++ b/BAN/include/BAN/Swap.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace BAN +{ + + template + void swap(T& lhs, T& rhs) + { + T tmp = move(lhs); + lhs = move(rhs); + rhs = move(tmp); + } + +} diff --git a/BAN/include/BAN/Time.h b/BAN/include/BAN/Time.h new file mode 100644 index 0000000..b3c420c --- /dev/null +++ b/BAN/include/BAN/Time.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +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 + 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); + } + +} diff --git a/BAN/include/BAN/Traits.h b/BAN/include/BAN/Traits.h new file mode 100644 index 0000000..ce755f8 --- /dev/null +++ b/BAN/include/BAN/Traits.h @@ -0,0 +1,150 @@ +#pragma once + +namespace BAN +{ + + template struct remove_reference { using type = T; }; + template struct remove_reference { using type = T; }; + template struct remove_reference { using type = T; }; + template using remove_reference_t = typename remove_reference::type; + + template struct remove_const { using type = T; }; + template struct remove_const { using type = T; }; + template using remove_const_t = typename remove_const::type; + + template struct remove_volatile { using type = T; }; + template struct remove_volatile { using type = T; }; + template using remove_volatile_t = typename remove_volatile::type; + + template struct remove_cv { using type = remove_volatile_t>; }; + template using remove_cv_t = typename remove_cv::type; + + template struct remove_const_and_reference { using type = remove_const_t>; }; + template using remove_const_and_reference_t = typename remove_const_and_reference::type; + + template struct enable_if {}; + template struct enable_if { using type = T; }; + template using enable_if_t = typename enable_if::type; + + template struct maybe_const { using type = T; }; + template struct maybe_const { using type = const T; }; + template using maybe_const_t = typename maybe_const::type; + + template struct either_or { using type = T2; }; + template struct either_or { using type = T1; }; + template using either_or_t = typename either_or::type; + + template struct integral_constant { static constexpr T value = V; }; + template inline constexpr T integral_constant_v = integral_constant::value; + using true_type = integral_constant; + using false_type = integral_constant; + + template struct is_same : false_type {}; + template struct is_same : true_type {}; + template inline constexpr bool is_same_v = is_same::value; + template concept same_as = BAN::is_same_v; + + template struct is_lvalue_reference : false_type {}; + template struct is_lvalue_reference : true_type {}; + template inline constexpr bool is_lvalue_reference_v = is_lvalue_reference::value; + template concept lvalue_reference = is_lvalue_reference_v; + + template struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); }; + template inline constexpr bool is_constructible_v = is_constructible::value; + + template struct is_default_constructible { static constexpr bool value = is_constructible_v; }; + template inline constexpr bool is_default_constructible_v = is_default_constructible::value; + + template struct is_copy_constructible { static constexpr bool value = is_constructible_v; }; + template inline constexpr bool is_copy_constructible_v = is_copy_constructible::value; + + template struct is_move_constructible { static constexpr bool value = is_constructible_v; }; + template inline constexpr bool is_move_constructible_v = is_move_constructible::value; + + template struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast(t); f(0); p + t; }; }; + template inline constexpr bool is_integral_v = is_integral::value; + template concept integral = is_integral_v; + + template struct is_floating_point : false_type {}; + template<> struct is_floating_point : true_type {}; + template<> struct is_floating_point : true_type {}; + template<> struct is_floating_point : true_type {}; + template inline constexpr bool is_floating_point_v = is_floating_point::value; + template concept floating_point = is_floating_point_v; + + template struct is_pointer : false_type {}; + template struct is_pointer : true_type {}; + template struct is_pointer : true_type {}; + template struct is_pointer : true_type {}; + template struct is_pointer : true_type {}; + template inline constexpr bool is_pointer_v = is_pointer::value; + template concept pointer = is_pointer_v; + + template struct is_const : false_type {}; + template struct is_const : true_type {}; + template inline constexpr bool is_const_v = is_const::value; + + template struct is_arithmetic { static constexpr bool value = is_integral_v || is_floating_point_v; }; + template inline constexpr bool is_arithmetic_v = is_arithmetic::value; + + template struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); }; + template inline constexpr bool is_base_of_v = is_base_of::value; + + template struct is_pod { static constexpr bool value = __is_pod(T); }; + template inline constexpr bool is_pod_v = is_pod::value; + + namespace detail + { + template> struct is_signed { static constexpr bool value = T(-1) < T(0); }; + template struct is_signed : false_type {}; + + template> struct is_unsigned { static constexpr bool value = T(0) < T(-1); }; + template struct is_unsigned : false_type {}; + } + template struct is_signed : detail::is_signed {}; + template inline constexpr bool is_signed_v = is_signed::value; + template concept signed_integral = is_signed_v && is_integral_v; + + template struct is_unsigned : detail::is_unsigned {}; + template inline constexpr bool is_unsigned_v = is_unsigned::value; + template concept unsigned_integral = is_unsigned_v && is_integral_v; + +#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \ + template<> struct make_unsigned<__type> { using type = unsigned __type; }; \ + template<> struct make_unsigned { using type = unsigned const __type; }; \ + template<> struct make_unsigned { using type = unsigned volatile __type; }; \ + template<> struct make_unsigned { using type = unsigned const volatile __type; }; + + template requires is_arithmetic_v 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 using make_unsigned_t = typename make_unsigned::type; +#undef __BAN_TRAITS_MAKE_UNSIGNED_CV + +#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \ + template<> struct make_signed { using type = __type; }; \ + template<> struct make_signed { using type = const __type; }; \ + template<> struct make_signed { using type = volatile __type; }; \ + template<> struct make_signed { using type = const volatile __type; }; + + template requires is_arithmetic_v 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 using make_signed_t = typename make_signed::type; +#undef __BAN_TRAITS_MAKE_SIGNED_CV + + template struct it_value_type { using value_type = T::value_type; }; + template struct it_value_type { using value_type = T; }; + template using it_value_type_t = typename it_value_type::value_type; + + template struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; + template struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; + template struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } }; + +} diff --git a/BAN/include/BAN/UTF8.h b/BAN/include/BAN/UTF8.h new file mode 100644 index 0000000..1976172 --- /dev/null +++ b/BAN/include/BAN/UTF8.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +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 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 + 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; + } + +} diff --git a/BAN/include/BAN/UniqPtr.h b/BAN/include/BAN/UniqPtr.h new file mode 100644 index 0000000..7d21467 --- /dev/null +++ b/BAN/include/BAN/UniqPtr.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +namespace BAN +{ + + template + class UniqPtr + { + BAN_NON_COPYABLE(UniqPtr); + + public: + UniqPtr() = default; + + template + UniqPtr(UniqPtr&& 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 as UniqPtr is allowed with friends + template + static BAN::ErrorOr create(Args&&... args) requires requires(Args&&... args) { T(forward(args)...); } + { + UniqPtr uniq; + uniq.m_pointer = new T(BAN::forward(args)...); + if (uniq.m_pointer == nullptr) + return BAN::Error::from_errno(ENOMEM); + return uniq; + } + + template + UniqPtr& operator=(UniqPtr&& 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 + friend class UniqPtr; + }; + +} diff --git a/BAN/include/BAN/Variant.h b/BAN/include/BAN/Variant.h new file mode 100644 index 0000000..0ce615c --- /dev/null +++ b/BAN/include/BAN/Variant.h @@ -0,0 +1,317 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace BAN +{ + + namespace detail + { + + template + constexpr size_t size_ref_as_ptr() { return is_lvalue_reference_v ? sizeof(remove_reference_t*) : sizeof(T); } + template + constexpr size_t align_ref_as_ptr() { return is_lvalue_reference_v ? alignof(remove_reference_t*) : alignof(T); } + + template + constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr(); } + template + constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr() > size_ref_as_ptr() ? max_size_ref_as_ptr() : max_size_ref_as_ptr(); } + + template + constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr(); } + template + constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr() > align_ref_as_ptr() ? max_align_ref_as_ptr() : max_align_ref_as_ptr(); } + + template + constexpr size_t index() + { + if constexpr(is_same_v) + return 0; + else if constexpr(sizeof...(Ts) == 0) + return 1; + else + return index() + 1; + } + + template + void destruct(size_t index, uint8_t* data) + { + if (index == 0) + { + if constexpr(!is_lvalue_reference_v) + reinterpret_cast(data)->~T(); + } + else if constexpr(sizeof...(Ts) > 0) + destruct(index - 1, data); + else + ASSERT_NOT_REACHED(); + } + + template + void move_construct(size_t index, uint8_t* source, uint8_t* target) + { + if (index == 0) + if constexpr(!is_lvalue_reference_v) + new (target) T(move(*reinterpret_cast(source))); + else + memcpy(target, source, sizeof(remove_reference_t*)); + else if constexpr(sizeof...(Ts) > 0) + move_construct(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void copy_construct(size_t index, const uint8_t* source, uint8_t* target) + { + if (index == 0) + if constexpr(!is_lvalue_reference_v) + new (target) T(*reinterpret_cast(source)); + else + memcpy(target, source, sizeof(remove_reference_t*)); + else if constexpr(sizeof...(Ts) > 0) + copy_construct(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void move_assign(size_t index, uint8_t* source, uint8_t* target) + { + if (index == 0) + if constexpr(!is_lvalue_reference_v) + *reinterpret_cast(target) = move(*reinterpret_cast(source)); + else + memcpy(target, source, sizeof(remove_reference_t*)); + else if constexpr(sizeof...(Ts) > 0) + move_assign(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void copy_assign(size_t index, const uint8_t* source, uint8_t* target) + { + if (index == 0) + if constexpr(!is_lvalue_reference_v) + *reinterpret_cast(target) = *reinterpret_cast(source); + else + memcpy(target, source, sizeof(remove_reference_t*)); + else if constexpr(sizeof...(Ts) > 0) + copy_assign(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + } + + template + requires (!is_const_v && ...) + class Variant + { + private: + template + static constexpr bool can_have() { return detail::index() != 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(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(other.m_index, other.m_storage, m_storage); + } + + template + Variant(T&& value) requires (can_have() && !is_lvalue_reference_v) + : m_index(detail::index()) + { + new (m_storage) T(move(value)); + } + + template + Variant(const T& value) requires (can_have() && !is_lvalue_reference_v) + : m_index(detail::index()) + { + new (m_storage) T(value); + } + + ~Variant() + { + clear(); + } + + Variant& operator=(Variant&& other) + { + if (m_index == other.m_index) + detail::move_assign(m_index, other.m_storage, m_storage); + else + { + clear(); + detail::move_construct(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(m_index, other.m_storage, m_storage); + else + { + clear(); + detail::copy_construct(other.m_index, other.m_storage, m_storage); + m_index = other.m_index; + } + return *this; + } + + template + Variant& operator=(T&& value) requires (can_have() && !is_lvalue_reference_v) + { + if (size_t index = detail::index(); index == m_index) + get() = move(value); + else + { + clear(); + new (m_storage) T(move(value)); + m_index = index; + } + return *this; + } + + template + Variant& operator=(const T& value) requires (can_have() && !is_lvalue_reference_v) + { + if (size_t index = detail::index(); index == m_index) + get() = value; + else + { + clear(); + new (m_storage) T(value); + m_index = index; + } + return *this; + } + + template + bool has() const requires (can_have()) + { + return m_index == detail::index(); + } + + template + void emplace(Args&&... args) requires (can_have() && is_constructible_v) + { + clear(); + m_index = detail::index(); + new (m_storage) T(BAN::forward(args)...); + } + + template + void set(T&& value) requires (can_have() && !is_lvalue_reference_v) + { + if (has()) + get() = move(value); + else + { + clear(); + m_index = detail::index(); + new (m_storage) T(move(value)); + } + } + + template + void set(const T& value) requires (can_have() && !is_lvalue_reference_v) + { + if (has()) + get() = value; + else + { + clear(); + m_index = detail::index(); + new (m_storage) T(value); + } + } + + template + void set(T value) requires (can_have() && is_lvalue_reference_v) + { + clear(); + m_index = detail::index(); + *reinterpret_cast**>(m_storage) = &value; + } + + template + T& get() requires (can_have() && !is_lvalue_reference_v) + { + ASSERT(has()); + return *reinterpret_cast(m_storage); + } + + template + const T& get() const requires (can_have() && !is_lvalue_reference_v) + { + ASSERT(has()); + return *reinterpret_cast(m_storage); + } + + template + T get() requires (can_have() && is_lvalue_reference_v) + { + ASSERT(has()); + return **reinterpret_cast**>(m_storage); + } + + template + const T get() const requires (can_have() && is_lvalue_reference_v) + { + ASSERT(has()); + return **reinterpret_cast**>(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(m_index, m_storage); + m_index = invalid_index(); + } + } + + private: + alignas(detail::max_align_ref_as_ptr()) uint8_t m_storage[detail::max_size_ref_as_ptr()] {}; + size_t m_index { invalid_index() }; + }; + +} diff --git a/BAN/include/BAN/Vector.h b/BAN/include/BAN/Vector.h new file mode 100644 index 0000000..7e90c0f --- /dev/null +++ b/BAN/include/BAN/Vector.h @@ -0,0 +1,418 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace BAN +{ + + // T must be move assignable, move constructable (and copy constructable for some functions) + template + class Vector + { + public: + using size_type = size_t; + using value_type = T; + using iterator = IteratorSimple; + using const_iterator = ConstIteratorSimple; + + public: + Vector() = default; + Vector(Vector&&); + Vector(const Vector&); + Vector(size_type, const T& = T()); + ~Vector(); + + Vector& operator=(Vector&&); + Vector& operator=(const Vector&); + + ErrorOr push_back(T&&); + ErrorOr push_back(const T&); + template + ErrorOr emplace_back(Args&&...) requires is_constructible_v; + template + ErrorOr emplace(size_type, Args&&...) requires is_constructible_v; + ErrorOr insert(size_type, T&&); + ErrorOr 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 span() { return Span(m_data, m_size); } + Span 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 resize(size_type) requires is_default_constructible_v; + ErrorOr resize(size_type, const T&) requires is_copy_constructible_v; + ErrorOr reserve(size_type); + ErrorOr shrink_to_fit(); + + bool empty() const; + size_type size() const; + size_type capacity() const; + + private: + ErrorOr ensure_capacity(size_type); + + private: + T* m_data = nullptr; + size_type m_capacity = 0; + size_type m_size = 0; + }; + + template + Vector::Vector(Vector&& 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 + Vector::Vector(const Vector& 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 + Vector::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 + Vector::~Vector() + { + clear(); + } + + template + Vector& Vector::operator=(Vector&& 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 + Vector& Vector::operator=(const Vector& 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 + ErrorOr Vector::push_back(T&& value) + { + TRY(ensure_capacity(m_size + 1)); + new (m_data + m_size) T(move(value)); + m_size++; + return {}; + } + + template + ErrorOr Vector::push_back(const T& value) + { + return push_back(move(T(value))); + } + + template + template + ErrorOr Vector::emplace_back(Args&&... args) requires is_constructible_v + { + TRY(ensure_capacity(m_size + 1)); + new (m_data + m_size) T(forward(args)...); + m_size++; + return {}; + } + + template + template + ErrorOr Vector::emplace(size_type index, Args&&... args) requires is_constructible_v + { + 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)...)); + } + else + { + new (m_data + m_size) T(forward(args)...); + } + m_size++; + return {}; + } + + template + ErrorOr Vector::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 + ErrorOr Vector::insert(size_type index, const T& value) + { + return insert(index, move(T(value))); + } + + template + void Vector::pop_back() + { + ASSERT(m_size > 0); + m_data[m_size - 1].~T(); + m_size--; + } + + template + void Vector::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 + void Vector::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 + bool Vector::contains(const T& other) const + { + for (size_type i = 0; i < m_size; i++) + if (m_data[i] == other) + return true; + return false; + } + + template + const T& Vector::operator[](size_type index) const + { + ASSERT(index < m_size); + return m_data[index]; + } + + template + T& Vector::operator[](size_type index) + { + ASSERT(index < m_size); + return m_data[index]; + } + + template + const T& Vector::back() const + { + ASSERT(m_size > 0); + return m_data[m_size - 1]; + } + + template + T& Vector::back() + { + ASSERT(m_size > 0); + return m_data[m_size - 1]; + } + + template + const T& Vector::front() const + { + ASSERT(m_size > 0); + return m_data[0]; + } + + template + T& Vector::front() + { + ASSERT(m_size > 0); + return m_data[0]; + } + + template + void Vector::reverse() + { + for (size_type i = 0; i < m_size / 2; i++) + BAN::swap(m_data[i], m_data[m_size - i - 1]); + } + + template + ErrorOr Vector::resize(size_type size) requires is_default_constructible_v + { + 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 + ErrorOr Vector::resize(size_type size, const T& value) requires is_copy_constructible_v + { + 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 + ErrorOr Vector::reserve(size_type size) + { + TRY(ensure_capacity(size)); + return {}; + } + + template + ErrorOr Vector::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 + bool Vector::empty() const + { + return m_size == 0; + } + + template + typename Vector::size_type Vector::size() const + { + return m_size; + } + + template + typename Vector::size_type Vector::capacity() const + { + return m_capacity; + } + + template + ErrorOr Vector::ensure_capacity(size_type size) + { + if (m_capacity >= size) + return {}; + size_type new_cap = BAN::Math::max(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 + void print_argument(F putc, const Vector& vector, const ValueFormat& format) + { + putc('['); + for (typename Vector::size_type i = 0; i < vector.size(); i++) + { + if (i != 0) putc(','); + print_argument(putc, vector[i], format); + } + putc(']'); + } + +} diff --git a/BAN/include/BAN/WeakPtr.h b/BAN/include/BAN/WeakPtr.h new file mode 100644 index 0000000..b5d0041 --- /dev/null +++ b/BAN/include/BAN/WeakPtr.h @@ -0,0 +1,128 @@ +#pragma once + +#include + +#if __is_kernel +#include +#endif + +namespace BAN +{ + + template + class Weakable; + + template + class WeakPtr; + + // FIXME: Write this without using locks... + template + class WeakLink : public RefCounted> + { + public: + RefPtr try_lock() const + { +#if __is_kernel + Kernel::SpinLockGuard _(m_weak_lock); +#endif + if (m_ptr && m_ptr->try_ref()) + return RefPtr::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>; + }; + + template + class Weakable + { + public: + virtual ~Weakable() + { + if (m_link) + m_link->invalidate(); + } + + ErrorOr> get_weak_ptr() const + { + if (!m_link) + m_link = TRY(RefPtr>::create((T*)this)); + return WeakPtr(m_link); + } + + private: + mutable RefPtr> m_link; + }; + + template + class WeakPtr + { + public: + WeakPtr() = default; + WeakPtr(WeakPtr&& other) { *this = move(other); } + WeakPtr(const WeakPtr& other) { *this = other; } + WeakPtr(const RefPtr& 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& other) + { + clear(); + if (other) + m_link = MUST(other->get_weak_ptr()).move_link(); + return *this; + } + + RefPtr 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>& link) + : m_link(link) + { } + + RefPtr>&& move_link() { return move(m_link); } + + private: + RefPtr> m_link; + + friend class Weakable; + }; + +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..af4462f --- /dev/null +++ b/CMakeLists.txt @@ -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 $/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) diff --git a/LibClipboard/CMakeLists.txt b/LibClipboard/CMakeLists.txt new file mode 100644 index 0000000..69c1de4 --- /dev/null +++ b/LibClipboard/CMakeLists.txt @@ -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) diff --git a/LibClipboard/Clipboard.cpp b/LibClipboard/Clipboard.cpp new file mode 100644 index 0000000..b65377a --- /dev/null +++ b/LibClipboard/Clipboard.cpp @@ -0,0 +1,201 @@ +#include + +#include +#include +#include +#include + +namespace LibClipboard +{ + + static int s_server_fd = -1; + + static BAN::ErrorOr 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(control_buffer); + + *control = { + .cmsg_len = control_size, + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_CREDENTIALS, + }; + + *reinterpret_cast(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 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(&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 recv_sized(void* data, size_t size) + { + ASSERT(s_server_fd != -1); + + uint8_t* u8_data = static_cast(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 send_sized(const void* data, size_t size) + { + ASSERT(s_server_fd != -1); + + const uint8_t* u8_data = static_cast(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::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 Clipboard::set_clipboard(DataType type, BAN::Span 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 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 Clipboard::set_clipboard_text(BAN::StringView string) + { + return set_clipboard(DataType::Text, { reinterpret_cast(string.data()), string.size() }); + } + +} diff --git a/LibClipboard/include/LibClipboard/Clipboard.h b/LibClipboard/include/LibClipboard/Clipboard.h new file mode 100644 index 0000000..30c655e --- /dev/null +++ b/LibClipboard/include/LibClipboard/Clipboard.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +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 data; + }; + + public: + static BAN::ErrorOr get_clipboard(); + static BAN::ErrorOr set_clipboard(DataType type, BAN::Span data); + + static BAN::ErrorOr get_clipboard_text(); + static BAN::ErrorOr set_clipboard_text(BAN::StringView string); + }; + +} diff --git a/LibDEFLATE/CMakeLists.txt b/LibDEFLATE/CMakeLists.txt new file mode 100644 index 0000000..fe23970 --- /dev/null +++ b/LibDEFLATE/CMakeLists.txt @@ -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) diff --git a/LibDEFLATE/Compressor.cpp b/LibDEFLATE/Compressor.cpp new file mode 100644 index 0000000..4664d04 --- /dev/null +++ b/LibDEFLATE/Compressor.cpp @@ -0,0 +1,620 @@ +#include +#include + +#include +#include +#include +#include + +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 create_huffman_tree(BAN::Span freq, BAN::Span 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 nodes; + TRY(nodes.resize(s_max_symbols)); +#else + BAN::Array 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(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(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, uint16_t) = + [](const node_t* node, BAN::Span 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(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 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(257 + i), + .extra_data = static_cast(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(i), + .extra_data = static_cast(distance - base[i]), + .extra_len = extra_bits[i], + }; + } + } + + static void get_frequencies(BAN::Span entries, BAN::Span lit_len_freq, BAN::Span 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; + BAN::Array code_length; + BAN::Array code_length_tree; + }; + + static BAN::ErrorOr build_code_length_info(BAN::Span lit_len_tree, BAN::Span dist_tree) + { + CodeLengthInfo result; + + const auto append_tree = + [&result](BAN::Span& tree) -> BAN::ErrorOr + { + 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(count - 3), + .extra_len = 3, + })); + } + else + { + TRY(result.encoding.push_back({ + .symbol = 18, + .extra_data = static_cast(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(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 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 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::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(length), + .distance = static_cast(distance), + } + } + }; + } + } + + return result; + } + + BAN::ErrorOr> Compressor::lz77_compress(BAN::ConstByteSpan data) + { + BAN::Vector 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 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 lit_len_freq, dist_freq; + TRY(lit_len_freq.resize(286, 0)); + TRY(dist_freq.resize(30, 0)); +#else + BAN::Array lit_len_freq(0); + BAN::Array dist_freq(0); +#endif + + get_frequencies(lz77_entries.span(), lit_len_freq.span(), dist_freq.span()); + +#if LIBDEFLATE_AVOID_STACK + BAN::Vector lit_len_tree, dist_tree; + TRY(lit_len_tree.resize(286)); + TRY(dist_tree.resize(30)); +#else + BAN::Array lit_len_tree; + BAN::Array 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> 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(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(); + } + +} diff --git a/LibDEFLATE/Decompressor.cpp b/LibDEFLATE/Decompressor.cpp new file mode 100644 index 0000000..c5d757b --- /dev/null +++ b/LibDEFLATE/Decompressor.cpp @@ -0,0 +1,277 @@ +#include +#include + +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 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 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 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 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 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 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 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> 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); + } + +} diff --git a/LibDEFLATE/HuffmanTree.cpp b/LibDEFLATE/HuffmanTree.cpp new file mode 100644 index 0000000..17bcc25 --- /dev/null +++ b/LibDEFLATE/HuffmanTree.cpp @@ -0,0 +1,141 @@ +#include + +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::create(BAN::Span bit_lengths) + { + HuffmanTree result; + TRY(result.initialize(bit_lengths)); + return result; + } + + BAN::ErrorOr HuffmanTree::initialize(BAN::Span 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 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 HuffmanTree::build_instant_table(BAN::Span tree) + { + m_instant_bits = BAN::Math::min(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 HuffmanTree::build_slow_table(BAN::Span 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::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 values; + }; + static constexpr BitLengths bit_lengths; + return TRY(HuffmanTree::create(bit_lengths.values.span())); + } + + BAN::Optional 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 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]; + } + +} diff --git a/LibDEFLATE/include/LibDEFLATE/BitStream.h b/LibDEFLATE/include/LibDEFLATE/BitStream.h new file mode 100644 index 0000000..dbde3ca --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/BitStream.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +namespace LibDEFLATE +{ + + class BitInputStream + { + public: + BitInputStream(BAN::ConstByteSpan data) + : m_data(data) + { } + + BAN::ErrorOr 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 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 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 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 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 take_buffer() + { + ASSERT(m_bit_buffer_len == 0); + return BAN::move(m_data); + } + + private: + BAN::Vector m_data; + uint32_t m_bit_buffer { 0 }; + uint8_t m_bit_buffer_len { 0 }; + }; + +} diff --git a/LibDEFLATE/include/LibDEFLATE/Compressor.h b/LibDEFLATE/include/LibDEFLATE/Compressor.h new file mode 100644 index 0000000..fccc524 --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/Compressor.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace LibDEFLATE +{ + + class Compressor + { + BAN_NON_COPYABLE(Compressor); + BAN_NON_MOVABLE(Compressor); + + public: + using HashChain = BAN::LinkedList; + + 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> compress(); + + private: + BAN::ErrorOr compress_block(BAN::ConstByteSpan, bool final); + + uint32_t get_hash_key(BAN::ConstByteSpan needle) const; + BAN::ErrorOr update_hash_chain(size_t count); + + BAN::ErrorOr find_longest_match(BAN::ConstByteSpan needle) const; + BAN::ErrorOr> 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 m_hash_chain; + }; + +} diff --git a/LibDEFLATE/include/LibDEFLATE/Decompressor.h b/LibDEFLATE/include/LibDEFLATE/Decompressor.h new file mode 100644 index 0000000..f293a47 --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/Decompressor.h @@ -0,0 +1,46 @@ +#pragma once + + +#include +#include +#include + +#include +#include +#include + +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> decompress(); + + private: + BAN::ErrorOr read_symbol(const HuffmanTree& tree); + BAN::ErrorOr inflate_block(const HuffmanTree& length_tree, const HuffmanTree& distance_tree); + + BAN::ErrorOr decompress_type0(); + BAN::ErrorOr decompress_type1(); + BAN::ErrorOr decompress_type2(); + + BAN::ErrorOr handle_header(); + BAN::ErrorOr handle_footer(); + + private: + const StreamType m_type; + BitInputStream m_stream; + BAN::Vector m_output; + BAN::Optional m_fixed_tree; + }; + +} diff --git a/LibDEFLATE/include/LibDEFLATE/HuffmanTree.h b/LibDEFLATE/include/LibDEFLATE/HuffmanTree.h new file mode 100644 index 0000000..89fdb7e --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/HuffmanTree.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +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 create(BAN::Span bit_lengths); + + static BAN::ErrorOr fixed_tree(); + BAN::Optional get_symbol_instant(uint16_t code) const; + + BAN::Optional 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 initialize(BAN::Span bit_lengths); + BAN::ErrorOr build_instant_table(BAN::Span tree); + BAN::ErrorOr build_slow_table(BAN::Span tree); + + private: + uint8_t m_instant_bits { 0 }; + uint8_t m_min_bits { 0 }; + uint8_t m_max_bits { 0 }; + + BAN::Vector m_instant; + BAN::Array m_min_code; + BAN::Vector> m_slow_table; + }; + +} diff --git a/LibDEFLATE/include/LibDEFLATE/StreamType.h b/LibDEFLATE/include/LibDEFLATE/StreamType.h new file mode 100644 index 0000000..8ec35f2 --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/StreamType.h @@ -0,0 +1,12 @@ +#pragma once + +namespace LibDEFLATE +{ + + enum class StreamType + { + Raw, + Zlib, + }; + +} diff --git a/LibDEFLATE/include/LibDEFLATE/Utils.h b/LibDEFLATE/include/LibDEFLATE/Utils.h new file mode 100644 index 0000000..699550b --- /dev/null +++ b/LibDEFLATE/include/LibDEFLATE/Utils.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +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; + } + +} diff --git a/LibFont/CMakeLists.txt b/LibFont/CMakeLists.txt new file mode 100644 index 0000000..a2a9bd4 --- /dev/null +++ b/LibFont/CMakeLists.txt @@ -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) diff --git a/LibFont/Font.cpp b/LibFont/Font.cpp new file mode 100644 index 0000000..c21b6cb --- /dev/null +++ b/LibFont/Font.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include + +#include +#include +#include +#include + +namespace LibFont +{ + + BAN::ErrorOr Font::load(BAN::StringView path) + { + BAN::Vector 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::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); + } + +} diff --git a/LibFont/PSF.cpp b/LibFont/PSF.cpp new file mode 100644 index 0000000..242b587 --- /dev/null +++ b/LibFont/PSF.cpp @@ -0,0 +1,214 @@ +#include +#include +#include + +#include + +#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 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(); + + 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 glyph_data; + TRY(glyph_data.resize(glyph_data_size)); + memcpy(glyph_data.data(), font_data.data() + sizeof(PSF1Header), glyph_data_size); + + BAN::HashMap 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 parse_psf2(BAN::ConstByteSpan font_data) + { + struct PSF2Header + { + uint8_t magic[4]; + BAN::LittleEndian version; + BAN::LittleEndian header_size; + BAN::LittleEndian flags; + BAN::LittleEndian glyph_count; + BAN::LittleEndian glyph_size; + BAN::LittleEndian height; + BAN::LittleEndian width; + }; + + if (font_data.size() < sizeof(PSF2Header)) + return BAN::Error::from_errno(EINVAL); + const auto& header = font_data.as(); + + 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 glyph_data; + TRY(glyph_data.resize(glyph_data_size)); + memcpy(glyph_data.data(), font_data.data() + header.header_size, glyph_data_size); + + BAN::HashMap 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); + } + +} diff --git a/LibFont/TTF.cpp b/LibFont/TTF.cpp new file mode 100644 index 0000000..3a7ba41 --- /dev/null +++ b/LibFont/TTF.cpp @@ -0,0 +1,8 @@ +#include + +namespace LibFont +{ + + + +} diff --git a/LibFont/include/LibFont/Font.h b/LibFont/include/LibFont/Font.h new file mode 100644 index 0000000..e2ae91a --- /dev/null +++ b/LibFont/include/LibFont/Font.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +namespace LibFont +{ + + class Font + { + public: + Font() = default; + Font(BAN::HashMap&& glyph_offsets, BAN::Vector&& 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 load(BAN::StringView path); + static BAN::ErrorOr load(BAN::ConstByteSpan font_data); +#if __is_kernel + static BAN::ErrorOr 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 m_glyph_offsets; + BAN::Vector m_glyph_data; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_pitch = 0; + }; + +} diff --git a/LibFont/include/LibFont/PSF.h b/LibFont/include/LibFont/PSF.h new file mode 100644 index 0000000..84f8df8 --- /dev/null +++ b/LibFont/include/LibFont/PSF.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace LibFont +{ + + bool is_psf1(BAN::ConstByteSpan); + BAN::ErrorOr parse_psf1(BAN::ConstByteSpan); + + bool is_psf2(BAN::ConstByteSpan); + BAN::ErrorOr parse_psf2(BAN::ConstByteSpan); + +} diff --git a/LibGUI/CMakeLists.txt b/LibGUI/CMakeLists.txt new file mode 100644 index 0000000..da0bb0e --- /dev/null +++ b/LibGUI/CMakeLists.txt @@ -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) diff --git a/LibGUI/MessageBox.cpp b/LibGUI/MessageBox.cpp new file mode 100644 index 0000000..b5a4519 --- /dev/null +++ b/LibGUI/MessageBox.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +namespace LibGUI +{ + + BAN::ErrorOr MessageBox::create(BAN::StringView message, BAN::StringView title) + { + BAN::StringView ok_button = "OK"; + TRY(create(message, title, { &ok_button, 1 })); + return {}; + } + + BAN::ErrorOr MessageBox::create(BAN::StringView message, BAN::StringView title, BAN::Span 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; + } + +} diff --git a/LibGUI/Texture.cpp b/LibGUI/Texture.cpp new file mode 100644 index 0000000..7620b77 --- /dev/null +++ b/LibGUI/Texture.cpp @@ -0,0 +1,258 @@ +#include +#include + +namespace LibGUI +{ + + BAN::ErrorOr 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 pixels; + TRY(pixels.resize(width * height, color)); + return Texture(BAN::move(pixels), width, height, color); + } + + BAN::ErrorOr 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 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(amount, height() - dst_y); + if (amount <= 0) + return; + + const int32_t copy_src_y = BAN::Math::clamp(src_y, 0, height()); + const int32_t copy_amount = BAN::Math::clamp(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(signed_x, m_clip_x); + const int32_t min_y = BAN::Math::max(signed_y, m_clip_y); + const int32_t max_x = BAN::Math::min(signed_x + (int32_t)width, m_clip_x + m_clip_w); + const int32_t max_y = BAN::Math::min(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(m_clip_x + m_clip_w)) + return false; + if (dst_y >= static_cast(m_clip_y + m_clip_h)) + return false; + + if (src_x >= static_cast(texture.width())) + return false; + if (src_y >= static_cast(texture.height())) + return false; + + if (dst_x + static_cast(width) > static_cast(m_clip_x + m_clip_w)) + width = m_clip_x + m_clip_w - dst_x; + if (src_x + static_cast(width) > static_cast(texture.width())) + width = texture.width() - src_x; + + if (dst_y + static_cast(height) > static_cast(m_clip_y + m_clip_h)) + height = m_clip_y + m_clip_h - dst_y; + if (src_y + static_cast(height) > static_cast(texture.height())) + height = texture.height() - src_y; + + int32_t off_x = 0; + if (dst_x < static_cast(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(width)) + return false; + + int32_t off_y = 0; + if (dst_y < static_cast(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(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; + } + +} diff --git a/LibGUI/Widget/Button.cpp b/LibGUI/Widget/Button.cpp new file mode 100644 index 0000000..eeebc96 --- /dev/null +++ b/LibGUI/Widget/Button.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +namespace LibGUI::Widget +{ + + BAN::ErrorOr> Button::create(BAN::RefPtr 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