Compare commits

..

20 Commits

Author SHA1 Message Date
b2c642f03d Don't make SHM extension dependent option
This was here because I though banan-os didn't define them, but they do
exist there :^)
2026-04-15 19:23:34 +03:00
654e878165 Allow compiling with just xorgproto installed 2026-04-15 19:22:56 +03:00
5076d3bbaf Add missing sys/socket.h include 2026-04-15 18:53:45 +03:00
7be8edada7 Add child window id to its parent
I had accidentally removed this in b228ef13c4 and it broke all programs
that were using child windows :D
2026-04-15 18:43:03 +03:00
07f2b5dbb7 Add hacky way to die when WindowServer dies
We keep a dummy window around which will receive a close event that
exits once WindowServer dies :^)
2026-04-15 18:21:03 +03:00
a497e4267f Add README and LICENSE 2026-04-15 17:54:40 +03:00
7252c985b8 Add banan-os as a submodule instead of manually copying libraries 2026-04-15 17:54:31 +03:00
f5e52b5ac8 Remove accidentally commited debug script ;) 2026-04-13 02:58:30 +03:00
b228ef13c4 Fix null depth on CreateWindow 2026-03-23 17:15:57 +02:00
f7150056c4 Cleanup debug prints 2026-02-23 03:02:07 +02:00
bb7632d53f Fix input event state and child window
state represents state just before the event, not current

set child window to null if wid == child_wid
2026-02-23 03:00:08 +02:00
862eaa205a Fix parenthesis being flipped 2026-02-23 02:58:48 +02:00
61f8b2fa66 Implement per-client event masks
This allows multiple clients to listen for events on the same window.
Mostly used for listening events on the root window
2026-02-23 00:40:53 +02:00
e376c57cda Remove unnecessary includes added by clangd 2026-02-22 22:16:33 +02:00
561aaecc3f Add support for changing window's fullscreen state 2026-02-22 22:16:33 +02:00
4886e71452 Cleanup WM_CLOSE_WINDOW atom handling
predefine needed atoms so we dont have to check if or when they are
made available
2026-02-22 22:15:48 +02:00
710b896a84 LibGUI/WindowServer: Add fullscreen events 2026-02-22 22:13:03 +02:00
8c2fea9edf Cleanup whitespace and suppress unused variable warnings 2026-02-22 22:13:03 +02:00
85bb292a31 Support WM_CLOSE_WINDOW 2026-02-22 22:13:03 +02:00
20f105f56c Fix predefined atom definitions
Atom names are not supposed to contain the XA_ prefix :D
2026-02-22 22:13:03 +02:00
141 changed files with 1410 additions and 17696 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "banan-os"]
path = banan-os
url = https://git.bananymous.com/Bananymous/banan-os.git

View File

@@ -0,0 +1,810 @@
From c9dd87198ce5262e6ddf6bf3b0c18eb84784d35e Mon Sep 17 00:00:00 2001
From: Oskari Alaranta <oskari.alaranta@bananymous.com>
Date: Wed, 15 Apr 2026 17:52:54 +0300
Subject: [PATCH] linux-window-server-sdl2
---
userspace/libraries/LibGUI/Widget/Widget.cpp | 2 +-
userspace/libraries/LibGUI/Window.cpp | 9 +-
userspace/programs/ProgramLauncher/main.cpp | 2 +-
userspace/programs/Terminal/Terminal.cpp | 4 +-
.../programs/WindowServer/CMakeLists.txt | 3 +
.../programs/WindowServer/Framebuffer.cpp | 52 +--
userspace/programs/WindowServer/Window.cpp | 29 +-
.../programs/WindowServer/WindowServer.cpp | 47 ++-
.../programs/WindowServer/WindowServer.h | 1 +
userspace/programs/WindowServer/main.cpp | 357 ++++++++++++------
10 files changed, 340 insertions(+), 166 deletions(-)
diff --git a/userspace/libraries/LibGUI/Widget/Widget.cpp b/userspace/libraries/LibGUI/Widget/Widget.cpp
index d6489d87..c532fb04 100644
--- a/userspace/libraries/LibGUI/Widget/Widget.cpp
+++ b/userspace/libraries/LibGUI/Widget/Widget.cpp
@@ -15,7 +15,7 @@ namespace LibGUI::Widget
const LibFont::Font& Widget::default_font()
{
if (!s_default_font.has_value())
- MUST(set_default_font("/usr/share/fonts/lat0-16.psfu"_sv));
+ MUST(set_default_font("./lat0-16.psfu"_sv));
return s_default_font.value();
}
diff --git a/userspace/libraries/LibGUI/Window.cpp b/userspace/libraries/LibGUI/Window.cpp
index b4172f70..3a0e9cca 100644
--- a/userspace/libraries/LibGUI/Window.cpp
+++ b/userspace/libraries/LibGUI/Window.cpp
@@ -4,9 +4,8 @@
#include <fcntl.h>
#include <stdlib.h>
-#include <sys/banan-os.h>
#include <sys/epoll.h>
-#include <sys/mman.h>
+#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
@@ -271,7 +270,7 @@ namespace LibGUI
void Window::cleanup()
{
- munmap(m_framebuffer_smo, m_width * m_height * 4);
+ shmdt(m_framebuffer_smo);
close(m_server_fd);
close(m_epoll_fd);
}
@@ -279,7 +278,7 @@ namespace LibGUI
BAN::ErrorOr<void> Window::handle_resize_event(const EventPacket::ResizeWindowEvent& event)
{
if (m_framebuffer_smo)
- munmap(m_framebuffer_smo, m_width * m_height * 4);
+ shmdt(m_framebuffer_smo);
m_framebuffer_smo = nullptr;
TRY(m_texture.resize(event.width, event.height));
@@ -287,7 +286,7 @@ namespace LibGUI
if (m_root_widget)
TRY(m_root_widget->set_fixed_geometry({ 0, 0, event.width, event.height }));
- void* framebuffer_addr = smo_map(event.smo_key);
+ void* framebuffer_addr = shmat(event.smo_key, nullptr, 0);
if (framebuffer_addr == nullptr)
return BAN::Error::from_errno(errno);
diff --git a/userspace/programs/ProgramLauncher/main.cpp b/userspace/programs/ProgramLauncher/main.cpp
index c833c582..0e1cc460 100644
--- a/userspace/programs/ProgramLauncher/main.cpp
+++ b/userspace/programs/ProgramLauncher/main.cpp
@@ -182,7 +182,7 @@ int main()
attributes.alpha_channel = true;
attributes.title_bar = false;
- auto font = MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv));
+ auto font = MUST(LibFont::Font::load("./lat0-16.psfu"_sv));
const auto full_program_list = get_program_list();
diff --git a/userspace/programs/Terminal/Terminal.cpp b/userspace/programs/Terminal/Terminal.cpp
index e1bd317d..8a6b5bd0 100644
--- a/userspace/programs/Terminal/Terminal.cpp
+++ b/userspace/programs/Terminal/Terminal.cpp
@@ -92,7 +92,7 @@ void Terminal::start_shell()
close(pts_slave);
close(pts_master);
- execl("/bin/Shell", "Shell", NULL);
+ execl("/usr/bin/zsh", "zsh", NULL);
exit(1);
}
@@ -127,7 +127,7 @@ void Terminal::run()
m_window->texture().set_bg_color(m_bg_color);
m_window->invalidate();
- m_font = MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv));
+ m_font = MUST(LibFont::Font::load("./lat0-16.psfu"_sv));
m_window->set_min_size(m_font.width() * 8, m_font.height() * 2);
diff --git a/userspace/programs/WindowServer/CMakeLists.txt b/userspace/programs/WindowServer/CMakeLists.txt
index 8fdf79f9..fcaf441d 100644
--- a/userspace/programs/WindowServer/CMakeLists.txt
+++ b/userspace/programs/WindowServer/CMakeLists.txt
@@ -13,4 +13,7 @@ banan_link_library(WindowServer libfont)
banan_link_library(WindowServer libimage)
banan_link_library(WindowServer libinput)
+find_package(SDL2 REQUIRED CONFIG REQUIRED COMPONENTS SDL2)
+target_link_libraries(WindowServer PRIVATE SDL2::SDL2)
+
install(TARGETS WindowServer OPTIONAL)
diff --git a/userspace/programs/WindowServer/Framebuffer.cpp b/userspace/programs/WindowServer/Framebuffer.cpp
index 0e727e8f..119f1451 100644
--- a/userspace/programs/WindowServer/Framebuffer.cpp
+++ b/userspace/programs/WindowServer/Framebuffer.cpp
@@ -1,45 +1,53 @@
#include "Framebuffer.h"
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/framebuffer.h>
-#include <sys/mman.h>
+
+#include <SDL2/SDL.h>
+
+static constexpr size_t window_width { 1280 };
+static constexpr size_t window_height { 800 };
+static constexpr size_t window_bpp { 32 };
+
+SDL_Renderer* g_renderer { nullptr };
+SDL_Texture* g_texture { nullptr };
+SDL_Window* g_window { nullptr };
Framebuffer open_framebuffer()
{
- int framebuffer_fd = open("/dev/fb0", O_RDWR | O_CLOEXEC);
- if (framebuffer_fd == -1)
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == -1)
{
- perror("open");
+ fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError());
exit(1);
}
- framebuffer_info_t framebuffer_info;
- if (pread(framebuffer_fd, &framebuffer_info, sizeof(framebuffer_info), -1) == -1)
+ g_window = SDL_CreateWindow("banan gui", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height, SDL_WINDOW_SHOWN);
+ if (g_window == nullptr)
{
- perror("pread");
+ fprintf(stderr, "Could not create SDL window: %s\n", SDL_GetError());
exit(1);
}
- const size_t framebuffer_bytes = framebuffer_info.width * framebuffer_info.height * (BANAN_FB_BPP / 8);
+ g_renderer = SDL_CreateRenderer(g_window, -1, SDL_RENDERER_ACCELERATED);
+ if (g_renderer == nullptr)
+ {
+ fprintf(stderr, "Could not get SDL renderer: %s\n", SDL_GetError());
+ exit(1);
+ }
- uint32_t* framebuffer_mmap = (uint32_t*)mmap(NULL, framebuffer_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, framebuffer_fd, 0);
- if (framebuffer_mmap == MAP_FAILED)
+ g_texture = SDL_CreateTexture(g_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window_width, window_height);
+ if (g_texture == nullptr)
{
- perror("mmap");
+ fprintf(stderr, "Could not get SDL texture: %s\n", SDL_GetError());
exit(1);
}
- memset(framebuffer_mmap, 0, framebuffer_bytes);
- msync(framebuffer_mmap, framebuffer_bytes, MS_SYNC);
+ uint32_t* pixels = new uint32_t[window_width * window_height];
+ memset(pixels, 0, window_width * window_height * 4);
Framebuffer framebuffer;
- framebuffer.fd = framebuffer_fd;
- framebuffer.mmap = framebuffer_mmap;
- framebuffer.width = framebuffer_info.width;
- framebuffer.height = framebuffer_info.height;
- framebuffer.bpp = BANAN_FB_BPP;
+ framebuffer.mmap = pixels;
+ framebuffer.width = window_width;
+ framebuffer.height = window_height;
+ framebuffer.bpp = window_bpp;
return framebuffer;
}
diff --git a/userspace/programs/WindowServer/Window.cpp b/userspace/programs/WindowServer/Window.cpp
index 1e66f522..9759eec9 100644
--- a/userspace/programs/WindowServer/Window.cpp
+++ b/userspace/programs/WindowServer/Window.cpp
@@ -5,15 +5,14 @@
#include <LibGUI/Window.h>
-#include <sys/banan-os.h>
-#include <sys/mman.h>
+#include <sys/shm.h>
#include <sys/socket.h>
#include <unistd.h>
Window::~Window()
{
- munmap(m_fb_addr, client_width() * client_height() * 4);
- smo_delete(m_smo_key);
+ shmdt(m_fb_addr);
+ shmctl(m_smo_key, IPC_RMID, nullptr);
LibGUI::EventPacket::DestroyWindowEvent packet;
@@ -47,16 +46,16 @@ BAN::ErrorOr<void> Window::resize(uint32_t width, uint32_t height)
{
const size_t fb_bytes = width * height * 4;
- long smo_key = smo_create(fb_bytes, PROT_READ | PROT_WRITE);
- if (smo_key == -1)
+ int shmid = shmget(rand(), fb_bytes, IPC_CREAT | IPC_EXCL | 0666);
+ if (shmid == -1)
return BAN::Error::from_errno(errno);
- BAN::ScopeGuard smo_deleter([&]() { smo_delete(smo_key); });
+ BAN::ScopeGuard shmdeleter([&]() { shmctl(shmid, IPC_RMID, nullptr); });
- uint32_t* fb_addr = static_cast<uint32_t*>(smo_map(smo_key));
- if (fb_addr == nullptr)
+ uint32_t* fb_addr = static_cast<uint32_t*>(shmat(shmid, nullptr, 0));
+ if (fb_addr == (void*)-1)
return BAN::Error::from_errno(errno);
memset(fb_addr, 0xFF, fb_bytes);
- BAN::ScopeGuard smo_unmapper([&]() { munmap(fb_addr, fb_bytes); });
+ BAN::ScopeGuard shmdetacher([&]() { shmdt(fb_addr); });
{
const auto old_area = m_client_area;
@@ -70,16 +69,14 @@ BAN::ErrorOr<void> Window::resize(uint32_t width, uint32_t height)
return title_bar_ret.release_error();
}
- smo_deleter.disable();
- smo_unmapper.disable();
+ shmdetacher.disable();
+ shmdeleter.disable();
if (m_fb_addr)
- munmap(m_fb_addr, client_width() * client_height() * 4);
- if (m_smo_key)
- smo_delete(m_smo_key);
+ shmdt(m_fb_addr);
m_fb_addr = fb_addr;
- m_smo_key = smo_key;
+ m_smo_key = shmid;
m_client_area.max_x = m_client_area.min_x + width;
m_client_area.max_y = m_client_area.min_y + height;
diff --git a/userspace/programs/WindowServer/WindowServer.cpp b/userspace/programs/WindowServer/WindowServer.cpp
index 2c67d6c0..600704fd 100644
--- a/userspace/programs/WindowServer/WindowServer.cpp
+++ b/userspace/programs/WindowServer/WindowServer.cpp
@@ -8,19 +8,20 @@
#include <LibInput/KeyboardLayout.h>
#include <stdlib.h>
-#include <sys/banan-os.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
#include <unistd.h>
#include <emmintrin.h>
+#include <SDL2/SDL.h>
+
+extern SDL_Renderer* g_renderer;
+extern SDL_Texture* g_texture;
+
WindowServer::WindowServer(Framebuffer& framebuffer, int32_t corner_radius)
: m_framebuffer(framebuffer)
, m_corner_radius(corner_radius)
, m_cursor({ framebuffer.width / 2, framebuffer.height / 2 })
- , m_font(MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv)))
+ , m_font(MUST(LibFont::Font::load("./lat0-16.psfu"_sv)))
{
MUST(m_background_image.resize(m_framebuffer.width * m_framebuffer.height, 0xFF101010));
@@ -440,7 +441,7 @@ static void update_volume(const char* new_volume)
void WindowServer::on_key_event(LibInput::KeyEvent event)
{
- if (event.key == LibInput::Key::Super)
+ if (event.key == LibInput::Key::RightCtrl)
m_is_mod_key_held = event.pressed();
if (event.pressed() && event.key == LibInput::Key::VolumeDown)
@@ -461,7 +462,7 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
pid_t pid = fork();
if (pid == 0)
{
- execl("/usr/bin/Terminal", "Terminal", nullptr);
+ execl("./build/banan-os/userspace/programs/Terminal/Terminal", "Terminal", nullptr);
exit(1);
}
if (pid == -1)
@@ -475,7 +476,7 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
pid_t pid = fork();
if (pid == 0)
{
- execl("/usr/bin/ProgramLauncher", "ProgramLauncher", nullptr);
+ execl("./build/banan-os/userspace/programs/ProgramLauncher/ProgramLauncher", "ProgramLauncher", nullptr);
exit(1);
}
if (pid == -1)
@@ -1599,16 +1600,34 @@ void WindowServer::sync()
for (size_t i = 0; i < m_damaged_area_count; i++)
{
- const fb_msync_region region {
- .min_x = static_cast<uint32_t>(m_damaged_areas[i].min_x),
- .min_y = static_cast<uint32_t>(m_damaged_areas[i].min_y),
- .max_x = static_cast<uint32_t>(m_damaged_areas[i].max_x),
- .max_y = static_cast<uint32_t>(m_damaged_areas[i].max_y),
+ const SDL_Rect rect {
+ .x = m_damaged_areas[i].min_x,
+ .y = m_damaged_areas[i].min_y,
+ .w = m_damaged_areas[i].max_x - m_damaged_areas[i].min_x,
+ .h = m_damaged_areas[i].max_y - m_damaged_areas[i].min_y,
};
- ioctl(m_framebuffer.fd, FB_MSYNC_RECTANGLE, &region);
+
+ void* pixels;
+ int pitch;
+ SDL_LockTexture(g_texture, &rect, &pixels, &pitch);
+
+ for (int32_t y_off = 0; y_off < rect.h; y_off++)
+ {
+ memcpy(
+ static_cast<uint8_t*>(pixels) + y_off * pitch,
+ &m_framebuffer.mmap[(rect.y + y_off) * m_framebuffer.width + rect.x],
+ rect.w * sizeof(uint32_t)
+ );
+ }
+
+ SDL_UnlockTexture(g_texture);
}
m_damaged_area_count = 0;
+
+ SDL_RenderClear(g_renderer);
+ SDL_RenderCopy(g_renderer, g_texture, NULL, NULL);
+ SDL_RenderPresent(g_renderer);
}
Rectangle WindowServer::cursor_area() const
diff --git a/userspace/programs/WindowServer/WindowServer.h b/userspace/programs/WindowServer/WindowServer.h
index 94fbc774..bcd7a6b9 100644
--- a/userspace/programs/WindowServer/WindowServer.h
+++ b/userspace/programs/WindowServer/WindowServer.h
@@ -62,6 +62,7 @@ public:
bool is_damaged() const { return m_damaged_area_count > 0 || m_is_bouncing_window; }
bool is_stopped() const { return m_is_stopped; }
+ void stop() { m_is_stopped = true; }
private:
void on_mouse_move_impl(int32_t new_x, int32_t new_y);
diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp
index 46f2ba6d..520c8e7d 100644
--- a/userspace/programs/WindowServer/main.cpp
+++ b/userspace/programs/WindowServer/main.cpp
@@ -10,7 +10,6 @@
#include <limits.h>
#include <signal.h>
#include <stdlib.h>
-#include <sys/banan-os.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/socket.h>
@@ -150,6 +149,8 @@ int open_server_fd()
int g_epoll_fd = -1;
+static void poll_sdl_events(WindowServer&);
+
int main()
{
srand(time(nullptr));
@@ -181,14 +182,6 @@ int main()
}
}
- if (tty_ctrl(STDIN_FILENO, TTY_CMD_UNSET, TTY_FLAG_ENABLE_INPUT) == -1)
- {
- dwarnln("tty_ctrl: {}", strerror(errno));
- return 1;
- }
-
- atexit([]() { tty_ctrl(STDIN_FILENO, TTY_CMD_SET, TTY_FLAG_ENABLE_INPUT); });
-
constexpr int non_terminating_signals[] {
SIGCHLD,
SIGCONT,
@@ -196,11 +189,12 @@ int main()
SIGTSTP,
SIGTTIN,
SIGTTOU,
+ SIGWINCH,
};
constexpr int ignored_signals[] {
SIGPIPE,
};
- for (int sig = _SIGMIN; sig <= _SIGMAX; sig++)
+ for (int sig = 1; sig < NSIG; sig++)
signal(sig, exit);
for (int sig : non_terminating_signals)
signal(sig, SIG_DFL);
@@ -208,51 +202,15 @@ int main()
signal(sig, SIG_IGN);
MUST(LibInput::KeyboardLayout::initialize());
- MUST(LibInput::KeyboardLayout::get().load_from_file("/usr/share/keymaps/us.keymap"_sv));
-
- int keyboard_fd = open("/dev/keyboard", O_RDONLY | O_CLOEXEC);
- if (keyboard_fd == -1)
- dwarnln("open keyboard: {}", strerror(errno));
- else
- {
- epoll_event event {
- .events = EPOLLIN,
- .data = { .fd = keyboard_fd },
- };
- if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, keyboard_fd, &event) == -1)
- {
- dwarnln("epoll_ctl keyboard: {}", strerror(errno));
- close(keyboard_fd);
- keyboard_fd = -1;
- }
- }
-
- int mouse_fd = open("/dev/mouse", O_RDONLY | O_CLOEXEC);
- if (mouse_fd == -1)
- dwarnln("open mouse: {}", strerror(errno));
- else
- {
- epoll_event event {
- .events = EPOLLIN,
- .data = { .fd = mouse_fd },
- };
- if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, mouse_fd, &event) == -1)
- {
- dwarnln("epoll_ctl mouse: {}", strerror(errno));
- close(mouse_fd);
- mouse_fd = -1;
- }
- }
+ MUST(LibInput::KeyboardLayout::get().load_from_file("./us.keymap"_sv));
dprintln("Window server started");
- if (access("/usr/bin/xbanan", X_OK) == 0)
+ if (access("./build/xbanan/xbanan", X_OK) == 0)
{
if (fork() == 0)
{
- dup2(STDDBG_FILENO, STDOUT_FILENO);
- dup2(STDDBG_FILENO, STDERR_FILENO);
- execl("/usr/bin/xbanan", "xbanan", NULL);
+ execl("./build/xbanan/xbanan", "xbanan", NULL);
exit(1);
}
}
@@ -276,26 +234,23 @@ int main()
uint64_t last_sync_us = get_current_us() - sync_interval_us;
while (!window_server.is_stopped())
{
- timespec* ptimeout = nullptr;
-
timespec timeout = {};
- if (window_server.is_damaged())
- {
- if (const auto current_us = get_current_us(); current_us - last_sync_us > sync_interval_us)
- {
- window_server.sync();
- const auto full_intervals = (current_us - last_sync_us) / sync_interval_us;
- last_sync_us += full_intervals * sync_interval_us;
- }
+ if (const auto current_us = get_current_us(); current_us - last_sync_us > sync_interval_us)
+ {
+ window_server.sync();
- if (const auto current_us = get_current_us(); current_us - last_sync_us < sync_interval_us)
- timeout.tv_nsec = (sync_interval_us - (current_us - last_sync_us)) * 1000;
- ptimeout = &timeout;
+ const auto full_intervals = (current_us - last_sync_us) / sync_interval_us;
+ last_sync_us += full_intervals * sync_interval_us;
}
+ if (const auto current_us = get_current_us(); current_us - last_sync_us < sync_interval_us)
+ timeout.tv_nsec = (sync_interval_us - (current_us - last_sync_us)) * 1000;
+
+ poll_sdl_events(window_server);
+
epoll_event events[16];
- int epoll_events = epoll_pwait2(g_epoll_fd, events, 16, ptimeout, nullptr);
+ int epoll_events = epoll_pwait2(g_epoll_fd, events, 16, &timeout, nullptr);
if (epoll_events == -1 && errno != EINTR)
{
dwarnln("epoll_pwait2: {}", strerror(errno));
@@ -333,48 +288,6 @@ int main()
continue;
}
- if (events[i].data.fd == keyboard_fd)
- {
- ASSERT(events[i].events & EPOLLIN);
-
- LibInput::RawKeyEvent event;
- if (read(keyboard_fd, &event, sizeof(event)) == -1)
- {
- dwarnln("read keyboard: {}", strerror(errno));
- continue;
- }
- window_server.on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw(event));
- continue;
- }
-
- if (events[i].data.fd == mouse_fd)
- {
- ASSERT(events[i].events & EPOLLIN);
-
- LibInput::MouseEvent event;
- if (read(mouse_fd, &event, sizeof(event)) == -1)
- {
- dwarnln("read mouse: {}", strerror(errno));
- continue;
- }
- switch (event.type)
- {
- case LibInput::MouseEventType::MouseButtonEvent:
- window_server.on_mouse_button(event.button_event);
- break;
- case LibInput::MouseEventType::MouseMoveEvent:
- window_server.on_mouse_move(event.move_event);
- break;
- case LibInput::MouseEventType::MouseMoveAbsEvent:
- window_server.on_mouse_move_abs(event.move_abs_event);
- break;
- case LibInput::MouseEventType::MouseScrollEvent:
- window_server.on_mouse_scroll(event.scroll_event);
- break;
- }
- continue;
- }
-
const int client_fd = events[i].data.fd;
if (events[i].events & (EPOLLHUP | EPOLLERR))
{
@@ -500,3 +413,237 @@ int main()
}
}
}
+
+#include <SDL2/SDL.h>
+
+extern SDL_Window* g_window;
+struct Keymap
+{
+ consteval Keymap()
+ {
+ for (auto& scancode : map)
+ scancode = 0;
+
+ using LibInput::keycode_normal;
+ using LibInput::keycode_function;
+ using LibInput::keycode_numpad;
+
+ map[SDL_SCANCODE_GRAVE] = keycode_normal(0, 0);
+ map[SDL_SCANCODE_1] = keycode_normal(0, 1);
+ map[SDL_SCANCODE_2] = keycode_normal(0, 2);
+ map[SDL_SCANCODE_3] = keycode_normal(0, 3);
+ map[SDL_SCANCODE_4] = keycode_normal(0, 4);
+ map[SDL_SCANCODE_5] = keycode_normal(0, 5);
+ map[SDL_SCANCODE_6] = keycode_normal(0, 6);
+ map[SDL_SCANCODE_7] = keycode_normal(0, 7);
+ map[SDL_SCANCODE_8] = keycode_normal(0, 8);
+ map[SDL_SCANCODE_9] = keycode_normal(0, 9);
+ map[SDL_SCANCODE_0] = keycode_normal(0, 10);
+ map[SDL_SCANCODE_MINUS] = keycode_normal(0, 11);
+ map[SDL_SCANCODE_EQUALS] = keycode_normal(0, 12);
+ map[SDL_SCANCODE_BACKSPACE] = keycode_normal(0, 13);
+
+ map[SDL_SCANCODE_TAB] = keycode_normal(1, 0);
+ map[SDL_SCANCODE_Q] = keycode_normal(1, 1);
+ map[SDL_SCANCODE_W] = keycode_normal(1, 2);
+ map[SDL_SCANCODE_E] = keycode_normal(1, 3);
+ map[SDL_SCANCODE_R] = keycode_normal(1, 4);
+ map[SDL_SCANCODE_T] = keycode_normal(1, 5);
+ map[SDL_SCANCODE_Y] = keycode_normal(1, 6);
+ map[SDL_SCANCODE_U] = keycode_normal(1, 7);
+ map[SDL_SCANCODE_I] = keycode_normal(1, 8);
+ map[SDL_SCANCODE_O] = keycode_normal(1, 9);
+ map[SDL_SCANCODE_P] = keycode_normal(1, 10);
+ map[SDL_SCANCODE_LEFTBRACKET] = keycode_normal(1, 11);
+ map[SDL_SCANCODE_RIGHTBRACKET] = keycode_normal(1, 12);
+
+ map[SDL_SCANCODE_CAPSLOCK] = keycode_normal(2, 0);
+ map[SDL_SCANCODE_A] = keycode_normal(2, 1);
+ map[SDL_SCANCODE_S] = keycode_normal(2, 2);
+ map[SDL_SCANCODE_D] = keycode_normal(2, 3);
+ map[SDL_SCANCODE_F] = keycode_normal(2, 4);
+ map[SDL_SCANCODE_G] = keycode_normal(2, 5);
+ map[SDL_SCANCODE_H] = keycode_normal(2, 6);
+ map[SDL_SCANCODE_J] = keycode_normal(2, 7);
+ map[SDL_SCANCODE_K] = keycode_normal(2, 8);
+ map[SDL_SCANCODE_L] = keycode_normal(2, 9);
+ map[SDL_SCANCODE_SEMICOLON] = keycode_normal(2, 10);
+ map[SDL_SCANCODE_APOSTROPHE] = keycode_normal(2, 11);
+ map[SDL_SCANCODE_BACKSLASH] = keycode_normal(2, 12);
+ map[SDL_SCANCODE_RETURN] = keycode_normal(2, 13);
+
+ map[SDL_SCANCODE_LSHIFT] = keycode_normal(3, 0);
+ map[SDL_SCANCODE_NONUSBACKSLASH] = keycode_normal(3, 1);
+ map[SDL_SCANCODE_Z] = keycode_normal(3, 2);
+ map[SDL_SCANCODE_X] = keycode_normal(3, 3);
+ map[SDL_SCANCODE_C] = keycode_normal(3, 4);
+ map[SDL_SCANCODE_V] = keycode_normal(3, 5);
+ map[SDL_SCANCODE_B] = keycode_normal(3, 6);
+ map[SDL_SCANCODE_N] = keycode_normal(3, 7);
+ map[SDL_SCANCODE_M] = keycode_normal(3, 8);
+ map[SDL_SCANCODE_COMMA] = keycode_normal(3, 9);
+ map[SDL_SCANCODE_PERIOD] = keycode_normal(3, 10);
+ map[SDL_SCANCODE_SLASH] = keycode_normal(3, 11);
+ map[SDL_SCANCODE_RSHIFT] = keycode_normal(3, 12);
+
+ map[SDL_SCANCODE_LCTRL] = keycode_normal(4, 0);
+ map[SDL_SCANCODE_LGUI] = keycode_normal(4, 1);
+ map[SDL_SCANCODE_LALT] = keycode_normal(4, 2);
+ map[SDL_SCANCODE_SPACE] = keycode_normal(4, 3);
+ map[SDL_SCANCODE_RALT] = keycode_normal(4, 4);
+ map[SDL_SCANCODE_RCTRL] = keycode_normal(4, 5);
+
+ map[SDL_SCANCODE_UP] = keycode_normal(5, 0);
+ map[SDL_SCANCODE_LEFT] = keycode_normal(5, 1);
+ map[SDL_SCANCODE_DOWN] = keycode_normal(5, 2);
+ map[SDL_SCANCODE_RIGHT] = keycode_normal(5, 3);
+
+ map[SDL_SCANCODE_ESCAPE] = keycode_function(0);
+ map[SDL_SCANCODE_F1] = keycode_function(1);
+ map[SDL_SCANCODE_F2] = keycode_function(2);
+ map[SDL_SCANCODE_F3] = keycode_function(3);
+ map[SDL_SCANCODE_F4] = keycode_function(4);
+ map[SDL_SCANCODE_F5] = keycode_function(5);
+ map[SDL_SCANCODE_F6] = keycode_function(6);
+ map[SDL_SCANCODE_F7] = keycode_function(7);
+ map[SDL_SCANCODE_F8] = keycode_function(8);
+ map[SDL_SCANCODE_F9] = keycode_function(9);
+ map[SDL_SCANCODE_F10] = keycode_function(10);
+ map[SDL_SCANCODE_F11] = keycode_function(11);
+ map[SDL_SCANCODE_F12] = keycode_function(12);
+ map[SDL_SCANCODE_INSERT] = keycode_function(13);
+ map[SDL_SCANCODE_PRINTSCREEN] = keycode_function(14);
+ map[SDL_SCANCODE_DELETE] = keycode_function(15);
+ map[SDL_SCANCODE_HOME] = keycode_function(16);
+ map[SDL_SCANCODE_END] = keycode_function(17);
+ map[SDL_SCANCODE_PAGEUP] = keycode_function(18);
+ map[SDL_SCANCODE_PAGEDOWN] = keycode_function(19);
+ map[SDL_SCANCODE_SCROLLLOCK] = keycode_function(20);
+
+ map[SDL_SCANCODE_NUMLOCKCLEAR] = keycode_numpad(0, 0);
+ map[SDL_SCANCODE_KP_DIVIDE] = keycode_numpad(0, 1);
+ map[SDL_SCANCODE_KP_MULTIPLY] = keycode_numpad(0, 2);
+ map[SDL_SCANCODE_KP_MINUS] = keycode_numpad(0, 3);
+ map[SDL_SCANCODE_KP_7] = keycode_numpad(1, 0);
+ map[SDL_SCANCODE_KP_8] = keycode_numpad(1, 1);
+ map[SDL_SCANCODE_KP_9] = keycode_numpad(1, 2);
+ map[SDL_SCANCODE_KP_PLUS] = keycode_numpad(1, 3);
+ map[SDL_SCANCODE_KP_4] = keycode_numpad(2, 0);
+ map[SDL_SCANCODE_KP_5] = keycode_numpad(2, 1);
+ map[SDL_SCANCODE_KP_6] = keycode_numpad(2, 2);
+ map[SDL_SCANCODE_KP_1] = keycode_numpad(3, 0);
+ map[SDL_SCANCODE_KP_2] = keycode_numpad(3, 1);
+ map[SDL_SCANCODE_KP_3] = keycode_numpad(3, 2);
+ map[SDL_SCANCODE_KP_ENTER] = keycode_numpad(3, 3);
+ map[SDL_SCANCODE_KP_0] = keycode_numpad(4, 0);
+ map[SDL_SCANCODE_KP_COMMA] = keycode_numpad(4, 1);
+ };
+
+ uint8_t map[SDL_NUM_SCANCODES];
+};
+static Keymap s_sdl_keymap;
+
+static void poll_sdl_events(WindowServer& window_server)
+{
+ {
+ static int prev_x { 0 };
+ static int prev_y { 0 };
+
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+
+ if (prev_x != x || prev_y != y)
+ {
+ int w, h;
+ SDL_GetWindowSize(g_window, &w, &h);
+
+ window_server.on_mouse_move_abs({
+ .abs_x = x,
+ .abs_y = y,
+ .min_x = 0,
+ .min_y = 0,
+ .max_x = w,
+ .max_y = h,
+ });
+ }
+
+ prev_x = x;
+ prev_y = y;
+ }
+
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ window_server.stop();
+ break;
+ case SDL_WINDOWEVENT:
+ if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
+ SDL_ShowCursor(SDL_DISABLE);
+ if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
+ SDL_ShowCursor(SDL_ENABLE);
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ {
+ uint16_t modifier = 0;
+ if (event.key.keysym.mod & KMOD_LSHIFT)
+ modifier |= LibInput::KeyEvent::Modifier::LShift;
+ if (event.key.keysym.mod & KMOD_RSHIFT)
+ modifier |= LibInput::KeyEvent::Modifier::RShift;
+ if (event.key.keysym.mod & KMOD_LCTRL)
+ modifier |= LibInput::KeyEvent::Modifier::LCtrl;
+ if (event.key.keysym.mod & KMOD_RCTRL)
+ modifier |= LibInput::KeyEvent::Modifier::RCtrl;
+ if (event.key.keysym.mod & KMOD_LALT)
+ modifier |= LibInput::KeyEvent::Modifier::LAlt;
+ if (event.key.keysym.mod & KMOD_RALT)
+ modifier |= LibInput::KeyEvent::Modifier::RAlt;
+ if (event.key.keysym.mod & KMOD_NUM)
+ modifier |= LibInput::KeyEvent::Modifier::NumLock;
+ if (event.key.keysym.mod & KMOD_SCROLL)
+ modifier |= LibInput::KeyEvent::Modifier::ScrollLock;
+ if (event.key.keysym.mod & KMOD_CAPS)
+ modifier |= LibInput::KeyEvent::Modifier::CapsLock;
+ if (event.key.state == SDL_PRESSED)
+ modifier |= LibInput::KeyEvent::Modifier::Pressed;
+
+ window_server.on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw({
+ .modifier = modifier,
+ .keycode = s_sdl_keymap.map[event.key.keysym.scancode],
+ }));
+
+ break;
+ }
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ {
+ LibInput::MouseButton button_map[] {
+ [0] = LibInput::MouseButton::Left,
+ [SDL_BUTTON_LEFT] = LibInput::MouseButton::Left,
+ [SDL_BUTTON_MIDDLE] = LibInput::MouseButton::Middle,
+ [SDL_BUTTON_RIGHT] = LibInput::MouseButton::Right,
+ [SDL_BUTTON_X1] = LibInput::MouseButton::Extra1,
+ [SDL_BUTTON_X2] = LibInput::MouseButton::Extra2,
+ };
+
+ window_server.on_mouse_button({
+ .button = button_map[event.button.button],
+ .pressed = (event.button.state == SDL_PRESSED),
+ });
+
+ break;
+ }
+ case SDL_MOUSEWHEEL:
+ {
+ window_server.on_mouse_scroll({
+ .scroll = event.wheel.y,
+ });
+
+ break;
+ }
+ }
+ }
+}
--
2.53.0

