From 47b935e2c85a794d60c22e8d96f53c0a2d364977 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 13:55:03 +0200 Subject: kstd: rename kstd header to include hpp --- arch/x86_64/arch/cpu/control_register.hpp | 2 +- arch/x86_64/arch/cpu/initialization.cpp | 2 +- arch/x86_64/arch/cpu/interrupts.cpp | 2 +- arch/x86_64/arch/cpu/model_specific_register.hpp | 2 +- arch/x86_64/arch/devices/init.cpp | 4 +- arch/x86_64/arch/devices/local_apic.cpp | 2 +- arch/x86_64/arch/memory/kernel_mapper.cpp | 4 +- arch/x86_64/arch/memory/page_table.hpp | 4 +- arch/x86_64/kapi/boot_modules.cpp | 2 +- arch/x86_64/kapi/cpu.cpp | 4 +- arch/x86_64/kapi/memory.cpp | 4 +- kapi/kapi/acpi.hpp | 4 +- kapi/kapi/boot_module/boot_module_registry.hpp | 2 +- kapi/kapi/cio.hpp | 2 +- kapi/kapi/cpu.hpp | 4 +- kapi/kapi/devices/bus.hpp | 8 +- kapi/kapi/devices/device.hpp | 4 +- kapi/kapi/devices/manager.hpp | 2 +- kapi/kapi/memory/address.hpp | 4 +- kapi/kapi/memory/chunk.hpp | 2 +- kapi/kapi/memory/layout.hpp | 2 +- kapi/kapi/memory/page_mapper.hpp | 2 +- kernel/kapi/acpi.cpp | 2 +- kernel/kapi/cpu.cpp | 2 +- kernel/kapi/devices.cpp | 6 +- kernel/kapi/devices/bus.cpp | 8 +- kernel/kapi/devices/device.cpp | 4 +- kernel/kapi/filesystem.cpp | 2 +- kernel/kapi/interrupts.cpp | 6 +- kernel/kapi/memory.cpp | 4 +- kernel/kapi/system.cpp | 2 +- kernel/kapi/system.tests.cpp | 2 +- kernel/kernel/acpi/manager.cpp | 4 +- kernel/kernel/acpi/manager.hpp | 6 +- kernel/kernel/devices/block_device.cpp | 2 +- kernel/kernel/devices/block_device.hpp | 2 +- kernel/kernel/devices/block_device.tests.cpp | 8 +- kernel/kernel/devices/block_device_utils.cpp | 6 +- kernel/kernel/devices/block_device_utils.hpp | 2 +- kernel/kernel/devices/block_device_utils.tests.cpp | 6 +- kernel/kernel/devices/storage/controller.cpp | 4 +- kernel/kernel/devices/storage/controller.hpp | 4 +- kernel/kernel/devices/storage/management.cpp | 4 +- kernel/kernel/devices/storage/management.hpp | 4 +- .../kernel/devices/storage/ram_disk/controller.cpp | 2 +- kernel/kernel/devices/storage/ram_disk/device.cpp | 4 +- kernel/kernel/filesystem/dentry.cpp | 4 +- kernel/kernel/filesystem/dentry.hpp | 6 +- kernel/kernel/filesystem/dentry.tests.cpp | 4 +- kernel/kernel/filesystem/devfs/filesystem.cpp | 2 +- kernel/kernel/filesystem/devfs/filesystem.hpp | 4 +- kernel/kernel/filesystem/devfs/inode.tests.cpp | 6 +- kernel/kernel/filesystem/device_inode.cpp | 2 +- kernel/kernel/filesystem/device_inode.hpp | 2 +- kernel/kernel/filesystem/device_inode.tests.cpp | 6 +- kernel/kernel/filesystem/ext2/filesystem.cpp | 4 +- kernel/kernel/filesystem/ext2/filesystem.hpp | 4 +- kernel/kernel/filesystem/ext2/filesystem.tests.cpp | 4 +- kernel/kernel/filesystem/ext2/inode.cpp | 2 +- kernel/kernel/filesystem/ext2/inode.hpp | 2 +- kernel/kernel/filesystem/ext2/inode.tests.cpp | 4 +- kernel/kernel/filesystem/filesystem.cpp | 2 +- kernel/kernel/filesystem/filesystem.hpp | 4 +- kernel/kernel/filesystem/mount.cpp | 4 +- kernel/kernel/filesystem/mount.hpp | 4 +- kernel/kernel/filesystem/mount.tests.cpp | 6 +- kernel/kernel/filesystem/mount_table.cpp | 4 +- kernel/kernel/filesystem/mount_table.hpp | 4 +- kernel/kernel/filesystem/mount_table.tests.cpp | 6 +- kernel/kernel/filesystem/open_file_descriptor.cpp | 2 +- kernel/kernel/filesystem/open_file_descriptor.hpp | 2 +- .../filesystem/open_file_descriptor.tests.cpp | 6 +- kernel/kernel/filesystem/open_file_table.cpp | 2 +- kernel/kernel/filesystem/open_file_table.hpp | 4 +- kernel/kernel/filesystem/open_file_table.tests.cpp | 6 +- kernel/kernel/filesystem/path.hpp | 2 +- kernel/kernel/filesystem/rootfs/filesystem.cpp | 2 +- kernel/kernel/filesystem/rootfs/filesystem.hpp | 6 +- .../kernel/filesystem/rootfs/filesystem.tests.cpp | 6 +- kernel/kernel/filesystem/rootfs/inode.hpp | 6 +- kernel/kernel/filesystem/rootfs/inode.tests.cpp | 6 +- kernel/kernel/filesystem/type.hpp | 2 +- kernel/kernel/filesystem/type_registry.cpp | 4 +- kernel/kernel/filesystem/type_registry.hpp | 6 +- kernel/kernel/filesystem/type_registry.tests.cpp | 2 +- kernel/kernel/filesystem/vfs.cpp | 4 +- kernel/kernel/filesystem/vfs.hpp | 2 +- kernel/kernel/filesystem/vfs.tests.cpp | 4 +- kernel/kernel/main.cpp | 8 +- kernel/kernel/memory.cpp | 4 +- kernel/kernel/memory/block_list_allocator.cpp | 4 +- kernel/kernel/memory/block_list_allocator.hpp | 4 +- .../kernel/memory/block_list_allocator.tests.cpp | 2 +- kernel/kernel/memory/heap_allocator.hpp | 2 +- kernel/kernel/memory/mmio_allocator.cpp | 2 +- kernel/kernel/memory/mmio_allocator.hpp | 4 +- kernel/kernel/memory/operators.cpp | 2 +- .../kernel/test_support/devices/block_device.cpp | 4 +- .../kernel/test_support/devices/block_device.hpp | 4 +- .../test_support/devices/character_device.cpp | 2 +- .../test_support/devices/character_device.hpp | 4 +- .../kernel/test_support/filesystem/filesystem.cpp | 2 +- .../kernel/test_support/filesystem/filesystem.hpp | 2 +- kernel/kernel/test_support/kapi/memory.cpp | 2 +- kernel/kernel/test_support/page_mapper.cpp | 2 +- kernel/kernel/test_support/page_mapper.hpp | 2 +- kernel/kernel/test_support/simulated_memory.cpp | 2 +- kernel/kernel/test_support/simulated_memory.hpp | 2 +- kernel/kstd/print.cpp | 4 +- kernel/kstd/print.tests.cpp | 2 +- libs/acpi/acpi/common/table_header.cpp | 4 +- libs/acpi/acpi/common/table_header.hpp | 2 +- libs/acpi/acpi/common/table_header.test.cpp | 2 +- libs/acpi/acpi/data/madt.cpp | 2 +- libs/acpi/acpi/data/madt.hpp | 4 +- libs/acpi/acpi/data/madt.test.cpp | 2 +- libs/acpi/acpi/data/rsdt.cpp | 2 +- libs/acpi/acpi/data/rsdt.test.cpp | 2 +- libs/acpi/acpi/data/xsdt.cpp | 2 +- libs/acpi/acpi/data/xsdt.test.cpp | 2 +- libs/acpi/acpi/pointers.cpp | 2 +- libs/acpi/acpi/pointers.hpp | 2 +- libs/kstd/kstd/allocator | 64 -- libs/kstd/kstd/allocator.hpp | 64 ++ libs/kstd/kstd/asm_ptr | 78 -- libs/kstd/kstd/asm_ptr.hpp | 78 ++ libs/kstd/kstd/bits/basic_string.hpp | 4 +- libs/kstd/kstd/bits/format/arg.hpp | 2 +- libs/kstd/kstd/bits/format/args.hpp | 2 +- libs/kstd/kstd/bits/format/context.hpp | 2 +- libs/kstd/kstd/bits/format/formatter.hpp | 2 +- libs/kstd/kstd/bits/format/output_buffer.hpp | 2 +- libs/kstd/kstd/bits/format/parse_context.hpp | 2 +- libs/kstd/kstd/bits/format/string.hpp | 2 +- libs/kstd/kstd/bits/format/vformat.cpp | 4 +- libs/kstd/kstd/bits/format/vformat.hpp | 2 +- libs/kstd/kstd/bits/observer_ptr.hpp | 2 +- libs/kstd/kstd/bits/observer_ptr.test.cpp | 2 +- libs/kstd/kstd/bits/print_sink.hpp | 2 +- libs/kstd/kstd/bits/shared_ptr.hpp | 2 +- libs/kstd/kstd/bits/unique_ptr.hpp | 2 +- libs/kstd/kstd/cstring | 21 - libs/kstd/kstd/cstring.hpp | 21 + libs/kstd/kstd/ext/bitfield_enum | 65 -- libs/kstd/kstd/ext/bitfield_enum.hpp | 65 ++ libs/kstd/kstd/flat_map | 406 -------- libs/kstd/kstd/flat_map.hpp | 406 ++++++++ libs/kstd/kstd/flat_map.test.cpp | 2 +- libs/kstd/kstd/format | 24 - libs/kstd/kstd/format.hpp | 24 + libs/kstd/kstd/format.test.cpp | 2 +- libs/kstd/kstd/libc/string.cpp | 2 +- libs/kstd/kstd/memory | 8 - libs/kstd/kstd/memory.hpp | 8 + libs/kstd/kstd/mutex | 101 -- libs/kstd/kstd/mutex.cpp | 2 +- libs/kstd/kstd/mutex.hpp | 101 ++ libs/kstd/kstd/print | 69 -- libs/kstd/kstd/print.hpp | 69 ++ libs/kstd/kstd/ranges | 21 - libs/kstd/kstd/ranges.hpp | 21 + libs/kstd/kstd/stack | 192 ---- libs/kstd/kstd/stack.hpp | 192 ++++ libs/kstd/kstd/string | 60 -- libs/kstd/kstd/string.hpp | 60 ++ libs/kstd/kstd/string.test.cpp | 4 +- libs/kstd/kstd/units | 149 --- libs/kstd/kstd/units.hpp | 149 +++ libs/kstd/kstd/vector | 1079 -------------------- libs/kstd/kstd/vector.hpp | 1079 ++++++++++++++++++++ libs/kstd/kstd/vector.test.cpp | 4 +- libs/multiboot2/multiboot2/information.hpp | 2 +- libs/multiboot2/multiboot2/information/data.hpp | 2 +- 173 files changed, 2575 insertions(+), 2575 deletions(-) delete mode 100644 libs/kstd/kstd/allocator create mode 100644 libs/kstd/kstd/allocator.hpp delete mode 100644 libs/kstd/kstd/asm_ptr create mode 100644 libs/kstd/kstd/asm_ptr.hpp delete mode 100644 libs/kstd/kstd/cstring create mode 100644 libs/kstd/kstd/cstring.hpp delete mode 100644 libs/kstd/kstd/ext/bitfield_enum create mode 100644 libs/kstd/kstd/ext/bitfield_enum.hpp delete mode 100644 libs/kstd/kstd/flat_map create mode 100644 libs/kstd/kstd/flat_map.hpp delete mode 100644 libs/kstd/kstd/format create mode 100644 libs/kstd/kstd/format.hpp delete mode 100644 libs/kstd/kstd/memory create mode 100644 libs/kstd/kstd/memory.hpp delete mode 100644 libs/kstd/kstd/mutex create mode 100644 libs/kstd/kstd/mutex.hpp delete mode 100644 libs/kstd/kstd/print create mode 100644 libs/kstd/kstd/print.hpp delete mode 100644 libs/kstd/kstd/ranges create mode 100644 libs/kstd/kstd/ranges.hpp delete mode 100644 libs/kstd/kstd/stack create mode 100644 libs/kstd/kstd/stack.hpp delete mode 100644 libs/kstd/kstd/string create mode 100644 libs/kstd/kstd/string.hpp delete mode 100644 libs/kstd/kstd/units create mode 100644 libs/kstd/kstd/units.hpp delete mode 100644 libs/kstd/kstd/vector create mode 100644 libs/kstd/kstd/vector.hpp diff --git a/arch/x86_64/arch/cpu/control_register.hpp b/arch/x86_64/arch/cpu/control_register.hpp index 9cedc35a..ad307081 100644 --- a/arch/x86_64/arch/cpu/control_register.hpp +++ b/arch/x86_64/arch/cpu/control_register.hpp @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/arch/x86_64/arch/cpu/initialization.cpp b/arch/x86_64/arch/cpu/initialization.cpp index 1be9c821..49870eb7 100644 --- a/arch/x86_64/arch/cpu/initialization.cpp +++ b/arch/x86_64/arch/cpu/initialization.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/arch/x86_64/arch/cpu/interrupts.cpp b/arch/x86_64/arch/cpu/interrupts.cpp index f40422fc..99186e2d 100644 --- a/arch/x86_64/arch/cpu/interrupts.cpp +++ b/arch/x86_64/arch/cpu/interrupts.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include diff --git a/arch/x86_64/arch/cpu/model_specific_register.hpp b/arch/x86_64/arch/cpu/model_specific_register.hpp index bd4aff9d..cf07737f 100644 --- a/arch/x86_64/arch/cpu/model_specific_register.hpp +++ b/arch/x86_64/arch/cpu/model_specific_register.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include +#include #include #include diff --git a/arch/x86_64/arch/devices/init.cpp b/arch/x86_64/arch/devices/init.cpp index c30e8cf0..134d6dac 100644 --- a/arch/x86_64/arch/devices/init.cpp +++ b/arch/x86_64/arch/devices/init.cpp @@ -10,8 +10,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/arch/x86_64/arch/devices/local_apic.cpp b/arch/x86_64/arch/devices/local_apic.cpp index 660921bd..b481a667 100644 --- a/arch/x86_64/arch/devices/local_apic.cpp +++ b/arch/x86_64/arch/devices/local_apic.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/arch/x86_64/arch/memory/kernel_mapper.cpp b/arch/x86_64/arch/memory/kernel_mapper.cpp index 74272a0f..0ddf08dd 100644 --- a/arch/x86_64/arch/memory/kernel_mapper.cpp +++ b/arch/x86_64/arch/memory/kernel_mapper.cpp @@ -8,8 +8,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/arch/x86_64/arch/memory/page_table.hpp b/arch/x86_64/arch/memory/page_table.hpp index ce3d3a14..f288e3da 100644 --- a/arch/x86_64/arch/memory/page_table.hpp +++ b/arch/x86_64/arch/memory/page_table.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/arch/x86_64/kapi/boot_modules.cpp b/arch/x86_64/kapi/boot_modules.cpp index fb6bf465..9ca867a5 100644 --- a/arch/x86_64/kapi/boot_modules.cpp +++ b/arch/x86_64/kapi/boot_modules.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 40dc2281..70cd3635 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -11,8 +11,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 5b870d51..a6f84f07 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -12,8 +12,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kapi/kapi/acpi.hpp b/kapi/kapi/acpi.hpp index 885fcde1..eb82c781 100644 --- a/kapi/kapi/acpi.hpp +++ b/kapi/kapi/acpi.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kapi/kapi/boot_module/boot_module_registry.hpp b/kapi/kapi/boot_module/boot_module_registry.hpp index fc3590fc..fdba0740 100644 --- a/kapi/kapi/boot_module/boot_module_registry.hpp +++ b/kapi/kapi/boot_module/boot_module_registry.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kapi/kapi/cio.hpp b/kapi/kapi/cio.hpp index 9bbf7fa7..5c4cc8b7 100644 --- a/kapi/kapi/cio.hpp +++ b/kapi/kapi/cio.hpp @@ -3,7 +3,7 @@ #include // IWYU pragma: export -#include +#include #include #include diff --git a/kapi/kapi/cpu.hpp b/kapi/kapi/cpu.hpp index deaf5cd7..41e00e40 100644 --- a/kapi/kapi/cpu.hpp +++ b/kapi/kapi/cpu.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kapi/kapi/devices/bus.hpp b/kapi/kapi/devices/bus.hpp index 59f49f73..8e2b5acf 100644 --- a/kapi/kapi/devices/bus.hpp +++ b/kapi/kapi/devices/bus.hpp @@ -5,10 +5,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/kapi/kapi/devices/device.hpp b/kapi/kapi/devices/device.hpp index 70cf01f3..104d7b8e 100644 --- a/kapi/kapi/devices/device.hpp +++ b/kapi/kapi/devices/device.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private, include -#include -#include +#include +#include #include diff --git a/kapi/kapi/devices/manager.hpp b/kapi/kapi/devices/manager.hpp index c9b90b4e..c317b61e 100644 --- a/kapi/kapi/devices/manager.hpp +++ b/kapi/kapi/devices/manager.hpp @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/kapi/kapi/memory/address.hpp b/kapi/kapi/memory/address.hpp index 9231cfc6..d176d2b8 100644 --- a/kapi/kapi/memory/address.hpp +++ b/kapi/kapi/memory/address.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private, include -#include -#include +#include +#include #include #include diff --git a/kapi/kapi/memory/chunk.hpp b/kapi/kapi/memory/chunk.hpp index 485a890d..36d83b2e 100644 --- a/kapi/kapi/memory/chunk.hpp +++ b/kapi/kapi/memory/chunk.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include +#include #include #include diff --git a/kapi/kapi/memory/layout.hpp b/kapi/kapi/memory/layout.hpp index 733fa960..5f720a51 100644 --- a/kapi/kapi/memory/layout.hpp +++ b/kapi/kapi/memory/layout.hpp @@ -5,7 +5,7 @@ #include -#include +#include namespace kapi::memory { diff --git a/kapi/kapi/memory/page_mapper.hpp b/kapi/kapi/memory/page_mapper.hpp index fb600b2d..579c14fd 100644 --- a/kapi/kapi/memory/page_mapper.hpp +++ b/kapi/kapi/memory/page_mapper.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index b6d5cdf9..19188590 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -6,7 +6,7 @@ #include -#include +#include #include #include diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index 7b1a43b3..44331908 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -2,7 +2,7 @@ #include -#include +#include namespace kapi::cpu { diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 572227e6..577a8898 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -4,9 +4,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index 59753f79..43310d2b 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -3,10 +3,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp index 8b5d6b97..5dc59a27 100644 --- a/kernel/kapi/devices/device.cpp +++ b/kernel/kapi/devices/device.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 68b51c97..4880ae38 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index 4efcaa37..18a1b29d 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -1,8 +1,8 @@ #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index 5ea08b16..91a6c2c7 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kapi/system.cpp b/kernel/kapi/system.cpp index 9819ceb7..86c99515 100644 --- a/kernel/kapi/system.cpp +++ b/kernel/kapi/system.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp index 1e300316..a05e348b 100644 --- a/kernel/kapi/system.tests.cpp +++ b/kernel/kapi/system.tests.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/acpi/manager.cpp b/kernel/kernel/acpi/manager.cpp index bb895fda..e99baed8 100644 --- a/kernel/kernel/acpi/manager.cpp +++ b/kernel/kernel/acpi/manager.cpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/acpi/manager.hpp b/kernel/kernel/acpi/manager.hpp index 1e8c1e85..ba05c682 100644 --- a/kernel/kernel/acpi/manager.hpp +++ b/kernel/kernel/acpi/manager.hpp @@ -3,9 +3,9 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/devices/block_device.cpp b/kernel/kernel/devices/block_device.cpp index 13d73acd..a3531121 100644 --- a/kernel/kernel/devices/block_device.cpp +++ b/kernel/kernel/devices/block_device.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/devices/block_device.hpp b/kernel/kernel/devices/block_device.hpp index a6d68ee2..31599502 100644 --- a/kernel/kernel/devices/block_device.hpp +++ b/kernel/kernel/devices/block_device.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/devices/block_device.tests.cpp b/kernel/kernel/devices/block_device.tests.cpp index a2ddd2bb..98722638 100644 --- a/kernel/kernel/devices/block_device.tests.cpp +++ b/kernel/kernel/devices/block_device.tests.cpp @@ -2,10 +2,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include diff --git a/kernel/kernel/devices/block_device_utils.cpp b/kernel/kernel/devices/block_device_utils.cpp index 18d1e9d7..68a0a306 100644 --- a/kernel/kernel/devices/block_device_utils.cpp +++ b/kernel/kernel/devices/block_device_utils.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/kernel/kernel/devices/block_device_utils.hpp b/kernel/kernel/devices/block_device_utils.hpp index 8be75b6f..88e23347 100644 --- a/kernel/kernel/devices/block_device_utils.hpp +++ b/kernel/kernel/devices/block_device_utils.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/devices/block_device_utils.tests.cpp b/kernel/kernel/devices/block_device_utils.tests.cpp index e2e1e65e..c8c4acfb 100644 --- a/kernel/kernel/devices/block_device_utils.tests.cpp +++ b/kernel/kernel/devices/block_device_utils.tests.cpp @@ -4,9 +4,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/devices/storage/controller.cpp b/kernel/kernel/devices/storage/controller.cpp index 171b918e..5ea9eb71 100644 --- a/kernel/kernel/devices/storage/controller.cpp +++ b/kernel/kernel/devices/storage/controller.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/devices/storage/controller.hpp b/kernel/kernel/devices/storage/controller.hpp index bea18f38..13494a52 100644 --- a/kernel/kernel/devices/storage/controller.hpp +++ b/kernel/kernel/devices/storage/controller.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/devices/storage/management.cpp b/kernel/kernel/devices/storage/management.cpp index 06efc27d..90c3696f 100644 --- a/kernel/kernel/devices/storage/management.cpp +++ b/kernel/kernel/devices/storage/management.cpp @@ -7,8 +7,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/devices/storage/management.hpp b/kernel/kernel/devices/storage/management.hpp index 9a840875..a9704462 100644 --- a/kernel/kernel/devices/storage/management.hpp +++ b/kernel/kernel/devices/storage/management.hpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/devices/storage/ram_disk/controller.cpp b/kernel/kernel/devices/storage/ram_disk/controller.cpp index 30441fa5..c1075733 100644 --- a/kernel/kernel/devices/storage/ram_disk/controller.cpp +++ b/kernel/kernel/devices/storage/ram_disk/controller.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/devices/storage/ram_disk/device.cpp b/kernel/kernel/devices/storage/ram_disk/device.cpp index 1557204b..1b53b0d3 100644 --- a/kernel/kernel/devices/storage/ram_disk/device.cpp +++ b/kernel/kernel/devices/storage/ram_disk/device.cpp @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/dentry.cpp b/kernel/kernel/filesystem/dentry.cpp index 01de276c..9321453a 100644 --- a/kernel/kernel/filesystem/dentry.cpp +++ b/kernel/kernel/filesystem/dentry.cpp @@ -4,8 +4,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/dentry.hpp b/kernel/kernel/filesystem/dentry.hpp index 096a6bbb..ea689485 100644 --- a/kernel/kernel/filesystem/dentry.hpp +++ b/kernel/kernel/filesystem/dentry.hpp @@ -3,9 +3,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/dentry.tests.cpp b/kernel/kernel/filesystem/dentry.tests.cpp index b7690f57..55cdf21e 100644 --- a/kernel/kernel/filesystem/dentry.tests.cpp +++ b/kernel/kernel/filesystem/dentry.tests.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index ce887ff3..3b1da25c 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -9,7 +9,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/devfs/filesystem.hpp b/kernel/kernel/filesystem/devfs/filesystem.hpp index dbaa387b..083d248c 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/kernel/filesystem/devfs/filesystem.hpp @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/devfs/inode.tests.cpp b/kernel/kernel/filesystem/devfs/inode.tests.cpp index ae26e742..ecf9a79b 100644 --- a/kernel/kernel/filesystem/devfs/inode.tests.cpp +++ b/kernel/kernel/filesystem/devfs/inode.tests.cpp @@ -1,8 +1,8 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/device_inode.cpp b/kernel/kernel/filesystem/device_inode.cpp index 81a784cd..091b5499 100644 --- a/kernel/kernel/filesystem/device_inode.cpp +++ b/kernel/kernel/filesystem/device_inode.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/filesystem/device_inode.hpp b/kernel/kernel/filesystem/device_inode.hpp index f4aa2d1e..b461f225 100644 --- a/kernel/kernel/filesystem/device_inode.hpp +++ b/kernel/kernel/filesystem/device_inode.hpp @@ -5,7 +5,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/filesystem/device_inode.tests.cpp b/kernel/kernel/filesystem/device_inode.tests.cpp index 025a22a2..d78d4a07 100644 --- a/kernel/kernel/filesystem/device_inode.tests.cpp +++ b/kernel/kernel/filesystem/device_inode.tests.cpp @@ -4,9 +4,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 3180a19a..82c6441d 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -8,9 +8,9 @@ #include #include -#include +#include #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index 2284d7bf..fc2ba7df 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -7,9 +7,9 @@ #include #include -#include +#include #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp index 83410702..ed3ac1ac 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp @@ -8,8 +8,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 35a32ee4..11f55cb6 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/ext2/inode.hpp b/kernel/kernel/filesystem/ext2/inode.hpp index f2496f0f..8b63f787 100644 --- a/kernel/kernel/filesystem/ext2/inode.hpp +++ b/kernel/kernel/filesystem/ext2/inode.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp index 4aecc04a..aa21f6ed 100644 --- a/kernel/kernel/filesystem/ext2/inode.tests.cpp +++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp @@ -10,8 +10,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/filesystem.cpp b/kernel/kernel/filesystem/filesystem.cpp index 24d0e229..f0b7fd28 100644 --- a/kernel/kernel/filesystem/filesystem.cpp +++ b/kernel/kernel/filesystem/filesystem.cpp @@ -5,7 +5,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/filesystem/filesystem.hpp b/kernel/kernel/filesystem/filesystem.hpp index bec1b160..b818d992 100644 --- a/kernel/kernel/filesystem/filesystem.hpp +++ b/kernel/kernel/filesystem/filesystem.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/mount.cpp b/kernel/kernel/filesystem/mount.cpp index ead7479d..009ab230 100644 --- a/kernel/kernel/filesystem/mount.cpp +++ b/kernel/kernel/filesystem/mount.cpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/mount.hpp b/kernel/kernel/filesystem/mount.hpp index ced4f814..589094e0 100644 --- a/kernel/kernel/filesystem/mount.hpp +++ b/kernel/kernel/filesystem/mount.hpp @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/mount.tests.cpp b/kernel/kernel/filesystem/mount.tests.cpp index c9ff82e9..800a0cfc 100644 --- a/kernel/kernel/filesystem/mount.tests.cpp +++ b/kernel/kernel/filesystem/mount.tests.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/mount_table.cpp b/kernel/kernel/filesystem/mount_table.cpp index e4baac7a..d3f252b4 100644 --- a/kernel/kernel/filesystem/mount_table.cpp +++ b/kernel/kernel/filesystem/mount_table.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/mount_table.hpp b/kernel/kernel/filesystem/mount_table.hpp index 4f2d1b7f..46e2a439 100644 --- a/kernel/kernel/filesystem/mount_table.hpp +++ b/kernel/kernel/filesystem/mount_table.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/filesystem/mount_table.tests.cpp b/kernel/kernel/filesystem/mount_table.tests.cpp index 19b47b23..39ad6b01 100644 --- a/kernel/kernel/filesystem/mount_table.tests.cpp +++ b/kernel/kernel/filesystem/mount_table.tests.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/open_file_descriptor.cpp b/kernel/kernel/filesystem/open_file_descriptor.cpp index a5567bf4..bec484d7 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.cpp +++ b/kernel/kernel/filesystem/open_file_descriptor.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/open_file_descriptor.hpp b/kernel/kernel/filesystem/open_file_descriptor.hpp index fd10e646..beb32ec6 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.hpp +++ b/kernel/kernel/filesystem/open_file_descriptor.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp index 8c24cf0e..c666ef43 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp @@ -6,9 +6,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/open_file_table.cpp b/kernel/kernel/filesystem/open_file_table.cpp index 2afe3aa4..38bd7a43 100644 --- a/kernel/kernel/filesystem/open_file_table.cpp +++ b/kernel/kernel/filesystem/open_file_table.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/open_file_table.hpp b/kernel/kernel/filesystem/open_file_table.hpp index 7e754ac1..a5e6b496 100644 --- a/kernel/kernel/filesystem/open_file_table.hpp +++ b/kernel/kernel/filesystem/open_file_table.hpp @@ -3,9 +3,9 @@ #include -#include +#include #include -#include +#include #include diff --git a/kernel/kernel/filesystem/open_file_table.tests.cpp b/kernel/kernel/filesystem/open_file_table.tests.cpp index 3e91111d..ffb4a650 100644 --- a/kernel/kernel/filesystem/open_file_table.tests.cpp +++ b/kernel/kernel/filesystem/open_file_table.tests.cpp @@ -4,9 +4,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/path.hpp b/kernel/kernel/filesystem/path.hpp index 4845bf1a..438c22f3 100644 --- a/kernel/kernel/filesystem/path.hpp +++ b/kernel/kernel/filesystem/path.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp index 7fe5c1ea..52c2cec2 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/filesystem/rootfs/filesystem.hpp b/kernel/kernel/filesystem/rootfs/filesystem.hpp index 3c2dcb1c..5d59486d 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.hpp @@ -4,9 +4,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp index ae320e95..8f16077a 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp @@ -2,9 +2,9 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/rootfs/inode.hpp b/kernel/kernel/filesystem/rootfs/inode.hpp index 0f21eaa0..ee5b9012 100644 --- a/kernel/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/kernel/filesystem/rootfs/inode.hpp @@ -3,9 +3,9 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/rootfs/inode.tests.cpp b/kernel/kernel/filesystem/rootfs/inode.tests.cpp index f4b634fa..0b48ca71 100644 --- a/kernel/kernel/filesystem/rootfs/inode.tests.cpp +++ b/kernel/kernel/filesystem/rootfs/inode.tests.cpp @@ -1,8 +1,8 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/kernel/kernel/filesystem/type.hpp b/kernel/kernel/filesystem/type.hpp index 0948e549..ebf43176 100644 --- a/kernel/kernel/filesystem/type.hpp +++ b/kernel/kernel/filesystem/type.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/filesystem/type_registry.cpp b/kernel/kernel/filesystem/type_registry.cpp index 74b12090..6850090a 100644 --- a/kernel/kernel/filesystem/type_registry.cpp +++ b/kernel/kernel/filesystem/type_registry.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/type_registry.hpp b/kernel/kernel/filesystem/type_registry.hpp index 3be72950..bda2326b 100644 --- a/kernel/kernel/filesystem/type_registry.hpp +++ b/kernel/kernel/filesystem/type_registry.hpp @@ -3,9 +3,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/type_registry.tests.cpp b/kernel/kernel/filesystem/type_registry.tests.cpp index 8382579f..dbd3cdfd 100644 --- a/kernel/kernel/filesystem/type_registry.tests.cpp +++ b/kernel/kernel/filesystem/type_registry.tests.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index e5dff8c4..f6c64f0f 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -11,8 +11,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index ddc9a9bc..bb1167fb 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index f1d0df0d..849300ac 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index 8dc13490..88f76f88 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -13,10 +13,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/kernel/kernel/memory.cpp b/kernel/kernel/memory.cpp index 6a85c0e7..663137eb 100644 --- a/kernel/kernel/memory.cpp +++ b/kernel/kernel/memory.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/memory/block_list_allocator.cpp b/kernel/kernel/memory/block_list_allocator.cpp index 6e68ada7..a682051f 100644 --- a/kernel/kernel/memory/block_list_allocator.cpp +++ b/kernel/kernel/memory/block_list_allocator.cpp @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/memory/block_list_allocator.hpp b/kernel/kernel/memory/block_list_allocator.hpp index 51b226ee..ac6257c0 100644 --- a/kernel/kernel/memory/block_list_allocator.hpp +++ b/kernel/kernel/memory/block_list_allocator.hpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/memory/block_list_allocator.tests.cpp b/kernel/kernel/memory/block_list_allocator.tests.cpp index c5f84c52..6efcd31a 100644 --- a/kernel/kernel/memory/block_list_allocator.tests.cpp +++ b/kernel/kernel/memory/block_list_allocator.tests.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/memory/heap_allocator.hpp b/kernel/kernel/memory/heap_allocator.hpp index fd39bef8..59fa98b0 100644 --- a/kernel/kernel/memory/heap_allocator.hpp +++ b/kernel/kernel/memory/heap_allocator.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP -#include +#include namespace kernel::memory { diff --git a/kernel/kernel/memory/mmio_allocator.cpp b/kernel/kernel/memory/mmio_allocator.cpp index ba23dbde..17f436f8 100644 --- a/kernel/kernel/memory/mmio_allocator.cpp +++ b/kernel/kernel/memory/mmio_allocator.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/kernel/kernel/memory/mmio_allocator.hpp b/kernel/kernel/memory/mmio_allocator.hpp index c7a8ed0c..862188fa 100644 --- a/kernel/kernel/memory/mmio_allocator.hpp +++ b/kernel/kernel/memory/mmio_allocator.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/memory/operators.cpp b/kernel/kernel/memory/operators.cpp index 5673d680..5de5101d 100644 --- a/kernel/kernel/memory/operators.cpp +++ b/kernel/kernel/memory/operators.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/test_support/devices/block_device.cpp b/kernel/kernel/test_support/devices/block_device.cpp index 9a9e544d..2dda5dec 100644 --- a/kernel/kernel/test_support/devices/block_device.cpp +++ b/kernel/kernel/test_support/devices/block_device.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/test_support/devices/block_device.hpp b/kernel/kernel/test_support/devices/block_device.hpp index 89a2bf15..d83dddd1 100644 --- a/kernel/kernel/test_support/devices/block_device.hpp +++ b/kernel/kernel/test_support/devices/block_device.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/kernel/kernel/test_support/devices/character_device.cpp b/kernel/kernel/test_support/devices/character_device.cpp index 3806654f..fef5baf5 100644 --- a/kernel/kernel/test_support/devices/character_device.cpp +++ b/kernel/kernel/test_support/devices/character_device.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/test_support/devices/character_device.hpp b/kernel/kernel/test_support/devices/character_device.hpp index aba183a1..5790a561 100644 --- a/kernel/kernel/test_support/devices/character_device.hpp +++ b/kernel/kernel/test_support/devices/character_device.hpp @@ -3,8 +3,8 @@ #include -#include -#include +#include +#include #include diff --git a/kernel/kernel/test_support/filesystem/filesystem.cpp b/kernel/kernel/test_support/filesystem/filesystem.cpp index ec706077..a4ea30bb 100644 --- a/kernel/kernel/test_support/filesystem/filesystem.cpp +++ b/kernel/kernel/test_support/filesystem/filesystem.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/test_support/filesystem/filesystem.hpp b/kernel/kernel/test_support/filesystem/filesystem.hpp index 5f260220..5809bb5e 100644 --- a/kernel/kernel/test_support/filesystem/filesystem.hpp +++ b/kernel/kernel/test_support/filesystem/filesystem.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/kernel/kernel/test_support/kapi/memory.cpp b/kernel/kernel/test_support/kapi/memory.cpp index 7fc95cb0..33b725bd 100644 --- a/kernel/kernel/test_support/kapi/memory.cpp +++ b/kernel/kernel/test_support/kapi/memory.cpp @@ -5,7 +5,7 @@ #include -#include +#include #include diff --git a/kernel/kernel/test_support/page_mapper.cpp b/kernel/kernel/test_support/page_mapper.cpp index 3d50ff1a..b1a6f92b 100644 --- a/kernel/kernel/test_support/page_mapper.cpp +++ b/kernel/kernel/test_support/page_mapper.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/test_support/page_mapper.hpp b/kernel/kernel/test_support/page_mapper.hpp index be4403b0..a9985393 100644 --- a/kernel/kernel/test_support/page_mapper.hpp +++ b/kernel/kernel/test_support/page_mapper.hpp @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/test_support/simulated_memory.cpp b/kernel/kernel/test_support/simulated_memory.cpp index 074e6b10..07adab17 100644 --- a/kernel/kernel/test_support/simulated_memory.cpp +++ b/kernel/kernel/test_support/simulated_memory.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/kernel/kernel/test_support/simulated_memory.hpp b/kernel/kernel/test_support/simulated_memory.hpp index a201c3d7..e7ca9f6c 100644 --- a/kernel/kernel/test_support/simulated_memory.hpp +++ b/kernel/kernel/test_support/simulated_memory.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index d0611b21..cff1ea34 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/kernel/kstd/print.tests.cpp b/kernel/kstd/print.tests.cpp index 4963f46e..a8c4ae25 100644 --- a/kernel/kstd/print.tests.cpp +++ b/kernel/kstd/print.tests.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/libs/acpi/acpi/common/table_header.cpp b/libs/acpi/acpi/common/table_header.cpp index 6a7a4dc3..ebceca1d 100644 --- a/libs/acpi/acpi/common/table_header.cpp +++ b/libs/acpi/acpi/common/table_header.cpp @@ -1,7 +1,7 @@ #include -#include -#include +#include +#include #include #include diff --git a/libs/acpi/acpi/common/table_header.hpp b/libs/acpi/acpi/common/table_header.hpp index 471fed87..8e1d7c59 100644 --- a/libs/acpi/acpi/common/table_header.hpp +++ b/libs/acpi/acpi/common/table_header.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include +#include #include #include diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp index ddc879ed..bbd42bd6 100644 --- a/libs/acpi/acpi/common/table_header.test.cpp +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include diff --git a/libs/acpi/acpi/data/madt.cpp b/libs/acpi/acpi/data/madt.cpp index 1a8b6d3a..fe6bae16 100644 --- a/libs/acpi/acpi/data/madt.cpp +++ b/libs/acpi/acpi/data/madt.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include diff --git a/libs/acpi/acpi/data/madt.hpp b/libs/acpi/acpi/data/madt.hpp index b76daa4f..4d992613 100644 --- a/libs/acpi/acpi/data/madt.hpp +++ b/libs/acpi/acpi/data/madt.hpp @@ -7,9 +7,9 @@ #include #include -#include +#include #include -#include +#include #include #include diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp index 1b95a74b..9ec0180a 100644 --- a/libs/acpi/acpi/data/madt.test.cpp +++ b/libs/acpi/acpi/data/madt.test.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include diff --git a/libs/acpi/acpi/data/rsdt.cpp b/libs/acpi/acpi/data/rsdt.cpp index 80e209db..a8626742 100644 --- a/libs/acpi/acpi/data/rsdt.cpp +++ b/libs/acpi/acpi/data/rsdt.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp index 47992ce6..826d0b4f 100644 --- a/libs/acpi/acpi/data/rsdt.test.cpp +++ b/libs/acpi/acpi/data/rsdt.test.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/libs/acpi/acpi/data/xsdt.cpp b/libs/acpi/acpi/data/xsdt.cpp index b77aeab7..c4b109b8 100644 --- a/libs/acpi/acpi/data/xsdt.cpp +++ b/libs/acpi/acpi/data/xsdt.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/libs/acpi/acpi/data/xsdt.test.cpp b/libs/acpi/acpi/data/xsdt.test.cpp index 77a53408..19d9bcdb 100644 --- a/libs/acpi/acpi/data/xsdt.test.cpp +++ b/libs/acpi/acpi/data/xsdt.test.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/libs/acpi/acpi/pointers.cpp b/libs/acpi/acpi/pointers.cpp index 2ac8d319..0393292c 100644 --- a/libs/acpi/acpi/pointers.cpp +++ b/libs/acpi/acpi/pointers.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/libs/acpi/acpi/pointers.hpp b/libs/acpi/acpi/pointers.hpp index 5771e7df..310447f5 100644 --- a/libs/acpi/acpi/pointers.hpp +++ b/libs/acpi/acpi/pointers.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include +#include #include #include diff --git a/libs/kstd/kstd/allocator b/libs/kstd/kstd/allocator deleted file mode 100644 index 0de0e10d..00000000 --- a/libs/kstd/kstd/allocator +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef KSTD_ALLOCATOR_HPP -#define KSTD_ALLOCATOR_HPP - -#include -#include -#include - -#if __has_builtin(__builtin_operator_new) >= 201'802L -#define KSTD_OPERATOR_NEW __builtin_operator_new -#define KSTD_OPERATOR_DELETE __builtin_operator_delete -#else -#define KSTD_OPERATOR_NEW ::operator new -#define KSTD_OPERATOR_DELETE ::operator delete -#endif - -namespace kstd -{ - - template - struct allocator - { - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using propagate_on_container_move_assignment = std::true_type; - using is_always_equal = std::true_type; - - constexpr allocator() noexcept = default; - - template - constexpr allocator(allocator const &) noexcept - {} - - constexpr auto allocate(std::size_t n) -> T * - { - if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - { - return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T), std::align_val_t{alignof(T)})); - } - return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T))); - } - - constexpr void deallocate(T * p, std::size_t n) noexcept - { - if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - { - KSTD_OPERATOR_DELETE(p, n * sizeof(T), std::align_val_t{alignof(T)}); - } - KSTD_OPERATOR_DELETE(p, n * sizeof(T)); - } - }; - - template - constexpr auto operator==(allocator const &, allocator const &) noexcept -> bool - { - return true; - } - -} // namespace kstd - -#undef KSTD_OPERATOR_NEW -#undef KSTD_OPERATOR_DELETE - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/allocator.hpp b/libs/kstd/kstd/allocator.hpp new file mode 100644 index 00000000..0de0e10d --- /dev/null +++ b/libs/kstd/kstd/allocator.hpp @@ -0,0 +1,64 @@ +#ifndef KSTD_ALLOCATOR_HPP +#define KSTD_ALLOCATOR_HPP + +#include +#include +#include + +#if __has_builtin(__builtin_operator_new) >= 201'802L +#define KSTD_OPERATOR_NEW __builtin_operator_new +#define KSTD_OPERATOR_DELETE __builtin_operator_delete +#else +#define KSTD_OPERATOR_NEW ::operator new +#define KSTD_OPERATOR_DELETE ::operator delete +#endif + +namespace kstd +{ + + template + struct allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + constexpr allocator() noexcept = default; + + template + constexpr allocator(allocator const &) noexcept + {} + + constexpr auto allocate(std::size_t n) -> T * + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T), std::align_val_t{alignof(T)})); + } + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T))); + } + + constexpr void deallocate(T * p, std::size_t n) noexcept + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + KSTD_OPERATOR_DELETE(p, n * sizeof(T), std::align_val_t{alignof(T)}); + } + KSTD_OPERATOR_DELETE(p, n * sizeof(T)); + } + }; + + template + constexpr auto operator==(allocator const &, allocator const &) noexcept -> bool + { + return true; + } + +} // namespace kstd + +#undef KSTD_OPERATOR_NEW +#undef KSTD_OPERATOR_DELETE + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/asm_ptr b/libs/kstd/kstd/asm_ptr deleted file mode 100644 index c06a8b52..00000000 --- a/libs/kstd/kstd/asm_ptr +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef KSTD_ASM_POINTER_HPP -#define KSTD_ASM_POINTER_HPP - -#include -#include - -namespace kstd -{ - - /** - * @brief A pointer that is defined in some assembly source file. - * - * @tparam Type The type of the pointer - */ - template - struct asm_ptr - { - using value_type = Type; - using pointer = value_type *; - using const_pointer = value_type const *; - using reference = value_type &; - using const_reference = value_type const &; - - asm_ptr() = delete; - asm_ptr(asm_ptr const &) = delete; - asm_ptr(asm_ptr &&) = delete; - ~asm_ptr() = delete; - - constexpr auto operator=(asm_ptr const &) = delete; - constexpr auto operator=(asm_ptr &&) = delete; - - auto get() const noexcept -> pointer - { - return m_ptr; - } - - constexpr auto operator+(std::ptrdiff_t offset) const noexcept -> pointer - { - return std::bit_cast(m_ptr) + offset; - } - - constexpr auto operator*() noexcept -> reference - { - return *(std::bit_cast(m_ptr)); - } - - constexpr auto operator*() const noexcept -> const_reference - { - return *(std::bit_cast(m_ptr)); - } - - constexpr auto operator[](std::ptrdiff_t offset) noexcept -> reference - { - return *(*this + offset); - } - - constexpr auto operator[](std::ptrdiff_t offset) const noexcept -> const_reference - { - return *(*this + offset); - } - - constexpr auto operator->() noexcept -> pointer - { - return m_ptr; - } - - constexpr auto operator->() const noexcept -> const_pointer - { - return m_ptr; - } - - private: - pointer m_ptr; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/asm_ptr.hpp b/libs/kstd/kstd/asm_ptr.hpp new file mode 100644 index 00000000..c06a8b52 --- /dev/null +++ b/libs/kstd/kstd/asm_ptr.hpp @@ -0,0 +1,78 @@ +#ifndef KSTD_ASM_POINTER_HPP +#define KSTD_ASM_POINTER_HPP + +#include +#include + +namespace kstd +{ + + /** + * @brief A pointer that is defined in some assembly source file. + * + * @tparam Type The type of the pointer + */ + template + struct asm_ptr + { + using value_type = Type; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + + asm_ptr() = delete; + asm_ptr(asm_ptr const &) = delete; + asm_ptr(asm_ptr &&) = delete; + ~asm_ptr() = delete; + + constexpr auto operator=(asm_ptr const &) = delete; + constexpr auto operator=(asm_ptr &&) = delete; + + auto get() const noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator+(std::ptrdiff_t offset) const noexcept -> pointer + { + return std::bit_cast(m_ptr) + offset; + } + + constexpr auto operator*() noexcept -> reference + { + return *(std::bit_cast(m_ptr)); + } + + constexpr auto operator*() const noexcept -> const_reference + { + return *(std::bit_cast(m_ptr)); + } + + constexpr auto operator[](std::ptrdiff_t offset) noexcept -> reference + { + return *(*this + offset); + } + + constexpr auto operator[](std::ptrdiff_t offset) const noexcept -> const_reference + { + return *(*this + offset); + } + + constexpr auto operator->() noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator->() const noexcept -> const_pointer + { + return m_ptr; + } + + private: + pointer m_ptr; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index 1675434c..a718bccc 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -1,10 +1,10 @@ #ifndef KSTD_BITS_BASIC_STRING_HPP #define KSTD_BITS_BASIC_STRING_HPP -#include +#include #include #include -#include +#include #include #include diff --git a/libs/kstd/kstd/bits/format/arg.hpp b/libs/kstd/kstd/bits/format/arg.hpp index e65b26fa..8271ca3d 100644 --- a/libs/kstd/kstd/bits/format/arg.hpp +++ b/libs/kstd/kstd/bits/format/arg.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_ARG_HPP #define KSTD_BITS_FORMAT_ARG_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/format/args.hpp b/libs/kstd/kstd/bits/format/args.hpp index e8e31145..01735011 100644 --- a/libs/kstd/kstd/bits/format/args.hpp +++ b/libs/kstd/kstd/bits/format/args.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_ARGS_HPP #define KSTD_BITS_FORMAT_ARGS_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/format/context.hpp b/libs/kstd/kstd/bits/format/context.hpp index c166ba9c..d5f2f4d9 100644 --- a/libs/kstd/kstd/bits/format/context.hpp +++ b/libs/kstd/kstd/bits/format/context.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_CONTEXT_HPP #define KSTD_BITS_FORMAT_CONTEXT_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/format/formatter.hpp b/libs/kstd/kstd/bits/format/formatter.hpp index eb288294..c26907b2 100644 --- a/libs/kstd/kstd/bits/format/formatter.hpp +++ b/libs/kstd/kstd/bits/format/formatter.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMATTER_HPP #define KSTD_BITS_FORMATTER_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/format/output_buffer.hpp b/libs/kstd/kstd/bits/format/output_buffer.hpp index fd7a2b4c..7e6f0575 100644 --- a/libs/kstd/kstd/bits/format/output_buffer.hpp +++ b/libs/kstd/kstd/bits/format/output_buffer.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP #define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include diff --git a/libs/kstd/kstd/bits/format/parse_context.hpp b/libs/kstd/kstd/bits/format/parse_context.hpp index cab8d726..81ed52cf 100644 --- a/libs/kstd/kstd/bits/format/parse_context.hpp +++ b/libs/kstd/kstd/bits/format/parse_context.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP #define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include diff --git a/libs/kstd/kstd/bits/format/string.hpp b/libs/kstd/kstd/bits/format/string.hpp index a2b298d4..ab034e7c 100644 --- a/libs/kstd/kstd/bits/format/string.hpp +++ b/libs/kstd/kstd/bits/format/string.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_STRING_HPP #define KSTD_BITS_FORMAT_STRING_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/format/vformat.cpp b/libs/kstd/kstd/bits/format/vformat.cpp index 1bf293a4..495f0a10 100644 --- a/libs/kstd/kstd/bits/format/vformat.cpp +++ b/libs/kstd/kstd/bits/format/vformat.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include diff --git a/libs/kstd/kstd/bits/format/vformat.hpp b/libs/kstd/kstd/bits/format/vformat.hpp index 2db56d17..829e0ea4 100644 --- a/libs/kstd/kstd/bits/format/vformat.hpp +++ b/libs/kstd/kstd/bits/format/vformat.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_VFORMAT_HPP #define KSTD_BITS_FORMAT_VFORMAT_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include #include diff --git a/libs/kstd/kstd/bits/observer_ptr.hpp b/libs/kstd/kstd/bits/observer_ptr.hpp index 2593d7a4..e63e5d77 100644 --- a/libs/kstd/kstd/bits/observer_ptr.hpp +++ b/libs/kstd/kstd/bits/observer_ptr.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_OBSERVER_PTR_HPP #define KSTD_OBSERVER_PTR_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include #include diff --git a/libs/kstd/kstd/bits/observer_ptr.test.cpp b/libs/kstd/kstd/bits/observer_ptr.test.cpp index f26fc09a..eca14ccd 100644 --- a/libs/kstd/kstd/bits/observer_ptr.test.cpp +++ b/libs/kstd/kstd/bits/observer_ptr.test.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/libs/kstd/kstd/bits/print_sink.hpp b/libs/kstd/kstd/bits/print_sink.hpp index af765e06..a1e9b0d4 100644 --- a/libs/kstd/kstd/bits/print_sink.hpp +++ b/libs/kstd/kstd/bits/print_sink.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_PRINT_SINK_HPP #define KSTD_BITS_PRINT_SINK_HPP -// IWYU pragma: private, include +// IWYU pragma: private, include namespace kstd { diff --git a/libs/kstd/kstd/bits/shared_ptr.hpp b/libs/kstd/kstd/bits/shared_ptr.hpp index 57a89c0d..ce4bfb7f 100644 --- a/libs/kstd/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/kstd/bits/shared_ptr.hpp @@ -6,7 +6,7 @@ #include #include -// IWYU pragma: private, include +// IWYU pragma: private, include namespace kstd { diff --git a/libs/kstd/kstd/bits/unique_ptr.hpp b/libs/kstd/kstd/bits/unique_ptr.hpp index 3d803b4a..1df18cf2 100644 --- a/libs/kstd/kstd/bits/unique_ptr.hpp +++ b/libs/kstd/kstd/bits/unique_ptr.hpp @@ -3,7 +3,7 @@ #include -// IWYU pragma: private, include +// IWYU pragma: private, include namespace kstd { diff --git a/libs/kstd/kstd/cstring b/libs/kstd/kstd/cstring deleted file mode 100644 index bd8b28d8..00000000 --- a/libs/kstd/kstd/cstring +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef KSTD_CSTRING -#define KSTD_CSTRING - -#include - -namespace kstd::libc -{ - - extern "C" - { - auto memcpy(void * dest, void const * src, std::size_t size) noexcept -> void *; - auto memset(void * dest, int value, std::size_t size) noexcept -> void *; - auto memmove(void * dest, void const * src, std::size_t size) noexcept -> void *; - auto memcmp(void const * lhs, void const * rhs, std::size_t size) noexcept -> int; - - auto strlen(char const * string) noexcept -> std::size_t; - } - -} // namespace kstd::libc - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/cstring.hpp b/libs/kstd/kstd/cstring.hpp new file mode 100644 index 00000000..bd8b28d8 --- /dev/null +++ b/libs/kstd/kstd/cstring.hpp @@ -0,0 +1,21 @@ +#ifndef KSTD_CSTRING +#define KSTD_CSTRING + +#include + +namespace kstd::libc +{ + + extern "C" + { + auto memcpy(void * dest, void const * src, std::size_t size) noexcept -> void *; + auto memset(void * dest, int value, std::size_t size) noexcept -> void *; + auto memmove(void * dest, void const * src, std::size_t size) noexcept -> void *; + auto memcmp(void const * lhs, void const * rhs, std::size_t size) noexcept -> int; + + auto strlen(char const * string) noexcept -> std::size_t; + } + +} // namespace kstd::libc + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ext/bitfield_enum b/libs/kstd/kstd/ext/bitfield_enum deleted file mode 100644 index 80fe9d29..00000000 --- a/libs/kstd/kstd/ext/bitfield_enum +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef KSTD_EXT_BITFIELD_ENUM_HPP -#define KSTD_EXT_BITFIELD_ENUM_HPP - -#include -#include -#include - -namespace kstd::ext -{ - - template - requires std::is_enum_v - struct is_bitfield_enum : std::false_type - { - }; - - //! @concept Specifies that an enum is to be used to define bits in a bitfield. - template - concept bitfield_enum = is_bitfield_enum::value; - -}; // namespace kstd::ext - -template -constexpr auto operator|(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); -} - -template -constexpr auto operator|=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs | rhs; -} - -template -constexpr auto operator&(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); -} - -template -constexpr auto operator&=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs & rhs; -} - -template -constexpr auto operator^(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) ^ std::to_underlying(rhs)); -} - -template -constexpr auto operator^=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs ^ rhs; -} - -template -constexpr auto operator~(EnumType lhs) -> EnumType -{ - return std::bit_cast(~std::to_underlying(lhs)); -} - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ext/bitfield_enum.hpp b/libs/kstd/kstd/ext/bitfield_enum.hpp new file mode 100644 index 00000000..80fe9d29 --- /dev/null +++ b/libs/kstd/kstd/ext/bitfield_enum.hpp @@ -0,0 +1,65 @@ +#ifndef KSTD_EXT_BITFIELD_ENUM_HPP +#define KSTD_EXT_BITFIELD_ENUM_HPP + +#include +#include +#include + +namespace kstd::ext +{ + + template + requires std::is_enum_v + struct is_bitfield_enum : std::false_type + { + }; + + //! @concept Specifies that an enum is to be used to define bits in a bitfield. + template + concept bitfield_enum = is_bitfield_enum::value; + +}; // namespace kstd::ext + +template +constexpr auto operator|(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); +} + +template +constexpr auto operator|=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs | rhs; +} + +template +constexpr auto operator&(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); +} + +template +constexpr auto operator&=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs & rhs; +} + +template +constexpr auto operator^(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) ^ std::to_underlying(rhs)); +} + +template +constexpr auto operator^=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs ^ rhs; +} + +template +constexpr auto operator~(EnumType lhs) -> EnumType +{ + return std::bit_cast(~std::to_underlying(lhs)); +} + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map b/libs/kstd/kstd/flat_map deleted file mode 100644 index e51eb918..00000000 --- a/libs/kstd/kstd/flat_map +++ /dev/null @@ -1,406 +0,0 @@ -#ifndef KSTD_FLAT_MAP_HPP -#define KSTD_FLAT_MAP_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - - template, - typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> - struct flat_map - { - //! The type of container used to store the map keys. - using key_container_type = KeyContainerType; - - //! The type of container used to store the map values. - using mapped_container_type = MappedContainerType; - - //! The type of the map keys. - using key_type = KeyType; - - //! The type of the mappe values. - using mapped_type = MappedType; - - //! The type of a single key-value value in the map. - using value_type = std::pair; - - //! The comparator used to sort the keys. - using key_compare = KeyCompare; - - using reference = std::pair; - using const_reference = std::pair; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using iterator = bits::flat_map_iterator; - using const_iterator = - bits::flat_map_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using containers = struct - { - key_container_type keys; - mapped_container_type values; - }; - - //! Compare two object of type value_type. - struct value_compare - { - constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool - { - return lhs.first < rhs.first; - } - }; - - //! Construct an empty flat map. - constexpr flat_map() - : flat_map{key_compare{}} - {} - - //! Construct an empty flat map using the given custom comparator. - //! - //! @param comparator The comparator to use for comparing keys. - constexpr explicit flat_map(key_compare const & comparator) - : m_containers{} - , m_comparator{comparator} - {} - - //! Replace the contents of this flat map with the one of a different one. - //! - //! @param other the flat map to copy from. - //! @return A reference to this flat map. - auto operator=(flat_map const & other) -> flat_map & - { - if (this != &other) - { - std::tie(m_containers, m_comparator) = std::tie(other.m_containers, other.m_comparator); - } - return *this; - } - - //! Get a reference to the mapped value associated with the given key. - //! - //! @warning This function will panic if the key is not found. - //! @param key The key to look up. - //! @return A reference to the mapped value. - [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return *(m_containers.values.begin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! - //! @warning This function will panic if the key is not found. - //! @param key The key to look up. - //! @return A const reference to the mapped value. - [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return *(m_containers.values.cbegin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @warning This function will panic if the key is not found. - //! @param x The key to look up. - //! @return A reference to the mapped value. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & - { - auto found = find(x); - if (found != end()) - { - auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); - return *(m_containers.values.begin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @warning This function will panic if the key is not found. - //! @param x The key to look up. - //! @return A const reference to the mapped value. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto at(K const & x) const -> mapped_type const & - { - auto found = find(x); - if (found != end()) - { - auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); - return *(m_containers.values.cbegin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get an iterator to the first element. - [[nodiscard]] auto begin() noexcept -> iterator - { - return iterator{m_containers.keys.begin(), m_containers.values.begin()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto begin() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto cbegin() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto end() noexcept -> iterator - { - return iterator{m_containers.keys.end(), m_containers.values.end()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto end() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto cend() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto rbegin() noexcept -> reverse_iterator - { - return reverse_iterator{end()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cend()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cend()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto rend() noexcept -> reverse_iterator - { - return reverse_iterator{begin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cbegin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cbegin()}; - } - - //! Check whether this flat map is empty or not - [[nodiscard]] auto empty() const noexcept -> bool - { - return m_containers.keys.empty(); - } - - //! Get the number of elements in this flat map. - [[nodiscard]] auto size() const noexcept -> size_type - { - return m_containers.keys.size(); - } - - //! Get the maximum number of elements possible in this flat map - [[nodiscard]] auto max_size() const noexcept -> size_type - { - return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); - } - - //! Try to insert a new key-value pair into the map. - //! - //! @param args Arguments to use for constructing the key-value pair. - //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. - template - auto emplace(Args &&... args) -> std::pair - requires std::constructible_from - { - auto value = value_type{std::forward(args)...}; - auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); - - if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return { - iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, - false - }; - } - - auto offset = std::distance(m_containers.keys.begin(), found); - auto key_iterator = m_containers.keys.begin() + offset; - auto mapped_iterator = m_containers.values.begin() + offset; - - auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); - auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); - - return { - iterator{inserted_key, inserted_mapped}, - true - }; - } - - //! Try to insert a element for the given key into this map. - //! - //! This function does nothing if the key is already present. - //! - //! @param key The key to insert a value for. - //! @param args The arguments to use to construct the mapped value. - template - auto try_emplace(key_type const & key, Args &&... args) -> std::pair - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found)) - { - return {found, false}; - } - - auto offset = std::distance(m_containers.keys.cbegin(), found); - auto intersertion_point = m_containers.value.begin() + offset; - - auto inserted_key = m_containers.keys.emplace(key); - auto inserted_mapped = m_containers.values.emplace(std::forward(args)...); - - return { - iterator{inserted_key, inserted_mapped}, - true - }; - } - - //! Find an element with an equivalent key. - //! - //! @param key The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - [[nodiscard]] auto find(key_type const & key) noexcept -> iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; - } - return end(); - } - - //! Find an element with an equivalent key. - //! - //! @param key The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; - } - return cend(); - } - - //! Find an element with an equivalent key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @param x The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto find(K const & x) noexcept -> iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; - } - return end(); - } - - //! Find an element with an equivalent key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @param x The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; - } - return cend(); - } - - //! Check if the map contains the given key. - //! - //! @param key The key to check. - //! @return true iff. the key is found, false otherwise. - [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool - { - return find(key) != cend(); - } - - //! Get a reference to the keys container. - //! - //! @return a reference to the keys container. - [[nodiscard]] constexpr auto keys() const noexcept -> key_container_type const & - { - return m_containers.keys; - } - - //! Get a reference to the values container. - //! - //! @return a reference to the values container. - [[nodiscard]] constexpr auto values() const noexcept -> mapped_container_type const & - { - return m_containers.values; - } - - private: - containers m_containers; - key_compare m_comparator; - }; -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map.hpp b/libs/kstd/kstd/flat_map.hpp new file mode 100644 index 00000000..31557760 --- /dev/null +++ b/libs/kstd/kstd/flat_map.hpp @@ -0,0 +1,406 @@ +#ifndef KSTD_FLAT_MAP_HPP +#define KSTD_FLAT_MAP_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template, + typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> + struct flat_map + { + //! The type of container used to store the map keys. + using key_container_type = KeyContainerType; + + //! The type of container used to store the map values. + using mapped_container_type = MappedContainerType; + + //! The type of the map keys. + using key_type = KeyType; + + //! The type of the mappe values. + using mapped_type = MappedType; + + //! The type of a single key-value value in the map. + using value_type = std::pair; + + //! The comparator used to sort the keys. + using key_compare = KeyCompare; + + using reference = std::pair; + using const_reference = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = bits::flat_map_iterator; + using const_iterator = + bits::flat_map_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using containers = struct + { + key_container_type keys; + mapped_container_type values; + }; + + //! Compare two object of type value_type. + struct value_compare + { + constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool + { + return lhs.first < rhs.first; + } + }; + + //! Construct an empty flat map. + constexpr flat_map() + : flat_map{key_compare{}} + {} + + //! Construct an empty flat map using the given custom comparator. + //! + //! @param comparator The comparator to use for comparing keys. + constexpr explicit flat_map(key_compare const & comparator) + : m_containers{} + , m_comparator{comparator} + {} + + //! Replace the contents of this flat map with the one of a different one. + //! + //! @param other the flat map to copy from. + //! @return A reference to this flat map. + auto operator=(flat_map const & other) -> flat_map & + { + if (this != &other) + { + std::tie(m_containers, m_comparator) = std::tie(other.m_containers, other.m_comparator); + } + return *this; + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A const reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A const reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto at(K const & x) const -> mapped_type const & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() noexcept -> iterator + { + return iterator{m_containers.keys.begin(), m_containers.values.begin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{m_containers.keys.end(), m_containers.values.end()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator + { + return reverse_iterator{end()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Check whether this flat map is empty or not + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_containers.keys.empty(); + } + + //! Get the number of elements in this flat map. + [[nodiscard]] auto size() const noexcept -> size_type + { + return m_containers.keys.size(); + } + + //! Get the maximum number of elements possible in this flat map + [[nodiscard]] auto max_size() const noexcept -> size_type + { + return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); + } + + //! Try to insert a new key-value pair into the map. + //! + //! @param args Arguments to use for constructing the key-value pair. + //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. + template + auto emplace(Args &&... args) -> std::pair + requires std::constructible_from + { + auto value = value_type{std::forward(args)...}; + auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); + + if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return { + iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, + false + }; + } + + auto offset = std::distance(m_containers.keys.begin(), found); + auto key_iterator = m_containers.keys.begin() + offset; + auto mapped_iterator = m_containers.values.begin() + offset; + + auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); + auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Try to insert a element for the given key into this map. + //! + //! This function does nothing if the key is already present. + //! + //! @param key The key to insert a value for. + //! @param args The arguments to use to construct the mapped value. + template + auto try_emplace(key_type const & key, Args &&... args) -> std::pair + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found)) + { + return {found, false}; + } + + auto offset = std::distance(m_containers.keys.cbegin(), found); + auto intersertion_point = m_containers.value.begin() + offset; + + auto inserted_key = m_containers.keys.emplace(key); + auto inserted_mapped = m_containers.values.emplace(std::forward(args)...); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Check if the map contains the given key. + //! + //! @param key The key to check. + //! @return true iff. the key is found, false otherwise. + [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool + { + return find(key) != cend(); + } + + //! Get a reference to the keys container. + //! + //! @return a reference to the keys container. + [[nodiscard]] constexpr auto keys() const noexcept -> key_container_type const & + { + return m_containers.keys; + } + + //! Get a reference to the values container. + //! + //! @return a reference to the values container. + [[nodiscard]] constexpr auto values() const noexcept -> mapped_container_type const & + { + return m_containers.values; + } + + private: + containers m_containers; + key_compare m_comparator; + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map.test.cpp b/libs/kstd/kstd/flat_map.test.cpp index 9df03bb4..bfd02303 100644 --- a/libs/kstd/kstd/flat_map.test.cpp +++ b/libs/kstd/kstd/flat_map.test.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/libs/kstd/kstd/format b/libs/kstd/kstd/format deleted file mode 100644 index 5ed798db..00000000 --- a/libs/kstd/kstd/format +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef KSTD_FORMAT_HPP -#define KSTD_FORMAT_HPP - -// IWYU pragma: begin_exports -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// IWYU pragma: end_exports - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/format.hpp b/libs/kstd/kstd/format.hpp new file mode 100644 index 00000000..5ed798db --- /dev/null +++ b/libs/kstd/kstd/format.hpp @@ -0,0 +1,24 @@ +#ifndef KSTD_FORMAT_HPP +#define KSTD_FORMAT_HPP + +// IWYU pragma: begin_exports +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: end_exports + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/format.test.cpp b/libs/kstd/kstd/format.test.cpp index 6126605f..d38a8107 100644 --- a/libs/kstd/kstd/format.test.cpp +++ b/libs/kstd/kstd/format.test.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/libs/kstd/kstd/libc/string.cpp b/libs/kstd/kstd/libc/string.cpp index b7cdb82e..15ecc70f 100644 --- a/libs/kstd/kstd/libc/string.cpp +++ b/libs/kstd/kstd/libc/string.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/libs/kstd/kstd/memory b/libs/kstd/kstd/memory deleted file mode 100644 index f108c6d6..00000000 --- a/libs/kstd/kstd/memory +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef KSTD_MEMORY_HPP -#define KSTD_MEMORY_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/memory.hpp b/libs/kstd/kstd/memory.hpp new file mode 100644 index 00000000..f108c6d6 --- /dev/null +++ b/libs/kstd/kstd/memory.hpp @@ -0,0 +1,8 @@ +#ifndef KSTD_MEMORY_HPP +#define KSTD_MEMORY_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/mutex b/libs/kstd/kstd/mutex deleted file mode 100644 index b2a31aa9..00000000 --- a/libs/kstd/kstd/mutex +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef KSTD_MUTEX_HPP -#define KSTD_MUTEX_HPP - -#include - -namespace kstd -{ - - //! A non-recursive mutex. - struct mutex - { - mutex(); - ~mutex(); - - mutex(mutex const &) = delete; - mutex(mutex &&) = delete; - - auto operator=(mutex const &) -> mutex & = delete; - auto operator=(mutex &&) -> mutex & = delete; - - //! Lock the mutex. - //! - //! @note This function blocks for as long as the mutex is not available. - auto lock() -> void; - - //! Try to lock the mutex. - //! - //! @note This function never blocks. - //! @return @p true iff. the mutex was successfully locked, @p false otherwise. - auto try_lock() -> bool; - - //! Unlock the mutex. - //! - //! @note The behavior is undefined if the mutex is not currently held by the thread unlocking it. - auto unlock() -> void; - - private: - std::atomic_flag m_locked{}; - }; - - //! A tag type to specify that a given @p lockable wrapper should adopt ownership of the @p lockable. - struct adopt_lock_t - { - explicit adopt_lock_t() = default; - } constexpr inline adopt_lock{}; - - //! A tag type to specify that a given @p lockable wrapper should defer locking the @p lockable. - struct defer_lock_t - { - explicit defer_lock_t() = default; - } constexpr inline defer_lock{}; - - //! A tag type to specify that a given @p lockable wrapper should attempt to lock the @p lockable. - struct try_to_lock_t - { - explicit try_to_lock_t() = default; - } constexpr inline try_to_lock{}; - - //! An RAII wrapper for a single @p lockable like a kstd::mutex. - template - struct lock_guard - { - using mutex_type = MutexType; - - //! Construct a new lock_guard and immediately lock the given mutex. - //! - //! @note This function will block until the mutex was successfully locked. - //! @param mutex The mutex to lock. - lock_guard(MutexType & mutex) noexcept - : m_mutex(mutex) - { - m_mutex.lock(); - } - - //! Construct a new lock_guard and take ownership of the given mutex. - //! - //! @note The behavior is undefined if the mutex is not owned by the current thread. - //! @param mutex The mutex to take ownership of. - lock_guard(MutexType & mutex, adopt_lock_t) noexcept - : m_mutex(mutex) - {} - - //! Destroy this lock_guard and release the owned mutex. - ~lock_guard() - { - m_mutex.unlock(); - } - - lock_guard(lock_guard const &) noexcept = delete; - lock_guard(lock_guard &&) noexcept = delete; - - auto operator=(lock_guard const &) noexcept -> lock_guard & = delete; - auto operator=(lock_guard &&) noexcept -> lock_guard & = delete; - - private: - mutex_type & m_mutex; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/mutex.cpp b/libs/kstd/kstd/mutex.cpp index 73876572..c1c5b190 100644 --- a/libs/kstd/kstd/mutex.cpp +++ b/libs/kstd/kstd/mutex.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/libs/kstd/kstd/mutex.hpp b/libs/kstd/kstd/mutex.hpp new file mode 100644 index 00000000..b2a31aa9 --- /dev/null +++ b/libs/kstd/kstd/mutex.hpp @@ -0,0 +1,101 @@ +#ifndef KSTD_MUTEX_HPP +#define KSTD_MUTEX_HPP + +#include + +namespace kstd +{ + + //! A non-recursive mutex. + struct mutex + { + mutex(); + ~mutex(); + + mutex(mutex const &) = delete; + mutex(mutex &&) = delete; + + auto operator=(mutex const &) -> mutex & = delete; + auto operator=(mutex &&) -> mutex & = delete; + + //! Lock the mutex. + //! + //! @note This function blocks for as long as the mutex is not available. + auto lock() -> void; + + //! Try to lock the mutex. + //! + //! @note This function never blocks. + //! @return @p true iff. the mutex was successfully locked, @p false otherwise. + auto try_lock() -> bool; + + //! Unlock the mutex. + //! + //! @note The behavior is undefined if the mutex is not currently held by the thread unlocking it. + auto unlock() -> void; + + private: + std::atomic_flag m_locked{}; + }; + + //! A tag type to specify that a given @p lockable wrapper should adopt ownership of the @p lockable. + struct adopt_lock_t + { + explicit adopt_lock_t() = default; + } constexpr inline adopt_lock{}; + + //! A tag type to specify that a given @p lockable wrapper should defer locking the @p lockable. + struct defer_lock_t + { + explicit defer_lock_t() = default; + } constexpr inline defer_lock{}; + + //! A tag type to specify that a given @p lockable wrapper should attempt to lock the @p lockable. + struct try_to_lock_t + { + explicit try_to_lock_t() = default; + } constexpr inline try_to_lock{}; + + //! An RAII wrapper for a single @p lockable like a kstd::mutex. + template + struct lock_guard + { + using mutex_type = MutexType; + + //! Construct a new lock_guard and immediately lock the given mutex. + //! + //! @note This function will block until the mutex was successfully locked. + //! @param mutex The mutex to lock. + lock_guard(MutexType & mutex) noexcept + : m_mutex(mutex) + { + m_mutex.lock(); + } + + //! Construct a new lock_guard and take ownership of the given mutex. + //! + //! @note The behavior is undefined if the mutex is not owned by the current thread. + //! @param mutex The mutex to take ownership of. + lock_guard(MutexType & mutex, adopt_lock_t) noexcept + : m_mutex(mutex) + {} + + //! Destroy this lock_guard and release the owned mutex. + ~lock_guard() + { + m_mutex.unlock(); + } + + lock_guard(lock_guard const &) noexcept = delete; + lock_guard(lock_guard &&) noexcept = delete; + + auto operator=(lock_guard const &) noexcept -> lock_guard & = delete; + auto operator=(lock_guard &&) noexcept -> lock_guard & = delete; + + private: + mutex_type & m_mutex; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/print b/libs/kstd/kstd/print deleted file mode 100644 index 1033f722..00000000 --- a/libs/kstd/kstd/print +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef KSTD_PRINT -#define KSTD_PRINT - -#include // IWYU pragma: export -#include -#include - -#include - -namespace kstd -{ - - //! @qualifier kernel-defined - //! Format the given string using the given arguments and print it to the currently active output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto print(kstd::format_string...> format, Args const &... args) -> void - { - auto const arg_store = kstd::make_format_args(args...); - os::vprint(print_sink::stdout, format.str_view, arg_store.args); - } - - //! @qualifier kernel-defined - //! Format the given error string using the given arguments and print it to the currently active output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto print(print_sink sink, kstd::format_string...> format, Args &&... args) -> void - { - auto const arg_store = kstd::make_format_args(args...); - os::vprint(sink, format.str_view, arg_store.args); - } - - //! @qualifier kernel-defined - //! Format the given string using the given arguments and print it, including a newline, to the currently active - //! output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto println(kstd::format_string...> format, Args &&... args) -> void - { - print(print_sink::stdout, format, std::forward(args)...); - print(print_sink::stdout, "\n"); - } - - //! @qualifier kernel-defined - //! Format the given error string using the given arguments and print it, including a newline, to the currently active - //! output device. - //! - //! @param format The format string - //! @param args The arguments - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto println(print_sink sink, kstd::format_string...> format, Args &&... args) -> void - { - print(sink, format, std::forward(args)...); - print(sink, "\n"); - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/print.hpp b/libs/kstd/kstd/print.hpp new file mode 100644 index 00000000..6d00d83f --- /dev/null +++ b/libs/kstd/kstd/print.hpp @@ -0,0 +1,69 @@ +#ifndef KSTD_PRINT +#define KSTD_PRINT + +#include // IWYU pragma: export +#include +#include + +#include + +namespace kstd +{ + + //! @qualifier kernel-defined + //! Format the given string using the given arguments and print it to the currently active output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print(kstd::format_string...> format, Args const &... args) -> void + { + auto const arg_store = kstd::make_format_args(args...); + os::vprint(print_sink::stdout, format.str_view, arg_store.args); + } + + //! @qualifier kernel-defined + //! Format the given error string using the given arguments and print it to the currently active output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print(print_sink sink, kstd::format_string...> format, Args &&... args) -> void + { + auto const arg_store = kstd::make_format_args(args...); + os::vprint(sink, format.str_view, arg_store.args); + } + + //! @qualifier kernel-defined + //! Format the given string using the given arguments and print it, including a newline, to the currently active + //! output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println(kstd::format_string...> format, Args &&... args) -> void + { + print(print_sink::stdout, format, std::forward(args)...); + print(print_sink::stdout, "\n"); + } + + //! @qualifier kernel-defined + //! Format the given error string using the given arguments and print it, including a newline, to the currently active + //! output device. + //! + //! @param format The format string + //! @param args The arguments + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println(print_sink sink, kstd::format_string...> format, Args &&... args) -> void + { + print(sink, format, std::forward(args)...); + print(sink, "\n"); + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ranges b/libs/kstd/kstd/ranges deleted file mode 100644 index 78c3adbf..00000000 --- a/libs/kstd/kstd/ranges +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef KSTD_RANGES -#define KSTD_RANGES - -#include // IWYU pragma: export - -namespace kstd -{ -#if __glibcxx_ranges_to_container - using std::from_range; - using std::from_range_t; -#else - struct from_range_t - { - explicit from_range_t() = default; - }; - constexpr auto inline from_range = from_range_t{}; -#endif - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ranges.hpp b/libs/kstd/kstd/ranges.hpp new file mode 100644 index 00000000..78c3adbf --- /dev/null +++ b/libs/kstd/kstd/ranges.hpp @@ -0,0 +1,21 @@ +#ifndef KSTD_RANGES +#define KSTD_RANGES + +#include // IWYU pragma: export + +namespace kstd +{ +#if __glibcxx_ranges_to_container + using std::from_range; + using std::from_range_t; +#else + struct from_range_t + { + explicit from_range_t() = default; + }; + constexpr auto inline from_range = from_range_t{}; +#endif + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/stack b/libs/kstd/kstd/stack deleted file mode 100644 index 02e44ea4..00000000 --- a/libs/kstd/kstd/stack +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef KSTD_STACK_HPP -#define KSTD_STACK_HPP - -#include - -#include -#include - -namespace kstd -{ - /** - * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our - * custom memory management. - * - * @tparam T Element the stack instance should contain. - * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires - * access to pop_back(), push_back(), back(), size(), empty() and emplace_back() - */ - template> - struct stack - { - using container_type = Container; ///< Type of the underlying container used to implement stack-like interface. - using value_type = Container::value_type; ///< Type of the elements contained in the underlying container. - using size_type = Container::size_type; ///< Type of the size in the underlying container. - using reference = Container::reference; ///< Type of reference to the elements. - using const_reference = Container::const_reference; ///< Type of constant reference to the elements. - - /** - * @brief Default Constructor. - */ - stack() = default; - - stack(stack const &) = delete; - stack(stack &&) = delete; - auto operator=(stack const &) -> stack & = delete; - auto operator=(stack &&) -> stack & = delete; - /** - * @brief Constructs data with the given amount of elements containing the given value or alternatively the default - * constructed value. - * - * @param n Amount of elements we want to create and set the given value for. - * @param initial Inital value of all elements in the underlying data array. - */ - explicit stack(size_type n, value_type initial = value_type{}) - : _container(n, initial) - { - // Nothing to do. - } - - /** - * @brief Constructs data by copying all element from the given exclusive range. - * - * @tparam InputIterator Template that should have atleast input iterator characteristics. - * @param first Input iterator to the first element in the range we want to copy from. - * @param last Input iterator to one past the last element in the range we want to copy from. - */ - template - explicit stack(InputIterator first, InputIterator last) - : _container(first, last) - { - // Nothing to do. - } - - /** - * @brief Construct data by copying all elements from the initializer list. - * - * @param elements List we want to copy all elements from. - */ - explicit stack(std::initializer_list elements) - : _container(elements) - { - // Nothing to do. - } - - /** - * @brief Copy constructor. - * - * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all - * elements from it. - * - * @param other Other instance of stack we want to copy the data from. - */ - stack(stack const & other) - : _container(other) - { - // Nothing to do. - } - - /** - * @brief Destructor. - */ - ~stack() = default; - - /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If - * that is the case the capacity is increased automatically. - * - * @return Current amount of elements. - */ - auto size() const -> size_type - { - return _container.size(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - auto top() -> reference - { - return _container.back(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - auto top() const -> const_reference - { - return _container.back(); - } - - /** - * @brief Appends the given element value to the end of the container. The element is assigned through the - * assignment operator of the template type. The value is forwarded to the constructor as - * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). - * - * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, - * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. - * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @param value The value of the element to append. - */ - template - auto push(U && value) -> void - { - _container.push_back(std::forward(value)); - } - - /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the - * template type. The arguments args... are forwarded to the constructor as std::forward(args).... - * - * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case - * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only - * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @tparam Args - * @param args Arguments to forward to the constructor of the element - * @return value_type& - */ - template - auto emplace(Args &&... args) -> reference - { - _container.emplace_back(std::forward(args)...); - } - - /** - * @brief Removes the last element of the container. - * - * @note Calling pop_back on an empty container results in halting the - * further execution. Iterators and references to the last element are invalidated. The end() - * iterator is also invalidated. - */ - auto pop() -> void - { - _container.pop_back(); - } - - /** - * @brief Whether there are currently any items this container or not. - * - * @return True if there are no elements, false if there are. - */ - auto empty() const -> bool - { - return _container.empty(); - } - - private: - container_type _container = {}; ///< Underlying container used by the stack to actually save the data. - }; - -} // namespace kstd - -#endif diff --git a/libs/kstd/kstd/stack.hpp b/libs/kstd/kstd/stack.hpp new file mode 100644 index 00000000..27e693f2 --- /dev/null +++ b/libs/kstd/kstd/stack.hpp @@ -0,0 +1,192 @@ +#ifndef KSTD_STACK_HPP +#define KSTD_STACK_HPP + +#include + +#include +#include + +namespace kstd +{ + /** + * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our + * custom memory management. + * + * @tparam T Element the stack instance should contain. + * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires + * access to pop_back(), push_back(), back(), size(), empty() and emplace_back() + */ + template> + struct stack + { + using container_type = Container; ///< Type of the underlying container used to implement stack-like interface. + using value_type = Container::value_type; ///< Type of the elements contained in the underlying container. + using size_type = Container::size_type; ///< Type of the size in the underlying container. + using reference = Container::reference; ///< Type of reference to the elements. + using const_reference = Container::const_reference; ///< Type of constant reference to the elements. + + /** + * @brief Default Constructor. + */ + stack() = default; + + stack(stack const &) = delete; + stack(stack &&) = delete; + auto operator=(stack const &) -> stack & = delete; + auto operator=(stack &&) -> stack & = delete; + /** + * @brief Constructs data with the given amount of elements containing the given value or alternatively the default + * constructed value. + * + * @param n Amount of elements we want to create and set the given value for. + * @param initial Inital value of all elements in the underlying data array. + */ + explicit stack(size_type n, value_type initial = value_type{}) + : _container(n, initial) + { + // Nothing to do. + } + + /** + * @brief Constructs data by copying all element from the given exclusive range. + * + * @tparam InputIterator Template that should have atleast input iterator characteristics. + * @param first Input iterator to the first element in the range we want to copy from. + * @param last Input iterator to one past the last element in the range we want to copy from. + */ + template + explicit stack(InputIterator first, InputIterator last) + : _container(first, last) + { + // Nothing to do. + } + + /** + * @brief Construct data by copying all elements from the initializer list. + * + * @param elements List we want to copy all elements from. + */ + explicit stack(std::initializer_list elements) + : _container(elements) + { + // Nothing to do. + } + + /** + * @brief Copy constructor. + * + * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all + * elements from it. + * + * @param other Other instance of stack we want to copy the data from. + */ + stack(stack const & other) + : _container(other) + { + // Nothing to do. + } + + /** + * @brief Destructor. + */ + ~stack() = default; + + /** + * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If + * that is the case the capacity is increased automatically. + * + * @return Current amount of elements. + */ + auto size() const -> size_type + { + return _container.size(); + } + + /** + * @brief Returns a reference to the last element in the container. Calling back on an empty container causes + * undefined behavior. + * + * @return Reference to the last element. + */ + auto top() -> reference + { + return _container.back(); + } + + /** + * @brief Returns a reference to the last element in the container. Calling back on an empty container causes + * undefined behavior. + * + * @return Reference to the last element. + */ + auto top() const -> const_reference + { + return _container.back(); + } + + /** + * @brief Appends the given element value to the end of the container. The element is assigned through the + * assignment operator of the template type. The value is forwarded to the constructor as + * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). + * + * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, + * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. + * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which + * allows the template method to be used by both lvalue and rvalues and compile a different implementation. + * + * @param value The value of the element to append. + */ + template + auto push(U && value) -> void + { + _container.push_back(std::forward(value)); + } + + /** + * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the + * template type. The arguments args... are forwarded to the constructor as std::forward(args).... + * + * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case + * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only + * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which + * allows the template method to be used by both lvalue and rvalues and compile a different implementation. + * + * @tparam Args + * @param args Arguments to forward to the constructor of the element + * @return value_type& + */ + template + auto emplace(Args &&... args) -> reference + { + _container.emplace_back(std::forward(args)...); + } + + /** + * @brief Removes the last element of the container. + * + * @note Calling pop_back on an empty container results in halting the + * further execution. Iterators and references to the last element are invalidated. The end() + * iterator is also invalidated. + */ + auto pop() -> void + { + _container.pop_back(); + } + + /** + * @brief Whether there are currently any items this container or not. + * + * @return True if there are no elements, false if there are. + */ + auto empty() const -> bool + { + return _container.empty(); + } + + private: + container_type _container = {}; ///< Underlying container used by the stack to actually save the data. + }; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string deleted file mode 100644 index 4a67a3a4..00000000 --- a/libs/kstd/kstd/string +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef KSTD_STRING_HPP -#define KSTD_STRING_HPP - -#include // IWYU pragma: export -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace kstd -{ - using string = basic_string; - - /** - * @brief Converts an integer to a string as if by kstd::format("{}", value). - * @tparam IntegralType The type of the unsigned integer to convert. - * @param value The unsigned integer to convert. - * @return A string representation of the given unsigned integer. - */ - template - [[nodiscard]] constexpr auto inline to_string(IntegralType value) -> string - { - return kstd::format("{}", value); - } - - template<> - struct formatter : formatter - { - auto format(string const & str, format_context & context) const -> void - { - formatter::format(str.data(), context); - } - }; - - inline namespace literals - { - inline namespace string_literals - { - //! Create a string new string from a literal C-style string. - //! - //! @param string The C-style string to use as a source for the new string. - //! @param length The length of the supplied C-style string. - //! @return a new string object, containing the same value as the provided C-style string. - constexpr auto operator""_s(char const * string, std::size_t length) -> kstd::string - { - return {string, length}; - } - } // namespace string_literals - } // namespace literals - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/kstd/string.hpp b/libs/kstd/kstd/string.hpp new file mode 100644 index 00000000..9978affc --- /dev/null +++ b/libs/kstd/kstd/string.hpp @@ -0,0 +1,60 @@ +#ifndef KSTD_STRING_HPP +#define KSTD_STRING_HPP + +#include // IWYU pragma: export +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace kstd +{ + using string = basic_string; + + /** + * @brief Converts an integer to a string as if by kstd::format("{}", value). + * @tparam IntegralType The type of the unsigned integer to convert. + * @param value The unsigned integer to convert. + * @return A string representation of the given unsigned integer. + */ + template + [[nodiscard]] constexpr auto inline to_string(IntegralType value) -> string + { + return kstd::format("{}", value); + } + + template<> + struct formatter : formatter + { + auto format(string const & str, format_context & context) const -> void + { + formatter::format(str.data(), context); + } + }; + + inline namespace literals + { + inline namespace string_literals + { + //! Create a string new string from a literal C-style string. + //! + //! @param string The C-style string to use as a source for the new string. + //! @param length The length of the supplied C-style string. + //! @return a new string object, containing the same value as the provided C-style string. + constexpr auto operator""_s(char const * string, std::size_t length) -> kstd::string + { + return {string, length}; + } + } // namespace string_literals + } // namespace literals + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index e11877e5..b8fa1139 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -1,6 +1,6 @@ -#include +#include -#include +#include #include #include diff --git a/libs/kstd/kstd/units b/libs/kstd/kstd/units deleted file mode 100644 index df5eb374..00000000 --- a/libs/kstd/kstd/units +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef KSTD_UNITS_HPP -#define KSTD_UNITS_HPP - -#include -#include -#include - -namespace kstd -{ - - //! A basic template for strongly typed units. - template - struct basic_unit - { - using value_type = ValueType; - - constexpr basic_unit() noexcept - : value{} - {} - - explicit constexpr basic_unit(value_type value) noexcept - : value{value} - {} - - explicit constexpr operator value_type() const noexcept - { - return value; - } - - constexpr auto operator+(basic_unit const & other) const noexcept -> basic_unit - { - return basic_unit{value + other.value}; - } - - constexpr auto operator+=(basic_unit const & other) noexcept -> basic_unit & - { - return *this = *this + other; - } - - constexpr auto operator-(basic_unit const & other) const noexcept -> basic_unit - { - return basic_unit{value - other.value}; - } - - constexpr auto operator-=(basic_unit const & other) noexcept -> basic_unit & - { - return *this = *this - other; - } - - constexpr auto operator*(std::integral auto factor) noexcept -> basic_unit - { - return basic_unit{value * factor}; - } - - constexpr auto operator*=(std::integral auto factor) noexcept -> basic_unit - { - return *this = *this * factor; - } - - constexpr auto operator/(std::integral auto divisor) noexcept -> basic_unit - { - return basic_unit{value / divisor}; - } - - constexpr auto operator/=(std::integral auto divisor) noexcept -> basic_unit - { - return *this = *this / divisor; - } - - constexpr auto operator/(basic_unit const & other) const noexcept - { - return value / other.value; - } - - constexpr auto operator<=>(basic_unit const & other) const noexcept -> std::strong_ordering = default; - - value_type value; - }; - - template - constexpr auto operator*(Factor factor, basic_unit const & unit) noexcept - -> basic_unit - { - return basic_unit{unit.value * factor}; - } - - namespace units - { - using bytes = basic_unit; - - constexpr auto KiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024}; - } - - constexpr auto MiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024 * 1024}; - } - - constexpr auto GiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024 * 1024 * 1024}; - } - - template - constexpr auto operator+(ValueType * pointer, bytes offset) -> ValueType * - { - return pointer + offset.value; - } - - } // namespace units - - namespace units_literals - { - constexpr auto operator""_B(unsigned long long value) noexcept -> units::bytes - { - return units::bytes{value}; - } - - constexpr auto operator""_KiB(unsigned long long value) noexcept -> units::bytes - { - return units::KiB(value); - } - - constexpr auto operator""_MiB(unsigned long long value) noexcept -> units::bytes - { - return units::MiB(value); - } - - constexpr auto operator""_GiB(unsigned long long value) noexcept -> units::bytes - { - return units::GiB(value); - } - - } // namespace units_literals - - template - constexpr auto object_size(ValueType const &) -> units::bytes - { - return units::bytes{sizeof(ValueType)}; - } - - template - constexpr auto type_size = units::bytes{sizeof(T)}; - -} // namespace kstd - -#endif diff --git a/libs/kstd/kstd/units.hpp b/libs/kstd/kstd/units.hpp new file mode 100644 index 00000000..df5eb374 --- /dev/null +++ b/libs/kstd/kstd/units.hpp @@ -0,0 +1,149 @@ +#ifndef KSTD_UNITS_HPP +#define KSTD_UNITS_HPP + +#include +#include +#include + +namespace kstd +{ + + //! A basic template for strongly typed units. + template + struct basic_unit + { + using value_type = ValueType; + + constexpr basic_unit() noexcept + : value{} + {} + + explicit constexpr basic_unit(value_type value) noexcept + : value{value} + {} + + explicit constexpr operator value_type() const noexcept + { + return value; + } + + constexpr auto operator+(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value + other.value}; + } + + constexpr auto operator+=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this + other; + } + + constexpr auto operator-(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value - other.value}; + } + + constexpr auto operator-=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this - other; + } + + constexpr auto operator*(std::integral auto factor) noexcept -> basic_unit + { + return basic_unit{value * factor}; + } + + constexpr auto operator*=(std::integral auto factor) noexcept -> basic_unit + { + return *this = *this * factor; + } + + constexpr auto operator/(std::integral auto divisor) noexcept -> basic_unit + { + return basic_unit{value / divisor}; + } + + constexpr auto operator/=(std::integral auto divisor) noexcept -> basic_unit + { + return *this = *this / divisor; + } + + constexpr auto operator/(basic_unit const & other) const noexcept + { + return value / other.value; + } + + constexpr auto operator<=>(basic_unit const & other) const noexcept -> std::strong_ordering = default; + + value_type value; + }; + + template + constexpr auto operator*(Factor factor, basic_unit const & unit) noexcept + -> basic_unit + { + return basic_unit{unit.value * factor}; + } + + namespace units + { + using bytes = basic_unit; + + constexpr auto KiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024}; + } + + constexpr auto MiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024}; + } + + constexpr auto GiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024 * 1024}; + } + + template + constexpr auto operator+(ValueType * pointer, bytes offset) -> ValueType * + { + return pointer + offset.value; + } + + } // namespace units + + namespace units_literals + { + constexpr auto operator""_B(unsigned long long value) noexcept -> units::bytes + { + return units::bytes{value}; + } + + constexpr auto operator""_KiB(unsigned long long value) noexcept -> units::bytes + { + return units::KiB(value); + } + + constexpr auto operator""_MiB(unsigned long long value) noexcept -> units::bytes + { + return units::MiB(value); + } + + constexpr auto operator""_GiB(unsigned long long value) noexcept -> units::bytes + { + return units::GiB(value); + } + + } // namespace units_literals + + template + constexpr auto object_size(ValueType const &) -> units::bytes + { + return units::bytes{sizeof(ValueType)}; + } + + template + constexpr auto type_size = units::bytes{sizeof(T)}; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/vector b/libs/kstd/kstd/vector deleted file mode 100644 index 736b854d..00000000 --- a/libs/kstd/kstd/vector +++ /dev/null @@ -1,1079 +0,0 @@ -#ifndef KSTD_VECTOR_HPP -#define KSTD_VECTOR_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - //! A resizable, contiguous container. - //! - //! @tparam ValueType The type of values contained in this vector. - //! @tparam Allocator The type of allocator used for memory management by this container. - template> - struct vector - { - //! The type of the elements contained in this vector. - using value_type = ValueType; - //! The allocator used by this vector for memory management. - using allocator_type = Allocator; - //! The type of all sizes used in and with this vector. - using size_type = std::size_t; - //! The type of the difference between two iterators. - using difference_type = std::ptrdiff_t; - //! The type of references to elements in this vector. - using reference = value_type &; - //! The type of references to constant elements in this vector. - using const_reference = value_type const &; - //! The type of pointers to elements in this vector. - using pointer = std::allocator_traits::pointer; - //! The type of pointers to constant elements in this vector. - using const_pointer = std::allocator_traits::const_pointer; - //! The type of iterators into this container. - using iterator = pointer; - //! The type of constant iterators into this container. - using const_iterator = const_pointer; - //! The type of reverse iterators into this container. - using reverse_iterator = std::reverse_iterator; - //! The type of constant reverse iterators into this container. - using const_reverse_iterator = std::reverse_iterator; - - //! Construct a new, empty vector. - constexpr vector() noexcept(std::is_nothrow_default_constructible_v) - : vector(allocator_type{}) - {} - - //! Construct a new, empty vector with a given allocator. - //! - //! @param allocator The allocator to use in the vector. - explicit constexpr vector(allocator_type const & allocator) noexcept( - std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{0} - , m_capacity{0} - , m_data{allocate_n(m_capacity)} - {} - - //! Construct a new vector and fill it with the given number of default constructed elements. - //! - //! @param count The number of element to create the vector with. - //! @param allocator The allocator to use in the vector. - explicit constexpr vector(size_type count, allocator_type const & allocator = allocator_type{}) noexcept( - std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{count} - , m_capacity{count} - , m_data{allocate_n(m_capacity)} - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i); - } - } - - //! Construct a new vector and fill it with the given number of copy constructed elements. - //! - //! @param count The number of element to create the vector with. - //! @param value The value to copy for each element - //! @param allocator The allocator to use in the vector. - constexpr vector(size_type count, const_reference value, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{count} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, value); - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! @tparam ForwardIterator An iterator type used to describe the source range. - //! @param first The start of the source range. - //! @param last The end of the source range. - template - constexpr vector(ForwardIterator first, ForwardIterator last, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{static_cast(std::ranges::distance(first, last))} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - for (auto destination = m_data; first != last; ++first, ++destination) - { - std::allocator_traits::construct(m_allocator, destination, *first); - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! @tparam InputIterator An iterator type used to describe the source range. - //! @param first The start of the source range. - //! @param last The end of the source range. - template - constexpr vector(InputIterator first, InputIterator last, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{0} - , m_capacity{0} - , m_data{} - { - while (first != last) - { - emplace_back(*first); - ++first; - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! - template - requires((std::ranges::forward_range || std::ranges::sized_range) && - kstd::bits::container_compatible_range) - constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) - : m_allocator{allocator} - , m_size{std::ranges::size(range)} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - auto destination = m_data; - for (auto && element : std::forward(range)) - { - std::allocator_traits::construct(m_allocator, destination++, - std::forward>(element)); - } - } - - //! Construct a new vector and initialize it's content by copying all elements from a given vector. - //! - //! @param other The source vector. - constexpr vector(vector const & other) - : m_allocator{other.m_allocator} - , m_size{other.m_size} - , m_capacity{other.m_capacity} - , m_data(allocate_n(m_capacity)) - { - uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); - } - - //! Construct a new vector and initialize it's content by moving from a given vector. - //! - //! @param other The source vector. - constexpr vector(vector && other) noexcept - : m_allocator{std::move(other.m_allocator)} - , m_size{std::exchange(other.m_size, size_type{})} - , m_capacity(std::exchange(other.m_capacity, size_type{})) - , m_data(std::exchange(other.m_data, nullptr)) - {} - - //! Construct a new vector and initialize it's content by copying from a given vector. - //! - //! @param other The source vector. - //! @param allocator The allocator to use in the vector. - constexpr vector(vector const & other, std::type_identity_t const & allocator) - : m_allocator{allocator} - , m_size{other.m_size} - , m_capacity{other.m_capacity} - , m_data{allocate_n(m_capacity)} - { - uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); - } - - //! Construct a new vector and initialize it's content by copying from a given vector. - //! - //! @param other The source vector. - //! @param allocator The allocator to use in the vector. - constexpr vector(vector && other, std::type_identity_t const & allocator) - : m_allocator{allocator} - , m_size{} - , m_capacity{} - , m_data{} - { - if constexpr (!std::allocator_traits::is_always_equal::value) - { - if (m_allocator != other.m_allocator) - { - m_capacity = other.size(); - m_data = allocate_n(capacity()); - m_size = other.size(); - uninitialized_move_with_allocator(other.begin(), begin(), other.size()); - other.clear(); - return; - } - } - m_size = std::exchange(other.m_size, size_type{}); - m_capacity = std::exchange(other.m_capacity, size_type{}); - m_data = std::exchange(other.m_data, nullptr); - } - - //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. - //! - //! @param list The initializer list containing the source objects. - vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) - : vector{std::ranges::begin(list), std::ranges::end(list), allocator} - {} - - //! Destroy this vector. - constexpr ~vector() - { - clear_and_deallocate(); - } - - //! Replace the contents of this vector by the copying from the given source vector. - //! - //! @param other The source vector. - constexpr auto operator=(vector const & other) -> vector & - { - if (this == std::addressof(other)) - { - return *this; - } - - if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value) - { - if (get_allocator() != other.get_allocator()) - { - clear_and_deallocate(); - } - m_allocator = other.get_allocator(); - } - - if (capacity() >= other.size()) - { - auto const overlap = std::min(m_size, other.m_size); - std::ranges::copy(other.begin(), other.begin() + overlap, begin()); - - if (m_size < other.m_size) - { - uninitialized_copy_with_allocator(other.begin() + size(), begin() + size(), other.m_size - size()); - } - else if (m_size > other.m_size) - { - destroy_n(begin() + other.size(), size() - other.size()); - } - } - else - { - auto new_data = allocate_n(other.size()); - uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = other.size(); - } - - m_size = other.size(); - return *this; - } - - //! Replace the contents fo this vector by moving from the given source. - //! - //! @param other The source vector. - constexpr auto operator=(vector && other) noexcept( - std::allocator_traits::propagate_on_container_move_assignment::value || - std::allocator_traits::is_always_equal::value) -> vector & - { - using std::swap; - - if (this == std::addressof(other)) - { - return *this; - } - - if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) - { - clear_and_deallocate(); - swap(m_allocator, other.m_allocator); - swap(m_size, other.m_size); - swap(m_capacity, other.m_capacity); - swap(m_data, other.m_data); - } - else if (m_allocator == other.m_allocator) - { - clear_and_deallocate(); - swap(m_size, other.m_size); - swap(m_capacity, other.m_capacity); - swap(m_data, other.m_data); - } - else - { - if (capacity() >= other.size()) - { - auto const overlap = std::min(size(), other.size()); - std::ranges::move(other.begin(), other.begin() + overlap, begin()); - - if (size() < other.size()) - { - uninitialized_move_with_allocator(other.begin() + size(), begin() + size(), other.size() - size()); - } - else if (size() > other.size()) - { - destroy_n(begin() + other.size(), size() - other.size()); - } - } - else - { - auto new_data = allocate_n(other.size()); - uninitialized_move_with_allocator(other.begin(), new_data, other.size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = other.m_size; - } - - other.destroy_n(other.begin(), other.size()); - m_size = std::exchange(other.m_size, size_type{}); - } - - return *this; - } - - //! Get a copy of the allocator associated with this vector. - [[nodiscard]] constexpr auto get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) - -> allocator_type - { - return m_allocator; - } - - //! Get a reference to the element at the given index. - //! - //! This function will panic if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto at(size_type index) -> reference - { - panic_if_out_of_bounds(index); - return (*this)[index]; - } - - //! Get a reference to the element at the given index. - //! - //! This function will panic if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto at(size_type index) const -> const_reference - { - panic_if_out_of_bounds(index); - return (*this)[index]; - } - - //! Get a reference to the element at the given index. - //! - //! The behavior is undefined if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto operator[](size_type index) -> reference - { - return data()[index]; - } - - //! Get a reference to the element at the given index. - //! - //! The behavior is undefined if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto operator[](size_type index) const -> const_reference - { - return data()[index]; - } - - //! Get a reference to the first element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto front() -> reference - { - return *begin(); - } - - //! Get a reference to the first element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto front() const -> const_reference - { - return *begin(); - } - - //! Get a reference to the last element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto back() -> reference - { - return *rbegin(); - } - - //! Get a reference to the last element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto back() const -> const_reference - { - return *rbegin(); - } - - //! Get a pointer to the beginning of the underlying contiguous storage. - [[nodiscard]] constexpr auto data() noexcept -> pointer - { - return m_data; - } - - //! Get a pointer to the beginning of the underlying contiguous storage. - [[nodiscard]] constexpr auto data() const noexcept -> const_pointer - { - return m_data; - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto begin() noexcept -> iterator - { - return empty() ? end() : data(); - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator - { - return empty() ? end() : data(); - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator - { - return begin(); - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto end() noexcept -> pointer - { - return capacity() ? data() + size() : nullptr; - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto end() const noexcept -> const_pointer - { - return capacity() ? data() + size() : nullptr; - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto cend() const noexcept -> const_pointer - { - return end(); - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator - { - return empty() ? rend() : reverse_iterator{end()}; - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator - { - return empty() ? rend() : const_reverse_iterator{end()}; - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator - { - return rbegin(); - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator - { - return reverse_iterator{begin()}; - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto rend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{begin()}; - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator - { - return rend(); - } - - //! Check whether this vector is empty. - [[nodiscard]] constexpr auto empty() const noexcept -> bool - { - return !size(); - } - - //! Get the number of elements present in this vector. - [[nodiscard]] constexpr auto size() const noexcept -> size_type - { - return m_size; - } - - //! Get the maximum possible number of element for this vector. - [[nodiscard]] constexpr auto max_size() const noexcept -> size_type - { - return std::allocator_traits::max_size(m_allocator); - } - - //! Reserve storage for at list the given number of elements. - constexpr auto reserve(size_type new_capacity) -> void - { - if (new_capacity <= capacity()) - { - return; - } - - if (new_capacity > max_size()) - { - kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); - } - - reallocate_exactly(new_capacity); - } - - //! Resize this vector to contain @p new_size elements. - constexpr auto resize(size_type new_size) -> void - { - resize(new_size, value_type{}); - } - - //! Resize this vector to contain @p new_size elements, filling new elements with @p value. - constexpr auto resize(size_type new_size, const_reference value) -> void - { - if (new_size < size()) - { - destroy_n(begin() + new_size, size() - new_size); - m_size = new_size; - return; - } - - if (new_size == size()) - { - return; - } - - if (new_size > max_size()) - { - kstd::os::panic("[kstd:vector] Tried to resize more space than theoretically possible."); - } - - if (new_size > capacity()) - { - reserve(new_size); - } - - for (auto i = size(); i < new_size; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, value); - } - - m_size = new_size; - } - - //! Get the number of element this vector has currently space for, including elements currently in this vector. - [[nodiscard]] constexpr auto capacity() const noexcept -> size_type - { - return m_capacity; - } - - //! Try to release unused storage space. - constexpr auto shrink_to_fit() -> void - { - if (m_size == m_capacity) - { - return; - } - - reallocate_exactly(m_size); - } - - //! Clear the contents of this vector. - constexpr auto clear() noexcept -> void - { - destroy_n(begin(), size()); - m_size = 0; - } - - //! Insert an element at a given position. - //! - //! @param position The position to insert the element at. - //! @param value The value to insert. - //! @return An iterator to the inserted element. - constexpr auto insert(const_iterator position, value_type const & value) -> iterator - { - return do_insert(position, value); - } - - //! Insert an element at a given position. - //! - //! @param position The position to insert the element at. - //! @param value The value to insert. - //! @return An iterator to the inserted element. - constexpr auto insert(const_iterator position, value_type && value) -> iterator - { - return do_insert(position, std::move(value)); - } - - //! Insert the element of a given range into the vector at a given position. - //! - //! @param range The source range to insert elements from. - //! @tparam SourceRange A container compatible range type. - template - requires requires(allocator_type allocator, pointer destination, SourceRange range) { - requires kstd::bits::container_compatible_range; - requires std::move_constructible; - requires std::is_move_assignable_v; - requires std::swappable; - std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); - } - // NOLINTNEXTLINE(misc-no-recursion) - constexpr auto insert_range(const_iterator position, SourceRange && range) -> iterator - { - auto prefix_size = std::ranges::distance(begin(), position); - - if (position == end()) - { - append_range(std::forward(range)); - return begin() + prefix_size; - } - - if constexpr (std::ranges::forward_range || std::ranges::sized_range) - { - auto number_of_elements = static_cast(std::ranges::distance(range)); - if (!number_of_elements) - { - return begin() + prefix_size; - } - - if (capacity() - size() < number_of_elements) - { - reserve(size() + std::max(size(), number_of_elements)); - } - - auto insert_position = begin() + prefix_size; - auto suffix_size = static_cast(std::ranges::distance(insert_position, end())); - - if (number_of_elements >= suffix_size) - { - uninitialized_move_with_allocator(insert_position, insert_position + number_of_elements, suffix_size); - auto result = std::ranges::copy_n(std::ranges::begin(range), suffix_size, insert_position); - uninitialized_copy_with_allocator(std::move(result.in), end(), number_of_elements - suffix_size); - } - else - { - uninitialized_move_with_allocator(end() - number_of_elements, end(), number_of_elements); - std::ranges::move_backward(insert_position, end() - number_of_elements, end()); - std::ranges::copy_n(std::ranges::begin(range), number_of_elements, insert_position); - } - - m_size += number_of_elements; - return insert_position; - } - - auto range_begin = std::ranges::begin(range); - auto range_end = std::ranges::end(range); - - auto remainder = vector{get_allocator()}; - for (; range_begin != range_end; ++range_begin) - { - remainder.emplace_back(*static_cast>(range_begin)); - } - reserve(size() + std::max(size(), remainder.size())); - - return insert_range(begin() + prefix_size, remainder); - } - - template - constexpr auto emplace(const_iterator position, Args &&... args) -> iterator - { - auto prefix_size = std::ranges::distance(cbegin(), position); - if (position == cend()) - { - emplace_back(std::forward(args)...); - } - else if (m_capacity == m_size) - { - reallocate_and_insert(begin() + prefix_size, std::forward(args)...); - } - else - { - auto to_insert = value_type{std::forward(args)...}; - auto insert_position = begin() + prefix_size; - shift_back(insert_position); - *insert_position = std::move(to_insert); - ++m_size; - } - - return begin() + prefix_size; - } - - //! Erase an element at a given position. - //! - //! @note This function will panic if position == end() - //! - //! @param position An interator pointing to the element to delete - //! @return An iterator pointing to the element after the deleted element - constexpr auto erase(const_iterator position) -> iterator - { - if (position == end()) - { - os::panic("[kstd:vector] Attempted to erase end()!"); - } - - auto prefix_size = std::ranges::distance(cbegin(), position); - - std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); - std::allocator_traits::destroy(m_allocator, end() - 1); - --m_size; - - return begin() + prefix_size; - } - - //! Erase a range of elements from this vector. - //! - //! @param first The start of the range to erase. - //! @param last The end of the range to erase. - //! @return An iterator pointing to the element after the last deleted element. - constexpr auto erase(const_iterator first, const_iterator last) -> iterator - { - if (first == last) - { - return begin() + std::ranges::distance(cbegin(), first); - } - - auto prefix_size = std::ranges::distance(cbegin(), first); - auto element_count = std::ranges::distance(first, last); - - std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); - destroy_n(end() - element_count, element_count); - m_size -= element_count; - - return begin() + prefix_size; - } - - //! Append a given element to this vector via copy construction. - constexpr auto push_back(value_type const & value) -> void - { - emplace_back(value); - } - - //! Append a given element to this vector via move construction. - constexpr auto push_back(value_type && value) -> void - { - emplace_back(std::move(value)); - } - - //! Append a given element to this vector via direct construction. - template - constexpr auto emplace_back(Args &&... args) -> reference - { - if (m_capacity == m_size) - { - reallocate_and_insert(end(), std::forward(args)...); - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); - ++m_size; - } - return this->back(); - } - - //! Append the elements of a given range to this vector. - //! - //! @param range The range of elements to be appended. - //! @tparam SourceRange A container compatible range type. - template SourceRange> - requires requires(Allocator allocator, pointer destination, SourceRange range) { - std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); - } - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion) - constexpr auto append_range(SourceRange && range) -> void - { - if constexpr (std::ranges::forward_range || std::ranges::sized_range) - { - auto number_of_elements = static_cast(std::ranges::distance(range)); - - if (!capacity()) - { - reserve(number_of_elements); - } - - if (capacity() - size() >= number_of_elements) - { - uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements); - m_size += number_of_elements; - return; - } - - auto new_capacity = m_capacity + std::max(size(), number_of_elements); - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - - uninitialized_move_with_allocator(begin(), new_data, size()); - uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements); - clear_and_deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size + number_of_elements; - return; - } - - auto range_begin = std::ranges::begin(range); - auto range_end = std::ranges::end(range); - - for (auto i = capacity() - size(); i > 0; --i, ++range_begin) - { - emplace_back(*range_begin); - } - - if (range_begin == range_end) - { - return; - } - - auto remainder = vector{get_allocator()}; - for (; range_begin != range_end; ++range_begin) - { - remainder.emplace_back(*static_cast>(range_begin)); - } - reserve(size() + std::max(size(), remainder.size())); - append_range(remainder); - } - - //! Remove the last element of this vector. - //! - //! If this vector is empty, the behavior is undefined. - constexpr auto pop_back() -> void - { - --m_size; - std::allocator_traits::destroy(m_allocator, data() + size()); - } - - private: - //! Use the allocator of this vector to allocate enough space for the given number of elements. - //! - //! @param count The number of elements to allocate space for. - [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer - { - if (count) - { - return std::allocator_traits::allocate(m_allocator, count); - } - return nullptr; - } - - //! Clear this vector and release it's memory. - constexpr auto clear_and_deallocate() -> void - { - clear(); - deallocate(); - } - - //! Release the memory of this vector. - constexpr auto deallocate() - { - if (m_data) - { - std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); - m_capacity = 0; - m_size = 0; - m_data = nullptr; - } - } - - //! Insert an element into this vector at the given position. - //! - //! @param position The position to insert the element at. - //! @param value The value to insert. - template - constexpr auto do_insert(const_iterator position, U && value) - { - auto prefix_size = std::ranges::distance(cbegin(), position); - if (position == cend()) - { - push_back(std::forward(value)); - } - else if (m_capacity == m_size) - { - reallocate_and_insert(begin() + prefix_size, std::forward(value)); - } - else if (&value >= cbegin() && &value < cend()) - { - auto temporary = std::forward(value); - shift_back(begin() + prefix_size); - *(begin() + prefix_size) = std::move(temporary); - ++m_size; - } - else - { - shift_back(begin() + prefix_size); - *(begin() + prefix_size) = std::forward(value); - ++m_size; - } - return begin() + prefix_size; - } - - //! Destroy a number of elements in this vector. - //! - //! @param first The start of the range of the elements to be destroyed. - //! @param count The number of elements to destroy. - constexpr auto destroy_n(iterator first, std::size_t count) -> void - { - std::ranges::for_each(first, first + count, [&](auto & element) { - std::allocator_traits::destroy(m_allocator, std::addressof(element)); - }); - } - - //! Panic the kernel if the given index is out of bounds. - //! - //! @param index The index to check. - constexpr auto panic_if_out_of_bounds(size_type index) const -> void - { - if (index >= m_size) - { - os::panic("[kstd:vector] Attempted to read element at invalid index"); - } - } - - //! Copy a number of elements from a source range into the uninitialized destination range inside this vector. - //! - //! @param from The start of the source range. - //! @param to The start of the target range inside this vector. - //! @param count The number of elements to copy - template - constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count) - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, to++, *from++); - } - } - - //! Move a number of elements from a source range into the uninitialized destination range inside this vector. - //! - //! @param from The start of the source range. - //! @param to The start of the target range inside this vector. - //! @param count The number of elements to copy - template - constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count) - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); - } - } - - //! Reallocate the storage space to be exactly as large as the given size. - constexpr auto reallocate_exactly(size_type new_capacity) -> void - { - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - uninitialized_move_with_allocator(begin(), new_data, old_size); - clear_and_deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - - //! Shift all elements, starting the given position, one position back inside the vector. - constexpr auto shift_back(iterator starting_at) - { - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(starting_at, end() - 1, end()); - } - - //! Reallocate the storage of this vector and insert an element at the given position. - //! - //! @param position The position to insert the element at. - //! @param args The constructor arguments for the inserted element. - template - constexpr auto reallocate_and_insert(iterator position, Args &&... args) - { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - - std::allocator_traits::construct(m_allocator, new_data + prefix_size, - std::forward(args)...); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size + 1; - } - - //! The allocator used by this vector. - [[no_unique_address]] allocator_type m_allocator{}; - - //! The number of elements in this vector. - size_type m_size{}; - - //! The number of elements this vector has room for. - size_type m_capacity{}; - - //! The pointer to the start of the memory managed by this vector. - value_type * m_data{}; - }; - - //! Check if the content of two vectors is equal. - template - constexpr auto operator==(vector const & lhs, vector const & rhs) -> bool - { - return std::ranges::equal(lhs, rhs); - } - - //! Perform a lexicographical comparison of the content of two vectors. - template - constexpr auto operator<=>(vector const & lhs, vector const & rhs) - -> decltype(std::declval() <=> std::declval()) - { - return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - //! Deduction guide for vector construction from an interator pair. - template::value_type>> - vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) - -> vector::value_type, Allocator>; - - //! Deduction guide for vector construction from an interator pair. - template::value_type>> - vector(InputIterator, InputIterator, Allocator = Allocator()) - -> vector::value_type, Allocator>; - - //! Deduction guide for vector construction from a range. - template>> - vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; - -} // namespace kstd - -#endif diff --git a/libs/kstd/kstd/vector.hpp b/libs/kstd/kstd/vector.hpp new file mode 100644 index 00000000..7cc14283 --- /dev/null +++ b/libs/kstd/kstd/vector.hpp @@ -0,0 +1,1079 @@ +#ifndef KSTD_VECTOR_HPP +#define KSTD_VECTOR_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + //! A resizable, contiguous container. + //! + //! @tparam ValueType The type of values contained in this vector. + //! @tparam Allocator The type of allocator used for memory management by this container. + template> + struct vector + { + //! The type of the elements contained in this vector. + using value_type = ValueType; + //! The allocator used by this vector for memory management. + using allocator_type = Allocator; + //! The type of all sizes used in and with this vector. + using size_type = std::size_t; + //! The type of the difference between two iterators. + using difference_type = std::ptrdiff_t; + //! The type of references to elements in this vector. + using reference = value_type &; + //! The type of references to constant elements in this vector. + using const_reference = value_type const &; + //! The type of pointers to elements in this vector. + using pointer = std::allocator_traits::pointer; + //! The type of pointers to constant elements in this vector. + using const_pointer = std::allocator_traits::const_pointer; + //! The type of iterators into this container. + using iterator = pointer; + //! The type of constant iterators into this container. + using const_iterator = const_pointer; + //! The type of reverse iterators into this container. + using reverse_iterator = std::reverse_iterator; + //! The type of constant reverse iterators into this container. + using const_reverse_iterator = std::reverse_iterator; + + //! Construct a new, empty vector. + constexpr vector() noexcept(std::is_nothrow_default_constructible_v) + : vector(allocator_type{}) + {} + + //! Construct a new, empty vector with a given allocator. + //! + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(allocator_type const & allocator) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{allocate_n(m_capacity)} + {} + + //! Construct a new vector and fill it with the given number of default constructed elements. + //! + //! @param count The number of element to create the vector with. + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(size_type count, allocator_type const & allocator = allocator_type{}) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{count} + , m_data{allocate_n(m_capacity)} + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i); + } + } + + //! Construct a new vector and fill it with the given number of copy constructed elements. + //! + //! @param count The number of element to create the vector with. + //! @param value The value to copy for each element + //! @param allocator The allocator to use in the vector. + constexpr vector(size_type count, const_reference value, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, value); + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam ForwardIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. + template + constexpr vector(ForwardIterator first, ForwardIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{static_cast(std::ranges::distance(first, last))} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + for (auto destination = m_data; first != last; ++first, ++destination) + { + std::allocator_traits::construct(m_allocator, destination, *first); + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam InputIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. + template + constexpr vector(InputIterator first, InputIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{} + { + while (first != last) + { + emplace_back(*first); + ++first; + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! + template + requires((std::ranges::forward_range || std::ranges::sized_range) && + kstd::bits::container_compatible_range) + constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) + : m_allocator{allocator} + , m_size{std::ranges::size(range)} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + auto destination = m_data; + for (auto && element : std::forward(range)) + { + std::allocator_traits::construct(m_allocator, destination++, + std::forward>(element)); + } + } + + //! Construct a new vector and initialize it's content by copying all elements from a given vector. + //! + //! @param other The source vector. + constexpr vector(vector const & other) + : m_allocator{other.m_allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data(allocate_n(m_capacity)) + { + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); + } + + //! Construct a new vector and initialize it's content by moving from a given vector. + //! + //! @param other The source vector. + constexpr vector(vector && other) noexcept + : m_allocator{std::move(other.m_allocator)} + , m_size{std::exchange(other.m_size, size_type{})} + , m_capacity(std::exchange(other.m_capacity, size_type{})) + , m_data(std::exchange(other.m_data, nullptr)) + {} + + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector const & other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data{allocate_n(m_capacity)} + { + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); + } + + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector && other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{} + , m_capacity{} + , m_data{} + { + if constexpr (!std::allocator_traits::is_always_equal::value) + { + if (m_allocator != other.m_allocator) + { + m_capacity = other.size(); + m_data = allocate_n(capacity()); + m_size = other.size(); + uninitialized_move_with_allocator(other.begin(), begin(), other.size()); + other.clear(); + return; + } + } + m_size = std::exchange(other.m_size, size_type{}); + m_capacity = std::exchange(other.m_capacity, size_type{}); + m_data = std::exchange(other.m_data, nullptr); + } + + //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. + //! + //! @param list The initializer list containing the source objects. + vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) + : vector{std::ranges::begin(list), std::ranges::end(list), allocator} + {} + + //! Destroy this vector. + constexpr ~vector() + { + clear_and_deallocate(); + } + + //! Replace the contents of this vector by the copying from the given source vector. + //! + //! @param other The source vector. + constexpr auto operator=(vector const & other) -> vector & + { + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value) + { + if (get_allocator() != other.get_allocator()) + { + clear_and_deallocate(); + } + m_allocator = other.get_allocator(); + } + + if (capacity() >= other.size()) + { + auto const overlap = std::min(m_size, other.m_size); + std::ranges::copy(other.begin(), other.begin() + overlap, begin()); + + if (m_size < other.m_size) + { + uninitialized_copy_with_allocator(other.begin() + size(), begin() + size(), other.m_size - size()); + } + else if (m_size > other.m_size) + { + destroy_n(begin() + other.size(), size() - other.size()); + } + } + else + { + auto new_data = allocate_n(other.size()); + uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); + clear_and_deallocate(); + m_data = new_data; + m_capacity = other.size(); + } + + m_size = other.size(); + return *this; + } + + //! Replace the contents fo this vector by moving from the given source. + //! + //! @param other The source vector. + constexpr auto operator=(vector && other) noexcept( + std::allocator_traits::propagate_on_container_move_assignment::value || + std::allocator_traits::is_always_equal::value) -> vector & + { + using std::swap; + + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + { + clear_and_deallocate(); + swap(m_allocator, other.m_allocator); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); + } + else if (m_allocator == other.m_allocator) + { + clear_and_deallocate(); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); + } + else + { + if (capacity() >= other.size()) + { + auto const overlap = std::min(size(), other.size()); + std::ranges::move(other.begin(), other.begin() + overlap, begin()); + + if (size() < other.size()) + { + uninitialized_move_with_allocator(other.begin() + size(), begin() + size(), other.size() - size()); + } + else if (size() > other.size()) + { + destroy_n(begin() + other.size(), size() - other.size()); + } + } + else + { + auto new_data = allocate_n(other.size()); + uninitialized_move_with_allocator(other.begin(), new_data, other.size()); + clear_and_deallocate(); + m_data = new_data; + m_capacity = other.m_size; + } + + other.destroy_n(other.begin(), other.size()); + m_size = std::exchange(other.m_size, size_type{}); + } + + return *this; + } + + //! Get a copy of the allocator associated with this vector. + [[nodiscard]] constexpr auto get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) + -> allocator_type + { + return m_allocator; + } + + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) -> reference + { + panic_if_out_of_bounds(index); + return (*this)[index]; + } + + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) const -> const_reference + { + panic_if_out_of_bounds(index); + return (*this)[index]; + } + + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) -> reference + { + return data()[index]; + } + + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) const -> const_reference + { + return data()[index]; + } + + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto front() -> reference + { + return *begin(); + } + + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto front() const -> const_reference + { + return *begin(); + } + + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() -> reference + { + return *rbegin(); + } + + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() const -> const_reference + { + return *rbegin(); + } + + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() noexcept -> pointer + { + return m_data; + } + + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() const noexcept -> const_pointer + { + return m_data; + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() noexcept -> iterator + { + return empty() ? end() : data(); + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return empty() ? end() : data(); + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() noexcept -> pointer + { + return capacity() ? data() + size() : nullptr; + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() const noexcept -> const_pointer + { + return capacity() ? data() + size() : nullptr; + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto cend() const noexcept -> const_pointer + { + return end(); + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator + { + return empty() ? rend() : reverse_iterator{end()}; + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator + { + return empty() ? rend() : const_reverse_iterator{end()}; + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator + { + return rbegin(); + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{begin()}; + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator + { + return rend(); + } + + //! Check whether this vector is empty. + [[nodiscard]] constexpr auto empty() const noexcept -> bool + { + return !size(); + } + + //! Get the number of elements present in this vector. + [[nodiscard]] constexpr auto size() const noexcept -> size_type + { + return m_size; + } + + //! Get the maximum possible number of element for this vector. + [[nodiscard]] constexpr auto max_size() const noexcept -> size_type + { + return std::allocator_traits::max_size(m_allocator); + } + + //! Reserve storage for at list the given number of elements. + constexpr auto reserve(size_type new_capacity) -> void + { + if (new_capacity <= capacity()) + { + return; + } + + if (new_capacity > max_size()) + { + kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); + } + + reallocate_exactly(new_capacity); + } + + //! Resize this vector to contain @p new_size elements. + constexpr auto resize(size_type new_size) -> void + { + resize(new_size, value_type{}); + } + + //! Resize this vector to contain @p new_size elements, filling new elements with @p value. + constexpr auto resize(size_type new_size, const_reference value) -> void + { + if (new_size < size()) + { + destroy_n(begin() + new_size, size() - new_size); + m_size = new_size; + return; + } + + if (new_size == size()) + { + return; + } + + if (new_size > max_size()) + { + kstd::os::panic("[kstd:vector] Tried to resize more space than theoretically possible."); + } + + if (new_size > capacity()) + { + reserve(new_size); + } + + for (auto i = size(); i < new_size; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, value); + } + + m_size = new_size; + } + + //! Get the number of element this vector has currently space for, including elements currently in this vector. + [[nodiscard]] constexpr auto capacity() const noexcept -> size_type + { + return m_capacity; + } + + //! Try to release unused storage space. + constexpr auto shrink_to_fit() -> void + { + if (m_size == m_capacity) + { + return; + } + + reallocate_exactly(m_size); + } + + //! Clear the contents of this vector. + constexpr auto clear() noexcept -> void + { + destroy_n(begin(), size()); + m_size = 0; + } + + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type const & value) -> iterator + { + return do_insert(position, value); + } + + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type && value) -> iterator + { + return do_insert(position, std::move(value)); + } + + //! Insert the element of a given range into the vector at a given position. + //! + //! @param range The source range to insert elements from. + //! @tparam SourceRange A container compatible range type. + template + requires requires(allocator_type allocator, pointer destination, SourceRange range) { + requires kstd::bits::container_compatible_range; + requires std::move_constructible; + requires std::is_move_assignable_v; + requires std::swappable; + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(misc-no-recursion) + constexpr auto insert_range(const_iterator position, SourceRange && range) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + + if (position == end()) + { + append_range(std::forward(range)); + return begin() + prefix_size; + } + + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + if (!number_of_elements) + { + return begin() + prefix_size; + } + + if (capacity() - size() < number_of_elements) + { + reserve(size() + std::max(size(), number_of_elements)); + } + + auto insert_position = begin() + prefix_size; + auto suffix_size = static_cast(std::ranges::distance(insert_position, end())); + + if (number_of_elements >= suffix_size) + { + uninitialized_move_with_allocator(insert_position, insert_position + number_of_elements, suffix_size); + auto result = std::ranges::copy_n(std::ranges::begin(range), suffix_size, insert_position); + uninitialized_copy_with_allocator(std::move(result.in), end(), number_of_elements - suffix_size); + } + else + { + uninitialized_move_with_allocator(end() - number_of_elements, end(), number_of_elements); + std::ranges::move_backward(insert_position, end() - number_of_elements, end()); + std::ranges::copy_n(std::ranges::begin(range), number_of_elements, insert_position); + } + + m_size += number_of_elements; + return insert_position; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + + return insert_range(begin() + prefix_size, remainder); + } + + template + constexpr auto emplace(const_iterator position, Args &&... args) -> iterator + { + auto prefix_size = std::ranges::distance(cbegin(), position); + if (position == cend()) + { + emplace_back(std::forward(args)...); + } + else if (m_capacity == m_size) + { + reallocate_and_insert(begin() + prefix_size, std::forward(args)...); + } + else + { + auto to_insert = value_type{std::forward(args)...}; + auto insert_position = begin() + prefix_size; + shift_back(insert_position); + *insert_position = std::move(to_insert); + ++m_size; + } + + return begin() + prefix_size; + } + + //! Erase an element at a given position. + //! + //! @note This function will panic if position == end() + //! + //! @param position An interator pointing to the element to delete + //! @return An iterator pointing to the element after the deleted element + constexpr auto erase(const_iterator position) -> iterator + { + if (position == end()) + { + os::panic("[kstd:vector] Attempted to erase end()!"); + } + + auto prefix_size = std::ranges::distance(cbegin(), position); + + std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); + std::allocator_traits::destroy(m_allocator, end() - 1); + --m_size; + + return begin() + prefix_size; + } + + //! Erase a range of elements from this vector. + //! + //! @param first The start of the range to erase. + //! @param last The end of the range to erase. + //! @return An iterator pointing to the element after the last deleted element. + constexpr auto erase(const_iterator first, const_iterator last) -> iterator + { + if (first == last) + { + return begin() + std::ranges::distance(cbegin(), first); + } + + auto prefix_size = std::ranges::distance(cbegin(), first); + auto element_count = std::ranges::distance(first, last); + + std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); + destroy_n(end() - element_count, element_count); + m_size -= element_count; + + return begin() + prefix_size; + } + + //! Append a given element to this vector via copy construction. + constexpr auto push_back(value_type const & value) -> void + { + emplace_back(value); + } + + //! Append a given element to this vector via move construction. + constexpr auto push_back(value_type && value) -> void + { + emplace_back(std::move(value)); + } + + //! Append a given element to this vector via direct construction. + template + constexpr auto emplace_back(Args &&... args) -> reference + { + if (m_capacity == m_size) + { + reallocate_and_insert(end(), std::forward(args)...); + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); + ++m_size; + } + return this->back(); + } + + //! Append the elements of a given range to this vector. + //! + //! @param range The range of elements to be appended. + //! @tparam SourceRange A container compatible range type. + template SourceRange> + requires requires(Allocator allocator, pointer destination, SourceRange range) { + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion) + constexpr auto append_range(SourceRange && range) -> void + { + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + + if (!capacity()) + { + reserve(number_of_elements); + } + + if (capacity() - size() >= number_of_elements) + { + uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements); + m_size += number_of_elements; + return; + } + + auto new_capacity = m_capacity + std::max(size(), number_of_elements); + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + + uninitialized_move_with_allocator(begin(), new_data, size()); + uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size + number_of_elements; + return; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + for (auto i = capacity() - size(); i > 0; --i, ++range_begin) + { + emplace_back(*range_begin); + } + + if (range_begin == range_end) + { + return; + } + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + append_range(remainder); + } + + //! Remove the last element of this vector. + //! + //! If this vector is empty, the behavior is undefined. + constexpr auto pop_back() -> void + { + --m_size; + std::allocator_traits::destroy(m_allocator, data() + size()); + } + + private: + //! Use the allocator of this vector to allocate enough space for the given number of elements. + //! + //! @param count The number of elements to allocate space for. + [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer + { + if (count) + { + return std::allocator_traits::allocate(m_allocator, count); + } + return nullptr; + } + + //! Clear this vector and release it's memory. + constexpr auto clear_and_deallocate() -> void + { + clear(); + deallocate(); + } + + //! Release the memory of this vector. + constexpr auto deallocate() + { + if (m_data) + { + std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); + m_capacity = 0; + m_size = 0; + m_data = nullptr; + } + } + + //! Insert an element into this vector at the given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + template + constexpr auto do_insert(const_iterator position, U && value) + { + auto prefix_size = std::ranges::distance(cbegin(), position); + if (position == cend()) + { + push_back(std::forward(value)); + } + else if (m_capacity == m_size) + { + reallocate_and_insert(begin() + prefix_size, std::forward(value)); + } + else if (&value >= cbegin() && &value < cend()) + { + auto temporary = std::forward(value); + shift_back(begin() + prefix_size); + *(begin() + prefix_size) = std::move(temporary); + ++m_size; + } + else + { + shift_back(begin() + prefix_size); + *(begin() + prefix_size) = std::forward(value); + ++m_size; + } + return begin() + prefix_size; + } + + //! Destroy a number of elements in this vector. + //! + //! @param first The start of the range of the elements to be destroyed. + //! @param count The number of elements to destroy. + constexpr auto destroy_n(iterator first, std::size_t count) -> void + { + std::ranges::for_each(first, first + count, [&](auto & element) { + std::allocator_traits::destroy(m_allocator, std::addressof(element)); + }); + } + + //! Panic the kernel if the given index is out of bounds. + //! + //! @param index The index to check. + constexpr auto panic_if_out_of_bounds(size_type index) const -> void + { + if (index >= m_size) + { + os::panic("[kstd:vector] Attempted to read element at invalid index"); + } + } + + //! Copy a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy + template + constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count) + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, to++, *from++); + } + } + + //! Move a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy + template + constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count) + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); + } + } + + //! Reallocate the storage space to be exactly as large as the given size. + constexpr auto reallocate_exactly(size_type new_capacity) -> void + { + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + uninitialized_move_with_allocator(begin(), new_data, old_size); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + + //! Shift all elements, starting the given position, one position back inside the vector. + constexpr auto shift_back(iterator starting_at) + { + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(starting_at, end() - 1, end()); + } + + //! Reallocate the storage of this vector and insert an element at the given position. + //! + //! @param position The position to insert the element at. + //! @param args The constructor arguments for the inserted element. + template + constexpr auto reallocate_and_insert(iterator position, Args &&... args) + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + + std::allocator_traits::construct(m_allocator, new_data + prefix_size, + std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size + 1; + } + + //! The allocator used by this vector. + [[no_unique_address]] allocator_type m_allocator{}; + + //! The number of elements in this vector. + size_type m_size{}; + + //! The number of elements this vector has room for. + size_type m_capacity{}; + + //! The pointer to the start of the memory managed by this vector. + value_type * m_data{}; + }; + + //! Check if the content of two vectors is equal. + template + constexpr auto operator==(vector const & lhs, vector const & rhs) -> bool + { + return std::ranges::equal(lhs, rhs); + } + + //! Perform a lexicographical comparison of the content of two vectors. + template + constexpr auto operator<=>(vector const & lhs, vector const & rhs) + -> decltype(std::declval() <=> std::declval()) + { + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(InputIterator, InputIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + + //! Deduction guide for vector construction from a range. + template>> + vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/vector.test.cpp b/libs/kstd/kstd/vector.test.cpp index 8bf8f79d..72c1127a 100644 --- a/libs/kstd/kstd/vector.test.cpp +++ b/libs/kstd/kstd/vector.test.cpp @@ -1,6 +1,6 @@ -#include +#include -#include +#include #include #include diff --git a/libs/multiboot2/multiboot2/information.hpp b/libs/multiboot2/multiboot2/information.hpp index f688fe5d..f12969e8 100644 --- a/libs/multiboot2/multiboot2/information.hpp +++ b/libs/multiboot2/multiboot2/information.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include // IWYU pragma: export #include // IWYU pragma: export diff --git a/libs/multiboot2/multiboot2/information/data.hpp b/libs/multiboot2/multiboot2/information/data.hpp index f39a6cb2..734bbc6f 100644 --- a/libs/multiboot2/multiboot2/information/data.hpp +++ b/libs/multiboot2/multiboot2/information/data.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include +#include #include #include -- cgit v1.2.3 From 14027b42ef30075dedbe4f08328da65e6cc308a9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 13:58:10 +0200 Subject: kstd: silence documentation warnings --- libs/kstd/kstd/print.hpp | 2 ++ libs/kstd/kstd/vector.hpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/libs/kstd/kstd/print.hpp b/libs/kstd/kstd/print.hpp index 6d00d83f..0d37efbe 100644 --- a/libs/kstd/kstd/print.hpp +++ b/libs/kstd/kstd/print.hpp @@ -26,6 +26,7 @@ namespace kstd //! @qualifier kernel-defined //! Format the given error string using the given arguments and print it to the currently active output device. //! + //! @param sink The output sink to print to. //! @param format The format string //! @param args The arguments to use to place in the format string's placeholders. template @@ -54,6 +55,7 @@ namespace kstd //! Format the given error string using the given arguments and print it, including a newline, to the currently active //! output device. //! + //! @param sink The output sink to print to. //! @param format The format string //! @param args The arguments template diff --git a/libs/kstd/kstd/vector.hpp b/libs/kstd/kstd/vector.hpp index 7cc14283..d6ad232a 100644 --- a/libs/kstd/kstd/vector.hpp +++ b/libs/kstd/kstd/vector.hpp @@ -107,6 +107,7 @@ namespace kstd //! @tparam ForwardIterator An iterator type used to describe the source range. //! @param first The start of the source range. //! @param last The end of the source range. + //! @param allocator The allocator to use in the vector. template constexpr vector(ForwardIterator first, ForwardIterator last, allocator_type const & allocator = @@ -127,6 +128,7 @@ namespace kstd //! @tparam InputIterator An iterator type used to describe the source range. //! @param first The start of the source range. //! @param last The end of the source range. + //! @param allocator The allocator to use in the vector. template constexpr vector(InputIterator first, InputIterator last, allocator_type const & allocator = @@ -228,6 +230,7 @@ namespace kstd //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. //! //! @param list The initializer list containing the source objects. + //! @param allocator The allocator to use in the vector. vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) : vector{std::ranges::begin(list), std::ranges::end(list), allocator} {} @@ -639,6 +642,7 @@ namespace kstd //! Insert the element of a given range into the vector at a given position. //! + //! @param position The position where to insert the elements. //! @param range The source range to insert elements from. //! @tparam SourceRange A container compatible range type. template -- cgit v1.2.3 From a950ab85c994040de2737f3545a0f8c59289e80b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 14:00:34 +0200 Subject: ide: remove unnecessary filetype overrides --- .nvim.lua | 11 ----------- .vscode/settings.json | 6 +----- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index 0f8f5119..ac52b6db 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -7,17 +7,6 @@ vim.opt.fixeol = false -- Enable Doxygen vim.g.load_doxygen_syntax = true --- C++ -vim.filetype.add({ - pattern = { - [".*/libs/kstd/kstd/.*"] = function(path, _) - if not path:match("%.[^/]+$") then - return "cpp" - end - end - } -}) - -- Debugging local dap = require("dap") local qemu_job_id = nil diff --git a/.vscode/settings.json b/.vscode/settings.json index 26154d1d..e83e19ac 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,11 +14,7 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { - "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S", - "*": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S" - }, - "files.associations": { - "**/kstd/kstd/**": "cpp", + "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.test.cpp, ${capture}.S", }, "[cpp]": { "editor.formatOnSave": true, -- cgit v1.2.3 From 58732f983b4c2b941af6581e0c8ccae32dda21ac Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 14:05:45 +0200 Subject: ide: fix test patterns --- .lazy.lua | 6 +++++- .vscode/settings.json | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.lazy.lua b/.lazy.lua index 76e8eb30..eca1d48a 100644 --- a/.lazy.lua +++ b/.lazy.lua @@ -68,7 +68,11 @@ return { nesting_rules = { ['cpp_under_hpp'] = { pattern = "(.*).hpp", - files = { "%1.cpp", "%1.test.cpp", "%1.tests.cpp", "%1.S" } + files = { "%1.cpp", "%1.tests.cpp", "%1.S" } + }, + ['tests_under_cpp'] = { + pattern = "(.*).cpp", + files = { "%1.tests.cpp" } }, }, }, diff --git a/.vscode/settings.json b/.vscode/settings.json index e83e19ac..7b537ecb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,8 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { - "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.test.cpp, ${capture}.S", + "*.hpp": "${capture}.cpp, ${capture}.tests.cpp, ${capture}.S", + "*.cpp": "${capture}.tests.cpp", }, "[cpp]": { "editor.formatOnSave": true, -- cgit v1.2.3 From 0c2d88cd36876df5eeb4ada2f9d339ce45b575be Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 14:09:57 +0200 Subject: chore: rename test source files --- .gdbinit | 1 + libs/acpi/CMakeLists.txt | 10 +- libs/acpi/acpi/common/table_header.test.cpp | 60 - libs/acpi/acpi/common/table_header.tests.cpp | 60 + libs/acpi/acpi/data/madt.test.cpp | 57 - libs/acpi/acpi/data/madt.tests.cpp | 57 + libs/acpi/acpi/data/rsdt.test.cpp | 48 - libs/acpi/acpi/data/rsdt.tests.cpp | 48 + libs/acpi/acpi/data/xsdt.test.cpp | 48 - libs/acpi/acpi/data/xsdt.tests.cpp | 48 + libs/acpi/acpi/pointers.test.cpp | 30 - libs/acpi/acpi/pointers.tests.cpp | 30 + libs/kstd/CMakeLists.txt | 12 +- libs/kstd/kstd/bits/observer_ptr.test.cpp | 360 --- libs/kstd/kstd/bits/observer_ptr.tests.cpp | 360 +++ libs/kstd/kstd/flat_map.test.cpp | 351 --- libs/kstd/kstd/flat_map.tests.cpp | 351 +++ libs/kstd/kstd/format.test.cpp | 170 -- libs/kstd/kstd/format.tests.cpp | 170 ++ libs/kstd/kstd/string.test.cpp | 3166 ------------------------ libs/kstd/kstd/string.tests.cpp | 3166 ++++++++++++++++++++++++ libs/kstd/kstd/test_support/os_panic.test.cpp | 15 - libs/kstd/kstd/test_support/os_panic.tests.cpp | 15 + libs/kstd/kstd/vector.test.cpp | 2003 --------------- libs/kstd/kstd/vector.tests.cpp | 2003 +++++++++++++++ 25 files changed, 6320 insertions(+), 6319 deletions(-) create mode 100644 .gdbinit delete mode 100644 libs/acpi/acpi/common/table_header.test.cpp create mode 100644 libs/acpi/acpi/common/table_header.tests.cpp delete mode 100644 libs/acpi/acpi/data/madt.test.cpp create mode 100644 libs/acpi/acpi/data/madt.tests.cpp delete mode 100644 libs/acpi/acpi/data/rsdt.test.cpp create mode 100644 libs/acpi/acpi/data/rsdt.tests.cpp delete mode 100644 libs/acpi/acpi/data/xsdt.test.cpp create mode 100644 libs/acpi/acpi/data/xsdt.tests.cpp delete mode 100644 libs/acpi/acpi/pointers.test.cpp create mode 100644 libs/acpi/acpi/pointers.tests.cpp delete mode 100644 libs/kstd/kstd/bits/observer_ptr.test.cpp create mode 100644 libs/kstd/kstd/bits/observer_ptr.tests.cpp delete mode 100644 libs/kstd/kstd/flat_map.test.cpp create mode 100644 libs/kstd/kstd/flat_map.tests.cpp delete mode 100644 libs/kstd/kstd/format.test.cpp create mode 100644 libs/kstd/kstd/format.tests.cpp delete mode 100644 libs/kstd/kstd/string.test.cpp create mode 100644 libs/kstd/kstd/string.tests.cpp delete mode 100644 libs/kstd/kstd/test_support/os_panic.test.cpp create mode 100644 libs/kstd/kstd/test_support/os_panic.tests.cpp delete mode 100644 libs/kstd/kstd/vector.test.cpp create mode 100644 libs/kstd/kstd/vector.tests.cpp diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 00000000..fd92c735 --- /dev/null +++ b/.gdbinit @@ -0,0 +1 @@ +skip -rfu Catch \ No newline at end of file diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 58661c5e..76941dcf 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -92,11 +92,11 @@ if(BUILD_TESTING) add_executable("acpi::tests" ALIAS "acpi_tests") target_sources("acpi_tests" PRIVATE - "acpi/common/table_header.test.cpp" - "acpi/data/madt.test.cpp" - "acpi/data/rsdt.test.cpp" - "acpi/data/xsdt.test.cpp" - "acpi/pointers.test.cpp" + "acpi/common/table_header.tests.cpp" + "acpi/data/madt.tests.cpp" + "acpi/data/rsdt.tests.cpp" + "acpi/data/xsdt.tests.cpp" + "acpi/pointers.tests.cpp" "acpi/test_data/tables.S" ) diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp deleted file mode 100644 index bbd42bd6..00000000 --- a/libs/acpi/acpi/common/table_header.test.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include - -#include - -#include - -#include - -SCENARIO("Common table header parsing", "[common_table_header]") -{ - GIVEN("A valid compiled table header") - { - auto data = acpi::test_data::tables::table_header(); - - WHEN("parsing the header") - { - auto header = reinterpret_cast(data.data()); - - THEN("the signature is correct") - { - REQUIRE(header->signature() == "TEST"); - } - - THEN("the revision is correct") - { - REQUIRE(header->revision() == 1); - } - - THEN("the length is correct") - { - REQUIRE(header->length() == kstd::type_size); - } - - THEN("the oem id is correct") - { - REQUIRE(header->oem_id() == "FEMO "); - } - - THEN("the oem table id is correct") - { - REQUIRE(header->oem_table_id() == "HDRTEST "); - } - - THEN("the oem revision is correct") - { - REQUIRE(header->oem_revision() == 1); - } - - THEN("the creator id is correct") - { - REQUIRE(header->creator_id() == "INTL"); - } - - THEN("the creator revision is non-zero") - { - REQUIRE(header->creator_revision() != 0); - } - } - } -} diff --git a/libs/acpi/acpi/common/table_header.tests.cpp b/libs/acpi/acpi/common/table_header.tests.cpp new file mode 100644 index 00000000..bbd42bd6 --- /dev/null +++ b/libs/acpi/acpi/common/table_header.tests.cpp @@ -0,0 +1,60 @@ +#include + +#include + +#include + +#include + +SCENARIO("Common table header parsing", "[common_table_header]") +{ + GIVEN("A valid compiled table header") + { + auto data = acpi::test_data::tables::table_header(); + + WHEN("parsing the header") + { + auto header = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(header->signature() == "TEST"); + } + + THEN("the revision is correct") + { + REQUIRE(header->revision() == 1); + } + + THEN("the length is correct") + { + REQUIRE(header->length() == kstd::type_size); + } + + THEN("the oem id is correct") + { + REQUIRE(header->oem_id() == "FEMO "); + } + + THEN("the oem table id is correct") + { + REQUIRE(header->oem_table_id() == "HDRTEST "); + } + + THEN("the oem revision is correct") + { + REQUIRE(header->oem_revision() == 1); + } + + THEN("the creator id is correct") + { + REQUIRE(header->creator_id() == "INTL"); + } + + THEN("the creator revision is non-zero") + { + REQUIRE(header->creator_revision() != 0); + } + } + } +} diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp deleted file mode 100644 index 9ec0180a..00000000 --- a/libs/acpi/acpi/data/madt.test.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include - -#include - -#include - -#include - -#include - -SCENARIO("MADT parsing", "[madt]") -{ - GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") - { - auto data = acpi::test_data::tables::basic_madt(); - - WHEN("parsing the table") - { - auto madt = reinterpret_cast(data.data()); - - THEN("the signature is correct") - { - REQUIRE(madt->signature() == "APIC"); - } - - THEN("validate returns true") - { - REQUIRE(madt->validate()); - } - - THEN("there is a single entry in the table") - { - REQUIRE(std::distance(madt->begin(), madt->end()) == 1); - } - - THEN("the LAPIC address is 0xfee00000") - { - REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); - } - - THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") - { - REQUIRE(madt->length().value == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); - } - - THEN("the first entry has type processor_local_apic") - { - REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); - } - - THEN("`only` can be used to get a view of all processor_local_apic entries") - { - REQUIRE(std::ranges::distance(madt->only()) == 1); - } - } - } -} diff --git a/libs/acpi/acpi/data/madt.tests.cpp b/libs/acpi/acpi/data/madt.tests.cpp new file mode 100644 index 00000000..9ec0180a --- /dev/null +++ b/libs/acpi/acpi/data/madt.tests.cpp @@ -0,0 +1,57 @@ +#include + +#include + +#include + +#include + +#include + +SCENARIO("MADT parsing", "[madt]") +{ + GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") + { + auto data = acpi::test_data::tables::basic_madt(); + + WHEN("parsing the table") + { + auto madt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(madt->signature() == "APIC"); + } + + THEN("validate returns true") + { + REQUIRE(madt->validate()); + } + + THEN("there is a single entry in the table") + { + REQUIRE(std::distance(madt->begin(), madt->end()) == 1); + } + + THEN("the LAPIC address is 0xfee00000") + { + REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); + } + + THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") + { + REQUIRE(madt->length().value == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); + } + + THEN("the first entry has type processor_local_apic") + { + REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); + } + + THEN("`only` can be used to get a view of all processor_local_apic entries") + { + REQUIRE(std::ranges::distance(madt->only()) == 1); + } + } + } +} diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp deleted file mode 100644 index 826d0b4f..00000000 --- a/libs/acpi/acpi/data/rsdt.test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include -#include - -#include - -#include - -#include - -SCENARIO("RSDT parsing", "[rsdt]") -{ - GIVEN("The basic compiled RSDT containing 8 table pointers") - { - auto data = acpi::test_data::tables::basic_rsdt(); - - WHEN("parsing the table") - { - auto rsdt = reinterpret_cast(data.data()); - - THEN("the signature is correct") - { - REQUIRE(rsdt->signature() == "RSDT"); - } - - THEN("validate returns true") - { - REQUIRE(rsdt->validate()); - } - - THEN("there are 8 entries in the table") - { - REQUIRE(std::distance(rsdt->begin(), rsdt->end()) == 8); - } - - THEN("the first entry has address 0x10") - { - REQUIRE(rsdt->cbegin()->address() == reinterpret_cast(0x10)); - } - - THEN("the length is sizeof(rsdt) + 8 * sizeof(rsdt_entry)") - { - REQUIRE(rsdt->length().value == sizeof(acpi::rsdt) + 8 * sizeof(acpi::rsdt_entry)); - } - } - } -} diff --git a/libs/acpi/acpi/data/rsdt.tests.cpp b/libs/acpi/acpi/data/rsdt.tests.cpp new file mode 100644 index 00000000..826d0b4f --- /dev/null +++ b/libs/acpi/acpi/data/rsdt.tests.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include + +#include + +#include + +#include + +SCENARIO("RSDT parsing", "[rsdt]") +{ + GIVEN("The basic compiled RSDT containing 8 table pointers") + { + auto data = acpi::test_data::tables::basic_rsdt(); + + WHEN("parsing the table") + { + auto rsdt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(rsdt->signature() == "RSDT"); + } + + THEN("validate returns true") + { + REQUIRE(rsdt->validate()); + } + + THEN("there are 8 entries in the table") + { + REQUIRE(std::distance(rsdt->begin(), rsdt->end()) == 8); + } + + THEN("the first entry has address 0x10") + { + REQUIRE(rsdt->cbegin()->address() == reinterpret_cast(0x10)); + } + + THEN("the length is sizeof(rsdt) + 8 * sizeof(rsdt_entry)") + { + REQUIRE(rsdt->length().value == sizeof(acpi::rsdt) + 8 * sizeof(acpi::rsdt_entry)); + } + } + } +} diff --git a/libs/acpi/acpi/data/xsdt.test.cpp b/libs/acpi/acpi/data/xsdt.test.cpp deleted file mode 100644 index 19d9bcdb..00000000 --- a/libs/acpi/acpi/data/xsdt.test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include -#include - -#include - -#include - -#include - -SCENARIO("XSDT parsing", "[xsdt]") -{ - GIVEN("The basic compiled XSDT containing 8 table pointers") - { - auto data = acpi::test_data::tables::basic_xsdt(); - - WHEN("parsing the table") - { - auto xsdt = reinterpret_cast(data.data()); - - THEN("the signature is correct") - { - REQUIRE(xsdt->signature() == "XSDT"); - } - - THEN("validate returns true") - { - REQUIRE(xsdt->validate()); - } - - THEN("there are 8 entries in the table") - { - REQUIRE(std::distance(xsdt->begin(), xsdt->end()) == 8); - } - - THEN("the first entry has address 0x10") - { - REQUIRE(xsdt->cbegin()->address() == reinterpret_cast(0x10)); - } - - THEN("the length is sizeof(xsdt) + 8 * sizeof(xsdt_entry)") - { - REQUIRE(xsdt->length().value == sizeof(acpi::xsdt) + 8 * sizeof(acpi::xsdt_entry)); - } - } - } -} diff --git a/libs/acpi/acpi/data/xsdt.tests.cpp b/libs/acpi/acpi/data/xsdt.tests.cpp new file mode 100644 index 00000000..19d9bcdb --- /dev/null +++ b/libs/acpi/acpi/data/xsdt.tests.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include + +#include + +#include + +#include + +SCENARIO("XSDT parsing", "[xsdt]") +{ + GIVEN("The basic compiled XSDT containing 8 table pointers") + { + auto data = acpi::test_data::tables::basic_xsdt(); + + WHEN("parsing the table") + { + auto xsdt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(xsdt->signature() == "XSDT"); + } + + THEN("validate returns true") + { + REQUIRE(xsdt->validate()); + } + + THEN("there are 8 entries in the table") + { + REQUIRE(std::distance(xsdt->begin(), xsdt->end()) == 8); + } + + THEN("the first entry has address 0x10") + { + REQUIRE(xsdt->cbegin()->address() == reinterpret_cast(0x10)); + } + + THEN("the length is sizeof(xsdt) + 8 * sizeof(xsdt_entry)") + { + REQUIRE(xsdt->length().value == sizeof(acpi::xsdt) + 8 * sizeof(acpi::xsdt_entry)); + } + } + } +} diff --git a/libs/acpi/acpi/pointers.test.cpp b/libs/acpi/acpi/pointers.test.cpp deleted file mode 100644 index d7b700d6..00000000 --- a/libs/acpi/acpi/pointers.test.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include - -#include - -#include -#include -#include - -SCENARIO("ACPI root pointer parsing", "[acpi]") -{ - GIVEN("A null-filled pointer") - { - auto data = std::array{}; - - WHEN("parsing the data") - { - auto rsdp = std::bit_cast(data); - - THEN("the signature is invalid") - { - REQUIRE(rsdp.signature() != "RSD PTR "); - } - - THEN("validate returns false") - { - REQUIRE_FALSE(rsdp.validate()); - } - } - } -} diff --git a/libs/acpi/acpi/pointers.tests.cpp b/libs/acpi/acpi/pointers.tests.cpp new file mode 100644 index 00000000..d7b700d6 --- /dev/null +++ b/libs/acpi/acpi/pointers.tests.cpp @@ -0,0 +1,30 @@ +#include + +#include + +#include +#include +#include + +SCENARIO("ACPI root pointer parsing", "[acpi]") +{ + GIVEN("A null-filled pointer") + { + auto data = std::array{}; + + WHEN("parsing the data") + { + auto rsdp = std::bit_cast(data); + + THEN("the signature is invalid") + { + REQUIRE(rsdp.signature() != "RSD PTR "); + } + + THEN("validate returns false") + { + REQUIRE_FALSE(rsdp.validate()); + } + } + } +} diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 002fb548..2c63f87d 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -74,12 +74,12 @@ if(BUILD_TESTING) add_executable("kstd::tests" ALIAS "kstd_tests") target_sources("kstd_tests" PRIVATE - "kstd/flat_map.test.cpp" - "kstd/format.test.cpp" - "kstd/vector.test.cpp" - "kstd/bits/observer_ptr.test.cpp" - "kstd/test_support/os_panic.test.cpp" - "kstd/string.test.cpp" + "kstd/flat_map.tests.cpp" + "kstd/format.tests.cpp" + "kstd/vector.tests.cpp" + "kstd/bits/observer_ptr.tests.cpp" + "kstd/test_support/os_panic.tests.cpp" + "kstd/string.tests.cpp" ) target_link_libraries("kstd_tests" PRIVATE diff --git a/libs/kstd/kstd/bits/observer_ptr.test.cpp b/libs/kstd/kstd/bits/observer_ptr.test.cpp deleted file mode 100644 index eca14ccd..00000000 --- a/libs/kstd/kstd/bits/observer_ptr.test.cpp +++ /dev/null @@ -1,360 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include - -namespace -{ - struct base - { - }; - - struct derived : base - { - }; - - struct element - { - int value{}; - - constexpr auto operator<=>(element const &) const noexcept = default; - }; -} // namespace - -SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto ptr = kstd::observer_ptr{}; - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("constructing from a nullptr") - { - auto ptr = kstd::observer_ptr{nullptr}; - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("constructing from a raw pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - - THEN("the observer pointer is not null") - { - REQUIRE(ptr); - } - - THEN("the observer pointer points to the correct object") - { - REQUIRE(&*ptr == &value); - } - } - - WHEN("copy constructing from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("copy constructing from an existing observer pointer with a compatible type") - { - auto value = derived{}; - auto ptr = kstd::observer_ptr(&value); - kstd::observer_ptr copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("copy assigning from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("move constructing from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = std::move(ptr); - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("move assigning from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = std::move(ptr); - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("constructing an observer pointer using make_observer") - { - auto value = 1; - auto ptr = kstd::make_observer(&value); - - THEN("the observer pointer points to the correct object") - { - REQUIRE(&*ptr == &value); - } - - THEN("the observe pointer has the correct element type") - { - STATIC_REQUIRE(std::is_same_v>); - } - } - } -} - -SCENARIO("Observer pointer modifiers", "[observer_ptr]") -{ - GIVEN("A non-null observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - - WHEN("releasing the observer pointer") - { - auto raw_ptr = ptr.release(); - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - - THEN("the returned pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("resetting the observer pointer to nullptr") - { - ptr.reset(); - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("resetting the observer pointer to a new object") - { - auto other_value = 2; - ptr.reset(&other_value); - - THEN("the observer pointer points to the new object") - { - REQUIRE(&*ptr == &other_value); - } - } - - WHEN("swapping it with another observer pointer") - { - auto other_value = 2; - auto other_ptr = kstd::observer_ptr{&other_value}; - ptr.swap(other_ptr); - - THEN("the observer pointer points to the other object") - { - REQUIRE(&*ptr == &other_value); - } - - THEN("the other observer pointer points to the original object") - { - REQUIRE(&*other_ptr == &value); - } - } - - WHEN("using namespace-level swap to swap it with another observer pointer") - { - using std::swap; - auto other_value = 2; - auto other_ptr = kstd::observer_ptr{&other_value}; - swap(ptr, other_ptr); - - THEN("the observer pointer points to the other object") - { - REQUIRE(&*ptr == &other_value); - } - - THEN("the other observer pointer points to the original object") - { - REQUIRE(&*other_ptr == &value); - } - } - } -} - -SCENARIO("Observer pointer observers", "[observer_ptr]") -{ - GIVEN("A non-null observer pointer") - { - auto value = element{1}; - auto ptr = kstd::observer_ptr{&value}; - - WHEN("getting the raw pointer") - { - auto raw_ptr = ptr.get(); - - THEN("the raw pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("dereferencing the observer pointer") - { - auto dereferenced = *ptr; - - THEN("the dereferenced value is the correct value") - { - REQUIRE(dereferenced == value); - } - } - - WHEN("writing through the observer pointer with the arrow operator") - { - ptr->value = 2; - - THEN("the value is updated") - { - REQUIRE(value.value == 2); - } - } - - WHEN("converting the observer pointer to a raw pointer") - { - auto raw_ptr = static_cast(ptr); - - THEN("the raw pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("checking the observer pointer as a boolean") - { - THEN("it returns true") - { - REQUIRE(static_cast(ptr)); - } - } - } - - GIVEN("A null observer pointer") - { - auto ptr = kstd::observer_ptr{}; - - WHEN("checking the observer pointer as a boolean") - { - THEN("it returns false") - { - REQUIRE_FALSE(static_cast(ptr)); - } - } - - WHEN("dereferencing the observer pointer") - { - THEN("the observer pointer panics") - { - REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); - } - } - - WHEN("writing through the observer pointer with the arrow operator") - { - THEN("the observer pointer panics") - { - REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); - } - } - } -} - -SCENARIO("Observer pointer comparisons", "[observer_ptr]") -{ - GIVEN("Observer pointers to elements of an array") - { - auto arr = std::array{1, 2}; - auto ptr1 = kstd::observer_ptr{&arr[0]}; - auto ptr2 = kstd::observer_ptr{&arr[1]}; - - WHEN("comparing the same observer pointer") - { - THEN("they are equal") - { - REQUIRE(ptr1 == ptr1); - REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); - } - } - - WHEN("comparing different observer pointers") - { - THEN("they are ordered correctly") - { - REQUIRE(ptr1 != ptr2); - REQUIRE(ptr1 < ptr2); - REQUIRE(ptr1 <= ptr2); - REQUIRE(ptr2 > ptr1); - REQUIRE(ptr2 >= ptr1); - REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); - REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); - } - } - } - - GIVEN("A null observer pointer") - { - auto ptr = kstd::observer_ptr{}; - - WHEN("comparing with another null observer pointer") - { - auto other_ptr = kstd::observer_ptr{}; - - THEN("they are equal") - { - REQUIRE(ptr == other_ptr); - REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); - } - } - } -} \ No newline at end of file diff --git a/libs/kstd/kstd/bits/observer_ptr.tests.cpp b/libs/kstd/kstd/bits/observer_ptr.tests.cpp new file mode 100644 index 00000000..eca14ccd --- /dev/null +++ b/libs/kstd/kstd/bits/observer_ptr.tests.cpp @@ -0,0 +1,360 @@ +#include +#include + +#include + +#include +#include +#include +#include + +namespace +{ + struct base + { + }; + + struct derived : base + { + }; + + struct element + { + int value{}; + + constexpr auto operator<=>(element const &) const noexcept = default; + }; +} // namespace + +SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto ptr = kstd::observer_ptr{}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a nullptr") + { + auto ptr = kstd::observer_ptr{nullptr}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a raw pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + THEN("the observer pointer is not null") + { + REQUIRE(ptr); + } + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + } + + WHEN("copy constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy constructing from an existing observer pointer with a compatible type") + { + auto value = derived{}; + auto ptr = kstd::observer_ptr(&value); + kstd::observer_ptr copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("constructing an observer pointer using make_observer") + { + auto value = 1; + auto ptr = kstd::make_observer(&value); + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + + THEN("the observe pointer has the correct element type") + { + STATIC_REQUIRE(std::is_same_v>); + } + } + } +} + +SCENARIO("Observer pointer modifiers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("releasing the observer pointer") + { + auto raw_ptr = ptr.release(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + + THEN("the returned pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("resetting the observer pointer to nullptr") + { + ptr.reset(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("resetting the observer pointer to a new object") + { + auto other_value = 2; + ptr.reset(&other_value); + + THEN("the observer pointer points to the new object") + { + REQUIRE(&*ptr == &other_value); + } + } + + WHEN("swapping it with another observer pointer") + { + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + ptr.swap(other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + + WHEN("using namespace-level swap to swap it with another observer pointer") + { + using std::swap; + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + swap(ptr, other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + } +} + +SCENARIO("Observer pointer observers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = element{1}; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("getting the raw pointer") + { + auto raw_ptr = ptr.get(); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("dereferencing the observer pointer") + { + auto dereferenced = *ptr; + + THEN("the dereferenced value is the correct value") + { + REQUIRE(dereferenced == value); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + ptr->value = 2; + + THEN("the value is updated") + { + REQUIRE(value.value == 2); + } + } + + WHEN("converting the observer pointer to a raw pointer") + { + auto raw_ptr = static_cast(ptr); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns true") + { + REQUIRE(static_cast(ptr)); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns false") + { + REQUIRE_FALSE(static_cast(ptr)); + } + } + + WHEN("dereferencing the observer pointer") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Observer pointer comparisons", "[observer_ptr]") +{ + GIVEN("Observer pointers to elements of an array") + { + auto arr = std::array{1, 2}; + auto ptr1 = kstd::observer_ptr{&arr[0]}; + auto ptr2 = kstd::observer_ptr{&arr[1]}; + + WHEN("comparing the same observer pointer") + { + THEN("they are equal") + { + REQUIRE(ptr1 == ptr1); + REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); + } + } + + WHEN("comparing different observer pointers") + { + THEN("they are ordered correctly") + { + REQUIRE(ptr1 != ptr2); + REQUIRE(ptr1 < ptr2); + REQUIRE(ptr1 <= ptr2); + REQUIRE(ptr2 > ptr1); + REQUIRE(ptr2 >= ptr1); + REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); + REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("comparing with another null observer pointer") + { + auto other_ptr = kstd::observer_ptr{}; + + THEN("they are equal") + { + REQUIRE(ptr == other_ptr); + REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map.test.cpp b/libs/kstd/kstd/flat_map.test.cpp deleted file mode 100644 index bfd02303..00000000 --- a/libs/kstd/kstd/flat_map.test.cpp +++ /dev/null @@ -1,351 +0,0 @@ -#include - -#include - -#include - -#include -#include -#include - -SCENARIO("Flat Map initialization and construction", "[flat_map]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto map = kstd::flat_map{}; - - THEN("the Flat Map does not contain elements") - { - REQUIRE_FALSE(map.contains(1)); - } - - THEN("the keys container is empty") - { - REQUIRE(map.keys().empty()); - } - - THEN("the values container is empty") - { - REQUIRE(map.values().empty()); - } - } - } -} - -SCENARIO("Flat Map modifiers", "[flat_map]") -{ - GIVEN("An empty Flat Map") - { - auto map = kstd::flat_map{}; - - WHEN("emplacing a new element") - { - auto [it, inserted] = map.emplace(1, 100); - - THEN("the map contains the new element") - { - REQUIRE(inserted); - REQUIRE(map.contains(1)); - } - } - - WHEN("emplacing an existing element") - { - map.emplace(1, 100); - auto [it, inserted] = map.emplace(1, 200); - - THEN("the map does not insert the duplicate") - { - REQUIRE_FALSE(inserted); - REQUIRE(map.contains(1)); - } - } - - AND_GIVEN("a populated Flat Map") - { - auto other = kstd::flat_map{}; - other.emplace(1, 10); - other.emplace(2, 20); - other.emplace(3, 30); - - WHEN("assigning the populated Flat Map to the empty one") - { - map = other; - - THEN("the elements are copied") - { - REQUIRE(map.at(1) == 10); - REQUIRE(map.at(2) == 20); - REQUIRE(map.at(3) == 30); - } - - THEN("the elements are still in the populated Flat Map") - { - REQUIRE(other.at(1) == 10); - REQUIRE(other.at(2) == 20); - REQUIRE(other.at(3) == 30); - } - } - } - } -} - -SCENARIO("Flat Map element access", "[flat_map]") -{ - GIVEN("A populated Flat Map") - { - auto map = kstd::flat_map{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("accessing an existing element with at()") - { - auto & val = map.at(2); - - THEN("it returns a reference to the mapped value") - { - REQUIRE(val == 20); - } - - THEN("the mapped value can be modified") - { - val = 200; - REQUIRE(map.at(2) == 200); - } - } - - WHEN("accessing a non-existent element with at()") - { - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); - } - } - } - - GIVEN("A const populated Flat Map") - { - auto map_builder = kstd::flat_map{}; - map_builder.emplace(1, 10); - map_builder.emplace(2, 20); - auto const map = map_builder; - - WHEN("accessing an existing element with const at()") - { - auto const & val = map.at(2); - - THEN("it returns a const reference to the mapped value") - { - REQUIRE(val == 20); - } - } - - WHEN("accessing a non-existent element with const at()") - { - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); - } - } - } -} - -SCENARIO("Flat Map iterators", "[flat_map]") -{ - GIVEN("A populated Flat Map") - { - auto map = kstd::flat_map{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("using forward iterators") - { - THEN("they navigate the elements in the correct forward order") - { - auto it = map.begin(); - REQUIRE(it != map.end()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it != map.end()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.end()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it == map.end()); - } - - THEN("const forward iterators provide correct access") - { - auto it = map.cbegin(); - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it == map.cend()); - } - - THEN("assignment through the proxy modifies the mapped value") - { - auto it = map.begin(); - - *it = std::pair{1, 100}; - - REQUIRE(it->second == 100); - REQUIRE(map.at(1) == 100); - } - - THEN("structured bindings evaluate correctly") - { - auto it = map.cbegin(); - - auto [key, value] = *it; - - REQUIRE(key == 1); - REQUIRE(value == 10); - - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - } - } - - WHEN("using reverse iterators") - { - THEN("they navigate the elements in the correct reverse order") - { - auto it = map.rbegin(); - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it == map.rend()); - } - - THEN("const reverse iterators provide correct access") - { - auto it = map.crbegin(); - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it == map.crend()); - } - } - } - - GIVEN("an empty Flat Map") - { - auto map = kstd::flat_map{}; - - WHEN("getting iterators") - { - THEN("begin() equals end() and cbegin() equals cend()") - { - REQUIRE(map.begin() == map.end()); - REQUIRE(map.cbegin() == map.cend()); - } - - THEN("rbegin() equals rend() and crbegin() equals crend()") - { - REQUIRE(map.rbegin() == map.rend()); - REQUIRE(map.crbegin() == map.crend()); - } - } - } -} - -SCENARIO("Flat Map heterogeneous element access", "[flat_map]") -{ - GIVEN("A populated Flat Map with a transparent comparator") - { - auto map = kstd::flat_map>{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("accessing an existing element with a different key type via at()") - { - long const key = 2L; - auto & val = map.at(key); - - THEN("it returns a reference to the mapped value") - { - REQUIRE(val == 20); - } - - THEN("the mapped value can be modified") - { - val = 200; - REQUIRE(map.at(2L) == 200); - } - } - - WHEN("accessing a non-existent element with a different key type via at()") - { - long const key = 4L; - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); - } - } - } - - GIVEN("A const populated Flat Map with a transparent comparator") - { - auto map_builder = kstd::flat_map>{}; - map_builder.emplace(1, 10); - map_builder.emplace(2, 20); - auto const map = map_builder; - - WHEN("accessing an existing element with a different key type via const at()") - { - long const key = 2L; - auto const & val = map.at(key); - - THEN("it returns a const reference to the mapped value") - { - REQUIRE(val == 20); - } - } - - WHEN("accessing a non-existent element with a different key type via const at()") - { - long const key = 4L; - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); - } - } - } -} diff --git a/libs/kstd/kstd/flat_map.tests.cpp b/libs/kstd/kstd/flat_map.tests.cpp new file mode 100644 index 00000000..bfd02303 --- /dev/null +++ b/libs/kstd/kstd/flat_map.tests.cpp @@ -0,0 +1,351 @@ +#include + +#include + +#include + +#include +#include +#include + +SCENARIO("Flat Map initialization and construction", "[flat_map]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto map = kstd::flat_map{}; + + THEN("the Flat Map does not contain elements") + { + REQUIRE_FALSE(map.contains(1)); + } + + THEN("the keys container is empty") + { + REQUIRE(map.keys().empty()); + } + + THEN("the values container is empty") + { + REQUIRE(map.values().empty()); + } + } + } +} + +SCENARIO("Flat Map modifiers", "[flat_map]") +{ + GIVEN("An empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("emplacing a new element") + { + auto [it, inserted] = map.emplace(1, 100); + + THEN("the map contains the new element") + { + REQUIRE(inserted); + REQUIRE(map.contains(1)); + } + } + + WHEN("emplacing an existing element") + { + map.emplace(1, 100); + auto [it, inserted] = map.emplace(1, 200); + + THEN("the map does not insert the duplicate") + { + REQUIRE_FALSE(inserted); + REQUIRE(map.contains(1)); + } + } + + AND_GIVEN("a populated Flat Map") + { + auto other = kstd::flat_map{}; + other.emplace(1, 10); + other.emplace(2, 20); + other.emplace(3, 30); + + WHEN("assigning the populated Flat Map to the empty one") + { + map = other; + + THEN("the elements are copied") + { + REQUIRE(map.at(1) == 10); + REQUIRE(map.at(2) == 20); + REQUIRE(map.at(3) == 30); + } + + THEN("the elements are still in the populated Flat Map") + { + REQUIRE(other.at(1) == 10); + REQUIRE(other.at(2) == 20); + REQUIRE(other.at(3) == 30); + } + } + } + } +} + +SCENARIO("Flat Map element access", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with at()") + { + auto & val = map.at(2); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2) == 200); + } + } + + WHEN("accessing a non-existent element with at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map") + { + auto map_builder = kstd::flat_map{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with const at()") + { + auto const & val = map.at(2); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with const at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Flat Map iterators", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = map.begin(); + REQUIRE(it != map.end()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = map.cbegin(); + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.cend()); + } + + THEN("assignment through the proxy modifies the mapped value") + { + auto it = map.begin(); + + *it = std::pair{1, 100}; + + REQUIRE(it->second == 100); + REQUIRE(map.at(1) == 100); + } + + THEN("structured bindings evaluate correctly") + { + auto it = map.cbegin(); + + auto [key, value] = *it; + + REQUIRE(key == 1); + REQUIRE(value == 10); + + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = map.rbegin(); + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = map.crbegin(); + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.crend()); + } + } + } + + GIVEN("an empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(map.begin() == map.end()); + REQUIRE(map.cbegin() == map.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(map.rbegin() == map.rend()); + REQUIRE(map.crbegin() == map.crend()); + } + } + } +} + +SCENARIO("Flat Map heterogeneous element access", "[flat_map]") +{ + GIVEN("A populated Flat Map with a transparent comparator") + { + auto map = kstd::flat_map>{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with a different key type via at()") + { + long const key = 2L; + auto & val = map.at(key); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2L) == 200); + } + } + + WHEN("accessing a non-existent element with a different key type via at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map with a transparent comparator") + { + auto map_builder = kstd::flat_map>{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with a different key type via const at()") + { + long const key = 2L; + auto const & val = map.at(key); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with a different key type via const at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } +} diff --git a/libs/kstd/kstd/format.test.cpp b/libs/kstd/kstd/format.test.cpp deleted file mode 100644 index d38a8107..00000000 --- a/libs/kstd/kstd/format.test.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include - -#include -#include -#include - -using namespace std::string_view_literals; - -SCENARIO("Formatting to a new string", "[format]") -{ - GIVEN("a format string without any placeholders") - { - constexpr auto fmt = "This is a test"sv; - - WHEN("calling format without any arguments.") - { - auto result = kstd::format(fmt); - - THEN("the result is the unmodified string") - { - REQUIRE(result == "This is a test"); - } - } - - WHEN("calling format with additional arguments") - { - auto result = kstd::format(fmt, 1, 2, 3); - - THEN("the result is the unmodified string") - { - REQUIRE(result == "This is a test"); - } - } - } - - GIVEN("a format string with placeholders") - { - constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; - - WHEN("calling format with the same number of arguments as there are placeholders") - { - auto result = kstd::format(fmt, 1, true, 'a'); - - THEN("the result is the formatted string") - { - REQUIRE(result == "Here are some placeholders: 1 true a"); - } - } - - WHEN("calling format with too many arguments") - { - auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6); - - THEN("the result is the formatted string") - { - REQUIRE(result == "Here are some placeholders: 2 false b"); - } - } - } -} - -SCENARIO("Formatting to an output iterator", "[format]") -{ - auto buffer = std::ostringstream{}; - - GIVEN("a format string without any placeholders") - { - constexpr auto fmt = "This is a test"sv; - - WHEN("calling format without any arguments.") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt); - - THEN("the unmodified string is written to the iterator") - { - REQUIRE(buffer.str() == "This is a test"); - } - } - - WHEN("calling format with additional arguments") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, 2, 3); - - THEN("the unmodified string is written to the iterator") - { - REQUIRE(buffer.str() == "This is a test"); - } - } - } - - GIVEN("a format string with placeholders") - { - constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; - - WHEN("calling format with the same number of arguments as there are placeholders") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, true, -100); - - THEN("the formatted string is written to the iterator") - { - REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100"); - } - } - - WHEN("calling format with too many arguments") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 2, false, -200, 4, 5, 6); - - THEN("the formatted string is written to the iterator") - { - REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200"); - } - } - } -} - -SCENARIO("Determining formatted size") -{ - GIVEN("a format string without any placeholders") - { - constexpr auto fmt = "This is a test"sv; - - WHEN("calling formatted_size without any arguments") - { - auto const size = kstd::formatted_size(fmt); - - THEN("the result is equal to the length of the format string") - { - REQUIRE(size == fmt.size()); - } - } - - WHEN("calling format with additional arguments") - { - auto const size = kstd::formatted_size(fmt, 1, 2, 3); - - THEN("the result is equal to the length of the format string") - { - REQUIRE(size == fmt.size()); - } - } - } - - GIVEN("a format string with placeholders") - { - constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; - - WHEN("calling format with the same number of arguments as there are placeholders") - { - auto result = kstd::formatted_size(fmt, 1, true, 'a'); - - THEN("the result is the formatted length") - { - REQUIRE(result == std::string_view{"Here are some placeholders: 1 true a"}.size()); - } - } - - WHEN("calling format with too many arguments") - { - auto result = kstd::formatted_size(fmt, 2, false, 'b', 4, 5, 6); - - THEN("the result is the formatted length") - { - REQUIRE(result == std::string_view{"Here are some placeholders: 2 false b"}.size()); - } - } - } -} \ No newline at end of file diff --git a/libs/kstd/kstd/format.tests.cpp b/libs/kstd/kstd/format.tests.cpp new file mode 100644 index 00000000..d38a8107 --- /dev/null +++ b/libs/kstd/kstd/format.tests.cpp @@ -0,0 +1,170 @@ +#include + +#include + +#include +#include +#include + +using namespace std::string_view_literals; + +SCENARIO("Formatting to a new string", "[format]") +{ + GIVEN("a format string without any placeholders") + { + constexpr auto fmt = "This is a test"sv; + + WHEN("calling format without any arguments.") + { + auto result = kstd::format(fmt); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + auto result = kstd::format(fmt, 1, 2, 3); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + auto result = kstd::format(fmt, 1, true, 'a'); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 1 true a"); + } + } + + WHEN("calling format with too many arguments") + { + auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 2 false b"); + } + } + } +} + +SCENARIO("Formatting to an output iterator", "[format]") +{ + auto buffer = std::ostringstream{}; + + GIVEN("a format string without any placeholders") + { + constexpr auto fmt = "This is a test"sv; + + WHEN("calling format without any arguments.") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, 2, 3); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, true, -100); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 2, false, -200, 4, 5, 6); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200"); + } + } + } +} + +SCENARIO("Determining formatted size") +{ + GIVEN("a format string without any placeholders") + { + constexpr auto fmt = "This is a test"sv; + + WHEN("calling formatted_size without any arguments") + { + auto const size = kstd::formatted_size(fmt); + + THEN("the result is equal to the length of the format string") + { + REQUIRE(size == fmt.size()); + } + } + + WHEN("calling format with additional arguments") + { + auto const size = kstd::formatted_size(fmt, 1, 2, 3); + + THEN("the result is equal to the length of the format string") + { + REQUIRE(size == fmt.size()); + } + } + } + + GIVEN("a format string with placeholders") + { + constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + auto result = kstd::formatted_size(fmt, 1, true, 'a'); + + THEN("the result is the formatted length") + { + REQUIRE(result == std::string_view{"Here are some placeholders: 1 true a"}.size()); + } + } + + WHEN("calling format with too many arguments") + { + auto result = kstd::formatted_size(fmt, 2, false, 'b', 4, 5, 6); + + THEN("the result is the formatted length") + { + REQUIRE(result == std::string_view{"Here are some placeholders: 2 false b"}.size()); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp deleted file mode 100644 index b8fa1139..00000000 --- a/libs/kstd/kstd/string.test.cpp +++ /dev/null @@ -1,3166 +0,0 @@ -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -TEST_CASE("String static interface") -{ - REQUIRE(std::is_same_v>); - REQUIRE(std::is_same_v); - REQUIRE(std::is_same_v>); - REQUIRE(std::is_same_v>::size_type>); - REQUIRE(std::is_same_v>::difference_type>); - REQUIRE(std::is_same_v); - REQUIRE(std::is_same_v); - REQUIRE(std::is_same_v>::pointer>); - REQUIRE(std::is_same_v>::const_pointer>); - REQUIRE(std::is_same_v); - REQUIRE(std::is_same_v); - REQUIRE(std::is_same_v>); - REQUIRE(std::is_same_v>); - - REQUIRE(kstd::string::npos == kstd::string::size_type(-1)); -} - -SCENARIO("String initialization and construction", "[string]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto s = kstd::string{}; - - THEN("the string is empty") - { - REQUIRE(s.empty()); - } - - THEN("the size is equal to zero") - { - REQUIRE(s.size() == 0); - } - - THEN("the length is equal to zero") - { - REQUIRE(s.length() == 0); - } - - THEN("at raises a panic") - { - REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); - } - - THEN("data returns a pointer poiting to a null byte") - { - REQUIRE(*s.data() == '\0'); - } - - THEN("c_str returns a pointer poiting to a null byte") - { - REQUIRE(*s.c_str() == '\0'); - } - - THEN("the string is convertible to an empty string view") - { - auto view = static_cast(s); - REQUIRE(view.empty()); - } - } - - WHEN("constructing with 10 copies of the letter 'a'") - { - auto s = kstd::string{10, 'a'}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 10") - { - REQUIRE(s.size() == 10); - } - - THEN("the length is equal to 10") - { - REQUIRE(s.length() == 10); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns a") - { - REQUIRE(s.at(s.size() - 1) == 'a'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns a") - { - REQUIRE(s[s.size() - 1] == 'a'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns a") - { - REQUIRE(s.back() == 'a'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 10") - { - REQUIRE(std::strlen(s.c_str()) == 10); - } - - THEN("the string converts to a string view of length 10") - { - auto view = static_cast(s); - REQUIRE(view.length() == 10); - } - } - - WHEN("constructing with 20 copies of the letter 'a'") - { - auto s = kstd::string{20, 'a'}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns a") - { - REQUIRE(s.at(s.size() - 1) == 'a'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns a") - { - REQUIRE(s[s.size() - 1] == 'a'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns a") - { - REQUIRE(s.back() == 'a'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing with an input iterator pair of 10 characters") - { - auto source = std::istringstream{"abcdefghij"}; - auto s = kstd::string{std::istream_iterator{source}, std::istream_iterator{}}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 10") - { - REQUIRE(s.size() == 10); - } - - THEN("the length is equal to 10") - { - REQUIRE(s.length() == 10); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns j") - { - REQUIRE(s.at(s.size() - 1) == 'j'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns j") - { - REQUIRE(s[s.size() - 1] == 'j'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns j") - { - REQUIRE(s.back() == 'j'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 10") - { - REQUIRE(std::strlen(s.c_str()) == 10); - } - - THEN("the string converts to a string view of length 10") - { - auto view = static_cast(s); - REQUIRE(view.length() == 10); - } - } - - WHEN("constructing with an input iterator pair of 20 characters") - { - auto source = std::istringstream{"abcdefghijABCDEFGHIJ"}; - auto s = kstd::string{std::istream_iterator{source}, std::istream_iterator{}}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is greater than or equal to 20") - { - REQUIRE(s.capacity() >= 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing with a forward iterator pair of 10 characters") - { - auto source = std::forward_list{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; - auto s = kstd::string{source.cbegin(), source.cend()}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 10") - { - REQUIRE(s.size() == 10); - } - - THEN("the length is equal to 10") - { - REQUIRE(s.length() == 10); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns j") - { - REQUIRE(s.at(s.size() - 1) == 'j'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns j") - { - REQUIRE(s[s.size() - 1] == 'j'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns j") - { - REQUIRE(s.back() == 'j'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 10") - { - REQUIRE(std::strlen(s.c_str()) == 10); - } - - THEN("the string converts to a string view of length 10") - { - auto view = static_cast(s); - REQUIRE(view.length() == 10); - } - } - - WHEN("constructing with a forward iterator pair of 20 characters") - { - auto source = std::forward_list{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; - auto s = kstd::string{source.cbegin(), source.cend()}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing from a null pointer with zero size") - { - auto s = kstd::string{static_cast(nullptr), 0}; - - THEN("the string is empty") - { - REQUIRE(s.empty()); - } - - THEN("the size is equal to 0") - { - REQUIRE(s.size() == 0); - } - - THEN("the length is equal to 0") - { - REQUIRE(s.length() == 0); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at raises a panic") - { - REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); - } - - THEN("data returns a pointer poiting to a null byte") - { - REQUIRE(*s.data() == '\0'); - } - - THEN("c_str returns a pointer poiting to a null byte") - { - REQUIRE(*s.c_str() == '\0'); - } - - THEN("the string is convertible to an empty string view") - { - auto view = static_cast(s); - REQUIRE(view.empty()); - } - } - - WHEN("constructing from a from a null pointer and non-zero size") - { - THEN("a panic is raised") - { - REQUIRE_THROWS_AS((kstd::string{static_cast(nullptr), 1}), kstd::tests::os_panic); - } - } - - WHEN("constructing from a non-null pointer and zero size") - { - auto ptr = "abcd"; - auto s = kstd::string{ptr, 0}; - - THEN("the string is empty") - { - REQUIRE(s.empty()); - } - - THEN("the size is equal to 0") - { - REQUIRE(s.size() == 0); - } - - THEN("the length is equal to 0") - { - REQUIRE(s.length() == 0); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at raises a panic") - { - REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); - } - - THEN("data returns a pointer poiting to a null byte") - { - REQUIRE(*s.data() == '\0'); - } - - THEN("c_str returns a pointer poiting to a null byte") - { - REQUIRE(*s.c_str() == '\0'); - } - - THEN("the string is convertible to an empty string view") - { - auto view = static_cast(s); - REQUIRE(view.empty()); - } - } - - WHEN("constructing from a non-null pointer and a size of 4") - { - auto ptr = "abcd"; - auto s = kstd::string{ptr, 4}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 4") - { - REQUIRE(s.size() == 4); - } - - THEN("the length is equal to 4") - { - REQUIRE(s.length() == 4); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns d") - { - REQUIRE(s.at(s.size() - 1) == 'd'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns d") - { - REQUIRE(s[s.size() - 1] == 'd'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns d") - { - REQUIRE(s.back() == 'd'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 4") - { - REQUIRE(std::strlen(s.c_str()) == 4); - } - - THEN("the string converts to a string view of length 4") - { - auto view = static_cast(s); - REQUIRE(view.length() == 4); - } - } - - WHEN("constructing from a non-null pointer and a size of 20") - { - auto ptr = "abcdefghijABCDEFGHIJ"; - auto s = kstd::string{ptr, 20}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing from a C-style string of length 4") - { - auto ptr = "abcd"; - auto s = kstd::string{ptr}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 4") - { - REQUIRE(s.size() == 4); - } - - THEN("the length is equal to 4") - { - REQUIRE(s.length() == 4); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns d") - { - REQUIRE(s.at(s.size() - 1) == 'd'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns d") - { - REQUIRE(s[s.size() - 1] == 'd'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns d") - { - REQUIRE(s.back() == 'd'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 4") - { - REQUIRE(std::strlen(s.c_str()) == 4); - } - - THEN("the string converts to a string view of length 4") - { - auto view = static_cast(s); - REQUIRE(view.length() == 4); - } - } - - WHEN("constructing from a C-style string of length 20") - { - auto ptr = "abcdefghijABCDEFGHIJ"; - auto s = kstd::string{ptr}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing from a string view of length 4") - { - using namespace std::string_view_literals; - - auto view = "abcd"sv; - auto s = kstd::string{view}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 4") - { - REQUIRE(s.size() == 4); - } - - THEN("the length is equal to 4") - { - REQUIRE(s.length() == 4); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns d") - { - REQUIRE(s.at(s.size() - 1) == 'd'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns d") - { - REQUIRE(s[s.size() - 1] == 'd'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns d") - { - REQUIRE(s.back() == 'd'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 4") - { - REQUIRE(std::strlen(s.c_str()) == 4); - } - - THEN("the string converts to a string view of length 4") - { - auto view = static_cast(s); - REQUIRE(view.length() == 4); - } - } - - WHEN("constructing from a string view of length 20") - { - using namespace std::string_view_literals; - - auto view = "abcdefghijABCDEFGHIJ"sv; - auto s = kstd::string{view}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - - THEN("the capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("at(0) returns a") - { - REQUIRE(s.at(0) == 'a'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns a") - { - REQUIRE(s[0] == 'a'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an a") - { - REQUIRE(*s.data() == 'a'); - } - - THEN("c_str returns a pointer pointing to an a") - { - REQUIRE(*s.c_str() == 'a'); - } - - THEN("c_str points to the start of a c-string of length 20") - { - REQUIRE(std::strlen(s.c_str()) == 20); - } - - THEN("the string converts to a string view of length 20") - { - auto view = static_cast(s); - REQUIRE(view.length() == 20); - } - } - - WHEN("constructing from a substring of a string view of length 4") - { - using namespace std::string_view_literals; - - auto view = "abcd"sv; - auto s = kstd::string{view, 1, 4}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 3") - { - REQUIRE(s.size() == 3); - } - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - - THEN("the capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("at(0) returns b") - { - REQUIRE(s.at(0) == 'b'); - } - - THEN("at(size() - 1) returns d") - { - REQUIRE(s.at(s.size() - 1) == 'd'); - } - - THEN("[0] returns b") - { - REQUIRE(s[0] == 'b'); - } - - THEN("[size() - 1] returns d") - { - REQUIRE(s[s.size() - 1] == 'd'); - } - - THEN("front returns b") - { - REQUIRE(s.front() == 'b'); - } - - THEN("back returns d") - { - REQUIRE(s.back() == 'd'); - } - - THEN("data returns a pointer pointing to an b") - { - REQUIRE(*s.data() == 'b'); - } - - THEN("c_str returns a pointer pointing to an b") - { - REQUIRE(*s.c_str() == 'b'); - } - - THEN("c_str points to the start of a c-string of length 3") - { - REQUIRE(std::strlen(s.c_str()) == 3); - } - - THEN("the string converts to a string view of length 3") - { - auto view = static_cast(s); - REQUIRE(view.length() == 3); - } - } - - WHEN("constructing from a substring of a string view of length 20") - { - using namespace std::string_view_literals; - - auto view = "abcdefghijABCDEFGHIJ"sv; - auto s = kstd::string{view, 1, 20}; - - THEN("the string is not empty") - { - REQUIRE_FALSE(s.empty()); - } - - THEN("the size is equal to 19") - { - REQUIRE(s.size() == 19); - } - - THEN("the length is equal to 19") - { - REQUIRE(s.length() == 19); - } - - THEN("the capacity is equal to 19") - { - REQUIRE(s.capacity() == 19); - } - - THEN("at(0) returns b") - { - REQUIRE(s.at(0) == 'b'); - } - - THEN("at(size() - 1) returns J") - { - REQUIRE(s.at(s.size() - 1) == 'J'); - } - - THEN("[0] returns b") - { - REQUIRE(s[0] == 'b'); - } - - THEN("[size() - 1] returns J") - { - REQUIRE(s[s.size() - 1] == 'J'); - } - - THEN("front returns b") - { - REQUIRE(s.front() == 'b'); - } - - THEN("back returns J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("data returns a pointer pointing to an b") - { - REQUIRE(*s.data() == 'b'); - } - - THEN("c_str returns a pointer pointing to an b") - { - REQUIRE(*s.c_str() == 'b'); - } - - THEN("c_str points to the start of a c-string of length 19") - { - REQUIRE(std::strlen(s.c_str()) == 19); - } - - THEN("the string converts to a string view of length 19") - { - auto view = static_cast(s); - REQUIRE(view.length() == 19); - } - } - } - - GIVEN("An existing short string, of length 10") - { - auto other = kstd::string{"abcdefghij"}; - - WHEN("constructing by copy") - { - auto s = other; - - THEN("the sizes are identical") - { - REQUIRE(s.size() == other.size()); - } - - THEN("the capacities are idendtical") - { - REQUIRE(s.capacity() == other.capacity()); - } - - THEN("the underlying strings are identical") - { - REQUIRE_FALSE(std::strcmp(s.data(), other.data())); - } - } - - WHEN("constructing by move") - { - auto s = std::move(other); - - THEN("size is equal to 10") - { - REQUIRE(s.size() == 10); - } - - THEN("capacity is equal to 15") - { - REQUIRE(s.capacity() == 15); - } - - THEN("front() return an a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back() return a j") - { - REQUIRE(s.back() == 'j'); - } - - THEN("The moved from object's size is 0") - { - REQUIRE(other.size() == 0); - } - - THEN("The moved from object's capacity is 15") - { - REQUIRE(other.capacity() == 15); - } - - THEN("c_str() on the moved from object points to a null byte") - { - REQUIRE(*other.c_str() == '\0'); - } - } - } - - GIVEN("An existing short string, of length 20") - { - auto other = kstd::string{"abcdefghijABCDEFGHIJ"}; - - WHEN("constructing by copy") - { - auto s = other; - - THEN("the sizes are identical") - { - REQUIRE(s.size() == other.size()); - } - - THEN("the capacities are idendtical") - { - REQUIRE(s.capacity() == other.capacity()); - } - - THEN("the underlying strings are identical") - { - REQUIRE_FALSE(std::strcmp(s.data(), other.data())); - } - } - - WHEN("constructing by move") - { - auto s = std::move(other); - - THEN("size is equal to 20") - { - REQUIRE(s.size() == 20); - } - - THEN("capacity is equal to 20") - { - REQUIRE(s.capacity() == 20); - } - - THEN("front() return an a") - { - REQUIRE(s.front() == 'a'); - } - - THEN("back() return a J") - { - REQUIRE(s.back() == 'J'); - } - - THEN("The moved from object's size is 0") - { - REQUIRE(other.size() == 0); - } - - THEN("The moved from object's capacity is 15") - { - REQUIRE(other.capacity() == 15); - } - - THEN("c_str() on the moved from object points to a null byte") - { - REQUIRE(*other.c_str() == '\0'); - } - } - } -} - -SCENARIO("String assignment", "[string]") -{ - GIVEN("Two short strings") - { - auto s1 = kstd::string{"abcd"}; - auto s2 = kstd::string{"defghij"}; - - WHEN("copy assigning the shorter to the longer one") - { - s2 = s1; - - THEN("the new length of the longer one is equal to the length of the shorter one") - { - REQUIRE(s2.length() == s1.length()); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); - } - } - - WHEN("copy assigning the longer to the shorter one") - { - s1 = s2; - - THEN("the new length of the shorter one is equal to the length of the longer one") - { - REQUIRE(s1.length() == s2.length()); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); - } - } - - WHEN("move assigning the shorter to the longer one") - { - s2 = std::move(s1); - - THEN("the new length of the longer one is equal to the original length of the shorter one") - { - REQUIRE(s2.length() == 4); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s1.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s1.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s2.capacity() == 15); - } - } - - WHEN("move assigning the longer to the shorter one") - { - s1 = std::move(s2); - - THEN("the new length of the shorter one is equal to the original length of the longer one") - { - REQUIRE(s1.length() == 7); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s2.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s2.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s1.capacity() == 15); - } - } - } - - GIVEN("One short and one long string") - { - auto s1 = kstd::string{"abcd"}; - auto s2 = kstd::string{"ABCDEFGHIJabcdefghij"}; - - WHEN("copy assigning the shorter to the longer one") - { - s2 = s1; - - THEN("the new length of the longer one is equal to the length of the shorter one") - { - REQUIRE(s2.length() == s1.length()); - } - - THEN("the new capacity of the longer one is 15") - { - REQUIRE(s2.capacity() == 15); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); - } - } - - WHEN("copy assigning the longer to the shorter one") - { - s1 = s2; - - THEN("the new length of the shorter one is equal to the length of the longer one") - { - REQUIRE(s1.length() == s2.length()); - } - - THEN("the new capacity of the shorter one is 20") - { - REQUIRE(s1.capacity() == 20); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); - } - } - - WHEN("move assigning the shorter to the longer one") - { - s2 = std::move(s1); - - THEN("the new length of the longer one is equal to the original length of the shorter one") - { - REQUIRE(s2.length() == 4); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s1.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s1.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s2.capacity() == 15); - } - } - - WHEN("move assigning the longer to the shorter one") - { - s1 = std::move(s2); - - THEN("the new length of the shorter one is equal to the original length of the longer one") - { - REQUIRE(s1.length() == 20); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s2.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s2.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s1.capacity() == 20); - } - } - } - - GIVEN("Two long strings") - { - auto s1 = kstd::string{"abcdefghijABCDEFGH"}; - auto s2 = kstd::string{"ABCDEFGHIJabcdefghij"}; - - WHEN("copy assigning the shorter to the longer one") - { - s2 = s1; - - THEN("the new length of the longer one is equal to the length of the shorter one") - { - REQUIRE(s2.length() == s1.length()); - } - - THEN("the new capacity of the longer one is still 20") - { - REQUIRE(s2.capacity() == 20); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); - } - } - - WHEN("copy assigning the longer to the shorter one") - { - s1 = s2; - - THEN("the new length of the shorter one is equal to the length of the longer one") - { - REQUIRE(s1.length() == s2.length()); - } - - THEN("the new capacity of the shorter one is 20") - { - REQUIRE(s1.capacity() == 20); - } - - THEN("the underlying data of the two is identical") - { - REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); - } - } - - WHEN("move assigning the shorter to the longer one") - { - s2 = std::move(s1); - - THEN("the new length of the longer one is equal to the original length of the shorter one") - { - REQUIRE(s2.length() == 18); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s1.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s1.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s2.capacity() == 18); - } - } - - WHEN("move assigning the longer to the shorter one") - { - s1 = std::move(s2); - - THEN("the new length of the shorter one is equal to the original length of the longer one") - { - REQUIRE(s1.length() == 20); - } - - THEN("the length of the moved from one is 0") - { - REQUIRE(s2.length() == 0); - } - - THEN("the capacity of the moved from one is 15") - { - REQUIRE(s2.capacity() == 15); - } - - THEN("the capacity of the moved to one is 15") - { - REQUIRE(s1.capacity() == 20); - } - } - } - - GIVEN("An short string") - { - auto s = kstd::string{"abcd"}; - - WHEN("assigning a new, shorter c-style string of length 3") - { - s = "def"; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning a new, longer, c-style string of length 5") - { - s = "defgh"; - - THEN("the length is equal to 5") - { - REQUIRE(s.length() == 5); - } - } - - WHEN("assigning a new, longer, c-style string of length 20") - { - s = "ABCDEFGHIJabcdefghij"; - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - } - - WHEN("assigning a single character") - { - s = 'z'; - - THEN("the length is equal to 1") - { - REQUIRE(s.length() == 1); - } - } - - WHEN("assigning an shorter initializer list of length 3") - { - s = {'d', 'e', 'f'}; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning an longer initializer list of length 5") - { - s = {'d', 'e', 'f', 'g', 'h'}; - - THEN("the length is equal to 5") - { - REQUIRE(s.length() == 5); - } - } - - WHEN("assigning an longer initializer list of length 20") - { - s = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - } - - WHEN("assigning from a shorter string view of length 3") - { - using namespace std::string_view_literals; - - s = "def"sv; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning from a longer string view of length 5") - { - using namespace std::string_view_literals; - - s = "defgh"sv; - - THEN("the length is equal to 5") - { - REQUIRE(s.length() == 5); - } - } - - WHEN("assigning from a longer string view of length 20") - { - using namespace std::string_view_literals; - - s = "ABCDEFGHIJabcdefghij"sv; - - THEN("the length is equal to 20") - { - REQUIRE(s.length() == 20); - } - } - } - - GIVEN("An long string") - { - auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; - - WHEN("assigning a new, shorter c-style string of length 3") - { - s = "def"; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning a new, longer, c-style string of length 22") - { - s = "ABCDEFGHIJabcdefghijkl"; - - THEN("the length is equal to 22") - { - REQUIRE(s.length() == 22); - } - } - - WHEN("assigning a single character") - { - s = 'z'; - - THEN("the length is equal to 1") - { - REQUIRE(s.length() == 1); - } - } - - WHEN("assigning an shorter initializer list of length 3") - { - s = {'d', 'e', 'f'}; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning an longer initializer list of length 22") - { - s = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'a', - 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'}; - - THEN("the length is equal to 22") - { - REQUIRE(s.length() == 22); - } - } - - WHEN("assigning from a shorter string view of length 3") - { - using namespace std::string_view_literals; - - s = "def"sv; - - THEN("the length is equal to 3") - { - REQUIRE(s.length() == 3); - } - } - - WHEN("assigning from a longer string view of length 22") - { - using namespace std::string_view_literals; - - s = "ABCDEFGHIJabcdefghijkl"sv; - - THEN("the length is equal to 22") - { - REQUIRE(s.length() == 22); - } - } - } -} - -SCENARIO("String modifiers", "[string]") -{ - GIVEN("A short string") - { - auto s = kstd::string{"abcd"}; - auto old_capacity = s.capacity(); - auto old_size = s.size(); - auto old_data = s.data(); - - WHEN("reserving a smaller capacity") - { - s.reserve(1); - - THEN("the capacity does not change") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcd"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer stays the same") - { - REQUIRE(s.data() == old_data); - } - } - - WHEN("reserving equal capacity") - { - s.reserve(old_capacity); - - THEN("the capacity does not change") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcd"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer stays the same") - { - REQUIRE(static_cast(s.data()) == static_cast(old_data)); - } - } - - WHEN("reserving larger capacity") - { - s.reserve(old_capacity + 1); - - THEN("the capacity increases") - { - REQUIRE(s.capacity() >= old_capacity + 1); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcd"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer changes") - { - REQUIRE(s.data() != old_data); - } - } - - WHEN("resizing to a greater size within the capacity") - { - s.resize(10); - - THEN("the capacity stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 10); - } - - THEN("the string get filled with null characters") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0", s.size() + 1) == 0); - } - } - - WHEN("resizing to a greater size with a given character within the capacity") - { - s.resize(10, 'x'); - - THEN("the capacity stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 10); - } - - THEN("the string get filled with the given character") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxx", s.size() + 1) == 0); - } - } - - WHEN("resizing to a greater size outside the capacity") - { - s.resize(20); - - THEN("the capacity increases") - { - REQUIRE(s.capacity() > old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 20); - } - - THEN("the string get filled with null characters") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", s.size() + 1) == - 0); - } - } - - WHEN("resizing to a greater size with a given character within the capacity") - { - s.resize(20, 'x'); - - THEN("the capacity increases") - { - REQUIRE(s.capacity() > old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 20); - } - - THEN("the string get filled with the given character") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxxxxxxxxxxxx", s.size() + 1) == 0); - } - } - - WHEN("resizing to a lesser size") - { - s.resize(2); - - THEN("the capacity stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the size decreases") - { - CHECK(s.size() < old_size); - REQUIRE(s.size() == 2); - } - - THEN("the string get filled with the given character") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0); - } - } - } - - GIVEN("A long string") - { - auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; - auto old_capacity = s.capacity(); - auto old_size = s.size(); - auto old_data = s.data(); - - WHEN("reserving a smaller capacity") - { - s.reserve(1); - - THEN("the capacity does not change") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcdefghijABCDEFGHIJ"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer stays the same") - { - REQUIRE(s.data() == old_data); - } - } - - WHEN("reserving equal capacity") - { - s.reserve(old_capacity); - - THEN("the capacity does not change") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcdefghijABCDEFGHIJ"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer stays the same") - { - REQUIRE(static_cast(s.data()) == static_cast(old_data)); - } - } - - WHEN("reserving larger capacity") - { - s.reserve(old_capacity + 1); - - THEN("the capacity increases") - { - REQUIRE(s.capacity() >= old_capacity + 1); - } - - THEN("the content stays the same") - { - REQUIRE(s == "abcdefghijABCDEFGHIJ"); - } - - THEN("the size stays the same") - { - REQUIRE(s.size() == old_size); - } - - THEN("the data pointer changes") - { - REQUIRE(s.data() != old_data); - } - } - - WHEN("resizing to a greater size") - { - s.resize(22); // NOLINT - - THEN("the capacity increases") - { - REQUIRE(s.capacity() > old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 22); - } - - THEN("the string get filled with null characters") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJ\0\0", s.size() + 1) == 0); - } - } - - WHEN("resizing to a greater size with a given character within the capacity") - { - s.resize(22, 'x'); // NOLINT - - THEN("the capacity increases") - { - REQUIRE(s.capacity() > old_capacity); - } - - THEN("the size increases") - { - CHECK(s.size() > old_size); - REQUIRE(s.size() == 22); - } - - THEN("the string get filled with the given character") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJxx", s.size() + 1) == 0); - } - } - - WHEN("resizing to a lesser size") - { - s.resize(2); - - THEN("the capacity stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("the size decreases") - { - CHECK(s.size() < old_size); - REQUIRE(s.size() == 2); - } - - THEN("the string get filled with the given character") - { - REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0); - } - } - } - - GIVEN("Two strings") - { - auto str1 = kstd::string{"Blub"}; - auto str2 = kstd::string{" Blub"}; - - WHEN("appending the second string to the first string") - { - str1.append(str2); - - THEN("the first string contains the characters of both strings concatenated") - { - REQUIRE(str1 == "Blub Blub"); - } - - THEN("the size of the first string is the sum of the sizes of both strings") - { - REQUIRE(str1.size() == str2.size() + 4); - } - } - - WHEN("using operator+= to append the second string to the first string") - { - str1 += str2; - - THEN("the first string contains the characters of both strings concatenated") - { - REQUIRE(str1 == "Blub Blub"); - } - - THEN("the size of the first string is the sum of the sizes of both strings") - { - REQUIRE(str1.size() == str2.size() + 4); - } - } - } - - GIVEN("A string and a string view") - { - auto str = kstd::string{"Blub"}; - auto view = std::string_view{" Blub"}; - - WHEN("appending the string view to the string") - { - str.append(view); - - THEN("the string contains the characters of both the original string and the appended view concatenated") - { - REQUIRE(str == "Blub Blub"); - } - - THEN("the size of the string is the sum of the sizes of the original string and the appended view") - { - REQUIRE(str.size() == view.size() + 4); - } - } - } - - GIVEN("A string and a character") - { - auto str = kstd::string{"Blub"}; - auto ch = '!'; - - WHEN("appending the character to the string") - { - str.push_back(ch); - - THEN("the string contains the original characters followed by the appended character") - { - REQUIRE(str == "Blub!"); - } - - THEN("the size of the string is one more than the original size") - { - REQUIRE(str.size() == 5); - } - } - - WHEN("using operator+= to append the character to the string") - { - str += ch; - - THEN("the string contains the original characters followed by the appended character") - { - REQUIRE(str == "Blub!"); - } - - THEN("the size of the string is one more than the original size") - { - REQUIRE(str.size() == 5); - } - } - } - - GIVEN("A non-empty string") - { - auto str = kstd::string{"Blub Blub"}; - - WHEN("clearing the string") - { - str.clear(); - - THEN("the string is empty and has size zero") - { - REQUIRE(str.empty()); - REQUIRE(str.size() == 0); - } - - THEN("the string contains no characters") - { - REQUIRE(str == std::string_view{}); - } - } - } -} - -SCENARIO("String insertion", "[string]") -{ - GIVEN("A short string") - { - auto s = kstd::string{"abcd"}; - auto old_capacity = s.capacity(); - auto old_size = s.size(); - - WHEN("inserting five copies of a character at the beginning") - { - s.insert(0, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters prepended") - { - REQUIRE(s == kstd::string{"AAAAAabcd"}); - } - } - - WHEN("inserting five copies of a character in the middle") - { - s.insert(2, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters inserted") - { - REQUIRE(s == kstd::string{"abAAAAAcd"}); - } - } - - WHEN("inserting five copies of a character at the end") - { - s.insert(4, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters appended") - { - REQUIRE(s == kstd::string{"abcdAAAAA"}); - } - } - - WHEN("inserting five copies of a character behind the end") - { - THEN("an OS panic is triggered") - { - REQUIRE_THROWS_AS(s.insert(5, 5, 'A'), kstd::tests::os_panic); - } - } - - WHEN("inserting 20 copies of a character") - { - s.insert(2, 20, 'A'); - - THEN("the capacity of the string grows") - { - REQUIRE(s.capacity() > old_capacity); - } - - THEN("the string is equal t o the old string with the new characters inserted") - { - REQUIRE(s == kstd::string{"abAAAAAAAAAAAAAAAAAAAAcd"}); - } - } - - WHEN("inserting a C-style string of length 5 at the beginning") - { - s.insert(0, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters prepended") - { - REQUIRE(s == kstd::string{"AAAAAabcd"}); - } - } - - WHEN("inserting a C-style string of length 5 in the middle") - { - s.insert(2, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters inserted") - { - REQUIRE(s == kstd::string{"abAAAAAcd"}); - } - } - - WHEN("inserting a C-style string of length 5 at the end") - { - s.insert(4, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string stays the same") - { - REQUIRE(s.capacity() == old_capacity); - } - - THEN("The string is equal to the old string with the new characters appended") - { - REQUIRE(s == kstd::string{"abcdAAAAA"}); - } - } - - WHEN("inserting a C-style string of length 5 behind the end") - { - THEN("an OS panic is triggered") - { - REQUIRE_THROWS_AS(s.insert(5, "AAAAA"), kstd::tests::os_panic); - } - } - } - - GIVEN("A long string") - { - auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; - auto old_capacity = s.capacity(); - auto old_size = s.size(); - - WHEN("inserting five copies of a character at the beginning") - { - s.insert(0, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters prepended") - { - REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"}); - } - } - - WHEN("inserting five copies of a character in the middle") - { - s.insert(10, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters inserted") - { - REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"}); - } - } - - WHEN("inserting five copies of a character at the end") - { - s.insert(20, 5, 'A'); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters appended") - { - REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"}); - } - } - - WHEN("inserting five copies of a character behind the end") - { - THEN("an OS panic is triggered") - { - REQUIRE_THROWS_AS(s.insert(21, 5, 'A'), kstd::tests::os_panic); - } - } - - WHEN("inserting a C-style string of length 5 at the beginning") - { - s.insert(0, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters prepended") - { - REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"}); - } - } - - WHEN("inserting a C-style string of length 5 in the middle") - { - s.insert(10, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the string increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters inserted") - { - REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"}); - } - } - - WHEN("inserting a C-style string of length 5 at the end") - { - s.insert(20, "AAAAA"); - - THEN("the size of the string is increased by 5") - { - REQUIRE(s.size() == old_size + 5); - } - - THEN("The capacity of the increases") - { - REQUIRE(s.capacity() >= old_capacity + 5); - } - - THEN("The string is equal to the old string with the new characters appended") - { - REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"}); - } - } - - WHEN("inserting a C-style string of length 5 behind the end") - { - THEN("an OS panic is triggered") - { - REQUIRE_THROWS_AS(s.insert(21, "AAAAA"), kstd::tests::os_panic); - } - } - } -} - -SCENARIO("String concatenation", "[string]") -{ - GIVEN("Two strings") - { - auto str1 = kstd::string{"Blub"}; - auto str2 = kstd::string{" Blub"}; - - WHEN("using operator+ to concatenate the two strings into a new string") - { - auto str3 = str1 + str2; - - THEN("the new string contains the characters of both strings concatenated") - { - REQUIRE(str3 == "Blub Blub"); - } - - THEN("the size of the new string is the sum of the sizes of both strings") - { - REQUIRE(str3.size() == str1.size() + str2.size()); - } - } - } -} - -SCENARIO("String conversion and comparison", "[string]") -{ - GIVEN("An unsigned integer") - { - constexpr auto value1 = 12345u; - constexpr auto value2 = 0u; - - WHEN("converting the unsigned integer to a string") - { - auto str1 = kstd::to_string(value1); - auto str2 = kstd::to_string(value2); - - THEN("the string contains the decimal representation of the unsigned integer") - { - REQUIRE(str1 == "12345"); - REQUIRE(str2 == "0"); - } - } - } - - GIVEN("Two strings with the same characters") - { - auto str1 = kstd::string{"Blub Blub"}; - auto str2 = kstd::string{"Blub Blub"}; - - THEN("the strings are equal") - { - REQUIRE(str1 == str2); - } - - THEN("the strings are not unequal") - { - REQUIRE_FALSE(str1 != str2); - } - } - - GIVEN("A string and a string view with the same characters") - { - auto str = kstd::string{"Blub Blub"}; - auto view = std::string_view{"Blub Blub"}; - - THEN("the string and the string view are equal") - { - REQUIRE(str == view); - REQUIRE(view == str); - } - - THEN("the string and the string view are not unequal") - { - REQUIRE_FALSE(str != view); - REQUIRE_FALSE(view != str); - } - } - - GIVEN("The string 'abc'") - { - auto s1 = kstd::string{"abc"}; - - WHEN("comparing with the string 'def'") - { - auto result = s1.compare(kstd::string{"def"}); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing with the string 'abb'") - { - auto result = s1.compare(kstd::string{"abb"}); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing with the string 'abc'") - { - auto result = s1.compare(kstd::string{"abc"}); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,2] with the string 'def'") - { - auto result = s1.compare(1, 2, kstd::string{"def"}); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,2] with the string 'abb'") - { - auto result = s1.compare(1, 2, kstd::string{"abb"}); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,2] with the string 'cc'") - { - auto result = s1.compare(1, 2, kstd::string{"bc"}); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,npos] with the string 'ef'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"ef"}); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,npos] with the string 'bb'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"bb"}); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,npos] with the string 'bc'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"bc"}); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,2] of 'def'") - { - auto result = s1.compare(1, 2, kstd::string{"def"}, 1, 2); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,2] of 'abb'") - { - auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, 2); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,2] of 'abc'") - { - auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, 2); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,2] of 'def'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, 2); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,2] of 'abb'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, 2); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,2] of 'abc'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, 2); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,npos] of 'def'") - { - auto result = s1.compare(1, 2, kstd::string{"def"}, 1, kstd::string::npos); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,npos] of 'abb'") - { - auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, kstd::string::npos); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,2] with the substring [1,npos] of 'abc'") - { - auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, kstd::string::npos); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,npos] of 'def'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, kstd::string::npos); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abb'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, kstd::string::npos); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abc'") - { - auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, kstd::string::npos); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing with the C-style string 'def'") - { - auto result = s1.compare("def"); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing with the C-style string 'abb'") - { - auto result = s1.compare("abb"); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing with the C-style string 'abc'") - { - auto result = s1.compare("abc"); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,2] with the C-style string 'def'") - { - auto result = s1.compare(1, 2, "def"); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,2] with the C-style string 'abb'") - { - auto result = s1.compare(1, 2, "abb"); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,2] with the C-style string 'bc'") - { - auto result = s1.compare(1, 2, "bc"); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - - WHEN("comparing substring [1,npos] with the C-style string 'def'") - { - auto result = s1.compare(1, 2, "ef"); - - THEN("the result is negative") - { - REQUIRE(result < 0); - } - } - - WHEN("comparing substring [1,npos] with the C-style string 'abb'") - { - auto result = s1.compare(1, 2, "abb"); - - THEN("the result is positive") - { - REQUIRE(result > 0); - } - } - - WHEN("comparing substring [1,npos] with the C-style string 'bc'") - { - auto result = s1.compare(1, 2, "bc"); - - THEN("the result is zero") - { - REQUIRE(result == 0); - } - } - } -} - -SCENARIO("String iteration", "[string]") -{ - GIVEN("A string") - { - auto str = kstd::string{"Blub"}; - - WHEN("iterating over the characters of the string as string_view using a range-based for loop") - { - kstd::string result; - - for (auto ch : static_cast(str)) - { - result.push_back(ch); - } - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str); - } - } - - WHEN("using std::ranges::for_each to iterate over the characters of the string") - { - kstd::string result; - - std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); }); - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str); - } - } - - WHEN("using front and back to access the first and last characters of the string") - { - THEN("front returns the first character of the string") - { - REQUIRE(str.front() == 'B'); - } - - THEN("back returns the last character of the string") - { - REQUIRE(str.back() == 'b'); - } - } - } - - GIVEN("A const string") - { - auto const str = kstd::string{"Blub"}; - - WHEN("iterating over the characters of the string as string_view using a range-based for loop") - { - kstd::string result; - - for (auto ch : static_cast(str)) - { - result.push_back(ch); - } - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == static_cast(str)); - } - } - - WHEN("using front and back to access the first and last characters of the string") - { - THEN("front returns the first character of the string") - { - REQUIRE(str.front() == 'B'); - } - - THEN("back returns the last character of the string") - { - REQUIRE(str.back() == 'b'); - } - } - } - - GIVEN("An empty string") - { - auto str = kstd::string{}; - - WHEN("iterating over the characters of an empty string") - { - kstd::string result; - - for (auto ch : static_cast(str)) - { - result.push_back(ch); - } - - THEN("no characters are iterated and the result is an empty string") - { - REQUIRE(result.empty()); - REQUIRE(result.size() == 0); - REQUIRE(static_cast(result) == std::string_view{}); - } - } - } -} - -SCENARIO("String STL integration", "[string]") -{ - GIVEN("An empty string") - { - auto s = kstd::string{""}; - - WHEN("hashing the string") - { - auto hash_result = std::hash{}(s); - - THEN("the result is equal to hashing an empty std::string") - { - REQUIRE(hash_result == std::hash{}("")); - } - } - } - - GIVEN("A non-empty string") - { - auto s = kstd::string{"abcd"}; - - WHEN("hashing the string") - { - auto hash_result = std::hash{}(s); - - THEN("the result is equal to hashing the same std::string") - { - REQUIRE(hash_result == std::hash{}("abcd")); - } - } - } -} - -SCENARIO("String user-defined literals") -{ - GIVEN("the user defined literals are in scope") - { - using namespace kstd::string_literals; - - WHEN("creating a string using the _s literal suffix") - { - auto s = "abcd"_s; - - THEN("the result is equal to the string created with the same literal") - { - REQUIRE(s == kstd::string{"abcd"}); - } - } - } -} diff --git a/libs/kstd/kstd/string.tests.cpp b/libs/kstd/kstd/string.tests.cpp new file mode 100644 index 00000000..b8fa1139 --- /dev/null +++ b/libs/kstd/kstd/string.tests.cpp @@ -0,0 +1,3166 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE("String static interface") +{ + REQUIRE(std::is_same_v>); + REQUIRE(std::is_same_v); + REQUIRE(std::is_same_v>); + REQUIRE(std::is_same_v>::size_type>); + REQUIRE(std::is_same_v>::difference_type>); + REQUIRE(std::is_same_v); + REQUIRE(std::is_same_v); + REQUIRE(std::is_same_v>::pointer>); + REQUIRE(std::is_same_v>::const_pointer>); + REQUIRE(std::is_same_v); + REQUIRE(std::is_same_v); + REQUIRE(std::is_same_v>); + REQUIRE(std::is_same_v>); + + REQUIRE(kstd::string::npos == kstd::string::size_type(-1)); +} + +SCENARIO("String initialization and construction", "[string]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto s = kstd::string{}; + + THEN("the string is empty") + { + REQUIRE(s.empty()); + } + + THEN("the size is equal to zero") + { + REQUIRE(s.size() == 0); + } + + THEN("the length is equal to zero") + { + REQUIRE(s.length() == 0); + } + + THEN("at raises a panic") + { + REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); + } + + THEN("data returns a pointer poiting to a null byte") + { + REQUIRE(*s.data() == '\0'); + } + + THEN("c_str returns a pointer poiting to a null byte") + { + REQUIRE(*s.c_str() == '\0'); + } + + THEN("the string is convertible to an empty string view") + { + auto view = static_cast(s); + REQUIRE(view.empty()); + } + } + + WHEN("constructing with 10 copies of the letter 'a'") + { + auto s = kstd::string{10, 'a'}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 10") + { + REQUIRE(s.size() == 10); + } + + THEN("the length is equal to 10") + { + REQUIRE(s.length() == 10); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns a") + { + REQUIRE(s.at(s.size() - 1) == 'a'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns a") + { + REQUIRE(s[s.size() - 1] == 'a'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns a") + { + REQUIRE(s.back() == 'a'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 10") + { + REQUIRE(std::strlen(s.c_str()) == 10); + } + + THEN("the string converts to a string view of length 10") + { + auto view = static_cast(s); + REQUIRE(view.length() == 10); + } + } + + WHEN("constructing with 20 copies of the letter 'a'") + { + auto s = kstd::string{20, 'a'}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns a") + { + REQUIRE(s.at(s.size() - 1) == 'a'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns a") + { + REQUIRE(s[s.size() - 1] == 'a'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns a") + { + REQUIRE(s.back() == 'a'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing with an input iterator pair of 10 characters") + { + auto source = std::istringstream{"abcdefghij"}; + auto s = kstd::string{std::istream_iterator{source}, std::istream_iterator{}}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 10") + { + REQUIRE(s.size() == 10); + } + + THEN("the length is equal to 10") + { + REQUIRE(s.length() == 10); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns j") + { + REQUIRE(s.at(s.size() - 1) == 'j'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns j") + { + REQUIRE(s[s.size() - 1] == 'j'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns j") + { + REQUIRE(s.back() == 'j'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 10") + { + REQUIRE(std::strlen(s.c_str()) == 10); + } + + THEN("the string converts to a string view of length 10") + { + auto view = static_cast(s); + REQUIRE(view.length() == 10); + } + } + + WHEN("constructing with an input iterator pair of 20 characters") + { + auto source = std::istringstream{"abcdefghijABCDEFGHIJ"}; + auto s = kstd::string{std::istream_iterator{source}, std::istream_iterator{}}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is greater than or equal to 20") + { + REQUIRE(s.capacity() >= 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing with a forward iterator pair of 10 characters") + { + auto source = std::forward_list{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; + auto s = kstd::string{source.cbegin(), source.cend()}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 10") + { + REQUIRE(s.size() == 10); + } + + THEN("the length is equal to 10") + { + REQUIRE(s.length() == 10); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns j") + { + REQUIRE(s.at(s.size() - 1) == 'j'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns j") + { + REQUIRE(s[s.size() - 1] == 'j'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns j") + { + REQUIRE(s.back() == 'j'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 10") + { + REQUIRE(std::strlen(s.c_str()) == 10); + } + + THEN("the string converts to a string view of length 10") + { + auto view = static_cast(s); + REQUIRE(view.length() == 10); + } + } + + WHEN("constructing with a forward iterator pair of 20 characters") + { + auto source = std::forward_list{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; + auto s = kstd::string{source.cbegin(), source.cend()}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing from a null pointer with zero size") + { + auto s = kstd::string{static_cast(nullptr), 0}; + + THEN("the string is empty") + { + REQUIRE(s.empty()); + } + + THEN("the size is equal to 0") + { + REQUIRE(s.size() == 0); + } + + THEN("the length is equal to 0") + { + REQUIRE(s.length() == 0); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at raises a panic") + { + REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); + } + + THEN("data returns a pointer poiting to a null byte") + { + REQUIRE(*s.data() == '\0'); + } + + THEN("c_str returns a pointer poiting to a null byte") + { + REQUIRE(*s.c_str() == '\0'); + } + + THEN("the string is convertible to an empty string view") + { + auto view = static_cast(s); + REQUIRE(view.empty()); + } + } + + WHEN("constructing from a from a null pointer and non-zero size") + { + THEN("a panic is raised") + { + REQUIRE_THROWS_AS((kstd::string{static_cast(nullptr), 1}), kstd::tests::os_panic); + } + } + + WHEN("constructing from a non-null pointer and zero size") + { + auto ptr = "abcd"; + auto s = kstd::string{ptr, 0}; + + THEN("the string is empty") + { + REQUIRE(s.empty()); + } + + THEN("the size is equal to 0") + { + REQUIRE(s.size() == 0); + } + + THEN("the length is equal to 0") + { + REQUIRE(s.length() == 0); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at raises a panic") + { + REQUIRE_THROWS_AS(s.at(0), kstd::tests::os_panic); + } + + THEN("data returns a pointer poiting to a null byte") + { + REQUIRE(*s.data() == '\0'); + } + + THEN("c_str returns a pointer poiting to a null byte") + { + REQUIRE(*s.c_str() == '\0'); + } + + THEN("the string is convertible to an empty string view") + { + auto view = static_cast(s); + REQUIRE(view.empty()); + } + } + + WHEN("constructing from a non-null pointer and a size of 4") + { + auto ptr = "abcd"; + auto s = kstd::string{ptr, 4}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 4") + { + REQUIRE(s.size() == 4); + } + + THEN("the length is equal to 4") + { + REQUIRE(s.length() == 4); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns d") + { + REQUIRE(s.at(s.size() - 1) == 'd'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns d") + { + REQUIRE(s[s.size() - 1] == 'd'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns d") + { + REQUIRE(s.back() == 'd'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 4") + { + REQUIRE(std::strlen(s.c_str()) == 4); + } + + THEN("the string converts to a string view of length 4") + { + auto view = static_cast(s); + REQUIRE(view.length() == 4); + } + } + + WHEN("constructing from a non-null pointer and a size of 20") + { + auto ptr = "abcdefghijABCDEFGHIJ"; + auto s = kstd::string{ptr, 20}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing from a C-style string of length 4") + { + auto ptr = "abcd"; + auto s = kstd::string{ptr}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 4") + { + REQUIRE(s.size() == 4); + } + + THEN("the length is equal to 4") + { + REQUIRE(s.length() == 4); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns d") + { + REQUIRE(s.at(s.size() - 1) == 'd'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns d") + { + REQUIRE(s[s.size() - 1] == 'd'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns d") + { + REQUIRE(s.back() == 'd'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 4") + { + REQUIRE(std::strlen(s.c_str()) == 4); + } + + THEN("the string converts to a string view of length 4") + { + auto view = static_cast(s); + REQUIRE(view.length() == 4); + } + } + + WHEN("constructing from a C-style string of length 20") + { + auto ptr = "abcdefghijABCDEFGHIJ"; + auto s = kstd::string{ptr}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing from a string view of length 4") + { + using namespace std::string_view_literals; + + auto view = "abcd"sv; + auto s = kstd::string{view}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 4") + { + REQUIRE(s.size() == 4); + } + + THEN("the length is equal to 4") + { + REQUIRE(s.length() == 4); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns d") + { + REQUIRE(s.at(s.size() - 1) == 'd'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns d") + { + REQUIRE(s[s.size() - 1] == 'd'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns d") + { + REQUIRE(s.back() == 'd'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 4") + { + REQUIRE(std::strlen(s.c_str()) == 4); + } + + THEN("the string converts to a string view of length 4") + { + auto view = static_cast(s); + REQUIRE(view.length() == 4); + } + } + + WHEN("constructing from a string view of length 20") + { + using namespace std::string_view_literals; + + auto view = "abcdefghijABCDEFGHIJ"sv; + auto s = kstd::string{view}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + + THEN("the capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("at(0) returns a") + { + REQUIRE(s.at(0) == 'a'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns a") + { + REQUIRE(s[0] == 'a'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an a") + { + REQUIRE(*s.data() == 'a'); + } + + THEN("c_str returns a pointer pointing to an a") + { + REQUIRE(*s.c_str() == 'a'); + } + + THEN("c_str points to the start of a c-string of length 20") + { + REQUIRE(std::strlen(s.c_str()) == 20); + } + + THEN("the string converts to a string view of length 20") + { + auto view = static_cast(s); + REQUIRE(view.length() == 20); + } + } + + WHEN("constructing from a substring of a string view of length 4") + { + using namespace std::string_view_literals; + + auto view = "abcd"sv; + auto s = kstd::string{view, 1, 4}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 3") + { + REQUIRE(s.size() == 3); + } + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + + THEN("the capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("at(0) returns b") + { + REQUIRE(s.at(0) == 'b'); + } + + THEN("at(size() - 1) returns d") + { + REQUIRE(s.at(s.size() - 1) == 'd'); + } + + THEN("[0] returns b") + { + REQUIRE(s[0] == 'b'); + } + + THEN("[size() - 1] returns d") + { + REQUIRE(s[s.size() - 1] == 'd'); + } + + THEN("front returns b") + { + REQUIRE(s.front() == 'b'); + } + + THEN("back returns d") + { + REQUIRE(s.back() == 'd'); + } + + THEN("data returns a pointer pointing to an b") + { + REQUIRE(*s.data() == 'b'); + } + + THEN("c_str returns a pointer pointing to an b") + { + REQUIRE(*s.c_str() == 'b'); + } + + THEN("c_str points to the start of a c-string of length 3") + { + REQUIRE(std::strlen(s.c_str()) == 3); + } + + THEN("the string converts to a string view of length 3") + { + auto view = static_cast(s); + REQUIRE(view.length() == 3); + } + } + + WHEN("constructing from a substring of a string view of length 20") + { + using namespace std::string_view_literals; + + auto view = "abcdefghijABCDEFGHIJ"sv; + auto s = kstd::string{view, 1, 20}; + + THEN("the string is not empty") + { + REQUIRE_FALSE(s.empty()); + } + + THEN("the size is equal to 19") + { + REQUIRE(s.size() == 19); + } + + THEN("the length is equal to 19") + { + REQUIRE(s.length() == 19); + } + + THEN("the capacity is equal to 19") + { + REQUIRE(s.capacity() == 19); + } + + THEN("at(0) returns b") + { + REQUIRE(s.at(0) == 'b'); + } + + THEN("at(size() - 1) returns J") + { + REQUIRE(s.at(s.size() - 1) == 'J'); + } + + THEN("[0] returns b") + { + REQUIRE(s[0] == 'b'); + } + + THEN("[size() - 1] returns J") + { + REQUIRE(s[s.size() - 1] == 'J'); + } + + THEN("front returns b") + { + REQUIRE(s.front() == 'b'); + } + + THEN("back returns J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("data returns a pointer pointing to an b") + { + REQUIRE(*s.data() == 'b'); + } + + THEN("c_str returns a pointer pointing to an b") + { + REQUIRE(*s.c_str() == 'b'); + } + + THEN("c_str points to the start of a c-string of length 19") + { + REQUIRE(std::strlen(s.c_str()) == 19); + } + + THEN("the string converts to a string view of length 19") + { + auto view = static_cast(s); + REQUIRE(view.length() == 19); + } + } + } + + GIVEN("An existing short string, of length 10") + { + auto other = kstd::string{"abcdefghij"}; + + WHEN("constructing by copy") + { + auto s = other; + + THEN("the sizes are identical") + { + REQUIRE(s.size() == other.size()); + } + + THEN("the capacities are idendtical") + { + REQUIRE(s.capacity() == other.capacity()); + } + + THEN("the underlying strings are identical") + { + REQUIRE_FALSE(std::strcmp(s.data(), other.data())); + } + } + + WHEN("constructing by move") + { + auto s = std::move(other); + + THEN("size is equal to 10") + { + REQUIRE(s.size() == 10); + } + + THEN("capacity is equal to 15") + { + REQUIRE(s.capacity() == 15); + } + + THEN("front() return an a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back() return a j") + { + REQUIRE(s.back() == 'j'); + } + + THEN("The moved from object's size is 0") + { + REQUIRE(other.size() == 0); + } + + THEN("The moved from object's capacity is 15") + { + REQUIRE(other.capacity() == 15); + } + + THEN("c_str() on the moved from object points to a null byte") + { + REQUIRE(*other.c_str() == '\0'); + } + } + } + + GIVEN("An existing short string, of length 20") + { + auto other = kstd::string{"abcdefghijABCDEFGHIJ"}; + + WHEN("constructing by copy") + { + auto s = other; + + THEN("the sizes are identical") + { + REQUIRE(s.size() == other.size()); + } + + THEN("the capacities are idendtical") + { + REQUIRE(s.capacity() == other.capacity()); + } + + THEN("the underlying strings are identical") + { + REQUIRE_FALSE(std::strcmp(s.data(), other.data())); + } + } + + WHEN("constructing by move") + { + auto s = std::move(other); + + THEN("size is equal to 20") + { + REQUIRE(s.size() == 20); + } + + THEN("capacity is equal to 20") + { + REQUIRE(s.capacity() == 20); + } + + THEN("front() return an a") + { + REQUIRE(s.front() == 'a'); + } + + THEN("back() return a J") + { + REQUIRE(s.back() == 'J'); + } + + THEN("The moved from object's size is 0") + { + REQUIRE(other.size() == 0); + } + + THEN("The moved from object's capacity is 15") + { + REQUIRE(other.capacity() == 15); + } + + THEN("c_str() on the moved from object points to a null byte") + { + REQUIRE(*other.c_str() == '\0'); + } + } + } +} + +SCENARIO("String assignment", "[string]") +{ + GIVEN("Two short strings") + { + auto s1 = kstd::string{"abcd"}; + auto s2 = kstd::string{"defghij"}; + + WHEN("copy assigning the shorter to the longer one") + { + s2 = s1; + + THEN("the new length of the longer one is equal to the length of the shorter one") + { + REQUIRE(s2.length() == s1.length()); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); + } + } + + WHEN("copy assigning the longer to the shorter one") + { + s1 = s2; + + THEN("the new length of the shorter one is equal to the length of the longer one") + { + REQUIRE(s1.length() == s2.length()); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); + } + } + + WHEN("move assigning the shorter to the longer one") + { + s2 = std::move(s1); + + THEN("the new length of the longer one is equal to the original length of the shorter one") + { + REQUIRE(s2.length() == 4); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s1.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s1.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s2.capacity() == 15); + } + } + + WHEN("move assigning the longer to the shorter one") + { + s1 = std::move(s2); + + THEN("the new length of the shorter one is equal to the original length of the longer one") + { + REQUIRE(s1.length() == 7); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s2.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s2.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s1.capacity() == 15); + } + } + } + + GIVEN("One short and one long string") + { + auto s1 = kstd::string{"abcd"}; + auto s2 = kstd::string{"ABCDEFGHIJabcdefghij"}; + + WHEN("copy assigning the shorter to the longer one") + { + s2 = s1; + + THEN("the new length of the longer one is equal to the length of the shorter one") + { + REQUIRE(s2.length() == s1.length()); + } + + THEN("the new capacity of the longer one is 15") + { + REQUIRE(s2.capacity() == 15); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); + } + } + + WHEN("copy assigning the longer to the shorter one") + { + s1 = s2; + + THEN("the new length of the shorter one is equal to the length of the longer one") + { + REQUIRE(s1.length() == s2.length()); + } + + THEN("the new capacity of the shorter one is 20") + { + REQUIRE(s1.capacity() == 20); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); + } + } + + WHEN("move assigning the shorter to the longer one") + { + s2 = std::move(s1); + + THEN("the new length of the longer one is equal to the original length of the shorter one") + { + REQUIRE(s2.length() == 4); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s1.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s1.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s2.capacity() == 15); + } + } + + WHEN("move assigning the longer to the shorter one") + { + s1 = std::move(s2); + + THEN("the new length of the shorter one is equal to the original length of the longer one") + { + REQUIRE(s1.length() == 20); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s2.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s2.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s1.capacity() == 20); + } + } + } + + GIVEN("Two long strings") + { + auto s1 = kstd::string{"abcdefghijABCDEFGH"}; + auto s2 = kstd::string{"ABCDEFGHIJabcdefghij"}; + + WHEN("copy assigning the shorter to the longer one") + { + s2 = s1; + + THEN("the new length of the longer one is equal to the length of the shorter one") + { + REQUIRE(s2.length() == s1.length()); + } + + THEN("the new capacity of the longer one is still 20") + { + REQUIRE(s2.capacity() == 20); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s1.data(), s2.data())); + } + } + + WHEN("copy assigning the longer to the shorter one") + { + s1 = s2; + + THEN("the new length of the shorter one is equal to the length of the longer one") + { + REQUIRE(s1.length() == s2.length()); + } + + THEN("the new capacity of the shorter one is 20") + { + REQUIRE(s1.capacity() == 20); + } + + THEN("the underlying data of the two is identical") + { + REQUIRE_FALSE(std::strcmp(s2.data(), s1.data())); + } + } + + WHEN("move assigning the shorter to the longer one") + { + s2 = std::move(s1); + + THEN("the new length of the longer one is equal to the original length of the shorter one") + { + REQUIRE(s2.length() == 18); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s1.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s1.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s2.capacity() == 18); + } + } + + WHEN("move assigning the longer to the shorter one") + { + s1 = std::move(s2); + + THEN("the new length of the shorter one is equal to the original length of the longer one") + { + REQUIRE(s1.length() == 20); + } + + THEN("the length of the moved from one is 0") + { + REQUIRE(s2.length() == 0); + } + + THEN("the capacity of the moved from one is 15") + { + REQUIRE(s2.capacity() == 15); + } + + THEN("the capacity of the moved to one is 15") + { + REQUIRE(s1.capacity() == 20); + } + } + } + + GIVEN("An short string") + { + auto s = kstd::string{"abcd"}; + + WHEN("assigning a new, shorter c-style string of length 3") + { + s = "def"; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning a new, longer, c-style string of length 5") + { + s = "defgh"; + + THEN("the length is equal to 5") + { + REQUIRE(s.length() == 5); + } + } + + WHEN("assigning a new, longer, c-style string of length 20") + { + s = "ABCDEFGHIJabcdefghij"; + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + } + + WHEN("assigning a single character") + { + s = 'z'; + + THEN("the length is equal to 1") + { + REQUIRE(s.length() == 1); + } + } + + WHEN("assigning an shorter initializer list of length 3") + { + s = {'d', 'e', 'f'}; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning an longer initializer list of length 5") + { + s = {'d', 'e', 'f', 'g', 'h'}; + + THEN("the length is equal to 5") + { + REQUIRE(s.length() == 5); + } + } + + WHEN("assigning an longer initializer list of length 20") + { + s = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + } + + WHEN("assigning from a shorter string view of length 3") + { + using namespace std::string_view_literals; + + s = "def"sv; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning from a longer string view of length 5") + { + using namespace std::string_view_literals; + + s = "defgh"sv; + + THEN("the length is equal to 5") + { + REQUIRE(s.length() == 5); + } + } + + WHEN("assigning from a longer string view of length 20") + { + using namespace std::string_view_literals; + + s = "ABCDEFGHIJabcdefghij"sv; + + THEN("the length is equal to 20") + { + REQUIRE(s.length() == 20); + } + } + } + + GIVEN("An long string") + { + auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; + + WHEN("assigning a new, shorter c-style string of length 3") + { + s = "def"; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning a new, longer, c-style string of length 22") + { + s = "ABCDEFGHIJabcdefghijkl"; + + THEN("the length is equal to 22") + { + REQUIRE(s.length() == 22); + } + } + + WHEN("assigning a single character") + { + s = 'z'; + + THEN("the length is equal to 1") + { + REQUIRE(s.length() == 1); + } + } + + WHEN("assigning an shorter initializer list of length 3") + { + s = {'d', 'e', 'f'}; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning an longer initializer list of length 22") + { + s = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'}; + + THEN("the length is equal to 22") + { + REQUIRE(s.length() == 22); + } + } + + WHEN("assigning from a shorter string view of length 3") + { + using namespace std::string_view_literals; + + s = "def"sv; + + THEN("the length is equal to 3") + { + REQUIRE(s.length() == 3); + } + } + + WHEN("assigning from a longer string view of length 22") + { + using namespace std::string_view_literals; + + s = "ABCDEFGHIJabcdefghijkl"sv; + + THEN("the length is equal to 22") + { + REQUIRE(s.length() == 22); + } + } + } +} + +SCENARIO("String modifiers", "[string]") +{ + GIVEN("A short string") + { + auto s = kstd::string{"abcd"}; + auto old_capacity = s.capacity(); + auto old_size = s.size(); + auto old_data = s.data(); + + WHEN("reserving a smaller capacity") + { + s.reserve(1); + + THEN("the capacity does not change") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcd"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer stays the same") + { + REQUIRE(s.data() == old_data); + } + } + + WHEN("reserving equal capacity") + { + s.reserve(old_capacity); + + THEN("the capacity does not change") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcd"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer stays the same") + { + REQUIRE(static_cast(s.data()) == static_cast(old_data)); + } + } + + WHEN("reserving larger capacity") + { + s.reserve(old_capacity + 1); + + THEN("the capacity increases") + { + REQUIRE(s.capacity() >= old_capacity + 1); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcd"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer changes") + { + REQUIRE(s.data() != old_data); + } + } + + WHEN("resizing to a greater size within the capacity") + { + s.resize(10); + + THEN("the capacity stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 10); + } + + THEN("the string get filled with null characters") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0", s.size() + 1) == 0); + } + } + + WHEN("resizing to a greater size with a given character within the capacity") + { + s.resize(10, 'x'); + + THEN("the capacity stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 10); + } + + THEN("the string get filled with the given character") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxx", s.size() + 1) == 0); + } + } + + WHEN("resizing to a greater size outside the capacity") + { + s.resize(20); + + THEN("the capacity increases") + { + REQUIRE(s.capacity() > old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 20); + } + + THEN("the string get filled with null characters") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", s.size() + 1) == + 0); + } + } + + WHEN("resizing to a greater size with a given character within the capacity") + { + s.resize(20, 'x'); + + THEN("the capacity increases") + { + REQUIRE(s.capacity() > old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 20); + } + + THEN("the string get filled with the given character") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxxxxxxxxxxxx", s.size() + 1) == 0); + } + } + + WHEN("resizing to a lesser size") + { + s.resize(2); + + THEN("the capacity stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the size decreases") + { + CHECK(s.size() < old_size); + REQUIRE(s.size() == 2); + } + + THEN("the string get filled with the given character") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0); + } + } + } + + GIVEN("A long string") + { + auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; + auto old_capacity = s.capacity(); + auto old_size = s.size(); + auto old_data = s.data(); + + WHEN("reserving a smaller capacity") + { + s.reserve(1); + + THEN("the capacity does not change") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcdefghijABCDEFGHIJ"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer stays the same") + { + REQUIRE(s.data() == old_data); + } + } + + WHEN("reserving equal capacity") + { + s.reserve(old_capacity); + + THEN("the capacity does not change") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcdefghijABCDEFGHIJ"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer stays the same") + { + REQUIRE(static_cast(s.data()) == static_cast(old_data)); + } + } + + WHEN("reserving larger capacity") + { + s.reserve(old_capacity + 1); + + THEN("the capacity increases") + { + REQUIRE(s.capacity() >= old_capacity + 1); + } + + THEN("the content stays the same") + { + REQUIRE(s == "abcdefghijABCDEFGHIJ"); + } + + THEN("the size stays the same") + { + REQUIRE(s.size() == old_size); + } + + THEN("the data pointer changes") + { + REQUIRE(s.data() != old_data); + } + } + + WHEN("resizing to a greater size") + { + s.resize(22); // NOLINT + + THEN("the capacity increases") + { + REQUIRE(s.capacity() > old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 22); + } + + THEN("the string get filled with null characters") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJ\0\0", s.size() + 1) == 0); + } + } + + WHEN("resizing to a greater size with a given character within the capacity") + { + s.resize(22, 'x'); // NOLINT + + THEN("the capacity increases") + { + REQUIRE(s.capacity() > old_capacity); + } + + THEN("the size increases") + { + CHECK(s.size() > old_size); + REQUIRE(s.size() == 22); + } + + THEN("the string get filled with the given character") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJxx", s.size() + 1) == 0); + } + } + + WHEN("resizing to a lesser size") + { + s.resize(2); + + THEN("the capacity stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("the size decreases") + { + CHECK(s.size() < old_size); + REQUIRE(s.size() == 2); + } + + THEN("the string get filled with the given character") + { + REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0); + } + } + } + + GIVEN("Two strings") + { + auto str1 = kstd::string{"Blub"}; + auto str2 = kstd::string{" Blub"}; + + WHEN("appending the second string to the first string") + { + str1.append(str2); + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1 == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + + WHEN("using operator+= to append the second string to the first string") + { + str1 += str2; + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1 == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + } + + GIVEN("A string and a string view") + { + auto str = kstd::string{"Blub"}; + auto view = std::string_view{" Blub"}; + + WHEN("appending the string view to the string") + { + str.append(view); + + THEN("the string contains the characters of both the original string and the appended view concatenated") + { + REQUIRE(str == "Blub Blub"); + } + + THEN("the size of the string is the sum of the sizes of the original string and the appended view") + { + REQUIRE(str.size() == view.size() + 4); + } + } + } + + GIVEN("A string and a character") + { + auto str = kstd::string{"Blub"}; + auto ch = '!'; + + WHEN("appending the character to the string") + { + str.push_back(ch); + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + + WHEN("using operator+= to append the character to the string") + { + str += ch; + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + } + + GIVEN("A non-empty string") + { + auto str = kstd::string{"Blub Blub"}; + + WHEN("clearing the string") + { + str.clear(); + + THEN("the string is empty and has size zero") + { + REQUIRE(str.empty()); + REQUIRE(str.size() == 0); + } + + THEN("the string contains no characters") + { + REQUIRE(str == std::string_view{}); + } + } + } +} + +SCENARIO("String insertion", "[string]") +{ + GIVEN("A short string") + { + auto s = kstd::string{"abcd"}; + auto old_capacity = s.capacity(); + auto old_size = s.size(); + + WHEN("inserting five copies of a character at the beginning") + { + s.insert(0, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters prepended") + { + REQUIRE(s == kstd::string{"AAAAAabcd"}); + } + } + + WHEN("inserting five copies of a character in the middle") + { + s.insert(2, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters inserted") + { + REQUIRE(s == kstd::string{"abAAAAAcd"}); + } + } + + WHEN("inserting five copies of a character at the end") + { + s.insert(4, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters appended") + { + REQUIRE(s == kstd::string{"abcdAAAAA"}); + } + } + + WHEN("inserting five copies of a character behind the end") + { + THEN("an OS panic is triggered") + { + REQUIRE_THROWS_AS(s.insert(5, 5, 'A'), kstd::tests::os_panic); + } + } + + WHEN("inserting 20 copies of a character") + { + s.insert(2, 20, 'A'); + + THEN("the capacity of the string grows") + { + REQUIRE(s.capacity() > old_capacity); + } + + THEN("the string is equal t o the old string with the new characters inserted") + { + REQUIRE(s == kstd::string{"abAAAAAAAAAAAAAAAAAAAAcd"}); + } + } + + WHEN("inserting a C-style string of length 5 at the beginning") + { + s.insert(0, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters prepended") + { + REQUIRE(s == kstd::string{"AAAAAabcd"}); + } + } + + WHEN("inserting a C-style string of length 5 in the middle") + { + s.insert(2, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters inserted") + { + REQUIRE(s == kstd::string{"abAAAAAcd"}); + } + } + + WHEN("inserting a C-style string of length 5 at the end") + { + s.insert(4, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string stays the same") + { + REQUIRE(s.capacity() == old_capacity); + } + + THEN("The string is equal to the old string with the new characters appended") + { + REQUIRE(s == kstd::string{"abcdAAAAA"}); + } + } + + WHEN("inserting a C-style string of length 5 behind the end") + { + THEN("an OS panic is triggered") + { + REQUIRE_THROWS_AS(s.insert(5, "AAAAA"), kstd::tests::os_panic); + } + } + } + + GIVEN("A long string") + { + auto s = kstd::string{"abcdefghijABCDEFGHIJ"}; + auto old_capacity = s.capacity(); + auto old_size = s.size(); + + WHEN("inserting five copies of a character at the beginning") + { + s.insert(0, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters prepended") + { + REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"}); + } + } + + WHEN("inserting five copies of a character in the middle") + { + s.insert(10, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters inserted") + { + REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"}); + } + } + + WHEN("inserting five copies of a character at the end") + { + s.insert(20, 5, 'A'); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters appended") + { + REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"}); + } + } + + WHEN("inserting five copies of a character behind the end") + { + THEN("an OS panic is triggered") + { + REQUIRE_THROWS_AS(s.insert(21, 5, 'A'), kstd::tests::os_panic); + } + } + + WHEN("inserting a C-style string of length 5 at the beginning") + { + s.insert(0, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters prepended") + { + REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"}); + } + } + + WHEN("inserting a C-style string of length 5 in the middle") + { + s.insert(10, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the string increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters inserted") + { + REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"}); + } + } + + WHEN("inserting a C-style string of length 5 at the end") + { + s.insert(20, "AAAAA"); + + THEN("the size of the string is increased by 5") + { + REQUIRE(s.size() == old_size + 5); + } + + THEN("The capacity of the increases") + { + REQUIRE(s.capacity() >= old_capacity + 5); + } + + THEN("The string is equal to the old string with the new characters appended") + { + REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"}); + } + } + + WHEN("inserting a C-style string of length 5 behind the end") + { + THEN("an OS panic is triggered") + { + REQUIRE_THROWS_AS(s.insert(21, "AAAAA"), kstd::tests::os_panic); + } + } + } +} + +SCENARIO("String concatenation", "[string]") +{ + GIVEN("Two strings") + { + auto str1 = kstd::string{"Blub"}; + auto str2 = kstd::string{" Blub"}; + + WHEN("using operator+ to concatenate the two strings into a new string") + { + auto str3 = str1 + str2; + + THEN("the new string contains the characters of both strings concatenated") + { + REQUIRE(str3 == "Blub Blub"); + } + + THEN("the size of the new string is the sum of the sizes of both strings") + { + REQUIRE(str3.size() == str1.size() + str2.size()); + } + } + } +} + +SCENARIO("String conversion and comparison", "[string]") +{ + GIVEN("An unsigned integer") + { + constexpr auto value1 = 12345u; + constexpr auto value2 = 0u; + + WHEN("converting the unsigned integer to a string") + { + auto str1 = kstd::to_string(value1); + auto str2 = kstd::to_string(value2); + + THEN("the string contains the decimal representation of the unsigned integer") + { + REQUIRE(str1 == "12345"); + REQUIRE(str2 == "0"); + } + } + } + + GIVEN("Two strings with the same characters") + { + auto str1 = kstd::string{"Blub Blub"}; + auto str2 = kstd::string{"Blub Blub"}; + + THEN("the strings are equal") + { + REQUIRE(str1 == str2); + } + + THEN("the strings are not unequal") + { + REQUIRE_FALSE(str1 != str2); + } + } + + GIVEN("A string and a string view with the same characters") + { + auto str = kstd::string{"Blub Blub"}; + auto view = std::string_view{"Blub Blub"}; + + THEN("the string and the string view are equal") + { + REQUIRE(str == view); + REQUIRE(view == str); + } + + THEN("the string and the string view are not unequal") + { + REQUIRE_FALSE(str != view); + REQUIRE_FALSE(view != str); + } + } + + GIVEN("The string 'abc'") + { + auto s1 = kstd::string{"abc"}; + + WHEN("comparing with the string 'def'") + { + auto result = s1.compare(kstd::string{"def"}); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing with the string 'abb'") + { + auto result = s1.compare(kstd::string{"abb"}); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing with the string 'abc'") + { + auto result = s1.compare(kstd::string{"abc"}); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,2] with the string 'def'") + { + auto result = s1.compare(1, 2, kstd::string{"def"}); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,2] with the string 'abb'") + { + auto result = s1.compare(1, 2, kstd::string{"abb"}); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,2] with the string 'cc'") + { + auto result = s1.compare(1, 2, kstd::string{"bc"}); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,npos] with the string 'ef'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"ef"}); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,npos] with the string 'bb'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"bb"}); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,npos] with the string 'bc'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"bc"}); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,2] of 'def'") + { + auto result = s1.compare(1, 2, kstd::string{"def"}, 1, 2); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,2] of 'abb'") + { + auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, 2); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,2] of 'abc'") + { + auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, 2); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,2] of 'def'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, 2); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,2] of 'abb'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, 2); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,2] of 'abc'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, 2); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,npos] of 'def'") + { + auto result = s1.compare(1, 2, kstd::string{"def"}, 1, kstd::string::npos); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,npos] of 'abb'") + { + auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, kstd::string::npos); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,2] with the substring [1,npos] of 'abc'") + { + auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, kstd::string::npos); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,npos] of 'def'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, kstd::string::npos); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abb'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, kstd::string::npos); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abc'") + { + auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, kstd::string::npos); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing with the C-style string 'def'") + { + auto result = s1.compare("def"); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing with the C-style string 'abb'") + { + auto result = s1.compare("abb"); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing with the C-style string 'abc'") + { + auto result = s1.compare("abc"); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,2] with the C-style string 'def'") + { + auto result = s1.compare(1, 2, "def"); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,2] with the C-style string 'abb'") + { + auto result = s1.compare(1, 2, "abb"); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,2] with the C-style string 'bc'") + { + auto result = s1.compare(1, 2, "bc"); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + + WHEN("comparing substring [1,npos] with the C-style string 'def'") + { + auto result = s1.compare(1, 2, "ef"); + + THEN("the result is negative") + { + REQUIRE(result < 0); + } + } + + WHEN("comparing substring [1,npos] with the C-style string 'abb'") + { + auto result = s1.compare(1, 2, "abb"); + + THEN("the result is positive") + { + REQUIRE(result > 0); + } + } + + WHEN("comparing substring [1,npos] with the C-style string 'bc'") + { + auto result = s1.compare(1, 2, "bc"); + + THEN("the result is zero") + { + REQUIRE(result == 0); + } + } + } +} + +SCENARIO("String iteration", "[string]") +{ + GIVEN("A string") + { + auto str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : static_cast(str)) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using std::ranges::for_each to iterate over the characters of the string") + { + kstd::string result; + + std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); }); + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } + + GIVEN("A const string") + { + auto const str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : static_cast(str)) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == static_cast(str)); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } + + GIVEN("An empty string") + { + auto str = kstd::string{}; + + WHEN("iterating over the characters of an empty string") + { + kstd::string result; + + for (auto ch : static_cast(str)) + { + result.push_back(ch); + } + + THEN("no characters are iterated and the result is an empty string") + { + REQUIRE(result.empty()); + REQUIRE(result.size() == 0); + REQUIRE(static_cast(result) == std::string_view{}); + } + } + } +} + +SCENARIO("String STL integration", "[string]") +{ + GIVEN("An empty string") + { + auto s = kstd::string{""}; + + WHEN("hashing the string") + { + auto hash_result = std::hash{}(s); + + THEN("the result is equal to hashing an empty std::string") + { + REQUIRE(hash_result == std::hash{}("")); + } + } + } + + GIVEN("A non-empty string") + { + auto s = kstd::string{"abcd"}; + + WHEN("hashing the string") + { + auto hash_result = std::hash{}(s); + + THEN("the result is equal to hashing the same std::string") + { + REQUIRE(hash_result == std::hash{}("abcd")); + } + } + } +} + +SCENARIO("String user-defined literals") +{ + GIVEN("the user defined literals are in scope") + { + using namespace kstd::string_literals; + + WHEN("creating a string using the _s literal suffix") + { + auto s = "abcd"_s; + + THEN("the result is equal to the string created with the same literal") + { + REQUIRE(s == kstd::string{"abcd"}); + } + } + } +} diff --git a/libs/kstd/kstd/test_support/os_panic.test.cpp b/libs/kstd/kstd/test_support/os_panic.test.cpp deleted file mode 100644 index c30411aa..00000000 --- a/libs/kstd/kstd/test_support/os_panic.test.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include -#include -#include - -namespace kstd::os -{ - - auto panic(std::string_view message, std::source_location location) - { - throw kstd::tests::os_panic{std::string{message}, location}; - } - -} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/kstd/test_support/os_panic.tests.cpp b/libs/kstd/kstd/test_support/os_panic.tests.cpp new file mode 100644 index 00000000..c30411aa --- /dev/null +++ b/libs/kstd/kstd/test_support/os_panic.tests.cpp @@ -0,0 +1,15 @@ +#include + +#include +#include +#include + +namespace kstd::os +{ + + auto panic(std::string_view message, std::source_location location) + { + throw kstd::tests::os_panic{std::string{message}, location}; + } + +} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/kstd/vector.test.cpp b/libs/kstd/kstd/vector.test.cpp deleted file mode 100644 index 72c1127a..00000000 --- a/libs/kstd/kstd/vector.test.cpp +++ /dev/null @@ -1,2003 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - -SCENARIO("Vector initialization and construction", "[vector]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto v = kstd::vector{}; - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - } - - THEN("the size and capacity are zero") - { - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == 0); - } - } - - WHEN("constructing with a specific size") - { - auto v = kstd::vector(10); - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size is and capacity match the specified value") - { - REQUIRE(v.size() == 10); - REQUIRE(v.capacity() == 10); - } - } - - WHEN("constructing from an initializer list") - { - auto v = kstd::vector{1, 2, 3, 4, 5}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size is and capacity match the specified value") - { - REQUIRE(v.size() == 5); - REQUIRE(v.capacity() == 5); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 4); - REQUIRE(v[4] == 5); - } - } - } - - GIVEN("A non-empty range") - { - auto range = std::array{1, 2, 3}; - - WHEN("constructing from a random-access iterator range") - { - auto v = kstd::vector{std::begin(range), std::end(range)}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size and capacity match the range size") - { - REQUIRE(v.size() == std::size(range)); - REQUIRE(v.capacity() == std::size(range)); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("constructing from a range") - { - auto v = kstd::vector{kstd::from_range, range}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size and capacity match the range size") - { - REQUIRE(v.size() == std::ranges::size(range)); - REQUIRE(v.capacity() == std::ranges::size(range)); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - } - - GIVEN("A populated vector") - { - auto source = kstd::vector{1, 2, 3, 4, 5}; - - WHEN("copy constructing a new vector") - { - auto copy = kstd::vector(source); - - THEN("the copy matches the original") - { - REQUIRE(copy.size() == source.size()); - REQUIRE(copy.capacity() == source.capacity()); - REQUIRE(copy[0] == 1); - REQUIRE(copy[1] == 2); - REQUIRE(copy[2] == 3); - REQUIRE(copy[3] == 4); - REQUIRE(copy[4] == 5); - } - - THEN("the original is left unchanged") - { - REQUIRE(source.size() == 5); - REQUIRE(source.capacity() == 5); - REQUIRE(source[0] == 1); - REQUIRE(source[1] == 2); - REQUIRE(source[2] == 3); - REQUIRE(source[3] == 4); - REQUIRE(source[4] == 5); - } - } - - WHEN("move constructing a new vector") - { - auto moved = kstd::vector(std::move(source)); - - THEN("The new vector has the original elements") - { - REQUIRE(moved.size() == 5); - REQUIRE(moved.capacity() == 5); - REQUIRE(moved[0] == 1); - REQUIRE(moved[1] == 2); - REQUIRE(moved[2] == 3); - REQUIRE(moved[3] == 4); - REQUIRE(moved[4] == 5); - } - - THEN("The original vector is left in a valid but unspecified state") - { - REQUIRE(source.empty()); - REQUIRE(source.size() == 0); - REQUIRE(source.capacity() == 0); - } - } - } -} - -SCENARIO("Vector element access", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{10, 20, 30}; - - WHEN("accessing elements for reading") - { - THEN("operator[] and at() return the correct elements") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - - REQUIRE(v.at(0) == 10); - REQUIRE(v.at(1) == 20); - REQUIRE(v.at(2) == 30); - } - - THEN("front() and back() return the first and last elements") - { - REQUIRE(v.front() == 10); - REQUIRE(v.back() == 30); - } - - THEN("data() return a pointer to the contiguous storage") - { - auto ptr = v.data(); - REQUIRE(ptr); - REQUIRE(ptr[0] == 10); - REQUIRE(ptr[1] == 20); - REQUIRE(ptr[2] == 30); - } - - THEN("accessing out of bounds elements panics") - { - REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); - } - } - - WHEN("accessing elements for writing") - { - v[0] = 100; - v.at(1) = 200; - v.back() = 300; - - THEN("the elements are correctly modified") - { - REQUIRE(v[0] == 100); - REQUIRE(v[1] == 200); - REQUIRE(v[2] == 300); - } - } - } -} - -SCENARIO("Vector iterators", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{1, 2, 3}; - - WHEN("using forward iterators") - { - THEN("they navigate the elements in the correct forward order") - { - auto it = v.begin(); - REQUIRE(it != v.end()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it != v.end()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.end()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it == v.end()); - } - - THEN("const forward iterators provide correct access") - { - auto it = v.cbegin(); - REQUIRE(it != v.cend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it != v.cend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.cend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it == v.cend()); - } - } - - WHEN("using reverse iterators") - { - THEN("they navigate the elements in the correct reverse order") - { - auto it = v.rbegin(); - REQUIRE(it != v.rend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it != v.rend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.rend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it == v.rend()); - } - - THEN("const reverse iterators provide correct access") - { - auto it = v.crbegin(); - REQUIRE(it != v.crend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it != v.crend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.crend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it == v.crend()); - } - } - } - - GIVEN("an empty vector") - { - auto v = kstd::vector{}; - - WHEN("getting iterators") - { - THEN("begin() equals end() and cbegin() equals cend()") - { - REQUIRE(v.begin() == v.end()); - REQUIRE(v.cbegin() == v.cend()); - } - - THEN("rbegin() equals rend() and crbegin() equals crend()") - { - REQUIRE(v.rbegin() == v.rend()); - REQUIRE(v.crbegin() == v.crend()); - } - } - } -} - -SCENARIO("Vector capacity management", "[vector]") -{ - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("reserving space") - { - v.reserve(10); - - THEN("the capacity is at least the reserved amount") - { - REQUIRE(v.capacity() >= 10); - } - - THEN("the size is still zero") - { - REQUIRE(v.size() == 0); - } - - THEN("the vector is still empty") - { - REQUIRE(v.empty()); - } - } - - WHEN("reserving space less than or equal to current capacity") - { - v.reserve(10); - auto const current_capacity = v.capacity(); - v.reserve(5); - - THEN("the capacity remains unchanged") - { - REQUIRE(v.capacity() == current_capacity); - } - } - - WHEN("reserving space greater than max_size") - { - THEN("a panic is triggered") - { - REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic); - } - } - } - - GIVEN("A populated vector with excess capacity") - { - auto v = kstd::vector{1, 2, 3}; - v.reserve(10); - - REQUIRE(v.capacity() == 10); - - WHEN("calling shrink_to_fit") - { - v.shrink_to_fit(); - - THEN("the capacity is reduced to match the size") - { - REQUIRE(v.capacity() == 3); - REQUIRE(v.size() == 3); - } - - THEN("the elements remain unchanged") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - } -} - -SCENARIO("Vector modifiers", "[vector]") -{ - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("push_back is called with a value") - { - v.push_back(10); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 10); - } - } - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(20); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 20); - } - } - - WHEN("elements are added while capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - v.push_back(10); - v.emplace_back(20); - - THEN("the elements are added without reallocation") - { - REQUIRE(v.size() == 2); - REQUIRE(v.capacity() == capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - } - } - - WHEN("emplace is called with the end iterator and constructor arguments") - { - v.emplace(v.end(), 20); - - THEN("the element is appended and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 20); - } - } - - WHEN("emplace is called while capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - v.emplace(v.end(), 20); - - THEN("the element is appended without reallocation") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() == capacity); - REQUIRE(v.back() == 20); - } - } - - WHEN("inserting an element") - { - auto it = v.insert(v.cbegin(), 40); - - THEN("the size and capacity increase and the element is inserted") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 40); - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting an lvalue element") - { - auto const value = 40; - auto it = v.insert(v.cbegin(), value); - - THEN("the size and capacity increase and the element is inserted") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 40); - REQUIRE(it == v.begin()); - } - } - - WHEN("appending a range") - { - auto const range = std::views::iota(0, 3); - v.append_range(range); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 0); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 2); - } - } - - WHEN("appending from an input range") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - v.append_range(std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("appending from an input range with sufficient capacity") - { - v.reserve(3); - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - v.append_range(std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity stays the same") - { - REQUIRE(v.capacity() == 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("resizing the vector to a greater size") - { - v.resize(3); - - THEN("the size and capacity increase and the elements are value initialized") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() >= 3); - REQUIRE(v[0] == 0); - REQUIRE(v[1] == 0); - REQUIRE(v[2] == 0); - } - } - - WHEN("resizing the vector to a greater size with initial value") - { - v.resize(3, 2); - - THEN("the size and capacity increase and the elements are initialized to the given value") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() >= 3); - REQUIRE(v[0] == 2); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 2); - } - } - - WHEN("inserting a range at the beginning") - { - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the end") - { - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.end(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting from an input range without sufficient capacity") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - } - - GIVEN("A populated vector") - { - auto v = kstd::vector{10, 20, 30}; - auto initial_capacity = v.capacity(); - - WHEN("push_back is called") - { - v.push_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - } - } - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - } - } - - WHEN("emplace is called with an iterator and constructor arguments") - { - auto it = v.emplace(v.begin() + 2, 25); - - THEN("the element is inserted and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v.at(2) == 25); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.cbegin() + 2); - REQUIRE(*it == 25); - } - } - - WHEN("emplace is called with an iterator and sufficient capacity") - { - v.reserve(v.size() + 1); - - auto it = v.emplace(v.begin() + 2, 25); - - THEN("the element is inserted and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v.at(2) == 25); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.cbegin() + 2); - REQUIRE(*it == 25); - } - } - - WHEN("push_back is called with a reference to an internal element") - { - v.shrink_to_fit(); - auto const original_value = v[0]; - - v.push_back(v[0]); - - THEN("reallocation handles the internal reference safely without dangling") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == original_value); - REQUIRE(v.back() == original_value); - } - } - - WHEN("pop_back is called") - { - v.pop_back(); - - THEN("the last element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - } - } - - WHEN("clear is called") - { - v.clear(); - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == initial_capacity); - } - } - - WHEN("inserting at the beginning") - { - auto it = v.insert(v.cbegin(), 5); - - THEN("the element is inserted at the front") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 5); - REQUIRE(v[1] == 10); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting in the middle") - { - auto it = v.insert(v.cbegin() + 1, 15); - - THEN("the element is inserted in the middle") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 15); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting at the end") - { - auto it = v.insert(v.cend(), 40); - - THEN("the element is inserted at the back") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - REQUIRE(it == v.begin() + 3); - } - } - - WHEN("inserting an lvalue at the end") - { - auto const value = 40; - auto it = v.insert(v.cend(), value); - - THEN("the element is inserted at the back") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - REQUIRE(it == v.begin() + 3); - } - } - - WHEN("inserting when capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - auto it = v.insert(v.cbegin() + 1, 15); - - THEN("the element is added without reallocation") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() == capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 15); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting a reference to an existing element with reallocation") - { - v.shrink_to_fit(); - REQUIRE(v.capacity() == v.size()); - auto it = v.insert(v.cbegin() + 1, v[2]); - - THEN("the element is correctly copied and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting a reference to an existing element without reallocation") - { - v.reserve(10); - REQUIRE(v.capacity() > v.size()); - auto it = v.insert(v.cbegin() + 1, v[2]); - - THEN("the element is correctly copied and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting an rvalue reference to an existing element with reallocation") - { - v.shrink_to_fit(); - REQUIRE(v.capacity() == v.size()); - auto it = v.insert(v.cbegin() + 1, std::move(v[2])); - - THEN("the element is correctly moved and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting an rvalue reference to an existing element without reallocation") - { - v.reserve(10); - REQUIRE(v.capacity() > v.size()); - auto it = v.insert(v.cbegin() + 1, std::move(v[2])); - - THEN("the element is correctly moved and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the first element") - { - auto it = v.erase(v.cbegin()); - - THEN("the first element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 20); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin()); - } - } - - WHEN("erasing a middle element") - { - auto it = v.erase(v.cbegin() + 1); - - THEN("the middle element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the last element") - { - auto it = v.erase(v.cend() - 1); - - THEN("the last element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(it == v.end()); - } - } - - WHEN("erasing the end() iterator") - { - THEN("a panic is triggered") - { - REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); - } - } - - WHEN("erasing a range of elements") - { - auto it = v.erase(v.cbegin() + 1, v.cend() - 1); - - THEN("the specified range is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing an empty range") - { - auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); - - THEN("the vector is unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("appending a range") - { - auto initial_size = v.size(); - v.append_range(std::views::iota(0, 3)); - - THEN("capacity is increased") - { - REQUIRE(v.capacity() >= initial_capacity); - } - - THEN("size is increased") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[initial_size + 0] == 0); - REQUIRE(v[initial_size + 1] == 1); - REQUIRE(v[initial_size + 2] == 2); - } - } - - WHEN("resizing the vector to a greater size") - { - auto initial_size = v.size(); - v.resize(initial_size + 3); - - THEN("the size and capacity increase and the elements are value initialized") - { - REQUIRE(v.size() == initial_size + 3); - REQUIRE(v.capacity() >= initial_size + 3); - REQUIRE(v[initial_size + 0] == 0); - REQUIRE(v[initial_size + 1] == 0); - REQUIRE(v[initial_size + 2] == 0); - } - } - - WHEN("resizing the vector to a greater size with initial value") - { - auto initial_size = v.size(); - v.resize(initial_size + 3, 2); - - THEN("the size and capacity increase and the elements are initialized to the given value") - { - REQUIRE(v.size() == initial_size + 3); - REQUIRE(v.capacity() >= initial_size + 3); - REQUIRE(v[initial_size + 0] == 2); - REQUIRE(v[initial_size + 1] == 2); - REQUIRE(v[initial_size + 2] == 2); - } - } - - WHEN("resizing the vector to a smaller size") - { - v.resize(1); - - THEN("the size decreases and the elements are destroyed") - { - REQUIRE(v.size() == 1); - REQUIRE(v[0] == 10); - } - } - - WHEN("inserting an empty range") - { - auto initial_size = v.size(); - auto it = v.insert_range(v.begin(), std::views::empty); - - THEN("the size does not change") - { - REQUIRE(v.size() == initial_size); - } - - THEN("the capacity does not change") - { - REQUIRE(v.capacity() == initial_capacity); - } - - THEN("the content is unchanged") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - } - - THEN("the returned iterator points to the position of insertion") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the beginning") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 10); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the end") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.end(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 1); - REQUIRE(v[4] == 2); - REQUIRE(v[5] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin() + initial_size); - } - } - - WHEN("inserting a range that causes reallocation") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 2); - REQUIRE(v[3] == 3); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting fewer elements than the suffix without reallocation") - { - v.reserve(10); - auto const capacity = v.capacity(); - auto const arr = std::array{1}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the elements are correctly placed") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - } - - THEN("no reallocation occurs") - { - REQUIRE(v.capacity() == capacity); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting fewer elements than the suffix without sufficient capacity") - { - v.shrink_to_fit(); - auto const arr = std::array{1}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the elements are correctly placed") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting from an input range without sufficient capacity") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 6); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 6); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 10); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - } -} - -SCENARIO("Vector comparison", "[vector]") -{ - GIVEN("Two identical vectors") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 3}; - - WHEN("comparing for equality") - { - THEN("the vectors are equal") - { - REQUIRE(v1 == v2); - } - - THEN("the vectors and not not-equal") - { - REQUIRE_FALSE(v1 != v2); - } - } - - WHEN("comparing using the spaceship operator") - { - THEN("the vectors are equivalent") - { - REQUIRE((v1 <=> v2) == 0); - REQUIRE(v1 <= v2); - REQUIRE(v1 >= v2); - } - } - } - - GIVEN("Two vectors of different sizes") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 3, 4}; - - WHEN("comparing for equality") - { - THEN("the vectors are not equal") - { - REQUIRE_FALSE(v1 == v2); - } - - THEN("the vectors are not-equal") - { - REQUIRE(v1 != v2); - } - } - - WHEN("comparing for ordering") - { - THEN("the shorter vector evaluates as less than the longer vector") - { - REQUIRE(v1 < v2); - REQUIRE(v2 > v1); - } - } - } - - GIVEN("Two vectors of the same size but different elements") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 4}; - - WHEN("comparing for ordering") - { - THEN("they are ordered lexicographically") - { - REQUIRE(v1 < v2); - REQUIRE(v2 > v1); - } - } - } -} - -SCENARIO("Vector with non-default-constructible types", "[vector]") -{ - GIVEN("A type without a default constructor") - { - WHEN("constructing an empty vector") - { - auto v = kstd::vector{}; - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - } - } - - WHEN("using emplace_back") - { - auto v = kstd::vector{}; - v.emplace_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back() == kstd::tests::non_default_constructible{40}); - } - } - } -} - -SCENARIO("Vector with custom allocator", "[vector]") -{ - GIVEN("a tracking allocator acting as the vector's memory manager") - { - auto allocations = 0; - auto allocator = kstd::tests::tracking_allocator{&allocations}; - - WHEN("a vector uses this allocator to allocate memory") - { - auto v = kstd::vector>(allocator); - REQUIRE(allocations == 0); - - v.reserve(10); - - THEN("the allocator was used to allocate memory") - { - REQUIRE(allocations > 0); - } - } - } -} - -SCENARIO("Vector modifier move semantics", "[vector]") -{ - GIVEN("An empty vector and a move tracker element") - { - auto v = kstd::vector{}; - auto tracker = kstd::tests::special_member_tracker{40}; - - WHEN("push_back is called with the move tracker") - { - v.push_back(std::move(tracker)); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().move_constructed_count == 1); - REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 40); - } - - THEN("the original tracker is left in a valid but unspecified state") - { - REQUIRE(tracker.value == -1); - } - } - } - - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(40); - - THEN("the element is constructed directly, without moves or copies") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().move_constructed_count == 0); - REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 40); - } - } - } - - GIVEN("A populated vector of move trackers") - { - auto v = kstd::vector{}; - v.reserve(10); - v.emplace_back(10); - v.emplace_back(20); - v.emplace_back(30); - - WHEN("inserting an element in the middle with sufficient capacity") - { - auto const tracker = kstd::tests::special_member_tracker{15}; - v.insert(v.cbegin() + 1, tracker); - - THEN("the shifted elements are move-assigned and the new element is copy-assigned") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - REQUIRE(v[1].value == 15); - REQUIRE(v[1].move_assigned_count == 0); - REQUIRE(v[1].copy_assigned_count == 1); - REQUIRE(tracker.copied_from_count == 1); - REQUIRE(v[2].value == 20); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[2].copy_assigned_count == 0); - REQUIRE(v[3].value == 30); - REQUIRE(v[3].move_constructed_count == 1); - } - } - - WHEN("inserting an rvalue element in the middle with sufficient capacity") - { - auto tracker = kstd::tests::special_member_tracker{15}; - v.insert(v.cbegin() + 1, std::move(tracker)); - - THEN("the shifted elements are move-assigned and the new element is move-assigned") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - REQUIRE(v[1].value == 15); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - REQUIRE(tracker.moved_from_count == 1); - REQUIRE(v[2].value == 20); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[3].value == 30); - REQUIRE(v[3].move_constructed_count == 1); - } - } - - WHEN("erasing an element in the middle") - { - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cbegin() + 1); - - THEN("the subsequent elements are move-assigned leftwards") - { - REQUIRE(v.size() == 2); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 30); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v.data()[2].destroyed_count == 1); - REQUIRE(v.data()[2].moved_from_count == 1); - - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the last element") - { - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cend() - 1); - - THEN("no elements are moved, just the last element destroyed") - { - REQUIRE(v.size() == 2); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_constructed_count == 0); - REQUIRE(v[0].copy_constructed_count == 0); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 20); - REQUIRE(v[1].move_constructed_count == 0); - REQUIRE(v[1].copy_constructed_count == 0); - REQUIRE(v[1].move_assigned_count == 0); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v.data()[2].destroyed_count == 1); - REQUIRE(v.data()[2].moved_from_count == 0); - REQUIRE(v.data()[2].copied_from_count == 0); - - REQUIRE(it == v.end()); - } - } - - WHEN("erasing a range of elements in the middle") - { - v.emplace_back(40); - v.emplace_back(50); - - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); - - THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") - { - REQUIRE(v.size() == 3); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_constructed_count == 0); - REQUIRE(v[0].copy_constructed_count == 0); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 40); - REQUIRE(v[1].move_constructed_count == 0); - REQUIRE(v[1].copy_constructed_count == 0); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v[2].value == 50); - REQUIRE(v[2].move_constructed_count == 0); - REQUIRE(v[2].copy_constructed_count == 0); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[2].copy_assigned_count == 0); - - REQUIRE(v.data()[3].destroyed_count == 1); - REQUIRE(v.data()[3].moved_from_count == 1); - REQUIRE(v.data()[3].copied_from_count == 0); - - REQUIRE(v.data()[4].destroyed_count == 1); - REQUIRE(v.data()[4].moved_from_count == 1); - REQUIRE(v.data()[4].copied_from_count == 0); - - REQUIRE(it == v.begin() + 1); - } - } - } -} - -SCENARIO("Vector advanced construction", "[vector]") -{ - GIVEN("A count and a default value") - { - WHEN("constructing with count and default argument") - { - auto const count = 5uz; - auto const value = 42; - auto v = kstd::vector(count, value); - - THEN("the vector is initialized with count copies of value") - { - REQUIRE(v.size() == 5); - REQUIRE(v.capacity() == 5); - REQUIRE(v.front() == 42); - REQUIRE(v.back() == 42); - } - } - } - - GIVEN("A pure input iterator range") - { - WHEN("constructing from input iterators") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - auto v = kstd::vector(first, last); - - THEN("the vector is generated dynamically and initialized correctly") - { - REQUIRE(v.size() == 3); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - } - - GIVEN("A tracking allocator and a source vector") - { - auto allocs = 0; - auto allocator = kstd::tests::tracking_allocator{&allocs}; - auto source = kstd::vector>{allocator}; - source.push_back(1); - source.push_back(2); - source.push_back(3); - - allocs = 0; - - WHEN("copy constructing with an allocator") - { - auto copy = kstd::vector>(source, allocator); - - THEN("the copy succeeds and the allocator is used") - { - REQUIRE(copy.size() == 3); - REQUIRE(allocs > 0); - REQUIRE(copy[0] == 1); - REQUIRE(copy[2] == 3); - } - } - - WHEN("move constructing with an identically comparing allocator") - { - auto moved = kstd::vector>(std::move(source), allocator); - - THEN("the move succeeds and no new allocations are made (memory is stolen)") - { - REQUIRE(moved.size() == 3); - REQUIRE(allocs == 0); - REQUIRE(moved[0] == 1); - REQUIRE(moved[2] == 3); - } - } - - WHEN("move constructing with a non-equal allocator") - { - auto allocs2 = 0; - auto allocator2 = kstd::tests::tracking_allocator{&allocs2}; - - auto moved = kstd::vector>(std::move(source), allocator2); - - THEN("the move allocates new memory and moves elements") - { - REQUIRE(allocs2 > 0); - REQUIRE(moved.size() == 3); - REQUIRE(source.empty()); - } - } - } -} - -SCENARIO("Vector assignment operators", "[vector]") -{ - GIVEN("A source vector and an empty target vector") - { - auto source = kstd::vector{1, 2, 3}; - auto target = kstd::vector{}; - - WHEN("copy assigning") - { - target = source; - - THEN("the target matches the source") - { - REQUIRE(target.size() == 3); - REQUIRE(target[0] == 1); - REQUIRE(target[2] == 3); - REQUIRE(source.size() == 3); - } - } - - WHEN("move assigning") - { - target = std::move(source); - - THEN("the target assumes the source's data") - { - REQUIRE(target.size() == 3); - REQUIRE(target[0] == 1); - REQUIRE(target[2] == 3); - REQUIRE(source.empty()); - } - } - } - - GIVEN("Vectors with propagating copy allocator") - { - auto allocs1 = 0; - auto allocs2 = 0; - auto alloc1 = kstd::tests::propagating_allocator{&allocs1}; - auto alloc2 = kstd::tests::propagating_allocator{&allocs2}; - - auto v1 = kstd::vector>{alloc1}; - v1.push_back(1); - v1.push_back(2); - auto v2 = kstd::vector>{alloc2}; - - WHEN("copy assigning") - { - v2 = v1; - THEN("the allocator propagates") - { - REQUIRE(v2.get_allocator() == v1.get_allocator()); - } - } - } - - GIVEN("Vectors for copy assignment overlap") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{4, 5}; - v2.reserve(10); - - WHEN("copy assigning a larger vector to a smaller one with enough capacity") - { - v2 = v1; - THEN("elements are copied and size is updated") - { - REQUIRE(v2.size() == 3); - REQUIRE(v2.capacity() >= 10); - REQUIRE(v2[2] == 3); - } - } - - auto v3 = kstd::vector{1, 2, 3, 4}; - v3.reserve(10); - WHEN("copy assigning a smaller vector to a larger one") - { - v3 = v1; - THEN("excess elements are destroyed") - { - REQUIRE(v3.size() == 3); - REQUIRE(v3[0] == 1); - } - } - } - - GIVEN("Vectors with the same tracking allocator") - { - auto allocs = 0; - auto alloc = kstd::tests::tracking_allocator{&allocs}; - auto v1 = kstd::vector>{alloc}; - v1.push_back(1); - auto v2 = kstd::vector>{alloc}; - - WHEN("move assigning") - { - v2 = std::move(v1); - THEN("memory is stolen without allocation") - { - REQUIRE(v2.size() == 1); - REQUIRE(allocs == 1); - } - } - } - - GIVEN("Vectors with different non-propagating tracking allocators") - { - auto allocs1 = 0; - auto allocs2 = 0; - auto alloc1 = kstd::tests::tracking_allocator{&allocs1}; - auto alloc2 = kstd::tests::tracking_allocator{&allocs2}; - - auto v1 = kstd::vector>{alloc1}; - v1.push_back(1); - v1.push_back(2); - v1.push_back(3); - - auto v2 = kstd::vector>{alloc2}; - v2.push_back(4); - v2.push_back(5); - - WHEN("move assigning a larger vector to a smaller one without enough capacity") - { - v2.shrink_to_fit(); - v2 = std::move(v1); - THEN("memory is reallocated and elements are moved") - { - REQUIRE(v2.size() == 3); - REQUIRE(allocs2 > 2); - } - } - - auto v3 = kstd::vector>{alloc2}; - v3.reserve(10); - v3.push_back(4); - v3.push_back(5); - WHEN("move assigning a larger vector to a smaller one with enough capacity") - { - v3 = std::move(v1); - THEN("elements are move-assigned over overlap and move-constructed over remainder") - { - REQUIRE(v3.size() == 3); - } - } - - auto v4 = kstd::vector>{alloc2}; - v4.reserve(10); - v4.push_back(4); - v4.push_back(5); - v4.push_back(6); - v4.push_back(7); - WHEN("move assigning a smaller vector to a larger one with enough capacity") - { - v4 = std::move(v1); - THEN("overlap is moved and excess is destroyed") - { - REQUIRE(v4.size() == 3); - } - } - } -} - -SCENARIO("Vector self-assignment operators", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{1, 2, 3}; - auto const initial_capacity = v.capacity(); - auto const * initial_data = v.data(); - - WHEN("copy assigning to itself") - { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wself-assign-overloaded" - v = v; -#pragma GCC diagnostic pop - - THEN("the vector remains unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v.data() == initial_data); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - - WHEN("move assigning to itself") - { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wself-move" - v = std::move(v); -#pragma GCC diagnostic pop - - THEN("the vector remains unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v.data() == initial_data); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - } -} - -SCENARIO("Vector const accessors and copy insertion", "[vector]") -{ - GIVEN("A const populated vector") - { - auto const v = kstd::vector{10, 20, 30}; - - WHEN("calling const accessors") - { - THEN("elements are read correctly as const references") - { - REQUIRE(v.front() == 10); - REQUIRE(v.back() == 30); - REQUIRE(v[1] == 20); - REQUIRE(v.at(1) == 20); - } - } - } - - GIVEN("An empty vector and a const lvalue tracker") - { - auto v = kstd::vector{}; - auto const tracker = kstd::tests::special_member_tracker{42}; - - WHEN("push_back is called with the const lvalue") - { - v.push_back(tracker); - - THEN("the element is gracefully copy-constructed") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().value == 42); - REQUIRE(v.back().copy_constructed_count == 1); - REQUIRE(v.back().move_constructed_count == 0); - } - } - - WHEN("push_back is called with a const lvalue when capacity is sufficient") - { - v.reserve(10); - auto const current_capacity = v.capacity(); - v.push_back(tracker); - - THEN("the element is copy-constructed without reallocation") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() == current_capacity); - REQUIRE(v.back().value == 42); - REQUIRE(v.back().copy_constructed_count == 1); - REQUIRE(v.back().move_constructed_count == 0); - } - } - } -} diff --git a/libs/kstd/kstd/vector.tests.cpp b/libs/kstd/kstd/vector.tests.cpp new file mode 100644 index 00000000..72c1127a --- /dev/null +++ b/libs/kstd/kstd/vector.tests.cpp @@ -0,0 +1,2003 @@ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +SCENARIO("Vector initialization and construction", "[vector]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto v = kstd::vector{}; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + + THEN("the size and capacity are zero") + { + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == 0); + } + } + + WHEN("constructing with a specific size") + { + auto v = kstd::vector(10); + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 10); + REQUIRE(v.capacity() == 10); + } + } + + WHEN("constructing from an initializer list") + { + auto v = kstd::vector{1, 2, 3, 4, 5}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 4); + REQUIRE(v[4] == 5); + } + } + } + + GIVEN("A non-empty range") + { + auto range = std::array{1, 2, 3}; + + WHEN("constructing from a random-access iterator range") + { + auto v = kstd::vector{std::begin(range), std::end(range)}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::size(range)); + REQUIRE(v.capacity() == std::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("constructing from a range") + { + auto v = kstd::vector{kstd::from_range, range}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::ranges::size(range)); + REQUIRE(v.capacity() == std::ranges::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A populated vector") + { + auto source = kstd::vector{1, 2, 3, 4, 5}; + + WHEN("copy constructing a new vector") + { + auto copy = kstd::vector(source); + + THEN("the copy matches the original") + { + REQUIRE(copy.size() == source.size()); + REQUIRE(copy.capacity() == source.capacity()); + REQUIRE(copy[0] == 1); + REQUIRE(copy[1] == 2); + REQUIRE(copy[2] == 3); + REQUIRE(copy[3] == 4); + REQUIRE(copy[4] == 5); + } + + THEN("the original is left unchanged") + { + REQUIRE(source.size() == 5); + REQUIRE(source.capacity() == 5); + REQUIRE(source[0] == 1); + REQUIRE(source[1] == 2); + REQUIRE(source[2] == 3); + REQUIRE(source[3] == 4); + REQUIRE(source[4] == 5); + } + } + + WHEN("move constructing a new vector") + { + auto moved = kstd::vector(std::move(source)); + + THEN("The new vector has the original elements") + { + REQUIRE(moved.size() == 5); + REQUIRE(moved.capacity() == 5); + REQUIRE(moved[0] == 1); + REQUIRE(moved[1] == 2); + REQUIRE(moved[2] == 3); + REQUIRE(moved[3] == 4); + REQUIRE(moved[4] == 5); + } + + THEN("The original vector is left in a valid but unspecified state") + { + REQUIRE(source.empty()); + REQUIRE(source.size() == 0); + REQUIRE(source.capacity() == 0); + } + } + } +} + +SCENARIO("Vector element access", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{10, 20, 30}; + + WHEN("accessing elements for reading") + { + THEN("operator[] and at() return the correct elements") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + + REQUIRE(v.at(0) == 10); + REQUIRE(v.at(1) == 20); + REQUIRE(v.at(2) == 30); + } + + THEN("front() and back() return the first and last elements") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + } + + THEN("data() return a pointer to the contiguous storage") + { + auto ptr = v.data(); + REQUIRE(ptr); + REQUIRE(ptr[0] == 10); + REQUIRE(ptr[1] == 20); + REQUIRE(ptr[2] == 30); + } + + THEN("accessing out of bounds elements panics") + { + REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); + } + } + + WHEN("accessing elements for writing") + { + v[0] = 100; + v.at(1) = 200; + v.back() = 300; + + THEN("the elements are correctly modified") + { + REQUIRE(v[0] == 100); + REQUIRE(v[1] == 200); + REQUIRE(v[2] == 300); + } + } + } +} + +SCENARIO("Vector iterators", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{1, 2, 3}; + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = v.begin(); + REQUIRE(it != v.end()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = v.cbegin(); + REQUIRE(it != v.cend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = v.rbegin(); + REQUIRE(it != v.rend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = v.crbegin(); + REQUIRE(it != v.crend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.crend()); + } + } + } + + GIVEN("an empty vector") + { + auto v = kstd::vector{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(v.begin() == v.end()); + REQUIRE(v.cbegin() == v.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(v.rbegin() == v.rend()); + REQUIRE(v.crbegin() == v.crend()); + } + } + } +} + +SCENARIO("Vector capacity management", "[vector]") +{ + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("reserving space") + { + v.reserve(10); + + THEN("the capacity is at least the reserved amount") + { + REQUIRE(v.capacity() >= 10); + } + + THEN("the size is still zero") + { + REQUIRE(v.size() == 0); + } + + THEN("the vector is still empty") + { + REQUIRE(v.empty()); + } + } + + WHEN("reserving space less than or equal to current capacity") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.reserve(5); + + THEN("the capacity remains unchanged") + { + REQUIRE(v.capacity() == current_capacity); + } + } + + WHEN("reserving space greater than max_size") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic); + } + } + } + + GIVEN("A populated vector with excess capacity") + { + auto v = kstd::vector{1, 2, 3}; + v.reserve(10); + + REQUIRE(v.capacity() == 10); + + WHEN("calling shrink_to_fit") + { + v.shrink_to_fit(); + + THEN("the capacity is reduced to match the size") + { + REQUIRE(v.capacity() == 3); + REQUIRE(v.size() == 3); + } + + THEN("the elements remain unchanged") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } +} + +SCENARIO("Vector modifiers", "[vector]") +{ + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("push_back is called with a value") + { + v.push_back(10); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 10); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(20); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + + WHEN("elements are added while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.push_back(10); + v.emplace_back(20); + + THEN("the elements are added without reallocation") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } + + WHEN("emplace is called with the end iterator and constructor arguments") + { + v.emplace(v.end(), 20); + + THEN("the element is appended and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + + WHEN("emplace is called while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.emplace(v.end(), 20); + + THEN("the element is appended without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == capacity); + REQUIRE(v.back() == 20); + } + } + + WHEN("inserting an element") + { + auto it = v.insert(v.cbegin(), 40); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 40); + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting an lvalue element") + { + auto const value = 40; + auto it = v.insert(v.cbegin(), value); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 40); + REQUIRE(it == v.begin()); + } + } + + WHEN("appending a range") + { + auto const range = std::views::iota(0, 3); + v.append_range(range); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + } + } + + WHEN("appending from an input range") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("appending from an input range with sufficient capacity") + { + v.reserve(3); + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity stays the same") + { + REQUIRE(v.capacity() == 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("resizing the vector to a greater size") + { + v.resize(3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 0); + REQUIRE(v[2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + v.resize(3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 2); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 2); + } + } + + WHEN("inserting a range at the beginning") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + } + + GIVEN("A populated vector") + { + auto v = kstd::vector{10, 20, 30}; + auto initial_capacity = v.capacity(); + + WHEN("push_back is called") + { + v.push_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("emplace is called with an iterator and constructor arguments") + { + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + + WHEN("emplace is called with an iterator and sufficient capacity") + { + v.reserve(v.size() + 1); + + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + + WHEN("push_back is called with a reference to an internal element") + { + v.shrink_to_fit(); + auto const original_value = v[0]; + + v.push_back(v[0]); + + THEN("reallocation handles the internal reference safely without dangling") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == original_value); + REQUIRE(v.back() == original_value); + } + } + + WHEN("pop_back is called") + { + v.pop_back(); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } + + WHEN("clear is called") + { + v.clear(); + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == initial_capacity); + } + } + + WHEN("inserting at the beginning") + { + auto it = v.insert(v.cbegin(), 5); + + THEN("the element is inserted at the front") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 5); + REQUIRE(v[1] == 10); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting in the middle") + { + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is inserted in the middle") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting at the end") + { + auto it = v.insert(v.cend(), 40); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + + WHEN("inserting an lvalue at the end") + { + auto const value = 40; + auto it = v.insert(v.cend(), value); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + + WHEN("inserting when capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is added without reallocation") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the first element") + { + auto it = v.erase(v.cbegin()); + + THEN("the first element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 20); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("erasing a middle element") + { + auto it = v.erase(v.cbegin() + 1); + + THEN("the middle element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + auto it = v.erase(v.cend() - 1); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(it == v.end()); + } + } + + WHEN("erasing the end() iterator") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); + } + } + + WHEN("erasing a range of elements") + { + auto it = v.erase(v.cbegin() + 1, v.cend() - 1); + + THEN("the specified range is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing an empty range") + { + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); + + THEN("the vector is unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("appending a range") + { + auto initial_size = v.size(); + v.append_range(std::views::iota(0, 3)); + + THEN("capacity is increased") + { + REQUIRE(v.capacity() >= initial_capacity); + } + + THEN("size is increased") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 1); + REQUIRE(v[initial_size + 2] == 2); + } + } + + WHEN("resizing the vector to a greater size") + { + auto initial_size = v.size(); + v.resize(initial_size + 3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 0); + REQUIRE(v[initial_size + 2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + auto initial_size = v.size(); + v.resize(initial_size + 3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 2); + REQUIRE(v[initial_size + 1] == 2); + REQUIRE(v[initial_size + 2] == 2); + } + } + + WHEN("resizing the vector to a smaller size") + { + v.resize(1); + + THEN("the size decreases and the elements are destroyed") + { + REQUIRE(v.size() == 1); + REQUIRE(v[0] == 10); + } + } + + WHEN("inserting an empty range") + { + auto initial_size = v.size(); + auto it = v.insert_range(v.begin(), std::views::empty); + + THEN("the size does not change") + { + REQUIRE(v.size() == initial_size); + } + + THEN("the capacity does not change") + { + REQUIRE(v.capacity() == initial_capacity); + } + + THEN("the content is unchanged") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + } + + THEN("the returned iterator points to the position of insertion") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the beginning") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 1); + REQUIRE(v[4] == 2); + REQUIRE(v[5] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + initial_size); + } + } + + WHEN("inserting a range that causes reallocation") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + REQUIRE(v[3] == 3); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without reallocation") + { + v.reserve(10); + auto const capacity = v.capacity(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("no reallocation occurs") + { + REQUIRE(v.capacity() == capacity); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without sufficient capacity") + { + v.shrink_to_fit(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 6); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 6); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + } +} + +SCENARIO("Vector comparison", "[vector]") +{ + GIVEN("Two identical vectors") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3}; + + WHEN("comparing for equality") + { + THEN("the vectors are equal") + { + REQUIRE(v1 == v2); + } + + THEN("the vectors and not not-equal") + { + REQUIRE_FALSE(v1 != v2); + } + } + + WHEN("comparing using the spaceship operator") + { + THEN("the vectors are equivalent") + { + REQUIRE((v1 <=> v2) == 0); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + } + } + } + + GIVEN("Two vectors of different sizes") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3, 4}; + + WHEN("comparing for equality") + { + THEN("the vectors are not equal") + { + REQUIRE_FALSE(v1 == v2); + } + + THEN("the vectors are not-equal") + { + REQUIRE(v1 != v2); + } + } + + WHEN("comparing for ordering") + { + THEN("the shorter vector evaluates as less than the longer vector") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } + + GIVEN("Two vectors of the same size but different elements") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 4}; + + WHEN("comparing for ordering") + { + THEN("they are ordered lexicographically") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } +} + +SCENARIO("Vector with non-default-constructible types", "[vector]") +{ + GIVEN("A type without a default constructor") + { + WHEN("constructing an empty vector") + { + auto v = kstd::vector{}; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + } + + WHEN("using emplace_back") + { + auto v = kstd::vector{}; + v.emplace_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back() == kstd::tests::non_default_constructible{40}); + } + } + } +} + +SCENARIO("Vector with custom allocator", "[vector]") +{ + GIVEN("a tracking allocator acting as the vector's memory manager") + { + auto allocations = 0; + auto allocator = kstd::tests::tracking_allocator{&allocations}; + + WHEN("a vector uses this allocator to allocate memory") + { + auto v = kstd::vector>(allocator); + REQUIRE(allocations == 0); + + v.reserve(10); + + THEN("the allocator was used to allocate memory") + { + REQUIRE(allocations > 0); + } + } + } +} + +SCENARIO("Vector modifier move semantics", "[vector]") +{ + GIVEN("An empty vector and a move tracker element") + { + auto v = kstd::vector{}; + auto tracker = kstd::tests::special_member_tracker{40}; + + WHEN("push_back is called with the move tracker") + { + v.push_back(std::move(tracker)); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().move_constructed_count == 1); + REQUIRE(v.back().copy_constructed_count == 0); + REQUIRE(v.back().value == 40); + } + + THEN("the original tracker is left in a valid but unspecified state") + { + REQUIRE(tracker.value == -1); + } + } + } + + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(40); + + THEN("the element is constructed directly, without moves or copies") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().move_constructed_count == 0); + REQUIRE(v.back().copy_constructed_count == 0); + REQUIRE(v.back().value == 40); + } + } + } + + GIVEN("A populated vector of move trackers") + { + auto v = kstd::vector{}; + v.reserve(10); + v.emplace_back(10); + v.emplace_back(20); + v.emplace_back(30); + + WHEN("inserting an element in the middle with sufficient capacity") + { + auto const tracker = kstd::tests::special_member_tracker{15}; + v.insert(v.cbegin() + 1, tracker); + + THEN("the shifted elements are move-assigned and the new element is copy-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + REQUIRE(v[1].value == 15); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 1); + REQUIRE(tracker.copied_from_count == 1); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].move_constructed_count == 1); + } + } + + WHEN("inserting an rvalue element in the middle with sufficient capacity") + { + auto tracker = kstd::tests::special_member_tracker{15}; + v.insert(v.cbegin() + 1, std::move(tracker)); + + THEN("the shifted elements are move-assigned and the new element is move-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + REQUIRE(v[1].value == 15); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + REQUIRE(tracker.moved_from_count == 1); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].move_constructed_count == 1); + } + } + + WHEN("erasing an element in the middle") + { + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cbegin() + 1); + + THEN("the subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 30); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 1); + + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cend() - 1); + + THEN("no elements are moved, just the last element destroyed") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 20); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 0); + REQUIRE(v.data()[2].copied_from_count == 0); + + REQUIRE(it == v.end()); + } + } + + WHEN("erasing a range of elements in the middle") + { + v.emplace_back(40); + v.emplace_back(50); + + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); + + THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 3); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 40); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v[2].value == 50); + REQUIRE(v[2].move_constructed_count == 0); + REQUIRE(v[2].copy_constructed_count == 0); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + + REQUIRE(v.data()[3].destroyed_count == 1); + REQUIRE(v.data()[3].moved_from_count == 1); + REQUIRE(v.data()[3].copied_from_count == 0); + + REQUIRE(v.data()[4].destroyed_count == 1); + REQUIRE(v.data()[4].moved_from_count == 1); + REQUIRE(v.data()[4].copied_from_count == 0); + + REQUIRE(it == v.begin() + 1); + } + } + } +} + +SCENARIO("Vector advanced construction", "[vector]") +{ + GIVEN("A count and a default value") + { + WHEN("constructing with count and default argument") + { + auto const count = 5uz; + auto const value = 42; + auto v = kstd::vector(count, value); + + THEN("the vector is initialized with count copies of value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + REQUIRE(v.front() == 42); + REQUIRE(v.back() == 42); + } + } + } + + GIVEN("A pure input iterator range") + { + WHEN("constructing from input iterators") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + auto v = kstd::vector(first, last); + + THEN("the vector is generated dynamically and initialized correctly") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A tracking allocator and a source vector") + { + auto allocs = 0; + auto allocator = kstd::tests::tracking_allocator{&allocs}; + auto source = kstd::vector>{allocator}; + source.push_back(1); + source.push_back(2); + source.push_back(3); + + allocs = 0; + + WHEN("copy constructing with an allocator") + { + auto copy = kstd::vector>(source, allocator); + + THEN("the copy succeeds and the allocator is used") + { + REQUIRE(copy.size() == 3); + REQUIRE(allocs > 0); + REQUIRE(copy[0] == 1); + REQUIRE(copy[2] == 3); + } + } + + WHEN("move constructing with an identically comparing allocator") + { + auto moved = kstd::vector>(std::move(source), allocator); + + THEN("the move succeeds and no new allocations are made (memory is stolen)") + { + REQUIRE(moved.size() == 3); + REQUIRE(allocs == 0); + REQUIRE(moved[0] == 1); + REQUIRE(moved[2] == 3); + } + } + + WHEN("move constructing with a non-equal allocator") + { + auto allocs2 = 0; + auto allocator2 = kstd::tests::tracking_allocator{&allocs2}; + + auto moved = kstd::vector>(std::move(source), allocator2); + + THEN("the move allocates new memory and moves elements") + { + REQUIRE(allocs2 > 0); + REQUIRE(moved.size() == 3); + REQUIRE(source.empty()); + } + } + } +} + +SCENARIO("Vector assignment operators", "[vector]") +{ + GIVEN("A source vector and an empty target vector") + { + auto source = kstd::vector{1, 2, 3}; + auto target = kstd::vector{}; + + WHEN("copy assigning") + { + target = source; + + THEN("the target matches the source") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.size() == 3); + } + } + + WHEN("move assigning") + { + target = std::move(source); + + THEN("the target assumes the source's data") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.empty()); + } + } + } + + GIVEN("Vectors with propagating copy allocator") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = kstd::tests::propagating_allocator{&allocs1}; + auto alloc2 = kstd::tests::propagating_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + auto v2 = kstd::vector>{alloc2}; + + WHEN("copy assigning") + { + v2 = v1; + THEN("the allocator propagates") + { + REQUIRE(v2.get_allocator() == v1.get_allocator()); + } + } + } + + GIVEN("Vectors for copy assignment overlap") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{4, 5}; + v2.reserve(10); + + WHEN("copy assigning a larger vector to a smaller one with enough capacity") + { + v2 = v1; + THEN("elements are copied and size is updated") + { + REQUIRE(v2.size() == 3); + REQUIRE(v2.capacity() >= 10); + REQUIRE(v2[2] == 3); + } + } + + auto v3 = kstd::vector{1, 2, 3, 4}; + v3.reserve(10); + WHEN("copy assigning a smaller vector to a larger one") + { + v3 = v1; + THEN("excess elements are destroyed") + { + REQUIRE(v3.size() == 3); + REQUIRE(v3[0] == 1); + } + } + } + + GIVEN("Vectors with the same tracking allocator") + { + auto allocs = 0; + auto alloc = kstd::tests::tracking_allocator{&allocs}; + auto v1 = kstd::vector>{alloc}; + v1.push_back(1); + auto v2 = kstd::vector>{alloc}; + + WHEN("move assigning") + { + v2 = std::move(v1); + THEN("memory is stolen without allocation") + { + REQUIRE(v2.size() == 1); + REQUIRE(allocs == 1); + } + } + } + + GIVEN("Vectors with different non-propagating tracking allocators") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = kstd::tests::tracking_allocator{&allocs1}; + auto alloc2 = kstd::tests::tracking_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + + auto v2 = kstd::vector>{alloc2}; + v2.push_back(4); + v2.push_back(5); + + WHEN("move assigning a larger vector to a smaller one without enough capacity") + { + v2.shrink_to_fit(); + v2 = std::move(v1); + THEN("memory is reallocated and elements are moved") + { + REQUIRE(v2.size() == 3); + REQUIRE(allocs2 > 2); + } + } + + auto v3 = kstd::vector>{alloc2}; + v3.reserve(10); + v3.push_back(4); + v3.push_back(5); + WHEN("move assigning a larger vector to a smaller one with enough capacity") + { + v3 = std::move(v1); + THEN("elements are move-assigned over overlap and move-constructed over remainder") + { + REQUIRE(v3.size() == 3); + } + } + + auto v4 = kstd::vector>{alloc2}; + v4.reserve(10); + v4.push_back(4); + v4.push_back(5); + v4.push_back(6); + v4.push_back(7); + WHEN("move assigning a smaller vector to a larger one with enough capacity") + { + v4 = std::move(v1); + THEN("overlap is moved and excess is destroyed") + { + REQUIRE(v4.size() == 3); + } + } + } +} + +SCENARIO("Vector self-assignment operators", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{1, 2, 3}; + auto const initial_capacity = v.capacity(); + auto const * initial_data = v.data(); + + WHEN("copy assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-assign-overloaded" + v = v; +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + + WHEN("move assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-move" + v = std::move(v); +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } +} + +SCENARIO("Vector const accessors and copy insertion", "[vector]") +{ + GIVEN("A const populated vector") + { + auto const v = kstd::vector{10, 20, 30}; + + WHEN("calling const accessors") + { + THEN("elements are read correctly as const references") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + REQUIRE(v[1] == 20); + REQUIRE(v.at(1) == 20); + } + } + } + + GIVEN("An empty vector and a const lvalue tracker") + { + auto v = kstd::vector{}; + auto const tracker = kstd::tests::special_member_tracker{42}; + + WHEN("push_back is called with the const lvalue") + { + v.push_back(tracker); + + THEN("the element is gracefully copy-constructed") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); + } + } + + WHEN("push_back is called with a const lvalue when capacity is sufficient") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.push_back(tracker); + + THEN("the element is copy-constructed without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == current_capacity); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); + } + } + } +} -- cgit v1.2.3 From 878af4b049e0236daa02d075e693c9c630d89aca Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 14:33:58 +0200 Subject: cmake: extract test target creation --- cmake/Modules/BuildHostTests.cmake | 35 +++++++++++++++++++++ kernel/CMakeLists.txt | 64 ++++---------------------------------- libs/acpi/CMakeLists.txt | 27 ++-------------- libs/kstd/CMakeLists.txt | 30 ++---------------- 4 files changed, 45 insertions(+), 111 deletions(-) create mode 100644 cmake/Modules/BuildHostTests.cmake diff --git a/cmake/Modules/BuildHostTests.cmake b/cmake/Modules/BuildHostTests.cmake new file mode 100644 index 00000000..aa3ae66d --- /dev/null +++ b/cmake/Modules/BuildHostTests.cmake @@ -0,0 +1,35 @@ +include_guard(GLOBAL) + +function("teachos_add_tests" NAME) + add_executable("${NAME}_tests") + add_executable("${NAME}::tests" ALIAS "${NAME}_tests") + + file(GLOB_RECURSE + TEST_SOURCES + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "*.tests.cpp" + ) + + target_sources("${NAME}_tests" PRIVATE + ${TEST_SOURCES} + ) + + target_link_libraries("${NAME}_tests" PRIVATE + "Catch2::Catch2WithMain" + "${NAME}::lib" + ) + + set_target_properties("${NAME}_tests" PROPERTIES + EXCLUDE_FROM_ALL NO + ) + + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bht_results") + + catch_discover_tests("${NAME}::tests" + REPORTER junit + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/bht_results" + OUTPUT_SUFFIX ".xml" + ) +endfunction() \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d50944e4..34d71de1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -140,79 +140,32 @@ endif() if(BUILD_TESTING) find_package("Catch2") include("Catch") + include("BuildHostTests") - enable_coverage("kernel_lib") - - add_executable("kernel_tests") - add_executable("kernel::tests" ALIAS "kernel_tests") + teachos_add_tests("kernel") target_sources("kernel_tests" PRIVATE - # Platform-defined KAPI "kernel/test_support/kapi/cpu.cpp" "kernel/test_support/kapi/cio.cpp" "kernel/test_support/kapi/interrupts.cpp" "kernel/test_support/kapi/memory.cpp" - # Device Subsystem Support "kernel/test_support/devices/block_device.cpp" "kernel/test_support/devices/character_device.cpp" - # Filesystem Subsystem Support "kernel/test_support/filesystem/inode.cpp" "kernel/test_support/filesystem/filesystem.cpp" "kernel/test_support/filesystem/ext2.cpp" "kernel/test_support/filesystem/storage_boot_module_fixture.cpp" "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" - # I/O Support "kernel/test_support/log_buffer.cpp" "kernel/test_support/output_device.cpp" - # Memory Support "kernel/test_support/page_mapper.cpp" "kernel/test_support/simulated_memory.cpp" - # Support System Listener "kernel/test_support/state_reset_listener.cpp" - - # KAPI Shim Tests - "kapi/cpu.tests.cpp" - "kapi/system.tests.cpp" - "kapi/filesystem.tests.cpp" - - # KSTD Shim Tests - "kstd/print.tests.cpp" - - # Memory Subsystem Tests - "kernel/memory/bitmap_allocator.tests.cpp" - "kernel/memory/block_list_allocator.tests.cpp" - - # Filesystem Subsystem Tests - "kernel/filesystem/devfs/filesystem.tests.cpp" - "kernel/filesystem/devfs/inode.tests.cpp" - "kernel/filesystem/ext2/filesystem.tests.cpp" - "kernel/filesystem/ext2/inode.tests.cpp" - "kernel/filesystem/path.tests.cpp" - "kernel/filesystem/rootfs/filesystem.tests.cpp" - "kernel/filesystem/rootfs/inode.tests.cpp" - "kernel/filesystem/dentry.tests.cpp" - "kernel/filesystem/device_inode.tests.cpp" - "kernel/filesystem/mount_table.tests.cpp" - "kernel/filesystem/mount.tests.cpp" - "kernel/filesystem/open_file_descriptor.tests.cpp" - "kernel/filesystem/open_file_table.tests.cpp" - "kernel/filesystem/type_registry.tests.cpp" - "kernel/filesystem/vfs.tests.cpp" - - # Storage Subsystem Tests - "kernel/devices/block_device_utils.tests.cpp" - "kernel/devices/block_device.tests.cpp" - "kernel/devices/storage/ram_disk/device.tests.cpp" - ) - - target_link_libraries("kernel_tests" PRIVATE - "kernel::lib" - "Catch2::Catch2WithMain" ) target_compile_definitions("kernel_tests" PRIVATE @@ -224,13 +177,8 @@ if(BUILD_TESTING) CXX_CLANG_TIDY "" ) - enable_coverage("kernel_tests") - - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bht_results") - - catch_discover_tests("kernel::tests" - REPORTER junit - OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/bht_results" - OUTPUT_SUFFIX ".xml" - ) + if(COMMAND "enable_coverage") + enable_coverage("kernel_lib") + enable_coverage("kernel_tests") + endif() endif() diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 76941dcf..caffa1c9 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -56,6 +56,7 @@ set_target_properties("acpi" PROPERTIES if(BUILD_TESTING) find_package("Catch2") include("Catch") + include("BuildHostTests") find_program(IASL_EXE NAMES "iasl" REQUIRED) @@ -88,37 +89,13 @@ if(BUILD_TESTING) enable_coverage("acpi") endif() - add_executable("acpi_tests") - add_executable("acpi::tests" ALIAS "acpi_tests") + teachos_add_tests("acpi") target_sources("acpi_tests" PRIVATE - "acpi/common/table_header.tests.cpp" - "acpi/data/madt.tests.cpp" - "acpi/data/rsdt.tests.cpp" - "acpi/data/xsdt.tests.cpp" - "acpi/pointers.tests.cpp" - "acpi/test_data/tables.S" ) target_include_directories("acpi_tests" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/test_data" ) - - target_link_libraries("acpi_tests" PRIVATE - "Catch2::Catch2WithMain" - "acpi::lib" - ) - - set_target_properties("acpi_tests" PROPERTIES - EXCLUDE_FROM_ALL NO - ) - - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bht_results") - - catch_discover_tests("acpi::tests" - REPORTER junit - OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/bht_results" - OUTPUT_SUFFIX ".xml" - ) endif() diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 2c63f87d..9c68feed 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -69,38 +69,12 @@ endif() if(BUILD_TESTING) find_package("Catch2") include("Catch") + include("BuildHostTests") - add_executable("kstd_tests") - add_executable("kstd::tests" ALIAS "kstd_tests") - - target_sources("kstd_tests" PRIVATE - "kstd/flat_map.tests.cpp" - "kstd/format.tests.cpp" - "kstd/vector.tests.cpp" - "kstd/bits/observer_ptr.tests.cpp" - "kstd/test_support/os_panic.tests.cpp" - "kstd/string.tests.cpp" - ) - - target_link_libraries("kstd_tests" PRIVATE - "Catch2::Catch2WithMain" - "kstd::lib" - ) - - set_target_properties("kstd_tests" PROPERTIES - EXCLUDE_FROM_ALL NO - ) + teachos_add_tests("kstd") if(COMMAND "enable_coverage") enable_coverage("kstd") enable_coverage("kstd_tests") endif() - - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bht_results") - - catch_discover_tests("kstd::tests" - REPORTER junit - OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/bht_results" - OUTPUT_SUFFIX ".xml" - ) endif() \ No newline at end of file -- cgit v1.2.3 From 7469985a4ee794ecc736de12f7d768f1746419c0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 14:38:31 +0200 Subject: ide: fix print include path --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 0be16fbd..f554b1cc 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -64,7 +64,7 @@ CheckOptions: modernize-use-std-print.ReplacementPrintFunction: "kstd::print" modernize-use-std-print.ReplacementPrintlnFunction: "kstd::println" - modernize-use-std-print.PrintHeader: "kstd/print" + modernize-use-std-print.PrintHeader: "kstd/print.hpp" modernize-use-std-ranges.UseReversePipe: true -- cgit v1.2.3 From 294db0b50e917602f9468f3ccfa0a83ef50f22c0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 16:14:02 +0200 Subject: kstd: begin implementation of system_error --- libs/kstd/kstd/system_error.hpp | 363 ++++++++++++++++++++++++++++++++++ libs/kstd/kstd/system_error.tests.cpp | 55 ++++++ 2 files changed, 418 insertions(+) create mode 100644 libs/kstd/kstd/system_error.hpp create mode 100644 libs/kstd/kstd/system_error.tests.cpp diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp new file mode 100644 index 00000000..221347a8 --- /dev/null +++ b/libs/kstd/kstd/system_error.hpp @@ -0,0 +1,363 @@ +#ifndef KSTD_SYSTEM_ERROR_HPP +#define KSTD_SYSTEM_ERROR_HPP + +#include +#include +#include +#include +#include + +namespace kstd +{ + + struct error_condition; + struct error_code; + + template + requires(std::is_enum_v) + struct is_error_condition_enum : std::false_type + { + }; + + template + constexpr auto inline is_error_condition_enum_v = is_error_condition_enum::value; + + template + concept error_condition_enum = std::is_enum_v && is_error_condition_enum_v; + + template + requires(std::is_enum_v) + struct is_error_code_enum : std::false_type + { + }; + + template + constexpr auto inline is_error_code_enum_v = is_error_code_enum::value; + + template + concept error_code_enum = std::is_enum_v && is_error_code_enum_v; + + struct error_category + { + constexpr error_category() noexcept = default; + + error_category(error_category const &) = delete; + + constexpr virtual ~error_category() = default; + + auto operator=(error_category const &) -> error_category & = delete; + + [[nodiscard]] constexpr auto virtual name() const noexcept -> std::string_view = 0; + + [[nodiscard]] constexpr auto virtual default_error_condition(int value) const noexcept -> error_condition; + + [[nodiscard]] constexpr auto virtual equivalent(int value, error_condition const & other) const noexcept -> bool; + + [[nodiscard]] constexpr auto virtual equivalent(error_code const &, int condition) const noexcept -> bool; + + [[nodiscard]] constexpr auto virtual message(int value) const noexcept -> std::string_view = 0; + + [[nodiscard]] constexpr auto operator==(error_category const & other) const noexcept -> bool + { + return this == &other; + } + + [[nodiscard]] constexpr auto operator<=>(error_category const & other) const noexcept -> std::strong_ordering + { + return std::compare_three_way{}(this, &other); + } + }; + + struct error_condition + { + constexpr error_condition() noexcept; + + constexpr error_condition(int value, error_category const & category) noexcept + : m_value{value} + , m_category{&category} + {} + + template + constexpr error_condition(Enum value) noexcept + : error_condition{make_error_condition(value)} + {} + + template + constexpr auto operator=(Enum value) noexcept -> error_condition & + { + *this = make_error_condition(value); + } + + constexpr auto assign(int value, error_category const & category) noexcept -> void + { + m_value = value; + m_category = &category; + } + + constexpr auto clear() noexcept -> void; + + [[nodiscard]] constexpr auto value() const noexcept -> int + { + return m_value; + } + + [[nodiscard]] constexpr auto category() const noexcept -> error_category const & + { + return *m_category; + } + + [[nodiscard]] constexpr auto message() const noexcept -> std::string_view + { + return m_category->message(m_value); + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_value != 0; + } + + [[nodiscard]] constexpr auto friend operator==(error_condition const & lhs, error_condition const & rhs) noexcept + -> bool + { + return lhs.category() == rhs.category() && lhs.value() == rhs.value(); + } + + [[nodiscard]] constexpr auto friend operator<=>(error_condition const & lhs, error_condition const & rhs) noexcept + -> std::strong_ordering + { + if (auto result = lhs.category() <=> rhs.category(); result != std::strong_ordering::equal) + { + return result; + } + + return lhs.value() <=> rhs.value(); + } + + private: + int m_value{}; + error_category const * m_category{}; + }; + + struct error_code + { + constexpr error_code() noexcept; + + constexpr error_code(int value, error_category const & category) noexcept + : m_value{value} + , m_category{&category} + {} + + template + constexpr error_code(Enum value) noexcept + : error_code{make_error_condition(value)} + {} + + template + constexpr auto operator=(Enum value) noexcept -> error_code & + { + return *this = make_error_code(value); + } + + constexpr auto assign(int value, error_category const & category) noexcept -> void + { + m_value = value; + m_category = &category; + } + + constexpr auto clear() noexcept -> void; + + [[nodiscard]] constexpr auto value() const noexcept -> int + { + return m_value; + } + + [[nodiscard]] constexpr auto category() const noexcept -> error_category const & + { + return *m_category; + } + + [[nodiscard]] constexpr auto default_error_condition() const noexcept -> error_condition + { + return m_category->default_error_condition(m_value); + } + + [[nodiscard]] constexpr auto message() const noexcept -> std::string_view + { + return m_category->message(m_value); + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_value != 0; + } + + [[nodiscard]] constexpr auto friend operator==(error_code const & lhs, error_code const & rhs) noexcept -> bool + { + return lhs.category() == rhs.category() && lhs.value() == rhs.value(); + } + + [[nodiscard]] constexpr auto friend operator<=>(error_code const & lhs, error_code const & rhs) noexcept + -> std::strong_ordering + { + if (auto result = lhs.category() <=> rhs.category(); result != std::strong_ordering::equal) + { + return result; + } + + return lhs.value() <=> rhs.value(); + } + + private: + int m_value{}; + error_category const * m_category{}; + }; + + enum struct errc : std::uint32_t + { + success = 0, + no_such_file_or_directory, + io_error, + not_enough_memory, + permission_denied, + device_or_resource_busy, + invalid_argument, + not_supported, + }; + + template<> + struct is_error_condition_enum : std::true_type + { + }; + + namespace bits + { + struct generic_category_t final : error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "generic"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast(value)) + { + case errc::success: + return "success"; + case errc::no_such_file_or_directory: + return "no such file or directory"; + case errc::io_error: + return "input/output error"; + case errc::not_enough_memory: + return "not enough memory"; + case errc::permission_denied: + return "permission denied"; + case errc::device_or_resource_busy: + return "device or resource busy"; + case errc::invalid_argument: + return "invalid argument"; + case errc::not_supported: + return "not supported"; + default: + return "unknown generic error"; + } + } + } constexpr inline generic_category_instance{}; + + struct system_category_t final : error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "system"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast(value)) + { + case errc::success: + return "success"; + case errc::no_such_file_or_directory: + return "no such file or directory"; + case errc::io_error: + return "input/output error"; + case errc::not_enough_memory: + return "not enough memory"; + case errc::permission_denied: + return "permission denied"; + case errc::device_or_resource_busy: + return "device or resource busy"; + case errc::invalid_argument: + return "invalid argument"; + case errc::not_supported: + return "not supported"; + default: + return "unknown system error"; + } + } + } constexpr inline system_category_instance{}; + } // namespace bits + + constexpr auto inline generic_category() noexcept -> error_category const & + { + return bits::generic_category_instance; + } + + constexpr auto inline system_category() noexcept -> error_category const & + { + return bits::system_category_instance; + } + + constexpr auto inline error_category::default_error_condition(int value) const noexcept -> error_condition + { + return error_condition{value, *this}; + } + + constexpr auto inline error_category::equivalent(int value, error_condition const & other) const noexcept -> bool + { + return default_error_condition(value) == other; + } + + constexpr auto error_category::equivalent(error_code const & code, int condition) const noexcept -> bool + { + return *this == code.category() && code.value() == condition; + } + + constexpr error_condition::error_condition() noexcept + : error_condition{0, generic_category()} + {} + + constexpr auto error_condition::clear() noexcept -> void + { + m_value = 0; + m_category = &generic_category(); + } + + constexpr auto operator==(error_code const & code, error_condition const & condition) noexcept -> bool + { + return code.category().equivalent(code.value(), condition) || + condition.category().equivalent(code, condition.value()); + } + + constexpr error_code::error_code() noexcept + : error_code{0, system_category()} {}; + + constexpr auto error_code::clear() noexcept -> void + { + m_value = 0; + m_category = &system_category(); + } + + [[nodiscard]] constexpr auto inline make_error_code(errc const value) noexcept -> error_code + { + return {static_cast(value), generic_category()}; + } + + [[nodiscard]] constexpr auto inline make_error_condition(errc const value) noexcept -> error_code + { + return {static_cast(value), generic_category()}; + } + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/system_error.tests.cpp b/libs/kstd/kstd/system_error.tests.cpp new file mode 100644 index 00000000..12f952b3 --- /dev/null +++ b/libs/kstd/kstd/system_error.tests.cpp @@ -0,0 +1,55 @@ +#include + +#include + +SCENARIO("Error condition initialization and construction", "[kstd][system_error]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto error = kstd::error_condition{}; + + THEN("the category is equal to generic_category") + { + REQUIRE(error.category() == kstd::generic_category()); + } + + THEN("the value is 0") + { + REQUIRE(error.value() == 0); + } + + THEN("conversion to bool yields false") + { + REQUIRE_FALSE(error); + } + } + } +} + +SCENARIO("Error code initialization and construction", "[kstd][system_error]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto error = kstd::error_code{}; + + THEN("the category is equal to system_category") + { + REQUIRE(error.category() == kstd::system_category()); + } + + THEN("the value is 0") + { + REQUIRE(error.value() == 0); + } + + THEN("conversion to bool yields false") + { + REQUIRE_FALSE(error); + } + } + } +} -- cgit v1.2.3 From 8f2b785f5e19f0c856edea46ad0b4c625365f177 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Jul 2026 21:49:30 +0200 Subject: kstd: add more POSIX error codes to kstd::errc --- libs/kstd/kstd/system_error.hpp | 403 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 367 insertions(+), 36 deletions(-) diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 221347a8..df378b5d 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -215,12 +215,127 @@ namespace kstd enum struct errc : std::uint32_t { success = 0, + operation_not_permitted, no_such_file_or_directory, + no_such_process, + interrupted, io_error, + no_such_device_or_address, + argument_list_too_long, + executable_format_error, + bad_file_descriptor, + no_child_process, + resource_unavailable_try_again, not_enough_memory, permission_denied, + bad_address, + not_a_block_device, device_or_resource_busy, + file_exists, + cross_device_link, + no_such_device, + not_a_directory, + is_a_directory, invalid_argument, + too_many_files_open_in_system, + too_many_files_open, + inappropriate_io_control_operation, + text_file_busy, + file_too_large, + no_space_on_device, + invalid_seek, + read_only_file_system, + too_many_links, + broken_pipe, + argument_out_of_range, + result_out_of_range, + resource_deadlock_would_occur, + file_name_too_long, + no_lock_available, + function_not_supported, + directory_not_empty, + too_many_symbolic_link_levels, + no_message = 42, + identifier_removed, + channel_number_out_of_range, + level_2_not_synchronized, + level_3_halted, + level_3_reset, + link_number_out_of_range, + protocol_driver_not_attached, + no_csi_structure_available, + level_2_halted, + invalid_exchange, + invalid_request_descriptor, + exchange_full, + no_anode, + invalid_request_code, + invalid_slot, + bad_font_file_format = 59, + machine_is_not_on_the_network = 64, + package_not_installed, + object_is_remote, + no_link, + advertise_error, + srmount_error, + communication_error_on_send, + protocol_error, + multihop_attempt, + bad_message = 74, + value_too_large, + name_not_unique_on_network, + file_descriptor_in_bad_state, + remote_address_changed, + shared_library_inaccessible, + shared_library_corrupted, + too_many_shared_libraries, + cannot_execute_shared_library = 83, + illegal_byte_sequence, + interrupted_try_again, + too_many_users = 87, + not_a_socket, + destination_address_required, + message_size, + wrong_protocol_type, + no_protocol_option, + protocol_not_supported, + socket_type_not_supported, + operation_not_supported, + protocol_family_not_supported, + address_family_not_supported, + address_in_use, + address_not_available, + network_down, + network_unreachable, + network_reset, + connection_aborted, + connection_reset, + no_buffer_space, + already_connected, + not_connected, + connection_shut_down, + too_many_references, + timed_out, + connection_refused, + host_is_down, + host_unreachable, + connection_already_in_progress, + operation_in_progress, + stale_file_handle, + structure_needs_cleaning, + remote_io_error = 121, + disk_quota_exceeded, + no_medium_found, + wrong_medium_type, + operation_canceled, + required_key_not_available, + key_expired, + key_revoked, + key_rejected, + owner_dead, + state_not_recoverable, + rf_kill, + memory_page_hardware_error, not_supported, }; @@ -242,24 +357,132 @@ namespace kstd { switch (static_cast(value)) { - case errc::success: - return "success"; - case errc::no_such_file_or_directory: - return "no such file or directory"; - case errc::io_error: - return "input/output error"; - case errc::not_enough_memory: - return "not enough memory"; - case errc::permission_denied: - return "permission denied"; - case errc::device_or_resource_busy: - return "device or resource busy"; - case errc::invalid_argument: - return "invalid argument"; - case errc::not_supported: - return "not supported"; - default: - return "unknown generic error"; + // clang-format off + case errc::success: return "success"; + case errc::operation_not_permitted: return "operation not permitted"; + case errc::no_such_file_or_directory: return "no such file or directory"; + case errc::no_such_process: return "no such process"; + case errc::interrupted: return "system call interrupted"; + case errc::io_error: return "input/output error"; + case errc::no_such_device_or_address: return "no such device or address"; + case errc::argument_list_too_long: return "argument list too long"; + case errc::executable_format_error: return "invalid executable format"; + case errc::bad_file_descriptor: return "bad file descriptor"; + case errc::no_child_process: return "no child process"; + case errc::resource_unavailable_try_again: return "resource unavailable, try again"; + case errc::not_enough_memory: return "not enough memory"; + case errc::permission_denied: return "permission denied"; + case errc::bad_address: return "bad address"; + case errc::not_a_block_device: return "not a block device"; + case errc::device_or_resource_busy: return "device or resource busy"; + case errc::file_exists: return "file exists"; + case errc::cross_device_link: return "cross device link"; + case errc::no_such_device: return "no such device"; + case errc::not_a_directory: return "not a directory"; + case errc::is_a_directory: return "is a directory"; + case errc::invalid_argument: return "invalid argument"; + case errc::too_many_files_open_in_system: return "too many files open in system"; + case errc::too_many_files_open: return "too many files open"; + case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; + case errc::text_file_busy: return "text file busy"; + case errc::file_too_large: return "file too large"; + case errc::no_space_on_device: return "no space on device"; + case errc::invalid_seek: return "invalid seek"; + case errc::read_only_file_system: return "read only file system"; + case errc::too_many_links: return "too many links"; + case errc::broken_pipe: return "broken pipe"; + case errc::argument_out_of_range: return "argument out of range"; + case errc::result_out_of_range: return "result out of range"; + case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; + case errc::file_name_too_long: return "file name too long"; + case errc::no_lock_available: return "no lock available"; + case errc::function_not_supported: return "function not supported"; + case errc::directory_not_empty: return "directory not empty"; + case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; + case errc::no_message: return "no message"; + case errc::identifier_removed: return "identifier removed"; + case errc::channel_number_out_of_range: return "channel number out of range"; + case errc::level_2_not_synchronized: return "level 2 not synchronized"; + case errc::level_3_halted: return "level 3 halted"; + case errc::level_3_reset: return "level 3 reset"; + case errc::link_number_out_of_range: return "link number out of range"; + case errc::protocol_driver_not_attached: return "protocol driver not attached"; + case errc::no_csi_structure_available: return "no csi structure available"; + case errc::level_2_halted: return "level 2 halted"; + case errc::invalid_exchange: return "invalid exchange"; + case errc::invalid_request_descriptor: return "invalid request descriptor"; + case errc::exchange_full: return "exchange full"; + case errc::no_anode: return "no anode"; + case errc::invalid_request_code: return "invalid request code"; + case errc::invalid_slot: return "invalid slot"; + case errc::bad_font_file_format: return "bad font file format"; + case errc::machine_is_not_on_the_network: return "machine is not on the network"; + case errc::package_not_installed: return "package not installed"; + case errc::object_is_remote: return "object is remote"; + case errc::no_link: return "no link"; + case errc::advertise_error: return "advertisement error"; + case errc::srmount_error: return "srmount error"; + case errc::communication_error_on_send: return "communication error on send"; + case errc::protocol_error: return "protocol error"; + case errc::multihop_attempt: return "multihop attempt"; + case errc::bad_message: return "bad message"; + case errc::value_too_large: return "value too large"; + case errc::name_not_unique_on_network: return "name not unique on network"; + case errc::file_descriptor_in_bad_state: return "file descriptor in bad state"; + case errc::remote_address_changed: return "remote address changed"; + case errc::shared_library_inaccessible: return "failed to access shared library"; + case errc::shared_library_corrupted: return "shared library corrupted"; + case errc::too_many_shared_libraries: return "attempted to load too many shared libraries"; + case errc::cannot_execute_shared_library: return "cannot execute shared library"; + case errc::illegal_byte_sequence: return "illegal byte sequence"; + case errc::interrupted_try_again: return "system call interrupted, try again"; + case errc::too_many_users: return "too many logged in users"; + case errc::not_a_socket: return "not a socket"; + case errc::destination_address_required: return "destination address required"; + case errc::message_size: return "message size"; + case errc::wrong_protocol_type: return "wrong protocol type"; + case errc::no_protocol_option: return "no protocol option"; + case errc::protocol_not_supported: return "protocol not supported"; + case errc::socket_type_not_supported: return "socket type not supported"; + case errc::operation_not_supported: return "operation not supported"; + case errc::protocol_family_not_supported: return "protocol family not supported"; + case errc::address_family_not_supported: return "address family not supported"; + case errc::address_in_use: return "address in use"; + case errc::address_not_available: return "address not available"; + case errc::network_down: return "network down"; + case errc::network_unreachable: return "network unreachable"; + case errc::network_reset: return "network reset"; + case errc::connection_aborted: return "connection aborted"; + case errc::connection_reset: return "connection reset"; + case errc::no_buffer_space: return "no buffer space"; + case errc::already_connected: return "already connected"; + case errc::not_connected: return "not connected"; + case errc::connection_shut_down: return "connection shut down"; + case errc::too_many_references: return "too many references"; + case errc::timed_out: return "timed out"; + case errc::connection_refused: return "connection refused"; + case errc::host_is_down: return "host is down"; + case errc::host_unreachable: return "host unreachable"; + case errc::connection_already_in_progress: return "connection already in progress"; + case errc::operation_in_progress: return "operation in progress"; + case errc::stale_file_handle: return "stale file handle"; + case errc::structure_needs_cleaning: return "structure needs cleaning"; + case errc::remote_io_error: return "remote input/output error"; + case errc::disk_quota_exceeded: return "disk quota exceeded"; + case errc::no_medium_found: return "no medium found"; + case errc::wrong_medium_type: return "wrong medium type"; + case errc::operation_canceled: return "operation canceled"; + case errc::required_key_not_available: return "required key not available"; + case errc::key_expired: return "key expired"; + case errc::key_revoked: return "key revoked"; + case errc::key_rejected: return "key rejected"; + case errc::owner_dead: return "owner dead"; + case errc::state_not_recoverable: return "state not recoverable"; + case errc::rf_kill: return "operation not possible due to RFkill"; + case errc::memory_page_hardware_error: return "memory page hardware error"; + case errc::not_supported: return "not supported"; + default: return "unknown system error"; + // clang-format on } } } constexpr inline generic_category_instance{}; @@ -275,24 +498,132 @@ namespace kstd { switch (static_cast(value)) { - case errc::success: - return "success"; - case errc::no_such_file_or_directory: - return "no such file or directory"; - case errc::io_error: - return "input/output error"; - case errc::not_enough_memory: - return "not enough memory"; - case errc::permission_denied: - return "permission denied"; - case errc::device_or_resource_busy: - return "device or resource busy"; - case errc::invalid_argument: - return "invalid argument"; - case errc::not_supported: - return "not supported"; - default: - return "unknown system error"; + // clang-format off + case errc::success: return "success"; + case errc::operation_not_permitted: return "operation not permitted"; + case errc::no_such_file_or_directory: return "no such file or directory"; + case errc::no_such_process: return "no such process"; + case errc::interrupted: return "system call interrupted"; + case errc::io_error: return "input/output error"; + case errc::no_such_device_or_address: return "no such device or address"; + case errc::argument_list_too_long: return "argument list too long"; + case errc::executable_format_error: return "invalid executable format"; + case errc::bad_file_descriptor: return "bad file descriptor"; + case errc::no_child_process: return "no child process"; + case errc::resource_unavailable_try_again: return "resource unavailable, try again"; + case errc::not_enough_memory: return "not enough memory"; + case errc::permission_denied: return "permission denied"; + case errc::bad_address: return "bad address"; + case errc::not_a_block_device: return "not a block device"; + case errc::device_or_resource_busy: return "device or resource busy"; + case errc::file_exists: return "file exists"; + case errc::cross_device_link: return "cross device link"; + case errc::no_such_device: return "no such device"; + case errc::not_a_directory: return "not a directory"; + case errc::is_a_directory: return "is a directory"; + case errc::invalid_argument: return "invalid argument"; + case errc::too_many_files_open_in_system: return "too many files open in system"; + case errc::too_many_files_open: return "too many files open"; + case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; + case errc::text_file_busy: return "text file busy"; + case errc::file_too_large: return "file too large"; + case errc::no_space_on_device: return "no space on device"; + case errc::invalid_seek: return "invalid seek"; + case errc::read_only_file_system: return "read only file system"; + case errc::too_many_links: return "too many links"; + case errc::broken_pipe: return "broken pipe"; + case errc::argument_out_of_range: return "argument out of range"; + case errc::result_out_of_range: return "result out of range"; + case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; + case errc::file_name_too_long: return "file name too long"; + case errc::no_lock_available: return "no lock available"; + case errc::function_not_supported: return "function not supported"; + case errc::directory_not_empty: return "directory not empty"; + case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; + case errc::no_message: return "no message"; + case errc::identifier_removed: return "identifier removed"; + case errc::channel_number_out_of_range: return "channel number out of range"; + case errc::level_2_not_synchronized: return "level 2 not synchronized"; + case errc::level_3_halted: return "level 3 halted"; + case errc::level_3_reset: return "level 3 reset"; + case errc::link_number_out_of_range: return "link number out of range"; + case errc::protocol_driver_not_attached: return "protocol driver not attached"; + case errc::no_csi_structure_available: return "no csi structure available"; + case errc::level_2_halted: return "level 2 halted"; + case errc::invalid_exchange: return "invalid exchange"; + case errc::invalid_request_descriptor: return "invalid request descriptor"; + case errc::exchange_full: return "exchange full"; + case errc::no_anode: return "no anode"; + case errc::invalid_request_code: return "invalid request code"; + case errc::invalid_slot: return "invalid slot"; + case errc::bad_font_file_format: return "bad font file format"; + case errc::machine_is_not_on_the_network: return "machine is not on the network"; + case errc::package_not_installed: return "package not installed"; + case errc::object_is_remote: return "object is remote"; + case errc::no_link: return "no link"; + case errc::advertise_error: return "advertisement error"; + case errc::srmount_error: return "srmount error"; + case errc::communication_error_on_send: return "communication error on send"; + case errc::protocol_error: return "protocol error"; + case errc::multihop_attempt: return "multihop attempt"; + case errc::bad_message: return "bad message"; + case errc::value_too_large: return "value too large"; + case errc::name_not_unique_on_network: return "name not unique on network"; + case errc::file_descriptor_in_bad_state: return "file descriptor in bad state"; + case errc::remote_address_changed: return "remote address changed"; + case errc::shared_library_inaccessible: return "failed to access shared library"; + case errc::shared_library_corrupted: return "shared library corrupted"; + case errc::too_many_shared_libraries: return "attempted to load too many shared libraries"; + case errc::cannot_execute_shared_library: return "cannot execute shared library"; + case errc::illegal_byte_sequence: return "illegal byte sequence"; + case errc::interrupted_try_again: return "system call interrupted, try again"; + case errc::too_many_users: return "too many logged in users"; + case errc::not_a_socket: return "not a socket"; + case errc::destination_address_required: return "destination address required"; + case errc::message_size: return "message size"; + case errc::wrong_protocol_type: return "wrong protocol type"; + case errc::no_protocol_option: return "no protocol option"; + case errc::protocol_not_supported: return "protocol not supported"; + case errc::socket_type_not_supported: return "socket type not supported"; + case errc::operation_not_supported: return "operation not supported"; + case errc::protocol_family_not_supported: return "protocol family not supported"; + case errc::address_family_not_supported: return "address family not supported"; + case errc::address_in_use: return "address in use"; + case errc::address_not_available: return "address not available"; + case errc::network_down: return "network down"; + case errc::network_unreachable: return "network unreachable"; + case errc::network_reset: return "network reset"; + case errc::connection_aborted: return "connection aborted"; + case errc::connection_reset: return "connection reset"; + case errc::no_buffer_space: return "no buffer space"; + case errc::already_connected: return "already connected"; + case errc::not_connected: return "not connected"; + case errc::connection_shut_down: return "connection shut down"; + case errc::too_many_references: return "too many references"; + case errc::timed_out: return "timed out"; + case errc::connection_refused: return "connection refused"; + case errc::host_is_down: return "host is down"; + case errc::host_unreachable: return "host unreachable"; + case errc::connection_already_in_progress: return "connection already in progress"; + case errc::operation_in_progress: return "operation in progress"; + case errc::stale_file_handle: return "stale file handle"; + case errc::structure_needs_cleaning: return "structure needs cleaning"; + case errc::remote_io_error: return "remote input/output error"; + case errc::disk_quota_exceeded: return "disk quota exceeded"; + case errc::no_medium_found: return "no medium found"; + case errc::wrong_medium_type: return "wrong medium type"; + case errc::operation_canceled: return "operation canceled"; + case errc::required_key_not_available: return "required key not available"; + case errc::key_expired: return "key expired"; + case errc::key_revoked: return "key revoked"; + case errc::key_rejected: return "key rejected"; + case errc::owner_dead: return "owner dead"; + case errc::state_not_recoverable: return "state not recoverable"; + case errc::rf_kill: return "operation not possible due to RFkill"; + case errc::memory_page_hardware_error: return "memory page hardware error"; + case errc::not_supported: return "not supported"; + default: return "unknown system error"; + // clang-format on } } } constexpr inline system_category_instance{}; -- cgit v1.2.3 From 49635f99dfa20f1cf17a53b24328871e05cc0757 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 11 Jul 2026 21:44:59 +0200 Subject: kstd: only support official POSIX error codes --- libs/kstd/kstd/system_error.hpp | 497 ++++++++++++++-------------------------- 1 file changed, 178 insertions(+), 319 deletions(-) diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index df378b5d..5cc16e74 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -214,129 +214,82 @@ namespace kstd enum struct errc : std::uint32_t { - success = 0, - operation_not_permitted, - no_such_file_or_directory, - no_such_process, - interrupted, - io_error, - no_such_device_or_address, + success, + address_family_not_supported, + address_in_use, + address_not_available, + already_connected, argument_list_too_long, - executable_format_error, - bad_file_descriptor, - no_child_process, - resource_unavailable_try_again, - not_enough_memory, - permission_denied, + argument_out_of_domain, bad_address, - not_a_block_device, + bad_file_descriptor, + bad_message, + broken_pipe, + connection_aborted, + connection_already_in_progress, + connection_refused, + connection_reset, + cross_device_link, + destination_address_required, device_or_resource_busy, + directory_not_empty, + executable_format_error, file_exists, - cross_device_link, - no_such_device, - not_a_directory, - is_a_directory, - invalid_argument, - too_many_files_open_in_system, - too_many_files_open, - inappropriate_io_control_operation, - text_file_busy, file_too_large, - no_space_on_device, - invalid_seek, - read_only_file_system, - too_many_links, - broken_pipe, - argument_out_of_range, - result_out_of_range, - resource_deadlock_would_occur, - file_name_too_long, - no_lock_available, + filename_too_long, function_not_supported, - directory_not_empty, - too_many_symbolic_link_levels, - no_message = 42, + host_unreachable, identifier_removed, - channel_number_out_of_range, - level_2_not_synchronized, - level_3_halted, - level_3_reset, - link_number_out_of_range, - protocol_driver_not_attached, - no_csi_structure_available, - level_2_halted, - invalid_exchange, - invalid_request_descriptor, - exchange_full, - no_anode, - invalid_request_code, - invalid_slot, - bad_font_file_format = 59, - machine_is_not_on_the_network = 64, - package_not_installed, - object_is_remote, - no_link, - advertise_error, - srmount_error, - communication_error_on_send, - protocol_error, - multihop_attempt, - bad_message = 74, - value_too_large, - name_not_unique_on_network, - file_descriptor_in_bad_state, - remote_address_changed, - shared_library_inaccessible, - shared_library_corrupted, - too_many_shared_libraries, - cannot_execute_shared_library = 83, illegal_byte_sequence, - interrupted_try_again, - too_many_users = 87, - not_a_socket, - destination_address_required, + inappropriate_io_control_operation, + interrupted, + invalid_argument, + invalid_seek, + io_error, + is_a_directory, message_size, - wrong_protocol_type, - no_protocol_option, - protocol_not_supported, - socket_type_not_supported, - operation_not_supported, - protocol_family_not_supported, - address_family_not_supported, - address_in_use, - address_not_available, network_down, - network_unreachable, network_reset, - connection_aborted, - connection_reset, + network_unreachable, no_buffer_space, - already_connected, + no_child_process, + no_link, + no_lock_available, + no_message, + no_protocol_option, + no_space_on_device, + no_such_device, + no_such_device_or_address, + no_such_file_or_directory, + no_such_process, + not_a_directory, + not_a_socket, not_connected, - connection_shut_down, - too_many_references, - timed_out, - connection_refused, - host_is_down, - host_unreachable, - connection_already_in_progress, - operation_in_progress, - stale_file_handle, - structure_needs_cleaning, - remote_io_error = 121, - disk_quota_exceeded, - no_medium_found, - wrong_medium_type, + not_enough_memory, + not_supported, operation_canceled, - required_key_not_available, - key_expired, - key_revoked, - key_rejected, + operation_in_progress, + operation_not_permitted, + operation_not_supported, + operation_would_block, owner_dead, + permission_denied, + protocol_error, + protocol_not_supported, + read_only_file_system, + resource_deadlock_would_occur, + resource_unavailable_try_again, + result_out_of_range, + socket_type_not_supported, state_not_recoverable, - rf_kill, - memory_page_hardware_error, - not_supported, + text_file_busy, + timed_out, + too_many_files_open, + too_many_files_open_in_system, + too_many_links, + too_many_symbolic_link_levels, + value_too_large, + wrong_protocol_type, }; template<> @@ -359,128 +312,81 @@ namespace kstd { // clang-format off case errc::success: return "success"; - case errc::operation_not_permitted: return "operation not permitted"; - case errc::no_such_file_or_directory: return "no such file or directory"; - case errc::no_such_process: return "no such process"; - case errc::interrupted: return "system call interrupted"; - case errc::io_error: return "input/output error"; - case errc::no_such_device_or_address: return "no such device or address"; + case errc::address_family_not_supported: return "address family not supported"; + case errc::address_in_use: return "address in use"; + case errc::address_not_available: return "address not available"; + case errc::already_connected: return "already connected"; case errc::argument_list_too_long: return "argument list too long"; - case errc::executable_format_error: return "invalid executable format"; - case errc::bad_file_descriptor: return "bad file descriptor"; - case errc::no_child_process: return "no child process"; - case errc::resource_unavailable_try_again: return "resource unavailable, try again"; - case errc::not_enough_memory: return "not enough memory"; - case errc::permission_denied: return "permission denied"; + case errc::argument_out_of_domain: return "argument out of domain"; case errc::bad_address: return "bad address"; - case errc::not_a_block_device: return "not a block device"; + case errc::bad_file_descriptor: return "bad file descriptor"; + case errc::bad_message: return "bad message"; + case errc::broken_pipe: return "broken pipe"; + case errc::connection_aborted: return "connection aborted"; + case errc::connection_already_in_progress: return "connection already in progress"; + case errc::connection_refused: return "connection refused"; + case errc::connection_reset: return "connection reset"; + case errc::cross_device_link: return "cross device link"; + case errc::destination_address_required: return "destination address required"; case errc::device_or_resource_busy: return "device or resource busy"; + case errc::directory_not_empty: return "directory not empty"; + case errc::executable_format_error: return "invalid executable format"; case errc::file_exists: return "file exists"; - case errc::cross_device_link: return "cross device link"; - case errc::no_such_device: return "no such device"; - case errc::not_a_directory: return "not a directory"; - case errc::is_a_directory: return "is a directory"; - case errc::invalid_argument: return "invalid argument"; - case errc::too_many_files_open_in_system: return "too many files open in system"; - case errc::too_many_files_open: return "too many files open"; - case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; - case errc::text_file_busy: return "text file busy"; case errc::file_too_large: return "file too large"; - case errc::no_space_on_device: return "no space on device"; - case errc::invalid_seek: return "invalid seek"; - case errc::read_only_file_system: return "read only file system"; - case errc::too_many_links: return "too many links"; - case errc::broken_pipe: return "broken pipe"; - case errc::argument_out_of_range: return "argument out of range"; - case errc::result_out_of_range: return "result out of range"; - case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; - case errc::file_name_too_long: return "file name too long"; - case errc::no_lock_available: return "no lock available"; + case errc::filename_too_long: return "filename too long"; case errc::function_not_supported: return "function not supported"; - case errc::directory_not_empty: return "directory not empty"; - case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; - case errc::no_message: return "no message"; + case errc::host_unreachable: return "host unreachable"; case errc::identifier_removed: return "identifier removed"; - case errc::channel_number_out_of_range: return "channel number out of range"; - case errc::level_2_not_synchronized: return "level 2 not synchronized"; - case errc::level_3_halted: return "level 3 halted"; - case errc::level_3_reset: return "level 3 reset"; - case errc::link_number_out_of_range: return "link number out of range"; - case errc::protocol_driver_not_attached: return "protocol driver not attached"; - case errc::no_csi_structure_available: return "no csi structure available"; - case errc::level_2_halted: return "level 2 halted"; - case errc::invalid_exchange: return "invalid exchange"; - case errc::invalid_request_descriptor: return "invalid request descriptor"; - case errc::exchange_full: return "exchange full"; - case errc::no_anode: return "no anode"; - case errc::invalid_request_code: return "invalid request code"; - case errc::invalid_slot: return "invalid slot"; - case errc::bad_font_file_format: return "bad font file format"; - case errc::machine_is_not_on_the_network: return "machine is not on the network"; - case errc::package_not_installed: return "package not installed"; - case errc::object_is_remote: return "object is remote"; - case errc::no_link: return "no link"; - case errc::advertise_error: return "advertisement error"; - case errc::srmount_error: return "srmount error"; - case errc::communication_error_on_send: return "communication error on send"; - case errc::protocol_error: return "protocol error"; - case errc::multihop_attempt: return "multihop attempt"; - case errc::bad_message: return "bad message"; - case errc::value_too_large: return "value too large"; - case errc::name_not_unique_on_network: return "name not unique on network"; - case errc::file_descriptor_in_bad_state: return "file descriptor in bad state"; - case errc::remote_address_changed: return "remote address changed"; - case errc::shared_library_inaccessible: return "failed to access shared library"; - case errc::shared_library_corrupted: return "shared library corrupted"; - case errc::too_many_shared_libraries: return "attempted to load too many shared libraries"; - case errc::cannot_execute_shared_library: return "cannot execute shared library"; case errc::illegal_byte_sequence: return "illegal byte sequence"; - case errc::interrupted_try_again: return "system call interrupted, try again"; - case errc::too_many_users: return "too many logged in users"; - case errc::not_a_socket: return "not a socket"; - case errc::destination_address_required: return "destination address required"; + case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; + case errc::interrupted: return "system call interrupted"; + case errc::invalid_argument: return "invalid argument"; + case errc::invalid_seek: return "invalid seek"; + case errc::io_error: return "input/output error"; + case errc::is_a_directory: return "is a directory"; case errc::message_size: return "message size"; - case errc::wrong_protocol_type: return "wrong protocol type"; - case errc::no_protocol_option: return "no protocol option"; - case errc::protocol_not_supported: return "protocol not supported"; - case errc::socket_type_not_supported: return "socket type not supported"; - case errc::operation_not_supported: return "operation not supported"; - case errc::protocol_family_not_supported: return "protocol family not supported"; - case errc::address_family_not_supported: return "address family not supported"; - case errc::address_in_use: return "address in use"; - case errc::address_not_available: return "address not available"; case errc::network_down: return "network down"; - case errc::network_unreachable: return "network unreachable"; case errc::network_reset: return "network reset"; - case errc::connection_aborted: return "connection aborted"; - case errc::connection_reset: return "connection reset"; + case errc::network_unreachable: return "network unreachable"; case errc::no_buffer_space: return "no buffer space"; - case errc::already_connected: return "already connected"; + case errc::no_child_process: return "no child process"; + case errc::no_link: return "no link"; + case errc::no_lock_available: return "no lock available"; + case errc::no_message: return "no message"; + case errc::no_protocol_option: return "no protocol option"; + case errc::no_space_on_device: return "no space on device"; + case errc::no_such_device: return "no such device"; + case errc::no_such_device_or_address: return "no such device or address"; + case errc::no_such_file_or_directory: return "no such file or directory"; + case errc::no_such_process: return "no such process"; + case errc::not_a_directory: return "not a directory"; + case errc::not_a_socket: return "not a socket"; case errc::not_connected: return "not connected"; - case errc::connection_shut_down: return "connection shut down"; - case errc::too_many_references: return "too many references"; - case errc::timed_out: return "timed out"; - case errc::connection_refused: return "connection refused"; - case errc::host_is_down: return "host is down"; - case errc::host_unreachable: return "host unreachable"; - case errc::connection_already_in_progress: return "connection already in progress"; - case errc::operation_in_progress: return "operation in progress"; - case errc::stale_file_handle: return "stale file handle"; - case errc::structure_needs_cleaning: return "structure needs cleaning"; - case errc::remote_io_error: return "remote input/output error"; - case errc::disk_quota_exceeded: return "disk quota exceeded"; - case errc::no_medium_found: return "no medium found"; - case errc::wrong_medium_type: return "wrong medium type"; + case errc::not_enough_memory: return "not enough memory"; + case errc::not_supported: return "not supported"; case errc::operation_canceled: return "operation canceled"; - case errc::required_key_not_available: return "required key not available"; - case errc::key_expired: return "key expired"; - case errc::key_revoked: return "key revoked"; - case errc::key_rejected: return "key rejected"; + case errc::operation_in_progress: return "operation in progress"; + case errc::operation_not_permitted: return "operation not permitted"; + case errc::operation_not_supported: return "operation not supported"; + case errc::operation_would_block: return "operation would block"; case errc::owner_dead: return "owner dead"; + case errc::permission_denied: return "permission denied"; + case errc::protocol_error: return "protocol error"; + case errc::protocol_not_supported: return "protocol not supported"; + case errc::read_only_file_system: return "read only file system"; + case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; + case errc::resource_unavailable_try_again: return "resource unavailable, try again"; + case errc::result_out_of_range: return "result out of range"; + case errc::socket_type_not_supported: return "socket type not supported"; case errc::state_not_recoverable: return "state not recoverable"; - case errc::rf_kill: return "operation not possible due to RFkill"; - case errc::memory_page_hardware_error: return "memory page hardware error"; - case errc::not_supported: return "not supported"; + case errc::text_file_busy: return "text file busy"; + case errc::timed_out: return "timed out"; + case errc::too_many_files_open: return "too many files open"; + case errc::too_many_files_open_in_system: return "too many files open in system"; + case errc::too_many_links: return "too many links"; + case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; + case errc::value_too_large: return "value too large"; + case errc::wrong_protocol_type: return "wrong protocol type"; default: return "unknown system error"; // clang-format on } @@ -500,128 +406,81 @@ namespace kstd { // clang-format off case errc::success: return "success"; - case errc::operation_not_permitted: return "operation not permitted"; - case errc::no_such_file_or_directory: return "no such file or directory"; - case errc::no_such_process: return "no such process"; - case errc::interrupted: return "system call interrupted"; - case errc::io_error: return "input/output error"; - case errc::no_such_device_or_address: return "no such device or address"; + case errc::address_family_not_supported: return "address family not supported"; + case errc::address_in_use: return "address in use"; + case errc::address_not_available: return "address not available"; + case errc::already_connected: return "already connected"; case errc::argument_list_too_long: return "argument list too long"; - case errc::executable_format_error: return "invalid executable format"; - case errc::bad_file_descriptor: return "bad file descriptor"; - case errc::no_child_process: return "no child process"; - case errc::resource_unavailable_try_again: return "resource unavailable, try again"; - case errc::not_enough_memory: return "not enough memory"; - case errc::permission_denied: return "permission denied"; + case errc::argument_out_of_domain: return "argument out of domain"; case errc::bad_address: return "bad address"; - case errc::not_a_block_device: return "not a block device"; + case errc::bad_file_descriptor: return "bad file descriptor"; + case errc::bad_message: return "bad message"; + case errc::broken_pipe: return "broken pipe"; + case errc::connection_aborted: return "connection aborted"; + case errc::connection_already_in_progress: return "connection already in progress"; + case errc::connection_refused: return "connection refused"; + case errc::connection_reset: return "connection reset"; + case errc::cross_device_link: return "cross device link"; + case errc::destination_address_required: return "destination address required"; case errc::device_or_resource_busy: return "device or resource busy"; + case errc::directory_not_empty: return "directory not empty"; + case errc::executable_format_error: return "invalid executable format"; case errc::file_exists: return "file exists"; - case errc::cross_device_link: return "cross device link"; - case errc::no_such_device: return "no such device"; - case errc::not_a_directory: return "not a directory"; - case errc::is_a_directory: return "is a directory"; - case errc::invalid_argument: return "invalid argument"; - case errc::too_many_files_open_in_system: return "too many files open in system"; - case errc::too_many_files_open: return "too many files open"; - case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; - case errc::text_file_busy: return "text file busy"; case errc::file_too_large: return "file too large"; - case errc::no_space_on_device: return "no space on device"; - case errc::invalid_seek: return "invalid seek"; - case errc::read_only_file_system: return "read only file system"; - case errc::too_many_links: return "too many links"; - case errc::broken_pipe: return "broken pipe"; - case errc::argument_out_of_range: return "argument out of range"; - case errc::result_out_of_range: return "result out of range"; - case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; - case errc::file_name_too_long: return "file name too long"; - case errc::no_lock_available: return "no lock available"; + case errc::filename_too_long: return "filename too long"; case errc::function_not_supported: return "function not supported"; - case errc::directory_not_empty: return "directory not empty"; - case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; - case errc::no_message: return "no message"; + case errc::host_unreachable: return "host unreachable"; case errc::identifier_removed: return "identifier removed"; - case errc::channel_number_out_of_range: return "channel number out of range"; - case errc::level_2_not_synchronized: return "level 2 not synchronized"; - case errc::level_3_halted: return "level 3 halted"; - case errc::level_3_reset: return "level 3 reset"; - case errc::link_number_out_of_range: return "link number out of range"; - case errc::protocol_driver_not_attached: return "protocol driver not attached"; - case errc::no_csi_structure_available: return "no csi structure available"; - case errc::level_2_halted: return "level 2 halted"; - case errc::invalid_exchange: return "invalid exchange"; - case errc::invalid_request_descriptor: return "invalid request descriptor"; - case errc::exchange_full: return "exchange full"; - case errc::no_anode: return "no anode"; - case errc::invalid_request_code: return "invalid request code"; - case errc::invalid_slot: return "invalid slot"; - case errc::bad_font_file_format: return "bad font file format"; - case errc::machine_is_not_on_the_network: return "machine is not on the network"; - case errc::package_not_installed: return "package not installed"; - case errc::object_is_remote: return "object is remote"; - case errc::no_link: return "no link"; - case errc::advertise_error: return "advertisement error"; - case errc::srmount_error: return "srmount error"; - case errc::communication_error_on_send: return "communication error on send"; - case errc::protocol_error: return "protocol error"; - case errc::multihop_attempt: return "multihop attempt"; - case errc::bad_message: return "bad message"; - case errc::value_too_large: return "value too large"; - case errc::name_not_unique_on_network: return "name not unique on network"; - case errc::file_descriptor_in_bad_state: return "file descriptor in bad state"; - case errc::remote_address_changed: return "remote address changed"; - case errc::shared_library_inaccessible: return "failed to access shared library"; - case errc::shared_library_corrupted: return "shared library corrupted"; - case errc::too_many_shared_libraries: return "attempted to load too many shared libraries"; - case errc::cannot_execute_shared_library: return "cannot execute shared library"; case errc::illegal_byte_sequence: return "illegal byte sequence"; - case errc::interrupted_try_again: return "system call interrupted, try again"; - case errc::too_many_users: return "too many logged in users"; - case errc::not_a_socket: return "not a socket"; - case errc::destination_address_required: return "destination address required"; + case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; + case errc::interrupted: return "system call interrupted"; + case errc::invalid_argument: return "invalid argument"; + case errc::invalid_seek: return "invalid seek"; + case errc::io_error: return "input/output error"; + case errc::is_a_directory: return "is a directory"; case errc::message_size: return "message size"; - case errc::wrong_protocol_type: return "wrong protocol type"; - case errc::no_protocol_option: return "no protocol option"; - case errc::protocol_not_supported: return "protocol not supported"; - case errc::socket_type_not_supported: return "socket type not supported"; - case errc::operation_not_supported: return "operation not supported"; - case errc::protocol_family_not_supported: return "protocol family not supported"; - case errc::address_family_not_supported: return "address family not supported"; - case errc::address_in_use: return "address in use"; - case errc::address_not_available: return "address not available"; case errc::network_down: return "network down"; - case errc::network_unreachable: return "network unreachable"; case errc::network_reset: return "network reset"; - case errc::connection_aborted: return "connection aborted"; - case errc::connection_reset: return "connection reset"; + case errc::network_unreachable: return "network unreachable"; case errc::no_buffer_space: return "no buffer space"; - case errc::already_connected: return "already connected"; + case errc::no_child_process: return "no child process"; + case errc::no_link: return "no link"; + case errc::no_lock_available: return "no lock available"; + case errc::no_message: return "no message"; + case errc::no_protocol_option: return "no protocol option"; + case errc::no_space_on_device: return "no space on device"; + case errc::no_such_device: return "no such device"; + case errc::no_such_device_or_address: return "no such device or address"; + case errc::no_such_file_or_directory: return "no such file or directory"; + case errc::no_such_process: return "no such process"; + case errc::not_a_directory: return "not a directory"; + case errc::not_a_socket: return "not a socket"; case errc::not_connected: return "not connected"; - case errc::connection_shut_down: return "connection shut down"; - case errc::too_many_references: return "too many references"; - case errc::timed_out: return "timed out"; - case errc::connection_refused: return "connection refused"; - case errc::host_is_down: return "host is down"; - case errc::host_unreachable: return "host unreachable"; - case errc::connection_already_in_progress: return "connection already in progress"; - case errc::operation_in_progress: return "operation in progress"; - case errc::stale_file_handle: return "stale file handle"; - case errc::structure_needs_cleaning: return "structure needs cleaning"; - case errc::remote_io_error: return "remote input/output error"; - case errc::disk_quota_exceeded: return "disk quota exceeded"; - case errc::no_medium_found: return "no medium found"; - case errc::wrong_medium_type: return "wrong medium type"; + case errc::not_enough_memory: return "not enough memory"; + case errc::not_supported: return "not supported"; case errc::operation_canceled: return "operation canceled"; - case errc::required_key_not_available: return "required key not available"; - case errc::key_expired: return "key expired"; - case errc::key_revoked: return "key revoked"; - case errc::key_rejected: return "key rejected"; + case errc::operation_in_progress: return "operation in progress"; + case errc::operation_not_permitted: return "operation not permitted"; + case errc::operation_not_supported: return "operation not supported"; + case errc::operation_would_block: return "operation would block"; case errc::owner_dead: return "owner dead"; + case errc::permission_denied: return "permission denied"; + case errc::protocol_error: return "protocol error"; + case errc::protocol_not_supported: return "protocol not supported"; + case errc::read_only_file_system: return "read only file system"; + case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; + case errc::resource_unavailable_try_again: return "resource unavailable, try again"; + case errc::result_out_of_range: return "result out of range"; + case errc::socket_type_not_supported: return "socket type not supported"; case errc::state_not_recoverable: return "state not recoverable"; - case errc::rf_kill: return "operation not possible due to RFkill"; - case errc::memory_page_hardware_error: return "memory page hardware error"; - case errc::not_supported: return "not supported"; + case errc::text_file_busy: return "text file busy"; + case errc::timed_out: return "timed out"; + case errc::too_many_files_open: return "too many files open"; + case errc::too_many_files_open_in_system: return "too many files open in system"; + case errc::too_many_links: return "too many links"; + case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; + case errc::value_too_large: return "value too large"; + case errc::wrong_protocol_type: return "wrong protocol type"; default: return "unknown system error"; // clang-format on } -- cgit v1.2.3 From 3aa2680cca25ba5f2e490a601461e09c3f081e3e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 11 Jul 2026 22:28:48 +0200 Subject: kstd: simplify errc message translation --- libs/kstd/kstd/system_error.hpp | 252 ++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 164 deletions(-) diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 5cc16e74..7618db2e 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -299,6 +299,92 @@ namespace kstd namespace bits { + constexpr auto to_message(errc error) -> std::string_view + { + switch (error) + { + // clang-format off + case errc::success: return "success"; + case errc::address_family_not_supported: return "address family not supported"; + case errc::address_in_use: return "address in use"; + case errc::address_not_available: return "address not available"; + case errc::already_connected: return "already connected"; + case errc::argument_list_too_long: return "argument list too long"; + case errc::argument_out_of_domain: return "argument out of domain"; + case errc::bad_address: return "bad address"; + case errc::bad_file_descriptor: return "bad file descriptor"; + case errc::bad_message: return "bad message"; + case errc::broken_pipe: return "broken pipe"; + case errc::connection_aborted: return "connection aborted"; + case errc::connection_already_in_progress: return "connection already in progress"; + case errc::connection_refused: return "connection refused"; + case errc::connection_reset: return "connection reset"; + case errc::cross_device_link: return "cross device link"; + case errc::destination_address_required: return "destination address required"; + case errc::device_or_resource_busy: return "device or resource busy"; + case errc::directory_not_empty: return "directory not empty"; + case errc::executable_format_error: return "invalid executable format"; + case errc::file_exists: return "file exists"; + case errc::file_too_large: return "file too large"; + case errc::filename_too_long: return "filename too long"; + case errc::function_not_supported: return "function not supported"; + case errc::host_unreachable: return "host unreachable"; + case errc::identifier_removed: return "identifier removed"; + case errc::illegal_byte_sequence: return "illegal byte sequence"; + case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; + case errc::interrupted: return "system call interrupted"; + case errc::invalid_argument: return "invalid argument"; + case errc::invalid_seek: return "invalid seek"; + case errc::io_error: return "input/output error"; + case errc::is_a_directory: return "is a directory"; + case errc::message_size: return "message size"; + case errc::network_down: return "network down"; + case errc::network_reset: return "network reset"; + case errc::network_unreachable: return "network unreachable"; + case errc::no_buffer_space: return "no buffer space"; + case errc::no_child_process: return "no child process"; + case errc::no_link: return "no link"; + case errc::no_lock_available: return "no lock available"; + case errc::no_message: return "no message"; + case errc::no_protocol_option: return "no protocol option"; + case errc::no_space_on_device: return "no space on device"; + case errc::no_such_device: return "no such device"; + case errc::no_such_device_or_address: return "no such device or address"; + case errc::no_such_file_or_directory: return "no such file or directory"; + case errc::no_such_process: return "no such process"; + case errc::not_a_directory: return "not a directory"; + case errc::not_a_socket: return "not a socket"; + case errc::not_connected: return "not connected"; + case errc::not_enough_memory: return "not enough memory"; + case errc::not_supported: return "not supported"; + case errc::operation_canceled: return "operation canceled"; + case errc::operation_in_progress: return "operation in progress"; + case errc::operation_not_permitted: return "operation not permitted"; + case errc::operation_not_supported: return "operation not supported"; + case errc::operation_would_block: return "operation would block"; + case errc::owner_dead: return "owner dead"; + case errc::permission_denied: return "permission denied"; + case errc::protocol_error: return "protocol error"; + case errc::protocol_not_supported: return "protocol not supported"; + case errc::read_only_file_system: return "read only file system"; + case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; + case errc::resource_unavailable_try_again: return "resource unavailable, try again"; + case errc::result_out_of_range: return "result out of range"; + case errc::socket_type_not_supported: return "socket type not supported"; + case errc::state_not_recoverable: return "state not recoverable"; + case errc::text_file_busy: return "text file busy"; + case errc::timed_out: return "timed out"; + case errc::too_many_files_open: return "too many files open"; + case errc::too_many_files_open_in_system: return "too many files open in system"; + case errc::too_many_links: return "too many links"; + case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; + case errc::value_too_large: return "value too large"; + case errc::wrong_protocol_type: return "wrong protocol type"; + default: return "unknown error"; + // clang-format on + } + } + struct generic_category_t final : error_category { [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override @@ -308,88 +394,7 @@ namespace kstd [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override { - switch (static_cast(value)) - { - // clang-format off - case errc::success: return "success"; - case errc::address_family_not_supported: return "address family not supported"; - case errc::address_in_use: return "address in use"; - case errc::address_not_available: return "address not available"; - case errc::already_connected: return "already connected"; - case errc::argument_list_too_long: return "argument list too long"; - case errc::argument_out_of_domain: return "argument out of domain"; - case errc::bad_address: return "bad address"; - case errc::bad_file_descriptor: return "bad file descriptor"; - case errc::bad_message: return "bad message"; - case errc::broken_pipe: return "broken pipe"; - case errc::connection_aborted: return "connection aborted"; - case errc::connection_already_in_progress: return "connection already in progress"; - case errc::connection_refused: return "connection refused"; - case errc::connection_reset: return "connection reset"; - case errc::cross_device_link: return "cross device link"; - case errc::destination_address_required: return "destination address required"; - case errc::device_or_resource_busy: return "device or resource busy"; - case errc::directory_not_empty: return "directory not empty"; - case errc::executable_format_error: return "invalid executable format"; - case errc::file_exists: return "file exists"; - case errc::file_too_large: return "file too large"; - case errc::filename_too_long: return "filename too long"; - case errc::function_not_supported: return "function not supported"; - case errc::host_unreachable: return "host unreachable"; - case errc::identifier_removed: return "identifier removed"; - case errc::illegal_byte_sequence: return "illegal byte sequence"; - case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; - case errc::interrupted: return "system call interrupted"; - case errc::invalid_argument: return "invalid argument"; - case errc::invalid_seek: return "invalid seek"; - case errc::io_error: return "input/output error"; - case errc::is_a_directory: return "is a directory"; - case errc::message_size: return "message size"; - case errc::network_down: return "network down"; - case errc::network_reset: return "network reset"; - case errc::network_unreachable: return "network unreachable"; - case errc::no_buffer_space: return "no buffer space"; - case errc::no_child_process: return "no child process"; - case errc::no_link: return "no link"; - case errc::no_lock_available: return "no lock available"; - case errc::no_message: return "no message"; - case errc::no_protocol_option: return "no protocol option"; - case errc::no_space_on_device: return "no space on device"; - case errc::no_such_device: return "no such device"; - case errc::no_such_device_or_address: return "no such device or address"; - case errc::no_such_file_or_directory: return "no such file or directory"; - case errc::no_such_process: return "no such process"; - case errc::not_a_directory: return "not a directory"; - case errc::not_a_socket: return "not a socket"; - case errc::not_connected: return "not connected"; - case errc::not_enough_memory: return "not enough memory"; - case errc::not_supported: return "not supported"; - case errc::operation_canceled: return "operation canceled"; - case errc::operation_in_progress: return "operation in progress"; - case errc::operation_not_permitted: return "operation not permitted"; - case errc::operation_not_supported: return "operation not supported"; - case errc::operation_would_block: return "operation would block"; - case errc::owner_dead: return "owner dead"; - case errc::permission_denied: return "permission denied"; - case errc::protocol_error: return "protocol error"; - case errc::protocol_not_supported: return "protocol not supported"; - case errc::read_only_file_system: return "read only file system"; - case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; - case errc::resource_unavailable_try_again: return "resource unavailable, try again"; - case errc::result_out_of_range: return "result out of range"; - case errc::socket_type_not_supported: return "socket type not supported"; - case errc::state_not_recoverable: return "state not recoverable"; - case errc::text_file_busy: return "text file busy"; - case errc::timed_out: return "timed out"; - case errc::too_many_files_open: return "too many files open"; - case errc::too_many_files_open_in_system: return "too many files open in system"; - case errc::too_many_links: return "too many links"; - case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; - case errc::value_too_large: return "value too large"; - case errc::wrong_protocol_type: return "wrong protocol type"; - default: return "unknown system error"; - // clang-format on - } + return to_message(static_cast(value)); } } constexpr inline generic_category_instance{}; @@ -402,88 +407,7 @@ namespace kstd [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override { - switch (static_cast(value)) - { - // clang-format off - case errc::success: return "success"; - case errc::address_family_not_supported: return "address family not supported"; - case errc::address_in_use: return "address in use"; - case errc::address_not_available: return "address not available"; - case errc::already_connected: return "already connected"; - case errc::argument_list_too_long: return "argument list too long"; - case errc::argument_out_of_domain: return "argument out of domain"; - case errc::bad_address: return "bad address"; - case errc::bad_file_descriptor: return "bad file descriptor"; - case errc::bad_message: return "bad message"; - case errc::broken_pipe: return "broken pipe"; - case errc::connection_aborted: return "connection aborted"; - case errc::connection_already_in_progress: return "connection already in progress"; - case errc::connection_refused: return "connection refused"; - case errc::connection_reset: return "connection reset"; - case errc::cross_device_link: return "cross device link"; - case errc::destination_address_required: return "destination address required"; - case errc::device_or_resource_busy: return "device or resource busy"; - case errc::directory_not_empty: return "directory not empty"; - case errc::executable_format_error: return "invalid executable format"; - case errc::file_exists: return "file exists"; - case errc::file_too_large: return "file too large"; - case errc::filename_too_long: return "filename too long"; - case errc::function_not_supported: return "function not supported"; - case errc::host_unreachable: return "host unreachable"; - case errc::identifier_removed: return "identifier removed"; - case errc::illegal_byte_sequence: return "illegal byte sequence"; - case errc::inappropriate_io_control_operation: return "inappropriate io control operation"; - case errc::interrupted: return "system call interrupted"; - case errc::invalid_argument: return "invalid argument"; - case errc::invalid_seek: return "invalid seek"; - case errc::io_error: return "input/output error"; - case errc::is_a_directory: return "is a directory"; - case errc::message_size: return "message size"; - case errc::network_down: return "network down"; - case errc::network_reset: return "network reset"; - case errc::network_unreachable: return "network unreachable"; - case errc::no_buffer_space: return "no buffer space"; - case errc::no_child_process: return "no child process"; - case errc::no_link: return "no link"; - case errc::no_lock_available: return "no lock available"; - case errc::no_message: return "no message"; - case errc::no_protocol_option: return "no protocol option"; - case errc::no_space_on_device: return "no space on device"; - case errc::no_such_device: return "no such device"; - case errc::no_such_device_or_address: return "no such device or address"; - case errc::no_such_file_or_directory: return "no such file or directory"; - case errc::no_such_process: return "no such process"; - case errc::not_a_directory: return "not a directory"; - case errc::not_a_socket: return "not a socket"; - case errc::not_connected: return "not connected"; - case errc::not_enough_memory: return "not enough memory"; - case errc::not_supported: return "not supported"; - case errc::operation_canceled: return "operation canceled"; - case errc::operation_in_progress: return "operation in progress"; - case errc::operation_not_permitted: return "operation not permitted"; - case errc::operation_not_supported: return "operation not supported"; - case errc::operation_would_block: return "operation would block"; - case errc::owner_dead: return "owner dead"; - case errc::permission_denied: return "permission denied"; - case errc::protocol_error: return "protocol error"; - case errc::protocol_not_supported: return "protocol not supported"; - case errc::read_only_file_system: return "read only file system"; - case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; - case errc::resource_unavailable_try_again: return "resource unavailable, try again"; - case errc::result_out_of_range: return "result out of range"; - case errc::socket_type_not_supported: return "socket type not supported"; - case errc::state_not_recoverable: return "state not recoverable"; - case errc::text_file_busy: return "text file busy"; - case errc::timed_out: return "timed out"; - case errc::too_many_files_open: return "too many files open"; - case errc::too_many_files_open_in_system: return "too many files open in system"; - case errc::too_many_links: return "too many links"; - case errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; - case errc::value_too_large: return "value too large"; - case errc::wrong_protocol_type: return "wrong protocol type"; - default: return "unknown system error"; - // clang-format on - } + return to_message(static_cast(value)); } } constexpr inline system_category_instance{}; } // namespace bits -- cgit v1.2.3 From f54e09351a967da21f863fd2d3b17ffd24d3ee87 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 12 Jul 2026 15:24:40 +0200 Subject: kernel: integrate kstd system_error facility --- kapi/kapi/filesystem.hpp | 4 +- kernel/kapi/filesystem.cpp | 11 +-- kernel/kapi/filesystem.tests.cpp | 32 +++---- kernel/kernel/filesystem/error.hpp | 106 +++++++++++++++++++++ .../filesystem/open_file_descriptor.tests.cpp | 4 +- kernel/kernel/filesystem/vfs.cpp | 36 ++++--- kernel/kernel/filesystem/vfs.hpp | 8 +- kernel/kernel/filesystem/vfs.tests.cpp | 51 +++++----- kernel/kernel/main.cpp | 20 ++-- libs/kstd/kstd/system_error.hpp | 2 +- 10 files changed, 192 insertions(+), 82 deletions(-) create mode 100644 kernel/kernel/filesystem/error.hpp diff --git a/kapi/kapi/filesystem.hpp b/kapi/kapi/filesystem.hpp index 3bd9aaf4..02f174e9 100644 --- a/kapi/kapi/filesystem.hpp +++ b/kapi/kapi/filesystem.hpp @@ -1,9 +1,11 @@ #ifndef TEACHOS_KAPI_FILESYSTEM_HPP #define TEACHOS_KAPI_FILESYSTEM_HPP +#include #include #include +#include #include namespace kapi::filesystem @@ -39,7 +41,7 @@ namespace kapi::filesystem @return A file descriptor on success, -1 on failure. @qualifier kernel-defined */ - auto open(std::string_view path) -> kstd::ssize_t; + auto open(std::string_view path) -> std::expected; /** @brief Closes a @p file_descriptor. diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 4880ae38..24a3adf3 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -5,9 +5,11 @@ #include #include +#include #include #include +#include #include namespace kapi::filesystem @@ -30,15 +32,12 @@ namespace kapi::filesystem return -1; } - auto open(std::string_view path) -> kstd::ssize_t + auto open(std::string_view path) -> std::expected { - if (auto dentry = kernel::filesystem::vfs::get().open(path)) - { + return kernel::filesystem::vfs::get().open(path).transform([](auto dentry) { auto open_file_descriptor = kstd::make_shared(dentry); return kernel::filesystem::open_file_table::get().add_file(open_file_descriptor); - } - - return -1; + }); } auto close(size_t file_descriptor) -> kstd::ssize_t diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index d241afa8..37d0c20d 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -25,8 +25,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("files can be opened, read and closed again") { - auto fd = kapi::filesystem::open("/information/info_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/information/info_1.txt").value(); auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -40,8 +39,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("files can be opened through absolute symbolic link, read and closed again") { - auto fd = kapi::filesystem::open("/symlinks/information_directory_absolute/info_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/symlinks/information_directory_absolute/info_1.txt").value(); auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -55,8 +53,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("files can be opened through relative symbolic link, read and closed again") { - auto fd = kapi::filesystem::open("/symlinks/information_directory_relative/info_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/symlinks/information_directory_relative/info_1.txt").value(); auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -72,8 +69,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap { kapi::filesystem::mount("/archiv/2024.img", "/information"); - auto fd = kapi::filesystem::open("/information/symlinks/traverse_back_twice/information/sheep_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/information/symlinks/traverse_back_twice/information/sheep_1.txt").value(); auto buffer = std::vector(7); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -89,8 +85,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap { REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); - auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt").value(); auto buffer = std::vector(8); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -107,8 +102,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap { REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); - auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt").value(); REQUIRE(kapi::filesystem::umount("/information") < 0); @@ -118,8 +112,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("device can be opened as file and read from") { - auto fd = kapi::filesystem::open("/dev/ram0"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/dev/ram0").value(); auto buffer = std::vector(512); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); @@ -130,15 +123,13 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("device can be opened as file and written to and read from again") { - auto read_fd = kapi::filesystem::open("/dev/ram16"); - REQUIRE(read_fd >= 0); + auto read_fd = kapi::filesystem::open("/dev/ram16").value(); auto buffer = std::vector(512, std::byte{0xAB}); auto bytes_written = kapi::filesystem::write(read_fd, buffer.data(), buffer.size()); REQUIRE(bytes_written >= 0); - auto write_fd = kapi::filesystem::open("/dev/ram16"); - REQUIRE(write_fd >= 0); + auto write_fd = kapi::filesystem::open("/dev/ram16").value(); auto read_buffer = std::vector(512); auto bytes_read = kapi::filesystem::read(write_fd, read_buffer.data(), read_buffer.size()); @@ -163,7 +154,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("non existent files cannot be opened") { auto fd = kapi::filesystem::open("/information/non_existent.txt"); - REQUIRE(fd < 0); + REQUIRE(!fd); } THEN("not opened files cannot closed") @@ -173,8 +164,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("same file cannot be closed twice") { - auto fd = kapi::filesystem::open("/information/info_1.txt"); - REQUIRE(fd >= 0); + auto fd = kapi::filesystem::open("/information/info_1.txt").value(); REQUIRE(kapi::filesystem::close(fd) == 0); REQUIRE(kapi::filesystem::close(fd) < 0); diff --git a/kernel/kernel/filesystem/error.hpp b/kernel/kernel/filesystem/error.hpp new file mode 100644 index 00000000..267e5b7e --- /dev/null +++ b/kernel/kernel/filesystem/error.hpp @@ -0,0 +1,106 @@ +#ifndef TEACHOS_KERNEL_FILESYSTEM_ERROR_HPP +#define TEACHOS_KERNEL_FILESYSTEM_ERROR_HPP + +#include + +#include +#include + +namespace kernel::filesystem +{ + + enum struct vfs_errc : int + { + invalid_path = 1, + non_existent_path, + mount_point_not_found, + mount_busy, + has_child_mounts, + invalid_filesystem, + unmount_failed, + }; + + namespace detail + { + struct vfs_category_t final : kstd::error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "vfs"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast(value)) + { + case vfs_errc::invalid_path: + return "invalid path"; + case vfs_errc::non_existent_path: + return "path does not exist"; + case vfs_errc::mount_point_not_found: + return "mount point not found"; + case vfs_errc::mount_busy: + return "mount point is busy"; + case vfs_errc::has_child_mounts: + return "mount point has child mounts"; + case vfs_errc::invalid_filesystem: + return "invalid filesystem"; + case vfs_errc::unmount_failed: + return "unmount failed"; + default: + return "unknown VFS error"; + } + } + + [[nodiscard]] constexpr auto default_error_condition(int value) const noexcept -> kstd::error_condition override + { + switch (static_cast(value)) + { + case vfs_errc::invalid_path: + case vfs_errc::invalid_filesystem: + return make_error_condition(kstd::errc::invalid_argument); + case vfs_errc::non_existent_path: + case vfs_errc::mount_point_not_found: + return make_error_condition(kstd::errc::no_such_file_or_directory); + case kernel::filesystem::vfs_errc::mount_busy: + case kernel::filesystem::vfs_errc::has_child_mounts: + case vfs_errc::unmount_failed: + return make_error_condition(kstd::errc::device_or_resource_busy); + default: + return kstd::error_condition{value, *this}; + } + } + } constexpr inline vfs_category_instance{}; + } // namespace detail + + [[nodiscard]] constexpr auto inline vfs_category() noexcept -> kstd::error_category const & + { + return detail::vfs_category_instance; + } + + [[nodiscard]] constexpr auto inline make_error_code(vfs_errc error) noexcept -> kstd::error_code + { + return {static_cast(error), vfs_category()}; + } + + [[nodiscard]] constexpr auto inline make_error_condition(vfs_errc error) noexcept -> kstd::error_condition + { + return {static_cast(error), vfs_category()}; + } + +} // namespace kernel::filesystem + +namespace kstd +{ + template<> + struct is_error_code_enum : std::true_type + { + }; + + template<> + struct is_error_condition_enum : std::true_type + { + }; +} // namespace kstd + +#endif diff --git a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp index c666ef43..a04fbaa5 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp @@ -81,8 +81,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope auto & vfs = kernel::filesystem::vfs::get(); auto dentry = vfs.open("/information/info_1.txt"); - REQUIRE(dentry != nullptr); - auto ofd = kstd::make_shared(dentry); + REQUIRE(dentry); + auto ofd = kstd::make_shared(dentry.value()); THEN("the file can be read and the offset is updated") { diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index f6c64f0f..a5c1593d 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -12,10 +13,12 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -54,7 +57,8 @@ namespace kernel::filesystem graft_persistent_device_fs(device_fs); // mount boot fs at / (shadows rootfs), re-graft devfs - auto [boot_device_dentry, boot_device_mount_context] = resolve_path_internal("/dev/ram0"); + auto [boot_device_dentry, boot_device_mount_context] = + resolve_path_internal("/dev/ram0").value_or(std::pair{nullptr, nullptr}); if (boot_device_dentry && boot_device_mount_context) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) @@ -78,13 +82,15 @@ namespace kernel::filesystem return *active_vfs; } - auto vfs::open(std::string_view path) -> kstd::shared_ptr + auto vfs::open(std::string_view path) -> std::expected, kstd::error_code> { - auto [dentry, mount] = resolve_path_internal(path); - if (!dentry || !mount) + auto resolved_path = resolve_path_internal(path); + if (!resolved_path) { - return nullptr; + return std::unexpected{resolved_path.error()}; } + + auto [dentry, mount] = resolved_path.value(); mount->increment_ref_count(); return dentry; } @@ -106,10 +112,10 @@ namespace kernel::filesystem return operation_result::invalid_path; } - auto [mount_point_dentry, mount_context] = resolve_path_internal(target); + auto [mount_point_dentry, mount_context] = resolve_path_internal(target).value_or(std::pair{nullptr, nullptr}); if (mount_point_dentry && mount_context) { - auto [source_dentry, source_mount_context] = resolve_path_internal(source); + auto [source_dentry, source_mount_context] = resolve_path_internal(source).value_or(std::pair{nullptr, nullptr}); if (source_dentry && source_mount_context) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) @@ -156,7 +162,7 @@ namespace kernel::filesystem auto vfs::graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void { - auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/"); + auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/").value_or(std::pair{nullptr, nullptr}); if (root_mount_point_dentry && root_mount) { auto dev_dentry = root_mount_point_dentry->find_child("dev"); @@ -171,11 +177,11 @@ namespace kernel::filesystem } auto vfs::resolve_path_internal(std::string_view path) const - -> std::pair, kstd::shared_ptr> + -> std::expected, kstd::shared_ptr>, kstd::error_code> { if (!path::is_valid_absolute_path(path)) { - return {nullptr, nullptr}; + return std::unexpected{make_error_code(vfs_errc::invalid_path)}; } auto current_mount = m_mount_table.find_mount("/"); @@ -231,7 +237,7 @@ namespace kernel::filesystem auto found_inode = current_fs->lookup(current_dentry->get_inode(), part); if (!found_inode) { - return {nullptr, nullptr}; + return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; } next_dentry = kstd::make_shared(current_dentry, found_inode, part); @@ -252,7 +258,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return {nullptr, nullptr}; + return std::unexpected{make_error_code(kstd::errc::too_many_symbolic_link_levels)}; } kstd::vector buffer(constants::symlink_max_path_length); @@ -275,17 +281,17 @@ namespace kernel::filesystem current_dentry = next_dentry; } - return {current_dentry, current_mount}; + return std::pair{current_dentry, current_mount}; } auto vfs::resolve_path(std::string_view path) const -> kstd::shared_ptr { - return resolve_path_internal(path).first; + return resolve_path_internal(path).transform([](auto result) { return result.first; }).value_or(nullptr); } auto vfs::find_mount(std::string_view path) const -> kstd::shared_ptr { - return resolve_path_internal(path).second; + return resolve_path_internal(path).transform([](auto result) { return result.second; }).value_or(nullptr); } } // namespace kernel::filesystem diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index bb1167fb..ef26aa84 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -8,7 +8,9 @@ #include #include +#include +#include #include #include @@ -58,9 +60,9 @@ namespace kernel::filesystem /** @brief Open a file by its @p path. This method resolves the path and returns the corresponding dentry. @param path The path to the file to open. - @return A shared pointer to the dentry or a null pointer if the file could not be opened. + @return A shared pointer to the dentry on success or an error code on failure. */ - auto open(std::string_view path) -> kstd::shared_ptr; + auto open(std::string_view path) -> std::expected, kstd::error_code>; /** @brief Close a file by its associated @p path. @@ -96,7 +98,7 @@ namespace kernel::filesystem * - find_mount() for the mount context only. */ [[nodiscard]] auto resolve_path_internal(std::string_view path) const - -> std::pair, kstd::shared_ptr>; + -> std::expected, kstd::shared_ptr>, kstd::error_code>; [[nodiscard]] auto resolve_path(std::string_view path) const -> kstd::shared_ptr; [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr; diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index 849300ac..c5130ef4 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -105,8 +105,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(mounted_monkey_1); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); @@ -125,11 +126,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); - REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(mounted_fish1 != nullptr); + REQUIRE(mounted_monkey_1); + REQUIRE(mounted_fish1); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(mounted_fish1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_fish1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == @@ -142,11 +144,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey_1 != nullptr); + REQUIRE(mounted_monkey_1); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); } @@ -160,10 +163,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("file cannot be closed twice") { auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); + REQUIRE(info_1); - REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE_THROWS_AS(vfs.close(info_1->absolute_path()), std::runtime_error); + REQUIRE(vfs.close(info_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE_THROWS_AS(vfs.close(info_1.value()->absolute_path()), std::runtime_error); } THEN("images can be stacked mounted and correct file system is unmounted again") @@ -172,9 +175,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram32", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); - REQUIRE(mounted_tickets != nullptr); + REQUIRE(mounted_tickets); - REQUIRE(vfs.close(mounted_tickets->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_tickets.value()->absolute_path()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); @@ -197,7 +201,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -218,7 +222,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16 == nullptr); @@ -236,7 +240,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -352,14 +356,14 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); - auto sheep_1_ofd = kstd::make_shared(dentry); + auto sheep_1_ofd = kstd::make_shared(dentry.value()); kstd::vector buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); - REQUIRE(vfs.close(dentry->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(dentry.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); @@ -379,8 +383,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); - auto sheep_1_ofd = kstd::make_shared(sheep_1); - auto goat_1_ofd = kstd::make_shared(goat_1); + auto sheep_1_ofd = kstd::make_shared(sheep_1.value()); + auto goat_1_ofd = kstd::make_shared(goat_1.value()); kstd::vector sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); @@ -392,8 +396,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); - REQUIRE(vfs.close(sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(goat_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(sheep_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(goat_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); @@ -404,7 +408,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); - REQUIRE(vfs.close(still_mounted_sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(still_mounted_sheep_1.value()->absolute_path()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index 88f76f88..76c05204 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -28,18 +28,18 @@ auto run_demo() -> void // 1) open a file kstd::println("attempting to open /entrance/tickets.txt"); auto fd_1 = kapi::filesystem::open("/entrance/tickets.txt"); - if (fd_1 == -1) + if (!fd_1) { kapi::system::panic("demo failed"); } else { - kstd::println("--> successfully opened /entrance/tickets.txt with file descriptor {}", fd_1); + kstd::println("--> successfully opened /entrance/tickets.txt with file descriptor {}", fd_1.value()); } // 2) read from the file kstd::vector buffer_1{10}; - auto bytes_read = kapi::filesystem::read(fd_1, buffer_1.data(), buffer_1.size()); + auto bytes_read = kapi::filesystem::read(fd_1.value(), buffer_1.data(), buffer_1.size()); auto buffer_as_str = std::string_view{reinterpret_cast(buffer_1.data()), static_cast(bytes_read)}; kstd::println("--> read {} bytes from /entrance/tickets.txt: {}", bytes_read, buffer_as_str); kstd::println(""); @@ -67,9 +67,9 @@ auto run_demo() -> void // 5) open a file from the new filesystem kstd::println("attempting to open /entrance/information/info_1.txt"); auto fd_2 = kapi::filesystem::open("/entrance/information/info_1.txt"); - if (fd_2 != -1) + if (fd_2) { - kstd::println("--> successfully opened /entrance/information/info_1.txt with file descriptor {}", fd_2); + kstd::println("--> successfully opened /entrance/information/info_1.txt with file descriptor {}", fd_2.value()); } else { @@ -78,16 +78,16 @@ auto run_demo() -> void // 6) read from the new file kstd::vector buffer_2{10}; - bytes_read = kapi::filesystem::read(fd_2, buffer_2.data(), buffer_2.size()); + bytes_read = kapi::filesystem::read(fd_2.value(), buffer_2.data(), buffer_2.size()); buffer_as_str = std::string_view{reinterpret_cast(buffer_2.data()), static_cast(bytes_read)}; kstd::println("--> read {} bytes from /entrance/information/info_1.txt: {} ", bytes_read, buffer_as_str); // 7) open device as file kstd::println("attempting to open /dev/ram32 as a file"); auto fd_3 = kapi::filesystem::open("/dev/ram32"); - if (fd_3 != -1) + if (fd_3) { - kstd::println("--> successfully opened /dev/ram32 as a file with file descriptor {}", fd_3); + kstd::println("--> successfully opened /dev/ram32 as a file with file descriptor {}", fd_3.value()); } else { @@ -96,13 +96,13 @@ auto run_demo() -> void // 8) read from the device file kstd::vector buffer_3{2}; - bytes_read = kapi::filesystem::read(fd_3, buffer_3.data(), buffer_3.size()); + bytes_read = kapi::filesystem::read(fd_3.value(), buffer_3.data(), buffer_3.size()); kstd::println("--> read {} bytes from /dev/ram32: {::#04x} ", bytes_read, buffer_3); // 9) write to the device file auto const default_buffer_value = std::byte{0xAA}; kstd::vector write_buffer{default_buffer_value, default_buffer_value}; - auto bytes_written = kapi::filesystem::write(fd_3, write_buffer.data(), write_buffer.size()); + auto bytes_written = kapi::filesystem::write(fd_3.value(), write_buffer.data(), write_buffer.size()); kstd::println("--> written {} bytes to /dev/ram32: {::#04x}", bytes_written, write_buffer); // 10) do memory dump to show that the write to the device file had an effect diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 7618db2e..f971703b 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -467,7 +467,7 @@ namespace kstd return {static_cast(value), generic_category()}; } - [[nodiscard]] constexpr auto inline make_error_condition(errc const value) noexcept -> error_code + [[nodiscard]] constexpr auto inline make_error_condition(errc const value) noexcept -> error_condition { return {static_cast(value), generic_category()}; } -- cgit v1.2.3 From 3dc0df78f96dae38a54208d78e14f977f085b9bb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 12 Jul 2026 21:37:50 +0200 Subject: kapi: enforce stricter error handling --- kapi/kapi/filesystem.hpp | 23 ++- kernel/kapi/filesystem.cpp | 58 +++---- kernel/kapi/filesystem.tests.cpp | 72 ++++---- kernel/kernel/filesystem/open_file_table.cpp | 20 ++- kernel/kernel/filesystem/open_file_table.hpp | 9 +- kernel/kernel/filesystem/open_file_table.tests.cpp | 21 +-- kernel/kernel/filesystem/vfs.cpp | 40 ++--- kernel/kernel/filesystem/vfs.hpp | 35 ++-- kernel/kernel/filesystem/vfs.tests.cpp | 185 ++++++++++----------- kernel/kernel/main.cpp | 15 +- 10 files changed, 225 insertions(+), 253 deletions(-) diff --git a/kapi/kapi/filesystem.hpp b/kapi/kapi/filesystem.hpp index 02f174e9..5346a96d 100644 --- a/kapi/kapi/filesystem.hpp +++ b/kapi/kapi/filesystem.hpp @@ -2,7 +2,6 @@ #define TEACHOS_KAPI_FILESYSTEM_HPP #include -#include #include #include @@ -22,23 +21,23 @@ namespace kapi::filesystem @brief Mounts a filesystem from the specified @p source at the specified @p target path. @param source The source device or filesystem to mount. @param target The target mount point. - @return 0 on success, -1 on failure. + @return Nothing on success, an error code otherwise. @qualifier kernel-defined */ - auto mount(std::string_view source, std::string_view target) -> kstd::ssize_t; + auto mount(std::string_view source, std::string_view target) -> std::expected; /** @brief Unmounts a filesystem from the specified @p target path. @param target The target mount point to unmount. - @return 0 on success, -1 on failure. + @return Nothing on success, an error code otherwise. @qualifier kernel-defined */ - auto umount(std::string_view target) -> kstd::ssize_t; + auto umount(std::string_view target) -> std::expected; /** @brief Opens a file at the specified @p path. @param path The path to the file to open. - @return A file descriptor on success, -1 on failure. + @return A file descriptor on success, an error code otherwise. @qualifier kernel-defined */ auto open(std::string_view path) -> std::expected; @@ -46,30 +45,30 @@ namespace kapi::filesystem /** @brief Closes a @p file_descriptor. @param file_descriptor The file descriptor to close. - @return 0 on success, -1 on failure. + @return Nothing on success, an error code otherwise . @qualifier kernel-defined */ - auto close(size_t file_descriptor) -> kstd::ssize_t; + auto close(size_t file_descriptor) -> std::expected; /** @brief Reads @p size bytes into @p buffer from a @p file_descriptor. @param file_descriptor The file descriptor to read from. @param buffer The buffer to store the read data. @param size The number of bytes to read. - @return The number of bytes read on success, -1 on failure. + @return The number of bytes read on success, an error code otherwise. @qualifier kernel-defined */ - auto read(size_t file_descriptor, void * buffer, size_t size) -> kstd::ssize_t; + auto read(size_t file_descriptor, void * buffer, size_t size) -> std::expected; /** @brief Writes @p size bytes from @p buffer to a @p file_descriptor. @param file_descriptor The file descriptor to write to. @param buffer The buffer containing the data to write. @param size The number of bytes to write. - @return The number of bytes written on success, -1 on failure. + @return The number of bytes written on success, an error code otherwise. @qualifier kernel-defined */ - auto write(size_t file_descriptor, void const * buffer, size_t size) -> kstd::ssize_t; + auto write(size_t file_descriptor, void const * buffer, size_t size) -> std::expected; } // namespace kapi::filesystem #endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 24a3adf3..f84949da 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -14,62 +13,45 @@ namespace kapi::filesystem { - auto mount(std::string_view source, std::string_view target) -> kstd::ssize_t + auto mount(std::string_view source, std::string_view target) -> std::expected { - if (kernel::filesystem::vfs::get().do_mount(source, target) == kernel::filesystem::vfs::operation_result::success) - { - return 0; - } - return -1; + return kernel::filesystem::vfs::get().do_mount(source, target); } - auto umount(std::string_view target) -> kstd::ssize_t + auto umount(std::string_view target) -> std::expected { - if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) - { - return 0; - } - return -1; + return kernel::filesystem::vfs::get().unmount(target); } auto open(std::string_view path) -> std::expected { - return kernel::filesystem::vfs::get().open(path).transform([](auto dentry) { + return kernel::filesystem::vfs::get().open(path).and_then([](auto dentry) { auto open_file_descriptor = kstd::make_shared(dentry); return kernel::filesystem::open_file_table::get().add_file(open_file_descriptor); }); } - auto close(size_t file_descriptor) -> kstd::ssize_t + auto close(size_t file_descriptor) -> std::expected { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) - { - if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success) - { - return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); - } - } - return -1; + return kernel::filesystem::open_file_table::get() + .file(file_descriptor) + .transform([](auto file) { return file->get_dentry()->absolute_path(); }) + .and_then([](auto path) { return kernel::filesystem::vfs::get().close(path); }) + .and_then([=]() { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); }) + .transform([](auto) { return; }); } - auto read(size_t file_descriptor, void * buffer, size_t size) -> kstd::ssize_t + auto read(size_t file_descriptor, void * buffer, size_t size) -> std::expected { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) - { - return open_file_descriptor->read(buffer, size); - } - - return -1; + return kernel::filesystem::open_file_table::get().file(file_descriptor).transform([=](auto descriptor) { + return descriptor->read(buffer, size); + }); } - auto write(size_t file_descriptor, void const * buffer, size_t size) -> kstd::ssize_t + auto write(size_t file_descriptor, void const * buffer, size_t size) -> std::expected { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) - { - return open_file_descriptor->write(buffer, size); - } - - return -1; + return kernel::filesystem::open_file_table::get().file(file_descriptor).transform([=](auto descriptor) { + return descriptor->write(buffer, size); + }); } } // namespace kapi::filesystem \ No newline at end of file diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index 37d0c20d..d1e01db6 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -29,12 +31,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(*bytes_read)}; REQUIRE(buffer_as_str == "info_1"); - REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd)); } THEN("files can be opened through absolute symbolic link, read and closed again") @@ -43,12 +45,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(*bytes_read)}; REQUIRE(buffer_as_str == "info_1"); - REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd)); } THEN("files can be opened through relative symbolic link, read and closed again") @@ -57,57 +59,57 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto buffer = std::vector(6); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(*bytes_read)}; REQUIRE(buffer_as_str == "info_1"); - REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd)); } THEN("files can be opened through relative symbolic link over multiple mount points, read and closed again") { - kapi::filesystem::mount("/archiv/2024.img", "/information"); + CHECK(kapi::filesystem::mount("/archiv/2024.img", "/information")); auto fd = kapi::filesystem::open("/information/symlinks/traverse_back_twice/information/sheep_1.txt").value(); auto buffer = std::vector(7); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(*bytes_read)}; REQUIRE(buffer_as_str == "sheep_1"); - REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd)); } THEN("a filesystem can be mounted, files can be opened, read and closed again and unmounted") { - REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); + REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information")); auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt").value(); auto buffer = std::vector(8); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(*bytes_read)}; REQUIRE(buffer_as_str == "monkey_1"); - REQUIRE(kapi::filesystem::close(fd) == 0); - REQUIRE(kapi::filesystem::umount("/information") == 0); + REQUIRE(kapi::filesystem::close(fd)); + REQUIRE(kapi::filesystem::umount("/information")); } THEN("a filesystem cannot be unmounted if files are still open and can be unmounted after files are closed") { - REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); + REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information")); auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt").value(); - REQUIRE(kapi::filesystem::umount("/information") < 0); + REQUIRE(!kapi::filesystem::umount("/information")); - REQUIRE(kapi::filesystem::close(fd) == 0); - REQUIRE(kapi::filesystem::umount("/information") == 0); + REQUIRE(kapi::filesystem::close(fd)); + REQUIRE(kapi::filesystem::umount("/information")); } THEN("device can be opened as file and read from") @@ -116,9 +118,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto buffer = std::vector(512); auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); - REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd)); } THEN("device can be opened as file and written to and read from again") @@ -127,28 +129,28 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto buffer = std::vector(512, std::byte{0xAB}); auto bytes_written = kapi::filesystem::write(read_fd, buffer.data(), buffer.size()); - REQUIRE(bytes_written >= 0); + REQUIRE(bytes_written); auto write_fd = kapi::filesystem::open("/dev/ram16").value(); auto read_buffer = std::vector(512); auto bytes_read = kapi::filesystem::read(write_fd, read_buffer.data(), read_buffer.size()); - REQUIRE(bytes_read >= 0); + REQUIRE(bytes_read); REQUIRE(std::equal(buffer.begin(), buffer.end(), read_buffer.begin())); - REQUIRE(kapi::filesystem::close(write_fd) == 0); - REQUIRE(kapi::filesystem::close(read_fd) == 0); + REQUIRE(kapi::filesystem::close(write_fd)); + REQUIRE(kapi::filesystem::close(read_fd)); } THEN("invalid paths cannot be mounted or unmounted") { - REQUIRE(kapi::filesystem::mount("/dev/ram16", "invalid_path") < 0); + REQUIRE(!kapi::filesystem::mount("/dev/ram16", "invalid_path")); } THEN("invalid paths cannot be unmounted") { - REQUIRE(kapi::filesystem::umount("invalid_path") < 0); + REQUIRE(!kapi::filesystem::umount("invalid_path")); } THEN("non existent files cannot be opened") @@ -159,15 +161,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("not opened files cannot closed") { - REQUIRE(kapi::filesystem::close(999) < 0); + REQUIRE(!kapi::filesystem::close(999)); } THEN("same file cannot be closed twice") { auto fd = kapi::filesystem::open("/information/info_1.txt").value(); - REQUIRE(kapi::filesystem::close(fd) == 0); - REQUIRE(kapi::filesystem::close(fd) < 0); + REQUIRE(kapi::filesystem::close(fd)); + REQUIRE(!kapi::filesystem::close(fd)); } THEN("not opened files cannot be read from") @@ -175,7 +177,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap std::vector buffer(10); auto const invalid_fd = 999uz; auto bytes_read = kapi::filesystem::read(invalid_fd, buffer.data(), buffer.size()); - REQUIRE(bytes_read < 0); + REQUIRE(!bytes_read); } THEN("not opened files cannot be written to") @@ -183,7 +185,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap std::vector buffer(10); auto const invalid_fd = 999uz; auto bytes_written = kapi::filesystem::write(invalid_fd, buffer.data(), buffer.size()); - REQUIRE(bytes_written < 0); + REQUIRE(!bytes_written); } } } diff --git a/kernel/kernel/filesystem/open_file_table.cpp b/kernel/kernel/filesystem/open_file_table.cpp index 38bd7a43..df5717a4 100644 --- a/kernel/kernel/filesystem/open_file_table.cpp +++ b/kernel/kernel/filesystem/open_file_table.cpp @@ -5,10 +5,11 @@ #include #include -#include +#include #include #include +#include #include namespace @@ -38,11 +39,12 @@ namespace kernel::filesystem return *global_open_file_table; } - auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> kstd::ssize_t + auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) + -> std::expected { if (!file_descriptor) { - return -1; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); @@ -56,21 +58,21 @@ namespace kernel::filesystem return m_open_files.size() - 1; } - auto open_file_table::file(size_t fd) const -> kstd::shared_ptr + auto open_file_table::file(size_t fd) const -> std::expected, kstd::error_code> { - if (fd >= m_open_files.size()) + if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return nullptr; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } return m_open_files.at(fd); } - auto open_file_table::remove_file(size_t fd) -> kstd::ssize_t + auto open_file_table::remove_file(size_t fd) -> std::expected { - if (fd >= m_open_files.size()) + if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return -1; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } m_open_files.at(fd) = nullptr; diff --git a/kernel/kernel/filesystem/open_file_table.hpp b/kernel/kernel/filesystem/open_file_table.hpp index a5e6b496..efdaf578 100644 --- a/kernel/kernel/filesystem/open_file_table.hpp +++ b/kernel/kernel/filesystem/open_file_table.hpp @@ -4,10 +4,11 @@ #include #include -#include +#include #include #include +#include namespace kernel::filesystem { @@ -40,21 +41,21 @@ namespace kernel::filesystem @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & fd) -> kstd::ssize_t; + auto add_file(kstd::shared_ptr const & fd) -> std::expected; /** @brief Get a file from the open file table. @param fd The file descriptor index to retrieve. @return A pointer to the requested file descriptor, or a null pointer if not found. */ - [[nodiscard]] auto file(size_t fd) const -> kstd::shared_ptr; + [[nodiscard]] auto file(size_t fd) const -> std::expected, kstd::error_code>; /** @brief Remove a file from the open file table. @param fd The file descriptor index to remove. @return 0 on success, or -1 on failure. */ - auto remove_file(size_t fd) -> kstd::ssize_t; + auto remove_file(size_t fd) -> std::expected; private: open_file_table() = default; diff --git a/kernel/kernel/filesystem/open_file_table.tests.cpp b/kernel/kernel/filesystem/open_file_table.tests.cpp index ffb4a650..78afaa6d 100644 --- a/kernel/kernel/filesystem/open_file_table.tests.cpp +++ b/kernel/kernel/filesystem/open_file_table.tests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") THEN("the file descriptor can be retrieved using the returned file descriptor") { - auto retrieved_descriptor = table.file(fd_1); + auto retrieved_descriptor = table.file(*fd_1); REQUIRE(retrieved_descriptor == file_descriptor_1); } } @@ -46,16 +47,16 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); - THEN("adding a null file descriptor returns an error code") + THEN("adding a null file descriptor returns an error") { auto fd = table.add_file(nullptr); - REQUIRE(fd == -1); + REQUIRE(fd.error() == make_error_condition(kstd::errc::invalid_argument)); } - THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") + THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns an error") { auto retrieved_descriptor = table.file(1000); - REQUIRE(retrieved_descriptor == nullptr); + REQUIRE(retrieved_descriptor.error() == make_error_condition(kstd::errc::invalid_argument)); } } } @@ -72,23 +73,23 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") WHEN("removing the file descriptor using the file descriptor") { - table.remove_file(fd); + CHECK(table.remove_file(*fd)); THEN("the file descriptor can no longer be retrieved using the file descriptor") { - auto retrieved_descriptor = table.file(fd); - REQUIRE(retrieved_descriptor == nullptr); + auto retrieved_descriptor = table.file(*fd); + REQUIRE(retrieved_descriptor.error() == make_error_condition(kstd::errc::invalid_argument)); } } WHEN("removing a file descriptor the other file descriptor keep the same index") { auto fd2 = table.add_file(file_descriptor); - table.remove_file(fd); + CHECK(table.remove_file(*fd)); THEN("the second file descriptor can still be retrieved using its file descriptor") { - auto retrieved_descriptor = table.file(fd2); + auto retrieved_descriptor = table.file(*fd2); REQUIRE(retrieved_descriptor == file_descriptor); } } diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index a5c1593d..55c83ca9 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -65,7 +65,7 @@ namespace kernel::filesystem { if (auto root_dentry = resolve_path("/")) { - do_mount_internal(root_dentry, root_mount, boot_root_fs, boot_device_mount_context); + do_mount_internal(*root_dentry, root_mount, boot_root_fs, boot_device_mount_context); graft_persistent_device_fs(device_fs); } } @@ -95,21 +95,21 @@ namespace kernel::filesystem return dentry; } - auto vfs::close(std::string_view path) -> operation_result + auto vfs::close(std::string_view path) -> std::expected { if (auto mount = find_mount(path)) { - mount->decrement_ref_count(); - return operation_result::success; + mount.value()->decrement_ref_count(); + return {}; } - return operation_result::invalid_path; + return std::unexpected{make_error_code(vfs_errc::invalid_path)}; } - auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result + auto vfs::do_mount(std::string_view source, std::string_view target) -> std::expected { if (!path::is_valid_path(source) || !path::is_valid_path(target)) { - return operation_result::invalid_path; + return std::unexpected{make_error_code(vfs_errc::invalid_path)}; } auto [mount_point_dentry, mount_context] = resolve_path_internal(target).value_or(std::pair{nullptr, nullptr}); @@ -121,33 +121,33 @@ namespace kernel::filesystem if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { do_mount_internal(mount_point_dentry, mount_context, fs, source_mount_context); - return operation_result::success; + return {}; } - return operation_result::invalid_filesystem; + return std::unexpected{make_error_code(vfs_errc::invalid_filesystem)}; } - return operation_result::non_existent_path; + return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; } - return operation_result::mount_point_not_found; + return std::unexpected{make_error_code(vfs_errc::mount_point_not_found)}; } - auto vfs::unmount(std::string_view path) -> operation_result + auto vfs::unmount(std::string_view path) -> std::expected { if (!path::is_valid_path(path)) { - return operation_result::invalid_path; + return std::unexpected{make_error_code(vfs_errc::invalid_path)}; } auto remove_result = m_mount_table.remove_mount(path); if (remove_result == mount_table::operation_result::removed) { - return operation_result::success; + return {}; } else if (remove_result == mount_table::operation_result::mount_not_found) { - return operation_result::mount_point_not_found; + return std::unexpected{make_error_code(vfs_errc::mount_point_not_found)}; } - return operation_result::unmount_failed; + return std::unexpected{make_error_code(vfs_errc::unmount_failed)}; } auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, @@ -284,14 +284,14 @@ namespace kernel::filesystem return std::pair{current_dentry, current_mount}; } - auto vfs::resolve_path(std::string_view path) const -> kstd::shared_ptr + auto vfs::resolve_path(std::string_view path) const -> std::expected, kstd::error_code> { - return resolve_path_internal(path).transform([](auto result) { return result.first; }).value_or(nullptr); + return resolve_path_internal(path).transform([](auto result) { return result.first; }); } - auto vfs::find_mount(std::string_view path) const -> kstd::shared_ptr + auto vfs::find_mount(std::string_view path) const -> std::expected, kstd::error_code> { - return resolve_path_internal(path).transform([](auto result) { return result.second; }).value_or(nullptr); + return resolve_path_internal(path).transform([](auto result) { return result.second; }); } } // namespace kernel::filesystem diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index ef26aa84..3e86eae5 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -19,24 +19,11 @@ namespace kernel::filesystem /** @brief The virtual filesystem (VFS) is responsible for managing mounted filesystems and providing a unified interface for file operations across different filesystem types. The VFS maintains a mount table to keep track of mounted - filesystems and their associated mount points. It provides methods for opening files by path, which involvesresolving + filesystems and their associated mount points. It provides methods for opening files by path, which involves resolving the path to the appropriate mounted filesystem and delegating the file operation to that filesystem's implementation. */ struct vfs { - /** - @brief Results for VFS operations. - */ - enum class operation_result : int - { - success = 0, - invalid_path = -1, - non_existent_path = -2, - mount_point_not_found = -3, - unmount_failed = -4, - invalid_filesystem = -5 - }; - vfs(); /** @@ -67,24 +54,24 @@ namespace kernel::filesystem /** @brief Close a file by its associated @p path. @param path The path to the file to close. - @return The result of the close operation. + @return Nothing on success or an error code on failure. */ - auto close(std::string_view path) -> operation_result; + auto close(std::string_view path) -> std::expected; /** @brief Mount a @p source path to a specific @p target path. @param source The source of the filesystem to mount. @param target The path where the filesystem should be mounted. - @return The result of the mount operation. + @return Nothing on success or an error code on failure. */ - auto do_mount(std::string_view source, std::string_view target) -> operation_result; + auto do_mount(std::string_view source, std::string_view target) -> std::expected; /** @brief Unmount the filesystem mounted at the specified @p path. @param path The path where the filesystem is mounted. - @return The result of the unmount operation. + @return Nothing on success or an error code on failure. */ - auto unmount(std::string_view path) -> operation_result; + auto unmount(std::string_view path) -> std::expected; private: /** @@ -99,8 +86,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto resolve_path_internal(std::string_view path) const -> std::expected, kstd::shared_ptr>, kstd::error_code>; - [[nodiscard]] auto resolve_path(std::string_view path) const -> kstd::shared_ptr; - [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr; + + [[nodiscard]] auto resolve_path(std::string_view path) const + -> std::expected, kstd::error_code>; + + [[nodiscard]] auto find_mount(std::string_view path) const + -> std::expected, kstd::error_code>; auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index c5130ef4..59589633 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -1,9 +1,11 @@ #include +#include #include #include #include +#include #include #include @@ -73,7 +75,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto & vfs = kernel::filesystem::vfs::get(); auto image_1 = vfs.open("/dev/image_1.txt"); - REQUIRE(image_1 == nullptr); + REQUIRE(image_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto dev = vfs.open("/dev/ram0"); REQUIRE(dev != nullptr); @@ -102,16 +104,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("second image can be mounted, data retrieved and unmounted again") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1); - REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information")); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(unmounted_monkey_1 == nullptr); + REQUIRE(unmounted_monkey_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); @@ -119,9 +120,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); @@ -129,34 +129,31 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey_1); REQUIRE(mounted_fish1); - REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(mounted_fish1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path())); + REQUIRE(vfs.close(mounted_fish1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(!vfs.unmount("/information")); + REQUIRE(vfs.unmount("/information/monkey_house/infrastructure")); + REQUIRE(vfs.unmount("/information")); } THEN("image can be mounted, unmount only if no files are open") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + REQUIRE(!vfs.unmount("/information")); - REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information")); } THEN("file with invalid path or not opened file cannot be closed") { - REQUIRE(vfs.close("invalid_path") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(!vfs.close("invalid_path")); REQUIRE_THROWS_AS(vfs.close("/information/info_1.txt"), std::runtime_error); } @@ -165,27 +162,26 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1); - REQUIRE(vfs.close(info_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1.value()->absolute_path())); REQUIRE_THROWS_AS(vfs.close(info_1.value()->absolute_path()), std::runtime_error); } THEN("images can be stacked mounted and correct file system is unmounted again") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/dev/ram32", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.do_mount("/dev/ram32", "/information")); auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets); - REQUIRE(vfs.close(mounted_tickets.value()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_tickets.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information")); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); - REQUIRE(mounted_tickets == nullptr); + REQUIRE(mounted_tickets.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto mounted_monkey = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey != nullptr); + REQUIRE(mounted_monkey); } THEN("image can be mounted on / file opened and unmounted again") @@ -193,17 +189,17 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); + REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water.value()->absolute_path())); - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/")); info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); @@ -214,22 +210,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); + REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water.value()->absolute_path())); auto dev_ram_16 = vfs.open("/dev/ram16"); - REQUIRE(dev_ram_16 == nullptr); + REQUIRE(dev_ram_16.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); - REQUIRE(vfs.do_mount("/dev/ram32", "/") == kernel::filesystem::vfs::operation_result::non_existent_path); + REQUIRE(vfs.do_mount("/dev/ram32", "/").error() == + make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/")); auto dev_ram_32 = vfs.open("/dev/ram32"); REQUIRE(dev_ram_32 != nullptr); @@ -240,58 +237,59 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/dev")); + REQUIRE(vfs.unmount("/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); + REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); - REQUIRE(vfs.do_mount("/dev/ram0", "/") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram0", "/")); info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); auto dev_ram_0 = vfs.open("/dev/ram0"); - REQUIRE(dev_ram_0 == nullptr); + REQUIRE(dev_ram_0.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); } THEN("mount with null file system fails") { - REQUIRE(vfs.do_mount("/closed.txt", "/information") == - kernel::filesystem::vfs::operation_result::invalid_filesystem); + REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == + make_error_code(kernel::filesystem::vfs_errc::invalid_filesystem)); } THEN("mount with invalid path fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "information") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); + REQUIRE(vfs.do_mount("/dev/ram16", "").error() == make_error_code(kernel::filesystem::vfs_errc::invalid_path)); + REQUIRE(vfs.do_mount("/dev/ram16", "information").error() == + make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); } THEN("mount with non-existent source path fails") { - REQUIRE(vfs.do_mount("/dev/nonexistent", "/information") == - kernel::filesystem::vfs::operation_result::non_existent_path); + REQUIRE(vfs.do_mount("/dev/nonexistent", "/information").error() == + make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); } THEN("mount with non-existent mount point fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); + REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent").error() == + make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); } THEN("unmount with invalid path fails") { - REQUIRE(vfs.unmount("") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::mount_point_not_found); + REQUIRE(vfs.unmount("").error() == make_error_code(kernel::filesystem::vfs_errc::invalid_path)); + REQUIRE(vfs.unmount("information").error() == + make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); } THEN("unmounting non-existent mount point returns expected error code") { - REQUIRE(vfs.unmount("/information/nonexistent") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); + REQUIRE(vfs.unmount("/information/nonexistent").error() == + make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); } THEN("a file can be access if . in the path") @@ -311,7 +309,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("a file can be accessed over multiple mounts if path contains .. or . ") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); auto img = vfs.open("/information/monkey_house/caretaker/../../../../../../archiv/2024.img"); REQUIRE(img != nullptr); @@ -325,9 +323,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("a file can be accessed over multiple mounts (device and file) if path contains .. ") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure")); auto pig_1 = vfs.open("/information/monkey_house/infrastructure/stable/pig_1.txt"); REQUIRE(pig_1 != nullptr); @@ -349,10 +346,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("the file-filesystem in the image can be mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); + REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); @@ -363,20 +360,19 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); - REQUIRE(vfs.close(dentry.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(dentry.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information")); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1 == nullptr); + REQUIRE(unmounted_sheep_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); } THEN("the file-filesystem in the image can be mounted and in this filesystem can another file-filesystem be " "mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable") == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); + REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable")); auto sheep_1 = vfs.open("/information/sheep_1.txt"); auto goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); @@ -396,24 +392,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); - REQUIRE(vfs.close(sheep_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(goat_1.value()->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(sheep_1.value()->absolute_path())); + REQUIRE(vfs.close(goat_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + REQUIRE(vfs.unmount("/information").error() == make_error_code(kernel::filesystem::vfs_errc::unmount_failed)); - REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information/stable")); auto unmounted_goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); - REQUIRE(unmounted_goat_1 == nullptr); + REQUIRE(unmounted_goat_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); - REQUIRE(vfs.close(still_mounted_sheep_1.value()->absolute_path()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(still_mounted_sheep_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information")); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1 == nullptr); + REQUIRE(unmounted_sheep_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); } } @@ -428,23 +423,21 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("cannot unmount a filesystem if files are mounted") { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures") == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/entrance")); + REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures")); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.unmount("/enclosures") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/entrance").error() == make_error_code(kernel::filesystem::vfs_errc::unmount_failed)); + REQUIRE(vfs.unmount("/enclosures")); + REQUIRE(vfs.unmount("/entrance")); } THEN("can mount filesystem onto the directory that contains it") { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance") == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/entrance")); + REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance")); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/entrance")); + REQUIRE(vfs.unmount("/entrance")); } } @@ -492,21 +485,21 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS { auto & vfs = kernel::filesystem::vfs::get(); auto invalid_symlink = vfs.open("/symlinks/invalid_absolute"); - REQUIRE(invalid_symlink == nullptr); + REQUIRE(invalid_symlink.error() == make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); } THEN("symbolic link containing an invalid relative path is handled correctly") { auto & vfs = kernel::filesystem::vfs::get(); auto invalid_symlink = vfs.open("/symlinks/invalid_relative"); - REQUIRE(invalid_symlink == nullptr); + REQUIRE(invalid_symlink.error() == make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); } THEN("circular symbolic links are detected and handled correctly") { auto & vfs = kernel::filesystem::vfs::get(); auto circular_symlink = vfs.open("/symlinks/symloop_a"); - REQUIRE(circular_symlink == nullptr); + REQUIRE(circular_symlink.error() == make_error_code(kstd::errc::too_many_symbolic_link_levels)); } } @@ -516,7 +509,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); auto & vfs = kernel::filesystem::vfs::get(); - vfs.do_mount("/archiv/2024.img", "/information"); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); THEN("file can be opened through symbolic link pointing to the parent filesystem") { @@ -533,7 +526,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); auto & vfs = kernel::filesystem::vfs::get(); - vfs.do_mount("/dev/ram16", "/information"); + REQUIRE(vfs.do_mount("/dev/ram16", "/information")); THEN("file can be opened through symbolic link pointing to the parent filesystem and back into the mounted " "filesystem again") diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index 76c05204..749040ce 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -39,22 +40,22 @@ auto run_demo() -> void // 2) read from the file kstd::vector buffer_1{10}; - auto bytes_read = kapi::filesystem::read(fd_1.value(), buffer_1.data(), buffer_1.size()); - auto buffer_as_str = std::string_view{reinterpret_cast(buffer_1.data()), static_cast(bytes_read)}; + auto bytes_read = *kapi::filesystem::read(fd_1.value(), buffer_1.data(), buffer_1.size()); + auto buffer_as_str = std::string_view{reinterpret_cast(buffer_1.data()), bytes_read}; kstd::println("--> read {} bytes from /entrance/tickets.txt: {}", bytes_read, buffer_as_str); kstd::println(""); // 3) show that /entrance/information/info_1.txt is not accessible before mounting kstd::println("attempting to open /entrance/information/info_1.txt before mounting"); auto fd_before_mount = kapi::filesystem::open("/entrance/information/info_1.txt"); - if (fd_before_mount == -1) + if (!fd_before_mount && fd_before_mount.error() == make_error_condition(kstd::errc::no_such_file_or_directory)) { kstd::println("--> as expected the file could not be opened before mounting"); } // 4) mount a new filesystem on top of /entrance kstd::println("mount /dev/ram16 to /entrance"); - if (kapi::filesystem::mount("/dev/ram16", "/entrance") == 0) + if (kapi::filesystem::mount("/dev/ram16", "/entrance")) { kstd::println("--> successfully mounted /dev/ram16 to /entrance"); } @@ -78,7 +79,7 @@ auto run_demo() -> void // 6) read from the new file kstd::vector buffer_2{10}; - bytes_read = kapi::filesystem::read(fd_2.value(), buffer_2.data(), buffer_2.size()); + bytes_read = *kapi::filesystem::read(fd_2.value(), buffer_2.data(), buffer_2.size()); buffer_as_str = std::string_view{reinterpret_cast(buffer_2.data()), static_cast(bytes_read)}; kstd::println("--> read {} bytes from /entrance/information/info_1.txt: {} ", bytes_read, buffer_as_str); @@ -96,13 +97,13 @@ auto run_demo() -> void // 8) read from the device file kstd::vector buffer_3{2}; - bytes_read = kapi::filesystem::read(fd_3.value(), buffer_3.data(), buffer_3.size()); + bytes_read = *kapi::filesystem::read(fd_3.value(), buffer_3.data(), buffer_3.size()); kstd::println("--> read {} bytes from /dev/ram32: {::#04x} ", bytes_read, buffer_3); // 9) write to the device file auto const default_buffer_value = std::byte{0xAA}; kstd::vector write_buffer{default_buffer_value, default_buffer_value}; - auto bytes_written = kapi::filesystem::write(fd_3.value(), write_buffer.data(), write_buffer.size()); + auto bytes_written = *kapi::filesystem::write(fd_3.value(), write_buffer.data(), write_buffer.size()); kstd::println("--> written {} bytes to /dev/ram32: {::#04x}", bytes_written, write_buffer); // 10) do memory dump to show that the write to the device file had an effect -- cgit v1.2.3 From 9f6b1af8b27d73db12025645aa54f78e352cd77c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 12 Jul 2026 22:03:45 +0200 Subject: ide: clean up header regex --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index f554b1cc..e110ab1f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -87,7 +87,7 @@ CheckOptions: readability-magic-numbers.IgnoreTypeAliases: true FormatStyle: file -HeaderFilterRegex: "(.*/kstd/kstd/.*)|(arch|kernel|kapi)/.*\\.hpp" +HeaderFilterRegex: "(arch|kernel|kapi|kstd)/.*\\.hpp" SystemHeaders: true RemovedArgs: - -fcondition-coverage -- cgit v1.2.3 From 48b95e0b46e32f35ea6f16f41ab99286856292bf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 12:00:39 +0200 Subject: kapi: extend panic formatting --- kapi/kapi/system.hpp | 44 ++++++++++++ kernel/kapi/system.cpp | 9 +++ libs/kstd/kstd/bits/format/formatter/cstring.hpp | 6 ++ libs/kstd/kstd/bits/format/vformat.cpp | 20 ++++++ libs/kstd/kstd/bits/format/vformat.hpp | 44 ++++++++++-- libs/kstd/kstd/format.tests.cpp | 90 ++++++++++++++++++++++++ 6 files changed, 209 insertions(+), 4 deletions(-) diff --git a/kapi/kapi/system.hpp b/kapi/kapi/system.hpp index 8a20af94..f7dd4d7c 100644 --- a/kapi/kapi/system.hpp +++ b/kapi/kapi/system.hpp @@ -1,8 +1,15 @@ #ifndef TEACHOS_KAPI_SYSTEM_HPP #define TEACHOS_KAPI_SYSTEM_HPP +#include +#include + +#include +#include #include +#include #include +#include namespace kapi::system { @@ -10,6 +17,26 @@ namespace kapi::system //! @addtogroup kapi-system-kernel-defined //! @{ + template + struct panic_format_string + { + kstd::format_string format; + std::source_location location; + + template + consteval panic_format_string(char const (&format)[Size], // NOLINT + std::source_location location = std::source_location::current()) + : format{format} + , location{location} + {} + + consteval panic_format_string(std::string_view const & format, + std::source_location location = std::source_location::current()) + : format{format} + , location{location} + {} + }; + //! Terminate kernel execution with the given error message. //! //! This function terminates the execution of the kernel and attempts to issue the given error message to the user. @@ -17,6 +44,14 @@ namespace kapi::system //! @param message The message associated with the panic [[noreturn]] auto panic(std::string_view message, std::source_location = std::source_location::current()) -> void; + //! Termite kernel execution with the given error message and error code. + //! + //! This function terminates the execution of the kernel and attempts to issue the given error message to the user. + //! @param message The message associated with the panic. + //! @param error The erro code associated with the panic. + [[noreturn]] auto panic(std::string_view message, kstd::error_code error, + std::source_location = std::source_location::current()) -> void; + //! @} // end group kernel-defined //! @addtogroup kapi-system-platform-defined @@ -26,6 +61,15 @@ namespace kapi::system auto memory_initialized() -> void; //! @} // end group platform-defined + // + template + [[noreturn]] auto panic(panic_format_string...> format, + FormatArguments &&... arguments) + { + auto buffer = std::array{}; + auto [_, length] = kstd::format_to(std::span{buffer}, format.format, std::forward(arguments)...); + panic(std::string_view{buffer.data(), length}, format.location); + } } // namespace kapi::system diff --git a/kernel/kapi/system.cpp b/kernel/kapi/system.cpp index 86c99515..23dadd3a 100644 --- a/kernel/kapi/system.cpp +++ b/kernel/kapi/system.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -18,4 +19,12 @@ namespace kapi::system cpu::halt(); } + [[gnu::weak]] + auto panic(std::string_view message, kstd::error_code error, std::source_location location) -> void + { + kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} ({}:{}) @ {}:{}", location.function_name(), message, + error.category().name(), error.message(), location.file_name(), location.line()); + cpu::halt(); + } + } // namespace kapi::system diff --git a/libs/kstd/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/kstd/bits/format/formatter/cstring.hpp index 553c8caf..12cb9c6f 100644 --- a/libs/kstd/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/kstd/bits/format/formatter/cstring.hpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace kstd @@ -24,6 +25,11 @@ namespace kstd { }; + template + struct formatter : formatter // NOLINT + { + }; + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/vformat.cpp b/libs/kstd/kstd/bits/format/vformat.cpp index 495f0a10..7210a0b6 100644 --- a/libs/kstd/kstd/bits/format/vformat.cpp +++ b/libs/kstd/kstd/bits/format/vformat.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -221,4 +222,23 @@ namespace kstd::bits::format return m_size; } + auto span_writer::push(std::string_view text) -> void + { + if (m_position < m_span.size() - 1) + { + auto space_left = m_span.size() - m_position; + auto to_copy = std::min(space_left, text.size()); + std::ranges::copy_n(text.begin(), to_copy, m_span.subspan(m_position).begin()); + m_position += to_copy; + } + } + + auto span_writer::push(char character) -> void + { + if (m_position < m_span.size() - 1) + { + m_span[m_position++] = character; + } + } + } // namespace kstd::bits::format \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/vformat.hpp b/libs/kstd/kstd/bits/format/vformat.hpp index 829e0ea4..63b41ab6 100644 --- a/libs/kstd/kstd/bits/format/vformat.hpp +++ b/libs/kstd/kstd/bits/format/vformat.hpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include namespace kstd { @@ -28,7 +30,7 @@ namespace kstd //! @param args The arguments for the format string. auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void; - struct string_writer : output_buffer + struct string_writer final : output_buffer { auto push(std::string_view text) -> void final; auto push(char character) -> void final; @@ -40,7 +42,7 @@ namespace kstd }; //! A buffer that merely records the size of the output written to it. - struct size_recorder : output_buffer + struct size_recorder final : output_buffer { auto push(std::string_view text) -> void final; auto push(char character) -> void final; @@ -52,9 +54,9 @@ namespace kstd }; template Output> - struct iterator_writer : output_buffer + struct iterator_writer final : output_buffer { - explicit iterator_writer(Output iterator) + constexpr explicit iterator_writer(Output iterator) : m_output{iterator} {} @@ -77,6 +79,25 @@ namespace kstd Output m_output{}; }; + struct span_writer final : output_buffer + { + constexpr explicit span_writer(std::span span) + : m_span{span} + {} + + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + [[nodiscard]] constexpr auto position() const noexcept -> std::size_t + { + return m_position; + } + + private: + std::size_t m_position{}; + std::span m_span{}; + }; + } // namespace bits::format //! Format a given string with the provided arguments. @@ -108,6 +129,21 @@ namespace kstd return buffer.iterator(); } + //! Format a given string with the provided arguments. + //! + //! @param span The span to write to. + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return A pair of the span written to and the next position to write to. + template + auto format_to(std::span span, format_string...> format, + ArgumentTypes &&... args) -> std::pair, std::size_t> + { + auto buffer = bits::format::span_writer{span}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); + return {span, buffer.position()}; + } + //! Determine the number of characters required to format the format string with the given arguments. //! //! @param format The format string. diff --git a/libs/kstd/kstd/format.tests.cpp b/libs/kstd/kstd/format.tests.cpp index d38a8107..a34cf063 100644 --- a/libs/kstd/kstd/format.tests.cpp +++ b/libs/kstd/kstd/format.tests.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -116,6 +117,95 @@ SCENARIO("Formatting to an output iterator", "[format]") } } +SCENARIO("Formatting to a span", "[format]") +{ + GIVEN("a format string without any placeholders") + { + constexpr auto fmt = "This is a test"sv; + + WHEN("calling format without any arguments.") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt); + + THEN("the unmodified string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, 2, 3); + + THEN("the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "This is a test"); + } + } + + WHEN("the span is too small") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, 2, 3); + + THEN("the written length is equal to the span size") + { + REQUIRE(length == span.size()); + } + + THEN("only part of the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "This"); + } + } + } + + GIVEN("a format string with placeholders") + { + constexpr auto fmt = "Here are some placeholders: {} {} {}"sv; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, true, -100); + + THEN("the formatted string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt, 2, false, -200, 4, 5, 6); + + THEN("the formatted string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "Here are some placeholders: 2 false -200"); + } + } + + WHEN("the span is too small") + { + auto buffer = std::array{}; + auto [span, length] = kstd::format_to(buffer, fmt, 2, false, -200, 4, 5, 6); + + THEN("the written length is equal to the span size") + { + REQUIRE(length == span.size()); + } + + THEN("only part of the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "Here"); + } + } + } +} + SCENARIO("Determining formatted size") { GIVEN("a format string without any placeholders") -- cgit v1.2.3 From 34ffa4de405d053ef4b53d58e1437f2e82d152b1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 12:18:08 +0200 Subject: kstd: make error_code formattable --- kernel/kapi/system.cpp | 4 ++-- libs/kstd/kstd/format.hpp | 1 + libs/kstd/kstd/system_error.hpp | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/kernel/kapi/system.cpp b/kernel/kapi/system.cpp index 23dadd3a..629e5990 100644 --- a/kernel/kapi/system.cpp +++ b/kernel/kapi/system.cpp @@ -22,8 +22,8 @@ namespace kapi::system [[gnu::weak]] auto panic(std::string_view message, kstd::error_code error, std::source_location location) -> void { - kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} ({}:{}) @ {}:{}", location.function_name(), message, - error.category().name(), error.message(), location.file_name(), location.line()); + kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} ({}) @ {}:{}", location.function_name(), message, error, + location.file_name(), location.line()); cpu::halt(); } diff --git a/libs/kstd/kstd/format.hpp b/libs/kstd/kstd/format.hpp index 5ed798db..5d7f4ae6 100644 --- a/libs/kstd/kstd/format.hpp +++ b/libs/kstd/kstd/format.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index f971703b..08725609 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -1,6 +1,8 @@ #ifndef KSTD_SYSTEM_ERROR_HPP #define KSTD_SYSTEM_ERROR_HPP +#include + #include #include #include @@ -472,6 +474,29 @@ namespace kstd return {static_cast(value), generic_category()}; } + template<> + struct formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + if (it != context.end() && *it != '}') + { + bits::format::error("Invalid specifier for string_view."); + } + return it; + } + + auto format(error_code const & error, format_context & context) const -> void + { + context.push(error.category().name()); + context.push(':'); + formatter{}.format(error.value(), context); + context.push(':'); + context.push(error.message()); + } + }; + } // namespace kstd #endif -- cgit v1.2.3 From 74e471800380573af6d63b2ad1ba95cfecbce246 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 13:50:11 +0200 Subject: kernel: initial fs driver system_error integration --- kernel/kernel/filesystem/devfs/filesystem.cpp | 20 +++-- kernel/kernel/filesystem/devfs/filesystem.hpp | 7 +- .../kernel/filesystem/devfs/filesystem.tests.cpp | 36 ++++---- kernel/kernel/filesystem/ext2/error.hpp | 96 ++++++++++++++++++++++ kernel/kernel/filesystem/ext2/filesystem.cpp | 50 ++++++----- kernel/kernel/filesystem/ext2/filesystem.hpp | 17 ++-- kernel/kernel/filesystem/ext2/filesystem.tests.cpp | 34 ++++---- kernel/kernel/filesystem/ext2/inode.cpp | 4 +- kernel/kernel/filesystem/ext2/inode.tests.cpp | 26 +++--- kernel/kernel/filesystem/filesystem.cpp | 13 +-- kernel/kernel/filesystem/filesystem.hpp | 24 ++---- kernel/kernel/filesystem/rootfs/filesystem.cpp | 10 ++- kernel/kernel/filesystem/rootfs/filesystem.hpp | 7 +- .../kernel/filesystem/rootfs/filesystem.tests.cpp | 14 ++-- kernel/kernel/filesystem/vfs.cpp | 16 ++-- .../kernel/test_support/filesystem/filesystem.cpp | 4 +- .../kernel/test_support/filesystem/filesystem.hpp | 4 +- 17 files changed, 258 insertions(+), 124 deletions(-) create mode 100644 kernel/kernel/filesystem/ext2/error.hpp diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index 3b1da25c..226102ba 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -4,14 +4,17 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include namespace kernel::filesystem::devfs @@ -37,25 +40,25 @@ namespace kernel::filesystem::devfs [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const &) -> operation_result + auto filesystem::mount(kstd::shared_ptr const &) -> std::expected { m_root_inode = kstd::make_shared(); build_device_inode_table(); - return operation_result::success; + return {}; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr + -> std::expected, kstd::error_code> { if (!parent || !parent->is_directory()) { - return nullptr; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } if (parent.get() != m_root_inode.get()) { - return nullptr; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { @@ -65,7 +68,12 @@ namespace kernel::filesystem::devfs } return false; }); - return (it != m_inodes.end()) ? *it : nullptr; + if (it != m_inodes.end()) + { + return *it; + } + + return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; } auto filesystem::build_device_inode_table() -> void diff --git a/kernel/kernel/filesystem/devfs/filesystem.hpp b/kernel/kernel/filesystem/devfs/filesystem.hpp index 083d248c..e96790b8 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/kernel/filesystem/devfs/filesystem.hpp @@ -6,8 +6,10 @@ #include #include +#include #include +#include #include namespace kernel::filesystem::devfs @@ -25,7 +27,8 @@ namespace kernel::filesystem::devfs @param backing_inode Backing inode passed by the vfs (not required by devfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) + -> std::expected override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -34,7 +37,7 @@ namespace kernel::filesystem::devfs @return A pointer to the found inode, or a null pointer if not found. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr override; + -> std::expected, kstd::error_code> override; private: auto build_device_inode_table() -> void; diff --git a/kernel/kernel/filesystem/devfs/filesystem.tests.cpp b/kernel/kernel/filesystem/devfs/filesystem.tests.cpp index 36cb4117..d5028231 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.tests.cpp @@ -17,40 +17,40 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, THEN("mount succeeds") { - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); - REQUIRE(fs.root_inode() != nullptr); + REQUIRE(result); + REQUIRE(fs.root_inode()); } THEN("lookup on root finds ram0 device inode") { auto inode = fs.lookup(fs.root_inode(), "ram0"); - REQUIRE(inode != nullptr); - REQUIRE(inode->is_device()); + REQUIRE(inode); + REQUIRE(inode.value()->is_device()); } - THEN("lookup of an unknown device returns null") + THEN("lookup of an unknown device returns error") { auto inode = fs.lookup(fs.root_inode(), "ram99"); - REQUIRE(inode == nullptr); + REQUIRE(!inode); } - THEN("lookup with wrong parent returns null") + THEN("lookup with wrong parent returns error") { auto other_fs = kernel::filesystem::devfs::filesystem{}; - other_fs.mount(nullptr); + CHECK(other_fs.mount(nullptr)); auto inode = fs.lookup(other_fs.root_inode(), "ram0"); - REQUIRE(inode == nullptr); + REQUIRE(!inode); } - THEN("lookup with a non-directory parent returns null") + THEN("lookup with a non-directory parent returns error") { auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); - REQUIRE(non_directory_inode != nullptr); - REQUIRE_FALSE(non_directory_inode->is_directory()); + REQUIRE(non_directory_inode); + REQUIRE_FALSE(non_directory_inode.value()->is_directory()); - auto result = fs.lookup(non_directory_inode, "anything"); - REQUIRE(result == nullptr); + auto result = fs.lookup(*non_directory_inode, "anything"); + REQUIRE(!result); } } @@ -60,13 +60,13 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, auto fs = kernel::filesystem::devfs::filesystem{}; auto result = fs.mount(nullptr); - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(result); THEN("lookup finds all generated RAM devices") { - REQUIRE(fs.lookup(fs.root_inode(), "ram0") != nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "ram16") != nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "ram32") != nullptr); + REQUIRE(fs.lookup(fs.root_inode(), "ram0")); + REQUIRE(fs.lookup(fs.root_inode(), "ram16")); + REQUIRE(fs.lookup(fs.root_inode(), "ram32")); } } } diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp new file mode 100644 index 00000000..e42b714b --- /dev/null +++ b/kernel/kernel/filesystem/ext2/error.hpp @@ -0,0 +1,96 @@ +#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_ERROR_HPP +#define TEACHOS_KERNEL_FILESYSTEM_EXT2_ERROR_HPP + +#include + +#include + +#include +#include + +namespace kernel::filesystem::ext2 +{ + + enum struct ext2_errc : int + { + invalid_magic_number = 1, + invalid_root_inode, + invalid_block_group_index, + invalid_block_index, + invalid_block_number, + }; + + namespace detail + { + struct ext2_category_t final : kstd::error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "ext2"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast(value)) + { + case ext2_errc::invalid_magic_number: + return "invalid filesystem magic"; + case ext2_errc::invalid_root_inode: + return "invalid root inode"; + case ext2_errc::invalid_block_group_index: + return "block group index out of bounds"; + case ext2_errc::invalid_block_index: + return "block index out of bounds"; + case ext2_errc::invalid_block_number: + return "block index out of bounds"; + default: + return "unknown ext2 error"; + }; + } + + [[nodiscard]] constexpr auto equivalent(int code, kstd::error_condition const & condition) const noexcept + -> bool override + { + switch (static_cast(code)) + { + case ext2_errc::invalid_magic_number: + case ext2_errc::invalid_root_inode: + case ext2_errc::invalid_block_group_index: + case ext2_errc::invalid_block_index: + case ext2_errc::invalid_block_number: + if (condition.category() == kernel::filesystem::vfs_category()) + { + return condition.value() == static_cast(kernel::filesystem::vfs_errc::invalid_filesystem); + } + else if (condition.category() == kstd::generic_category()) + { + return condition.value() == static_cast(kstd::errc::io_error); + } + } + + return kstd::error_category::equivalent(code, condition); + } + } constexpr inline ext2_category_instance{}; + } // namespace detail + + [[nodiscard]] constexpr auto inline ext2_category() noexcept -> kstd::error_category const & + { + return detail::ext2_category_instance; + } + + [[nodiscard]] constexpr auto inline make_error_code(ext2_errc error) noexcept -> kstd::error_code + { + return {static_cast(error), ext2_category()}; + } + +} // namespace kernel::filesystem::ext2 + +namespace kstd +{ + template<> + struct is_error_code_enum : std::true_type + { + }; +} // namespace kstd + +#endif diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 82c6441d..fd77a0a2 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -1,6 +1,8 @@ #include +#include #include +#include #include #include #include @@ -9,13 +11,15 @@ #include #include -#include +#include #include #include #include #include +#include #include +#include namespace kernel::filesystem::ext2 { @@ -41,15 +45,16 @@ namespace kernel::filesystem::ext2 [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result + auto filesystem::mount(kstd::shared_ptr const & backing_inode) + -> std::expected { - kernel::filesystem::filesystem::mount(backing_inode); + std::ignore = kernel::filesystem::filesystem::mount(backing_inode); m_backing_inode->read(&m_superblock, constants::superblock_offset, sizeof(m_superblock)); if (m_superblock.magic != constants::magic_number) { - return operation_result::invalid_magic_number; + return std::unexpected{make_error_code(ext2_errc::invalid_magic_number)}; } auto const blocks_per_group = m_superblock.blocks_per_group; @@ -60,36 +65,41 @@ namespace kernel::filesystem::ext2 m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset(), num_block_groups * sizeof(block_group_descriptor)); - m_root_inode = read_inode(constants::root_inode_number); + if (auto root_inode = read_inode(constants::root_inode_number)) + { + m_root_inode = root_inode.value(); + } if (!m_root_inode || !m_root_inode->is_directory()) { - return operation_result::invalid_root_inode; + return std::unexpected{make_error_code(ext2_errc::invalid_root_inode)}; } - return operation_result::success; + + return {}; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr + -> std::expected, kstd::error_code> { if (!parent || !parent->is_directory()) { - return nullptr; + return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; } auto * ext2_parent = static_cast(parent.get()); - if (!ext2_parent) - { - return nullptr; - } - auto const & inode_data = ext2_parent->data(); kstd::vector buffer(block_size()); for (uint32_t i = 0; i < inode_block_count(inode_data); ++i) { auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); - auto const block_offset = global_block_number * block_size(); + + if (!global_block_number) + { + return std::unexpected{make_error_code(ext2_errc::invalid_block_number)}; + } + + auto const block_offset = global_block_number.value() * block_size(); m_backing_inode->read(buffer.data(), block_offset, block_size()); auto const * entry = reinterpret_cast(buffer.data()); @@ -108,10 +118,10 @@ namespace kernel::filesystem::ext2 } } - return nullptr; + return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; } - auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr + auto filesystem::read_inode(uint32_t inode_number) const -> std::expected, kstd::error_code> { auto const inodes_per_group = m_superblock.inodes_per_group; auto const block_group_index = (inode_number - 1) / inodes_per_group; @@ -119,7 +129,7 @@ namespace kernel::filesystem::ext2 if (block_group_index >= m_block_group_descriptors.size()) { - return nullptr; + return std::unexpected{make_error_code(ext2_errc::invalid_block_group_index)}; } auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); @@ -143,7 +153,7 @@ namespace kernel::filesystem::ext2 } auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> kstd::ssize_t + -> std::expected { if (inode_block_index < constants::direct_block_count) { @@ -186,7 +196,7 @@ namespace kernel::filesystem::ext2 return block_number; } - return -1; + return std::unexpected{make_error_code(ext2_errc::invalid_block_index)}; } auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index fc2ba7df..da7ae64e 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -8,12 +8,13 @@ #include #include -#include +#include #include #include #include #include +#include #include namespace kernel::filesystem::ext2 @@ -47,14 +48,15 @@ namespace kernel::filesystem::ext2 @brief A filesystem implementation for the ext2 filesystem format. This class provides methods for mounting an ext2 filesystem, and looking up inodes. */ - struct filesystem : kernel::filesystem::filesystem + struct filesystem final : kernel::filesystem::filesystem { /** @brief Initializes the ext2 filesystem with the given @p backing_inode. @param backing_inode The backing inode to mount. @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) + -> std::expected override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -63,7 +65,7 @@ namespace kernel::filesystem::ext2 @return A pointer to the found inode, or a null pointer if not found. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr override; + -> std::expected, kstd::error_code> override; /** @brief Gets the size of a block in the filesystem. @@ -81,10 +83,10 @@ namespace kernel::filesystem::ext2 @brief Maps an inode block index to a global block number. @param inode_block_index The index of the block within the inode. @param data The inode data. - @return The global block number. + @return The global block number on success, an error otherwise. */ [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> kstd::ssize_t; + -> std::expected; private: struct indirect_level @@ -95,7 +97,8 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto indirect_levels() const -> std::array; - [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; + [[nodiscard]] auto read_inode(uint32_t inode_number) const + -> std::expected, kstd::error_code>; [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t; [[nodiscard]] auto inode_size() const -> uint16_t; diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp index ed3ac1ac..9a677981 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, auto dev_inode = kstd::make_shared(boot_device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); THEN("the root inode is available and is a directory") { @@ -44,25 +45,25 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, THEN("lookup resolves known entries from the image") { auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - REQUIRE(information->is_directory()); + REQUIRE(information); + REQUIRE(information.value()->is_directory()); - auto info_1 = fs.lookup(information, "info_1.txt"); - REQUIRE(info_1 != nullptr); - REQUIRE(info_1->is_regular()); + auto info_1 = fs.lookup(*information, "info_1.txt"); + REQUIRE(info_1); + REQUIRE(info_1.value()->is_regular()); } THEN("lookup returns null for invalid inputs") { - REQUIRE(fs.lookup(nullptr, "information") == nullptr); + REQUIRE(!fs.lookup(nullptr, "information")); auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - auto info_1 = fs.lookup(information, "info_1.txt"); - REQUIRE(info_1 != nullptr); + REQUIRE(information); + auto info_1 = fs.lookup(*information, "info_1.txt"); + REQUIRE(info_1); - REQUIRE(fs.lookup(info_1, "anything") == nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "does_not_exist") == nullptr); + REQUIRE(!fs.lookup(*info_1, "anything")); + REQUIRE(!fs.lookup(fs.root_inode(), "does_not_exist")); } } } @@ -81,7 +82,8 @@ SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem THEN("mount fails with invalid_magic_number") { - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); + REQUIRE(fs.mount(dev_inode).error() == + make_error_code(kernel::filesystem::ext2::ext2_errc::invalid_magic_number)); } } } @@ -100,7 +102,7 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto inode_data = kernel::filesystem::ext2::inode_data{}; inode_data.block[0] = 7; @@ -130,10 +132,10 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53); } - THEN("mapping returns zero for out-of-range indexes") + THEN("mapping returns error for out-of-range indexes") { auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; - REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == -1); + REQUIRE(!fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data)); } } } diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 11f55cb6..23344a7b 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -43,7 +43,7 @@ namespace kernel::filesystem::ext2 while (bytes_read < requested_size) { auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); - if (block_number == -1) + if (!block_number) { break; } @@ -56,7 +56,7 @@ namespace kernel::filesystem::ext2 } else { - auto const block_start_offset = block_number * m_filesystem->block_size(); + auto const block_start_offset = block_number.value() * m_filesystem->block_size(); auto const read_offset = block_start_offset + in_block_offset; bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp index aa21f6ed..325ba956 100644 --- a/kernel/kernel/filesystem/ext2/inode.tests.cpp +++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp @@ -97,18 +97,18 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in auto dev_inode = kstd::make_shared(boot_device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - auto file = fs.lookup(information, "info_1.txt"); - REQUIRE(file != nullptr); - REQUIRE(file->is_regular()); + REQUIRE(information); + auto file = fs.lookup(*information, "info_1.txt"); + REQUIRE(file); + REQUIRE(file.value()->is_regular()); THEN("reading from offset zero returns expected file prefix") { auto buffer = kstd::vector(6); - auto const bytes_read = file->read(buffer.data(), 0, buffer.size()); + auto const bytes_read = file.value()->read(buffer.data(), 0, buffer.size()); REQUIRE(bytes_read == 6); @@ -119,7 +119,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in THEN("reading with an offset returns the expected byte") { auto buffer = kstd::vector(1); - auto const bytes_read = file->read(buffer.data(), 5, buffer.size()); + auto const bytes_read = file.value()->read(buffer.data(), 5, buffer.size()); REQUIRE(bytes_read == 1); REQUIRE(static_cast(buffer[0]) == '1'); @@ -139,7 +139,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto data = kernel::filesystem::ext2::inode_data{}; data.block[0] = 30; @@ -176,7 +176,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto data = kernel::filesystem::ext2::inode_data{}; data.block[0] = 30; @@ -221,7 +221,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto data = kernel::filesystem::ext2::inode_data{}; data.block[12] = 0; @@ -252,7 +252,7 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto inode_data = kernel::filesystem::ext2::inode_data{}; inode_data.size = block_size * 2; @@ -313,7 +313,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto data = kernel::filesystem::ext2::inode_data{}; data.size = 256; @@ -349,7 +349,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto dev_inode = kstd::make_shared(device); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto data = kernel::filesystem::ext2::inode_data{}; data.size = 256; diff --git a/kernel/kernel/filesystem/filesystem.cpp b/kernel/kernel/filesystem/filesystem.cpp index f0b7fd28..cb60734f 100644 --- a/kernel/kernel/filesystem/filesystem.cpp +++ b/kernel/kernel/filesystem/filesystem.cpp @@ -6,8 +6,10 @@ #include #include +#include #include +#include namespace kernel::filesystem { @@ -18,7 +20,8 @@ namespace kernel::filesystem }; } // namespace - auto filesystem::probe_and_mount(kstd::shared_ptr const & backing_inode) -> kstd::shared_ptr + auto filesystem::probe_and_mount(kstd::shared_ptr const & backing_inode) + -> std::expected, kstd::error_code> { if (!backing_inode) { @@ -28,16 +31,16 @@ namespace kernel::filesystem for (auto & factory : filesystem_factories) { auto fs = factory(); - if (fs->mount(backing_inode) == operation_result::success) + if (fs->mount(backing_inode)) { return fs; } } - return nullptr; + return std::unexpected{make_error_code(kstd::errc::not_supported)}; } - auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> std::expected { if (!backing_inode) { @@ -45,7 +48,7 @@ namespace kernel::filesystem } m_backing_inode = backing_inode; - return operation_result::success; + return {}; } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/kernel/filesystem/filesystem.hpp b/kernel/kernel/filesystem/filesystem.hpp index b818d992..f9521d1f 100644 --- a/kernel/kernel/filesystem/filesystem.hpp +++ b/kernel/kernel/filesystem/filesystem.hpp @@ -4,8 +4,10 @@ #include #include +#include #include +#include #include namespace kernel::filesystem @@ -16,14 +18,6 @@ namespace kernel::filesystem */ struct filesystem { - enum class operation_result : int - { - success = 0, - invalid_magic_number = -1, - invalid_root_inode = -2, - unmount_failed = -3 - }; - /** @brief Virtual destructor for the filesystem. */ @@ -34,19 +28,19 @@ namespace kernel::filesystem and returns a pointer to the mounted filesystem instance. This method iterates through known filesystem types and attempts to initialize it with the backing inode until the mount was successful or all types have been tried. @param backing_inode The inode to probe and mount. - @return A pointer to the mounted filesystem instance if successful, or a null pointer if no recognizable filesystem - is found on the backing inode. + @return A pointer to the mounted filesystem instance if successful, an error otherwise. @warning Panics if @p backing_inode is null. */ - auto static probe_and_mount(kstd::shared_ptr const & backing_inode) -> kstd::shared_ptr; + auto static probe_and_mount(kstd::shared_ptr const & backing_inode) + -> std::expected, kstd::error_code>; /** @brief Initializes the filesystem with the given @p backing_inode. @param backing_inode The inode to use as the backing inode for the filesystem. (This is typically the inode representing the block device or another inode which contains the filesystem data.) - @return The result of the mount operation. + @return Nothing on success, and error otherwise. */ - virtual auto mount(kstd::shared_ptr const & backing_inode) -> operation_result; + virtual auto mount(kstd::shared_ptr const & backing_inode) -> std::expected; /** @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be @@ -54,10 +48,10 @@ namespace kernel::filesystem finding the requested inode. @param parent The parent inode. @param name The name of the child inode to look up. - @return A pointer to the requested child inode, or a null pointer if not found. + @return A pointer to the requested child inode, an error otherwise. */ [[nodiscard]] virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr = 0; + -> std::expected, kstd::error_code> = 0; /** @brief Returns a reference to the root inode of the filesystem. diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp index 52c2cec2..06b6d5c9 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp @@ -6,7 +6,9 @@ #include #include +#include +#include #include namespace kernel::filesystem::rootfs @@ -33,15 +35,15 @@ namespace kernel::filesystem::rootfs [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const &) -> operation_result + auto filesystem::mount(kstd::shared_ptr const &) -> std::expected { m_root_inode = kstd::make_shared(); - return operation_result::success; + return {}; } auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const - -> kstd::shared_ptr + -> std::expected, kstd::error_code> { - return nullptr; + return std::unexpected{make_error_code(kstd::errc::no_such_file_or_directory)}; } } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/rootfs/filesystem.hpp b/kernel/kernel/filesystem/rootfs/filesystem.hpp index 5d59486d..e05cc808 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.hpp @@ -6,8 +6,10 @@ #include #include +#include #include +#include #include namespace kernel::filesystem::rootfs @@ -25,7 +27,8 @@ namespace kernel::filesystem::rootfs @param backing_inode The backing inode to mount (not required by rootfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) + -> std::expected override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -34,7 +37,7 @@ namespace kernel::filesystem::rootfs @return Always returns nullptr. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr override; + -> std::expected, kstd::error_code> override; }; } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp index 8f16077a..347502ca 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp @@ -17,22 +17,22 @@ SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem] THEN("the filesystem can be mounted successfully") { - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); - REQUIRE(fs.root_inode() != nullptr); + REQUIRE(result); + REQUIRE(fs.root_inode()); } - THEN("looking up a non-existent directory returns null") + THEN("looking up a non-existent directory returns an error") { auto non_existent_inode_1 = fs.lookup(fs.root_inode(), ""); - REQUIRE(non_existent_inode_1 == nullptr); + REQUIRE(!non_existent_inode_1); auto non_existent_inode_2 = fs.lookup(fs.root_inode(), "nonexistent"); - REQUIRE(non_existent_inode_2 == nullptr); + REQUIRE(!non_existent_inode_2); } - THEN("looking up with a null parent inode returns null") + THEN("looking up with a null parent inode returns an error") { auto result = fs.lookup(nullptr, "dev"); - REQUIRE(result == nullptr); + REQUIRE(!result); } } } diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 55c83ca9..7de2b79f 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -45,7 +45,10 @@ namespace kernel::filesystem { // mount rootfs at / auto root_fs = kstd::make_shared(); - root_fs->mount(nullptr); + if (auto result = root_fs->mount(nullptr); !result) + { + kapi::system::panic("[FILESYSTEM] failed to mount root FS", result.error()); + } auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr); @@ -53,7 +56,10 @@ namespace kernel::filesystem // mount devfs at /dev (inside rootfs, temporary, will be shadowed) auto device_fs = kstd::make_shared(); - device_fs->mount(nullptr); + if (auto result = device_fs->mount(nullptr); !result) + { + kapi::system::panic("[FILESYSTEM] failed to mount device FS", result.error()); + } graft_persistent_device_fs(device_fs); // mount boot fs at / (shadows rootfs), re-graft devfs @@ -65,7 +71,7 @@ namespace kernel::filesystem { if (auto root_dentry = resolve_path("/")) { - do_mount_internal(*root_dentry, root_mount, boot_root_fs, boot_device_mount_context); + do_mount_internal(*root_dentry, root_mount, *boot_root_fs, boot_device_mount_context); graft_persistent_device_fs(device_fs); } } @@ -120,7 +126,7 @@ namespace kernel::filesystem { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(mount_point_dentry, mount_context, fs, source_mount_context); + do_mount_internal(mount_point_dentry, mount_context, *fs, source_mount_context); return {}; } return std::unexpected{make_error_code(vfs_errc::invalid_filesystem)}; @@ -240,7 +246,7 @@ namespace kernel::filesystem return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; } - next_dentry = kstd::make_shared(current_dentry, found_inode, part); + next_dentry = kstd::make_shared(current_dentry, *found_inode, part); current_dentry->add_child(next_dentry); } else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) diff --git a/kernel/kernel/test_support/filesystem/filesystem.cpp b/kernel/kernel/test_support/filesystem/filesystem.cpp index a4ea30bb..38cea2f1 100644 --- a/kernel/kernel/test_support/filesystem/filesystem.cpp +++ b/kernel/kernel/test_support/filesystem/filesystem.cpp @@ -4,13 +4,15 @@ #include #include +#include +#include #include namespace kernel::tests::filesystem { auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const - -> kstd::shared_ptr + -> std::expected, kstd::error_code> { return kstd::make_shared(); } diff --git a/kernel/kernel/test_support/filesystem/filesystem.hpp b/kernel/kernel/test_support/filesystem/filesystem.hpp index 5809bb5e..e6e0f416 100644 --- a/kernel/kernel/test_support/filesystem/filesystem.hpp +++ b/kernel/kernel/test_support/filesystem/filesystem.hpp @@ -5,7 +5,9 @@ #include #include +#include +#include #include namespace kernel::tests::filesystem @@ -15,7 +17,7 @@ namespace kernel::tests::filesystem filesystem() = default; [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr override; + -> std::expected, kstd::error_code> override; }; } // namespace kernel::tests::filesystem -- cgit v1.2.3 From 44c50280e92e014c2b63380d032b23864649c744 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 14:02:45 +0200 Subject: kernel: forward path resolution error --- kernel/kernel/filesystem/vfs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 7de2b79f..56e164bd 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -243,7 +243,7 @@ namespace kernel::filesystem auto found_inode = current_fs->lookup(current_dentry->get_inode(), part); if (!found_inode) { - return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; + return std::unexpected{found_inode.error()}; } next_dentry = kstd::make_shared(current_dentry, *found_inode, part); -- cgit v1.2.3 From 29e78eb35bdd6d1a73357ec8bcf9f45645e33637 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 14:17:50 +0200 Subject: kernel/vfs: simplify do_mount --- kernel/kernel/filesystem/vfs.cpp | 34 ++++++++++++++++++++-------------- kernel/kernel/filesystem/vfs.tests.cpp | 7 +++---- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 56e164bd..45424ee2 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -118,22 +118,28 @@ namespace kernel::filesystem return std::unexpected{make_error_code(vfs_errc::invalid_path)}; } - auto [mount_point_dentry, mount_context] = resolve_path_internal(target).value_or(std::pair{nullptr, nullptr}); - if (mount_point_dentry && mount_context) + auto resolved_target = resolve_path_internal(target); + if (!resolved_target) { - auto [source_dentry, source_mount_context] = resolve_path_internal(source).value_or(std::pair{nullptr, nullptr}); - if (source_dentry && source_mount_context) - { - if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) - { - do_mount_internal(mount_point_dentry, mount_context, *fs, source_mount_context); - return {}; - } - return std::unexpected{make_error_code(vfs_errc::invalid_filesystem)}; - } - return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; + return std::unexpected{resolved_target.error()}; } - return std::unexpected{make_error_code(vfs_errc::mount_point_not_found)}; + auto [mount_point_dentry, mount_context] = *resolved_target; + + auto resolved_source = resolve_path_internal(source); + if (!resolved_source) + { + return std::unexpected{resolved_source.error()}; + } + auto [source_dentry, source_mount_context] = *resolved_source; + + auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode()); + if (!fs) + { + return std::unexpected{fs.error()}; + } + + do_mount_internal(mount_point_dentry, mount_context, *fs, source_mount_context); + return {}; } auto vfs::unmount(std::string_view path) -> std::expected diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index 59589633..e4f042e7 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -256,15 +256,14 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with null file system fails") { - REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == - make_error_code(kernel::filesystem::vfs_errc::invalid_filesystem)); + REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == make_error_condition(kstd::errc::not_supported)); } THEN("mount with invalid path fails") { REQUIRE(vfs.do_mount("/dev/ram16", "").error() == make_error_code(kernel::filesystem::vfs_errc::invalid_path)); REQUIRE(vfs.do_mount("/dev/ram16", "information").error() == - make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); + make_error_code(kernel::filesystem::vfs_errc::invalid_path)); } THEN("mount with non-existent source path fails") @@ -276,7 +275,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with non-existent mount point fails") { REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent").error() == - make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); + make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); } THEN("unmount with invalid path fails") -- cgit v1.2.3 From 9576497d8b3f525754e12b9a9ad7342fcf5da7ef Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 14:20:23 +0200 Subject: kernel/ext2: fix error message --- kernel/kernel/filesystem/ext2/error.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp index e42b714b..aed33ece 100644 --- a/kernel/kernel/filesystem/ext2/error.hpp +++ b/kernel/kernel/filesystem/ext2/error.hpp @@ -42,7 +42,7 @@ namespace kernel::filesystem::ext2 case ext2_errc::invalid_block_index: return "block index out of bounds"; case ext2_errc::invalid_block_number: - return "block index out of bounds"; + return "block number out of bounds"; default: return "unknown ext2 error"; }; -- cgit v1.2.3 From 3e73968fdad5d476a205be8c86d3b93d5ce7478c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 14:20:45 +0200 Subject: kstd: fix typo in error_code ctor --- libs/kstd/kstd/system_error.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 08725609..2f1e38bf 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -151,7 +151,7 @@ namespace kstd template constexpr error_code(Enum value) noexcept - : error_code{make_error_condition(value)} + : error_code{make_error_code(value)} {} template -- cgit v1.2.3 From b105039ed6a0be0ba3da1827e711c649edef6d8e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 14:41:46 +0200 Subject: kernel: clean up error API --- kernel/kernel/filesystem/devfs/filesystem.cpp | 7 +-- kernel/kernel/filesystem/error.hpp | 19 +++++++ kernel/kernel/filesystem/ext2/filesystem.cpp | 14 +++--- kernel/kernel/filesystem/ext2/filesystem.tests.cpp | 3 +- kernel/kernel/filesystem/filesystem.cpp | 3 +- kernel/kernel/filesystem/open_file_table.cpp | 7 +-- kernel/kernel/filesystem/open_file_table.tests.cpp | 6 +-- kernel/kernel/filesystem/rootfs/filesystem.cpp | 3 +- kernel/kernel/filesystem/vfs.cpp | 14 +++--- kernel/kernel/filesystem/vfs.tests.cpp | 58 ++++++++++------------ kernel/kernel/main.cpp | 2 +- 11 files changed, 76 insertions(+), 60 deletions(-) diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index 226102ba..8af12131 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -53,12 +53,12 @@ namespace kernel::filesystem::devfs { if (!parent || !parent->is_directory()) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_inode}; } if (parent.get() != m_root_inode.get()) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_inode}; } auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { @@ -68,12 +68,13 @@ namespace kernel::filesystem::devfs } return false; }); + if (it != m_inodes.end()) { return *it; } - return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; + return std::unexpected{vfs_errc::non_existent_path}; } auto filesystem::build_device_inode_table() -> void diff --git a/kernel/kernel/filesystem/error.hpp b/kernel/kernel/filesystem/error.hpp index 267e5b7e..041b7b98 100644 --- a/kernel/kernel/filesystem/error.hpp +++ b/kernel/kernel/filesystem/error.hpp @@ -18,6 +18,10 @@ namespace kernel::filesystem has_child_mounts, invalid_filesystem, unmount_failed, + symbolic_link_loop, + unsupported_filesystem, + invalid_file_descriptor, + invalid_inode, }; namespace detail @@ -47,6 +51,14 @@ namespace kernel::filesystem return "invalid filesystem"; case vfs_errc::unmount_failed: return "unmount failed"; + case vfs_errc::symbolic_link_loop: + return "too many symbolic link levels"; + case vfs_errc::unsupported_filesystem: + return "unsupported filesystem"; + case vfs_errc::invalid_file_descriptor: + return "invalid file descriptor"; + case vfs_errc::invalid_inode: + return "invalid inode"; default: return "unknown VFS error"; } @@ -58,6 +70,7 @@ namespace kernel::filesystem { case vfs_errc::invalid_path: case vfs_errc::invalid_filesystem: + case vfs_errc::invalid_inode: return make_error_condition(kstd::errc::invalid_argument); case vfs_errc::non_existent_path: case vfs_errc::mount_point_not_found: @@ -66,6 +79,12 @@ namespace kernel::filesystem case kernel::filesystem::vfs_errc::has_child_mounts: case vfs_errc::unmount_failed: return make_error_condition(kstd::errc::device_or_resource_busy); + case vfs_errc::symbolic_link_loop: + return make_error_condition(kstd::errc::too_many_symbolic_link_levels); + case vfs_errc::unsupported_filesystem: + return make_error_condition(kstd::errc::not_supported); + case vfs_errc::invalid_file_descriptor: + return make_error_condition(kstd::errc::bad_file_descriptor); default: return kstd::error_condition{value, *this}; } diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index fd77a0a2..55da8976 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -54,7 +54,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != constants::magic_number) { - return std::unexpected{make_error_code(ext2_errc::invalid_magic_number)}; + return std::unexpected{ext2_errc::invalid_magic_number}; } auto const blocks_per_group = m_superblock.blocks_per_group; @@ -72,7 +72,7 @@ namespace kernel::filesystem::ext2 if (!m_root_inode || !m_root_inode->is_directory()) { - return std::unexpected{make_error_code(ext2_errc::invalid_root_inode)}; + return std::unexpected{ext2_errc::invalid_root_inode}; } return {}; @@ -83,7 +83,7 @@ namespace kernel::filesystem::ext2 { if (!parent || !parent->is_directory()) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_inode}; } auto * ext2_parent = static_cast(parent.get()); @@ -96,7 +96,7 @@ namespace kernel::filesystem::ext2 if (!global_block_number) { - return std::unexpected{make_error_code(ext2_errc::invalid_block_number)}; + return std::unexpected{ext2_errc::invalid_block_number}; } auto const block_offset = global_block_number.value() * block_size(); @@ -118,7 +118,7 @@ namespace kernel::filesystem::ext2 } } - return std::unexpected{make_error_code(vfs_errc::non_existent_path)}; + return std::unexpected{vfs_errc::non_existent_path}; } auto filesystem::read_inode(uint32_t inode_number) const -> std::expected, kstd::error_code> @@ -129,7 +129,7 @@ namespace kernel::filesystem::ext2 if (block_group_index >= m_block_group_descriptors.size()) { - return std::unexpected{make_error_code(ext2_errc::invalid_block_group_index)}; + return std::unexpected{ext2_errc::invalid_block_group_index}; } auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); @@ -196,7 +196,7 @@ namespace kernel::filesystem::ext2 return block_number; } - return std::unexpected{make_error_code(ext2_errc::invalid_block_index)}; + return std::unexpected{ext2_errc::invalid_block_index}; } auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp index 9a677981..1702526e 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp @@ -82,8 +82,7 @@ SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem THEN("mount fails with invalid_magic_number") { - REQUIRE(fs.mount(dev_inode).error() == - make_error_code(kernel::filesystem::ext2::ext2_errc::invalid_magic_number)); + REQUIRE(fs.mount(dev_inode).error() == kernel::filesystem::ext2::ext2_errc::invalid_magic_number); } } } diff --git a/kernel/kernel/filesystem/filesystem.cpp b/kernel/kernel/filesystem/filesystem.cpp index cb60734f..1d15d410 100644 --- a/kernel/kernel/filesystem/filesystem.cpp +++ b/kernel/kernel/filesystem/filesystem.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -37,7 +38,7 @@ namespace kernel::filesystem } } - return std::unexpected{make_error_code(kstd::errc::not_supported)}; + return std::unexpected{vfs_errc::unsupported_filesystem}; } auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> std::expected diff --git a/kernel/kernel/filesystem/open_file_table.cpp b/kernel/kernel/filesystem/open_file_table.cpp index df5717a4..1109249a 100644 --- a/kernel/kernel/filesystem/open_file_table.cpp +++ b/kernel/kernel/filesystem/open_file_table.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -44,7 +45,7 @@ namespace kernel::filesystem { if (!file_descriptor) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_file_descriptor}; } auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); @@ -62,7 +63,7 @@ namespace kernel::filesystem { if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_file_descriptor}; } return m_open_files.at(fd); @@ -72,7 +73,7 @@ namespace kernel::filesystem { if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return std::unexpected{make_error_code(kstd::errc::invalid_argument)}; + return std::unexpected{vfs_errc::invalid_file_descriptor}; } m_open_files.at(fd) = nullptr; diff --git a/kernel/kernel/filesystem/open_file_table.tests.cpp b/kernel/kernel/filesystem/open_file_table.tests.cpp index 78afaa6d..69188528 100644 --- a/kernel/kernel/filesystem/open_file_table.tests.cpp +++ b/kernel/kernel/filesystem/open_file_table.tests.cpp @@ -50,13 +50,13 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") THEN("adding a null file descriptor returns an error") { auto fd = table.add_file(nullptr); - REQUIRE(fd.error() == make_error_condition(kstd::errc::invalid_argument)); + REQUIRE(fd.error() == kstd::errc::bad_file_descriptor); } THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns an error") { auto retrieved_descriptor = table.file(1000); - REQUIRE(retrieved_descriptor.error() == make_error_condition(kstd::errc::invalid_argument)); + REQUIRE(retrieved_descriptor.error() == kstd::errc::bad_file_descriptor); } } } @@ -78,7 +78,7 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") THEN("the file descriptor can no longer be retrieved using the file descriptor") { auto retrieved_descriptor = table.file(*fd); - REQUIRE(retrieved_descriptor.error() == make_error_condition(kstd::errc::invalid_argument)); + REQUIRE(retrieved_descriptor.error() == kstd::errc::bad_file_descriptor); } } diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp index 06b6d5c9..4efacb6b 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -44,6 +45,6 @@ namespace kernel::filesystem::rootfs auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const -> std::expected, kstd::error_code> { - return std::unexpected{make_error_code(kstd::errc::no_such_file_or_directory)}; + return std::unexpected{vfs_errc::non_existent_path}; } } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 45424ee2..5ffc3468 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -108,14 +108,14 @@ namespace kernel::filesystem mount.value()->decrement_ref_count(); return {}; } - return std::unexpected{make_error_code(vfs_errc::invalid_path)}; + return std::unexpected{vfs_errc::invalid_path}; } auto vfs::do_mount(std::string_view source, std::string_view target) -> std::expected { if (!path::is_valid_path(source) || !path::is_valid_path(target)) { - return std::unexpected{make_error_code(vfs_errc::invalid_path)}; + return std::unexpected{vfs_errc::invalid_path}; } auto resolved_target = resolve_path_internal(target); @@ -146,7 +146,7 @@ namespace kernel::filesystem { if (!path::is_valid_path(path)) { - return std::unexpected{make_error_code(vfs_errc::invalid_path)}; + return std::unexpected{vfs_errc::invalid_path}; } auto remove_result = m_mount_table.remove_mount(path); @@ -156,10 +156,10 @@ namespace kernel::filesystem } else if (remove_result == mount_table::operation_result::mount_not_found) { - return std::unexpected{make_error_code(vfs_errc::mount_point_not_found)}; + return std::unexpected{vfs_errc::mount_point_not_found}; } - return std::unexpected{make_error_code(vfs_errc::unmount_failed)}; + return std::unexpected{vfs_errc::unmount_failed}; } auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, @@ -193,7 +193,7 @@ namespace kernel::filesystem { if (!path::is_valid_absolute_path(path)) { - return std::unexpected{make_error_code(vfs_errc::invalid_path)}; + return std::unexpected{vfs_errc::invalid_path}; } auto current_mount = m_mount_table.find_mount("/"); @@ -270,7 +270,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return std::unexpected{make_error_code(kstd::errc::too_many_symbolic_link_levels)}; + return std::unexpected{vfs_errc::symbolic_link_loop}; } kstd::vector buffer(constants::symlink_max_path_length); diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index e4f042e7..7ec70d9c 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -75,7 +75,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto & vfs = kernel::filesystem::vfs::get(); auto image_1 = vfs.open("/dev/image_1.txt"); - REQUIRE(image_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(image_1.error() == kstd::errc::no_such_file_or_directory); auto dev = vfs.open("/dev/ram0"); REQUIRE(dev != nullptr); @@ -112,7 +112,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information")); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(unmounted_monkey_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(unmounted_monkey_1.error() == kstd::errc::no_such_file_or_directory); auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); @@ -178,7 +178,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information")); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); - REQUIRE(mounted_tickets.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(mounted_tickets.error() == kstd::errc::no_such_file_or_directory); auto mounted_monkey = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey); @@ -192,7 +192,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); @@ -213,7 +213,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); @@ -221,10 +221,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.close(water.value()->absolute_path())); auto dev_ram_16 = vfs.open("/dev/ram16"); - REQUIRE(dev_ram_16.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(dev_ram_16.error() == kstd::errc::no_such_file_or_directory); - REQUIRE(vfs.do_mount("/dev/ram32", "/").error() == - make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); + REQUIRE(vfs.do_mount("/dev/ram32", "/").error() == kstd::errc::no_such_file_or_directory); REQUIRE(vfs.unmount("/")); @@ -243,7 +242,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/")); info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); REQUIRE(vfs.do_mount("/dev/ram0", "/")); @@ -251,44 +250,39 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(info_1 != nullptr); auto dev_ram_0 = vfs.open("/dev/ram0"); - REQUIRE(dev_ram_0.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(dev_ram_0.error() == kstd::errc::no_such_file_or_directory); } THEN("mount with null file system fails") { - REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == make_error_condition(kstd::errc::not_supported)); + REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == kstd::errc::not_supported); } THEN("mount with invalid path fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "").error() == make_error_code(kernel::filesystem::vfs_errc::invalid_path)); - REQUIRE(vfs.do_mount("/dev/ram16", "information").error() == - make_error_code(kernel::filesystem::vfs_errc::invalid_path)); + REQUIRE(vfs.do_mount("/dev/ram16", "").error() == kstd::errc::invalid_argument); + REQUIRE(vfs.do_mount("/dev/ram16", "information").error() == kstd::errc::invalid_argument); } THEN("mount with non-existent source path fails") { - REQUIRE(vfs.do_mount("/dev/nonexistent", "/information").error() == - make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); + REQUIRE(vfs.do_mount("/dev/nonexistent", "/information").error() == kstd::errc::no_such_file_or_directory); } THEN("mount with non-existent mount point fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent").error() == - make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); + REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent").error() == kstd::errc::no_such_file_or_directory); } THEN("unmount with invalid path fails") { - REQUIRE(vfs.unmount("").error() == make_error_code(kernel::filesystem::vfs_errc::invalid_path)); - REQUIRE(vfs.unmount("information").error() == - make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); + REQUIRE(vfs.unmount("").error() == kstd::errc::invalid_argument); + REQUIRE(vfs.unmount("information").error() == kstd::errc::no_such_file_or_directory); } THEN("unmounting non-existent mount point returns expected error code") { - REQUIRE(vfs.unmount("/information/nonexistent").error() == - make_error_code(kernel::filesystem::vfs_errc::mount_point_not_found)); + REQUIRE(vfs.unmount("/information/nonexistent").error() == kstd::errc::no_such_file_or_directory); } THEN("a file can be access if . in the path") @@ -348,7 +342,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); @@ -363,7 +357,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information")); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(unmounted_sheep_1.error() == kstd::errc::no_such_file_or_directory); } THEN("the file-filesystem in the image can be mounted and in this filesystem can another file-filesystem be " @@ -394,11 +388,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.close(sheep_1.value()->absolute_path())); REQUIRE(vfs.close(goat_1.value()->absolute_path())); - REQUIRE(vfs.unmount("/information").error() == make_error_code(kernel::filesystem::vfs_errc::unmount_failed)); + REQUIRE(vfs.unmount("/information").error() == kstd::errc::device_or_resource_busy); REQUIRE(vfs.unmount("/information/stable")); auto unmounted_goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); - REQUIRE(unmounted_goat_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(unmounted_goat_1.error() == kstd::errc::no_such_file_or_directory); auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); @@ -407,7 +401,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information")); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1.error() == make_error_condition(kstd::errc::no_such_file_or_directory)); + REQUIRE(unmounted_sheep_1.error() == kstd::errc::no_such_file_or_directory); } } @@ -425,7 +419,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.do_mount("/dev/ram16", "/entrance")); REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures")); - REQUIRE(vfs.unmount("/entrance").error() == make_error_code(kernel::filesystem::vfs_errc::unmount_failed)); + REQUIRE(vfs.unmount("/entrance").error() == kstd::errc::device_or_resource_busy); REQUIRE(vfs.unmount("/enclosures")); REQUIRE(vfs.unmount("/entrance")); } @@ -484,21 +478,21 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS { auto & vfs = kernel::filesystem::vfs::get(); auto invalid_symlink = vfs.open("/symlinks/invalid_absolute"); - REQUIRE(invalid_symlink.error() == make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); + REQUIRE(invalid_symlink.error() == kstd::errc::no_such_file_or_directory); } THEN("symbolic link containing an invalid relative path is handled correctly") { auto & vfs = kernel::filesystem::vfs::get(); auto invalid_symlink = vfs.open("/symlinks/invalid_relative"); - REQUIRE(invalid_symlink.error() == make_error_code(kernel::filesystem::vfs_errc::non_existent_path)); + REQUIRE(invalid_symlink.error() == kstd::errc::no_such_file_or_directory); } THEN("circular symbolic links are detected and handled correctly") { auto & vfs = kernel::filesystem::vfs::get(); auto circular_symlink = vfs.open("/symlinks/symloop_a"); - REQUIRE(circular_symlink.error() == make_error_code(kstd::errc::too_many_symbolic_link_levels)); + REQUIRE(circular_symlink.error() == kstd::errc::too_many_symbolic_link_levels); } } diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index 749040ce..28429b84 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -48,7 +48,7 @@ auto run_demo() -> void // 3) show that /entrance/information/info_1.txt is not accessible before mounting kstd::println("attempting to open /entrance/information/info_1.txt before mounting"); auto fd_before_mount = kapi::filesystem::open("/entrance/information/info_1.txt"); - if (!fd_before_mount && fd_before_mount.error() == make_error_condition(kstd::errc::no_such_file_or_directory)) + if (!fd_before_mount && fd_before_mount.error() == kstd::errc::no_such_file_or_directory) { kstd::println("--> as expected the file could not be opened before mounting"); } -- cgit v1.2.3 From b7edb751d4224161361ccb80ad8e747cd294b166 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:21:41 +0200 Subject: kernel/ext2: improve error mapping --- kernel/kernel/filesystem/ext2/error.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp index aed33ece..885f82e6 100644 --- a/kernel/kernel/filesystem/ext2/error.hpp +++ b/kernel/kernel/filesystem/ext2/error.hpp @@ -55,19 +55,24 @@ namespace kernel::filesystem::ext2 { case ext2_errc::invalid_magic_number: case ext2_errc::invalid_root_inode: - case ext2_errc::invalid_block_group_index: - case ext2_errc::invalid_block_index: - case ext2_errc::invalid_block_number: if (condition.category() == kernel::filesystem::vfs_category()) { return condition.value() == static_cast(kernel::filesystem::vfs_errc::invalid_filesystem); } else if (condition.category() == kstd::generic_category()) + { + return condition.value() == static_cast(kstd::errc::invalid_argument); + } + break; + case ext2_errc::invalid_block_group_index: + case ext2_errc::invalid_block_index: + case ext2_errc::invalid_block_number: + if (condition.category() == kstd::generic_category()) { return condition.value() == static_cast(kstd::errc::io_error); } + break; } - return kstd::error_category::equivalent(code, condition); } } constexpr inline ext2_category_instance{}; -- cgit v1.2.3 From 2b027860ed1e75187d9c3b5ddd98957da7bd6e15 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:27:18 +0200 Subject: kernel/fs: clean up file system error names --- kernel/kernel/filesystem/devfs/filesystem.cpp | 2 +- kernel/kernel/filesystem/error.hpp | 20 +++++++++++++------- kernel/kernel/filesystem/ext2/filesystem.cpp | 2 +- kernel/kernel/filesystem/rootfs/filesystem.cpp | 2 +- kernel/kernel/filesystem/vfs.cpp | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index 8af12131..59a1ae9c 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -74,7 +74,7 @@ namespace kernel::filesystem::devfs return *it; } - return std::unexpected{vfs_errc::non_existent_path}; + return std::unexpected{vfs_errc::no_such_file_or_directory}; } auto filesystem::build_device_inode_table() -> void diff --git a/kernel/kernel/filesystem/error.hpp b/kernel/kernel/filesystem/error.hpp index 041b7b98..678d95c5 100644 --- a/kernel/kernel/filesystem/error.hpp +++ b/kernel/kernel/filesystem/error.hpp @@ -12,16 +12,18 @@ namespace kernel::filesystem enum struct vfs_errc : int { invalid_path = 1, - non_existent_path, + no_such_file_or_directory, mount_point_not_found, mount_busy, has_child_mounts, invalid_filesystem, unmount_failed, - symbolic_link_loop, + too_many_symbolic_link_levels, unsupported_filesystem, invalid_file_descriptor, invalid_inode, + not_a_directory, + is_a_directory, }; namespace detail @@ -39,8 +41,8 @@ namespace kernel::filesystem { case vfs_errc::invalid_path: return "invalid path"; - case vfs_errc::non_existent_path: - return "path does not exist"; + case vfs_errc::no_such_file_or_directory: + return "no such file or directory"; case vfs_errc::mount_point_not_found: return "mount point not found"; case vfs_errc::mount_busy: @@ -51,7 +53,7 @@ namespace kernel::filesystem return "invalid filesystem"; case vfs_errc::unmount_failed: return "unmount failed"; - case vfs_errc::symbolic_link_loop: + case vfs_errc::too_many_symbolic_link_levels: return "too many symbolic link levels"; case vfs_errc::unsupported_filesystem: return "unsupported filesystem"; @@ -59,6 +61,10 @@ namespace kernel::filesystem return "invalid file descriptor"; case vfs_errc::invalid_inode: return "invalid inode"; + case vfs_errc::not_a_directory: + return "not a directory"; + case vfs_errc::is_a_directory: + return "is a directory"; default: return "unknown VFS error"; } @@ -72,14 +78,14 @@ namespace kernel::filesystem case vfs_errc::invalid_filesystem: case vfs_errc::invalid_inode: return make_error_condition(kstd::errc::invalid_argument); - case vfs_errc::non_existent_path: + case vfs_errc::no_such_file_or_directory: case vfs_errc::mount_point_not_found: return make_error_condition(kstd::errc::no_such_file_or_directory); case kernel::filesystem::vfs_errc::mount_busy: case kernel::filesystem::vfs_errc::has_child_mounts: case vfs_errc::unmount_failed: return make_error_condition(kstd::errc::device_or_resource_busy); - case vfs_errc::symbolic_link_loop: + case vfs_errc::too_many_symbolic_link_levels: return make_error_condition(kstd::errc::too_many_symbolic_link_levels); case vfs_errc::unsupported_filesystem: return make_error_condition(kstd::errc::not_supported); diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 55da8976..78a8aead 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -118,7 +118,7 @@ namespace kernel::filesystem::ext2 } } - return std::unexpected{vfs_errc::non_existent_path}; + return std::unexpected{vfs_errc::no_such_file_or_directory}; } auto filesystem::read_inode(uint32_t inode_number) const -> std::expected, kstd::error_code> diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp index 4efacb6b..ea2ceedf 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp @@ -45,6 +45,6 @@ namespace kernel::filesystem::rootfs auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const -> std::expected, kstd::error_code> { - return std::unexpected{vfs_errc::non_existent_path}; + return std::unexpected{vfs_errc::no_such_file_or_directory}; } } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 5ffc3468..aa462eae 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -270,7 +270,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return std::unexpected{vfs_errc::symbolic_link_loop}; + return std::unexpected{vfs_errc::too_many_symbolic_link_levels}; } kstd::vector buffer(constants::symlink_max_path_length); -- cgit v1.2.3 From ea6793ca34a77b1b5ed3a590c8aa235ac0d42ee7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:31:34 +0200 Subject: kernel/vfs: add missing error mappings --- kernel/kernel/filesystem/error.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/kernel/filesystem/error.hpp b/kernel/kernel/filesystem/error.hpp index 678d95c5..56f56ce4 100644 --- a/kernel/kernel/filesystem/error.hpp +++ b/kernel/kernel/filesystem/error.hpp @@ -91,6 +91,10 @@ namespace kernel::filesystem return make_error_condition(kstd::errc::not_supported); case vfs_errc::invalid_file_descriptor: return make_error_condition(kstd::errc::bad_file_descriptor); + case vfs_errc::not_a_directory: + return make_error_condition(kstd::errc::not_a_directory); + case vfs_errc::is_a_directory: + return make_error_condition(kstd::errc::is_a_directory); default: return kstd::error_condition{value, *this}; } -- cgit v1.2.3 From 12dbc8892027feceec2576c2e5dd03b30a1da2da Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:31:52 +0200 Subject: kernel/vfs: add early check --- kernel/kernel/filesystem/vfs.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index aa462eae..e0e79094 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -215,6 +215,11 @@ namespace kernel::filesystem auto part = path_parts_vector.back(); path_parts_vector.pop_back(); + if (!current_dentry->get_inode()->is_directory()) + { + return std::unexpected{vfs_errc::not_a_directory}; + } + if (part == ".") { continue; -- cgit v1.2.3 From 21a5d0ae7afa0345936fb5124518c679efe3df2a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:35:22 +0200 Subject: kernel/fs: separate out checks --- kernel/kernel/filesystem/devfs/filesystem.cpp | 7 ++++++- kernel/kernel/filesystem/ext2/filesystem.cpp | 7 ++++++- kernel/kernel/filesystem/vfs.cpp | 8 ++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index 59a1ae9c..d91ee1e4 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -51,11 +51,16 @@ namespace kernel::filesystem::devfs auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const -> std::expected, kstd::error_code> { - if (!parent || !parent->is_directory()) + if (!parent) { return std::unexpected{vfs_errc::invalid_inode}; } + if (!parent->is_directory()) + { + return std::unexpected{vfs_errc::not_a_directory}; + } + if (parent.get() != m_root_inode.get()) { return std::unexpected{vfs_errc::invalid_inode}; diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 78a8aead..7aa2bd2d 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -81,11 +81,16 @@ namespace kernel::filesystem::ext2 auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const -> std::expected, kstd::error_code> { - if (!parent || !parent->is_directory()) + if (!parent) { return std::unexpected{vfs_errc::invalid_inode}; } + if (!parent->is_directory()) + { + return std::unexpected{vfs_errc::not_a_directory}; + } + auto * ext2_parent = static_cast(parent.get()); auto const & inode_data = ext2_parent->data(); kstd::vector buffer(block_size()); diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index e0e79094..31b9e4d0 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -158,6 +158,14 @@ namespace kernel::filesystem { return std::unexpected{vfs_errc::mount_point_not_found}; } + else if (remove_result == mount_table::operation_result::cannot_be_unmounted) + { + return std::unexpected{vfs_errc::mount_busy}; + } + else if (remove_result == mount_table::operation_result::has_child_mounts) + { + return std::unexpected{vfs_errc::has_child_mounts}; + } return std::unexpected{vfs_errc::unmount_failed}; } -- cgit v1.2.3 From 4cdbf7811ed69feac2b5561841bdfad5f5068f5e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 22:40:43 +0200 Subject: kernel/ext2: forward block number error --- kernel/kernel/filesystem/ext2/filesystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 7aa2bd2d..5b0e0907 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -101,7 +101,7 @@ namespace kernel::filesystem::ext2 if (!global_block_number) { - return std::unexpected{ext2_errc::invalid_block_number}; + return std::unexpected{global_block_number.error()}; } auto const block_offset = global_block_number.value() * block_size(); -- cgit v1.2.3 From b137e22dc32b38cdc254cbd7b3293a00578a12a3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 23:15:00 +0200 Subject: kstd: add posix-like error constants --- libs/kstd/kstd/posix.hpp | 85 +++++++++++++++ libs/kstd/kstd/system_error.hpp | 222 +++++++++++++++++++++++++--------------- libs/kstd/kstd/unikstd.h | 12 --- 3 files changed, 227 insertions(+), 92 deletions(-) create mode 100644 libs/kstd/kstd/posix.hpp delete mode 100644 libs/kstd/kstd/unikstd.h diff --git a/libs/kstd/kstd/posix.hpp b/libs/kstd/kstd/posix.hpp new file mode 100644 index 00000000..12bd5ec0 --- /dev/null +++ b/libs/kstd/kstd/posix.hpp @@ -0,0 +1,85 @@ +#ifndef KSTD_CERRNO_HPP +#define KSTD_CERRNO_HPP + +namespace kstd::posix +{ + + constexpr auto static inline eafnosupport = 1; + constexpr auto static inline eaddrinuse = 2; + constexpr auto static inline eaddrnotavail = 3; + constexpr auto static inline eisconn = 4; + constexpr auto static inline e2big = 5; + constexpr auto static inline edom = 6; + constexpr auto static inline efault = 7; + constexpr auto static inline ebadf = 8; + constexpr auto static inline ebadmsg = 9; + constexpr auto static inline epipe = 10; + constexpr auto static inline econnaborted = 11; + constexpr auto static inline ealready = 12; + constexpr auto static inline econnrefused = 13; + constexpr auto static inline econnreset = 14; + constexpr auto static inline exdev = 15; + constexpr auto static inline edestaddrreq = 16; + constexpr auto static inline ebusy = 17; + constexpr auto static inline enotempty = 18; + constexpr auto static inline enoexec = 19; + constexpr auto static inline eexist = 20; + constexpr auto static inline efbig = 21; + constexpr auto static inline enametoolong = 22; + constexpr auto static inline enosys = 23; + constexpr auto static inline ehostunreach = 24; + constexpr auto static inline eidrm = 25; + constexpr auto static inline eilseq = 26; + constexpr auto static inline enotty = 27; + constexpr auto static inline eintr = 28; + constexpr auto static inline einval = 29; + constexpr auto static inline espipe = 30; + constexpr auto static inline eio = 31; + constexpr auto static inline eisdir = 32; + constexpr auto static inline emsgsize = 33; + constexpr auto static inline enetdown = 34; + constexpr auto static inline enetreset = 35; + constexpr auto static inline enetunreach = 36; + constexpr auto static inline enobufs = 37; + constexpr auto static inline echild = 38; + constexpr auto static inline enolink = 39; + constexpr auto static inline enolck = 40; + constexpr auto static inline enomsg = 41; + constexpr auto static inline enoprotoopt = 42; + constexpr auto static inline enospc = 43; + constexpr auto static inline enodev = 44; + constexpr auto static inline enxio = 45; + constexpr auto static inline enoent = 46; + constexpr auto static inline esrch = 47; + constexpr auto static inline enotdir = 48; + constexpr auto static inline enotsock = 49; + constexpr auto static inline enotconn = 50; + constexpr auto static inline enomem = 51; + constexpr auto static inline enotsup = 52; + constexpr auto static inline ecanceled = 53; + constexpr auto static inline einprogress = 54; + constexpr auto static inline eperm = 55; + constexpr auto static inline eopnotsupp = 56; + constexpr auto static inline ewouldblock = 57; + constexpr auto static inline eownerdead = 58; + constexpr auto static inline eacces = 59; + constexpr auto static inline eproto = 60; + constexpr auto static inline eprotonosupport = 61; + constexpr auto static inline erofs = 62; + constexpr auto static inline edeadlk = 63; + constexpr auto static inline eagain = 64; + constexpr auto static inline erange = 65; + constexpr auto static inline esocktnosupport = 66; + constexpr auto static inline enotrecoverable = 67; + constexpr auto static inline etxtbsy = 68; + constexpr auto static inline etimedout = 69; + constexpr auto static inline emfile = 70; + constexpr auto static inline enfile = 71; + constexpr auto static inline emlink = 72; + constexpr auto static inline eloop = 73; + constexpr auto static inline eoverflow = 74; + constexpr auto static inline eprototype = 75; + +} // namespace kstd::posix + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 2f1e38bf..77d85172 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -2,6 +2,7 @@ #define KSTD_SYSTEM_ERROR_HPP #include +#include // IWYU pragma: export #include #include @@ -15,30 +16,74 @@ namespace kstd struct error_condition; struct error_code; + //! Determine if a given enumeration type is an error condition enumeration. + //! + //! An enumeration type is an error condition enumeration if it defines generic, system-level error conditions. + //! Specific subsystem error enumerations on the other hand are not generic enough to be considered error conditions + //! and are thus called error codes. + //! + //! @note Types may opt in to being an error condition enumeration by specializing this class template. + //! + //! @tparam CandidateType The type to check. template requires(std::is_enum_v) struct is_error_condition_enum : std::false_type { }; + //! Determine if a given enumeration type is an error condition enumeration. + //! + //! @see is_error_condition_enum + //! + //! @tparam CandidateType The type to check. template constexpr auto inline is_error_condition_enum_v = is_error_condition_enum::value; + //! Determine if a given enumeration type is an error condition enumeration. + //! + //! @see is_error_condition_enum + //! @tparam CandidateType The type to check. template concept error_condition_enum = std::is_enum_v && is_error_condition_enum_v; + //! Determine if a given enumeration type is an error code enumeration. + //! + //! An enumeration type is an error code enumeration if it defines specific, subsystem-level error codes. + //! Generic error conditions are not specific enough to be considered error codes and are thus called error + //! conditions. + //! + //! @note Types may opt in to being an error code enumeration by specializing this class template. + //! + //! @tparam CandidateType The type to check. template requires(std::is_enum_v) struct is_error_code_enum : std::false_type { }; + //! Determine if a given enumeration type is an error code enumeration. + //! + //! @see is_error_code_enum + //! + //! @tparam CandidateType The type to check. template constexpr auto inline is_error_code_enum_v = is_error_code_enum::value; + //! Determine if a given enumeration type is an error code enumeration. + //! + //! @see is_error_code_enum + //! + //! @tparam CandidateType The type to check. template concept error_code_enum = std::is_enum_v && is_error_code_enum_v; + //! A category of error codes or conditions. + //! + //! An error category is a group of related error codes or conditions. Each error code or condition belongs to exactly + //! one error category. Error categories are used to provide additional context and information about the error codes + //! or conditions they contain. They can also be used to compare error codes or conditions from different categories. + //! + //! Subsystems shall defined their own error categories for their error codes, by inheriting from this class. struct error_category { constexpr error_category() noexcept = default; @@ -49,21 +94,42 @@ namespace kstd auto operator=(error_category const &) -> error_category & = delete; + //! Get the name of this error category. [[nodiscard]] constexpr auto virtual name() const noexcept -> std::string_view = 0; + //! Get the default error condition for a given error code value in this category. + //! + //! @param value The error code value to get the default error condition for. + //! @return The default error condition for the given error code value in this category. [[nodiscard]] constexpr auto virtual default_error_condition(int value) const noexcept -> error_condition; + //! Check if a given error code value is equivalent to a given error condition in this category. + //! + //! @param value The error code value to check. + //! @param other The error condition to compare against. + //! @return True if the error code value is equivalent to the error condition in this category, false otherwise. [[nodiscard]] constexpr auto virtual equivalent(int value, error_condition const & other) const noexcept -> bool; - [[nodiscard]] constexpr auto virtual equivalent(error_code const &, int condition) const noexcept -> bool; - + //! Check if a given error code is equivalent to a given error condition in this category. + //! + //! @param code The error code to check. + //! @param condition The error condition to compare against. + //! @return True if the error code is equivalent to the error condition in this category, false otherwise. + [[nodiscard]] constexpr auto virtual equivalent(error_code const & code, int condition) const noexcept -> bool; + + //! Get the message for a given error code value in this category. + //! + //! @param value The error code value to get the message for. + //! @return The message for the given error code value in this category. [[nodiscard]] constexpr auto virtual message(int value) const noexcept -> std::string_view = 0; + //! Check if this error category is equal to another one. [[nodiscard]] constexpr auto operator==(error_category const & other) const noexcept -> bool { return this == &other; } + //! Lexicographically compare this error category to a different one. [[nodiscard]] constexpr auto operator<=>(error_category const & other) const noexcept -> std::strong_ordering { return std::compare_three_way{}(this, &other); @@ -216,82 +282,80 @@ namespace kstd enum struct errc : std::uint32_t { - success, - address_family_not_supported, - address_in_use, - address_not_available, - already_connected, - argument_list_too_long, - argument_out_of_domain, - bad_address, - bad_file_descriptor, - bad_message, - broken_pipe, - connection_aborted, - connection_already_in_progress, - connection_refused, - connection_reset, - cross_device_link, - destination_address_required, - device_or_resource_busy, - directory_not_empty, - executable_format_error, - file_exists, - file_too_large, - filename_too_long, - function_not_supported, - host_unreachable, - identifier_removed, - illegal_byte_sequence, - inappropriate_io_control_operation, - interrupted, - invalid_argument, - invalid_seek, - io_error, - is_a_directory, - message_size, - network_down, - network_reset, - network_unreachable, - no_buffer_space, - no_child_process, - no_link, - no_lock_available, - no_message, - no_protocol_option, - no_space_on_device, - no_such_device, - no_such_device_or_address, - no_such_file_or_directory, - no_such_process, - not_a_directory, - not_a_socket, - not_connected, - not_enough_memory, - not_supported, - operation_canceled, - operation_in_progress, - operation_not_permitted, - operation_not_supported, - operation_would_block, - owner_dead, - permission_denied, - protocol_error, - protocol_not_supported, - read_only_file_system, - resource_deadlock_would_occur, - resource_unavailable_try_again, - result_out_of_range, - socket_type_not_supported, - state_not_recoverable, - text_file_busy, - timed_out, - too_many_files_open, - too_many_files_open_in_system, - too_many_links, - too_many_symbolic_link_levels, - value_too_large, - wrong_protocol_type, + address_family_not_supported = posix::eafnosupport, + address_in_use = posix::eaddrinuse, + address_not_available = posix::eaddrnotavail, + already_connected = posix::eisconn, + argument_list_too_long = posix::e2big, + argument_out_of_domain = posix::edom, + bad_address = posix::efault, + bad_file_descriptor = posix::ebadf, + bad_message = posix::ebadmsg, + broken_pipe = posix::epipe, + connection_aborted = posix::econnaborted, + connection_already_in_progress = posix::ealready, + connection_refused = posix::econnrefused, + connection_reset = posix::econnreset, + cross_device_link = posix::exdev, + destination_address_required = posix::edestaddrreq, + device_or_resource_busy = posix::ebusy, + directory_not_empty = posix::enotempty, + executable_format_error = posix::enoexec, + file_exists = posix::eexist, + file_too_large = posix::efbig, + filename_too_long = posix::enametoolong, + function_not_supported = posix::enosys, + host_unreachable = posix::ehostunreach, + identifier_removed = posix::eidrm, + illegal_byte_sequence = posix::eilseq, + inappropriate_io_control_operation = posix::enotty, + interrupted = posix::eintr, + invalid_argument = posix::einval, + invalid_seek = posix::espipe, + io_error = posix::eio, + is_a_directory = posix::eisdir, + message_size = posix::emsgsize, + network_down = posix::enetdown, + network_reset = posix::enetreset, + network_unreachable = posix::enetunreach, + no_buffer_space = posix::enobufs, + no_child_process = posix::echild, + no_link = posix::enolink, + no_lock_available = posix::enolck, + no_message = posix::enomsg, + no_protocol_option = posix::enoprotoopt, + no_space_on_device = posix::enospc, + no_such_device_or_address = posix::enxio, + no_such_device = posix::enodev, + no_such_file_or_directory = posix::enoent, + no_such_process = posix::esrch, + not_a_directory = posix::enotdir, + not_a_socket = posix::enotsock, + not_connected = posix::enotconn, + not_enough_memory = posix::enomem, + not_supported = posix::enotsup, + operation_canceled = posix::ecanceled, + operation_in_progress = posix::einprogress, + operation_not_permitted = posix::eperm, + operation_not_supported = posix::eopnotsupp, + operation_would_block = posix::ewouldblock, + owner_dead = posix::eownerdead, + permission_denied = posix::eacces, + protocol_error = posix::eproto, + protocol_not_supported = posix::eprotonosupport, + read_only_file_system = posix::erofs, + resource_deadlock_would_occur = posix::edeadlk, + resource_unavailable_try_again = posix::eagain, + result_out_of_range = posix::erange, + state_not_recoverable = posix::enotrecoverable, + text_file_busy = posix::etxtbsy, + timed_out = posix::etimedout, + too_many_files_open_in_system = posix::enfile, + too_many_files_open = posix::emfile, + too_many_links = posix::emlink, + too_many_symbolic_link_levels = posix::eloop, + value_too_large = posix::eoverflow, + wrong_protocol_type = posix::eprototype, }; template<> @@ -306,7 +370,6 @@ namespace kstd switch (error) { // clang-format off - case errc::success: return "success"; case errc::address_family_not_supported: return "address family not supported"; case errc::address_in_use: return "address in use"; case errc::address_not_available: return "address not available"; @@ -372,7 +435,6 @@ namespace kstd case errc::resource_deadlock_would_occur: return "resource deadlock would occur"; case errc::resource_unavailable_try_again: return "resource unavailable, try again"; case errc::result_out_of_range: return "result out of range"; - case errc::socket_type_not_supported: return "socket type not supported"; case errc::state_not_recoverable: return "state not recoverable"; case errc::text_file_busy: return "text file busy"; case errc::timed_out: return "timed out"; diff --git a/libs/kstd/kstd/unikstd.h b/libs/kstd/kstd/unikstd.h deleted file mode 100644 index aa60be63..00000000 --- a/libs/kstd/kstd/unikstd.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef KSTD_UNIKSTD_HPP -#define KSTD_UNIKSTD_HPP - -#include -#include - -namespace kstd -{ - using ssize_t = std::make_signed_t; -} // namespace kstd - -#endif \ No newline at end of file -- cgit v1.2.3 From 1a9acb2a6a085bbb11d71824512913c0a15dbd68 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Jul 2026 23:26:54 +0200 Subject: kstd: artificially inflate test coverage --- libs/kstd/kstd/system_error.tests.cpp | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/libs/kstd/kstd/system_error.tests.cpp b/libs/kstd/kstd/system_error.tests.cpp index 12f952b3..59e11926 100644 --- a/libs/kstd/kstd/system_error.tests.cpp +++ b/libs/kstd/kstd/system_error.tests.cpp @@ -1,6 +1,7 @@ #include #include +#include SCENARIO("Error condition initialization and construction", "[kstd][system_error]") { @@ -53,3 +54,47 @@ SCENARIO("Error code initialization and construction", "[kstd][system_error]") } } } + +SCENARIO("Error code messages", "[kstd][system_error]") +{ + GIVEN("An error code") + { + auto enumerator = GENERATE( + kstd::errc::address_family_not_supported, kstd::errc::address_in_use, kstd::errc::address_not_available, + kstd::errc::already_connected, kstd::errc::argument_list_too_long, kstd::errc::argument_out_of_domain, + kstd::errc::bad_address, kstd::errc::bad_file_descriptor, kstd::errc::bad_message, kstd::errc::broken_pipe, + kstd::errc::connection_aborted, kstd::errc::connection_already_in_progress, kstd::errc::connection_refused, + kstd::errc::connection_reset, kstd::errc::cross_device_link, kstd::errc::destination_address_required, + kstd::errc::device_or_resource_busy, kstd::errc::directory_not_empty, kstd::errc::executable_format_error, + kstd::errc::file_exists, kstd::errc::file_too_large, kstd::errc::filename_too_long, + kstd::errc::function_not_supported, kstd::errc::host_unreachable, kstd::errc::identifier_removed, + kstd::errc::illegal_byte_sequence, kstd::errc::inappropriate_io_control_operation, kstd::errc::interrupted, + kstd::errc::invalid_argument, kstd::errc::invalid_seek, kstd::errc::io_error, kstd::errc::is_a_directory, + kstd::errc::message_size, kstd::errc::network_down, kstd::errc::network_reset, kstd::errc::network_unreachable, + kstd::errc::no_buffer_space, kstd::errc::no_child_process, kstd::errc::no_link, kstd::errc::no_lock_available, + kstd::errc::no_message, kstd::errc::no_protocol_option, kstd::errc::no_space_on_device, + kstd::errc::no_such_device_or_address, kstd::errc::no_such_device, kstd::errc::no_such_file_or_directory, + kstd::errc::no_such_process, kstd::errc::not_a_directory, kstd::errc::not_a_socket, kstd::errc::not_connected, + kstd::errc::not_enough_memory, kstd::errc::not_supported, kstd::errc::operation_canceled, + kstd::errc::operation_in_progress, kstd::errc::operation_not_permitted, kstd::errc::operation_not_supported, + kstd::errc::operation_would_block, kstd::errc::owner_dead, kstd::errc::permission_denied, + kstd::errc::protocol_error, kstd::errc::protocol_not_supported, kstd::errc::read_only_file_system, + kstd::errc::resource_deadlock_would_occur, kstd::errc::resource_unavailable_try_again, + kstd::errc::result_out_of_range, kstd::errc::state_not_recoverable, kstd::errc::text_file_busy, + kstd::errc::timed_out, kstd::errc::too_many_files_open_in_system, kstd::errc::too_many_files_open, + kstd::errc::too_many_links, kstd::errc::too_many_symbolic_link_levels, kstd::errc::value_too_large, + kstd::errc::wrong_protocol_type); + + auto error = kstd::make_error_code(enumerator); + + WHEN("getting the message") + { + auto message = error.message(); + + THEN("the message is not 'unknown error'") + { + REQUIRE(message != "unknown error"); + } + } + } +} -- cgit v1.2.3 From 6dd7107c4e2aeb65eb1e04d260bd9effd179993a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Jul 2026 14:46:32 +0200 Subject: kernel/vfs: rename do_mount to mount --- kernel/kapi/filesystem.cpp | 2 +- kernel/kernel/filesystem/vfs.cpp | 15 +++++----- kernel/kernel/filesystem/vfs.hpp | 10 +++---- kernel/kernel/filesystem/vfs.tests.cpp | 54 +++++++++++++++++----------------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index f84949da..a7c8e403 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -15,7 +15,7 @@ namespace kapi::filesystem { auto mount(std::string_view source, std::string_view target) -> std::expected { - return kernel::filesystem::vfs::get().do_mount(source, target); + return kernel::filesystem::vfs::get().mount(source, target); } auto umount(std::string_view target) -> std::expected diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 31b9e4d0..c38e19d7 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -51,7 +51,7 @@ namespace kernel::filesystem } auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); - auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr); + auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr); m_mount_table.add_mount(root_mount); // mount devfs at /dev (inside rootfs, temporary, will be shadowed) @@ -111,7 +111,7 @@ namespace kernel::filesystem return std::unexpected{vfs_errc::invalid_path}; } - auto vfs::do_mount(std::string_view source, std::string_view target) -> std::expected + auto vfs::mount(std::string_view source, std::string_view target) -> std::expected { if (!path::is_valid_path(source) || !path::is_valid_path(target)) { @@ -171,12 +171,13 @@ namespace kernel::filesystem } auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, - kstd::shared_ptr const & source_mount) -> void + kstd::shared_ptr const & parent_mount, + kstd::shared_ptr const & fs, + kstd::shared_ptr const & source_mount) -> void { auto new_fs_root = kstd::make_shared(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount, source_mount); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount, source_mount); m_mount_table.add_mount(new_mount); } @@ -197,7 +198,7 @@ namespace kernel::filesystem } auto vfs::resolve_path_internal(std::string_view path) const - -> std::expected, kstd::shared_ptr>, kstd::error_code> + -> std::expected, kstd::shared_ptr>, kstd::error_code> { if (!path::is_valid_absolute_path(path)) { @@ -314,7 +315,7 @@ namespace kernel::filesystem return resolve_path_internal(path).transform([](auto result) { return result.first; }); } - auto vfs::find_mount(std::string_view path) const -> std::expected, kstd::error_code> + auto vfs::find_mount(std::string_view path) const -> std::expected, kstd::error_code> { return resolve_path_internal(path).transform([](auto result) { return result.second; }); } diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index 3e86eae5..f3f94e81 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -64,7 +64,7 @@ namespace kernel::filesystem @param target The path where the filesystem should be mounted. @return Nothing on success or an error code on failure. */ - auto do_mount(std::string_view source, std::string_view target) -> std::expected; + auto mount(std::string_view source, std::string_view target) -> std::expected; /** @brief Unmount the filesystem mounted at the specified @p path. @@ -85,17 +85,17 @@ namespace kernel::filesystem * - find_mount() for the mount context only. */ [[nodiscard]] auto resolve_path_internal(std::string_view path) const - -> std::expected, kstd::shared_ptr>, kstd::error_code>; + -> std::expected, kstd::shared_ptr>, kstd::error_code>; [[nodiscard]] auto resolve_path(std::string_view path) const -> std::expected, kstd::error_code>; [[nodiscard]] auto find_mount(std::string_view path) const - -> std::expected, kstd::error_code>; + -> std::expected, kstd::error_code>; auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, - kstd::shared_ptr const & source_mount = nullptr) -> void; + kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, + kstd::shared_ptr const & source_mount = nullptr) -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index 7ec70d9c..8ef24a15 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -104,7 +104,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("second image can be mounted, data retrieved and unmounted again") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1); @@ -120,8 +120,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); - REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram32", "/information/monkey_house/infrastructure")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); @@ -139,7 +139,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("image can be mounted, unmount only if no files are open") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1); @@ -168,8 +168,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("images can be stacked mounted and correct file system is unmounted again") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); - REQUIRE(vfs.do_mount("/dev/ram32", "/information")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram32", "/information")); auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets); @@ -189,7 +189,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.do_mount("/dev/ram16", "/")); + REQUIRE(vfs.mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); @@ -210,7 +210,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.do_mount("/dev/ram16", "/")); + REQUIRE(vfs.mount("/dev/ram16", "/")); info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); @@ -223,7 +223,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16.error() == kstd::errc::no_such_file_or_directory); - REQUIRE(vfs.do_mount("/dev/ram32", "/").error() == kstd::errc::no_such_file_or_directory); + REQUIRE(vfs.mount("/dev/ram32", "/").error() == kstd::errc::no_such_file_or_directory); REQUIRE(vfs.unmount("/")); @@ -244,7 +244,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); - REQUIRE(vfs.do_mount("/dev/ram0", "/")); + REQUIRE(vfs.mount("/dev/ram0", "/")); info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); @@ -255,23 +255,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with null file system fails") { - REQUIRE(vfs.do_mount("/closed.txt", "/information").error() == kstd::errc::not_supported); + REQUIRE(vfs.mount("/closed.txt", "/information").error() == kstd::errc::not_supported); } THEN("mount with invalid path fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "").error() == kstd::errc::invalid_argument); - REQUIRE(vfs.do_mount("/dev/ram16", "information").error() == kstd::errc::invalid_argument); + REQUIRE(vfs.mount("/dev/ram16", "").error() == kstd::errc::invalid_argument); + REQUIRE(vfs.mount("/dev/ram16", "information").error() == kstd::errc::invalid_argument); } THEN("mount with non-existent source path fails") { - REQUIRE(vfs.do_mount("/dev/nonexistent", "/information").error() == kstd::errc::no_such_file_or_directory); + REQUIRE(vfs.mount("/dev/nonexistent", "/information").error() == kstd::errc::no_such_file_or_directory); } THEN("mount with non-existent mount point fails") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent").error() == kstd::errc::no_such_file_or_directory); + REQUIRE(vfs.mount("/dev/ram16", "/information/nonexistent").error() == kstd::errc::no_such_file_or_directory); } THEN("unmount with invalid path fails") @@ -302,7 +302,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("a file can be accessed over multiple mounts if path contains .. or . ") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); auto img = vfs.open("/information/monkey_house/caretaker/../../../../../../archiv/2024.img"); REQUIRE(img != nullptr); @@ -316,8 +316,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("a file can be accessed over multiple mounts (device and file) if path contains .. ") { - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/archiv/2024.img", "/information/monkey_house/infrastructure")); auto pig_1 = vfs.open("/information/monkey_house/infrastructure/stable/pig_1.txt"); REQUIRE(pig_1 != nullptr); @@ -339,7 +339,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("the file-filesystem in the image can be mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); + REQUIRE(vfs.mount("/archiv/2024.img", "/information")); auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1.error() == kstd::errc::no_such_file_or_directory); @@ -364,8 +364,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS "mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); - REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable")); + REQUIRE(vfs.mount("/archiv/2024.img", "/information")); + REQUIRE(vfs.mount("/archiv/2025.img", "/information/stable")); auto sheep_1 = vfs.open("/information/sheep_1.txt"); auto goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); @@ -416,8 +416,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("cannot unmount a filesystem if files are mounted") { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance")); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures")); + REQUIRE(vfs.mount("/dev/ram16", "/entrance")); + REQUIRE(vfs.mount("/entrance/archiv/2024.img", "/enclosures")); REQUIRE(vfs.unmount("/entrance").error() == kstd::errc::device_or_resource_busy); REQUIRE(vfs.unmount("/enclosures")); @@ -426,8 +426,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("can mount filesystem onto the directory that contains it") { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance")); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance")); + REQUIRE(vfs.mount("/dev/ram16", "/entrance")); + REQUIRE(vfs.mount("/entrance/archiv/2024.img", "/entrance")); REQUIRE(vfs.unmount("/entrance")); REQUIRE(vfs.unmount("/entrance")); @@ -502,7 +502,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information")); + REQUIRE(vfs.mount("/archiv/2024.img", "/information")); THEN("file can be opened through symbolic link pointing to the parent filesystem") { @@ -519,7 +519,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/dev/ram16", "/information")); + REQUIRE(vfs.mount("/dev/ram16", "/information")); THEN("file can be opened through symbolic link pointing to the parent filesystem and back into the mounted " "filesystem again") -- cgit v1.2.3 From 5d98c13c0a1aa1e5415dbe820d7b537477b79c18 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Jul 2026 14:54:39 +0200 Subject: kernel/vfs: extract type aliases --- kernel/kernel/filesystem/vfs.cpp | 14 ++++++-------- kernel/kernel/filesystem/vfs.hpp | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index c38e19d7..40f2c3aa 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -88,7 +88,7 @@ namespace kernel::filesystem return *active_vfs; } - auto vfs::open(std::string_view path) -> std::expected, kstd::error_code> + auto vfs::open(std::string_view path) -> std::expected { auto resolved_path = resolve_path_internal(path); if (!resolved_path) @@ -170,10 +170,8 @@ namespace kernel::filesystem return std::unexpected{vfs_errc::unmount_failed}; } - auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, - kstd::shared_ptr const & fs, - kstd::shared_ptr const & source_mount) -> void + auto vfs::do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs, + mount_ptr const & source_mount) -> void { auto new_fs_root = kstd::make_shared(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name()); @@ -198,7 +196,7 @@ namespace kernel::filesystem } auto vfs::resolve_path_internal(std::string_view path) const - -> std::expected, kstd::shared_ptr>, kstd::error_code> + -> std::expected, kstd::error_code> { if (!path::is_valid_absolute_path(path)) { @@ -310,12 +308,12 @@ namespace kernel::filesystem return std::pair{current_dentry, current_mount}; } - auto vfs::resolve_path(std::string_view path) const -> std::expected, kstd::error_code> + auto vfs::resolve_path(std::string_view path) const -> std::expected { return resolve_path_internal(path).transform([](auto result) { return result.first; }); } - auto vfs::find_mount(std::string_view path) const -> std::expected, kstd::error_code> + auto vfs::find_mount(std::string_view path) const -> std::expected { return resolve_path_internal(path).transform([](auto result) { return result.second; }); } diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index f3f94e81..2645fe14 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -24,6 +24,10 @@ namespace kernel::filesystem */ struct vfs { + using dentry_ptr = kstd::shared_ptr; + using mount_ptr = kstd::shared_ptr; + using fs_ptr = kstd::shared_ptr; + vfs(); /** @@ -49,7 +53,7 @@ namespace kernel::filesystem @param path The path to the file to open. @return A shared pointer to the dentry on success or an error code on failure. */ - auto open(std::string_view path) -> std::expected, kstd::error_code>; + auto open(std::string_view path) -> std::expected; /** @brief Close a file by its associated @p path. @@ -85,17 +89,14 @@ namespace kernel::filesystem * - find_mount() for the mount context only. */ [[nodiscard]] auto resolve_path_internal(std::string_view path) const - -> std::expected, kstd::shared_ptr>, kstd::error_code>; + -> std::expected, kstd::error_code>; - [[nodiscard]] auto resolve_path(std::string_view path) const - -> std::expected, kstd::error_code>; + [[nodiscard]] auto resolve_path(std::string_view path) const -> std::expected; - [[nodiscard]] auto find_mount(std::string_view path) const - -> std::expected, kstd::error_code>; + [[nodiscard]] auto find_mount(std::string_view path) const -> std::expected; - auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, - kstd::shared_ptr const & source_mount = nullptr) -> void; + auto do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs, + mount_ptr const & source_mount = nullptr) -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; -- cgit v1.2.3 From af017fa7983e6730b2f8fb6acee221dd39c02b7a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Jul 2026 15:27:49 +0200 Subject: kstd: extract result helper alias --- kernel/kernel/filesystem/devfs/filesystem.cpp | 16 ++++---- kernel/kernel/filesystem/devfs/filesystem.hpp | 7 ++-- kernel/kernel/filesystem/ext2/filesystem.cpp | 24 ++++++------ kernel/kernel/filesystem/ext2/filesystem.hpp | 12 +++--- kernel/kernel/filesystem/filesystem.cpp | 10 ++--- kernel/kernel/filesystem/filesystem.hpp | 8 ++-- kernel/kernel/filesystem/open_file_table.cpp | 18 ++++----- kernel/kernel/filesystem/open_file_table.hpp | 8 ++-- kernel/kernel/filesystem/rootfs/filesystem.cpp | 10 ++--- kernel/kernel/filesystem/rootfs/filesystem.hpp | 7 ++-- kernel/kernel/filesystem/vfs.cpp | 52 +++++++++++++------------- kernel/kernel/filesystem/vfs.hpp | 16 ++++---- libs/kstd/kstd/result.hpp | 35 +++++++++++++++++ 13 files changed, 127 insertions(+), 96 deletions(-) create mode 100644 libs/kstd/kstd/result.hpp diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp index d91ee1e4..7ffcb3c4 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.cpp +++ b/kernel/kernel/filesystem/devfs/filesystem.cpp @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include #include namespace kernel::filesystem::devfs @@ -40,30 +40,30 @@ namespace kernel::filesystem::devfs [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const &) -> std::expected + auto filesystem::mount(kstd::shared_ptr const &) -> kstd::result { m_root_inode = kstd::make_shared(); build_device_inode_table(); - return {}; + return kstd::success(); } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> + -> kstd::result> { if (!parent) { - return std::unexpected{vfs_errc::invalid_inode}; + return kstd::failure(vfs_errc::invalid_inode); } if (!parent->is_directory()) { - return std::unexpected{vfs_errc::not_a_directory}; + return kstd::failure(vfs_errc::not_a_directory); } if (parent.get() != m_root_inode.get()) { - return std::unexpected{vfs_errc::invalid_inode}; + return kstd::failure(vfs_errc::invalid_inode); } auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { @@ -79,7 +79,7 @@ namespace kernel::filesystem::devfs return *it; } - return std::unexpected{vfs_errc::no_such_file_or_directory}; + return kstd::failure(vfs_errc::no_such_file_or_directory); } auto filesystem::build_device_inode_table() -> void diff --git a/kernel/kernel/filesystem/devfs/filesystem.hpp b/kernel/kernel/filesystem/devfs/filesystem.hpp index e96790b8..dadeaf7d 100644 --- a/kernel/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/kernel/filesystem/devfs/filesystem.hpp @@ -6,10 +6,10 @@ #include #include +#include #include #include -#include #include namespace kernel::filesystem::devfs @@ -27,8 +27,7 @@ namespace kernel::filesystem::devfs @param backing_inode Backing inode passed by the vfs (not required by devfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) - -> std::expected override; + auto mount(kstd::shared_ptr const & backing_inode) -> kstd::result override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -37,7 +36,7 @@ namespace kernel::filesystem::devfs @return A pointer to the found inode, or a null pointer if not found. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> override; + -> kstd::result> override; private: auto build_device_inode_table() -> void; diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 5b0e0907..4195ad05 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -45,8 +46,7 @@ namespace kernel::filesystem::ext2 [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const & backing_inode) - -> std::expected + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> kstd::result { std::ignore = kernel::filesystem::filesystem::mount(backing_inode); @@ -54,7 +54,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != constants::magic_number) { - return std::unexpected{ext2_errc::invalid_magic_number}; + return kstd::failure(ext2_errc::invalid_magic_number); } auto const blocks_per_group = m_superblock.blocks_per_group; @@ -72,23 +72,23 @@ namespace kernel::filesystem::ext2 if (!m_root_inode || !m_root_inode->is_directory()) { - return std::unexpected{ext2_errc::invalid_root_inode}; + return kstd::failure(ext2_errc::invalid_root_inode); } return {}; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> + -> kstd::result> { if (!parent) { - return std::unexpected{vfs_errc::invalid_inode}; + return kstd::failure(vfs_errc::invalid_inode); } if (!parent->is_directory()) { - return std::unexpected{vfs_errc::not_a_directory}; + return kstd::failure(vfs_errc::not_a_directory); } auto * ext2_parent = static_cast(parent.get()); @@ -101,7 +101,7 @@ namespace kernel::filesystem::ext2 if (!global_block_number) { - return std::unexpected{global_block_number.error()}; + return kstd::failure(global_block_number.error()); } auto const block_offset = global_block_number.value() * block_size(); @@ -123,10 +123,10 @@ namespace kernel::filesystem::ext2 } } - return std::unexpected{vfs_errc::no_such_file_or_directory}; + return kstd::failure(vfs_errc::no_such_file_or_directory); } - auto filesystem::read_inode(uint32_t inode_number) const -> std::expected, kstd::error_code> + auto filesystem::read_inode(uint32_t inode_number) const -> kstd::result> { auto const inodes_per_group = m_superblock.inodes_per_group; auto const block_group_index = (inode_number - 1) / inodes_per_group; @@ -134,7 +134,7 @@ namespace kernel::filesystem::ext2 if (block_group_index >= m_block_group_descriptors.size()) { - return std::unexpected{ext2_errc::invalid_block_group_index}; + return kstd::failure(ext2_errc::invalid_block_group_index); } auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); @@ -201,7 +201,7 @@ namespace kernel::filesystem::ext2 return block_number; } - return std::unexpected{ext2_errc::invalid_block_index}; + return kstd::failure(ext2_errc::invalid_block_index); } auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index da7ae64e..58620596 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -8,13 +8,13 @@ #include #include +#include #include #include #include #include #include -#include #include namespace kernel::filesystem::ext2 @@ -55,8 +55,7 @@ namespace kernel::filesystem::ext2 @param backing_inode The backing inode to mount. @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) - -> std::expected override; + auto mount(kstd::shared_ptr const & backing_inode) -> kstd::result override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -65,7 +64,7 @@ namespace kernel::filesystem::ext2 @return A pointer to the found inode, or a null pointer if not found. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> override; + -> kstd::result> override; /** @brief Gets the size of a block in the filesystem. @@ -86,7 +85,7 @@ namespace kernel::filesystem::ext2 @return The global block number on success, an error otherwise. */ [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> std::expected; + -> kstd::result; private: struct indirect_level @@ -97,8 +96,7 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto indirect_levels() const -> std::array; - [[nodiscard]] auto read_inode(uint32_t inode_number) const - -> std::expected, kstd::error_code>; + [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result>; [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t; [[nodiscard]] auto inode_size() const -> uint16_t; diff --git a/kernel/kernel/filesystem/filesystem.cpp b/kernel/kernel/filesystem/filesystem.cpp index 1d15d410..ac0d0ebc 100644 --- a/kernel/kernel/filesystem/filesystem.cpp +++ b/kernel/kernel/filesystem/filesystem.cpp @@ -7,10 +7,10 @@ #include #include +#include #include #include -#include namespace kernel::filesystem { @@ -22,7 +22,7 @@ namespace kernel::filesystem } // namespace auto filesystem::probe_and_mount(kstd::shared_ptr const & backing_inode) - -> std::expected, kstd::error_code> + -> kstd::result> { if (!backing_inode) { @@ -38,10 +38,10 @@ namespace kernel::filesystem } } - return std::unexpected{vfs_errc::unsupported_filesystem}; + return kstd::failure(vfs_errc::unsupported_filesystem); } - auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> std::expected + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> kstd::result { if (!backing_inode) { @@ -49,7 +49,7 @@ namespace kernel::filesystem } m_backing_inode = backing_inode; - return {}; + return kstd::success(); } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/kernel/filesystem/filesystem.hpp b/kernel/kernel/filesystem/filesystem.hpp index f9521d1f..1264d00d 100644 --- a/kernel/kernel/filesystem/filesystem.hpp +++ b/kernel/kernel/filesystem/filesystem.hpp @@ -4,10 +4,10 @@ #include #include +#include #include #include -#include #include namespace kernel::filesystem @@ -32,7 +32,7 @@ namespace kernel::filesystem @warning Panics if @p backing_inode is null. */ auto static probe_and_mount(kstd::shared_ptr const & backing_inode) - -> std::expected, kstd::error_code>; + -> kstd::result>; /** @brief Initializes the filesystem with the given @p backing_inode. @@ -40,7 +40,7 @@ namespace kernel::filesystem representing the block device or another inode which contains the filesystem data.) @return Nothing on success, and error otherwise. */ - virtual auto mount(kstd::shared_ptr const & backing_inode) -> std::expected; + virtual auto mount(kstd::shared_ptr const & backing_inode) -> kstd::result; /** @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be @@ -51,7 +51,7 @@ namespace kernel::filesystem @return A pointer to the requested child inode, an error otherwise. */ [[nodiscard]] virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> = 0; + -> kstd::result> = 0; /** @brief Returns a reference to the root inode of the filesystem. diff --git a/kernel/kernel/filesystem/open_file_table.cpp b/kernel/kernel/filesystem/open_file_table.cpp index 1109249a..f33fed2e 100644 --- a/kernel/kernel/filesystem/open_file_table.cpp +++ b/kernel/kernel/filesystem/open_file_table.cpp @@ -6,11 +6,11 @@ #include #include +#include #include #include #include -#include #include namespace @@ -41,11 +41,11 @@ namespace kernel::filesystem } auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) - -> std::expected + -> kstd::result { if (!file_descriptor) { - return std::unexpected{vfs_errc::invalid_file_descriptor}; + return kstd::failure(vfs_errc::invalid_file_descriptor); } auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); @@ -59,25 +59,25 @@ namespace kernel::filesystem return m_open_files.size() - 1; } - auto open_file_table::file(size_t fd) const -> std::expected, kstd::error_code> + auto open_file_table::file(size_t fd) const -> kstd::result> { if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return std::unexpected{vfs_errc::invalid_file_descriptor}; + return kstd::failure(vfs_errc::invalid_file_descriptor); } - return m_open_files.at(fd); + return kstd::success(m_open_files.at(fd)); } - auto open_file_table::remove_file(size_t fd) -> std::expected + auto open_file_table::remove_file(size_t fd) -> kstd::result { if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { - return std::unexpected{vfs_errc::invalid_file_descriptor}; + return kstd::failure(vfs_errc::invalid_file_descriptor); } m_open_files.at(fd) = nullptr; - return 0; + return kstd::success(0); } } // namespace kernel::filesystem diff --git a/kernel/kernel/filesystem/open_file_table.hpp b/kernel/kernel/filesystem/open_file_table.hpp index efdaf578..a2c0e028 100644 --- a/kernel/kernel/filesystem/open_file_table.hpp +++ b/kernel/kernel/filesystem/open_file_table.hpp @@ -4,11 +4,11 @@ #include #include +#include #include #include #include -#include namespace kernel::filesystem { @@ -41,21 +41,21 @@ namespace kernel::filesystem @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & fd) -> std::expected; + auto add_file(kstd::shared_ptr const & fd) -> kstd::result; /** @brief Get a file from the open file table. @param fd The file descriptor index to retrieve. @return A pointer to the requested file descriptor, or a null pointer if not found. */ - [[nodiscard]] auto file(size_t fd) const -> std::expected, kstd::error_code>; + [[nodiscard]] auto file(size_t fd) const -> kstd::result>; /** @brief Remove a file from the open file table. @param fd The file descriptor index to remove. @return 0 on success, or -1 on failure. */ - auto remove_file(size_t fd) -> std::expected; + auto remove_file(size_t fd) -> kstd::result; private: open_file_table() = default; diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp index ea2ceedf..fef43e28 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.cpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp @@ -7,9 +7,9 @@ #include #include +#include #include -#include #include namespace kernel::filesystem::rootfs @@ -36,15 +36,15 @@ namespace kernel::filesystem::rootfs [[gnu::used]] constexpr auto registration = type_registration{}; - auto filesystem::mount(kstd::shared_ptr const &) -> std::expected + auto filesystem::mount(kstd::shared_ptr const &) -> kstd::result { m_root_inode = kstd::make_shared(); - return {}; + return kstd::success(); } auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const - -> std::expected, kstd::error_code> + -> kstd::result> { - return std::unexpected{vfs_errc::no_such_file_or_directory}; + return kstd::failure(vfs_errc::no_such_file_or_directory); } } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/rootfs/filesystem.hpp b/kernel/kernel/filesystem/rootfs/filesystem.hpp index e05cc808..8507d8b8 100644 --- a/kernel/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/kernel/filesystem/rootfs/filesystem.hpp @@ -5,11 +5,11 @@ #include #include +#include #include #include #include -#include #include namespace kernel::filesystem::rootfs @@ -27,8 +27,7 @@ namespace kernel::filesystem::rootfs @param backing_inode The backing inode to mount (not required by rootfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & backing_inode) - -> std::expected override; + auto mount(kstd::shared_ptr const & backing_inode) -> kstd::result override; /** @brief Looks up an inode by @p name within a @p parent directory. @@ -37,7 +36,7 @@ namespace kernel::filesystem::rootfs @return Always returns nullptr. */ [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> std::expected, kstd::error_code> override; + -> kstd::result> override; }; } // namespace kernel::filesystem::rootfs diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 40f2c3aa..9aa8b2df 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -88,12 +89,12 @@ namespace kernel::filesystem return *active_vfs; } - auto vfs::open(std::string_view path) -> std::expected + auto vfs::open(std::string_view path) -> kstd::result { auto resolved_path = resolve_path_internal(path); if (!resolved_path) { - return std::unexpected{resolved_path.error()}; + return kstd::failure(resolved_path.error()); } auto [dentry, mount] = resolved_path.value(); @@ -101,73 +102,73 @@ namespace kernel::filesystem return dentry; } - auto vfs::close(std::string_view path) -> std::expected + auto vfs::close(std::string_view path) -> kstd::result { if (auto mount = find_mount(path)) { mount.value()->decrement_ref_count(); - return {}; + return kstd::success(); } - return std::unexpected{vfs_errc::invalid_path}; + return kstd::failure(vfs_errc::invalid_path); } - auto vfs::mount(std::string_view source, std::string_view target) -> std::expected + auto vfs::mount(std::string_view source, std::string_view target) -> kstd::result { if (!path::is_valid_path(source) || !path::is_valid_path(target)) { - return std::unexpected{vfs_errc::invalid_path}; + return kstd::failure(vfs_errc::invalid_path); } auto resolved_target = resolve_path_internal(target); if (!resolved_target) { - return std::unexpected{resolved_target.error()}; + return kstd::failure(resolved_target.error()); } auto [mount_point_dentry, mount_context] = *resolved_target; auto resolved_source = resolve_path_internal(source); if (!resolved_source) { - return std::unexpected{resolved_source.error()}; + return kstd::failure(resolved_source.error()); } auto [source_dentry, source_mount_context] = *resolved_source; auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode()); if (!fs) { - return std::unexpected{fs.error()}; + return kstd::failure(fs.error()); } do_mount_internal(mount_point_dentry, mount_context, *fs, source_mount_context); - return {}; + return kstd::success(); } - auto vfs::unmount(std::string_view path) -> std::expected + auto vfs::unmount(std::string_view path) -> kstd::result { if (!path::is_valid_path(path)) { - return std::unexpected{vfs_errc::invalid_path}; + return kstd::failure(vfs_errc::invalid_path); } auto remove_result = m_mount_table.remove_mount(path); if (remove_result == mount_table::operation_result::removed) { - return {}; + return kstd::success(); } else if (remove_result == mount_table::operation_result::mount_not_found) { - return std::unexpected{vfs_errc::mount_point_not_found}; + return kstd::failure(vfs_errc::mount_point_not_found); } else if (remove_result == mount_table::operation_result::cannot_be_unmounted) { - return std::unexpected{vfs_errc::mount_busy}; + return kstd::failure(vfs_errc::mount_busy); } else if (remove_result == mount_table::operation_result::has_child_mounts) { - return std::unexpected{vfs_errc::has_child_mounts}; + return kstd::failure(vfs_errc::has_child_mounts); } - return std::unexpected{vfs_errc::unmount_failed}; + return kstd::failure(vfs_errc::unmount_failed); } auto vfs::do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs, @@ -195,12 +196,11 @@ namespace kernel::filesystem } } - auto vfs::resolve_path_internal(std::string_view path) const - -> std::expected, kstd::error_code> + auto vfs::resolve_path_internal(std::string_view path) const -> kstd::result> { if (!path::is_valid_absolute_path(path)) { - return std::unexpected{vfs_errc::invalid_path}; + return kstd::failure(vfs_errc::invalid_path); } auto current_mount = m_mount_table.find_mount("/"); @@ -224,7 +224,7 @@ namespace kernel::filesystem if (!current_dentry->get_inode()->is_directory()) { - return std::unexpected{vfs_errc::not_a_directory}; + return kstd::failure(vfs_errc::not_a_directory); } if (part == ".") @@ -261,7 +261,7 @@ namespace kernel::filesystem auto found_inode = current_fs->lookup(current_dentry->get_inode(), part); if (!found_inode) { - return std::unexpected{found_inode.error()}; + return kstd::failure(found_inode.error()); } next_dentry = kstd::make_shared(current_dentry, *found_inode, part); @@ -282,7 +282,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return std::unexpected{vfs_errc::too_many_symbolic_link_levels}; + return kstd::failure(vfs_errc::too_many_symbolic_link_levels); } kstd::vector buffer(constants::symlink_max_path_length); @@ -308,12 +308,12 @@ namespace kernel::filesystem return std::pair{current_dentry, current_mount}; } - auto vfs::resolve_path(std::string_view path) const -> std::expected + auto vfs::resolve_path(std::string_view path) const -> kstd::result { return resolve_path_internal(path).transform([](auto result) { return result.first; }); } - auto vfs::find_mount(std::string_view path) const -> std::expected + auto vfs::find_mount(std::string_view path) const -> kstd::result { return resolve_path_internal(path).transform([](auto result) { return result.second; }); } diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp index 2645fe14..9dfd3637 100644 --- a/kernel/kernel/filesystem/vfs.hpp +++ b/kernel/kernel/filesystem/vfs.hpp @@ -8,9 +8,9 @@ #include #include +#include #include -#include #include #include @@ -53,14 +53,14 @@ namespace kernel::filesystem @param path The path to the file to open. @return A shared pointer to the dentry on success or an error code on failure. */ - auto open(std::string_view path) -> std::expected; + auto open(std::string_view path) -> kstd::result; /** @brief Close a file by its associated @p path. @param path The path to the file to close. @return Nothing on success or an error code on failure. */ - auto close(std::string_view path) -> std::expected; + auto close(std::string_view path) -> kstd::result; /** @brief Mount a @p source path to a specific @p target path. @@ -68,14 +68,14 @@ namespace kernel::filesystem @param target The path where the filesystem should be mounted. @return Nothing on success or an error code on failure. */ - auto mount(std::string_view source, std::string_view target) -> std::expected; + auto mount(std::string_view source, std::string_view target) -> kstd::result; /** @brief Unmount the filesystem mounted at the specified @p path. @param path The path where the filesystem is mounted. @return Nothing on success or an error code on failure. */ - auto unmount(std::string_view path) -> std::expected; + auto unmount(std::string_view path) -> kstd::result; private: /** @@ -89,11 +89,11 @@ namespace kernel::filesystem * - find_mount() for the mount context only. */ [[nodiscard]] auto resolve_path_internal(std::string_view path) const - -> std::expected, kstd::error_code>; + -> kstd::result>; - [[nodiscard]] auto resolve_path(std::string_view path) const -> std::expected; + [[nodiscard]] auto resolve_path(std::string_view path) const -> kstd::result; - [[nodiscard]] auto find_mount(std::string_view path) const -> std::expected; + [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::result; auto do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs, mount_ptr const & source_mount = nullptr) -> void; diff --git a/libs/kstd/kstd/result.hpp b/libs/kstd/kstd/result.hpp new file mode 100644 index 00000000..7b027a8d --- /dev/null +++ b/libs/kstd/kstd/result.hpp @@ -0,0 +1,35 @@ +#ifndef KSTD_RESULT_HPP +#define KSTD_RESULT_HPP + +#include + +#include +#include +#include + +namespace kstd +{ + + template + using result = std::expected; + + template + requires(!std::is_void_v) + constexpr auto inline success(SuccessType && value) -> result> + { + return result>{std::in_place, std::forward(value)}; + } + + constexpr auto inline success() -> result + { + return result{std::in_place}; + } + + constexpr auto inline failure(error_code error) -> std::unexpected + { + return std::unexpected(error); + } + +} // namespace kstd + +#endif \ No newline at end of file -- cgit v1.2.3 From 55adee63740c489c7fe6e639d5e35d0bc46f1f8c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Jul 2026 18:39:45 +0200 Subject: kernel/vfs: expand error_code integration --- kernel/kapi/filesystem.cpp | 7 ++-- kernel/kernel/filesystem/devfs/inode.cpp | 6 ++- kernel/kernel/filesystem/devfs/inode.hpp | 6 ++- kernel/kernel/filesystem/device_inode.cpp | 5 ++- kernel/kernel/filesystem/device_inode.hpp | 7 ++-- kernel/kernel/filesystem/ext2/error.hpp | 8 ++++ kernel/kernel/filesystem/ext2/filesystem.cpp | 43 ++++++++++++++++++---- kernel/kernel/filesystem/ext2/filesystem.hpp | 2 +- kernel/kernel/filesystem/ext2/inode.cpp | 16 +++++--- kernel/kernel/filesystem/ext2/inode.hpp | 5 ++- kernel/kernel/filesystem/ext2/inode.tests.cpp | 14 ++++--- kernel/kernel/filesystem/inode.hpp | 6 ++- kernel/kernel/filesystem/open_file_descriptor.cpp | 31 ++++++++++++---- kernel/kernel/filesystem/open_file_descriptor.hpp | 5 ++- .../filesystem/open_file_descriptor.tests.cpp | 4 +- kernel/kernel/filesystem/open_file_table.cpp | 4 +- kernel/kernel/filesystem/open_file_table.hpp | 2 +- kernel/kernel/filesystem/rootfs/inode.cpp | 6 ++- kernel/kernel/filesystem/rootfs/inode.hpp | 5 ++- kernel/kernel/filesystem/vfs.cpp | 7 +++- kernel/kernel/filesystem/vfs.tests.cpp | 6 +-- kernel/kernel/main.cpp | 2 +- kernel/kernel/test_support/filesystem/inode.cpp | 6 ++- kernel/kernel/test_support/filesystem/inode.hpp | 6 ++- 24 files changed, 145 insertions(+), 64 deletions(-) diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index a7c8e403..efde1336 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -37,20 +37,19 @@ namespace kapi::filesystem .file(file_descriptor) .transform([](auto file) { return file->get_dentry()->absolute_path(); }) .and_then([](auto path) { return kernel::filesystem::vfs::get().close(path); }) - .and_then([=]() { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); }) - .transform([](auto) { return; }); + .and_then([=]() { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); }); } auto read(size_t file_descriptor, void * buffer, size_t size) -> std::expected { - return kernel::filesystem::open_file_table::get().file(file_descriptor).transform([=](auto descriptor) { + return kernel::filesystem::open_file_table::get().file(file_descriptor).and_then([=](auto descriptor) { return descriptor->read(buffer, size); }); } auto write(size_t file_descriptor, void const * buffer, size_t size) -> std::expected { - return kernel::filesystem::open_file_table::get().file(file_descriptor).transform([=](auto descriptor) { + return kernel::filesystem::open_file_table::get().file(file_descriptor).and_then([=](auto descriptor) { return descriptor->write(buffer, size); }); } diff --git a/kernel/kernel/filesystem/devfs/inode.cpp b/kernel/kernel/filesystem/devfs/inode.cpp index 7bbfbbed..2e4ee7de 100644 --- a/kernel/kernel/filesystem/devfs/inode.cpp +++ b/kernel/kernel/filesystem/devfs/inode.cpp @@ -1,15 +1,17 @@ #include +#include + #include namespace kernel::filesystem::devfs { - auto inode::read(void *, size_t, size_t) const -> size_t + auto inode::read(void *, size_t, size_t) const -> kstd::result { return 0; } - auto inode::write(void const *, size_t, size_t) -> size_t + auto inode::write(void const *, size_t, size_t) -> kstd::result { return 0; } diff --git a/kernel/kernel/filesystem/devfs/inode.hpp b/kernel/kernel/filesystem/devfs/inode.hpp index e4288918..3472079f 100644 --- a/kernel/kernel/filesystem/devfs/inode.hpp +++ b/kernel/kernel/filesystem/devfs/inode.hpp @@ -3,6 +3,8 @@ #include +#include + #include namespace kernel::filesystem::devfs @@ -20,7 +22,7 @@ namespace kernel::filesystem::devfs @param size Number of bytes requested. @return Number of bytes read (always 0 because this inode does not expose file data). */ - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> kstd::result override; /** @brief Writes to the devfs directory inode. @@ -29,7 +31,7 @@ namespace kernel::filesystem::devfs @param size Number of bytes requested. @return Number of bytes written (always 0 because writes are not supported for this inode). */ - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> kstd::result override; /** @brief Check if this inode represents a directory. diff --git a/kernel/kernel/filesystem/device_inode.cpp b/kernel/kernel/filesystem/device_inode.cpp index 091b5499..644e3df7 100644 --- a/kernel/kernel/filesystem/device_inode.cpp +++ b/kernel/kernel/filesystem/device_inode.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -20,7 +21,7 @@ namespace kernel::filesystem } } - auto device_inode::read(void * buffer, size_t offset, size_t size) const -> size_t + auto device_inode::read(void * buffer, size_t offset, size_t size) const -> kstd::result { if (m_device->is_block_device()) { @@ -32,7 +33,7 @@ namespace kernel::filesystem } } - auto device_inode::write(void const * buffer, size_t offset, size_t size) -> size_t + auto device_inode::write(void const * buffer, size_t offset, size_t size) -> kstd::result { if (m_device->is_block_device()) { diff --git a/kernel/kernel/filesystem/device_inode.hpp b/kernel/kernel/filesystem/device_inode.hpp index b461f225..5b22366a 100644 --- a/kernel/kernel/filesystem/device_inode.hpp +++ b/kernel/kernel/filesystem/device_inode.hpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -32,7 +33,7 @@ namespace kernel::filesystem @param size The number of bytes to read. @return The number of bytes read. */ - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> kstd::result override; /** @brief Write data to the device inode (and in the background from the associated device) from a @p buffer, starting @@ -42,7 +43,7 @@ namespace kernel::filesystem @param size The number of bytes to write. @return The number of bytes written. */ - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> kstd::result override; /** @brief Get the associated device. @@ -52,7 +53,7 @@ namespace kernel::filesystem /** @brief Check if this inode represents a device. - @return returns true, since this indoe is a device inode and represents a device. + @return returns true, since this inode is a device inode and represents a device. */ [[nodiscard]] auto is_device() const -> bool override; diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp index 885f82e6..79f40517 100644 --- a/kernel/kernel/filesystem/ext2/error.hpp +++ b/kernel/kernel/filesystem/ext2/error.hpp @@ -18,6 +18,8 @@ namespace kernel::filesystem::ext2 invalid_block_group_index, invalid_block_index, invalid_block_number, + failed_to_read_superblock, + failed_to_read_block_group_descriptors, }; namespace detail @@ -43,6 +45,10 @@ namespace kernel::filesystem::ext2 return "block index out of bounds"; case ext2_errc::invalid_block_number: return "block number out of bounds"; + case ext2_errc::failed_to_read_superblock: + return "failed to read superblock"; + case ext2_errc::failed_to_read_block_group_descriptors: + return "failed to read block group descriptors"; default: return "unknown ext2 error"; }; @@ -67,6 +73,8 @@ namespace kernel::filesystem::ext2 case ext2_errc::invalid_block_group_index: case ext2_errc::invalid_block_index: case ext2_errc::invalid_block_number: + case ext2_errc::failed_to_read_superblock: + case ext2_errc::failed_to_read_block_group_descriptors: if (condition.category() == kstd::generic_category()) { return condition.value() == static_cast(kstd::errc::io_error); diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 4195ad05..34ee1f75 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -50,7 +50,11 @@ namespace kernel::filesystem::ext2 { std::ignore = kernel::filesystem::filesystem::mount(backing_inode); - m_backing_inode->read(&m_superblock, constants::superblock_offset, sizeof(m_superblock)); + if (auto read_result = m_backing_inode->read(&m_superblock, constants::superblock_offset, sizeof(m_superblock)); + !read_result) + { + return kstd::failure(read_result.error()); + } if (m_superblock.magic != constants::magic_number) { @@ -62,8 +66,13 @@ namespace kernel::filesystem::ext2 m_block_group_descriptors = kstd::vector(num_block_groups); - m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset(), - num_block_groups * sizeof(block_group_descriptor)); + if (auto read_result = + m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset(), + num_block_groups * sizeof(block_group_descriptor)); + !read_result) + { + return kstd::failure(read_result.error()); + } if (auto root_inode = read_inode(constants::root_inode_number)) { @@ -105,7 +114,10 @@ namespace kernel::filesystem::ext2 } auto const block_offset = global_block_number.value() * block_size(); - m_backing_inode->read(buffer.data(), block_offset, block_size()); + if (auto read_result = m_backing_inode->read(buffer.data(), block_offset, block_size()); !read_result) + { + return kstd::failure(read_result.error()); + } auto const * entry = reinterpret_cast(buffer.data()); auto bytes_read = 0uz; @@ -143,7 +155,10 @@ namespace kernel::filesystem::ext2 auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size(); auto new_inode_data = inode_data{}; - m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); + if (auto read_result = m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); !read_result) + { + return kstd::failure(read_result.error()); + } return kstd::make_shared(this, new_inode_data); } @@ -186,7 +201,15 @@ namespace kernel::filesystem::ext2 auto const idx = inode_block_index / stride; inode_block_index %= stride; - block_number = read_block_number_at_index(block_number, idx); + if (auto read_result = read_block_number_at_index(block_number, idx); !read_result) + { + return kstd::failure(read_result.error()); + } + else + { + block_number = read_result.value(); + } + if (block_number == 0) { return 0; @@ -204,13 +227,17 @@ namespace kernel::filesystem::ext2 return kstd::failure(ext2_errc::invalid_block_index); } - auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t + auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result { uint32_t block_number_buffer = 0; auto const block_start_offset = block_number * block_size(); auto const number_start_address = block_start_offset + index * sizeof(uint32_t); - m_backing_inode->read(&block_number_buffer, number_start_address, sizeof(uint32_t)); + if (auto read_result = m_backing_inode->read(&block_number_buffer, number_start_address, sizeof(uint32_t)); + !read_result) + { + return kstd::failure(read_result.error()); + } return block_number_buffer; } diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index 58620596..5e5913bf 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -97,7 +97,7 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto indirect_levels() const -> std::array; [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result>; - [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t; + [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result; [[nodiscard]] auto inode_size() const -> uint16_t; [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t; diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 23344a7b..6dadbec5 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -23,7 +24,7 @@ namespace kernel::filesystem::ext2 } } - auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t + auto inode::read(void * buffer, size_t offset, size_t size) const -> kstd::result { auto const max_readable = this->size() - offset; auto const requested_size = std::min(size, max_readable); @@ -58,9 +59,14 @@ namespace kernel::filesystem::ext2 { auto const block_start_offset = block_number.value() * m_filesystem->block_size(); auto const read_offset = block_start_offset + in_block_offset; - - bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, - bytes_to_read); + auto const read_result = m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, + read_offset, bytes_to_read); + if (!read_result) + { + return kstd::failure(read_result.error()); + } + + bytes_read += read_result.value(); } block_index++; @@ -70,7 +76,7 @@ namespace kernel::filesystem::ext2 return bytes_read; } - auto inode::write(void const *, size_t, size_t) -> size_t + auto inode::write(void const *, size_t, size_t) -> kstd::result { kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; diff --git a/kernel/kernel/filesystem/ext2/inode.hpp b/kernel/kernel/filesystem/ext2/inode.hpp index 8b63f787..16cb04e0 100644 --- a/kernel/kernel/filesystem/ext2/inode.hpp +++ b/kernel/kernel/filesystem/ext2/inode.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -54,7 +55,7 @@ namespace kernel::filesystem::ext2 @param size Number of bytes requested. @return Number of bytes read. */ - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> kstd::result override; /** @brief Writes to the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. @@ -64,7 +65,7 @@ namespace kernel::filesystem::ext2 @param size Number of bytes requested. @return Number of bytes written. */ - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> kstd::result override; /** @brief Get the data associated with this inode. diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp index 325ba956..496e7c4f 100644 --- a/kernel/kernel/filesystem/ext2/inode.tests.cpp +++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp @@ -20,6 +20,8 @@ #include #include +// NOLINTBEGIN(readability-magic-numbers) + SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]") { GIVEN("an ext2 filesystem") @@ -112,7 +114,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in REQUIRE(bytes_read == 6); - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + auto const text = std::string_view{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(text == "info_1"); } @@ -159,7 +161,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); REQUIRE(bytes_read == data.size); - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + auto const text = std::string_view{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(text.substr(0, 5) == "Hello"); REQUIRE(std::ranges::all_of(text.substr(5, block_size - 5), [](char c) { return c == '\0'; })); REQUIRE(text.substr(2 * block_size, 6) == "World!"); @@ -200,7 +202,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); REQUIRE(bytes_read == data.size); - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + auto const text = std::string_view{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(text.substr(0, 5) == "Hello"); REQUIRE(std::ranges::all_of(text.substr(5, 12 * block_size - 5), [](char c) { return c == '\0'; })); REQUIRE(text.substr(12 * block_size, 4) == "Blub"); @@ -269,7 +271,7 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") auto const bytes_read = inode.read(buffer.data(), block_size - 6, buffer.size()); REQUIRE(bytes_read == 12); - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + auto const text = std::string_view{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(text == "Hello World!"); } } @@ -373,4 +375,6 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev REQUIRE(inode.size() == 256); } } -} \ No newline at end of file +} + +// NOLINTEND(readability-magic-numbers) \ No newline at end of file diff --git a/kernel/kernel/filesystem/inode.hpp b/kernel/kernel/filesystem/inode.hpp index b34b921e..cfddebf6 100644 --- a/kernel/kernel/filesystem/inode.hpp +++ b/kernel/kernel/filesystem/inode.hpp @@ -1,6 +1,8 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP +#include + #include namespace kernel::filesystem @@ -28,7 +30,7 @@ namespace kernel::filesystem @param size Number of bytes requested. @return Number of bytes read. */ - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto read(void * buffer, size_t offset, size_t size) const -> kstd::result = 0; /** @brief Writes to the inode into a @p buffer, starting at the specified @p offset and for a given @p size. This @@ -38,7 +40,7 @@ namespace kernel::filesystem @param size Number of bytes to write. @return Number of bytes written. */ - virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> kstd::result = 0; /** @brief Returns whether the inode is a directory. diff --git a/kernel/kernel/filesystem/open_file_descriptor.cpp b/kernel/kernel/filesystem/open_file_descriptor.cpp index bec484d7..ecf3e725 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.cpp +++ b/kernel/kernel/filesystem/open_file_descriptor.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -19,18 +20,32 @@ namespace kernel::filesystem } } - auto open_file_descriptor::read(void * buffer, size_t size) -> size_t + auto open_file_descriptor::read(void * buffer, size_t size) -> kstd::result { - auto read_bytes = m_dentry->get_inode()->read(buffer, m_offset, size); - m_offset += read_bytes; - return read_bytes; + if (auto result = m_dentry->get_inode()->read(buffer, m_offset, size); !result) + { + return kstd::failure(result.error()); + } + else + { + auto read_bytes = result.value(); + m_offset += read_bytes; + return read_bytes; + } } - auto open_file_descriptor::write(void const * buffer, size_t size) -> size_t + auto open_file_descriptor::write(void const * buffer, size_t size) -> kstd::result { - auto written_bytes = m_dentry->get_inode()->write(buffer, m_offset, size); - m_offset += written_bytes; - return written_bytes; + if (auto result = m_dentry->get_inode()->write(buffer, m_offset, size); !result) + { + return kstd::failure(result.error()); + } + else + { + auto written_bytes = result.value(); + m_offset += written_bytes; + return written_bytes; + } } auto open_file_descriptor::offset() const -> size_t diff --git a/kernel/kernel/filesystem/open_file_descriptor.hpp b/kernel/kernel/filesystem/open_file_descriptor.hpp index beb32ec6..f6899a31 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.hpp +++ b/kernel/kernel/filesystem/open_file_descriptor.hpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -34,7 +35,7 @@ namespace kernel::filesystem @param size The number of bytes to read. @return The number of bytes read. */ - auto read(void * buffer, size_t size) -> size_t; + auto read(void * buffer, size_t size) -> kstd::result; /** @brief Writes data to the open file descriptor from a @p buffer, starting at the current file offset and for a @@ -44,7 +45,7 @@ namespace kernel::filesystem @param size The number of bytes to write. @return The number of bytes written. */ - auto write(void const * buffer, size_t size) -> size_t; + auto write(void const * buffer, size_t size) -> kstd::result; /** @brief Returns the current file offset for this open file descriptor. diff --git a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp index a04fbaa5..06488ba0 100644 --- a/kernel/kernel/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/kernel/filesystem/open_file_descriptor.tests.cpp @@ -91,7 +91,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope REQUIRE(bytes_read == 7); REQUIRE(ofd->offset() == 7); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(buffer_as_str == "info_1\n"); } @@ -106,7 +106,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope REQUIRE(bytes_read_2 == buffer.size() / 2); REQUIRE(ofd->offset() == buffer.size()); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), *bytes_read_1 + *bytes_read_2}; REQUIRE(buffer_as_str == "info"); } } diff --git a/kernel/kernel/filesystem/open_file_table.cpp b/kernel/kernel/filesystem/open_file_table.cpp index f33fed2e..dea504e4 100644 --- a/kernel/kernel/filesystem/open_file_table.cpp +++ b/kernel/kernel/filesystem/open_file_table.cpp @@ -69,7 +69,7 @@ namespace kernel::filesystem return kstd::success(m_open_files.at(fd)); } - auto open_file_table::remove_file(size_t fd) -> kstd::result + auto open_file_table::remove_file(size_t fd) -> kstd::result { if (fd >= m_open_files.size() || m_open_files.at(fd) == nullptr) { @@ -77,7 +77,7 @@ namespace kernel::filesystem } m_open_files.at(fd) = nullptr; - return kstd::success(0); + return kstd::success(); } } // namespace kernel::filesystem diff --git a/kernel/kernel/filesystem/open_file_table.hpp b/kernel/kernel/filesystem/open_file_table.hpp index a2c0e028..7c9a0c0c 100644 --- a/kernel/kernel/filesystem/open_file_table.hpp +++ b/kernel/kernel/filesystem/open_file_table.hpp @@ -55,7 +55,7 @@ namespace kernel::filesystem @param fd The file descriptor index to remove. @return 0 on success, or -1 on failure. */ - auto remove_file(size_t fd) -> kstd::result; + auto remove_file(size_t fd) -> kstd::result; private: open_file_table() = default; diff --git a/kernel/kernel/filesystem/rootfs/inode.cpp b/kernel/kernel/filesystem/rootfs/inode.cpp index f64fb87c..e3e9d641 100644 --- a/kernel/kernel/filesystem/rootfs/inode.cpp +++ b/kernel/kernel/filesystem/rootfs/inode.cpp @@ -2,16 +2,18 @@ #include +#include + #include namespace kernel::filesystem::rootfs { - auto inode::read(void *, size_t, size_t) const -> size_t + auto inode::read(void *, size_t, size_t) const -> kstd::result { return 0; } - auto inode::write(void const *, size_t, size_t) -> size_t + auto inode::write(void const *, size_t, size_t) -> kstd::result { return 0; } diff --git a/kernel/kernel/filesystem/rootfs/inode.hpp b/kernel/kernel/filesystem/rootfs/inode.hpp index ee5b9012..777f13f9 100644 --- a/kernel/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/kernel/filesystem/rootfs/inode.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -23,7 +24,7 @@ namespace kernel::filesystem::rootfs @param size Number of bytes requested. @return Number of bytes read (always 0 because this inode does not expose file data). */ - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> kstd::result override; /** @brief Writes to the rootfs directory inode. @@ -32,7 +33,7 @@ namespace kernel::filesystem::rootfs @param size Number of bytes requested. @return Number of bytes written (always 0 because writes are not supported for this inode). */ - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> kstd::result override; /** @brief Check if this inode represents a directory. diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp index 9aa8b2df..d128bbf5 100644 --- a/kernel/kernel/filesystem/vfs.cpp +++ b/kernel/kernel/filesystem/vfs.cpp @@ -287,7 +287,12 @@ namespace kernel::filesystem kstd::vector buffer(constants::symlink_max_path_length); auto const bytes_read = next_dentry->get_inode()->read(buffer.data(), 0, buffer.size()); - auto const symbolic_link_path = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + if (!bytes_read) + { + return kstd::failure(bytes_read.error()); + } + + auto const symbolic_link_path = std::string_view{reinterpret_cast(buffer.data()), *bytes_read}; auto symbolic_link_parts = path::split(symbolic_link_path); kstd::vector symbolic_link_parts_vector(symbolic_link_parts.begin(), symbolic_link_parts.end()); diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp index 8ef24a15..c99cea90 100644 --- a/kernel/kernel/filesystem/vfs.tests.cpp +++ b/kernel/kernel/filesystem/vfs.tests.cpp @@ -350,7 +350,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS kstd::vector buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), *bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); REQUIRE(vfs.close(dentry.value()->absolute_path())); @@ -377,12 +377,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS kstd::vector sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); - std::string_view buffer_as_str{reinterpret_cast(sheep_buffer.data()), bytes_read}; + std::string_view buffer_as_str{reinterpret_cast(sheep_buffer.data()), *bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); kstd::vector goat_buffer(6); bytes_read = goat_1_ofd->read(goat_buffer.data(), goat_buffer.size()); - buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; + buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), *bytes_read}; REQUIRE(buffer_as_str == "goat_1"); REQUIRE(vfs.close(sheep_1.value()->absolute_path())); diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index 28429b84..8dce5143 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -147,7 +147,7 @@ auto main() -> int kstd::println("[OS] Virtual filesystem initialized."); // TODO BA-FS26 remove demo code? - // run_demo(); + run_demo(); kapi::system::panic("Returning from kernel main!"); } diff --git a/kernel/kernel/test_support/filesystem/inode.cpp b/kernel/kernel/test_support/filesystem/inode.cpp index 0c8d9563..fd8e4d69 100644 --- a/kernel/kernel/test_support/filesystem/inode.cpp +++ b/kernel/kernel/test_support/filesystem/inode.cpp @@ -2,16 +2,18 @@ #include +#include + #include namespace kernel::tests::filesystem { - auto inode::read(void *, size_t, size_t size) const -> size_t + auto inode::read(void *, size_t, size_t size) const -> kstd::result { return size; } - auto inode::write(void const *, size_t, size_t size) -> size_t + auto inode::write(void const *, size_t, size_t size) -> kstd::result { return size; } diff --git a/kernel/kernel/test_support/filesystem/inode.hpp b/kernel/kernel/test_support/filesystem/inode.hpp index 8a764375..da2e3e21 100644 --- a/kernel/kernel/test_support/filesystem/inode.hpp +++ b/kernel/kernel/test_support/filesystem/inode.hpp @@ -3,14 +3,16 @@ #include +#include + #include namespace kernel::tests::filesystem { struct inode : kernel::filesystem::inode { - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> kstd::result override; + auto write(void const * buffer, size_t offset, size_t size) -> kstd::result override; [[nodiscard]] auto is_regular() const -> bool override; }; -- cgit v1.2.3 From 9558074af8ceef4dc2c3b168a0263775f8a377e0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Jul 2026 19:22:01 +0200 Subject: kstd: flesh out system_error documentation --- libs/kstd/kstd/system_error.hpp | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp index 77d85172..09efb097 100644 --- a/libs/kstd/kstd/system_error.hpp +++ b/libs/kstd/kstd/system_error.hpp @@ -136,60 +136,93 @@ namespace kstd } }; + //! An error condition represents a generic, system-level error. + //! + //! Error conditions are used to represent errors to be tested for. struct error_condition { + //! Construct an error condition with a default value and category. constexpr error_condition() noexcept; + //! Construct an error condition with a specific value and category. + //! + //! @param value The error code value. + //! @param category The error category. constexpr error_condition(int value, error_category const & category) noexcept : m_value{value} , m_category{&category} {} + //! Construct an error condition from an error condition enumeration value. + //! + //! @tparam Enum The error condition enumeration type. + //! @param value The error condition enumeration value. template constexpr error_condition(Enum value) noexcept : error_condition{make_error_condition(value)} {} + //! Assign an error condition enumeration value to this error condition. + //! + //! @tparam Enum The error condition enumeration type. + //! @param value The error condition enumeration value. template constexpr auto operator=(Enum value) noexcept -> error_condition & { *this = make_error_condition(value); } + //! Assign a new value and category to this error condition. + //! + //! @param value The new error code value. + //! @param category The new error category. constexpr auto assign(int value, error_category const & category) noexcept -> void { m_value = value; m_category = &category; } + //! Clear this error condition. + //! + //! This sets the value to 0 and the category to the generic error category. constexpr auto clear() noexcept -> void; + //! Get the raw error code value of this error condition. [[nodiscard]] constexpr auto value() const noexcept -> int { return m_value; } + //! Get the error category of this error condition. [[nodiscard]] constexpr auto category() const noexcept -> error_category const & { return *m_category; } + //! Get the message associated with this error condition based on its value and category. [[nodiscard]] constexpr auto message() const noexcept -> std::string_view { return m_category->message(m_value); } + //! Check if this error condition holds an error. + //! + //! @return @p true iff. this error condition holds a non-zero value, @p false otherwise. [[nodiscard]] constexpr explicit operator bool() const noexcept { return m_value != 0; } + //! Check if two error conditions are equal. + //! + //! Error conditions are considered equal if they hold the same value and reference the same category. [[nodiscard]] constexpr auto friend operator==(error_condition const & lhs, error_condition const & rhs) noexcept -> bool { return lhs.category() == rhs.category() && lhs.value() == rhs.value(); } + //! Lexicographically compare two error conditions. [[nodiscard]] constexpr auto friend operator<=>(error_condition const & lhs, error_condition const & rhs) noexcept -> std::strong_ordering { @@ -206,64 +239,95 @@ namespace kstd error_category const * m_category{}; }; + //! An error code represents a specific, subsystem-level error. + //! + //! Error codes are used to represent errors that are specific to a subsystem. Equivalence between error codes and + //! error conditions can be defined per each error enumeration type. struct error_code { + //! Construct an error code with a default value and category. constexpr error_code() noexcept; + //! Construct an error code with a specific value and category. constexpr error_code(int value, error_category const & category) noexcept : m_value{value} , m_category{&category} {} + //! Construct an error code from an error code enumeration value. + //! + //! @tparam Enum The error code enumeration type. + //! @param value The error code enumeration value. template constexpr error_code(Enum value) noexcept : error_code{make_error_code(value)} {} + //! Assign an error code enumeration value to this error code. + //! + //! The category is chosen based on the enumeration type of the value. + //! @tparam Enum The error code enumeration type. + //! @param value The error code enumeration value. template constexpr auto operator=(Enum value) noexcept -> error_code & { return *this = make_error_code(value); } + //! Assign a new value and category to this error code. + //! + //! @param value The new error code value. + //! @param category The new error code category. constexpr auto assign(int value, error_category const & category) noexcept -> void { m_value = value; m_category = &category; } + //! Clear this error code. constexpr auto clear() noexcept -> void; + //! Get the raw error code value of this error code. [[nodiscard]] constexpr auto value() const noexcept -> int { return m_value; } + //! Get the error category of this error code. [[nodiscard]] constexpr auto category() const noexcept -> error_category const & { return *m_category; } + //! Get and error condition that is equivalent to this error code. [[nodiscard]] constexpr auto default_error_condition() const noexcept -> error_condition { return m_category->default_error_condition(m_value); } + //! Get the message associated with this error code based on its value and category. [[nodiscard]] constexpr auto message() const noexcept -> std::string_view { return m_category->message(m_value); } + //! Check if this error code holds an error. + //! + //! @return @p true iff. this error code holds a non-zero value, @p false otherwise. [[nodiscard]] constexpr explicit operator bool() const noexcept { return m_value != 0; } + //! Check if two error codes are equal. + //! + //! Error codes are considered equal if they hold the same value and reference the same category. [[nodiscard]] constexpr auto friend operator==(error_code const & lhs, error_code const & rhs) noexcept -> bool { return lhs.category() == rhs.category() && lhs.value() == rhs.value(); } + //! Lexicographically compare two error codes. [[nodiscard]] constexpr auto friend operator<=>(error_code const & lhs, error_code const & rhs) noexcept -> std::strong_ordering { @@ -358,6 +422,7 @@ namespace kstd wrong_protocol_type = posix::eprototype, }; + //! Mark the kstd::errc enumeration as an error condition enumeration. template<> struct is_error_condition_enum : std::true_type { @@ -476,11 +541,13 @@ namespace kstd } constexpr inline system_category_instance{}; } // namespace bits + //! Get a reference to the generic error category. constexpr auto inline generic_category() noexcept -> error_category const & { return bits::generic_category_instance; } + //! Get a reference to the system error category. constexpr auto inline system_category() noexcept -> error_category const & { return bits::system_category_instance; @@ -526,16 +593,21 @@ namespace kstd m_category = &system_category(); } + //! Create an error code from an kstd::errc enumeration value. [[nodiscard]] constexpr auto inline make_error_code(errc const value) noexcept -> error_code { return {static_cast(value), generic_category()}; } + //! Create an error condition from an kstd::errc enumeration value. [[nodiscard]] constexpr auto inline make_error_condition(errc const value) noexcept -> error_condition { return {static_cast(value), generic_category()}; } + //! Enable formatting of kstd::error_code + //! + //! There are currently no valid format specifiers for error codes. template<> struct formatter { -- cgit v1.2.3