View File

@@ -1,22 +0,0 @@
#include <BAN/Assert.h>
#if __is_kernel
#include <kernel/Panic.h>
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
{
Kernel::panic_impl(location, msg);
}
#else
#include <BAN/Debug.h>
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
{
derrorln("{}: {}", location, msg);
__builtin_trap();
}
#endif

Binary file not shown.

View File

@@ -1,9 +0,0 @@
#include <BAN/New.h>
void* operator new(size_t size) { return BAN::allocator(size); }
void* operator new[](size_t size) { return BAN::allocator(size); }
void operator delete(void* addr) { BAN::deallocator(addr); }
void operator delete[](void* addr) { BAN::deallocator(addr); }
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }

Binary file not shown.

View File

@@ -1,11 +0,0 @@
#include <BAN/String.h>
#include <BAN/StringView.h>
namespace BAN
{
StringView::StringView(const String& other)
: StringView(other.data(), other.size())
{ }
}

Binary file not shown.

View File

@@ -1,71 +0,0 @@
#include <BAN/Time.h>
namespace BAN
{
static constexpr bool is_leap_year(uint64_t year)
{
if (year % 400 == 0)
return true;
if (year % 100 == 0)
return false;
if (year % 4 == 0)
return true;
return false;
}
static constexpr uint64_t leap_days_since_epoch(const BAN::Time& time)
{
uint64_t leap_years = 0;
for (uint32_t year = 1970; year < time.year; year++)
if (is_leap_year(year))
leap_years++;
if (is_leap_year(time.year) && time.month >= 3)
leap_years++;
return leap_years;
}
static constexpr uint64_t month_days[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
uint64_t to_unix_time(const BAN::Time& time)
{
uint64_t years = time.year - 1970;
uint64_t days = years * 365 + month_days[time.month - 1] + leap_days_since_epoch(time) + (time.day - 1);
uint64_t hours = days * 24 + time.hour;
uint64_t minutes = hours * 60 + time.minute;
uint64_t seconds = minutes * 60 + time.second;
return seconds;
}
BAN::Time from_unix_time(uint64_t unix_time)
{
BAN::Time time {};
time.second = unix_time % 60; unix_time /= 60;
time.minute = unix_time % 60; unix_time /= 60;
time.hour = unix_time % 24; unix_time /= 24;
uint64_t total_days = unix_time;
time.week_day = (total_days + 4) % 7 + 1;
time.year = 1970;
while (total_days >= 365U + is_leap_year(time.year))
{
total_days -= 365U + is_leap_year(time.year);
time.year++;
}
bool is_leap_day = is_leap_year(time.year) && total_days == month_days[2];
bool had_leap_day = is_leap_year(time.year) && total_days > month_days[2];
for (time.month = 1; time.month < 12; time.month++)
if (total_days < month_days[time.month] + (is_leap_day || had_leap_day))
break;
time.day = total_days - month_days[time.month - 1] + !had_leap_day;
return time;
}
}

Binary file not shown.

View File

@@ -1,16 +0,0 @@
set(BAN_SOURCES
BAN/Assert.cpp
BAN/New.cpp
BAN/StringView.cpp
BAN/Time.cpp
)
add_library(ban ${BAN_SOURCES})
set_target_properties(ban PROPERTIES OUTPUT_NAME libban)
# set SONAME as cmake doesn't set it for some reason??
set_target_properties(ban PROPERTIES LINK_FLAGS "-Wl,-soname,libban.so")
banan_install_headers(ban)
install(TARGETS ban OPTIONAL)

View File

@@ -1,104 +0,0 @@
#pragma once
#include <BAN/Iterators.h>
#include <BAN/Span.h>
#include <stddef.h>
namespace BAN
{
template<typename T, size_t S>
class Array
{
public:
using size_type = decltype(S);
using value_type = T;
using iterator = IteratorSimple<T, Array>;
using const_iterator = ConstIteratorSimple<T, Array>;
public:
constexpr Array() = default;
constexpr Array(const T&);
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + size()); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + size()); }
constexpr const T& operator[](size_type) const;
constexpr T& operator[](size_type);
constexpr const T& back() const;
constexpr T& back();
constexpr const T& front() const;
constexpr T& front();
Span<T> span() { return Span(m_data, size()); }
Span<const T> span() const { return Span(m_data, size()); }
constexpr size_type size() const;
constexpr const T* data() const { return m_data; }
constexpr T* data() { return m_data; }
private:
T m_data[S] {};
};
template<typename T, size_t S>
constexpr Array<T, S>::Array(const T& value)
{
for (size_type i = 0; i < S; i++)
m_data[i] = value;
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::operator[](size_type index) const
{
ASSERT(index < S);
return m_data[index];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::operator[](size_type index)
{
ASSERT(index < S);
return m_data[index];
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::back() const
{
ASSERT(S != 0);
return m_data[S - 1];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::back()
{
ASSERT(S != 0);
return m_data[S - 1];
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::front() const
{
ASSERT(S != 0);
return m_data[0];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::front()
{
ASSERT(S != 0);
return m_data[0];
}
template<typename T, size_t S>
constexpr typename Array<T, S>::size_type Array<T, S>::size() const
{
return S;
}
}

View File

@@ -1,14 +0,0 @@
#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);

View File

@@ -1,99 +0,0 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN
{
enum MemoryOrder
{
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST,
};
template<typename T> concept atomic_c = is_integral_v<T> || is_pointer_v<T>;
template<typename T> concept atomic_lockfree_c = (is_integral_v<T> || is_pointer_v<T>) && __atomic_always_lock_free(sizeof(T), 0);
template<atomic_lockfree_c T, atomic_c U>
inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); }
template<atomic_lockfree_c T>
inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); }
template<atomic_lockfree_c T, atomic_c U>
inline T atomic_exchange(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_exchange_n(&obj, value, mem_order); }
template<atomic_lockfree_c T, atomic_lockfree_c U, atomic_c V>
inline bool atomic_compare_exchange(T& obj, U& expected, V value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_compare_exchange_n(&obj, &expected, value, false, mem_order, mem_order); }
#define DECL_ATOMIC_INLINE template<atomic_lockfree_c T, atomic_c U> inline
DECL_ATOMIC_INLINE T atomic_add_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_add_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_sub_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_sub_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_and_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_and_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_xor_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_xor_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_or_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_or_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_nand_fetch(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_nand_fetch(&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_add (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_add (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_sub (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_sub (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_and (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_and (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_xor (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_xor (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_or (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_or (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_nand(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_nand(&obj, value, mem_order); }
#undef DECL_ATOMIC_INLINE
template<atomic_lockfree_c T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
class Atomic
{
Atomic(const Atomic&) = delete;
Atomic(Atomic&&) = delete;
Atomic& operator=(const Atomic&) volatile = delete;
Atomic& operator=(Atomic&&) volatile = delete;
public:
constexpr Atomic() : m_value(0) {}
constexpr Atomic(T val) : m_value(val) {}
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return atomic_load(m_value, mem_order); }
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { atomic_store(m_value, val, mem_order); }
inline T operator=(T val) volatile { store(val); return val; }
inline operator T() const volatile { return load(); }
inline T operator+=(T val) volatile { return atomic_add_fetch(m_value, val, MEM_ORDER); }
inline T operator-=(T val) volatile { return atomic_sub_fetch(m_value, val, MEM_ORDER); }
inline T operator&=(T val) volatile { return atomic_and_fetch(m_value, val, MEM_ORDER); }
inline T operator^=(T val) volatile { return atomic_xor_fetch(m_value, val, MEM_ORDER); }
inline T operator|=(T val) volatile { return atomic_or_fetch(m_value, val, MEM_ORDER); }
inline T operator--() volatile { return atomic_sub_fetch(m_value, 1, MEM_ORDER); }
inline T operator++() volatile { return atomic_add_fetch(m_value, 1, MEM_ORDER); }
inline T operator--(int) volatile { return atomic_fetch_sub(m_value, 1, MEM_ORDER); }
inline T operator++(int) volatile { return atomic_fetch_add(m_value, 1, MEM_ORDER); }
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_compare_exchange(m_value, expected, desired, mem_order); }
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_exchange(m_value, desired, mem_order); };
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_add_fetch (m_value, val, mem_order); }
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_sub_fetch (m_value, val, mem_order); }
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_and_fetch (m_value, val, mem_order); }
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_xor_fetch (m_value, val, mem_order); }
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_or_fetch (m_value, val, mem_order); }
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_nand_fetch(m_value, val, mem_order); }
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_add (m_value, val, mem_order); }
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_sub (m_value, val, mem_order); }
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_and (m_value, val, mem_order); }
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_xor (m_value, val, mem_order); }
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_or (m_value, val, mem_order); }
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_nand(m_value, val, mem_order); }
private:
T m_value;
};
}

View File

@@ -1,12 +0,0 @@
#pragma once
namespace BAN
{
template<typename To, typename From>
constexpr To bit_cast(const From& from)
{
return __builtin_bit_cast(To, from);
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include <BAN/Array.h>
#include <stddef.h>
namespace BAN
{
template<size_t N>
class Bitset
{
public:
using internal_type = uint64_t;
public:
private:
BAN::Array<internal_type, BAN::Math::div_round_up(N, sizeof(internal_type) * 8)> m_bits;
};
void foo()
{
sizeof(Bitset<65>);
};
}

View File

@@ -1,124 +0,0 @@
#pragma once
#include <BAN/Span.h>
#include <stdint.h>
namespace BAN
{
template<bool CONST>
class ByteSpanGeneral
{
public:
using value_type = maybe_const_t<CONST, uint8_t>;
using size_type = size_t;
public:
ByteSpanGeneral() = default;
ByteSpanGeneral(value_type* data, size_type size)
: m_data(data)
, m_size(size)
{ }
template<bool SRC_CONST>
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
: m_data(other.data())
, m_size(other.size())
{ }
template<bool SRC_CONST>
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
: m_data(other.data())
, m_size(other.size())
{
other.clear();
}
template<typename T>
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
: m_data(other.data())
, m_size(other.size())
{ }
template<typename T>
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
: m_data(other.data())
, m_size(other.size())
{
other.clear();
}
template<bool SRC_CONST>
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
template<bool SRC_CONST>
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
{
m_data = other.data();
m_size = other.size();
other.clear();
return *this;
}
template<typename S>
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
{
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
}
template<typename S>
S& as() const requires(!CONST || is_const_v<S>)
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
Span<S> as_span() const requires(!CONST || is_const_v<S>)
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
{
ASSERT(m_data);
ASSERT(m_size >= offset);
if (length == size_type(-1))
length = m_size - offset;
ASSERT(m_size >= offset + length);
return ByteSpanGeneral(m_data + offset, length);
}
value_type& operator[](size_type offset) const
{
ASSERT(offset < m_size);
return m_data[offset];
}
value_type* data() const { return m_data; }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
void clear()
{
m_data = nullptr;
m_size = 0;
}
private:
value_type* m_data { nullptr };
size_type m_size { 0 };
friend class ByteSpanGeneral<!CONST>;
};
using ByteSpan = ByteSpanGeneral<false>;
using ConstByteSpan = ByteSpanGeneral<true>;
}

View File

@@ -1,158 +0,0 @@
#pragma once
#include <BAN/Assert.h>
#include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <stdint.h>
#include <stddef.h>
namespace BAN
{
template<typename T, size_t S>
class CircularQueue
{
public:
using size_type = size_t;
using value_type = T;
public:
CircularQueue() = default;
~CircularQueue();
void push(const T&);
void push(T&&);
template<typename... Args>
void emplace(Args&&... args) requires is_constructible_v<T, Args...>;
void pop();
const T& front() const;
T& front();
const T& back() const;
T& back();
const T& operator[](size_t index) const;
T& operator[](size_t index);
void clear();
size_type size() const { return m_size; }
bool empty() const { return size() == 0; }
bool full() const { return size() == capacity(); }
static constexpr size_type capacity() { return S; }
private:
T* element_at(size_type);
const T* element_at(size_type) const;
private:
alignas(T) uint8_t m_storage[sizeof(T) * capacity()];
size_type m_first { 0 };
size_type m_size { 0 };
};
template<typename T, size_t S>
CircularQueue<T, S>::~CircularQueue()
{
clear();
}
template<typename T, size_t S>
void CircularQueue<T, S>::push(const T& value)
{
emplace(BAN::move(T(value)));
}
template<typename T, size_t S>
void CircularQueue<T, S>::push(T&& value)
{
emplace(BAN::move(value));
}
template<typename T, size_t S>
template<typename... Args>
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(!full());
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
m_size++;
}
template<typename T, size_t S>
void CircularQueue<T, S>::pop()
{
ASSERT(!empty());
element_at(m_first)->~T();
m_first = (m_first + 1) % capacity();
m_size--;
}
template<typename T, size_t S>
const T& CircularQueue<T, S>::front() const
{
ASSERT(!empty());
return *element_at(m_first);
}
template<typename T, size_t S>
T& CircularQueue<T, S>::front()
{
ASSERT(!empty());
return *element_at(m_first);
}
template<typename T, size_t S>
const T& CircularQueue<T, S>::back() const
{
ASSERT(!empty());
return *element_at((m_first + m_size - 1) % capacity());
}
template<typename T, size_t S>
T& CircularQueue<T, S>::back()
{
ASSERT(!empty());
return *element_at((m_first + m_size - 1) % capacity());
}
template<typename T, size_t S>
const T& CircularQueue<T, S>::operator[](size_t index) const
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
T& CircularQueue<T, S>::operator[](size_t index)
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
void CircularQueue<T, S>::clear()
{
for (size_type i = 0; i < m_size; i++)
element_at((m_first + i) % capacity())->~T();
m_size = 0;
}
template<typename T, size_t S>
const T* CircularQueue<T, S>::element_at(size_type index) const
{
ASSERT(index < capacity());
return (const T*)(m_storage + index * sizeof(T));
}
template<typename T, size_t S>
T* CircularQueue<T, S>::element_at(size_type index)
{
ASSERT(index < capacity());
return (T*)(m_storage + index * sizeof(T));
}
}

View File

@@ -1,82 +0,0 @@
#pragma once
#if __is_kernel
#include <kernel/Debug.h>
#else
#include <BAN/Formatter.h>
#include <stdio.h>
#include <time.h>
#define __debug_putchar [](int c) { putc_unlocked(c, stddbg); }
inline uint64_t _ban_init_start_ms()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
inline uint64_t _ban_start_ms = _ban_init_start_ms();
#define __print_timestamp() \
do { \
timespec ts; \
clock_gettime(CLOCK_MONOTONIC, &ts); \
const auto ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000 - _ban_start_ms; \
BAN::Formatter::print(__debug_putchar, "[{}.{03}] ", ms / 1000, ms % 1000); \
} while (false)
#define dprintln(...) \
do { \
flockfile(stddbg); \
__print_timestamp(); \
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"); \
__print_timestamp(); \
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"); \
__print_timestamp(); \
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

View File

@@ -1,125 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN
{
template<integral T>
constexpr T swap_endianness(T value)
{
if constexpr(sizeof(T) == 1)
return value;
if constexpr(sizeof(T) == 2)
return (((value >> 8) & 0xFF) << 0)
| (((value >> 0) & 0xFF) << 8);
if constexpr(sizeof(T) == 4)
return (((value >> 24) & 0xFF) << 0)
| (((value >> 16) & 0xFF) << 8)
| (((value >> 8) & 0xFF) << 16)
| (((value >> 0) & 0xFF) << 24);
if constexpr(sizeof(T) == 8)
return (((value >> 56) & 0xFF) << 0)
| (((value >> 48) & 0xFF) << 8)
| (((value >> 40) & 0xFF) << 16)
| (((value >> 32) & 0xFF) << 24)
| (((value >> 24) & 0xFF) << 32)
| (((value >> 16) & 0xFF) << 40)
| (((value >> 8) & 0xFF) << 48)
| (((value >> 0) & 0xFF) << 56);
T result { 0 };
for (size_t i = 0; i < sizeof(T); i++)
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
return result;
}
template<integral T>
constexpr T host_to_little_endian(T value)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return value;
#else
return swap_endianness(value);
#endif
}
template<integral T>
constexpr T little_endian_to_host(T value)
{
return host_to_little_endian(value);
}
template<integral T>
constexpr T host_to_big_endian(T value)
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return value;
#else
return swap_endianness(value);
#endif
}
template<integral T>
constexpr T big_endian_to_host(T value)
{
return host_to_big_endian(value);
}
template<integral T>
struct LittleEndian
{
constexpr LittleEndian()
: raw(0)
{ }
constexpr LittleEndian(T value)
: raw(host_to_little_endian(value))
{ }
constexpr operator T() const
{
return host_to_little_endian(raw);
}
private:
T raw;
};
template<integral T>
struct BigEndian
{
constexpr BigEndian()
: raw(0)
{ }
constexpr BigEndian(T value)
: raw(host_to_big_endian(value))
{ }
constexpr operator T() const
{
return host_to_big_endian(raw);
}
private:
T raw;
};
template<integral T>
using NetworkEndian = BigEndian<T>;
template<integral T>
constexpr T host_to_network_endian(T value)
{
return host_to_big_endian(value);
}
template<integral T>
constexpr T network_endian_to_host(T value)
{
return big_endian_to_host(value);
}
}

View File

@@ -1,194 +0,0 @@
#pragma once
#include <BAN/Formatter.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Variant.h>
#include <errno.h>
#include <string.h>
#ifdef __is_kernel
#include <kernel/Panic.h>
#include <kernel/Errors.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
#else
#include <BAN/Debug.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } &e.release_value(); })
#endif
#define TRY(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); e.release_value(); })
#define TRY_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); &e.release_value(); })
namespace BAN
{
class Error
{
#ifdef __is_kernel
private:
static constexpr uint64_t kernel_error_mask = uint64_t(1) << 63;
#endif
public:
#ifdef __is_kernel
static Error from_error_code(Kernel::ErrorCode error)
{
return Error((uint64_t)error | kernel_error_mask);
}
#else
template<size_t N>
consteval static Error from_literal(const char (&message)[N])
{
return Error(message);
}
#endif
static Error from_errno(int error)
{
return Error(error);
}
#ifdef __is_kernel
Kernel::ErrorCode kernel_error() const
{
return (Kernel::ErrorCode)(m_error_code & ~kernel_error_mask);
}
bool is_kernel_error() const
{
return m_error_code & kernel_error_mask;
}
#endif
constexpr uint64_t get_error_code() const { return m_error_code; }
const char* get_message() const
{
#ifdef __is_kernel
if (m_error_code & kernel_error_mask)
return Kernel::error_string(kernel_error());
#else
if (m_message)
return m_message;
#endif
if (auto* desc = strerrordesc_np(m_error_code))
return desc;
return "Unknown error";
}
private:
constexpr Error(uint64_t error)
: m_error_code(error)
{}
#ifndef __is_kernel
constexpr Error(const char* message)
: m_message(message)
{}
#endif
uint64_t m_error_code { 0 };
#ifndef __is_kernel
const char* m_message { nullptr };
#endif
};
template<typename T>
class [[nodiscard]] ErrorOr
{
BAN_NON_COPYABLE(ErrorOr);
public:
ErrorOr(const T& value)
: m_data(value)
{}
ErrorOr(T&& value)
: m_data(move(value))
{}
ErrorOr(const Error& error)
: m_data(error)
{}
ErrorOr(Error&& error)
: m_data(move(error))
{}
ErrorOr(ErrorOr&& other)
: m_data(move(other.m_data))
{}
ErrorOr& operator=(ErrorOr&& other)
{
m_data = move(other.m_data);
return *this;
}
bool is_error() const { return m_data.template has<Error>(); }
const Error& error() const { return m_data.template get<Error>(); }
Error& error() { return m_data.template get<Error>(); }
const T& value() const { return m_data.template get<T>(); }
T& value() { return m_data.template get<T>(); }
Error release_error() { return move(error()); m_data.clear(); }
T release_value() { return move(value()); m_data.clear(); }
private:
Variant<Error, T> m_data;
};
template<lvalue_reference T>
class [[nodiscard]] ErrorOr<T>
{
public:
ErrorOr(T value)
{
m_data.template set<T>(value);
}
ErrorOr(Error&& error)
: m_data(move(error))
{ }
ErrorOr(const Error& error)
: m_data(error)
{ }
bool is_error() const { return m_data.template has<Error>(); }
Error& error() { return m_data.template get<Error>(); }
const Error& error() const { return m_data.template get<Error>(); }
T value() { return m_data.template get<T>(); }
Error release_error() { return move(error()); m_data.clear(); }
T release_value() { return value(); m_data.clear(); }
private:
Variant<Error, T> m_data;
};
template<>
class [[nodiscard]] ErrorOr<void>
{
public:
ErrorOr() {}
ErrorOr(const Error& error) : m_data(error), m_has_error(true) {}
ErrorOr(Error&& error) : m_data(move(error)), m_has_error(true) {}
bool is_error() const { return m_has_error; }
Error& error() { return m_data; }
const Error& error() const { return m_data; }
void value() { }
Error release_error() { return move(m_data); }
void release_value() { }
private:
Error m_data { Error::from_errno(0) };
bool m_has_error { false };
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const Error& error, const ValueFormat& format)
{
print_argument(putc, error.get_message(), format);
}
}

View File

@@ -1,257 +0,0 @@
#pragma once
#include <BAN/Move.h>
#include <stdint.h>
#include <stddef.h>
namespace BAN::Formatter
{
struct ValueFormat;
template<typename F, typename... Args>
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format)
{
(print_argument(putc, BAN::forward<Args>(args), format), ...);
};
template<typename F, typename T>
inline void print_argument(F putc, T value, const ValueFormat& format);
namespace detail
{
template<typename F, typename T>
inline size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
}
/*
IMPLEMENTATION
*/
struct ValueFormat
{
int base = 10;
int percision = 3;
int fill = 0;
char fill_char = '0';
bool upper = false;
};
template<typename F>
inline void print(F putc, const char* format)
{
while (*format)
{
putc(*format);
format++;
}
}
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...>
inline void print(F putc, const char* format, Arg&& arg, Args&&... args)
{
while (*format && *format != '{')
{
putc(*format);
format++;
}
if (*format == '{')
{
size_t arg_len = detail::parse_format_and_print_argument(putc, format, forward<Arg>(arg));
if (arg_len == size_t(-1))
return print(putc, format);
print(putc, format + arg_len, forward<Args>(args)...);
}
}
template<typename F, typename... Args>
inline void println(F putc, const char* format, Args&&... args)
{
print(putc, format, args...);
putc('\n');
}
namespace detail
{
template<typename F, typename Arg>
inline size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
{
ValueFormat value_format;
if (format[0] != '{')
return size_t(-1);
size_t i = 1;
do
{
if (!format[i] || format[i] == '}')
break;
if (format[i] == ' ')
{
value_format.fill_char = ' ';
i++;
}
if ('0' <= format[i] && format[i] <= '9')
{
int fill = 0;
while ('0' <= format[i] && format[i] <= '9')
{
fill = (fill * 10) + (format[i] - '0');
i++;
}
value_format.fill = fill;
}
switch (format[i])
{
case 'b': value_format.base = 2; value_format.upper = false; i++; break;
case 'B': value_format.base = 2; value_format.upper = true; i++; break;
case 'o': value_format.base = 8; value_format.upper = false; i++; break;
case 'O': value_format.base = 8; value_format.upper = true; i++; break;
case 'd': value_format.base = 10; value_format.upper = false; i++; break;
case 'D': value_format.base = 10; value_format.upper = true; i++; break;
case 'h': value_format.base = 16; value_format.upper = false; i++; break;
case 'H': value_format.base = 16; value_format.upper = true; i++; break;
default: break;
}
if (!format[i] || format[i] == '}')
break;
if (format[i] == '.')
{
i++;
int percision = 0;
while ('0' <= format[i] && format[i] <= '9')
{
percision = (percision * 10) + (format[i] - '0');
i++;
}
value_format.percision = percision;
}
} while(false);
if (format[i] != '}')
return size_t(-1);
print_argument(putc, forward<Arg>(argument), value_format);
return i + 1;
}
inline char value_to_base_char(uint8_t value, int base, bool upper)
{
if (base <= 10)
return value + '0';
if (base <= 36)
{
if (value < 10)
return value + '0';
return value + (upper ? 'A' : 'a') - 10;
}
return '?';
}
template<typename F, typename T>
inline void print_integer(F putc, T value, const ValueFormat& format)
{
if (value == 0)
{
for (int i = 0; i < format.fill - 1; i++)
putc(format.fill_char);
putc('0');
return;
}
bool sign = false;
// Fits signed 64-bit binary number and null
char buffer[66];
char* ptr = buffer + sizeof(buffer);
*(--ptr) = '\0';
if (value < 0)
{
sign = true;
T digit = (format.base - (value % format.base)) % format.base;
*(--ptr) = value_to_base_char(digit, format.base, format.upper);
value = -(value / format.base);
}
while (value)
{
*(--ptr) = value_to_base_char(value % format.base, format.base, format.upper);
value /= format.base;
}
while (ptr >= buffer + sizeof(buffer) - format.fill)
*(--ptr) = format.fill_char;
if (sign)
*(--ptr) = '-';
print(putc, ptr);
}
template<typename F, typename T>
inline void print_floating(F putc, T value, const ValueFormat& format)
{
if (value < 0)
{
putc('-');
return print_floating(putc, -value, format);
}
int64_t int_part = (int64_t)value;
T frac_part = value - (T)int_part;
print_integer(putc, int_part, format);
if (format.percision > 0)
putc('.');
for (int i = 0; i < format.percision; i++)
{
frac_part *= format.base;
if (i == format.percision - 1)
frac_part += 0.5;
putc(value_to_base_char((uint8_t)frac_part % format.base, format.base, format.upper));
}
}
template<typename F>
inline void print_pointer(F putc, void* ptr, const ValueFormat& format)
{
uintptr_t value = (uintptr_t)ptr;
print(putc, "0x");
for (int i = sizeof(void*) * 8 - 4; i >= 0; i -= 4)
putc(value_to_base_char((value >> i) & 0xF, 16, format.upper));
}
}
/*
TEMPLATE SPECIALIZATIONS
*/
template<typename F, integral T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
template<typename F, floating_point T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
template<typename F, pointer T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
template<typename F> inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
template<typename F> inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
template<typename F> inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
template<typename F> inline void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN
{
template<typename, size_t> class Array;
template<typename> class ErrorOr;
template<typename> class Function;
template<typename> class Queue;
class String;
class StringView;
template<typename> class Vector;
template<typename> class LinkedList;
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
}

View File

@@ -1,148 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Move.h>
#include <BAN/PlacementNew.h>
namespace BAN
{
template<typename>
class Function;
template<typename Ret, typename... Args>
class Function<Ret(Args...)>
{
public:
Function() = default;
Function(Ret(*function)(Args...))
{
static_assert(sizeof(CallablePointer) <= m_size);
new (m_storage) CallablePointer(function);
}
template<typename Own>
Function(Ret(Own::*function)(Args...), Own& owner)
{
static_assert(sizeof(CallableMember<Own>) <= m_size);
new (m_storage) CallableMember<Own>(function, owner);
}
template<typename Own>
Function(Ret(Own::*function)(Args...) const, const Own& owner)
{
static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
new (m_storage) CallableMemberConst<Own>(function, owner);
}
template<typename Lambda>
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; }
{
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
new (m_storage) CallableLambda<Lambda>(lambda);
}
~Function()
{
clear();
}
Ret operator()(Args... args) const
{
ASSERT(*this);
return reinterpret_cast<const CallableBase*>(m_storage)->call(forward<Args>(args)...);
}
operator bool() const
{
for (size_t i = 0; i < m_size; i++)
if (m_storage[i])
return true;
return false;
}
void clear()
{
if (*this)
reinterpret_cast<CallableBase*>(m_storage)->~CallableBase();
memset(m_storage, 0, m_size);
}
static constexpr size_t size() { return m_size; }
private:
struct CallableBase
{
virtual ~CallableBase() {}
virtual Ret call(Args...) const = 0;
};
struct CallablePointer : public CallableBase
{
CallablePointer(Ret(*function)(Args...))
: m_function(function)
{ }
virtual Ret call(Args... args) const override
{
return m_function(forward<Args>(args)...);
}
private:
Ret(*m_function)(Args...) = nullptr;
};
template<typename Own>
struct CallableMember : public CallableBase
{
CallableMember(Ret(Own::*function)(Args...), Own& owner)
: m_owner(owner)
, m_function(function)
{ }
virtual Ret call(Args... args) const override
{
return (m_owner.*m_function)(forward<Args>(args)...);
}
private:
Own& m_owner;
Ret(Own::*m_function)(Args...) = nullptr;
};
template<typename Own>
struct CallableMemberConst : public CallableBase
{
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner)
: m_owner(owner)
, m_function(function)
{ }
virtual Ret call(Args... args) const override
{
return (m_owner.*m_function)(forward<Args>(args)...);
}
private:
const Own& m_owner;
Ret(Own::*m_function)(Args...) const = nullptr;
};
template<typename Lambda>
struct CallableLambda : public CallableBase
{
CallableLambda(Lambda lambda)
: m_lambda(lambda)
{ }
virtual Ret call(Args... args) const override
{
return m_lambda(forward<Args>(args)...);
}
private:
Lambda m_lambda;
};
private:
static constexpr size_t m_size = sizeof(void*) * 8;
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
};
}

View File

@@ -1,73 +0,0 @@
#pragma once
#include <BAN/Optional.h>
#include <BAN/String.h>
#include <string.h>
namespace BAN
{
struct GUID
{
uint32_t component1 { 0 };
uint16_t component2 { 0 };
uint16_t component3 { 0 };
uint8_t component45[8] { };
bool operator==(const GUID& other) const
{
return memcmp(this, &other, sizeof(GUID)) == 0;
}
BAN::ErrorOr<BAN::String> to_string() const
{
char buffer[37];
char* ptr = buffer;
const auto append_hex_nibble =
[&ptr](uint8_t nibble)
{
if (nibble < 10)
*ptr++ = '0' + nibble;
else
*ptr++ = 'A' + nibble - 10;
};
const auto append_hex_byte =
[&append_hex_nibble](uint8_t byte)
{
append_hex_nibble(byte >> 4);
append_hex_nibble(byte & 0xF);
};
append_hex_byte((component1 >> 24) & 0xFF);
append_hex_byte((component1 >> 16) & 0xFF);
append_hex_byte((component1 >> 8) & 0xFF);
append_hex_byte((component1 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte((component2 >> 8) & 0xFF);
append_hex_byte((component2 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte((component3 >> 8) & 0xFF);
append_hex_byte((component3 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte(component45[0]);
append_hex_byte(component45[1]);
*ptr++ = '-';
append_hex_byte(component45[2]);
append_hex_byte(component45[3]);
append_hex_byte(component45[4]);
append_hex_byte(component45[5]);
append_hex_byte(component45[6]);
append_hex_byte(component45[7]);
*ptr = '\0';
BAN::String guid;
TRY(guid.append(buffer));
return BAN::move(guid);
}
};
static_assert(sizeof(GUID) == 16);
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stddef.h>
#include <stdint.h>
namespace BAN
{
template<typename T>
struct hash;
using hash_t = uint32_t;
inline constexpr hash_t u32_hash(uint32_t val)
{
val = ((val >> 16) ^ val) * 0x119de1f3;
val = ((val >> 16) ^ val) * 0x119de1f3;
val = ((val >> 16) ^ val);
return val;
}
inline constexpr hash_t u64_hash(uint64_t val)
{
hash_t low = u32_hash(val);
hash_t high = u32_hash(val >> 32);
return low ^ high;
}
template<integral T>
struct hash<T>
{
constexpr hash_t operator()(T val) const
{
if constexpr(sizeof(val) <= sizeof(uint32_t))
return u32_hash(val);
return u64_hash(val);
}
};
template<pointer T>
struct hash<T>
{
constexpr hash_t operator()(T val) const
{
return hash<uintptr_t>()((uintptr_t)val);
}
};
}

View File

@@ -1,319 +0,0 @@
#pragma once
#include <BAN/Hash.h>
#include <BAN/LinkedList.h>
#include <BAN/Vector.h>
namespace BAN
{
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
class HashMap
{
public:
struct Entry
{
template<typename... Args>
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
: key(key)
, value(forward<Args>(args)...)
{}
template<typename... Args>
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
: key(BAN::move(key))
, value(forward<Args>(args)...)
{}
Key key;
T value;
};
public:
using size_type = size_t;
using key_type = Key;
using value_type = T;
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
public:
HashMap() = default;
HashMap(const HashMap<Key, T, HASH>&);
HashMap(HashMap<Key, T, HASH>&&);
~HashMap();
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
template<typename... Args>
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace(Key(key), forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
ErrorOr<void> reserve(size_type);
void remove(const Key&);
void remove(iterator it);
void clear();
T& operator[](const Key&);
const T& operator[](const Key&) const;
iterator find(const Key& key);
const_iterator find(const Key& key) const;
bool contains(const Key&) const;
bool empty() const;
size_type size() const;
private:
ErrorOr<void> rebucket(size_type);
LinkedList<Entry>& get_bucket(const Key&);
const LinkedList<Entry>& get_bucket(const Key&) const;
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
private:
Vector<LinkedList<Entry>> m_buckets;
size_type m_size = 0;
friend iterator;
};
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
{
*this = other;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
{
*this = move(other);
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::~HashMap()
{
clear();
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
{
clear();
m_buckets = other.m_buckets;
m_size = other.m_size;
return *this;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
{
clear();
m_buckets = move(other.m_buckets);
m_size = other.m_size;
other.m_size = 0;
return *this;
}
template<typename Key, typename T, typename HASH>
template<typename... Args>
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(!contains(key));
TRY(rebucket(m_size + 1));
auto bucket_it = get_bucket_iterator(key);
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
m_size++;
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
}
template<typename Key, typename T, typename HASH>
template<typename... Args>
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
if (empty())
return emplace(move(key), forward<Args>(args)...);
auto bucket_it = get_bucket_iterator(key);
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++)
{
if (entry_it->key != key)
continue;
entry_it->value = T(forward<Args>(args)...);
return iterator(m_buckets.end(), bucket_it, entry_it);
}
return emplace(move(key), forward<Args>(args)...);
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
{
TRY(rebucket(size));
return {};
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(const Key& key)
{
auto it = find(key);
if (it != end())
remove(it);
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(iterator it)
{
it.outer_current()->remove(it.inner_current());
m_size--;
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::clear()
{
m_buckets.clear();
m_size = 0;
}
template<typename Key, typename T, typename HASH>
T& HashMap<Key, T, HASH>::operator[](const Key& key)
{
ASSERT(!empty());
auto& bucket = get_bucket(key);
for (Entry& entry : bucket)
if (entry.key == key)
return entry.value;
ASSERT_NOT_REACHED();
}
template<typename Key, typename T, typename HASH>
const T& HashMap<Key, T, HASH>::operator[](const Key& key) const
{
ASSERT(!empty());
const auto& bucket = get_bucket(key);
for (const Entry& entry : bucket)
if (entry.key == key)
return entry.value;
ASSERT_NOT_REACHED();
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return iterator(m_buckets.end(), bucket_it, it);
return end();
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return const_iterator(m_buckets.end(), bucket_it, it);
return end();
}
template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::contains(const Key& key) const
{
return find(key) != end();
}
template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::empty() const
{
return m_size == 0;
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
{
return m_size;
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
{
if (m_buckets.size() >= bucket_count)
return {};
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<Entry>> new_buckets;
TRY(new_buckets.resize(new_bucket_count));
for (auto& bucket : m_buckets)
{
for (auto it = bucket.begin(); it != bucket.end();)
{
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
}
}
m_buckets = move(new_buckets);
return {};
}
template<typename Key, typename T, typename HASH>
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
}

View File

@@ -1,199 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Hash.h>
#include <BAN/Iterators.h>
#include <BAN/LinkedList.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/Vector.h>
namespace BAN
{
template<typename T, typename HASH = hash<T>>
class HashSet
{
public:
using value_type = T;
using size_type = size_t;
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
public:
HashSet() = default;
HashSet(const HashSet&);
HashSet(HashSet&&);
HashSet& operator=(const HashSet&);
HashSet& operator=(HashSet&&);
ErrorOr<void> insert(const T&);
ErrorOr<void> insert(T&&);
void remove(const T&);
void clear();
ErrorOr<void> reserve(size_type);
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
bool contains(const T&) const;
size_type size() const;
bool empty() const;
private:
ErrorOr<void> rebucket(size_type);
LinkedList<T>& get_bucket(const T&);
const LinkedList<T>& get_bucket(const T&) const;
private:
Vector<LinkedList<T>> m_buckets;
size_type m_size = 0;
};
template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(const HashSet& other)
: m_buckets(other.m_buckets)
, m_size(other.m_size)
{
}
template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(HashSet&& other)
: m_buckets(move(other.m_buckets))
, m_size(other.m_size)
{
other.clear();
}
template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
{
clear();
m_buckets = other.m_buckets;
m_size = other.m_size;
return *this;
}
template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
{
clear();
m_buckets = move(other.m_buckets);
m_size = other.m_size;
other.clear();
return *this;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
{
return insert(move(T(key)));
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
{
if (!empty() && get_bucket(key).contains(key))
return {};
TRY(rebucket(m_size + 1));
TRY(get_bucket(key).push_back(move(key)));
m_size++;
return {};
}
template<typename T, typename HASH>
void HashSet<T, HASH>::remove(const T& key)
{
if (empty()) return;
auto& bucket = get_bucket(key);
for (auto it = bucket.begin(); it != bucket.end(); it++)
{
if (*it == key)
{
bucket.remove(it);
m_size--;
break;
}
}
}
template<typename T, typename HASH>
void HashSet<T, HASH>::clear()
{
m_buckets.clear();
m_size = 0;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
{
TRY(rebucket(size));
return {};
}
template<typename T, typename HASH>
bool HashSet<T, HASH>::contains(const T& key) const
{
if (empty()) return false;
return get_bucket(key).contains(key);
}
template<typename T, typename HASH>
typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
{
return m_size;
}
template<typename T, typename HASH>
bool HashSet<T, HASH>::empty() const
{
return m_size == 0;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
{
if (m_buckets.size() >= bucket_count)
return {};
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<T>> new_buckets;
if (new_buckets.resize(new_bucket_count).is_error())
return Error::from_errno(ENOMEM);
for (auto& bucket : m_buckets)
{
for (auto it = bucket.begin(); it != bucket.end();)
{
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
}
}
m_buckets = move(new_buckets);
return {};
}
template<typename T, typename HASH>
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
{
ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size();
return m_buckets[index];
}
template<typename T, typename HASH>
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
{
ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size();
return m_buckets[index];
}
}

View File

@@ -1,89 +0,0 @@
#pragma once
#include <BAN/Iterators.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
namespace BAN
{
namespace detail
{
template<typename It, typename Comp>
void heapify_up(It begin, size_t index, Comp comp)
{
size_t parent = (index - 1) / 2;
while (parent < index)
{
if (comp(*(begin + index), *(begin + parent)))
break;
swap(*(begin + parent), *(begin + index));
index = parent;
parent = (index - 1) / 2;
}
}
template<typename It, typename Comp>
void heapify_down(It begin, size_t index, size_t len, Comp comp)
{
for (;;)
{
const size_t lchild = 2 * index + 1;
const size_t rchild = 2 * index + 2;
size_t child = 0;
if (lchild < len && !comp(*(begin + lchild), *(begin + index)))
{
if (rchild < len && !comp(*(begin + rchild), *(begin + lchild)))
child = rchild;
else
child = lchild;
}
else if (rchild < len && !comp(*(begin + rchild), *(begin + index)))
child = rchild;
else
break;
swap(*(begin + child), *(begin + index));
index = child;
}
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void make_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t index = (len - 2) / 2;
while (index < len)
detail::heapify_down(begin, index--, len, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void push_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
detail::heapify_up(begin, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void pop_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
swap(*begin, *(begin + len - 1));
detail::heapify_down(begin, 0, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void sort_heap(It begin, It end, Comp comp = {})
{
while (begin != end)
pop_heap(begin, end--, comp);
}
}

View File

@@ -1,75 +0,0 @@
#pragma once
#include <BAN/Endianness.h>
#include <BAN/Formatter.h>
#include <BAN/Hash.h>
namespace BAN
{
struct IPv4Address
{
constexpr IPv4Address(uint32_t u32_address)
{
raw = u32_address;
}
constexpr IPv4Address(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4)
{
octets[0] = oct1;
octets[1] = oct2;
octets[2] = oct3;
octets[3] = oct4;
}
constexpr bool operator==(const IPv4Address& other) const
{
return raw == other.raw;
}
constexpr IPv4Address mask(const IPv4Address& other) const
{
return IPv4Address(raw & other.raw);
}
union
{
uint8_t octets[4];
uint32_t raw;
} __attribute__((packed));
};
static_assert(sizeof(IPv4Address) == 4);
template<>
struct hash<IPv4Address>
{
constexpr hash_t operator()(IPv4Address ipv4) const
{
return hash<uint32_t>()(ipv4.raw);
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const IPv4Address& ipv4, const ValueFormat&)
{
ValueFormat format {
.base = 10,
.percision = 0,
.fill = 0,
.upper = false,
};
print_argument(putc, ipv4.octets[0], format);
for (size_t i = 1; i < 4; i++)
{
putc('.');
print_argument(putc, ipv4.octets[i], format);
}
}
}

View File

@@ -1,12 +0,0 @@
#pragma once
namespace BAN
{
enum class Iteration
{
Continue,
Break
};
}

View File

@@ -1,330 +0,0 @@
#pragma once
#include <BAN/Assert.h>
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN
{
template<typename It>
constexpr It next(It it, size_t count)
{
for (size_t i = 0; i < count; i++)
++it;
return it;
}
template<typename It>
requires requires(It it, size_t n) { requires is_same_v<decltype(it + n), It>; }
constexpr It next(It it, size_t count)
{
return it + count;
}
template<typename It>
constexpr It prev(It it, size_t count)
{
for (size_t i = 0; i < count; i++)
--it;
return it;
}
template<typename It>
requires requires(It it, size_t n) { requires is_same_v<decltype(it - n), It>; }
constexpr It prev(It it, size_t count)
{
return it - count;
}
template<typename It>
constexpr size_t distance(It it1, It it2)
{
size_t dist = 0;
while (it1 != it2)
{
++it1;
++dist;
}
return dist;
}
template<typename It>
requires requires(It it1, It it2) { requires is_integral_v<decltype(it2 - it1)>; }
constexpr size_t distance(It it1, It it2)
{
return it2 - it1;
}
template<typename T, typename Container, bool CONST>
class IteratorSimpleGeneral
{
public:
using value_type = T;
public:
constexpr IteratorSimpleGeneral() = default;
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
: m_pointer(other.m_pointer)
, m_valid(other.m_valid)
{
}
constexpr const T& operator*() const
{
ASSERT(m_pointer);
return *m_pointer;
}
template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T&> operator*()
{
ASSERT(*this);
ASSERT(m_pointer);
return *m_pointer;
}
constexpr const T* operator->() const
{
ASSERT(*this);
ASSERT(m_pointer);
return m_pointer;
}
template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T*> operator->()
{
ASSERT(*this);
ASSERT(m_pointer);
return m_pointer;
}
constexpr IteratorSimpleGeneral& operator++()
{
ASSERT(*this);
ASSERT(m_pointer);
++m_pointer;
return *this;
}
constexpr IteratorSimpleGeneral operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
constexpr IteratorSimpleGeneral& operator--()
{
ASSERT(*this);
ASSERT(m_pointer);
--m_pointer;
return *this;
}
constexpr IteratorSimpleGeneral operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
constexpr size_t operator-(const IteratorSimpleGeneral& other) const
{
ASSERT(*this && other);
return m_pointer - other.m_pointer;
}
constexpr IteratorSimpleGeneral operator+(size_t offset) const
{
return IteratorSimpleGeneral(m_pointer + offset);
}
constexpr IteratorSimpleGeneral operator-(size_t offset) const
{
return IteratorSimpleGeneral(m_pointer - offset);
}
constexpr bool operator<(const IteratorSimpleGeneral& other) const
{
ASSERT(*this);
return m_pointer < other.m_pointer;
}
constexpr bool operator==(const IteratorSimpleGeneral& other) const
{
ASSERT(*this);
return m_pointer == other.m_pointer;
}
constexpr bool operator!=(const IteratorSimpleGeneral& other) const
{
ASSERT(*this);
return !(*this == other);
}
constexpr explicit operator bool() const
{
return m_valid;
}
private:
constexpr IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
: m_pointer(pointer)
, m_valid(true)
{
}
private:
maybe_const_t<CONST, T>* m_pointer = nullptr;
bool m_valid = false;
friend IteratorSimpleGeneral<T, Container, !CONST>;
friend Container;
};
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container, bool CONST>
class IteratorDoubleGeneral
{
public:
using Inner = InnerContainer<T>;
using Outer = OuterContainer<Inner>;
using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>;
using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>;
using value_type = T;
public:
constexpr IteratorDoubleGeneral() = default;
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
: m_outer_end(other.m_outer_end)
, m_outer_current(other.m_outer_current)
, m_inner_current(other.m_inner_current)
{
}
constexpr const T& operator*() const
{
ASSERT(*this);
ASSERT(m_outer_current != m_outer_end);
ASSERT(m_inner_current);
return m_inner_current.operator*();
}
template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T&> operator*()
{
ASSERT(*this);
ASSERT(m_outer_current != m_outer_end);
ASSERT(m_inner_current);
return m_inner_current.operator*();
}
constexpr const T* operator->() const
{
ASSERT(*this);
ASSERT(m_outer_current != m_outer_end);
ASSERT(m_inner_current);
return m_inner_current.operator->();
}
template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T*> operator->()
{
ASSERT(*this);
ASSERT(m_outer_current != m_outer_end);
ASSERT(m_inner_current);
return m_inner_current.operator->();
}
constexpr IteratorDoubleGeneral& operator++()
{
ASSERT(*this);
ASSERT(m_outer_current != m_outer_end);
ASSERT(m_inner_current);
m_inner_current++;
find_valid_or_end();
return *this;
}
constexpr IteratorDoubleGeneral operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
constexpr bool operator==(const IteratorDoubleGeneral& other) const
{
ASSERT(*this && other);
if (m_outer_end != other.m_outer_end)
return false;
if (m_outer_current != other.m_outer_current)
return false;
if (m_outer_current == m_outer_end)
return true;
ASSERT(m_inner_current && other.m_inner_current);
return m_inner_current == other.m_inner_current;
}
constexpr bool operator!=(const IteratorDoubleGeneral& other) const
{
return !(*this == other);
}
constexpr explicit operator bool() const
{
return !!m_outer_current;
}
private:
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
: m_outer_end(outer_end)
, m_outer_current(outer_current)
{
if (outer_current != outer_end)
{
m_inner_current = m_outer_current->begin();
find_valid_or_end();
}
}
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current, const InnerIterator& inner_current)
: m_outer_end(outer_end)
, m_outer_current(outer_current)
, m_inner_current(inner_current)
{
find_valid_or_end();
}
constexpr void find_valid_or_end()
{
while (m_inner_current == m_outer_current->end())
{
m_outer_current++;
if (m_outer_current == m_outer_end)
break;
m_inner_current = m_outer_current->begin();
}
}
constexpr OuterIterator outer_current() { return m_outer_current; }
constexpr InnerIterator inner_current() { return m_inner_current; }
private:
OuterIterator m_outer_end;
OuterIterator m_outer_current;
InnerIterator m_inner_current;
friend class IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, !CONST>;
friend Container;
};
template<typename T, typename Container>
using IteratorSimple = IteratorSimpleGeneral<T, Container, false>;
template<typename T, typename Container>
using ConstIteratorSimple = IteratorSimpleGeneral<T, Container, true>;
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
using IteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, false>;
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
using ConstIteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, true>;
}

View File

@@ -1,156 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stdint.h>
namespace BAN
{
template<typename T>
class numeric_limits
{
public:
numeric_limits() = delete;
static inline constexpr T max()
{
if constexpr(is_same_v<T, char>)
return __SCHAR_MAX__;
if constexpr(is_same_v<T, signed char>)
return __SCHAR_MAX__;
if constexpr(is_same_v<T, unsigned char>)
return (T)__SCHAR_MAX__ * 2 + 1;
if constexpr(is_same_v<T, short>)
return __SHRT_MAX__;
if constexpr(is_same_v<T, int>)
return __INT_MAX__;
if constexpr(is_same_v<T, long>)
return __LONG_MAX__;
if constexpr(is_same_v<T, long long>)
return __LONG_LONG_MAX__;
if constexpr(is_same_v<T, unsigned short>)
return (T)__SHRT_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned int>)
return (T)__INT_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned long>)
return (T)__LONG_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned long long>)
return (T)__LONG_LONG_MAX__ * 2 + 1;
if constexpr(is_same_v<T, float>)
return __FLT_MAX__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX__;
}
static inline constexpr T min()
{
if constexpr(is_signed_v<T> && is_integral_v<T>)
return -max() - 1;
if constexpr(is_unsigned_v<T> && is_integral_v<T>)
return 0;
if constexpr(is_same_v<T, float>)
return __FLT_MIN__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN__;
}
static inline constexpr bool has_infinity()
{
if constexpr(is_same_v<T, float>)
return __FLT_HAS_INFINITY__;
if constexpr(is_same_v<T, double>)
return __DBL_HAS_INFINITY__;
if constexpr(is_same_v<T, long double>)
return __LDBL_HAS_INFINITY__;
return false;
}
static inline constexpr T infinity() requires(has_infinity())
{
if constexpr(is_same_v<T, float>)
return __builtin_inff();
if constexpr(is_same_v<T, double>)
return __builtin_inf();
if constexpr(is_same_v<T, long double>)
return __builtin_infl();
}
static inline constexpr bool has_quiet_NaN()
{
if constexpr(is_same_v<T, float>)
return __FLT_HAS_QUIET_NAN__;
if constexpr(is_same_v<T, double>)
return __DBL_HAS_QUIET_NAN__;
if constexpr(is_same_v<T, long double>)
return __LDBL_HAS_QUIET_NAN__;
return false;
}
static inline constexpr T quiet_NaN() requires(has_quiet_NaN())
{
if constexpr(is_same_v<T, float>)
return __builtin_nanf("");
if constexpr(is_same_v<T, double>)
return __builtin_nan("");
if constexpr(is_same_v<T, long double>)
return __builtin_nanl("");
}
static inline constexpr int max_exponent2()
{
static_assert(__FLT_RADIX__ == 2);
if constexpr(is_same_v<T, float>)
return __FLT_MAX_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX_EXP__;
return 0;
}
static inline constexpr int max_exponent10()
{
if constexpr(is_same_v<T, float>)
return __FLT_MAX_10_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX_10_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX_10_EXP__;
return 0;
}
static inline constexpr int min_exponent2()
{
static_assert(__FLT_RADIX__ == 2);
if constexpr(is_same_v<T, float>)
return __FLT_MIN_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN_EXP__;
return 0;
}
static inline constexpr int min_exponent10()
{
if constexpr(is_same_v<T, float>)
return __FLT_MIN_10_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN_10_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN_10_EXP__;
return 0;
}
};
}

View File

@@ -1,426 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/PlacementNew.h>
namespace BAN
{
template<typename T, bool CONST>
class LinkedListIterator;
template<typename T>
class LinkedList
{
public:
using size_type = size_t;
using value_type = T;
using iterator = LinkedListIterator<T, false>;
using const_iterator = LinkedListIterator<T, true>;
public:
LinkedList() = default;
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
LinkedList(LinkedList<T>&& other) { *this = move(other); }
~LinkedList() { clear(); }
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
LinkedList<T>& operator=(LinkedList<T>&&);
ErrorOr<void> push_back(const T&);
ErrorOr<void> push_back(T&&);
ErrorOr<void> insert(iterator, const T&);
ErrorOr<void> insert(iterator, T&&);
template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
template<typename... Args>
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>;
void pop_back();
iterator remove(iterator);
void clear();
iterator move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter);
iterator begin() { return iterator(m_data, empty()); }
const_iterator begin() const { return const_iterator(m_data, empty()); }
iterator end() { return iterator(m_last, true); }
const_iterator end() const { return const_iterator(m_last, true); }
const T& back() const;
T& back();
const T& front() const;
T& front();
bool contains(const T&) const;
size_type size() const;
bool empty() const;
private:
struct Node
{
T value;
Node* next;
Node* prev;
};
template<typename... Args>
ErrorOr<Node*> allocate_node(Args&&...) const;
Node* remove_node(iterator);
void insert_node(iterator, Node*);
Node* m_data = nullptr;
Node* m_last = nullptr;
size_type m_size = 0;
friend class LinkedListIterator<T, true>;
friend class LinkedListIterator<T, false>;
};
template<typename T, bool CONST>
class LinkedListIterator
{
public:
using value_type = T;
using data_type = maybe_const_t<CONST, typename LinkedList<T>::Node>;
public:
LinkedListIterator() = default;
template<bool C>
LinkedListIterator(const LinkedListIterator<T, C>&, enable_if_t<C == CONST || !C>* = 0);
LinkedListIterator<T, CONST>& operator++();
LinkedListIterator<T, CONST>& operator--();
LinkedListIterator<T, CONST> operator++(int);
LinkedListIterator<T, CONST> operator--(int);
template<bool ENABLE = !CONST>
enable_if_t<ENABLE, T&> operator*();
const T& operator*() const;
template<bool ENABLE = !CONST>
enable_if_t<ENABLE, T*> operator->();
const T* operator->() const;
bool operator==(const LinkedListIterator<T, CONST>&) const;
bool operator!=(const LinkedListIterator<T, CONST>&) const;
operator bool() const;
private:
LinkedListIterator(data_type*, bool);
private:
data_type* m_current = nullptr;
bool m_past_end = false;
friend class LinkedList<T>;
friend class LinkedListIterator<T, !CONST>;
};
template<typename T>
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
{
clear();
for (const T& elem : other)
MUST(push_back(elem));
return *this;
}
template<typename T>
LinkedList<T>& LinkedList<T>::operator=(LinkedList<T>&& other)
{
clear();
m_data = other.m_data;
m_last = other.m_last;
m_size = other.m_size;
other.m_data = nullptr;
other.m_last = nullptr;
other.m_size = 0;
return *this;
}
template<typename T>
LinkedList<T>::Node* LinkedList<T>::remove_node(iterator iter)
{
ASSERT(!empty() && iter);
Node* node = iter.m_current;
Node* prev = node->prev;
Node* next = node->next;
(prev ? prev->next : m_data) = next;
(next ? next->prev : m_last) = prev;
m_size--;
return node;
}
template<typename T>
void LinkedList<T>::insert_node(iterator iter, Node* node)
{
Node* next = iter.m_past_end ? nullptr : iter.m_current;
Node* prev = next ? next->prev : m_last;
node->next = next;
node->prev = prev;
(prev ? prev->next : m_data) = node;
(next ? next->prev : m_last) = node;
m_size++;
}
template<typename T>
ErrorOr<void> LinkedList<T>::push_back(const T& value)
{
return push_back(move(T(value)));
}
template<typename T>
ErrorOr<void> LinkedList<T>::push_back(T&& value)
{
return insert(end(), move(value));
}
template<typename T>
ErrorOr<void> LinkedList<T>::insert(iterator iter, const T& value)
{
return insert(iter, move(T(value)));
}
template<typename T>
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
{
Node* new_node = TRY(allocate_node(move(value)));
insert_node(iter, new_node);
return {};
}
template<typename T>
template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
{
return emplace(end(), forward<Args>(args)...);
}
template<typename T>
template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...>
{
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
insert_node(iter, new_node);
return {};
}
template<typename T>
void LinkedList<T>::pop_back()
{
ASSERT(!empty());
remove(iterator(m_last, false));
}
template<typename T>
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
{
ASSERT(!empty() && iter);
Node* node = remove_node(iter);
Node* next = node->next;
node->value.~T();
BAN::deallocator(node);
return next ? iterator(next, false) : iterator(m_last, true);
}
template<typename T>
void LinkedList<T>::clear()
{
Node* ptr = m_data;
while (ptr)
{
Node* next = ptr->next;
ptr->value.~T();
BAN::deallocator(ptr);
ptr = next;
}
m_data = nullptr;
m_last = nullptr;
m_size = 0;
}
template<typename T>
LinkedList<T>::iterator LinkedList<T>::move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter)
{
ASSERT(!empty() && src_iter);
Node* node = remove_node(src_iter);
iterator ret = node->next ? iterator(node->next, false) : iterator(m_last, true);
dest_list.insert_node(dest_iter, node);
return ret;
}
template<typename T>
const T& LinkedList<T>::back() const
{
ASSERT(!empty());
return *const_iterator(m_last, false);
}
template<typename T>
T& LinkedList<T>::back()
{
ASSERT(!empty());
return *iterator(m_last, false);
}
template<typename T>
const T& LinkedList<T>::front() const
{
ASSERT(!empty());
return *const_iterator(m_data, false);
}
template<typename T>
T& LinkedList<T>::front()
{
ASSERT(!empty());
return *iterator(m_data, false);
}
template<typename T>
bool LinkedList<T>::contains(const T& value) const
{
if (empty()) return false;
for (Node* node = m_data;; node = node->next)
{
if (node->value == value)
return true;
if (node == m_last)
return false;
}
}
template<typename T>
typename LinkedList<T>::size_type LinkedList<T>::size() const
{
return m_size;
}
template<typename T>
bool LinkedList<T>::empty() const
{
return m_size == 0;
}
template<typename T>
template<typename... Args>
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
{
Node* node = (Node*)BAN::allocator(sizeof(Node));
if (node == nullptr)
return Error::from_errno(ENOMEM);
new (&node->value) T(forward<Args>(args)...);
return node;
}
template<typename T, bool CONST>
template<bool C>
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
: m_current(other.m_current)
, m_past_end(other.m_past_end)
{
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST>::LinkedListIterator(data_type* node, bool past_end)
: m_current(node)
, m_past_end(past_end)
{
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST>& LinkedListIterator<T, CONST>::operator++()
{
ASSERT(m_current);
ASSERT(m_current->next || !m_past_end);
if (m_current->next)
m_current = m_current->next;
else
m_past_end = true;
return *this;
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST>& LinkedListIterator<T, CONST>::operator--()
{
ASSERT(m_current);
ASSERT(m_current->prev || m_past_end);
if (m_past_end)
m_past_end = false;
else
m_current = m_current->prev;
return *this;
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST> LinkedListIterator<T, CONST>::operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST> LinkedListIterator<T, CONST>::operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
template<typename T, bool CONST>
template<bool ENABLE>
enable_if_t<ENABLE, T&> LinkedListIterator<T, CONST>::operator*()
{
ASSERT(m_current);
return m_current->value;
}
template<typename T, bool CONST>
const T& LinkedListIterator<T, CONST>::operator*() const
{
ASSERT(m_current);
return m_current->value;
}
template<typename T, bool CONST>
template<bool ENABLE>
enable_if_t<ENABLE, T*> LinkedListIterator<T, CONST>::operator->()
{
ASSERT(m_current);
return &m_current->value;
}
template<typename T, bool CONST>
const T* LinkedListIterator<T, CONST>::operator->() const
{
ASSERT(m_current);
return &m_current->value;
}
template<typename T, bool CONST>
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
{
if (m_current != other.m_current)
return false;
return m_past_end == other.m_past_end;
}
template<typename T, bool CONST>
bool LinkedListIterator<T, CONST>::operator!=(const LinkedListIterator<T, CONST>& other) const
{
return !(*this == other);
}
template<typename T, bool CONST>
LinkedListIterator<T, CONST>::operator bool() const
{
return m_current;
}
}

View File

@@ -1,47 +0,0 @@
#pragma once
#include <BAN/Formatter.h>
namespace BAN
{
struct MACAddress
{
uint8_t address[6];
constexpr bool operator==(const MACAddress& other) const
{
return
address[0] == other.address[0] &&
address[1] == other.address[1] &&
address[2] == other.address[2] &&
address[3] == other.address[3] &&
address[4] == other.address[4] &&
address[5] == other.address[5];
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const MACAddress& mac, const ValueFormat&)
{
ValueFormat format {
.base = 16,
.percision = 0,
.fill = 2,
.upper = true,
};
print_argument(putc, mac.address[0], format);
for (size_t i = 1; i < 6; i++)
{
putc(':');
print_argument(putc, mac.address[i], format);
}
}
}

View File

@@ -1,443 +0,0 @@
#pragma once
#include <BAN/Limits.h>
#include <BAN/Numbers.h>
#include <BAN/Traits.h>
#include <float.h>
namespace BAN::Math
{
template<typename T>
inline constexpr T abs(T x)
{
return x < 0 ? -x : x;
}
template<typename T>
inline constexpr T min(T a, T b)
{
return a < b ? a : b;
}
template<typename T>
inline constexpr T max(T a, T b)
{
return a > b ? a : b;
}
template<typename T>
inline constexpr T clamp(T x, T min, T max)
{
return x < min ? min : x > max ? max : x;
}
template<integral T>
inline constexpr T gcd(T a, T b)
{
T t;
while (b)
{
t = b;
b = a % b;
a = t;
}
return a;
}
template<integral T>
inline constexpr T lcm(T a, T b)
{
return a / gcd(a, b) * b;
}
template<integral T>
inline constexpr T div_round_up(T a, T b)
{
return (a + b - 1) / b;
}
template<integral T>
inline constexpr bool is_power_of_two(T x)
{
if (x == 0)
return false;
return (x & (x - 1)) == 0;
}
template<BAN::integral T>
static constexpr bool will_multiplication_overflow(T a, T b)
{
if (a == 0 || b == 0)
return false;
if ((a > 0) == (b > 0))
return a > BAN::numeric_limits<T>::max() / b;
else
return a < BAN::numeric_limits<T>::min() / b;
}
template<BAN::integral T>
static constexpr bool will_addition_overflow(T a, T b)
{
if (a > 0 && b > 0)
return a > BAN::numeric_limits<T>::max() - b;
if (a < 0 && b < 0)
return a < BAN::numeric_limits<T>::min() - b;
return false;
}
template<typename T>
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
inline constexpr T ilog2(T x)
{
if constexpr(is_same_v<T, unsigned int>)
return sizeof(T) * 8 - __builtin_clz(x) - 1;
if constexpr(is_same_v<T, unsigned long>)
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
}
template<floating_point T>
inline constexpr T floor(T x)
{
if constexpr(is_same_v<T, float>)
return __builtin_floorf(x);
if constexpr(is_same_v<T, double>)
return __builtin_floor(x);
if constexpr(is_same_v<T, long double>)
return __builtin_floorl(x);
}
template<floating_point T>
inline constexpr T ceil(T x)
{
if constexpr(is_same_v<T, float>)
return __builtin_ceilf(x);
if constexpr(is_same_v<T, double>)
return __builtin_ceil(x);
if constexpr(is_same_v<T, long double>)
return __builtin_ceill(x);
}
template<floating_point T>
inline constexpr T round(T x)
{
if (x == (T)0.0)
return x;
if (x > (T)0.0)
return floor<T>(x + (T)0.5);
return ceil<T>(x - (T)0.5);
}
template<floating_point T>
inline constexpr T trunc(T x)
{
if constexpr(is_same_v<T, float>)
return __builtin_truncf(x);
if constexpr(is_same_v<T, double>)
return __builtin_trunc(x);
if constexpr(is_same_v<T, long double>)
return __builtin_truncl(x);
}
template<floating_point T>
inline constexpr T rint(T x)
{
asm("frndint" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T fmod(T a, T b)
{
asm(
"1:"
"fprem;"
"fnstsw %%ax;"
"testb $4, %%ah;"
"jne 1b;"
: "+t"(a)
: "u"(b)
: "ax"
);
return a;
}
template<floating_point T>
static T modf(T x, T* iptr)
{
const T frac = BAN::Math::fmod<T>(x, 1);
*iptr = x - frac;
return frac;
}
template<floating_point T>
inline constexpr T frexp(T num, int* exp)
{
if (num == 0.0)
{
*exp = 0;
return 0.0;
}
T _exp;
asm("fxtract" : "+t"(num), "=u"(_exp));
*exp = (int)_exp + 1;
return num / (T)2.0;
}
template<floating_point T>
inline constexpr T copysign(T x, T y)
{
if ((x < (T)0.0) != (y < (T)0.0))
x = -x;
return x;
}
namespace detail
{
template<floating_point T>
inline constexpr T fyl2x(T x, T y)
{
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
return x;
}
}
template<floating_point T>
inline constexpr T log(T x)
{
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
}
template<floating_point T>
inline constexpr T log2(T x)
{
return detail::fyl2x<T>(x, 1.0);
}
template<floating_point T>
inline constexpr T log10(T x)
{
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
}
template<floating_point T>
inline constexpr T logb(T x)
{
static_assert(FLT_RADIX == 2);
return log2<T>(x);
}
template<floating_point T>
inline constexpr T exp2(T x)
{
if (abs(x) <= (T)1.0)
{
asm("f2xm1" : "+t"(x));
return x + (T)1.0;
}
asm(
"fld1;"
"fld %%st(1);"
"fprem;"
"f2xm1;"
"faddp;"
"fscale;"
"fstp %%st(1);"
: "+t"(x)
);
return x;
}
template<floating_point T>
inline constexpr T exp(T x)
{
return exp2<T>(x * numbers::log2e_v<T>);
}
template<floating_point T>
inline constexpr T pow(T x, T y)
{
asm(
"fyl2x;"
"fld1;"
"fld %%st(1);"
"fprem;"
"f2xm1;"
"faddp;"
"fscale;"
: "+t"(x), "+u"(y)
);
return x;
}
template<floating_point T>
inline constexpr T scalbn(T x, int n)
{
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
return x;
}
template<floating_point T>
inline constexpr T ldexp(T x, int y)
{
const bool exp_sign = y < 0;
if (exp_sign)
y = -y;
T exp = (T)1.0;
T mult = (T)2.0;
while (y)
{
if (y & 1)
exp *= mult;
mult *= mult;
y >>= 1;
}
if (exp_sign)
exp = (T)1.0 / exp;
return x * exp;
}
template<floating_point T>
inline constexpr T sqrt(T x)
{
asm("fsqrt" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T cbrt(T value)
{
if (value == 0.0)
return 0.0;
return pow<T>(value, 1.0 / 3.0);
}
template<floating_point T>
inline constexpr T sin(T x)
{
asm("fsin" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T cos(T x)
{
asm("fcos" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr void sincos(T x, T& sin, T& cos)
{
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
}
template<floating_point T>
inline constexpr T tan(T x)
{
T one, ret;
asm(
"fptan"
: "=t"(one), "=u"(ret)
: "0"(x)
);
return ret;
}
template<floating_point T>
inline constexpr T atan2(T y, T x)
{
asm(
"fpatan"
: "+t"(x)
: "u"(y)
: "st(1)"
);
return x;
}
template<floating_point T>
inline constexpr T atan(T x)
{
return atan2<T>(x, 1.0);
}
template<floating_point T>
inline constexpr T asin(T x)
{
if (x == (T)0.0)
return (T)0.0;
if (x == (T)1.0)
return numbers::pi_v<T> / (T)2.0;
if (x == (T)-1.0)
return -numbers::pi_v<T> / (T)2.0;
return (T)2.0 * atan<T>(x / (T(1.0) + sqrt<T>((T)1.0 - x * x)));
}
template<floating_point T>
inline constexpr T acos(T x)
{
if (x == (T)0.0)
return numbers::pi_v<T> / (T)2.0;
if (x == (T)1.0)
return (T)0.0;
if (x == (T)-1.0)
return numbers::pi_v<T>;
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
}
template<floating_point T>
inline constexpr T sinh(T x)
{
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T cosh(T x)
{
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T tanh(T x)
{
const T exp_px = exp<T>(x);
const T exp_nx = exp<T>(-x);
return (exp_px - exp_nx) / (exp_px + exp_nx);
}
template<floating_point T>
inline constexpr T asinh(T x)
{
return log<T>(x + sqrt<T>(x * x + (T)1.0));
}
template<floating_point T>
inline constexpr T acosh(T x)
{
return log<T>(x + sqrt<T>(x * x - (T)1.0));
}
template<floating_point T>
inline constexpr T atanh(T x)
{
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
}
template<floating_point T>
inline constexpr T hypot(T x, T y)
{
return sqrt<T>(x * x + y * y);
}
}

View File

@@ -1,29 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN
{
template<typename T>
constexpr remove_reference_t<T>&& move(T&& arg)
{
return static_cast<remove_reference_t<T>&&>(arg);
}
template<typename T>
constexpr T&& forward(remove_reference_t<T>& arg)
{
return static_cast<T&&>(arg);
}
template<typename T>
constexpr T&& forward(remove_reference_t<T>&& arg)
{
static_assert(!is_lvalue_reference_v<T>);
return static_cast<T&&>(arg);
}
}

View File

@@ -1,20 +0,0 @@
#pragma once
#if defined(__is_kernel)
#include <kernel/Memory/kmalloc.h>
#else
#include <stdlib.h>
#endif
namespace BAN
{
#if defined(__is_kernel)
static constexpr void*(&allocator)(size_t) = kmalloc;
static constexpr void*(&reallocator)(void*, size_t) = nullptr;
static constexpr void(&deallocator)(void*) = kfree;
#else
static constexpr void*(&allocator)(size_t) = malloc;
static constexpr void*(&reallocator)(void*, size_t) = realloc;
static constexpr void(&deallocator)(void*) = free;
#endif
}

View File

@@ -1,11 +0,0 @@
#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

View File

@@ -1,28 +0,0 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN::numbers
{
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
inline constexpr double e = e_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double lge = lge_v<double>;
inline constexpr double lg2 = lge_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
}

View File

@@ -1,204 +0,0 @@
#pragma once
#include <BAN/Assert.h>
#include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <stdint.h>
namespace BAN
{
template<typename T>
class Optional
{
public:
constexpr Optional();
constexpr Optional(Optional&&);
constexpr Optional(const Optional&);
constexpr Optional(const T&);
constexpr Optional(T&&);
~Optional();
constexpr Optional& operator=(Optional&&);
constexpr Optional& operator=(const Optional&);
template<typename... Args>
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>;
constexpr T* operator->();
constexpr const T* operator->() const;
constexpr T& operator*();
constexpr const T& operator*() const;
constexpr bool has_value() const;
constexpr T release_value();
constexpr T& value();
constexpr const T& value() const;
constexpr T& value_or(T&);
constexpr const T& value_or(const T&) const;
constexpr void clear();
private:
alignas(T) uint8_t m_storage[sizeof(T)] {};
bool m_has_value { false };
};
template<typename T>
constexpr Optional<T>::Optional()
: m_has_value(false)
{}
template<typename T>
constexpr Optional<T>::Optional(Optional<T>&& other)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(move(other.release_value()));
}
template<typename T>
constexpr Optional<T>::Optional(const Optional<T>& other)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(other.value());
}
template<typename T>
constexpr Optional<T>::Optional(const T& value)
: m_has_value(true)
{
new (m_storage) T(value);
}
template<typename T>
constexpr Optional<T>::Optional(T&& value)
: m_has_value(true)
{
new (m_storage) T(move(value));
}
template<typename T>
Optional<T>::~Optional()
{
clear();
}
template<typename T>
constexpr Optional<T>& Optional<T>::operator=(Optional&& other)
{
clear();
m_has_value = other.has_value();
if (other.has_value())
new (m_storage) T(move(other.release_value()));
return *this;
}
template<typename T>
constexpr Optional<T>& Optional<T>::operator=(const Optional& other)
{
clear();
m_has_value = other.has_value();
if (other.has_value())
new (m_storage) T(other.value());
return *this;
}
template<typename T>
template<typename... Args>
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
clear();
m_has_value = true;
new (m_storage) T(forward<Args>(args)...);
return *this;
}
template<typename T>
constexpr T* Optional<T>::operator->()
{
ASSERT(has_value());
return &value();
}
template<typename T>
constexpr const T* Optional<T>::operator->() const
{
ASSERT(has_value());
return &value();
}
template<typename T>
constexpr T& Optional<T>::operator*()
{
ASSERT(has_value());
return value();
}
template<typename T>
constexpr const T& Optional<T>::operator*() const
{
ASSERT(has_value());
return value();
}
template<typename T>
constexpr bool Optional<T>::has_value() const
{
return m_has_value;
}
template<typename T>
constexpr T Optional<T>::release_value()
{
ASSERT(has_value());
T released_value = move(value());
value().~T();
m_has_value = false;
return move(released_value);
}
template<typename T>
constexpr T& Optional<T>::value()
{
ASSERT(has_value());
return *reinterpret_cast<T*>(&m_storage);
}
template<typename T>
constexpr const T& Optional<T>::value() const
{
ASSERT(has_value());
return *reinterpret_cast<const T*>(&m_storage);
}
template<typename T>
constexpr T& Optional<T>::value_or(T& empty)
{
if (!has_value())
return empty;
return value();
}
template<typename T>
constexpr const T& Optional<T>::value_or(const T& empty) const
{
if (!has_value())
return empty;
return value();
}
template<typename T>
constexpr void Optional<T>::clear()
{
if (m_has_value)
value().~T();
m_has_value = false;
}
}

View File

@@ -1,10 +0,0 @@
#pragma once
#if __has_include(<new>)
#include <new>
#else
#include <stddef.h>
inline void* operator new(size_t, void* addr) { return addr; }
inline void* operator new[](size_t, void* addr) { return addr; }
#endif

View File

@@ -1,64 +0,0 @@
#pragma once
#include "BAN/Errors.h"
#include <BAN/Vector.h>
#include <BAN/Heap.h>
namespace BAN
{
template<typename T, typename Comp = less<T>>
class PriorityQueue
{
public:
PriorityQueue() = default;
PriorityQueue(Comp comp)
: m_comp(comp)
{ }
ErrorOr<void> push(const T& value)
{
TRY(m_data.push_back(value));
push_heap(m_data.begin(), m_data.end());
return {};
}
ErrorOr<void> push(T&& value)
{
TRY(m_data.push_back(move(value)));
push_heap(m_data.begin(), m_data.end());
return {};
}
template<typename... Args>
ErrorOr<void> emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
TRY(m_data.emplace_back(forward<Args>(args)...));
push_heap(m_data.begin(), m_data.end());
return {};
}
void pop()
{
pop_heap(m_data.begin(), m_data.end());
m_data.pop_back();
}
BAN::ErrorOr<void> reserve(Vector<T>::size_type size)
{
return m_data.reserve(size);
}
T& top() { return m_data.front(); }
const T& top() const { return m_data.front(); }
bool empty() const { return m_data.empty(); }
Vector<T>::size_type size() const { return m_data.size(); }
Vector<T>::size_type capacity() const { return m_data.capacity(); }
private:
Comp m_comp;
Vector<T> m_data;
};
}

View File

@@ -1,236 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Iterators.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/PlacementNew.h>
namespace BAN
{
template<typename T>
class Queue
{
public:
using size_type = size_t;
using value_type = T;
using iterator = IteratorSimple<T, Queue>;
using const_iterator = ConstIteratorSimple<T, Queue>;
public:
Queue() = default;
Queue(Queue<T>&&);
Queue(const Queue<T>&);
~Queue();
Queue<T>& operator=(Queue<T>&&);
Queue<T>& operator=(const Queue<T>&);
ErrorOr<void> push(T&&);
ErrorOr<void> push(const T&);
template<typename... Args>
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit();
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
void pop();
void clear();
bool empty() const;
size_type capacity() const;
size_type size() const;
const T& front() const;
T& front();
private:
ErrorOr<void> ensure_capacity(size_type size);
private:
T* m_data = nullptr;
size_type m_capacity = 0;
size_type m_size = 0;
};
template<typename T>
Queue<T>::Queue(Queue<T>&& other)
{
m_data = other.m_data;
m_capacity = other.m_capacity;
m_size = other.m_size;
other.m_data = nullptr;
other.m_capacity = 0;
other.m_size = 0;
}
template<typename T>
Queue<T>::Queue(const Queue<T>& other)
{
MUST(ensure_capacity(other.size()));
for (size_type i = 0; i < other.size(); i++)
new (m_data + i) T(other.m_data[i]);
m_size = other.m_size;
}
template<typename T>
Queue<T>::~Queue()
{
clear();
}
template<typename T>
Queue<T>& Queue<T>::operator=(Queue<T>&& other)
{
clear();
m_data = other.m_data;
m_capacity = other.m_capacity;
m_size = other.m_size;
other.m_data = nullptr;
other.m_capacity = 0;
other.m_size = 0;
return *this;
}
template<typename T>
Queue<T>& Queue<T>::operator=(const Queue<T>& other)
{
clear();
MUST(ensure_capacity(other.size()));
for (size_type i = 0; i < other.size(); i++)
new (m_data + i) T(other.m_data[i]);
m_size = other.m_size;
return *this;
}
template<typename T>
ErrorOr<void> Queue<T>::push(T&& value)
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(move(value));
m_size++;
return {};
}
template<typename T>
ErrorOr<void> Queue<T>::push(const T& value)
{
return push(move(T(value)));
}
template<typename T>
template<typename... Args>
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...);
m_size++;
return {};
}
template<typename T>
ErrorOr<void> Queue<T>::reserve(size_type size)
{
TRY(ensure_capacity(size));
return {};
}
template<typename T>
ErrorOr<void> Queue<T>::shrink_to_fit()
{
size_type temp = m_capacity;
m_capacity = 0;
auto error_or = ensure_capacity(m_size);
if (error_or.is_error())
{
m_capacity = temp;
return error_or;
}
return {};
}
template<typename T>
void Queue<T>::pop()
{
ASSERT(m_size > 0);
for (size_type i = 0; i < m_size - 1; i++)
m_data[i] = move(m_data[i + 1]);
m_data[m_size - 1].~T();
m_size--;
}
template<typename T>
void Queue<T>::clear()
{
for (size_type i = 0; i < m_size; i++)
m_data[i].~T();
BAN::deallocator(m_data);
m_data = nullptr;
m_capacity = 0;
m_size = 0;
}
template<typename T>
bool Queue<T>::empty() const
{
return m_size == 0;
}
template<typename T>
typename Queue<T>::size_type Queue<T>::capacity() const
{
return m_capacity;
}
template<typename T>
typename Queue<T>::size_type Queue<T>::size() const
{
return m_size;
}
template<typename T>
const T& Queue<T>::front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
template<typename T>
T& Queue<T>::front()
{
ASSERT(m_size > 0);
return m_data[0];
}
template<typename T>
ErrorOr<void> Queue<T>::ensure_capacity(size_type size)
{
if (m_capacity > size)
return {};
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
for (size_type i = 0; i < m_size; i++)
{
new (new_data + i) T(move(m_data[i]));
m_data[i].~T();
}
BAN::deallocator(m_data);
m_data = new_data;
m_capacity = new_cap;
return {};
}
}

View File

@@ -1,161 +0,0 @@
#pragma once
#include <BAN/Atomic.h>
#include <BAN/Errors.h>
#include <BAN/Move.h>
#include <BAN/NoCopyMove.h>
#include <stdint.h>
namespace BAN
{
template<typename T>
class RefCounted
{
BAN_NON_COPYABLE(RefCounted);
BAN_NON_MOVABLE(RefCounted);
public:
uint32_t ref_count() const
{
return m_ref_count;
}
void ref() const
{
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed);
ASSERT(old > 0);
}
bool try_ref() const
{
uint32_t expected = m_ref_count.load(MemoryOrder::memory_order_relaxed);
for (;;)
{
if (expected == 0)
return false;
if (m_ref_count.compare_exchange(expected, expected + 1, MemoryOrder::memory_order_acquire))
return true;
}
}
void unref() const
{
uint32_t old = m_ref_count.fetch_sub(1);
ASSERT(old > 0);
if (old == 1)
delete static_cast<const T*>(this);
}
protected:
RefCounted() = default;
virtual ~RefCounted() { ASSERT(m_ref_count == 0); }
private:
mutable Atomic<uint32_t> m_ref_count = 1;
};
template<typename T>
class RefPtr
{
public:
RefPtr() = default;
RefPtr(T* pointer)
{
m_pointer = pointer;
if (m_pointer)
m_pointer->ref();
}
~RefPtr() { clear(); }
template<typename U>
static RefPtr adopt(U* pointer)
{
RefPtr ptr;
ptr.m_pointer = pointer;
return ptr;
}
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
template<typename... Args>
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
{
T* pointer = new T(forward<Args>(args)...);
if (pointer == nullptr)
return Error::from_errno(ENOMEM);
return adopt(pointer);
}
RefPtr(const RefPtr& other) { *this = other; }
RefPtr(RefPtr&& other) { *this = move(other); }
template<typename U>
RefPtr(const RefPtr<U>& other) { *this = other; }
template<typename U>
RefPtr(RefPtr<U>&& other) { *this = move(other); }
RefPtr& operator=(const RefPtr& other)
{
clear();
m_pointer = other.m_pointer;
if (m_pointer)
m_pointer->ref();
return *this;
}
RefPtr& operator=(RefPtr&& other)
{
clear();
m_pointer = other.m_pointer;
other.m_pointer = nullptr;
return *this;
}
template<typename U>
RefPtr& operator=(const RefPtr<U>& other)
{
clear();
m_pointer = other.m_pointer;
if (m_pointer)
m_pointer->ref();
return *this;
}
template<typename U>
RefPtr& operator=(RefPtr<U>&& other)
{
clear();
m_pointer = other.m_pointer;
other.m_pointer = nullptr;
return *this;
}
T* ptr() { ASSERT(!empty()); return m_pointer; }
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
T& operator*() { return *ptr(); }
const T& operator*() const { return *ptr(); }
T* operator->() { return ptr(); }
const T* operator->() const { return ptr(); }
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
bool empty() const { return m_pointer == nullptr; }
explicit operator bool() const { return m_pointer; }
void clear()
{
if (m_pointer)
m_pointer->unref();
m_pointer = nullptr;
}
private:
T* m_pointer = nullptr;
template<typename U>
friend class RefPtr;
};
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <BAN/Function.h>
namespace BAN
{
class ScopeGuard
{
public:
ScopeGuard(const BAN::Function<void()>& func)
: m_func(func)
{ }
~ScopeGuard()
{
if (m_enabled)
m_func();
}
void disable()
{
m_enabled = false;
}
private:
BAN::Function<void()> m_func;
bool m_enabled { true };
};
}

View File

@@ -1,165 +0,0 @@
#pragma once
#include <BAN/Heap.h>
#include <BAN/Math.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
#include <BAN/Vector.h>
namespace BAN::sort
{
template<typename It, typename Comp = less<it_value_type_t<It>>>
void exchange_sort(It begin, It end, Comp comp = {})
{
for (It lhs = begin; lhs != end; ++lhs)
for (It rhs = next(lhs, 1); rhs != end; ++rhs)
if (!comp(*lhs, *rhs))
swap(*lhs, *rhs);
}
namespace detail
{
template<typename It, typename Comp>
It partition(It begin, It end, Comp comp)
{
It pivot = prev(end, 1);
It it1 = begin;
for (It it2 = begin; it2 != pivot; ++it2)
{
if (comp(*it2, *pivot))
{
swap(*it1, *it2);
++it1;
}
}
swap(*it1, *pivot);
return it1;
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void quick_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
It mid = detail::partition(begin, end, comp);
quick_sort(begin, mid, comp);
quick_sort(++mid, end, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void insertion_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
for (It it1 = next(begin, 1); it1 != end; ++it1)
{
auto x = move(*it1);
It it2 = it1;
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
*it2 = move(*prev(it2, 1));
*it2 = move(x);
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void heap_sort(It begin, It end, Comp comp = {})
{
make_heap(begin, end, comp);
sort_heap(begin, end, comp);
}
namespace detail
{
template<typename It, typename Comp>
void intro_sort_impl(It begin, It end, size_t max_depth, Comp comp)
{
if (distance(begin, end) <= 16)
return insertion_sort(begin, end, comp);
if (max_depth == 0)
return heap_sort(begin, end, comp);
It mid = detail::partition(begin, end, comp);
intro_sort_impl(begin, mid, max_depth - 1, comp);
intro_sort_impl(++mid, end, max_depth - 1, comp);
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void intro_sort(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
detail::intro_sort_impl(begin, end, 2 * Math::ilog2(len), comp);
}
namespace detail
{
template<unsigned_integral T>
consteval T lsb_index(T value)
{
for (T result = 0;; result++)
if (value & (1 << result))
return result;
}
}
template<typename It, size_t radix = 256>
requires is_unsigned_v<it_value_type_t<It>> && (radix > 0 && (radix & (radix - 1)) == 0)
BAN::ErrorOr<void> radix_sort(It begin, It end)
{
using value_type = it_value_type_t<It>;
const size_t len = distance(begin, end);
if (len <= 1)
return {};
Vector<value_type> temp;
TRY(temp.resize(len));
Vector<size_t> counts;
TRY(counts.resize(radix));
constexpr size_t mask = radix - 1;
constexpr size_t shift = detail::lsb_index(radix);
for (size_t s = 0; s < sizeof(value_type) * 8; s += shift)
{
for (auto& cnt : counts)
cnt = 0;
for (It it = begin; it != end; ++it)
counts[(*it >> s) & mask]++;
for (size_t i = 0; i < radix - 1; i++)
counts[i + 1] += counts[i];
for (It it = end; it != begin;)
{
--it;
temp[--counts[(*it >> s) & mask]] = *it;
}
for (size_t j = 0; j < temp.size(); j++)
*next(begin, j) = temp[j];
}
return {};
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void sort(It begin, It end, Comp comp = {})
{
return intro_sort(begin, end, comp);
}
}

View File

@@ -1,102 +0,0 @@
#pragma once
#include <BAN/Assert.h>
#include <BAN/Iterators.h>
#include <stddef.h>
namespace BAN
{
template<typename T>
class Span
{
public:
using value_type = T;
using size_type = size_t;
using iterator = IteratorSimple<value_type, Span>;
using const_iterator = ConstIteratorSimple<value_type, Span>;
private:
template<typename S>
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
public:
Span() = default;
Span(value_type* data, size_type size)
: m_data(data)
, m_size(size)
{ }
template<typename S>
Span(const Span<S>& other) requires can_init_from_v<S>
: m_data(other.m_data)
, m_size(other.m_size)
{ }
template<typename S>
Span(Span<S>&& other) requires can_init_from_v<S>
: m_data(other.m_data)
, m_size(other.m_size)
{
other.clear();
}
template<typename S>
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
template<typename S>
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
value_type& operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
value_type* data() const
{
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(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
Span<const value_type> as_const() const { return *this; }
private:
value_type* m_data = nullptr;
size_type m_size = 0;
friend class Span<const value_type>;
};
}

View File

@@ -1,362 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Formatter.h>
#include <BAN/Hash.h>
#include <BAN/Iterators.h>
#include <BAN/New.h>
#include <BAN/StringView.h>
namespace BAN
{
class String
{
public:
using size_type = size_t;
using value_type = char;
using iterator = IteratorSimple<char, String>;
using const_iterator = ConstIteratorSimple<char, String>;
static constexpr size_type sso_capacity = 15;
public:
String() {}
String(const String& other) { *this = other; }
String(String&& other) { *this = move(other); }
String(StringView other) { *this = other; }
~String() { clear(); }
template<typename... Args>
static BAN::ErrorOr<String> formatted(const char* format, Args&&... args)
{
size_type length = 0;
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
String result;
TRY(result.reserve(length));
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
return result;
}
String& operator=(const String& other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size() + 1);
m_size = other.size();
return *this;
}
String& operator=(String&& other)
{
clear();
if (other.has_sso())
memcpy(data(), other.data(), other.size() + 1);
else
{
m_storage.general_storage = other.m_storage.general_storage;
m_has_sso = false;
}
m_size = other.m_size;
other.m_size = 0;
other.m_storage.sso_storage = SSOStorage();
other.m_has_sso = true;
return *this;
}
String& operator=(StringView other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size());
m_size = other.size();
data()[m_size] = '\0';
return *this;
}
ErrorOr<void> push_back(char c)
{
TRY(ensure_capacity(m_size + 1));
data()[m_size] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> insert(char c, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
memmove(data() + index + 1, data() + index, m_size - index);
data()[index] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> insert(StringView str, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + str.size()));
memmove(data() + index + str.size(), data() + index, m_size - index);
memcpy(data() + index, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
ErrorOr<void> append(StringView str)
{
TRY(ensure_capacity(m_size + str.size()));
memcpy(data() + m_size, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
void pop_back()
{
ASSERT(m_size > 0);
m_size--;
data()[m_size] = '\0';
}
void remove(size_type index)
{
ASSERT(index < m_size);
memmove(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
void clear()
{
if (!has_sso())
{
deallocator(m_storage.general_storage.data);
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
}
m_size = 0;
data()[m_size] = '\0';
}
const_iterator begin() const { return const_iterator(data()); }
iterator begin() { return iterator(data()); }
const_iterator end() const { return const_iterator(data() + size()); }
iterator end() { return iterator(data() + size()); }
char front() const { ASSERT(m_size > 0); return data()[0]; }
char& front() { ASSERT(m_size > 0); return data()[0]; }
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
bool operator==(const String& str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool operator==(StringView str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool operator==(const char* cstr) const
{
for (size_type i = 0; i < m_size; i++)
if (data()[i] != cstr[i])
return false;
if (cstr[size()] != '\0')
return false;
return true;
}
ErrorOr<void> resize(size_type new_size, char init_c = '\0')
{
if (m_size == new_size)
return {};
// expanding
if (m_size < new_size)
{
TRY(ensure_capacity(new_size));
memset(data() + m_size, init_c, new_size - m_size);
m_size = new_size;
data()[m_size] = '\0';
return {};
}
m_size = new_size;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> reserve(size_type new_size)
{
TRY(ensure_capacity(new_size));
return {};
}
ErrorOr<void> shrink_to_fit()
{
if (has_sso())
return {};
if (fits_in_sso())
{
char* data = m_storage.general_storage.data;
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
memcpy(this->data(), data, m_size + 1);
deallocator(data);
return {};
}
GeneralStorage& storage = m_storage.general_storage;
if (storage.capacity == m_size)
return {};
char* new_data = (char*)allocator(m_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, storage.data, m_size);
deallocator(storage.data);
storage.capacity = m_size;
storage.data = new_data;
return {};
}
StringView sv() const { return StringView(data(), size()); }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
size_type capacity() const
{
if (has_sso())
return sso_capacity;
return m_storage.general_storage.capacity;
}
char* data()
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
const char* data() const
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
private:
ErrorOr<void> ensure_capacity(size_type new_size)
{
if (m_size >= new_size)
return {};
if (has_sso() && fits_in_sso(new_size))
return {};
char* new_data = (char*)allocator(new_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
if (m_size)
memcpy(new_data, data(), m_size + 1);
if (has_sso())
{
m_storage.general_storage = GeneralStorage();
m_has_sso = false;
}
else
deallocator(m_storage.general_storage.data);
auto& storage = m_storage.general_storage;
storage.capacity = new_size;
storage.data = new_data;
return {};
}
bool has_sso() const { return m_has_sso; }
bool fits_in_sso() const { return fits_in_sso(m_size); }
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
private:
struct SSOStorage
{
char data[sso_capacity + 1] {};
};
struct GeneralStorage
{
size_type capacity { 0 };
char* data { nullptr };
};
private:
union {
SSOStorage sso_storage;
GeneralStorage general_storage;
} m_storage { .sso_storage = SSOStorage() };
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
size_type m_has_sso : 1 { true };
};
template<>
struct hash<String>
{
hash_t operator()(const String& string) const
{
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
constexpr hash_t FNV_prime = 0x01000193;
hash_t hash = FNV_offset_basis;
for (String::size_type i = 0; i < string.size(); i++)
{
hash *= FNV_prime;
hash ^= (uint8_t)string[i];
}
return hash;
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const String& string, const ValueFormat&)
{
for (String::size_type i = 0; i < string.size(); i++)
putc(string[i]);
}
}

View File

@@ -1,256 +0,0 @@
#pragma once
#include <BAN/Formatter.h>
#include <BAN/ForwardList.h>
#include <BAN/Hash.h>
#include <BAN/Iterators.h>
#include <BAN/Optional.h>
#include <BAN/Vector.h>
namespace BAN
{
class StringView
{
public:
using size_type = size_t;
using value_type = char;
using const_iterator = ConstIteratorSimple<char, StringView>;
public:
constexpr StringView() {}
constexpr StringView(const char* string, size_type len = -1)
{
if (len == size_type(-1))
for (len = 0; string[len];)
len++;
m_data = string;
m_size = len;
}
StringView(const String&);
constexpr const_iterator begin() const { return const_iterator(m_data); }
constexpr const_iterator end() const { return const_iterator(m_data + m_size); }
constexpr char operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
constexpr bool operator==(StringView other) const
{
if (m_size != other.m_size)
return false;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] != other.m_data[i])
return false;
return true;
}
constexpr bool operator==(const char* other) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] != other[i])
return false;
return other[m_size] == '\0';
}
constexpr StringView substring(size_type index, size_type len = -1) const
{
ASSERT(index <= m_size);
if (len == size_type(-1))
len = m_size - index;
ASSERT(len <= m_size - index); // weird order to avoid overflow
StringView result;
result.m_data = m_data + index;
result.m_size = len;
return result;
}
ErrorOr<Vector<StringView>> split(char delim, bool allow_empties = false) const
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool allow_empties = false) const
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
constexpr char back() const
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
constexpr char front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
BAN::Optional<size_type> find(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return i;
return {};
}
BAN::Optional<size_type> find(bool(*comp)(char)) const
{
for (size_type i = 0; i < m_size; i++)
if (comp(m_data[i]))
return i;
return {};
}
BAN::Optional<size_type> rfind(char ch) const
{
for (size_type i = m_size; i > 0; i--)
if (m_data[i - 1] == ch)
return i - 1;
return {};
}
BAN::Optional<size_type> rfind(bool(*comp)(char)) const
{
for (size_type i = m_size; i > 0; i--)
if (comp(m_data[i - 1]))
return i - 1;
return {};
}
constexpr bool starts_with(BAN::StringView target) const
{
if (target.size() > m_size)
return false;
for (size_type i = 0; i < target.size(); i++)
if (m_data[i] != target[i])
return false;
return true;
}
constexpr bool contains(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return true;
return false;
}
constexpr size_type count(char ch) const
{
size_type result = 0;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
result++;
return result;
}
constexpr bool empty() const { return m_size == 0; }
constexpr size_type size() const { return m_size; }
constexpr const char* data() const { return m_data; }
private:
const char* m_data = nullptr;
size_type m_size = 0;
};
template<>
struct hash<StringView>
{
hash_t operator()(StringView string) const
{
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
constexpr hash_t FNV_prime = 0x01000193;
hash_t hash = FNV_offset_basis;
for (StringView::size_type i = 0; i < string.size(); i++)
{
hash *= FNV_prime;
hash ^= (uint8_t)string[i];
}
return hash;
}
};
}
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const StringView& sv, const ValueFormat&)
{
for (StringView::size_type i = 0; i < sv.size(); i++)
putc(sv[i]);
}
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <BAN/Move.h>
namespace BAN
{
template<typename T>
void swap(T& lhs, T& rhs)
{
T tmp = move(lhs);
lhs = move(rhs);
rhs = move(tmp);
}
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <BAN/Formatter.h>
#include <stdint.h>
namespace BAN
{
struct Time
{
uint32_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t week_day;
};
uint64_t to_unix_time(const BAN::Time&);
BAN::Time from_unix_time(uint64_t);
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const Time& time, const ValueFormat&)
{
constexpr const char* week_days[] { "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
constexpr const char* months[] { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
print(putc, "{} {} {} {2}:{2}:{2} GMT+0 {4}", week_days[time.week_day], months[time.month], time.day, time.hour, time.minute, time.second, time.year);
}
}

View File

@@ -1,153 +0,0 @@
#pragma once
namespace BAN
{
template<typename T> struct remove_reference { using type = T; };
template<typename T> struct remove_reference<T&> { using type = T; };
template<typename T> struct remove_reference<T&&> { using type = T; };
template<typename T> using remove_reference_t = typename remove_reference<T>::type;
template<typename T> struct remove_const { using type = T; };
template<typename T> struct remove_const<const T> { using type = T; };
template<typename T> using remove_const_t = typename remove_const<T>::type;
template<typename T> struct remove_volatile { using type = T; };
template<typename T> struct remove_volatile<volatile T> { using type = T; };
template<typename T> using remove_volatile_t = typename remove_volatile<T>::type;
template<typename T> struct remove_cv { using type = remove_volatile_t<remove_const_t<T>>; };
template<typename T> using remove_cv_t = typename remove_cv<T>::type;
template<typename T> struct remove_const_and_reference { using type = remove_const_t<remove_reference_t<T>>; };
template<typename T> using remove_const_and_reference_t = typename remove_const_and_reference<T>::type;
template<bool B, typename T = void> struct enable_if {};
template<typename T> struct enable_if<true, T> { using type = T; };
template<bool B, typename T = void> using enable_if_t = typename enable_if<B, T>::type;
template<bool B, typename T> struct maybe_const { using type = T; };
template<typename T> struct maybe_const<true, T> { using type = const T; };
template<bool B, typename T> using maybe_const_t = typename maybe_const<B, T>::type;
template<bool B, typename T1, typename T2> struct either_or { using type = T2; };
template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; };
template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type;
template<typename T, T V> struct integral_constant { static constexpr T value = V; };
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value;
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
template<typename T, typename S> struct is_same : false_type {};
template<typename T> struct is_same<T, T> : true_type {};
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
template<typename T> struct is_lvalue_reference : false_type {};
template<typename T> struct is_lvalue_reference<T&> : true_type {};
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
template<typename T> struct is_trivially_copyable { static constexpr bool value = __is_trivially_copyable(T); };
template<typename T> inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
template<typename T> concept integral = is_integral_v<T>;
template<typename T> struct is_floating_point : false_type {};
template<> struct is_floating_point<float> : true_type {};
template<> struct is_floating_point<double> : true_type {};
template<> struct is_floating_point<long double> : true_type {};
template<typename T> inline constexpr bool is_floating_point_v = is_floating_point<T>::value;
template<typename T> concept floating_point = is_floating_point_v<T>;
template<typename T> struct is_pointer : false_type {};
template<typename T> struct is_pointer<T*> : true_type {};
template<typename T> struct is_pointer<T* const> : true_type {};
template<typename T> struct is_pointer<T* volatile> : true_type {};
template<typename T> struct is_pointer<T* const volatile> : true_type {};
template<typename T> inline constexpr bool is_pointer_v = is_pointer<T>::value;
template<typename T> concept pointer = is_pointer_v<T>;
template<typename T> struct is_const : false_type {};
template<typename T> struct is_const<const T> : true_type {};
template<typename T> inline constexpr bool is_const_v = is_const<T>::value;
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
template<typename T> inline constexpr bool is_pod_v = is_pod<T>::value;
namespace detail
{
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
template<typename T> struct is_signed<T, false> : false_type {};
template<typename T, bool = is_arithmetic_v<T>> struct is_unsigned { static constexpr bool value = T(0) < T(-1); };
template<typename T> struct is_unsigned<T, false> : false_type {};
}
template<typename T> struct is_signed : detail::is_signed<T> {};
template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value;
template<typename T> concept signed_integral = is_signed_v<T> && is_integral_v<T>;
template<typename T> struct is_unsigned : detail::is_unsigned<T> {};
template<typename T> inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
template<typename T> concept unsigned_integral = is_unsigned_v<T> && is_integral_v<T>;
#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \
template<> struct make_unsigned<__type> { using type = unsigned __type; }; \
template<> struct make_unsigned<const __type> { using type = unsigned const __type; }; \
template<> struct make_unsigned<volatile __type> { using type = unsigned volatile __type; }; \
template<> struct make_unsigned<const volatile __type> { using type = unsigned const volatile __type; };
template<typename T> requires is_arithmetic_v<T> struct make_unsigned { using type = T; };
__BAN_TRAITS_MAKE_UNSIGNED_CV(char)
__BAN_TRAITS_MAKE_UNSIGNED_CV(short)
__BAN_TRAITS_MAKE_UNSIGNED_CV(int)
__BAN_TRAITS_MAKE_UNSIGNED_CV(long)
__BAN_TRAITS_MAKE_UNSIGNED_CV(long long)
template<typename T> using make_unsigned_t = typename make_unsigned<T>::type;
#undef __BAN_TRAITS_MAKE_UNSIGNED_CV
#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \
template<> struct make_signed<unsigned __type> { using type = __type; }; \
template<> struct make_signed<unsigned const __type> { using type = const __type; }; \
template<> struct make_signed<unsigned volatile __type> { using type = volatile __type; }; \
template<> struct make_signed<unsigned const volatile __type> { using type = const volatile __type; };
template<typename T> requires is_arithmetic_v<T> struct make_signed { using type = T; };
__BAN_TRAITS_MAKE_SIGNED_CV(char)
__BAN_TRAITS_MAKE_SIGNED_CV(short)
__BAN_TRAITS_MAKE_SIGNED_CV(int)
__BAN_TRAITS_MAKE_SIGNED_CV(long)
__BAN_TRAITS_MAKE_SIGNED_CV(long long)
template<typename T> using make_signed_t = typename make_signed<T>::type;
#undef __BAN_TRAITS_MAKE_SIGNED_CV
template<typename T> struct it_value_type { using value_type = T::value_type; };
template<typename T> struct it_value_type<T*> { using value_type = T; };
template<typename T> using it_value_type_t = typename it_value_type<T>::value_type;
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };
}

View File

@@ -1,84 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
namespace BAN::UTF8
{
static constexpr uint32_t invalid = 0xFFFFFFFF;
constexpr uint32_t byte_length(uint8_t first_byte)
{
if ((first_byte & 0x80) == 0x00)
return 1;
if ((first_byte & 0xE0) == 0xC0)
return 2;
if ((first_byte & 0xF0) == 0xE0)
return 3;
if ((first_byte & 0xF8) == 0xF0)
return 4;
return UTF8::invalid;
}
template<typename T> requires (sizeof(T) == 1)
constexpr uint32_t to_codepoint(const T* bytes)
{
uint32_t length = byte_length(bytes[0]);
for (uint32_t i = 1; i < length; i++)
if (((uint8_t)bytes[i] & 0xC0) != 0x80)
return UTF8::invalid;
switch (length)
{
case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid : (uint8_t)bytes[0];
case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) << 6) | ((uint8_t)bytes[1] & 0x3F);
case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) << 6) | ((uint8_t)bytes[2] & 0x3F);
case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F);
}
return UTF8::invalid;
}
template<typename T>
constexpr bool from_codepoints(const T* codepoints, size_t count, char* out)
{
uint8_t* ptr = (uint8_t*)out;
for (size_t i = 0; i < count; i++)
{
if (codepoints[i] < 0x80)
{
*ptr++ = codepoints[i];
}
else if (codepoints[i] < 0x800)
{
*ptr++ = 0xC0 | ((codepoints[i] >> 6) & 0x1F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else if (codepoints[i] < 0x10000)
{
*ptr++ = 0xE0 | ((codepoints[i] >> 12) & 0x0F);
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else if (codepoints[i] < 0x110000)
{
*ptr++ = 0xF0 | ((codepoints[i] >> 18) & 0x07);
*ptr++ = 0x80 | ((codepoints[i] >> 12) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else
{
return false;
}
}
*ptr = '\0';
return true;
}
}

View File

@@ -1,99 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/NoCopyMove.h>
namespace BAN
{
template<typename T>
class UniqPtr
{
BAN_NON_COPYABLE(UniqPtr);
public:
UniqPtr() = default;
template<typename U>
UniqPtr(UniqPtr<U>&& other)
{
m_pointer = other.m_pointer;
other.m_pointer = nullptr;
}
~UniqPtr()
{
clear();
}
static UniqPtr adopt(T* pointer)
{
UniqPtr uniq;
uniq.m_pointer = pointer;
return uniq;
}
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
template<typename... Args>
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
{
UniqPtr uniq;
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
if (uniq.m_pointer == nullptr)
return BAN::Error::from_errno(ENOMEM);
return uniq;
}
template<typename U>
UniqPtr& operator=(UniqPtr<U>&& other)
{
clear();
m_pointer = other.m_pointer;
other.m_pointer = nullptr;
return *this;
}
T& operator*()
{
ASSERT(m_pointer);
return *m_pointer;
}
const T& operator*() const
{
ASSERT(m_pointer);
return *m_pointer;
}
T* operator->()
{
ASSERT(m_pointer);
return m_pointer;
}
const T* operator->() const
{
ASSERT(m_pointer);
return m_pointer;
}
T* ptr() { return m_pointer; }
const T* ptr() const { return m_pointer; }
void clear()
{
if (m_pointer)
delete m_pointer;
m_pointer = nullptr;
}
operator bool() const { return m_pointer != nullptr; }
private:
T* m_pointer = nullptr;
template<typename U>
friend class UniqPtr;
};
}

View File

@@ -1,317 +0,0 @@
#pragma once
#include <BAN/Assert.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <string.h>
namespace BAN
{
namespace detail
{
template<typename T>
constexpr size_t size_ref_as_ptr() { return is_lvalue_reference_v<T> ? sizeof(remove_reference_t<T>*) : sizeof(T); }
template<typename T>
constexpr size_t align_ref_as_ptr() { return is_lvalue_reference_v<T> ? alignof(remove_reference_t<T>*) : alignof(T); }
template<typename T>
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T>(); }
template<typename T0, typename T1, typename... Ts>
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T0>() > size_ref_as_ptr<T1>() ? max_size_ref_as_ptr<T0, Ts...>() : max_size_ref_as_ptr<T1, Ts...>(); }
template<typename T>
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T>(); }
template<typename T0, typename T1, typename... Ts>
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T0>() > align_ref_as_ptr<T1>() ? max_align_ref_as_ptr<T0, Ts...>() : max_align_ref_as_ptr<T1, Ts...>(); }
template<typename T, typename T0, typename... Ts>
constexpr size_t index()
{
if constexpr(is_same_v<T, T0>)
return 0;
else if constexpr(sizeof...(Ts) == 0)
return 1;
else
return index<T, Ts...>() + 1;
}
template<typename T, typename... Ts>
void destruct(size_t index, uint8_t* data)
{
if (index == 0)
{
if constexpr(!is_lvalue_reference_v<T>)
reinterpret_cast<T*>(data)->~T();
}
else if constexpr(sizeof...(Ts) > 0)
destruct<Ts...>(index - 1, data);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void move_construct(size_t index, uint8_t* source, uint8_t* target)
{
if (index == 0)
if constexpr(!is_lvalue_reference_v<T>)
new (target) T(move(*reinterpret_cast<T*>(source)));
else
memcpy(target, source, sizeof(remove_reference_t<T>*));
else if constexpr(sizeof...(Ts) > 0)
move_construct<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void copy_construct(size_t index, const uint8_t* source, uint8_t* target)
{
if (index == 0)
if constexpr(!is_lvalue_reference_v<T>)
new (target) T(*reinterpret_cast<const T*>(source));
else
memcpy(target, source, sizeof(remove_reference_t<T>*));
else if constexpr(sizeof...(Ts) > 0)
copy_construct<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void move_assign(size_t index, uint8_t* source, uint8_t* target)
{
if (index == 0)
if constexpr(!is_lvalue_reference_v<T>)
*reinterpret_cast<T*>(target) = move(*reinterpret_cast<T*>(source));
else
memcpy(target, source, sizeof(remove_reference_t<T>*));
else if constexpr(sizeof...(Ts) > 0)
move_assign<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void copy_assign(size_t index, const uint8_t* source, uint8_t* target)
{
if (index == 0)
if constexpr(!is_lvalue_reference_v<T>)
*reinterpret_cast<T*>(target) = *reinterpret_cast<const T*>(source);
else
memcpy(target, source, sizeof(remove_reference_t<T>*));
else if constexpr(sizeof...(Ts) > 0)
copy_assign<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
}
template<typename... Ts>
requires (!is_const_v<Ts> && ...)
class Variant
{
private:
template<typename T>
static constexpr bool can_have() { return detail::index<T, Ts...>() != invalid_index(); }
static constexpr size_t invalid_index() { return sizeof...(Ts); }
public:
Variant() = default;
Variant(Variant&& other)
: m_index(other.m_index)
{
if (!other.has_value())
return;
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
other.clear();
}
Variant(const Variant& other)
: m_index(other.m_index)
{
if (!other.has_value())
return;
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
}
template<typename T>
Variant(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
: m_index(detail::index<T, Ts...>())
{
new (m_storage) T(move(value));
}
template<typename T>
Variant(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
: m_index(detail::index<T, Ts...>())
{
new (m_storage) T(value);
}
~Variant()
{
clear();
}
Variant& operator=(Variant&& other)
{
if (m_index == other.m_index)
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
else
{
clear();
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index;
}
other.clear();
return *this;
}
Variant& operator=(const Variant& other)
{
if (m_index == other.m_index)
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
else
{
clear();
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index;
}
return *this;
}
template<typename T>
Variant& operator=(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
if (size_t index = detail::index<T, Ts...>(); index == m_index)
get<T>() = move(value);
else
{
clear();
new (m_storage) T(move(value));
m_index = index;
}
return *this;
}
template<typename T>
Variant& operator=(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
if (size_t index = detail::index<T, Ts...>(); index == m_index)
get<T>() = value;
else
{
clear();
new (m_storage) T(value);
m_index = index;
}
return *this;
}
template<typename T>
bool has() const requires (can_have<T>())
{
return m_index == detail::index<T, Ts...>();
}
template<typename T, typename... Args>
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(BAN::forward<Args>(args)...);
}
template<typename T>
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
if (has<T>())
get<T>() = move(value);
else
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(move(value));
}
}
template<typename T>
void set(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
if (has<T>())
get<T>() = value;
else
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(value);
}
}
template<typename T>
void set(T value) requires (can_have<T>() && is_lvalue_reference_v<T>)
{
clear();
m_index = detail::index<T, Ts...>();
*reinterpret_cast<remove_reference_t<T>**>(m_storage) = &value;
}
template<typename T>
T& get() requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
ASSERT(has<T>());
return *reinterpret_cast<T*>(m_storage);
}
template<typename T>
const T& get() const requires (can_have<T>() && !is_lvalue_reference_v<T>)
{
ASSERT(has<T>());
return *reinterpret_cast<const T*>(m_storage);
}
template<typename T>
T get() requires (can_have<T>() && is_lvalue_reference_v<T>)
{
ASSERT(has<T>());
return **reinterpret_cast<remove_reference_t<T>**>(m_storage);
}
template<typename T>
const T get() const requires (can_have<T>() && is_lvalue_reference_v<T>)
{
ASSERT(has<T>());
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
}
bool has_value() const
{
return m_index != invalid_index();
}
explicit operator bool() const
{
return has_value();
}
void clear()
{
if (m_index != invalid_index())
{
detail::destruct<Ts...>(m_index, m_storage);
m_index = invalid_index();
}
}
private:
alignas(detail::max_align_ref_as_ptr<Ts...>()) uint8_t m_storage[detail::max_size_ref_as_ptr<Ts...>()] {};
size_t m_index { invalid_index() };
};
}

View File

@@ -1,445 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Iterators.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/PlacementNew.h>
#include <BAN/Span.h>
#include <BAN/Swap.h>
namespace BAN
{
// T must be move assignable, move constructable (and copy constructable for some functions)
template<typename T>
class Vector
{
public:
using size_type = size_t;
using value_type = T;
using iterator = IteratorSimple<T, Vector>;
using const_iterator = ConstIteratorSimple<T, Vector>;
public:
Vector() = default;
Vector(Vector<T>&&);
Vector(const Vector<T>&);
Vector(size_type, const T& = T());
~Vector();
Vector<T>& operator=(Vector<T>&&);
Vector<T>& operator=(const Vector<T>&);
ErrorOr<void> push_back(T&&);
ErrorOr<void> push_back(const T&);
template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
template<typename... Args>
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> insert(size_type, T&&);
ErrorOr<void> insert(size_type, const T&);
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
void pop_back();
void remove(size_type);
void clear();
T* data() { return m_data; }
const T* data() const { return m_data; }
bool contains(const T&) const;
Span<T> span() { return Span(m_data, m_size); }
Span<const T> span() const { return Span(m_data, m_size); }
const T& operator[](size_type) const;
T& operator[](size_type);
const T& back() const;
T& back();
const T& front() const;
T& front();
void reverse();
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit();
bool empty() const;
size_type size() const;
size_type capacity() const;
private:
ErrorOr<void> ensure_capacity(size_type);
private:
T* m_data = nullptr;
size_type m_capacity = 0;
size_type m_size = 0;
};
template<typename T>
Vector<T>::Vector(Vector<T>&& other)
{
m_data = other.m_data;
m_capacity = other.m_capacity;
m_size = other.m_size;
other.m_data = nullptr;
other.m_capacity = 0;
other.m_size = 0;
}
template<typename T>
Vector<T>::Vector(const Vector<T>& other)
{
MUST(ensure_capacity(other.m_size));
for (size_type i = 0; i < other.m_size; i++)
new (m_data + i) T(other.m_data[i]);
m_size = other.m_size;
}
template<typename T>
Vector<T>::Vector(size_type size, const T& value)
{
MUST(ensure_capacity(size));
for (size_type i = 0; i < size; i++)
new (m_data + i) T(value);
m_size = size;
}
template<typename T>
Vector<T>::~Vector()
{
clear();
}
template<typename T>
Vector<T>& Vector<T>::operator=(Vector<T>&& other)
{
clear();
m_data = other.m_data;
m_capacity = other.m_capacity;
m_size = other.m_size;
other.m_data = nullptr;
other.m_capacity = 0;
other.m_size = 0;
return *this;
}
template<typename T>
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
{
MUST(ensure_capacity(other.size()));
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++)
m_data[i] = other.m_data[i];
for (size_type i = size(); i < other.size(); i++)
new (m_data + i) T(other[i]);
for (size_type i = other.size(); i < size(); i++)
m_data[i].~T();
m_size = other.m_size;
return *this;
}
template<typename T>
ErrorOr<void> Vector<T>::push_back(T&& value)
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(move(value));
m_size++;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::push_back(const T& value)
{
return push_back(move(T(value)));
}
template<typename T>
template<typename... Args>
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...);
m_size++;
return {};
}
template<typename T>
template<typename... Args>
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
if (index < m_size)
{
new (m_data + m_size) T(move(m_data[m_size - 1]));
for (size_type i = m_size - 1; i > index; i--)
m_data[i] = move(m_data[i - 1]);
m_data[index] = move(T(forward<Args>(args)...));
}
else
{
new (m_data + m_size) T(forward<Args>(args)...);
}
m_size++;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::insert(size_type index, T&& value)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
if (index < m_size)
{
new (m_data + m_size) T(move(m_data[m_size - 1]));
for (size_type i = m_size - 1; i > index; i--)
m_data[i] = move(m_data[i - 1]);
m_data[index] = move(value);
}
else
{
new (m_data + m_size) T(move(value));
}
m_size++;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
{
return insert(index, move(T(value)));
}
template<typename T>
void Vector<T>::pop_back()
{
ASSERT(m_size > 0);
m_data[m_size - 1].~T();
m_size--;
}
template<typename T>
void Vector<T>::remove(size_type index)
{
ASSERT(index < m_size);
for (size_type i = index; i < m_size - 1; i++)
m_data[i] = move(m_data[i + 1]);
m_data[m_size - 1].~T();
m_size--;
}
template<typename T>
void Vector<T>::clear()
{
for (size_type i = 0; i < m_size; i++)
m_data[i].~T();
BAN::deallocator(m_data);
m_data = nullptr;
m_capacity = 0;
m_size = 0;
}
template<typename T>
bool Vector<T>::contains(const T& other) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == other)
return true;
return false;
}
template<typename T>
const T& Vector<T>::operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
template<typename T>
T& Vector<T>::operator[](size_type index)
{
ASSERT(index < m_size);
return m_data[index];
}
template<typename T>
const T& Vector<T>::back() const
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
template<typename T>
T& Vector<T>::back()
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
template<typename T>
const T& Vector<T>::front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
template<typename T>
T& Vector<T>::front()
{
ASSERT(m_size > 0);
return m_data[0];
}
template<typename T>
void Vector<T>::reverse()
{
for (size_type i = 0; i < m_size / 2; i++)
BAN::swap(m_data[i], m_data[m_size - i - 1]);
}
template<typename T>
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
{
TRY(ensure_capacity(size));
if (size < m_size)
for (size_type i = size; i < m_size; i++)
m_data[i].~T();
if (size > m_size)
for (size_type i = m_size; i < size; i++)
new (m_data + i) T();
m_size = size;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
{
TRY(ensure_capacity(size));
if (size < m_size)
for (size_type i = size; i < m_size; i++)
m_data[i].~T();
if (size > m_size)
for (size_type i = m_size; i < size; i++)
new (m_data + i) T(value);
m_size = size;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::reserve(size_type size)
{
TRY(ensure_capacity(size));
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::shrink_to_fit()
{
size_type temp = m_capacity;
m_capacity = 0;
auto error_or = ensure_capacity(m_size);
if (error_or.is_error())
{
m_capacity = temp;
return error_or;
}
return {};
}
template<typename T>
bool Vector<T>::empty() const
{
return m_size == 0;
}
template<typename T>
typename Vector<T>::size_type Vector<T>::size() const
{
return m_size;
}
template<typename T>
typename Vector<T>::size_type Vector<T>::capacity() const
{
return m_capacity;
}
template<typename T>
ErrorOr<void> Vector<T>::ensure_capacity(size_type size)
{
static_assert(alignof(T) <= alignof(max_align_t), "over aligned types not supported");
if (m_capacity >= size)
return {};
const size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
if constexpr (BAN::is_trivially_copyable_v<T>)
{
if constexpr (BAN::reallocator)
{
auto* new_data = static_cast<T*>(BAN::reallocator(m_data, new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
m_data = new_data;
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, m_data, m_size * sizeof(T));
BAN::deallocator(m_data);
m_data = new_data;
}
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
for (size_type i = 0; i < m_size; i++)
{
new (new_data + i) T(move(m_data[i]));
m_data[i].~T();
}
BAN::deallocator(m_data);
m_data = new_data;
}
m_capacity = new_cap;
return {};
}
}
namespace BAN::Formatter
{
template<typename F, typename T>
void print_argument(F putc, const Vector<T>& vector, const ValueFormat& format)
{
putc('[');
for (typename Vector<T>::size_type i = 0; i < vector.size(); i++)
{
if (i != 0) putc(',');
print_argument(putc, vector[i], format);
}
putc(']');
}
}

View File

@@ -1,128 +0,0 @@
#pragma once
#include <BAN/RefPtr.h>
#if __is_kernel
#include <kernel/Lock/SpinLock.h>
#endif
namespace BAN
{
template<typename T>
class Weakable;
template<typename T>
class WeakPtr;
// FIXME: Write this without using locks...
template<typename T>
class WeakLink : public RefCounted<WeakLink<T>>
{
public:
RefPtr<T> try_lock() const
{
#if __is_kernel
Kernel::SpinLockGuard _(m_weak_lock);
#endif
if (m_ptr && m_ptr->try_ref())
return RefPtr<T>::adopt(m_ptr);
return nullptr;
}
bool valid() const { return m_ptr; }
void invalidate()
{
#if __is_kernel
Kernel::SpinLockGuard _(m_weak_lock);
#endif
m_ptr = nullptr;
}
private:
WeakLink(T* ptr) : m_ptr(ptr) {}
private:
T* m_ptr;
#if __is_kernel
mutable Kernel::SpinLock m_weak_lock;
#endif
friend class RefPtr<WeakLink<T>>;
};
template<typename T>
class Weakable
{
public:
virtual ~Weakable()
{
if (m_link)
m_link->invalidate();
}
ErrorOr<WeakPtr<T>> get_weak_ptr() const
{
if (!m_link)
m_link = TRY(RefPtr<WeakLink<T>>::create((T*)this));
return WeakPtr<T>(m_link);
}
private:
mutable RefPtr<WeakLink<T>> m_link;
};
template<typename T>
class WeakPtr
{
public:
WeakPtr() = default;
WeakPtr(WeakPtr&& other) { *this = move(other); }
WeakPtr(const WeakPtr& other) { *this = other; }
WeakPtr(const RefPtr<T>& other) { *this = other; }
WeakPtr& operator=(WeakPtr&& other)
{
clear();
m_link = move(other.m_link);
return *this;
}
WeakPtr& operator=(const WeakPtr& other)
{
clear();
m_link = other.m_link;
return *this;
}
WeakPtr& operator=(const RefPtr<T>& other)
{
clear();
if (other)
m_link = MUST(other->get_weak_ptr()).move_link();
return *this;
}
RefPtr<T> lock() const
{
if (m_link)
return m_link->try_lock();
return nullptr;
}
void clear() { m_link.clear(); }
bool valid() const { return m_link && m_link->valid(); }
explicit operator bool() const { return valid(); }
private:
WeakPtr(const RefPtr<WeakLink<T>>& link)
: m_link(link)
{ }
RefPtr<WeakLink<T>>&& move_link() { return move(m_link); }
private:
RefPtr<WeakLink<T>> m_link;
friend class Weakable<T>;
};
}

View File

@@ -43,16 +43,21 @@ if(BANAN_OS)
add_library(libdeflate SHARED IMPORTED) add_library(libdeflate SHARED IMPORTED)
set_target_properties(libdeflate PROPERTIES IMPORTED_LOCATION "${LIBDEFLATE}") set_target_properties(libdeflate PROPERTIES IMPORTED_LOCATION "${LIBDEFLATE}")
else() else()
add_subdirectory(BAN) find_library(LIBC c REQUIRED)
add_subdirectory(LibClipboard) add_library(libc SHARED IMPORTED)
add_subdirectory(LibDEFLATE) set_target_properties(libc PROPERTIES IMPORTED_LOCATION "${LIBC}")
add_subdirectory(LibFont)
add_subdirectory(LibGUI) add_subdirectory(banan-os/BAN)
add_subdirectory(LibImage) add_subdirectory(banan-os/userspace/libraries/LibClipboard)
add_subdirectory(LibInput) add_subdirectory(banan-os/userspace/libraries/LibDEFLATE)
add_subdirectory(Terminal) add_subdirectory(banan-os/userspace/libraries/LibFont)
add_subdirectory(ProgramLauncher) add_subdirectory(banan-os/userspace/libraries/LibGUI)
add_subdirectory(WindowServer) add_subdirectory(banan-os/userspace/libraries/LibImage)
add_subdirectory(banan-os/userspace/libraries/LibInput)
add_subdirectory(banan-os/userspace/programs/ProgramLauncher)
add_subdirectory(banan-os/userspace/programs/Terminal)
add_subdirectory(banan-os/userspace/programs/WindowServer)
endif() endif()
add_subdirectory(xbanan) add_subdirectory(xbanan)

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
BSD 2-Clause License
Copyright (c) 2026, Oskari Alaranta
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,9 +0,0 @@
set(LIBCLIPBOARD_SOURCES
Clipboard.cpp
)
add_library(libclipboard ${LIBCLIPBOARD_SOURCES})
banan_link_library(libclipboard ban)
banan_install_headers(libclipboard)
install(TARGETS libclipboard OPTIONAL)

View File

@@ -1,201 +0,0 @@
#include <LibClipboard/Clipboard.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
namespace LibClipboard
{
static int s_server_fd = -1;
static BAN::ErrorOr<void> send_credentials(int fd)
{
char dummy = '\0';
iovec iovec {
.iov_base = &dummy,
.iov_len = 1,
};
constexpr size_t control_size = CMSG_LEN(sizeof(ucred));
uint8_t control_buffer[control_size];
cmsghdr* control = reinterpret_cast<cmsghdr*>(control_buffer);
*control = {
.cmsg_len = control_size,
.cmsg_level = SOL_SOCKET,
.cmsg_type = SCM_CREDENTIALS,
};
*reinterpret_cast<ucred*>(CMSG_DATA(control)) = {
.pid = getpid(),
.uid = getuid(),
.gid = getgid(),
};
const msghdr message {
.msg_name = nullptr,
.msg_namelen = 0,
.msg_iov = &iovec,
.msg_iovlen = 1,
.msg_control = control,
.msg_controllen = control_size,
.msg_flags = 0,
};
if (sendmsg(fd, &message, 0) < 0)
return BAN::Error::from_errno(errno);
return {};
}
static BAN::ErrorOr<void> ensure_connected()
{
if (s_server_fd != -1)
return {};
const int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
return BAN::Error::from_errno(errno);
sockaddr_un server_addr;
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, s_clipboard_server_socket.data());
if (connect(sock, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == -1)
{
close(sock);
return BAN::Error::from_errno(errno);
}
if (auto ret = send_credentials(sock); ret.is_error())
{
close(sock);
return ret;
}
s_server_fd = sock;
return {};
}
static BAN::ErrorOr<void> recv_sized(void* data, size_t size)
{
ASSERT(s_server_fd != -1);
uint8_t* u8_data = static_cast<uint8_t*>(data);
size_t total_recv = 0;
while (total_recv < size)
{
const ssize_t nrecv = recv(s_server_fd, u8_data + total_recv, size - total_recv, 0);
if (nrecv <= 0)
{
const int error = nrecv ? errno : ECONNRESET;
close(s_server_fd);
s_server_fd = -1;
return BAN::Error::from_errno(error);
}
total_recv += nrecv;
}
return {};
}
static BAN::ErrorOr<void> send_sized(const void* data, size_t size)
{
ASSERT(s_server_fd != -1);
const uint8_t* u8_data = static_cast<const uint8_t*>(data);
size_t total_sent = 0;
while (total_sent < size)
{
const ssize_t nsend = send(s_server_fd, u8_data + total_sent, size - total_sent, 0);
if (nsend <= 0)
{
const int error = nsend ? errno : ECONNRESET;
close(s_server_fd);
s_server_fd = -1;
return BAN::Error::from_errno(error);
}
total_sent += nsend;
}
return {};
}
BAN::ErrorOr<Clipboard::Info> Clipboard::get_clipboard()
{
TRY(ensure_connected());
{
DataType type = DataType::__get;
TRY(send_sized(&type, sizeof(type)));
}
Info info;
TRY(recv_sized(&info.type, sizeof(info.type)));
switch (info.type)
{
case DataType::__get:
ASSERT_NOT_REACHED();
case DataType::None:
break;
case DataType::Text:
size_t data_size;
TRY(recv_sized(&data_size, sizeof(data_size)));
TRY(info.data.resize(data_size));
TRY(recv_sized(info.data.data(), data_size));
break;
}
return info;
}
BAN::ErrorOr<void> Clipboard::set_clipboard(DataType type, BAN::Span<const uint8_t> data)
{
ASSERT(type != DataType::__get);
TRY(ensure_connected());
TRY(send_sized(&type, sizeof(type)));
switch (type)
{
case DataType::__get:
ASSERT_NOT_REACHED();
case DataType::None:
break;
case DataType::Text:
const size_t size = data.size();
TRY(send_sized(&size, sizeof(size)));
TRY(send_sized(data.data(), size));
break;
}
return {};
}
BAN::ErrorOr<BAN::String> Clipboard::get_clipboard_text()
{
auto info = TRY(get_clipboard());
if (info.type != DataType::Text)
return BAN::String {};
BAN::String string;
TRY(string.resize(info.data.size()));
memcpy(string.data(), info.data.data(), info.data.size());
return string;
}
BAN::ErrorOr<void> Clipboard::set_clipboard_text(BAN::StringView string)
{
return set_clipboard(DataType::Text, { reinterpret_cast<const uint8_t*>(string.data()), string.size() });
}
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <BAN/Span.h>
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
namespace LibClipboard
{
static constexpr BAN::StringView s_clipboard_server_socket = "/tmp/clipboard-server.socket"_sv;
class Clipboard
{
public:
enum class DataType : uint32_t
{
None,
Text,
__get = UINT32_MAX,
};
struct Info
{
DataType type = DataType::None;
BAN::Vector<uint8_t> data;
};
public:
static BAN::ErrorOr<Info> get_clipboard();
static BAN::ErrorOr<void> set_clipboard(DataType type, BAN::Span<const uint8_t> data);
static BAN::ErrorOr<BAN::String> get_clipboard_text();
static BAN::ErrorOr<void> set_clipboard_text(BAN::StringView string);
};
}

View File

@@ -1,11 +0,0 @@
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)

View File

@@ -1,647 +0,0 @@
#include <LibDEFLATE/Compressor.h>
#include <LibDEFLATE/Utils.h>
#include <BAN/Array.h>
#include <BAN/Heap.h>
#include <BAN/Optional.h>
#include <BAN/Sort.h>
namespace LibDEFLATE
{
constexpr size_t s_max_length = 258;
constexpr size_t s_max_distance = 32768;
constexpr size_t s_max_symbols = 288;
constexpr uint8_t s_max_bits = 15;
struct Leaf
{
uint16_t code;
uint8_t length;
};
static BAN::ErrorOr<void> create_huffman_tree(BAN::Span<const size_t> freq, BAN::Span<Leaf> output)
{
ASSERT(freq.size() <= s_max_symbols);
ASSERT(freq.size() == output.size());
struct node_t
{
size_t symbol;
size_t freq;
node_t* left;
node_t* right;
};
#if LIBDEFLATE_AVOID_STACK
BAN::Vector<node_t*> nodes;
TRY(nodes.resize(s_max_symbols));
#else
BAN::Array<node_t*, s_max_symbols> nodes;
#endif
size_t node_count = 0;
for (size_t sym = 0; sym < freq.size(); sym++)
{
if (freq[sym] == 0)
continue;
nodes[node_count] = static_cast<node_t*>(BAN::allocator(sizeof(node_t)));
if (nodes[node_count] == nullptr)
{
for (size_t j = 0; j < node_count; j++)
BAN::deallocator(nodes[j]);
return BAN::Error::from_errno(ENOMEM);
}
*nodes[node_count++] = {
.symbol = sym,
.freq = freq[sym],
.left = nullptr,
.right = nullptr,
};
}
for (auto& symbol : output)
symbol = { .code = 0, .length = 0 };
if (node_count == 0)
{
output[0] = { .code = 0, .length = 1 };
return {};
}
static void (*free_tree)(node_t*) =
[](node_t* root) -> void {
if (root == nullptr)
return;
free_tree(root->left);
free_tree(root->right);
BAN::deallocator(root);
};
const auto comp =
[](const node_t* a, const node_t* b) -> bool {
if (a->freq != b->freq)
return a->freq > b->freq;
return a->symbol > b->symbol;
};
auto end_it = nodes.begin() + node_count;
BAN::make_heap(nodes.begin(), end_it, comp);
while (nodes.begin() + 1 != end_it)
{
node_t* parent = static_cast<node_t*>(BAN::allocator(sizeof(node_t)));
if (parent == nullptr)
{
for (auto it = nodes.begin(); it != end_it; it++)
free_tree(*it);
return BAN::Error::from_errno(ENOMEM);
}
node_t* node1 = nodes.front();
BAN::pop_heap(nodes.begin(), end_it--, comp);
node_t* node2 = nodes.front();
BAN::pop_heap(nodes.begin(), end_it--, comp);
*parent = {
.symbol = 0,
.freq = node1->freq + node2->freq,
.left = node1,
.right = node2,
};
*end_it++ = parent;
BAN::push_heap(nodes.begin(), end_it, comp);
}
static uint16_t (*gather_lengths)(const node_t*, BAN::Span<Leaf>, uint16_t) =
[](const node_t* node, BAN::Span<Leaf> symbols, uint16_t depth) -> uint16_t {
if (node == nullptr)
return 0;
uint16_t count = (depth > s_max_bits);
if (node->left == nullptr && node->right == nullptr)
symbols[node->symbol].length = BAN::Math::min<uint16_t>(depth, s_max_bits);
else
{
count += gather_lengths(node->left, symbols, depth + 1);
count += gather_lengths(node->right, symbols, depth + 1);
}
return count;
};
const auto too_long_count = gather_lengths(nodes[0], output, 0);
free_tree(nodes[0]);
uint16_t bl_count[s_max_bits + 1] {};
for (size_t sym = 0; sym < freq.size(); sym++)
if (const uint8_t len = output[sym].length)
bl_count[len]++;
if (too_long_count > 0)
{
for (size_t i = 0; i < too_long_count / 2; i++)
{
uint16_t bits = s_max_bits - 1;
while (bl_count[bits] == 0)
bits--;
bl_count[bits + 0]--;
bl_count[bits + 1] += 2;
bl_count[s_max_bits]--;
}
struct SymFreq
{
size_t symbol;
size_t freq;
};
BAN::Vector<SymFreq> sym_freq;
for (size_t sym = 0; sym < output.size(); sym++)
if (freq[sym] != 0)
TRY(sym_freq.push_back({ .symbol = sym, .freq = freq[sym] }));
BAN::sort::sort(sym_freq.begin(), sym_freq.end(),
[](auto a, auto b) { return a.freq < b.freq; }
);
size_t index = 0;
for (uint16_t bits = s_max_bits; bits > 0; bits--)
for (size_t i = 0; i < bl_count[bits]; i++)
output[sym_freq[index++].symbol].length = bits;
ASSERT(index == sym_freq.size());
}
uint16_t next_code[s_max_bits + 1] {};
uint16_t code = 0;
for (uint8_t bits = 1; bits <= s_max_bits; bits++)
{
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
for (size_t sym = 0; sym < freq.size(); sym++)
if (const uint16_t len = output[sym].length)
output[sym].code = next_code[len]++;
return {};
}
struct Encoding
{
uint16_t symbol;
uint16_t extra_data { 0 };
uint8_t extra_len { 0 };
};
static constexpr Encoding get_len_encoding(uint16_t length)
{
ASSERT(3 <= length && length <= s_max_length);
constexpr uint16_t base[] {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
constexpr uint8_t extra_bits[] {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
constexpr size_t count = sizeof(base) / sizeof(*base);
for (size_t i = 0;; i++)
{
if (i + 1 < count && length >= base[i + 1])
continue;
return {
.symbol = static_cast<uint16_t>(257 + i),
.extra_data = static_cast<uint16_t>(length - base[i]),
.extra_len = extra_bits[i],
};
}
}
static constexpr Encoding get_dist_encoding(uint16_t distance)
{
ASSERT(1 <= distance && distance <= s_max_distance);
constexpr uint16_t base[] {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
constexpr uint8_t extra_bits[] {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
constexpr size_t count = sizeof(base) / sizeof(*base);
for (size_t i = 0;; i++)
{
if (i + 1 < count && distance >= base[i + 1])
continue;
return {
.symbol = static_cast<uint16_t>(i),
.extra_data = static_cast<uint16_t>(distance - base[i]),
.extra_len = extra_bits[i],
};
}
}
static void get_frequencies(BAN::Span<const Compressor::LZ77Entry> entries, BAN::Span<size_t> lit_len_freq, BAN::Span<size_t> dist_freq)
{
ASSERT(lit_len_freq.size() == 286);
ASSERT(dist_freq.size() == 30);
for (auto entry : entries)
{
switch (entry.type)
{
case Compressor::LZ77Entry::Type::Literal:
lit_len_freq[entry.as.literal]++;
break;
case Compressor::LZ77Entry::Type::DistLength:
lit_len_freq[get_len_encoding(entry.as.dist_length.length).symbol]++;
dist_freq[get_dist_encoding(entry.as.dist_length.distance).symbol]++;
break;
}
}
lit_len_freq[256]++;
}
struct CodeLengthInfo
{
uint16_t hlit;
uint8_t hdist;
uint8_t hclen;
BAN::Vector<Encoding> encoding;
BAN::Array<uint8_t, 19> code_length;
BAN::Array<Leaf, 19> code_length_tree;
};
static BAN::ErrorOr<CodeLengthInfo> build_code_length_info(BAN::Span<const Leaf> lit_len_tree, BAN::Span<const Leaf> dist_tree)
{
CodeLengthInfo result;
const auto append_tree =
[&result](BAN::Span<const Leaf>& tree) -> BAN::ErrorOr<void>
{
while (!tree.empty() && tree[tree.size() - 1].length == 0)
tree = tree.slice(0, tree.size() - 1);
for (size_t i = 0; i < tree.size();)
{
size_t count = 1;
while (i + count < tree.size() && tree[i].length == tree[i + count].length)
count++;
if (tree[i].length == 0)
{
if (count > 138)
count = 138;
if (count < 3)
{
for (size_t j = 0; j < count; j++)
TRY(result.encoding.push_back({ .symbol = 0 }));
}
else if (count < 11)
{
TRY(result.encoding.push_back({
.symbol = 17,
.extra_data = static_cast<uint8_t>(count - 3),
.extra_len = 3,
}));
}
else
{
TRY(result.encoding.push_back({
.symbol = 18,
.extra_data = static_cast<uint8_t>(count - 11),
.extra_len = 7,
}));
}
}
else
{
if (count >= 3 && !result.encoding.empty() && result.encoding.back().symbol == tree[i].length)
{
if (count > 6)
count = 6;
TRY(result.encoding.push_back({
.symbol = 16,
.extra_data = static_cast<uint8_t>(count - 3),
.extra_len = 2,
}));
}
else
{
count = 1;
TRY(result.encoding.push_back({ .symbol = tree[i].length }));
}
}
i += count;
}
return {};
};
TRY(append_tree(lit_len_tree));
result.hlit = lit_len_tree.size();
TRY(append_tree(dist_tree));
result.hdist = dist_tree.size();
BAN::Array<size_t, 19> code_len_freq(0);
for (auto entry : result.encoding)
code_len_freq[entry.symbol]++;
TRY(create_huffman_tree(code_len_freq.span(), result.code_length_tree.span()));
constexpr uint8_t code_length_order[] {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
for (size_t i = 0; i < result.code_length_tree.size(); i++)
result.code_length[i] = result.code_length_tree[code_length_order[i]].length;
result.hclen = 19;
while (result.hclen > 4 && result.code_length[result.hclen - 1] == 0)
result.hclen--;
return BAN::move(result);
}
uint32_t Compressor::get_hash_key(BAN::ConstByteSpan needle) const
{
ASSERT(needle.size() >= 3);
return (needle[2] << 16) | (needle[1] << 8) | needle[0];
}
BAN::ErrorOr<void> Compressor::update_hash_chain(size_t count)
{
if (m_hash_chain.size() >= s_max_distance * 2)
{
const uint8_t* current = m_data.data() + m_hash_chain_index + count;
for (auto& [_, chain] : m_hash_chain)
{
for (auto it = chain.begin(); it != chain.end(); it++)
{
const size_t distance = current - it->data();
if (distance < s_max_distance)
continue;
while (it != chain.end())
it = chain.remove(it);
break;
}
}
}
for (size_t i = 0; i < count; i++)
{
auto slice = m_data.slice(m_hash_chain_index + i);
if (slice.size() < 3)
break;
const uint32_t key = get_hash_key(slice);
auto it = m_hash_chain.find(key);
if (it != m_hash_chain.end())
TRY(it->value.insert(it->value.begin(), slice));
else
{
HashChain new_chain;
TRY(new_chain.push_back(slice));
TRY(m_hash_chain.insert(key, BAN::move(new_chain)));
}
}
m_hash_chain_index += count;
return {};
}
BAN::ErrorOr<Compressor::LZ77Entry> Compressor::find_longest_match(BAN::ConstByteSpan needle) const
{
LZ77Entry result = {
.type = LZ77Entry::Type::Literal,
.as = { .literal = needle[0] }
};
if (needle.size() < 3)
return result;
const uint32_t key = get_hash_key(needle);
auto it = m_hash_chain.find(key);
if (it == m_hash_chain.end())
return result;
auto& chain = it->value;
for (const auto node : chain)
{
const size_t distance = needle.data() - node.data();
if (distance > s_max_distance)
break;
size_t length = 3;
const size_t max_length = BAN::Math::min(needle.size(), s_max_length);
while (length < max_length && needle[length] == node[length])
length++;
if (result.type != LZ77Entry::Type::DistLength || length > result.as.dist_length.length)
{
result = LZ77Entry {
.type = LZ77Entry::Type::DistLength,
.as = {
.dist_length = {
.length = static_cast<uint16_t>(length),
.distance = static_cast<uint16_t>(distance),
}
}
};
}
}
return result;
}
BAN::ErrorOr<BAN::Vector<Compressor::LZ77Entry>> Compressor::lz77_compress(BAN::ConstByteSpan data)
{
BAN::Vector<LZ77Entry> result;
size_t advance = 0;
for (size_t i = 0; i < data.size(); i += advance)
{
TRY(update_hash_chain(advance));
auto match = TRY(find_longest_match(data.slice(i)));
if (match.type == LZ77Entry::Type::Literal)
{
TRY(result.push_back(match));
advance = 1;
continue;
}
ASSERT(match.type == LZ77Entry::Type::DistLength);
auto lazy_match = TRY(find_longest_match(data.slice(i + 1)));
if (lazy_match.type == LZ77Entry::Type::DistLength && lazy_match.as.dist_length.length > match.as.dist_length.length)
{
TRY(result.push_back({ .type = LZ77Entry::Type::Literal, .as = { .literal = data[i] }}));
TRY(result.push_back(lazy_match));
advance = 1 + lazy_match.as.dist_length.length;
}
else
{
TRY(result.push_back(match));
advance = match.as.dist_length.length;
}
}
return result;
}
BAN::ErrorOr<void> Compressor::compress_block(BAN::ConstByteSpan data, bool final)
{
// FIXME: use fixed trees or uncompressed blocks
auto lz77_entries = TRY(lz77_compress(data));
#if LIBDEFLATE_AVOID_STACK
BAN::Vector<size_t> lit_len_freq, dist_freq;
TRY(lit_len_freq.resize(286, 0));
TRY(dist_freq.resize(30, 0));
#else
BAN::Array<size_t, 286> lit_len_freq(0);
BAN::Array<size_t, 30> dist_freq(0);
#endif
get_frequencies(lz77_entries.span(), lit_len_freq.span(), dist_freq.span());
#if LIBDEFLATE_AVOID_STACK
BAN::Vector<Leaf> lit_len_tree, dist_tree;
TRY(lit_len_tree.resize(286));
TRY(dist_tree.resize(30));
#else
BAN::Array<Leaf, 286> lit_len_tree;
BAN::Array<Leaf, 30> dist_tree;
#endif
TRY(create_huffman_tree(lit_len_freq.span(), lit_len_tree.span()));
TRY(create_huffman_tree(dist_freq.span(), dist_tree.span()));
auto info = TRY(build_code_length_info(lit_len_tree.span(), dist_tree.span()));
TRY(m_stream.write_bits(final, 1));
TRY(m_stream.write_bits(2, 2));
TRY(m_stream.write_bits(info.hlit - 257, 5));
TRY(m_stream.write_bits(info.hdist - 1, 5));
TRY(m_stream.write_bits(info.hclen - 4, 4));
for (size_t i = 0; i < info.hclen; i++)
TRY(m_stream.write_bits(info.code_length[i], 3));
for (const auto entry : info.encoding)
{
const auto symbol = info.code_length_tree[entry.symbol];
TRY(m_stream.write_bits(reverse_bits(symbol.code, symbol.length), symbol.length));
TRY(m_stream.write_bits(entry.extra_data, entry.extra_len));
}
for (const auto entry : lz77_entries)
{
switch (entry.type)
{
case LZ77Entry::Type::Literal:
{
const auto symbol = lit_len_tree[entry.as.literal];
TRY(m_stream.write_bits(reverse_bits(symbol.code, symbol.length), symbol.length));
break;
}
case LZ77Entry::Type::DistLength:
{
const auto len_encoding = get_len_encoding(entry.as.dist_length.length);
const auto len_code = lit_len_tree[len_encoding.symbol];
TRY(m_stream.write_bits(reverse_bits(len_code.code, len_code.length), len_code.length));
TRY(m_stream.write_bits(len_encoding.extra_data, len_encoding.extra_len));
const auto dist_encoding = get_dist_encoding(entry.as.dist_length.distance);
const auto dist_code = dist_tree[dist_encoding.symbol];
TRY(m_stream.write_bits(reverse_bits(dist_code.code, dist_code.length), dist_code.length));
TRY(m_stream.write_bits(dist_encoding.extra_data, dist_encoding.extra_len));
break;
}
}
}
const auto end_code = lit_len_tree[256];
TRY(m_stream.write_bits(reverse_bits(end_code.code, end_code.length), end_code.length));
return {};
}
BAN::ErrorOr<BAN::Vector<uint8_t>> Compressor::compress()
{
const uint32_t data_size = m_data.size();
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;
case StreamType::GZip:
{
const time_t current_time = time(nullptr);
TRY(m_stream.write_bits(0x1F, 8)); // ID1
TRY(m_stream.write_bits(0x8B, 8)); // ID2
TRY(m_stream.write_bits(8, 8)); // CM (deflate)
TRY(m_stream.write_bits(0, 8)); // FLG
TRY(m_stream.write_bits(current_time >> 0, 8)); // MTIME
TRY(m_stream.write_bits(current_time >> 8, 8));
TRY(m_stream.write_bits(current_time >> 16, 8));
TRY(m_stream.write_bits(current_time >> 24, 8));
TRY(m_stream.write_bits(0, 8)); // XFL
TRY(m_stream.write_bits(3, 8)); // OS (Unix)
checksum = calculate_crc32(m_data);
}
}
constexpr size_t max_block_size = 16 * 1024;
while (!m_data.empty())
{
const size_t block_size = BAN::Math::min<size_t>(m_data.size(), max_block_size);
TRY(compress_block(m_data.slice(0, block_size), block_size == m_data.size()));
m_data = m_data.slice(block_size);
}
TRY(m_stream.pad_to_byte_boundary());
switch (m_type)
{
case StreamType::Raw:
break;
case StreamType::Zlib:
TRY(m_stream.write_bits(checksum >> 24, 8));
TRY(m_stream.write_bits(checksum >> 16, 8));
TRY(m_stream.write_bits(checksum >> 8, 8));
TRY(m_stream.write_bits(checksum >> 0, 8));
break;
case StreamType::GZip:
TRY(m_stream.write_bits(checksum >> 0, 8));
TRY(m_stream.write_bits(checksum >> 8, 8));
TRY(m_stream.write_bits(checksum >> 16, 8));
TRY(m_stream.write_bits(checksum >> 24, 8));
TRY(m_stream.write_bits(data_size >> 0, 8));
TRY(m_stream.write_bits(data_size >> 8, 8));
TRY(m_stream.write_bits(data_size >> 16, 8));
TRY(m_stream.write_bits(data_size >> 24, 8));
break;
}
return m_stream.take_buffer();
}
}

View File

@@ -1,355 +0,0 @@
#include <LibDEFLATE/Decompressor.h>
#include <LibDEFLATE/Utils.h>
namespace LibDEFLATE
{
union ZLibHeader
{
struct
{
uint8_t cm : 4;
uint8_t cinfo : 4;
uint8_t fcheck : 5;
uint8_t fdict : 1;
uint8_t flevel : 2;
};
struct
{
uint8_t raw1;
uint8_t raw2;
};
};
BAN::ErrorOr<uint16_t> Decompressor::read_symbol(const HuffmanTree& tree)
{
const uint8_t instant_bits = tree.instant_bits();
uint16_t code = reverse_bits(TRY(m_stream.peek_bits(instant_bits)), instant_bits);
if (auto symbol = tree.get_symbol_instant(code); symbol.has_value())
{
MUST(m_stream.take_bits(symbol->len));
return symbol->symbol;
}
MUST(m_stream.take_bits(instant_bits));
uint8_t len = instant_bits;
while (len < tree.max_bits())
{
code = (code << 1) | TRY(m_stream.take_bits(1));
len++;
if (auto symbol = tree.get_symbol(code, len); symbol.has_value())
return symbol.value();
}
return BAN::Error::from_errno(EINVAL);
}
BAN::ErrorOr<void> Decompressor::inflate_block(const HuffmanTree& length_tree, const HuffmanTree& distance_tree)
{
uint16_t symbol;
while ((symbol = TRY(read_symbol(length_tree))) != 256)
{
if (symbol < 256)
{
TRY(m_output.push_back(symbol));
continue;
}
constexpr uint16_t length_base[] {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
constexpr uint8_t length_extra_bits[] {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
constexpr uint16_t distance_base[] {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
constexpr uint8_t distance_extra_bits[] {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
if (symbol > 285)
return BAN::Error::from_errno(EINVAL);
symbol -= 257;
const uint16_t length = length_base[symbol] + TRY(m_stream.take_bits(length_extra_bits[symbol]));
uint16_t distance_code;
if (distance_tree.empty())
distance_code = reverse_bits(TRY(m_stream.take_bits(5)), 5);
else
distance_code = TRY(read_symbol(distance_tree));
if (distance_code > 29)
return BAN::Error::from_errno(EINVAL);
const uint16_t distance = distance_base[distance_code] + TRY(m_stream.take_bits(distance_extra_bits[distance_code]));
const size_t orig_size = m_output.size();
const size_t offset = orig_size - distance;
TRY(m_output.resize(orig_size + length));
for (size_t i = 0; i < length; i++)
m_output[orig_size + i] = m_output[offset + i];
}
return {};
}
BAN::ErrorOr<void> Decompressor::handle_header()
{
switch (m_type)
{
case StreamType::Raw:
return {};
case StreamType::Zlib:
{
ZLibHeader header;
header.raw1 = TRY(m_stream.take_bits(8));
header.raw2 = TRY(m_stream.take_bits(8));
if (((header.raw1 << 8) | header.raw2) % 31)
{
dwarnln("zlib header checksum failed");
return BAN::Error::from_errno(EINVAL);
}
if (header.cm != 8)
{
dwarnln("zlib does not use DEFLATE");
return BAN::Error::from_errno(EINVAL);
}
if (header.fdict)
{
TRY(m_stream.take_bits(16));
TRY(m_stream.take_bits(16));
}
return {};
}
case StreamType::GZip:
{
const uint8_t id1 = TRY(m_stream.take_bits(8));
const uint8_t id2 = TRY(m_stream.take_bits(8));
if (id1 != 0x1F || id2 != 0x8B)
{
dwarnln("gzip header invalid identification");
return BAN::Error::from_errno(EINVAL);
}
const uint8_t cm = TRY(m_stream.take_bits(8));
if (cm != 8)
{
dwarnln("gzip does not use DEFLATE");
return BAN::Error::from_errno(EINVAL);
}
const uint8_t flg = TRY(m_stream.take_bits(8));
TRY(m_stream.take_bits(16)); // mtime
TRY(m_stream.take_bits(16));
TRY(m_stream.take_bits(8)); // xfl
TRY(m_stream.take_bits(8)); // os
// extra fields
if (flg & (1 << 2))
{
const uint16_t xlen = TRY(m_stream.take_bits(16));
for (size_t i = 0; i < xlen; i++)
TRY(m_stream.take_bits(8));
}
// file name
if (flg & (1 << 3))
while (TRY(m_stream.take_bits(8)) != '\0')
continue;
// file comment
if (flg & (1 << 4))
while (TRY(m_stream.take_bits(8)) != '\0')
continue;
// crc16
// TODO: validate
if (flg & (1 << 1))
TRY(m_stream.take_bits(16));
return {};
}
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Decompressor::handle_footer()
{
switch (m_type)
{
case StreamType::Raw:
return {};
case StreamType::Zlib:
{
m_stream.skip_to_byte_boundary();
uint32_t adler32 = 0;
for (size_t i = 0; i < 4; i++)
adler32 = (adler32 << 8) | TRY(m_stream.take_bits(8));
if (adler32 != calculate_adler32(m_output.span()))
{
dwarnln("zlib final adler32 checksum failed");
return BAN::Error::from_errno(EINVAL);
}
return {};
}
case StreamType::GZip:
{
m_stream.skip_to_byte_boundary();
const uint32_t crc32 =
static_cast<uint32_t>(TRY(m_stream.take_bits(16))) |
static_cast<uint32_t>(TRY(m_stream.take_bits(16))) << 16;
if (crc32 != calculate_crc32(m_output.span()))
{
dwarnln("gzip final crc32 checksum failed");
return BAN::Error::from_errno(EINVAL);
}
const uint32_t isize =
static_cast<uint32_t>(TRY(m_stream.take_bits(16))) |
static_cast<uint32_t>(TRY(m_stream.take_bits(16))) << 16;
if (isize != m_output.size() % UINT32_MAX)
{
dwarnln("gzip final isize does not match {} vs {}", isize, m_output.size());
return BAN::Error::from_errno(EINVAL);
}
return {};
}
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Decompressor::decompress_type0()
{
m_stream.skip_to_byte_boundary();
const uint16_t len = TRY(m_stream.take_bits(16));
const uint16_t nlen = TRY(m_stream.take_bits(16));
if (len != 0xFFFF - nlen)
return BAN::Error::from_errno(EINVAL);
const size_t orig_size = m_output.size();
TRY(m_output.resize(orig_size + len));
TRY(m_stream.take_byte_aligned(&m_output[orig_size], len));
return {};
}
BAN::ErrorOr<void> Decompressor::decompress_type1()
{
if (!m_fixed_tree.has_value())
m_fixed_tree = TRY(HuffmanTree::fixed_tree());
TRY(inflate_block(m_fixed_tree.value(), {}));
return {};
}
BAN::ErrorOr<void> Decompressor::decompress_type2()
{
constexpr uint8_t code_length_order[] {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
const uint16_t hlit = TRY(m_stream.take_bits(5)) + 257;
const uint8_t hdist = TRY(m_stream.take_bits(5)) + 1;
const uint8_t hclen = TRY(m_stream.take_bits(4)) + 4;
uint8_t code_lengths[19] {};
for (size_t i = 0; i < hclen; i++)
code_lengths[code_length_order[i]] = TRY(m_stream.take_bits(3));
const auto code_length_tree = TRY(HuffmanTree::create({ code_lengths, 19 }));
uint8_t bit_lengths[286 + 32] {};
size_t bit_lengths_len = 0;
uint16_t last_symbol = 0;
while (bit_lengths_len < hlit + hdist)
{
uint16_t symbol = TRY(read_symbol(code_length_tree));
if (symbol > 18)
return BAN::Error::from_errno(EINVAL);
uint8_t count;
if (symbol <= 15)
{
count = 1;
}
else if (symbol == 16)
{
symbol = last_symbol;
count = TRY(m_stream.take_bits(2)) + 3;
}
else if (symbol == 17)
{
symbol = 0;
count = TRY(m_stream.take_bits(3)) + 3;
}
else
{
symbol = 0;
count = TRY(m_stream.take_bits(7)) + 11;
}
ASSERT(bit_lengths_len + count <= hlit + hdist);
for (uint8_t i = 0; i < count; i++)
bit_lengths[bit_lengths_len++] = symbol;
last_symbol = symbol;
}
TRY(inflate_block(
TRY(HuffmanTree::create({ bit_lengths, hlit })),
TRY(HuffmanTree::create({ bit_lengths + hlit, hdist }))
));
return {};
}
BAN::ErrorOr<BAN::Vector<uint8_t>> Decompressor::decompress()
{
TRY(handle_header());
bool bfinal = false;
while (!bfinal)
{
bfinal = TRY(m_stream.take_bits(1));
switch (TRY(m_stream.take_bits(2)))
{
case 0b00:
TRY(decompress_type0());
break;
case 0b01:
TRY(decompress_type1());
break;
case 0b10:
TRY(decompress_type2());
break;
default:
return BAN::Error::from_errno(EINVAL);
}
}
TRY(handle_footer());
return BAN::move(m_output);
}
}

View File

@@ -1,141 +0,0 @@
#include <LibDEFLATE/HuffmanTree.h>
namespace LibDEFLATE
{
HuffmanTree& HuffmanTree::operator=(HuffmanTree&& other)
{
m_instant_bits = other.m_instant_bits;
m_min_bits = other.m_min_bits;
m_max_bits = other.m_max_bits;
m_instant = BAN::move(other.m_instant);
m_min_code = BAN::move(other.m_min_code);
m_slow_table = BAN::move(other.m_slow_table);
return *this;
}
BAN::ErrorOr<HuffmanTree> HuffmanTree::create(BAN::Span<const uint8_t> bit_lengths)
{
HuffmanTree result;
TRY(result.initialize(bit_lengths));
return result;
}
BAN::ErrorOr<void> HuffmanTree::initialize(BAN::Span<const uint8_t> bit_lengths)
{
m_max_bits = 0;
m_min_bits = MAX_BITS;
uint16_t max_sym = 0;
uint16_t bl_count[MAX_BITS + 1] {};
for (size_t sym = 0; sym < bit_lengths.size(); sym++)
{
if (bit_lengths[sym] == 0)
continue;
m_max_bits = BAN::Math::max(bit_lengths[sym], m_max_bits);
m_min_bits = BAN::Math::min(bit_lengths[sym], m_min_bits);
bl_count[bit_lengths[sym]]++;
max_sym = sym;
}
uint16_t next_code[MAX_BITS + 1] {};
uint16_t code = 0;
for (uint8_t bits = 1; bits <= MAX_BITS; bits++)
{
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
m_min_code[bits] = code;
}
BAN::Vector<Leaf> tree;
TRY(tree.resize(max_sym + 1, { .code = 0, .len = 0 }));
for (uint16_t sym = 0; sym <= max_sym; sym++)
{
tree[sym].len = bit_lengths[sym];
if (const uint8_t len = tree[sym].len)
tree[sym].code = next_code[len]++;
}
TRY(build_instant_table(tree.span()));
TRY(build_slow_table(tree.span()));
return {};
}
BAN::ErrorOr<void> HuffmanTree::build_instant_table(BAN::Span<const Leaf> tree)
{
m_instant_bits = BAN::Math::min<uint8_t>(9, m_max_bits);
TRY(m_instant.resize(1 << m_instant_bits, {}));
for (uint16_t sym = 0; sym < tree.size(); sym++)
{
if (tree[sym].len == 0 || tree[sym].len > m_instant_bits)
continue;
const uint16_t code = tree[sym].code;
const uint16_t shift = m_instant_bits - tree[sym].len;
for (uint16_t j = code << shift; j < (code + 1) << shift; j++)
m_instant[j] = { sym, tree[sym].len };
}
return {};
}
BAN::ErrorOr<void> HuffmanTree::build_slow_table(BAN::Span<const Leaf> tree)
{
TRY(m_slow_table.resize(MAX_BITS + 1));
for (uint16_t sym = 0; sym < tree.size(); sym++)
{
const auto leaf = tree[sym];
if (leaf.len == 0)
continue;
const size_t offset = leaf.code - m_min_code[leaf.len];
if (offset >= m_slow_table[leaf.len].size())
TRY(m_slow_table[leaf.len].resize(offset + 1));
m_slow_table[leaf.len][offset] = sym;
}
return {};
}
BAN::ErrorOr<HuffmanTree> HuffmanTree::fixed_tree()
{
struct BitLengths
{
consteval BitLengths()
{
size_t i = 0;
for (; i <= 143; i++) values[i] = 8;
for (; i <= 255; i++) values[i] = 9;
for (; i <= 279; i++) values[i] = 7;
for (; i <= 287; i++) values[i] = 8;
}
BAN::Array<uint8_t, 288> values;
};
static constexpr BitLengths bit_lengths;
return TRY(HuffmanTree::create(bit_lengths.values.span()));
}
BAN::Optional<HuffmanTree::Instant> HuffmanTree::get_symbol_instant(uint16_t code) const
{
ASSERT(code < m_instant.size());
if (const auto entry = m_instant[code]; entry.len)
return entry;
return {};
}
BAN::Optional<uint16_t> HuffmanTree::get_symbol(uint16_t code, uint8_t len) const
{
ASSERT(len <= m_max_bits);
const auto& symbols = m_slow_table[len];
const size_t offset = code - m_min_code[len];
if (symbols.size() <= offset)
return {};
return symbols[offset];
}
}

View File

@@ -1,118 +0,0 @@
#pragma once
#include <BAN/Vector.h>
#include <BAN/ByteSpan.h>
namespace LibDEFLATE
{
class BitInputStream
{
public:
BitInputStream(BAN::ConstByteSpan data)
: m_data(data)
{ }
BAN::ErrorOr<uint16_t> peek_bits(size_t count)
{
ASSERT(count <= 16);
while (m_bit_buffer_len < count)
{
if (m_data.empty())
return BAN::Error::from_errno(ENOBUFS);
m_bit_buffer |= m_data[0] << m_bit_buffer_len;
m_bit_buffer_len += 8;
m_data = m_data.slice(1);
}
return m_bit_buffer & ((1 << count) - 1);
}
BAN::ErrorOr<uint16_t> take_bits(size_t count)
{
const uint16_t result = TRY(peek_bits(count));
m_bit_buffer >>= count;
m_bit_buffer_len -= count;
return result;
}
BAN::ErrorOr<void> take_byte_aligned(uint8_t* output, size_t bytes)
{
ASSERT(m_bit_buffer % 8 == 0);
while (m_bit_buffer_len && bytes)
{
*output++ = m_bit_buffer;
m_bit_buffer >>= 8;
m_bit_buffer_len -= 8;
bytes--;
}
if (bytes > m_data.size())
return BAN::Error::from_errno(EINVAL);
memcpy(output, m_data.data(), bytes);
m_data = m_data.slice(bytes);
return {};
}
void skip_to_byte_boundary()
{
const size_t bits_to_remove = m_bit_buffer_len % 8;
m_bit_buffer >>= bits_to_remove;
m_bit_buffer_len -= bits_to_remove;
}
private:
BAN::ConstByteSpan m_data;
uint32_t m_bit_buffer { 0 };
uint8_t m_bit_buffer_len { 0 };
};
class BitOutputStream
{
public:
BAN::ErrorOr<void> write_bits(uint16_t value, size_t count)
{
ASSERT(m_bit_buffer_len < 8);
ASSERT(count <= 16);
const uint16_t mask = (1 << count) - 1;
m_bit_buffer |= (value & mask) << m_bit_buffer_len;
m_bit_buffer_len += count;
while (m_bit_buffer_len >= 8)
{
TRY(m_data.push_back(m_bit_buffer));
m_bit_buffer >>= 8;
m_bit_buffer_len -= 8;
}
return {};
}
BAN::ErrorOr<void> pad_to_byte_boundary()
{
ASSERT(m_bit_buffer_len < 8);
if (m_bit_buffer_len == 0)
return {};
TRY(m_data.push_back(m_bit_buffer));
m_bit_buffer = 0;
m_bit_buffer_len = 0;
return {};
}
BAN::Vector<uint8_t> take_buffer()
{
ASSERT(m_bit_buffer_len == 0);
return BAN::move(m_data);
}
private:
BAN::Vector<uint8_t> m_data;
uint32_t m_bit_buffer { 0 };
uint8_t m_bit_buffer_len { 0 };
};
}

View File

@@ -1,67 +0,0 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/HashMap.h>
#include <BAN/LinkedList.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Vector.h>
#include <LibDEFLATE/BitStream.h>
#include <LibDEFLATE/StreamType.h>
namespace LibDEFLATE
{
class Compressor
{
BAN_NON_COPYABLE(Compressor);
BAN_NON_MOVABLE(Compressor);
public:
using HashChain = BAN::LinkedList<BAN::ConstByteSpan>;
struct LZ77Entry
{
enum class Type
{
Literal,
DistLength,
} type;
union
{
uint8_t literal;
struct
{
uint16_t length;
uint16_t distance;
} dist_length;
} as;
};
public:
Compressor(BAN::ConstByteSpan data, StreamType type)
: m_type(type)
, m_data(data)
{ }
BAN::ErrorOr<BAN::Vector<uint8_t>> compress();
private:
BAN::ErrorOr<void> compress_block(BAN::ConstByteSpan, bool final);
uint32_t get_hash_key(BAN::ConstByteSpan needle) const;
BAN::ErrorOr<void> update_hash_chain(size_t count);
BAN::ErrorOr<LZ77Entry> find_longest_match(BAN::ConstByteSpan needle) const;
BAN::ErrorOr<BAN::Vector<LZ77Entry>> lz77_compress(BAN::ConstByteSpan data);
private:
const StreamType m_type;
BAN::ConstByteSpan m_data;
BitOutputStream m_stream;
size_t m_hash_chain_index { 0 };
BAN::HashMap<uint32_t, HashChain> m_hash_chain;
};
}

View File

@@ -1,46 +0,0 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Vector.h>
#include <LibDEFLATE/BitStream.h>
#include <LibDEFLATE/HuffmanTree.h>
#include <LibDEFLATE/StreamType.h>
namespace LibDEFLATE
{
class Decompressor
{
BAN_NON_COPYABLE(Decompressor);
BAN_NON_MOVABLE(Decompressor);
public:
Decompressor(BAN::ConstByteSpan data, StreamType type)
: m_type(type)
, m_stream(data)
{ }
BAN::ErrorOr<BAN::Vector<uint8_t>> decompress();
private:
BAN::ErrorOr<uint16_t> read_symbol(const HuffmanTree& tree);
BAN::ErrorOr<void> inflate_block(const HuffmanTree& length_tree, const HuffmanTree& distance_tree);
BAN::ErrorOr<void> decompress_type0();
BAN::ErrorOr<void> decompress_type1();
BAN::ErrorOr<void> decompress_type2();
BAN::ErrorOr<void> handle_header();
BAN::ErrorOr<void> handle_footer();
private:
const StreamType m_type;
BitInputStream m_stream;
BAN::Vector<uint8_t> m_output;
BAN::Optional<HuffmanTree> m_fixed_tree;
};
}

View File

@@ -1,61 +0,0 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Optional.h>
#include <BAN/Vector.h>
namespace LibDEFLATE
{
class HuffmanTree
{
BAN_NON_COPYABLE(HuffmanTree);
public:
static constexpr uint8_t MAX_BITS = 15;
struct Leaf
{
uint16_t code;
uint8_t len;
};
struct Instant
{
uint16_t symbol;
uint8_t len;
};
HuffmanTree() {}
HuffmanTree(HuffmanTree&& other) { *this = BAN::move(other); }
HuffmanTree& operator=(HuffmanTree&& other);
static BAN::ErrorOr<HuffmanTree> create(BAN::Span<const uint8_t> bit_lengths);
static BAN::ErrorOr<HuffmanTree> fixed_tree();
BAN::Optional<Instant> get_symbol_instant(uint16_t code) const;
BAN::Optional<uint16_t> get_symbol(uint16_t code, uint8_t len) const;
uint8_t instant_bits() const { return m_instant_bits; }
uint8_t min_bits() const { return m_min_bits; }
uint8_t max_bits() const { return m_max_bits; }
bool empty() const { return m_min_bits == 0; }
private:
BAN::ErrorOr<void> initialize(BAN::Span<const uint8_t> bit_lengths);
BAN::ErrorOr<void> build_instant_table(BAN::Span<const Leaf> tree);
BAN::ErrorOr<void> build_slow_table(BAN::Span<const Leaf> tree);
private:
uint8_t m_instant_bits { 0 };
uint8_t m_min_bits { 0 };
uint8_t m_max_bits { 0 };
BAN::Vector<Instant> m_instant;
BAN::Array<uint16_t, MAX_BITS + 1> m_min_code;
BAN::Vector<BAN::Vector<uint16_t>> m_slow_table;
};
}

View File

@@ -1,13 +0,0 @@
#pragma once
namespace LibDEFLATE
{
enum class StreamType
{
Raw,
Zlib,
GZip,
};
}

View File

@@ -1,49 +0,0 @@
#pragma once
#include <BAN/ByteSpan.h>
namespace LibDEFLATE
{
inline uint32_t calculate_adler32(BAN::ConstByteSpan data)
{
uint32_t s1 = 1;
uint32_t s2 = 0;
for (size_t i = 0; i < data.size(); i++)
{
s1 = (s1 + data[i]) % 65521;
s2 = (s2 + s1) % 65521;
}
return (s2 << 16) | s1;
}
inline uint32_t calculate_crc32(BAN::ConstByteSpan data)
{
uint32_t crc32 = 0xFFFFFFFF;
uint32_t polynomial = 0xEDB88320;
for (size_t i = 0; i < data.size(); i++) {
crc32 ^= data[i];
for (size_t j = 0; j < 8; j++) {
if (crc32 & 1)
crc32 = (crc32 >> 1) ^ polynomial;
else
crc32 >>= 1;
}
}
return ~crc32;
}
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;
}
}

View File

@@ -1,10 +0,0 @@
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)

View File

@@ -1,53 +0,0 @@
#include <BAN/ScopeGuard.h>
#include <LibFont/Font.h>
#include <LibFont/PSF.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
namespace LibFont
{
BAN::ErrorOr<Font> Font::load(BAN::StringView path)
{
BAN::Vector<uint8_t> file_data;
char path_buffer[PATH_MAX];
strncpy(path_buffer, path.data(), path.size());
path_buffer[path.size()] = '\0';
int fd = open(path_buffer, O_RDONLY);
if (fd == -1)
return BAN::Error::from_errno(errno);
BAN::ScopeGuard file_closer([fd] { close(fd); });
struct stat st;
if (fstat(fd, &st) == -1)
return BAN::Error::from_errno(errno);
TRY(file_data.resize(st.st_size));
ssize_t total_read = 0;
while (total_read < st.st_size)
{
ssize_t nread = read(fd, file_data.data() + total_read, st.st_size - total_read);
if (nread == -1)
return BAN::Error::from_errno(errno);
total_read += nread;
}
return load(BAN::ConstByteSpan(file_data.span()));
}
BAN::ErrorOr<Font> Font::load(BAN::ConstByteSpan font_data)
{
if (is_psf1(font_data))
return TRY(parse_psf1(font_data));
if (is_psf2(font_data))
return TRY(parse_psf2(font_data));
return BAN::Error::from_errno(ENOTSUP);
}
}

View File

@@ -1,214 +0,0 @@
#include <BAN/Debug.h>
#include <BAN/Endianness.h>
#include <BAN/UTF8.h>
#include <LibFont/PSF.h>
#define PSF1_MAGIC0 0x36
#define PSF1_MAGIC1 0x04
#define PSF1_MODE512 0x01
#define PSF1_MODEHASTAB 0x02
#define PSF1_MODEHASSEQ 0x04
#define PSF1_STARTSEQ 0xFFFE
#define PSF1_SEPARATOR 0xFFFF
#define PSF2_MAGIC0 0x72
#define PSF2_MAGIC1 0xB5
#define PSF2_MAGIC2 0x4A
#define PSF2_MAGIC3 0x86
#define PSF2_HAS_UNICODE_TABLE 0x01
#define PSF2_STARTSEQ 0xFE
#define PSF2_SEPARATOR 0xFF
namespace LibFont
{
bool is_psf1(BAN::ConstByteSpan font_data)
{
if (font_data.size() < 2)
return false;
return font_data[0] == PSF1_MAGIC0 && font_data[1] == PSF1_MAGIC1;
}
BAN::ErrorOr<Font> parse_psf1(BAN::ConstByteSpan font_data)
{
struct PSF1Header
{
uint8_t magic[2];
uint8_t mode;
uint8_t char_size;
};
if (font_data.size() < sizeof(PSF1Header))
return BAN::Error::from_errno(EINVAL);
const auto& header = font_data.as<const PSF1Header>();
uint32_t glyph_count = header.mode & PSF1_MODE512 ? 512 : 256;
uint32_t glyph_size = header.char_size;
uint32_t glyph_data_size = glyph_size * glyph_count;
if (font_data.size() < sizeof(PSF1Header) + glyph_data_size)
return BAN::Error::from_errno(EINVAL);
BAN::Vector<uint8_t> glyph_data;
TRY(glyph_data.resize(glyph_data_size));
memcpy(glyph_data.data(), font_data.data() + sizeof(PSF1Header), glyph_data_size);
BAN::HashMap<uint32_t, uint32_t> glyph_offsets;
TRY(glyph_offsets.reserve(glyph_count));
bool codepoint_redef = false;
bool codepoint_sequence = false;
if (header.mode & (PSF1_MODEHASTAB | PSF1_MODEHASSEQ))
{
uint32_t current_index = sizeof(PSF1Header) + glyph_data_size;
uint32_t glyph_index = 0;
while (current_index < font_data.size())
{
uint16_t lo = font_data[current_index];
uint16_t hi = font_data[current_index + 1];
uint16_t codepoint = (hi << 8) | lo;
if (codepoint == PSF1_STARTSEQ)
{
codepoint_sequence = true;
break;
}
else if (codepoint == PSF1_SEPARATOR)
{
glyph_index++;
}
else
{
if (glyph_offsets.contains(codepoint))
codepoint_redef = true;
else
TRY(glyph_offsets.insert(codepoint, glyph_index * glyph_size));
}
current_index += 2;
}
}
else
{
for (uint32_t i = 0; i < glyph_count; i++)
TRY(glyph_offsets.insert(i, i * glyph_size));
}
if (codepoint_redef)
dwarnln("Font contains multiple definitions for same codepoint(s)");
if (codepoint_sequence)
dwarnln("Font contains codepoint sequences (not supported)");
return Font(BAN::move(glyph_offsets), BAN::move(glyph_data), 8, header.char_size, 1);
}
bool is_psf2(BAN::ConstByteSpan font_data)
{
if (font_data.size() < 4)
return false;
return font_data[0] == PSF2_MAGIC0 && font_data[1] == PSF2_MAGIC1 && font_data[2] == PSF2_MAGIC2 && font_data[3] == PSF2_MAGIC3;
}
BAN::ErrorOr<Font> parse_psf2(BAN::ConstByteSpan font_data)
{
struct PSF2Header
{
uint8_t magic[4];
BAN::LittleEndian<uint32_t> version;
BAN::LittleEndian<uint32_t> header_size;
BAN::LittleEndian<uint32_t> flags;
BAN::LittleEndian<uint32_t> glyph_count;
BAN::LittleEndian<uint32_t> glyph_size;
BAN::LittleEndian<uint32_t> height;
BAN::LittleEndian<uint32_t> width;
};
if (font_data.size() < sizeof(PSF2Header))
return BAN::Error::from_errno(EINVAL);
const auto& header = font_data.as<const PSF2Header>();
uint32_t glyph_data_size = header.glyph_count * header.glyph_size;
if (font_data.size() < glyph_data_size + header.header_size)
return BAN::Error::from_errno(EINVAL);
BAN::Vector<uint8_t> glyph_data;
TRY(glyph_data.resize(glyph_data_size));
memcpy(glyph_data.data(), font_data.data() + header.header_size, glyph_data_size);
BAN::HashMap<uint32_t, uint32_t> glyph_offsets;
TRY(glyph_offsets.reserve(400));
bool invalid_utf = false;
bool codepoint_redef = false;
bool codepoint_sequence = false;
uint8_t bytes[4] {};
uint32_t byte_index = 0;
if (header.flags & PSF2_HAS_UNICODE_TABLE)
{
uint32_t glyph_index = 0;
for (uint32_t i = glyph_data_size + header.header_size; i < font_data.size(); i++)
{
uint8_t byte = font_data[i];
if (byte == PSF2_STARTSEQ)
{
codepoint_sequence = true;
break;
}
else if (byte == PSF2_SEPARATOR)
{
if (byte_index)
{
invalid_utf = true;
byte_index = 0;
}
glyph_index++;
}
else
{
ASSERT(byte_index < 4);
bytes[byte_index++] = byte;
uint32_t len = BAN::UTF8::byte_length(bytes[0]);
if (len == BAN::UTF8::invalid)
{
invalid_utf = true;
byte_index = 0;
}
else if (len == byte_index)
{
uint32_t codepoint = BAN::UTF8::to_codepoint(bytes);
if (codepoint == BAN::UTF8::invalid)
invalid_utf = true;
else if (glyph_offsets.contains(codepoint))
codepoint_redef = true;
else
TRY(glyph_offsets.insert(codepoint, glyph_index * header.glyph_size));
byte_index = 0;
}
}
}
}
else
{
for (uint32_t i = 0; i < header.glyph_count; i++)
TRY(glyph_offsets.insert(i, i * header.glyph_size));
}
if (invalid_utf)
dwarnln("Font contains invalid UTF-8 codepoint(s)");
if (codepoint_redef)
dwarnln("Font contains multiple definitions for same codepoint(s)");
if (codepoint_sequence)
dwarnln("Font contains codepoint sequences (not supported)");
return Font(BAN::move(glyph_offsets), BAN::move(glyph_data), header.width, header.height, header.glyph_size / header.height);
}
}

View File

@@ -1,8 +0,0 @@
#include <LibFont/Font.h>
namespace LibFont
{
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/HashMap.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
namespace LibFont
{
class Font
{
public:
Font() = default;
Font(BAN::HashMap<uint32_t, uint32_t>&& glyph_offsets, BAN::Vector<uint8_t>&& glyph_data, uint32_t width, uint32_t height, uint32_t pitch)
: m_glyph_offsets(BAN::move(glyph_offsets))
, m_glyph_data(BAN::move(glyph_data))
, m_width(width)
, m_height(height)
, m_pitch(pitch)
{ }
static BAN::ErrorOr<Font> load(BAN::StringView path);
static BAN::ErrorOr<Font> load(BAN::ConstByteSpan font_data);
#if __is_kernel
static BAN::ErrorOr<Font> prefs();
#endif
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
uint32_t pitch() const { return m_pitch; }
bool has_glyph(uint32_t codepoint) const { return glyph(codepoint) != nullptr; }
const uint8_t* glyph(uint32_t codepoint) const
{
auto it = m_glyph_offsets.find(codepoint);
if (it == m_glyph_offsets.end())
return nullptr;
return m_glyph_data.data() + it->value;
}
private:
BAN::HashMap<uint32_t, uint32_t> m_glyph_offsets;
BAN::Vector<uint8_t> m_glyph_data;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_pitch = 0;
};
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include <LibFont/Font.h>
namespace LibFont
{
bool is_psf1(BAN::ConstByteSpan);
BAN::ErrorOr<Font> parse_psf1(BAN::ConstByteSpan);
bool is_psf2(BAN::ConstByteSpan);
BAN::ErrorOr<Font> parse_psf2(BAN::ConstByteSpan);
}

View File

@@ -1,19 +0,0 @@
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)

View File

@@ -1,67 +0,0 @@
#include <LibGUI/MessageBox.h>
#include <LibGUI/Window.h>
#include <LibGUI/Widget/Button.h>
#include <LibGUI/Widget/Grid.h>
#include <LibGUI/Widget/TextArea.h>
#include <LibGUI/Widget/Widget.h>
namespace LibGUI
{
BAN::ErrorOr<void> MessageBox::create(BAN::StringView message, BAN::StringView title)
{
BAN::StringView ok_button = "OK";
TRY(create(message, title, { &ok_button, 1 }));
return {};
}
BAN::ErrorOr<size_t> MessageBox::create(BAN::StringView message, BAN::StringView title, BAN::Span<BAN::StringView> buttons)
{
if (buttons.empty())
return BAN::Error::from_errno(EINVAL);
const uint32_t window_width = 300;
auto root_widget = TRY(Widget::Widget::create({}, 0xFFFFFF, { 0, 0, window_width, 0 }));
auto text_area = TRY(Widget::TextArea::create(root_widget, message, { 0, 0, window_width, 0}));
text_area->style().border_width = 0;
text_area->style().color_normal = Widget::Widget::color_invisible;
text_area->style().corner_radius = 0;
TRY(text_area->set_relative_geometry({ 0.0, 0.0, 1.0, 0.8 }));
text_area->show();
bool waiting = true;
size_t result = 0;
auto button_area = TRY(Widget::Grid::create(root_widget, buttons.size(), 1));
for (size_t i = 0; i < buttons.size(); i++)
{
auto button = TRY(Widget::Button::create(button_area, buttons[i]));
TRY(button_area->set_widget_position(button, i, 1, 0, 1));
button->set_click_callback([&result, &waiting, i] { result = i; waiting = false; });
button->show();
}
TRY(button_area->set_relative_geometry({ 0.0, 0.8, 1.0, 0.2 }));
button_area->show();
const uint32_t button_height = 20;
const uint32_t window_height = text_area->get_required_height() + button_height;
auto attributes = Window::default_attributes;
attributes.resizable = true;
auto window = TRY(Window::create(window_width, window_height, title, attributes));
TRY(window->set_root_widget(root_widget));
window->set_close_window_event_callback([&waiting] { waiting = false; });
while (waiting)
{
window->wait_events();
window->poll_events();
}
return result;
}
}

View File

@@ -1,258 +0,0 @@
#include <LibGUI/Texture.h>
#include <LibFont/Font.h>
namespace LibGUI
{
BAN::ErrorOr<Texture> Texture::create(uint32_t width, uint32_t height, uint32_t color)
{
if (BAN::Math::will_addition_overflow(width, height))
return BAN::Error::from_errno(EOVERFLOW);
BAN::Vector<uint32_t> pixels;
TRY(pixels.resize(width * height, color));
return Texture(BAN::move(pixels), width, height, color);
}
BAN::ErrorOr<void> Texture::resize(uint32_t new_width, uint32_t new_height)
{
if (BAN::Math::will_addition_overflow(new_width, new_height))
return BAN::Error::from_errno(EOVERFLOW);
BAN::Vector<uint32_t> pixels;
TRY(pixels.resize(new_width * new_height, m_bg_color));
const uint32_t max_x = BAN::Math::min(new_width, m_width);
const uint32_t max_y = BAN::Math::min(new_height, m_height);
for (uint32_t y = 0; y < max_y; y++)
for (uint32_t x = 0; x < max_x; x++)
pixels[y * new_width + x] = m_pixels[y * m_width + x];
m_width = new_width;
m_height = new_height;
m_pixels = BAN::move(pixels);
if (m_has_set_clip)
set_clip_area(m_clip_x, m_clip_y, m_clip_w, m_clip_h);
else
{
m_clip_w = new_width;
m_clip_h = new_height;
}
return {};
}
void Texture::set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
m_clip_x = 0;
m_clip_y = 0;
m_clip_w = this->width();
m_clip_h = this->height();
if (!clamp_to_texture(x, y, width, height))
{
m_clip_h = 0;
m_clip_w = 0;
}
else
{
m_clip_x = x;
m_clip_y = y;
m_clip_w = width;
m_clip_h = height;
}
m_has_set_clip = true;
}
void Texture::fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color)
{
if (!clamp_to_texture(x, y, width, height))
return;
for (uint32_t y_off = 0; y_off < height; y_off++)
for (uint32_t x_off = 0; x_off < width; x_off++)
set_pixel(x + x_off, y + y_off, color);
}
void Texture::copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x, uint32_t sub_y, uint32_t width, uint32_t height)
{
int32_t src_x = sub_x, src_y = sub_y;
if (!clamp_to_texture(x, y, src_x, src_y, width, height, texture))
return;
sub_x = src_x;
sub_y = src_y;
for (uint32_t y_off = 0; y_off < height; y_off++)
for (uint32_t x_off = 0; x_off < width; x_off++)
if (const uint32_t color = texture.get_pixel(sub_x + x_off, sub_y + y_off); color != color_invisible)
set_pixel(x + x_off, y + y_off, color);
}
void Texture::draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
{
if (tl_y + (int32_t)font.height() < 0 || tl_y >= (int32_t)height())
return;
if (tl_x + (int32_t)font.width() < 0 || tl_x >= (int32_t)width())
return;
auto glyph = font.glyph(codepoint);
if (glyph == nullptr)
return;
for (int32_t off_y = 0; off_y < (int32_t)font.height(); off_y++)
{
if (tl_y + off_y < 0)
continue;
uint32_t abs_y = tl_y + off_y;
if (abs_y >= height())
break;
for (int32_t off_x = 0; off_x < (int32_t)font.width(); off_x++)
{
if (tl_x + off_x < 0)
continue;
uint32_t abs_x = tl_x + off_x;
if (abs_x >= width())
break;
const uint8_t bitmask = 1 << (font.width() - off_x - 1);
if (glyph[off_y * font.pitch()] & bitmask)
set_pixel(abs_x, abs_y, color);
}
}
}
void Texture::draw_text(BAN::StringView text, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
{
for (size_t i = 0; i < text.size(); i++)
draw_character(text[i], font, tl_x + (int32_t)(i * font.width()), tl_y, color);
}
void Texture::shift_vertical(int32_t amount)
{
const uint32_t amount_abs = BAN::Math::abs(amount);
if (amount_abs == 0 || amount_abs >= height())
return;
uint32_t* dst = (amount > 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
uint32_t* src = (amount < 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
memmove(dst, src, width() * (height() - amount_abs) * 4);
}
void Texture::copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t uamount)
{
int32_t amount = uamount;
if (dst_y < 0)
{
amount -= -dst_y;
src_y += -dst_y;
dst_y = 0;
}
amount = BAN::Math::min<int32_t>(amount, height() - dst_y);
if (amount <= 0)
return;
const int32_t copy_src_y = BAN::Math::clamp<int32_t>(src_y, 0, height());
const int32_t copy_amount = BAN::Math::clamp<int32_t>(src_y + amount, 0, height()) - copy_src_y;
if (copy_amount > 0)
{
memmove(
&m_pixels[width() * (dst_y + (copy_src_y - src_y))],
&m_pixels[width() * copy_src_y],
copy_amount * width() * 4
);
}
}
void Texture::copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height)
{
if (!clamp_to_texture(dst_x, dst_y, src_x, src_y, width, height, *this))
return;
const bool copy_dir = dst_y < src_y;
for (uint32_t i = 0; i < height; i++)
{
const uint32_t y_off = copy_dir ? i : height - i - 1;
memmove(
&m_pixels[(dst_y + y_off) * this->width() + dst_x],
&m_pixels[(src_y + y_off) * this->width() + src_x],
width * 4
);
}
}
bool Texture::clamp_to_texture(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
{
const int32_t min_x = BAN::Math::max<int32_t>(signed_x, m_clip_x);
const int32_t min_y = BAN::Math::max<int32_t>(signed_y, m_clip_y);
const int32_t max_x = BAN::Math::min<int32_t>(signed_x + (int32_t)width, m_clip_x + m_clip_w);
const int32_t max_y = BAN::Math::min<int32_t>(signed_y + (int32_t)height, m_clip_y + m_clip_h);
if (min_x >= max_x)
return false;
if (min_y >= max_y)
return false;
signed_x = min_x;
signed_y = min_y;
width = max_x - min_x;
height = max_y - min_y;
return true;
}
bool Texture::clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture& texture) const
{
if (width > texture.width())
width = texture.width();
if (height > texture.height())
height = texture.height();
if (dst_x >= static_cast<int32_t>(m_clip_x + m_clip_w))
return false;
if (dst_y >= static_cast<int32_t>(m_clip_y + m_clip_h))
return false;
if (src_x >= static_cast<int32_t>(texture.width()))
return false;
if (src_y >= static_cast<int32_t>(texture.height()))
return false;
if (dst_x + static_cast<int32_t>(width) > static_cast<int32_t>(m_clip_x + m_clip_w))
width = m_clip_x + m_clip_w - dst_x;
if (src_x + static_cast<int32_t>(width) > static_cast<int32_t>(texture.width()))
width = texture.width() - src_x;
if (dst_y + static_cast<int32_t>(height) > static_cast<int32_t>(m_clip_y + m_clip_h))
height = m_clip_y + m_clip_h - dst_y;
if (src_y + static_cast<int32_t>(height) > static_cast<int32_t>(texture.height()))
height = texture.height() - src_y;
int32_t off_x = 0;
if (dst_x < static_cast<int32_t>(m_clip_x))
off_x = m_clip_x - dst_x;
if (src_x + off_x < 0)
off_x = -src_x;
if (off_x >= static_cast<int32_t>(width))
return false;
int32_t off_y = 0;
if (dst_y < static_cast<int32_t>(m_clip_y))
off_y = m_clip_y - dst_y;
if (src_y + off_y < 0)
off_y = -src_y;
if (off_y >= static_cast<int32_t>(height))
return false;
dst_x += off_x;
src_x += off_x;
dst_y += off_y;
src_y += off_y;
width -= off_x;
height -= off_y;
return true;
}
}

View File

@@ -1,60 +0,0 @@
#include <LibFont/Font.h>
#include <LibGUI/Widget/Button.h>
#include <LibGUI/Window.h>
namespace LibGUI::Widget
{
BAN::ErrorOr<BAN::RefPtr<Button>> Button::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
{
auto* button_ptr = new Button(parent, geometry);
if (button_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto button = BAN::RefPtr<Button>::adopt(button_ptr);
TRY(button->initialize(color_invisible));
TRY(button->m_text.append(text));
return button;
}
BAN::ErrorOr<void> Button::set_text(BAN::StringView text)
{
m_text.clear();
TRY(m_text.append(text));
if (is_shown())
show();
return {};
}
void Button::update_impl()
{
const bool hover_color = is_hovered() && !is_child_hovered();
if (hover_color != m_hover_state)
show();
}
void Button::show_impl()
{
m_hover_state = is_hovered() && !is_child_hovered();
const auto& font = default_font();
const int32_t text_h = font.height();
const int32_t text_w = font.width() * m_text.size();
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
m_texture.fill(m_hover_state ? m_style.color_hovered : m_style.color_normal);
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
RoundedWidget::style() = m_style;
RoundedWidget::show_impl();
}
bool Button::on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t event)
{
if (event.pressed && event.button == LibInput::MouseButton::Left && m_click_callback)
m_click_callback();
return true;
}
}

View File

@@ -1,53 +0,0 @@
#include <LibGUI/Widget/Grid.h>
namespace LibGUI::Widget
{
BAN::ErrorOr<BAN::RefPtr<Grid>> Grid::create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color, Rectangle geometry)
{
auto* grid_ptr = new Grid(parent, geometry, cols, rows);
if (grid_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto grid = BAN::RefPtr<Grid>::adopt(grid_ptr);
TRY(grid->initialize(color));
return grid;
}
Widget::Rectangle Grid::grid_element_area(const GridElement& element) const
{
const uint32_t this_x = element.col * width() / m_cols;
const uint32_t this_y = element.row * height() / m_rows;
const uint32_t next_x = (element.col + element.col_span) * width() / m_cols;
const uint32_t next_y = (element.row + element.row_span) * height() / m_rows;
return Widget::Rectangle {
.x = static_cast<int32_t>(this_x),
.y = static_cast<int32_t>(this_y),
.w = next_x - this_x,
.h = next_y - this_y,
};
}
BAN::ErrorOr<void> Grid::update_geometry_impl()
{
for (auto& grid_element : m_grid_elements)
TRY(grid_element.widget->set_fixed_geometry(grid_element_area(grid_element)));
return {};
}
BAN::ErrorOr<void> Grid::set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span)
{
if (col_span == 0 || row_span == 0)
return BAN::Error::from_errno(EINVAL);
if (col + col_span > m_cols)
return BAN::Error::from_errno(EINVAL);
if (row + row_span > m_rows)
return BAN::Error::from_errno(EINVAL);
ASSERT(widget->parent() == this);
TRY(m_grid_elements.emplace_back(widget, col, col_span, row, row_span));
TRY(widget->set_fixed_geometry(grid_element_area(m_grid_elements.back())));
return {};
}
}

View File

@@ -1,44 +0,0 @@
#include <LibFont/Font.h>
#include <LibGUI/Widget/Label.h>
#include <LibGUI/Window.h>
namespace LibGUI::Widget
{
BAN::ErrorOr<BAN::RefPtr<Label>> Label::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
{
auto* label_ptr = new Label(parent, geometry);
if (label_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto label = BAN::RefPtr<Label>::adopt(label_ptr);
TRY(label->initialize(color_invisible));
TRY(label->m_text.append(text));
return label;
}
BAN::ErrorOr<void> Label::set_text(BAN::StringView text)
{
m_text.clear();
TRY(m_text.append(text));
if (is_shown())
show();
return {};
}
void Label::show_impl()
{
const auto& font = default_font();
const int32_t text_h = font.height();
const int32_t text_w = font.width() * m_text.size();
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
m_texture.fill(m_style.color_normal);
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
RoundedWidget::style() = m_style;
RoundedWidget::show_impl();
}
}

View File

@@ -1,117 +0,0 @@
#include <LibGUI/Widget/RoundedWidget.h>
namespace LibGUI::Widget
{
bool RoundedWidget::contains(Point point) const
{
if (!Widget::contains(point))
return false;
const auto is_outside_corner =
[this]<uint8_t quadrant>(Point point) -> bool
{
const auto radius = m_style.corner_radius;
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
if (point.x < static_cast<int32_t>(base_x) || point.x > static_cast<int32_t>(base_x + radius))
return false;
if (point.y < static_cast<int32_t>(base_y) || point.y > static_cast<int32_t>(base_y + radius))
return false;
const uint32_t x_off = point.x - base_x;
const uint32_t y_off = point.y - base_y;
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
const uint32_t distance = dx * dx + dy * dy;
return distance >= radius * radius;
};
if (is_outside_corner.operator()<0>(point))
return false;
if (is_outside_corner.operator()<1>(point))
return false;
if (is_outside_corner.operator()<2>(point))
return false;
if (is_outside_corner.operator()<3>(point))
return false;
return true;
}
void RoundedWidget::show_impl()
{
if (m_style.border_width)
{
m_texture.fill_rect(
0,
0,
m_texture.width(),
m_style.border_width,
m_style.color_border
);
m_texture.fill_rect(
0,
0,
m_style.border_width,
m_texture.height(),
m_style.color_border
);
m_texture.fill_rect(
0,
m_texture.height() - m_style.border_width,
m_texture.width(),
m_style.border_width,
m_style.color_border
);
m_texture.fill_rect(
m_texture.width() - m_style.border_width,
0,
m_style.border_width,
m_texture.height(),
m_style.color_border
);
}
if (m_style.corner_radius)
{
const auto draw_corner =
[this]<uint8_t quadrant>()
{
const auto radius = m_style.corner_radius;
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
const uint32_t distance_max = radius * radius;
const uint32_t distance_min = (radius - m_style.border_width) * (radius - m_style.border_width);
for (uint32_t y_off = 0; y_off <= radius; y_off++)
{
for (uint32_t x_off = 0; x_off <= radius; x_off++)
{
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
const uint32_t distance = dx * dx + dy * dy;
if (base_x + x_off >= m_texture.width())
continue;
if (base_y + y_off >= m_texture.height())
continue;
if (distance > distance_max)
m_texture.set_pixel(base_x + x_off, base_y + y_off, color_invisible);
else if (distance >= distance_min)
m_texture.set_pixel(base_x + x_off, base_y + y_off, m_style.color_border);
}
}
};
draw_corner.operator()<0>();
draw_corner.operator()<1>();
draw_corner.operator()<2>();
draw_corner.operator()<3>();
}
}
}

View File

@@ -1,96 +0,0 @@
#include <LibFont/Font.h>
#include <LibGUI/Widget/TextArea.h>
#include <LibGUI/Window.h>
#include <ctype.h>
namespace LibGUI::Widget
{
BAN::ErrorOr<BAN::RefPtr<TextArea>> TextArea::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
{
auto* text_area_ptr = new TextArea(parent, geometry);
if (text_area_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto text_area = BAN::RefPtr<TextArea>::adopt(text_area_ptr);
TRY(text_area->initialize(color_invisible));
TRY(text_area->set_text(text));
return text_area;
}
BAN::ErrorOr<void> TextArea::set_text(BAN::StringView text)
{
m_text.clear();
TRY(m_text.append(text));
TRY(wrap_text());
if (is_shown())
show();
return {};
}
uint32_t TextArea::get_required_height() const
{
auto& font = default_font();
return m_wrapped_text.size() * font.height();
}
BAN::ErrorOr<void> TextArea::wrap_text()
{
m_wrapped_text.clear();
if (width() == 0)
return {};
auto& font = default_font();
const uint32_t total_columns = width() / font.width();
ASSERT(total_columns != 0);
TRY(m_wrapped_text.emplace_back());
for (size_t i = 0; i < m_text.size(); i++)
{
if (m_text[i] == '\n')
TRY(m_wrapped_text.emplace_back());
else if (isspace(m_text[i]) && m_wrapped_text.back().size() == 0)
;
else
{
TRY(m_wrapped_text.back().push_back(m_text[i]));
if (i + 1 < m_text.size() && isspace(m_text[i]) && !isspace(m_text[i + 1]))
{
size_t word_len = 0;
for (size_t j = i + 1; j < m_text.size() && !isspace(m_text[j]); j++)
word_len++;
if (word_len <= total_columns && m_wrapped_text.back().size() + word_len > total_columns)
TRY(m_wrapped_text.emplace_back());
}
if (m_wrapped_text.back().size() >= total_columns)
TRY(m_wrapped_text.emplace_back());
}
}
return {};
}
BAN::ErrorOr<void> TextArea::update_geometry_impl()
{
TRY(wrap_text());
return Widget::update_geometry_impl();
}
void TextArea::show_impl()
{
const auto& font = default_font();
m_texture.fill(m_style.color_normal);
for (int32_t row = 0; row < static_cast<int32_t>(m_wrapped_text.size()); row++)
m_texture.draw_text(m_wrapped_text[row].sv(), font, 0, row * font.height(), m_style.color_text);
RoundedWidget::style() = m_style;
RoundedWidget::show_impl();
}
}

View File

@@ -1,241 +0,0 @@
#include <LibGUI/Widget/Widget.h>
#include <LibFont/Font.h>
namespace LibGUI::Widget
{
static BAN::Optional<LibFont::Font> s_default_font;
BAN::ErrorOr<void> Widget::set_default_font(BAN::StringView path)
{
s_default_font = TRY(LibFont::Font::load(path));
return {};
}
const LibFont::Font& Widget::default_font()
{
if (!s_default_font.has_value())
MUST(set_default_font("/usr/share/fonts/lat0-16.psfu"_sv));
return s_default_font.value();
}
BAN::ErrorOr<BAN::RefPtr<Widget>> Widget::create(BAN::RefPtr<Widget> parent, uint32_t color, Rectangle area)
{
auto* widget_ptr = new Widget(parent, area);
if (widget_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto widget = BAN::RefPtr<Widget>::adopt(widget_ptr);
TRY(widget->initialize(color));
return widget;
}
BAN::ErrorOr<void> Widget::initialize(uint32_t color)
{
m_texture = TRY(Texture::create(width(), height(), color));
if (m_parent)
TRY(m_parent->m_children.push_back(this));
return {};
}
bool Widget::is_child_hovered() const
{
for (auto child : m_children)
if (child->m_hovered || child->is_child_hovered())
return true;
return false;
}
BAN::ErrorOr<void> Widget::set_fixed_geometry(Rectangle area)
{
TRY(m_texture.resize(area.w, area.h));
m_fixed_area = area;
m_relative_area.clear();
TRY(update_geometry_impl());
if (is_shown())
show();
return {};
}
BAN::ErrorOr<void> Widget::set_relative_geometry(FloatRectangle area)
{
if (area.w < 0.0f || area.h < 0.0f)
return BAN::Error::from_errno(EINVAL);
ASSERT(m_parent);
TRY(set_fixed_geometry({
.x = static_cast<int32_t>(area.x * m_parent->width()),
.y = static_cast<int32_t>(area.y * m_parent->height()),
.w = static_cast<uint32_t>(area.w * m_parent->width()),
.h = static_cast<uint32_t>(area.h * m_parent->height()),
}));
m_relative_area = area;
return {};
}
BAN::ErrorOr<void> Widget::update_geometry_impl()
{
for (auto child : m_children)
{
if (!child->m_relative_area.has_value())
continue;
TRY(child->set_relative_geometry(child->m_relative_area.value()));
}
return {};
}
void Widget::show()
{
m_shown = true;
show_impl();
m_changed = true;
for (auto child : m_children)
if (child->is_shown())
child->show();
}
void Widget::hide()
{
if (!is_shown())
return;
m_shown = false;
auto root = m_parent;
while (root && root->m_parent)
root = root->m_parent;
if (root)
root->show();
}
void Widget::before_mouse_move()
{
if (!is_shown())
return;
m_old_hovered = m_hovered;
m_hovered = false;
for (auto child : m_children)
child->before_mouse_move();
}
void Widget::after_mouse_move()
{
if (!is_shown())
return;
if (m_old_hovered != m_hovered)
on_hover_change_impl(m_hovered);
for (auto child : m_children)
child->after_mouse_move();
}
bool Widget::on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t event)
{
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
return false;
if (!is_shown())
return false;
m_hovered = contains({ event.x, event.y });
for (auto child : m_children)
{
auto rel_event = event;
rel_event.x -= child->m_fixed_area.x;
rel_event.y -= child->m_fixed_area.y;
if (child->on_mouse_move(rel_event))
return true;
}
if (!m_hovered)
return false;
return on_mouse_move_impl(event);
}
bool Widget::on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t event)
{
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
return false;
if (!is_shown())
return false;
for (auto child : m_children)
{
auto rel_event = event;
rel_event.x -= child->m_fixed_area.x;
rel_event.y -= child->m_fixed_area.y;
if (child->on_mouse_button(rel_event))
return true;
}
if (!contains({ event.x, event.y }))
return false;
return on_mouse_button_impl(event);
}
Widget::Rectangle Widget::render(Texture& output, Point parent_position, Rectangle out_area)
{
if (!is_shown())
return {};
update_impl();
Rectangle invalidated {};
if (m_changed)
{
auto where_i_would_be = Rectangle {
.x = parent_position.x + m_fixed_area.x,
.y = parent_position.y + m_fixed_area.y,
.w = m_fixed_area.w,
.h = m_fixed_area.h,
};
auto where_to_draw = out_area.overlap(where_i_would_be);
if (where_to_draw.w && where_to_draw.h)
{
output.copy_texture(
m_texture,
where_to_draw.x,
where_to_draw.y,
where_to_draw.x - where_i_would_be.x,
where_to_draw.y - where_i_would_be.y,
where_to_draw.w,
where_to_draw.h
);
}
invalidated = where_to_draw;
m_changed = false;
}
out_area = out_area.overlap({
.x = parent_position.x + m_fixed_area.x,
.y = parent_position.y + m_fixed_area.y,
.w = m_fixed_area.w,
.h = m_fixed_area.h,
});
parent_position = {
.x = parent_position.x + m_fixed_area.x,
.y = parent_position.y + m_fixed_area.y,
};
for (auto child : m_children)
{
invalidated = invalidated.bounding_box(
child->render(output, parent_position, out_area)
);
}
return invalidated;
}
}

View File

@@ -1,395 +0,0 @@
#include <LibGUI/Window.h>
#include <BAN/ScopeGuard.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
namespace LibGUI
{
struct ReceivePacket
{
PacketType type;
BAN::Vector<uint8_t> data_with_type;
};
static BAN::ErrorOr<ReceivePacket> recv_packet(int socket)
{
uint32_t packet_size;
{
const ssize_t nrecv = recv(socket, &packet_size, sizeof(uint32_t), 0);
if (nrecv < 0)
return BAN::Error::from_errno(errno);
if (nrecv == 0)
return BAN::Error::from_errno(ECONNRESET);
}
if (packet_size < sizeof(uint32_t))
return BAN::Error::from_literal("invalid packet, does not fit packet id");
BAN::Vector<uint8_t> packet_data;
TRY(packet_data.resize(packet_size));
size_t total_recv = 0;
while (total_recv < packet_size)
{
const ssize_t nrecv = recv(socket, packet_data.data() + total_recv, packet_size - total_recv, 0);
if (nrecv < 0)
return BAN::Error::from_errno(errno);
if (nrecv == 0)
return BAN::Error::from_errno(ECONNRESET);
total_recv += nrecv;
}
return ReceivePacket {
*reinterpret_cast<PacketType*>(packet_data.data()),
packet_data
};
}
Window::~Window()
{
cleanup();
}
BAN::ErrorOr<BAN::UniqPtr<Window>> Window::create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes)
{
int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd == -1)
return BAN::Error::from_errno(errno);
BAN::ScopeGuard server_closer([server_fd] { close(server_fd); });
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1)
return BAN::Error::from_errno(errno);
BAN::ScopeGuard epoll_closer([epoll_fd] { close(epoll_fd); });
epoll_event epoll_event {
.events = EPOLLIN,
.data = { .fd = server_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &epoll_event) == -1)
return BAN::Error::from_errno(errno);
if (fcntl(server_fd, F_SETFD, fcntl(server_fd, F_GETFD) | FD_CLOEXEC) == -1)
return BAN::Error::from_errno(errno);
timespec start_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
for (;;)
{
sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, s_window_server_socket.data());
if (connect(server_fd, (sockaddr*)&server_address, sizeof(server_address)) == 0)
break;
timespec current_time;
clock_gettime(CLOCK_MONOTONIC, &current_time);
time_t duration_s = (current_time.tv_sec - start_time.tv_sec) + (current_time.tv_nsec >= start_time.tv_nsec);
if (duration_s > 1)
return BAN::Error::from_errno(ETIMEDOUT);
timespec sleep_time;
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = 1'000'000;
nanosleep(&sleep_time, nullptr);
}
WindowPacket::WindowCreate create_packet;
create_packet.width = width;
create_packet.height = height;
create_packet.attributes = attributes;
TRY(create_packet.title.append(title));
TRY(create_packet.send_serialized(server_fd));
auto window = TRY(BAN::UniqPtr<Window>::create(server_fd, epoll_fd, attributes));
bool resized = false;
window->set_resize_window_event_callback([&]() { resized = true; });
while (!resized)
window->poll_events();
window->set_resize_window_event_callback({});
server_closer.disable();
epoll_closer.disable();
return window;
}
BAN::ErrorOr<void> Window::set_root_widget(BAN::RefPtr<Widget::Widget> widget)
{
TRY(widget->set_fixed_geometry({ 0, 0, m_width, m_height }));
m_root_widget = widget;
m_root_widget->show();
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
if (invalidated.w && invalidated.h)
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
return {};
}
void Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
if (!m_texture.clamp_to_texture(x, y, width, height))
return;
for (uint32_t i = 0; i < height; i++)
memcpy(&m_framebuffer_smo[(y + i) * m_width + x], &m_texture.pixels()[(y + i) * m_width + x], width * sizeof(uint32_t));
WindowPacket::WindowInvalidate packet;
packet.x = x;
packet.y = y;
packet.width = width;
packet.height = height;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_mouse_relative(bool enabled)
{
WindowPacket::WindowSetMouseRelative packet;
packet.enabled = enabled;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_fullscreen(bool fullscreen)
{
WindowPacket::WindowSetFullscreen packet;
packet.fullscreen = fullscreen;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_title(BAN::StringView title)
{
WindowPacket::WindowSetTitle packet;
MUST(packet.title.append(title));
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_position(int32_t x, int32_t y)
{
WindowPacket::WindowSetPosition packet;
packet.x = x;
packet.y = y;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_cursor_visible(bool visible)
{
auto attributes = m_attributes;
if (attributes.cursor_visible == visible)
return;
attributes.cursor_visible = visible;
set_attributes(attributes);
}
void Window::set_cursor(uint32_t width, uint32_t height, BAN::Span<const uint32_t> pixels, int32_t origin_x, int32_t origin_y)
{
WindowPacket::WindowSetCursor packet;
packet.width = width;
packet.height = height;
packet.origin_x = origin_x;
packet.origin_y = origin_y;
MUST(packet.pixels.resize(pixels.size()));
for (size_t i = 0; i < packet.pixels.size(); i++)
packet.pixels[i] = pixels[i];
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_min_size(uint32_t width, uint32_t height)
{
WindowPacket::WindowSetMinSize packet;
packet.width = width;
packet.height = height;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_max_size(uint32_t width, uint32_t height)
{
WindowPacket::WindowSetMaxSize packet;
packet.width = width;
packet.height = height;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_attributes(Attributes attributes)
{
WindowPacket::WindowSetAttributes packet;
packet.attributes = attributes;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
m_attributes = attributes;
}
void Window::request_resize(uint32_t width, uint32_t height)
{
WindowPacket::WindowSetSize packet;
packet.width = width;
packet.height = height;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::on_socket_error(BAN::StringView function)
{
if (m_handling_socket_error)
return;
m_handling_socket_error = true;
dprintln("Socket error while running Window::{}", function);
if (!m_socket_error_callback)
exit(1);
m_socket_error_callback();
cleanup();
}
void Window::cleanup()
{
shmdt(m_framebuffer_smo);
close(m_server_fd);
close(m_epoll_fd);
}
BAN::ErrorOr<void> Window::handle_resize_event(const EventPacket::ResizeWindowEvent& event)
{
if (m_framebuffer_smo)
shmdt(m_framebuffer_smo);
m_framebuffer_smo = nullptr;
TRY(m_texture.resize(event.width, event.height));
if (m_root_widget)
TRY(m_root_widget->set_fixed_geometry({ 0, 0, event.width, event.height }));
void* framebuffer_addr = shmat(event.smo_key, nullptr, 0);
if (framebuffer_addr == (void*)-1)
return BAN::Error::from_errno(errno);
m_framebuffer_smo = static_cast<uint32_t*>(framebuffer_addr);
m_width = event.width;
m_height = event.height;
invalidate();
return {};
}
void Window::wait_events()
{
epoll_event dummy;
epoll_wait(m_epoll_fd, &dummy, 1, -1);
}
void Window::poll_events()
{
#define TRY_OR_BREAK(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) break; e.release_value(); })
for (;;)
{
epoll_event event;
if (epoll_wait(m_epoll_fd, &event, 1, 0) == 0)
break;
auto packet_or_error = recv_packet(m_server_fd);
if (packet_or_error.is_error())
return on_socket_error(__FUNCTION__);
const auto [packet_type, packet_data] = packet_or_error.release_value();
switch (packet_type)
{
case PacketType::DestroyWindowEvent:
exit(1);
case PacketType::CloseWindowEvent:
if (m_close_window_event_callback)
m_close_window_event_callback();
else
exit(0);
break;
case PacketType::ResizeWindowEvent:
{
MUST(handle_resize_event(TRY_OR_BREAK(EventPacket::ResizeWindowEvent::deserialize(packet_data.span()))));
if (m_resize_window_event_callback)
m_resize_window_event_callback();
break;
}
case PacketType::WindowShownEvent:
if (m_window_shown_event_callback)
m_window_shown_event_callback(TRY_OR_BREAK(EventPacket::WindowShownEvent::deserialize(packet_data.span())).event);
break;
case PacketType::WindowFocusEvent:
if (m_window_focus_event_callback)
m_window_focus_event_callback(TRY_OR_BREAK(EventPacket::WindowFocusEvent::deserialize(packet_data.span())).event);
break;
case PacketType::KeyEvent:
if (m_key_event_callback)
m_key_event_callback(TRY_OR_BREAK(EventPacket::KeyEvent::deserialize(packet_data.span())).event);
break;
case PacketType::MouseButtonEvent:
{
auto event = TRY_OR_BREAK(EventPacket::MouseButtonEvent::deserialize(packet_data.span())).event;
if (m_mouse_button_event_callback)
m_mouse_button_event_callback(event);
if (m_root_widget)
m_root_widget->on_mouse_button(event);
break;
}
case PacketType::MouseMoveEvent:
{
auto event = TRY_OR_BREAK(EventPacket::MouseMoveEvent::deserialize(packet_data.span())).event;
if (m_mouse_move_event_callback)
m_mouse_move_event_callback(event);
if (m_root_widget)
{
m_root_widget->before_mouse_move();
m_root_widget->on_mouse_move(event);
m_root_widget->after_mouse_move();
}
break;
}
case PacketType::MouseScrollEvent:
if (m_mouse_scroll_event_callback)
m_mouse_scroll_event_callback(TRY_OR_BREAK(EventPacket::MouseScrollEvent::deserialize(packet_data.span())).event);
break;
default:
break;
}
}
#undef TRY_OR_BREAK
if (m_root_widget)
{
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
if (invalidated.w && invalidated.h)
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
}
}
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include <BAN/Span.h>
#include <BAN/StringView.h>
#include <stddef.h>
namespace LibGUI
{
class MessageBox
{
public:
static BAN::ErrorOr<void> create(BAN::StringView message, BAN::StringView title);
static BAN::ErrorOr<size_t> create(BAN::StringView message, BAN::StringView title, BAN::Span<BAN::StringView> buttons);
};
}

View File

@@ -1,384 +0,0 @@
#pragma once
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/ByteSpan.h>
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
#include <cstdint>
#include <sys/socket.h>
#define FOR_EACH_0(macro)
#define FOR_EACH_2(macro, type, name) macro(type, name)
#define FOR_EACH_4(macro, type, name, ...) macro(type, name) FOR_EACH_2(macro, __VA_ARGS__)
#define FOR_EACH_6(macro, type, name, ...) macro(type, name) FOR_EACH_4(macro, __VA_ARGS__)
#define FOR_EACH_8(macro, type, name, ...) macro(type, name) FOR_EACH_6(macro, __VA_ARGS__)
#define FOR_EACH_10(macro, type, name, ...) macro(type, name) FOR_EACH_8(macro, __VA_ARGS__)
#define CONCATENATE_2(arg1, arg2) arg1 ## arg2
#define CONCATENATE_1(arg1, arg2) CONCATENATE_2(arg1, arg2)
#define CONCATENATE(arg1, arg2) CONCATENATE_1(arg1, arg2)
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__ __VA_OPT__(,) FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define FOR_EACH_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define FOR_EACH_(N, what, ...) CONCATENATE(FOR_EACH_, N)(what __VA_OPT__(,) __VA_ARGS__)
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what __VA_OPT__(,) __VA_ARGS__)
#define FIELD_DECL(type, name) type name;
#define ADD_SERIALIZED_SIZE(type, name) serialized_size += Serialize::serialized_size_impl<type>(this->name);
#define SEND_SERIALIZED(type, name) TRY(Serialize::send_serialized_impl<type>(socket, this->name));
#define DESERIALIZE(type, name) value.name = TRY(Serialize::deserialize_impl<type>(buffer));
#define DEFINE_PACKET_EXTRA(name, extra, ...) \
struct name \
{ \
static constexpr PacketType type = PacketType::name; \
static constexpr uint32_t type_u32 = static_cast<uint32_t>(type); \
\
extra; \
\
FOR_EACH(FIELD_DECL, __VA_ARGS__) \
\
size_t serialized_size() \
{ \
size_t serialized_size = Serialize::serialized_size_impl<uint32_t>(type_u32); \
FOR_EACH(ADD_SERIALIZED_SIZE, __VA_ARGS__) \
return serialized_size; \
} \
\
BAN::ErrorOr<void> send_serialized(int socket) \
{ \
const uint32_t serialized_size = this->serialized_size(); \
TRY(Serialize::send_serialized_impl<uint32_t>(socket, serialized_size)); \
TRY(Serialize::send_serialized_impl<uint32_t>(socket, type_u32)); \
FOR_EACH(SEND_SERIALIZED, __VA_ARGS__) \
return {}; \
} \
\
static BAN::ErrorOr<name> deserialize(BAN::ConstByteSpan buffer) \
{ \
const uint32_t type_u32 = TRY(Serialize::deserialize_impl<uint32_t>(buffer)); \
if (type_u32 != name::type_u32) \
return BAN::Error::from_errno(EINVAL); \
name value; \
FOR_EACH(DESERIALIZE, __VA_ARGS__) \
return value; \
} \
}
#define DEFINE_PACKET(name, ...) DEFINE_PACKET_EXTRA(name, , __VA_ARGS__)
namespace LibGUI
{
namespace detail
{
template<typename T>
concept Vector = requires {
requires BAN::same_as<T, BAN::Vector<typename T::value_type>>;
};
}
static constexpr BAN::StringView s_window_server_socket = "/tmp/window-server.socket"_sv;
namespace Serialize
{
inline BAN::ErrorOr<void> send_raw_data(int socket, BAN::ConstByteSpan data)
{
size_t send_done = 0;
while (send_done < data.size())
{
const ssize_t nsend = ::send(socket, data.data() + send_done, data.size() - send_done, 0);
if (nsend < 0)
return BAN::Error::from_errno(errno);
if (nsend == 0)
return BAN::Error::from_errno(ECONNRESET);
send_done += nsend;
}
return {};
}
template<typename T> requires BAN::is_pod_v<T>
inline size_t serialized_size_impl(const T&)
{
return sizeof(T);
}
template<typename T> requires BAN::is_pod_v<T>
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& value)
{
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value)));
return {};
}
template<typename T> requires BAN::is_pod_v<T>
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
{
if (buffer.size() < sizeof(T))
return BAN::Error::from_errno(ENOBUFS);
const T value = buffer.as<const T>();
buffer = buffer.slice(sizeof(T));
return value;
}
template<typename T> requires BAN::is_same_v<T, BAN::String>
inline size_t serialized_size_impl(const T& value)
{
return sizeof(uint32_t) + value.size();
}
template<typename T> requires BAN::is_same_v<T, BAN::String>
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& value)
{
const uint32_t value_size = value.size();
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value_size)));
auto* u8_data = reinterpret_cast<const uint8_t*>(value.data());
TRY(send_raw_data(socket, BAN::ConstByteSpan(u8_data, value.size())));
return {};
}
template<typename T> requires BAN::is_same_v<T, BAN::String>
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
{
if (buffer.size() < sizeof(uint32_t))
return BAN::Error::from_errno(ENOBUFS);
const uint32_t string_len = buffer.as<const uint32_t>();
buffer = buffer.slice(sizeof(uint32_t));
if (buffer.size() < string_len)
return BAN::Error::from_errno(ENOBUFS);
BAN::String string;
TRY(string.resize(string_len));
memcpy(string.data(), buffer.data(), string_len);
buffer = buffer.slice(string_len);
return string;
}
template<detail::Vector T>
inline size_t serialized_size_impl(const T& vector)
{
size_t result = sizeof(uint32_t);
for (const auto& element : vector)
result += serialized_size_impl(element);
return result;
}
template<detail::Vector T>
inline BAN::ErrorOr<void> send_serialized_impl(int socket, const T& vector)
{
const uint32_t value_size = vector.size();
TRY(send_raw_data(socket, BAN::ConstByteSpan::from(value_size)));
for (const auto& element : vector)
TRY(send_serialized_impl(socket, element));
return {};
}
template<detail::Vector T>
inline BAN::ErrorOr<T> deserialize_impl(BAN::ConstByteSpan& buffer)
{
if (buffer.size() < sizeof(uint32_t))
return BAN::Error::from_errno(ENOBUFS);
const uint32_t vector_size = buffer.as<const uint32_t>();
buffer = buffer.slice(sizeof(uint32_t));
T vector;
TRY(vector.resize(vector_size));
for (auto& element : vector)
element = TRY(deserialize_impl<typename T::value_type>(buffer));
return vector;
}
}
enum class PacketType : uint32_t
{
WindowCreate,
WindowCreateResponse,
WindowInvalidate,
WindowSetPosition,
WindowSetAttributes,
WindowSetMouseRelative,
WindowSetSize,
WindowSetMinSize,
WindowSetMaxSize,
WindowSetFullscreen,
WindowSetTitle,
WindowSetCursor,
DestroyWindowEvent,
CloseWindowEvent,
ResizeWindowEvent,
WindowShownEvent,
WindowFocusEvent,
KeyEvent,
MouseButtonEvent,
MouseMoveEvent,
MouseScrollEvent,
};
namespace WindowPacket
{
struct Attributes
{
bool title_bar;
bool movable;
bool focusable;
bool rounded_corners;
bool alpha_channel;
bool resizable;
bool shown;
bool cursor_visible;
};
DEFINE_PACKET(
WindowCreate,
uint32_t, width,
uint32_t, height,
Attributes, attributes,
BAN::String, title
);
DEFINE_PACKET(
WindowInvalidate,
uint32_t, x,
uint32_t, y,
uint32_t, width,
uint32_t, height
);
DEFINE_PACKET(
WindowSetPosition,
int32_t, x,
int32_t, y
);
DEFINE_PACKET(
WindowSetAttributes,
Attributes, attributes
);
DEFINE_PACKET(
WindowSetMouseRelative,
bool, enabled
);
DEFINE_PACKET(
WindowSetSize,
uint32_t, width,
uint32_t, height
);
DEFINE_PACKET(
WindowSetMinSize,
uint32_t, width,
uint32_t, height
);
DEFINE_PACKET(
WindowSetMaxSize,
uint32_t, width,
uint32_t, height
);
DEFINE_PACKET(
WindowSetFullscreen,
bool, fullscreen
);
DEFINE_PACKET(
WindowSetTitle,
BAN::String, title
);
DEFINE_PACKET(
WindowSetCursor,
uint32_t, width,
uint32_t, height,
int32_t, origin_x,
int32_t, origin_y,
BAN::Vector<uint32_t>, pixels
);
}
namespace EventPacket
{
DEFINE_PACKET(
DestroyWindowEvent
);
DEFINE_PACKET(
CloseWindowEvent
);
DEFINE_PACKET(
ResizeWindowEvent,
uint32_t, width,
uint32_t, height,
long, smo_key
);
DEFINE_PACKET_EXTRA(
WindowShownEvent,
struct event_t {
bool shown;
},
event_t, event
);
DEFINE_PACKET_EXTRA(
WindowFocusEvent,
struct event_t {
bool focused;
},
event_t, event
);
DEFINE_PACKET_EXTRA(
KeyEvent,
using event_t = LibInput::KeyEvent,
event_t, event
);
DEFINE_PACKET_EXTRA(
MouseButtonEvent,
struct event_t {
LibInput::MouseButton button;
bool pressed;
int32_t x;
int32_t y;
},
event_t, event
);
DEFINE_PACKET_EXTRA(
MouseMoveEvent,
struct event_t {
int32_t x;
int32_t y;
},
event_t, event
);
DEFINE_PACKET_EXTRA(
MouseScrollEvent,
struct event_t {
int32_t scroll;
},
event_t, event
);
}
}

View File

@@ -1,103 +0,0 @@
#pragma once
#include <BAN/StringView.h>
#include <cstdint>
#include <stdint.h>
namespace LibFont { class Font; }
namespace LibGUI
{
class Texture
{
public:
static constexpr uint32_t color_invisible = 0x69000000;
public:
static BAN::ErrorOr<Texture> create(uint32_t width, uint32_t height, uint32_t color);
Texture() = default;
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);
void set_pixel(uint32_t x, uint32_t y, uint32_t color)
{
ASSERT(x < m_width);
ASSERT(y < m_height);
if (x < m_clip_x || x >= m_clip_x + m_clip_w)
return;
if (y < m_clip_y || y >= m_clip_y + m_clip_h)
return;
m_pixels[y * m_width + x] = color;
}
uint32_t get_pixel(uint32_t x, uint32_t y) const
{
ASSERT(x < m_width);
ASSERT(y < m_height);
return m_pixels[y * m_width + x];
}
BAN::Span<uint32_t> pixels() { return m_pixels.span(); }
BAN::Span<const uint32_t> pixels() const { return m_pixels.span(); }
void set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height);
void fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color);
void fill(uint32_t color) { return fill_rect(0, 0, width(), height(), color); }
void clear_rect(int32_t x, int32_t y, uint32_t width, uint32_t height) { fill_rect(x, y, width, height, m_bg_color); }
void clear() { return clear_rect(0, 0, width(), height()); }
void copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x = 0, uint32_t sub_y = 0, uint32_t width = -1, uint32_t height = -1);
void draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
void draw_text(BAN::StringView text, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
// shift whole vertically by amount pixels, sign determines the direction
void shift_vertical(int32_t amount);
// copy horizontal slice [src_y, src_y + amount[ to [dst_y, dst_y + amount[
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount);
// copy rect (src_x, src_y, width, height) to (dst_x, dst_y, width, height)
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height);
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
// used on resize to fill empty space
void set_bg_color(uint32_t bg_color) { m_bg_color = bg_color; }
private:
Texture(BAN::Vector<uint32_t>&& pixels, uint32_t width, uint32_t height, uint32_t color)
: m_pixels(BAN::move(pixels))
, m_width(width)
, m_height(height)
, m_bg_color(color)
, m_clip_x(0)
, m_clip_y(0)
, m_clip_w(width)
, m_clip_h(height)
{}
bool clamp_to_texture(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const;
bool clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture&) const;
private:
BAN::Vector<uint32_t> m_pixels;
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_bg_color { 0xFFFFFFFF };
uint32_t m_clip_x { 0 };
uint32_t m_clip_y { 0 };
uint32_t m_clip_w { 0 };
uint32_t m_clip_h { 0 };
bool m_has_set_clip { false };
friend class Window;
};
}

View File

@@ -1,54 +0,0 @@
#pragma once
#include <BAN/Function.h>
#include <BAN/StringView.h>
#include <LibGUI/Widget/RoundedWidget.h>
namespace LibGUI::Widget
{
class Button : public RoundedWidget
{
public:
struct Style : RoundedWidget::Style
{
Style()
: RoundedWidget::Style()
, color_hovered(0x808080)
, color_text(0x000000)
{}
uint32_t color_hovered;
uint32_t color_text;
};
public:
static BAN::ErrorOr<BAN::RefPtr<Button>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
BAN::ErrorOr<void> set_text(BAN::StringView);
Style& style() { return m_style; }
const Style& style() const { return m_style; }
void set_click_callback(BAN::Function<void()> callback) { m_click_callback = callback; }
protected:
Button(BAN::RefPtr<Widget> parent, Rectangle area)
: RoundedWidget(parent, area)
{ }
void update_impl() override;
void show_impl() override;
bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) override;
private:
Style m_style;
bool m_hover_state { false };
BAN::String m_text;
BAN::Function<void()> m_click_callback;
};
}

View File

@@ -1,42 +0,0 @@
#pragma once
#include <LibGUI/Widget/Widget.h>
namespace LibGUI::Widget
{
class Grid : public Widget
{
public:
static BAN::ErrorOr<BAN::RefPtr<Grid>> create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color = color_invisible, Rectangle geometry = {});
BAN::ErrorOr<void> set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span);
protected:
Grid(BAN::RefPtr<Widget> parent, Rectangle geometry, uint32_t cols, uint32_t rows)
: Widget(parent, geometry)
, m_cols(cols)
, m_rows(rows)
{ }
BAN::ErrorOr<void> update_geometry_impl() override;
private:
struct GridElement
{
BAN::RefPtr<Widget> widget;
uint32_t col;
uint32_t col_span;
uint32_t row;
uint32_t row_span;
};
Rectangle grid_element_area(const GridElement& element) const;
private:
const uint32_t m_cols;
const uint32_t m_rows;
BAN::Vector<GridElement> m_grid_elements;
};
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include <BAN/StringView.h>
#include <LibGUI/Widget/RoundedWidget.h>
namespace LibGUI::Widget
{
class Label : public RoundedWidget
{
public:
struct Style : RoundedWidget::Style
{
Style()
: RoundedWidget::Style()
, color_text(0x000000)
{}
uint32_t color_text;
};
public:
static BAN::ErrorOr<BAN::RefPtr<Label>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
BAN::StringView text() const { return m_text; }
BAN::ErrorOr<void> set_text(BAN::StringView);
Style& style() { return m_style; }
const Style& style() const { return m_style; }
protected:
Label(BAN::RefPtr<Widget> parent, Rectangle area)
: RoundedWidget(parent, area)
{ }
void show_impl() override;
private:
Style m_style;
BAN::String m_text;
};
}

View File

@@ -1,42 +0,0 @@
#pragma once
#include <LibGUI/Widget/Widget.h>
namespace LibGUI::Widget
{
class RoundedWidget : public Widget
{
public:
struct Style
{
Style(uint32_t color_normal = 0xA0A0A0, uint32_t border_width = 1, uint32_t color_border = 0x000000, uint32_t corner_radius = 5)
: color_normal(color_normal)
, border_width(border_width)
, color_border(color_border)
, corner_radius(corner_radius)
{}
uint32_t color_normal;
uint32_t border_width;
uint32_t color_border;
uint32_t corner_radius;
};
Style& style() { return m_style; }
const Style& style() const { return m_style; }
protected:
RoundedWidget(BAN::RefPtr<Widget> parent, Rectangle area)
: Widget(parent, area)
{ }
bool contains(Point point) const override;
void show_impl() override;
private:
Style m_style;
};
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include <BAN/StringView.h>
#include <LibGUI/Widget/RoundedWidget.h>
namespace LibGUI::Widget
{
class TextArea : public RoundedWidget
{
public:
struct Style : RoundedWidget::Style
{
Style()
: RoundedWidget::Style()
, color_text(0x000000)
{}
uint32_t color_text;
};
public:
static BAN::ErrorOr<BAN::RefPtr<TextArea>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
BAN::StringView text() const { return m_text; }
BAN::ErrorOr<void> set_text(BAN::StringView);
uint32_t get_required_height() const;
Style& style() { return m_style; }
const Style& style() const { return m_style; }
protected:
TextArea(BAN::RefPtr<Widget> parent, Rectangle area)
: RoundedWidget(parent, area)
{ }
BAN::ErrorOr<void> wrap_text();
BAN::ErrorOr<void> update_geometry_impl() override;
void show_impl() override;
private:
Style m_style;
BAN::String m_text;
BAN::Vector<BAN::String> m_wrapped_text;
};
}

View File

@@ -1,163 +0,0 @@
#pragma once
#include <BAN/RefPtr.h>
#include <LibGUI/Texture.h>
#include <LibGUI/Packet.h>
namespace LibGUI { class Window; }
namespace LibGUI::Widget
{
class Widget : public BAN::RefCounted<Widget>
{
public:
static constexpr uint32_t color_invisible = Texture::color_invisible;
struct Point
{
int32_t x, y;
};
struct FloatRectangle
{
float x, y;
float w, h;
};
struct Rectangle
{
int32_t x, y;
uint32_t w, h;
struct Bounds
{
int32_t min_x, min_y;
int32_t max_x, max_y;
};
bool contains(Point point) const
{
if (point.x < x || point.x >= x + static_cast<int32_t>(w))
return false;
if (point.y < y || point.y >= y + static_cast<int32_t>(h))
return false;
return true;
}
Bounds bounds(Rectangle other) const
{
return Bounds {
.min_x = BAN::Math::max(x, other.x),
.min_y = BAN::Math::max(y, other.y),
.max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w)),
.max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h)),
};
};
Rectangle overlap(Rectangle other) const
{
const auto min_x = BAN::Math::max(x, other.x);
const auto min_y = BAN::Math::max(y, other.y);
const auto max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
const auto max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
if (min_x >= max_x || min_y >= max_y)
return {};
return Rectangle {
.x = min_x,
.y = min_y,
.w = static_cast<uint32_t>(max_x - min_x),
.h = static_cast<uint32_t>(max_y - min_y),
};
}
Rectangle bounding_box(Rectangle other) const
{
if (w == 0 || h == 0)
return other;
if (other.w == 0 || other.h == 0)
return *this;
const auto min_x = BAN::Math::min(x, other.x);
const auto min_y = BAN::Math::min(y, other.y);
const auto max_x = BAN::Math::max(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
const auto max_y = BAN::Math::max(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
return Rectangle {
.x = min_x,
.y = min_y,
.w = static_cast<uint32_t>(max_x - min_x),
.h = static_cast<uint32_t>(max_y - min_y),
};
}
};
public:
static BAN::ErrorOr<BAN::RefPtr<Widget>> create(BAN::RefPtr<Widget> parent, uint32_t color = color_invisible, Rectangle geometry = {});
static BAN::ErrorOr<void> set_default_font(BAN::StringView path);
static const LibFont::Font& default_font();
void show();
void hide();
BAN::ErrorOr<void> set_fixed_geometry(Rectangle);
BAN::ErrorOr<void> set_relative_geometry(FloatRectangle);
BAN::RefPtr<Widget> parent() { return m_parent; }
uint32_t width() const { return m_fixed_area.w; }
uint32_t height() const { return m_fixed_area.h; }
private:
void before_mouse_move();
void after_mouse_move();
bool on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t);
bool on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t);
protected:
Widget(BAN::RefPtr<Widget> parent, Rectangle area)
: m_parent(parent)
, m_fixed_area(area)
{ }
BAN::ErrorOr<void> initialize(uint32_t color);
virtual bool contains(Point point) const { return Rectangle { 0, 0, width(), height() }.contains(point); }
bool is_hovered() const { return m_hovered; }
bool is_child_hovered() const;
bool is_shown() const { return m_shown; }
Rectangle render(Texture& output, Point parent_position, Rectangle out_area);
virtual void update_impl() {}
virtual void show_impl() {}
virtual BAN::ErrorOr<void> update_geometry_impl();
virtual void on_hover_change_impl(bool hovered) { (void)hovered; }
virtual bool on_mouse_move_impl(LibGUI::EventPacket::MouseMoveEvent::event_t) { return true; }
virtual bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) { return true; }
protected:
Texture m_texture;
private:
BAN::RefPtr<Widget> m_parent;
BAN::Vector<BAN::RefPtr<Widget>> m_children;
bool m_shown { false };
Rectangle m_fixed_area;
BAN::Optional<FloatRectangle> m_relative_area;
bool m_changed { false };
bool m_hovered { false };
bool m_old_hovered { false };
friend class LibGUI::Window;
};
}

View File

@@ -1,123 +0,0 @@
#pragma once
#include <BAN/Function.h>
#include <BAN/StringView.h>
#include <BAN/UniqPtr.h>
#include <LibGUI/Packet.h>
#include <LibGUI/Texture.h>
#include <LibGUI/Widget/Widget.h>
namespace LibFont { class Font; }
namespace LibGUI
{
class Window
{
public:
using Attributes = WindowPacket::Attributes;
static constexpr Attributes default_attributes = {
.title_bar = true,
.movable = true,
.focusable = true,
.rounded_corners = true,
.alpha_channel = false,
.resizable = false,
.shown = true,
.cursor_visible = true,
};
public:
~Window();
static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes = default_attributes);
BAN::ErrorOr<void> set_root_widget(BAN::RefPtr<Widget::Widget> widget);
BAN::RefPtr<Widget::Widget> root_widget() { return m_root_widget; }
Texture& texture() { return m_texture; }
const Texture& texture() const { return m_texture; }
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
void invalidate() { return invalidate(0, 0, width(), height()); }
void set_mouse_relative(bool enabled);
void set_fullscreen(bool fullscreen);
void set_title(BAN::StringView title);
void set_position(int32_t x, int32_t y);
void set_cursor_visible(bool visible);
void set_cursor(uint32_t width, uint32_t height, BAN::Span<const uint32_t> pixels, int32_t origin_x = 0, int32_t origin_y = 0);
Attributes get_attributes() const { return m_attributes; }
void set_attributes(Attributes attributes);
void set_min_size(uint32_t width, uint32_t height);
void set_max_size(uint32_t width, uint32_t height);
// send resize request to window server
// actual resize is only done after resize callback is called
void request_resize(uint32_t width, uint32_t height);
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
void wait_events();
void poll_events();
void set_socket_error_callback(BAN::Function<void()> callback) { m_socket_error_callback = callback; }
void set_close_window_event_callback(BAN::Function<void()> callback) { m_close_window_event_callback = callback; }
void set_resize_window_event_callback(BAN::Function<void()> callback) { m_resize_window_event_callback = callback; }
void set_key_event_callback(BAN::Function<void(EventPacket::KeyEvent::event_t)> callback) { m_key_event_callback = callback; }
void set_mouse_button_event_callback(BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> callback) { m_mouse_button_event_callback = callback; }
void set_mouse_move_event_callback(BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> callback) { m_mouse_move_event_callback = callback; }
void set_mouse_scroll_event_callback(BAN::Function<void(EventPacket::MouseScrollEvent::event_t)> callback) { m_mouse_scroll_event_callback = callback; }
void set_window_shown_event_callback(BAN::Function<void(EventPacket::WindowShownEvent::event_t)> callback) { m_window_shown_event_callback = callback; }
void set_window_focus_event_callback(BAN::Function<void(EventPacket::WindowFocusEvent::event_t)> callback) { m_window_focus_event_callback = callback; }
int server_fd() const { return m_server_fd; }
private:
Window(int server_fd, int epoll_fd, Attributes attributes)
: m_server_fd(server_fd)
, m_epoll_fd(epoll_fd)
, m_attributes(attributes)
{ }
void on_socket_error(BAN::StringView function);
void cleanup();
BAN::ErrorOr<void> handle_resize_event(const EventPacket::ResizeWindowEvent&);
private:
const int m_server_fd;
const int m_epoll_fd;
bool m_handling_socket_error { false };
Attributes m_attributes;
uint32_t* m_framebuffer_smo { nullptr };
uint32_t m_width { 0 };
uint32_t m_height { 0 };
Texture m_texture;
BAN::RefPtr<Widget::Widget> m_root_widget;
BAN::Function<void()> m_socket_error_callback;
BAN::Function<void()> m_close_window_event_callback;
BAN::Function<void()> m_resize_window_event_callback;
BAN::Function<void(EventPacket::WindowShownEvent::event_t)> m_window_shown_event_callback;
BAN::Function<void(EventPacket::WindowFocusEvent::event_t)> m_window_focus_event_callback;
BAN::Function<void(EventPacket::KeyEvent::event_t)> m_key_event_callback;
BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> m_mouse_button_event_callback;
BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> m_mouse_move_event_callback;
BAN::Function<void(EventPacket::MouseScrollEvent::event_t)> m_mouse_scroll_event_callback;
friend class BAN::UniqPtr<Window>;
};
}

View File

@@ -1,12 +0,0 @@
set(LIBIMAGE_SOURCES
Image.cpp
Netbpm.cpp
PNG.cpp
)
add_library(libimage ${LIBIMAGE_SOURCES})
banan_link_library(libimage ban)
banan_link_library(libimage libdeflate)
banan_install_headers(libimage)
install(TARGETS libimage OPTIONAL)

Some files were not shown because too many files have changed in this diff Show More