diff options
34 files changed, 905 insertions, 347 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index bebda51..80844ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,10 @@ "--clang-tidy", "--header-insertion=iwyu" ], + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.patterns": { + "*.hpp": "${capture}.cpp, ${capture}.test.cpp" + }, "files.associations": { "**/kstd/include/kstd/**": "cpp", }, @@ -32,6 +36,7 @@ "crtc", "crtp", "efer", + "FACS", "functors", "hhdm", "idtr", @@ -75,5 +80,5 @@ "env": "${envObject}", "environment": "${envObjArray}", "sourceFileMap": "${sourceFileMapObj}" - } + }, }
\ No newline at end of file diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 22936c2..965998c 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -23,8 +23,8 @@ namespace kapi::cpu namespace { - constexpr auto candidate_flags = ::acpi::processor_local_apic::flags::processor_enabled // - | ::acpi::processor_local_apic::flags::online_capable; + constexpr auto candidate_flags = ::acpi::processor_local_apic_entry::flags::processor_enabled // + | ::acpi::processor_local_apic_entry::flags::online_capable; } auto init() -> void @@ -52,7 +52,7 @@ namespace kapi::cpu auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto madt = kapi::acpi::get_table<::acpi::madt_table_signature>(); + auto madt = kapi::acpi::get_table<::acpi::table_signature_v<::acpi::madt>>(); if (!madt) { kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); @@ -62,10 +62,8 @@ namespace kapi::cpu auto lapic_entries = *madt | std::views::filter([](auto const & entry) { return entry.type() == ::acpi::madt_entry::type::processor_local_apic; }) | std::views::transform([](auto const & entry) { - return static_cast<::acpi::processor_local_apic const &>(entry); - }) | std::views::filter([](auto const & entry) { - return static_cast<bool>(entry.active_flags() & candidate_flags); - }); + return static_cast<::acpi::processor_local_apic_entry const &>(entry); + }) | std::views::filter([](auto const & entry) { return static_cast<bool>(entry.flags() & candidate_flags); }); auto bsp_found = false; auto core_count = 0uz; @@ -77,8 +75,8 @@ namespace kapi::cpu auto is_bsp = !bsp_found; bsp_found = true; auto core = kstd::make_unique<devices::cpu::core>(core_major, core_count, apic.processor_id(), is_bsp); - core->add_child(kstd::make_unique<arch::devices::local_apic>(interrupt_controller_major, core_count, - apic.apic_id(), local_apic_address, is_bsp)); + core->add_child(kstd::make_unique<arch::devices::local_apic>(interrupt_controller_major, core_count, apic.id(), + local_apic_address, is_bsp)); cpu_bus->add_child(std::move(core)); ++core_count; } diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 2835496..b607ee0 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -23,7 +23,7 @@ namespace kapi::acpi //! //! @param signature The signature of the table to get. //! @return A pointer to the table if found, nullptr otherwise. - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; //! Get a type-cast pointer to an ACPI table by its signature. //! diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index 420b44a..860d609 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -18,12 +18,12 @@ namespace kernel::acpi auto load_tables() -> bool; - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; private: ::acpi::rsdp const * m_sdp{}; - ::acpi::sdt const * m_rsdt{}; - kstd::flat_map<std::string_view, ::acpi::sdt const *> m_tables{}; + ::acpi::table_header const * m_rsdt{}; + kstd::flat_map<std::string_view, ::acpi::table_header const *> m_tables{}; bool m_extended{}; }; diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index df2bf05..5a2f227 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -32,7 +32,7 @@ namespace kapi::acpi return manager->load_tables(); } - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const> { return manager->get_table(signature); } diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 501ce92..5876799 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -8,8 +8,10 @@ #include <acpi/acpi.hpp> +#include <algorithm> #include <cstddef> #include <cstdint> +#include <ranges> #include <string_view> namespace kernel::acpi @@ -32,7 +34,7 @@ namespace kernel::acpi } auto physical_extended_table_address = kapi::memory::physical_address{xsdp->table_address()}; auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); - m_rsdt = static_cast<::acpi::sdt const *>(linear_extended_table_address); + m_rsdt = static_cast<::acpi::xsdt const *>(linear_extended_table_address); m_extended = true; } else @@ -43,7 +45,7 @@ namespace kernel::acpi } auto physical_root_table_address = kapi::memory::physical_address{m_sdp->table_address()}; auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); - m_rsdt = static_cast<::acpi::sdt const *>(linear_root_table_address); + m_rsdt = static_cast<::acpi::rsdt const *>(linear_root_table_address); } } @@ -54,29 +56,12 @@ namespace kernel::acpi kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); } - auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); - auto entry_count = (m_rsdt->length().value - sizeof(::acpi::sdt)) / entry_size; - auto entries_base = reinterpret_cast<std::byte const *>(m_rsdt) + sizeof(::acpi::sdt); + auto check_and_register_table = [&](auto table_address) -> void { + auto physical_table_address = kapi::memory::physical_address{reinterpret_cast<std::uintptr_t>(table_address)}; + auto mapped_table = kapi::memory::hhdm_to_linear(physical_table_address); + auto table = static_cast<::acpi::table_header const *>(mapped_table); - for (std::size_t i = 0; i < entry_count; ++i) - { - auto physical_table_address = kapi::memory::physical_address{}; - - if (m_extended) - { - auto entry = reinterpret_cast<std::uint64_t const *>(entries_base + (i * entry_size)); - physical_table_address = kapi::memory::physical_address{*entry}; - } - else - { - auto entry = reinterpret_cast<std::uint32_t const *>(entries_base + (i * entry_size)); - physical_table_address = kapi::memory::physical_address{*entry}; - } - - auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); - auto table = static_cast<::acpi::sdt const *>(linear_table_address); - - if (!::acpi::validate_checksum({reinterpret_cast<std::byte const *>(table), table->length().value})) + if (!::acpi::validate_checksum({static_cast<std::byte const *>(mapped_table), table->length().value})) { kstd::println(kstd::print_sink::stderr, "[OS:ACPI] Invalid table checksum!"); } @@ -85,12 +70,25 @@ namespace kernel::acpi kstd::println("[OS:ACPI] Found '{}' ACPI table", table->signature()); m_tables.emplace(table->signature(), table); } + }; + + if (m_extended) + { + auto xsdt = static_cast<::acpi::xsdt const *>(m_rsdt); + std::ranges::for_each(*xsdt | std::views::transform([](auto const & entry) { return entry.address(); }), + check_and_register_table); + } + else + { + auto rsdt = static_cast<::acpi::rsdt const *>(m_rsdt); + std::ranges::for_each(*rsdt | std::views::transform([](auto const & entry) { return entry.address(); }), + check_and_register_table); } return !m_tables.empty(); } - auto manager::get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> + auto manager::get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const> { if (m_tables.contains(signature)) { diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 30e1aca..55d5b54 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -19,10 +19,12 @@ file(GLOB_RECURSE ACPI_HEADERS ) target_sources("acpi" PRIVATE - "acpi/checksum.cpp" - "acpi/madt.cpp" + "acpi/common/checksum.cpp" + "acpi/common/table_header.cpp" + "acpi/data/madt.cpp" + "acpi/data/rsdt.cpp" + "acpi/data/xsdt.cpp" "acpi/pointers.cpp" - "acpi/sdt.cpp" ) target_sources("acpi" PUBLIC @@ -43,6 +45,8 @@ if(NOT CMAKE_CROSSCOMPILING) "basic_madt" "basic_rsdt" "basic_rsdp" + "basic_xsdt" + "table_header" ) foreach(TABLE IN LISTS TEST_TABLES) @@ -58,7 +62,10 @@ if(NOT CMAKE_CROSSCOMPILING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") add_executable("acpi_tests" - "acpi/madt.test.cpp" + "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" "test_data/tables.S" diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index fb358cc..0da44a8 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -1,10 +1,12 @@ #ifndef ACPI_ACPI_HPP #define ACPI_ACPI_HPP -#include <acpi/checksum.hpp> // IWYU pragma: export -#include <acpi/madt.hpp> // IWYU pragma: export -#include <acpi/pointers.hpp> // IWYU pragma: export -#include <acpi/sdt.hpp> // IWYU pragma: export -#include <acpi/table_type.hpp> // IWYU pragma: export +#include <acpi/common/checksum.hpp> // IWYU pragma: export +#include <acpi/common/table_signature.hpp> // IWYU pragma: export +#include <acpi/common/table_type.hpp> // IWYU pragma: export +#include <acpi/data/madt.hpp> // IWYU pragma: export +#include <acpi/data/rsdt.hpp> // IWYU pragma: export +#include <acpi/data/xsdt.hpp> // IWYU pragma: export +#include <acpi/pointers.hpp> // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/common/basic_table.hpp b/libs/acpi/acpi/common/basic_table.hpp new file mode 100644 index 0000000..33f23d5 --- /dev/null +++ b/libs/acpi/acpi/common/basic_table.hpp @@ -0,0 +1,35 @@ +#ifndef ACPI_COMMON_BASIC_TABLE_HPP +#define ACPI_COMMON_BASIC_TABLE_HPP + +#include <acpi/common/checksum.hpp> +#include <acpi/common/table_header.hpp> +#include <acpi/common/table_signature.hpp> + +#include <cstddef> +#include <span> + +namespace acpi +{ + + template<typename TableType> + struct basic_table : table_header + { + [[nodiscard]] auto validate_checksum() const noexcept -> bool + { + return acpi::validate_checksum(as_span()); + } + + [[nodiscard]] auto as_span() const noexcept -> std::span<std::byte const> + { + return {reinterpret_cast<std::byte const *>(this), length().value}; + } + + [[nodiscard]] auto validate() const noexcept -> bool + { + return signature() == table_signature_v<TableType> && validate_checksum(); + } + }; + +} // namespace acpi + +#endif
\ No newline at end of file diff --git a/libs/acpi/acpi/checksum.cpp b/libs/acpi/acpi/common/checksum.cpp index 56275c8..09425dd 100644 --- a/libs/acpi/acpi/checksum.cpp +++ b/libs/acpi/acpi/common/checksum.cpp @@ -1,4 +1,4 @@ -#include <acpi/checksum.hpp> +#include <acpi/common/checksum.hpp> #include <algorithm> #include <cstddef> diff --git a/libs/acpi/acpi/checksum.hpp b/libs/acpi/acpi/common/checksum.hpp index a92c242..a92c242 100644 --- a/libs/acpi/acpi/checksum.hpp +++ b/libs/acpi/acpi/common/checksum.hpp diff --git a/libs/acpi/acpi/common/table_header.cpp b/libs/acpi/acpi/common/table_header.cpp new file mode 100644 index 0000000..c69ff5d --- /dev/null +++ b/libs/acpi/acpi/common/table_header.cpp @@ -0,0 +1,130 @@ +#include <kstd/cstring> +#include <kstd/units> + +#include <acpi/common/table_header.hpp> + +#include <array> +#include <cstddef> +#include <cstdint> +#include <span> +#include <string_view> + +namespace acpi +{ + + struct common_table_header_data + { + std::array<char, 4> signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array<char, 6> oem_id; + std::array<char, 8> oem_table_id; + std::uint32_t oem_revision; + std::array<char, 4> creator_id; + std::uint32_t creator_revision; + }; + + static_assert(sizeof(common_table_header_data) == common_table_header_size); + + auto table_header::creator_revision() const noexcept -> decltype(common_table_header_data::creator_revision) + { + using type = decltype(common_table_header_data::creator_revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, creator_revision); + + auto const data = std::span<std::byte const, size>{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto table_header::creator_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::creator_id); + constexpr auto offset = offsetof(common_table_header_data, creator_id); + + auto const base = reinterpret_cast<char const *>(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto table_header::length() const noexcept -> kstd::units::bytes + { + using type = decltype(common_table_header_data::length); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, length); + + auto const data = std::span<std::byte const, size>{m_data.data() + offset, size}; + auto raw_value = type{}; + + kstd::libc::memcpy(&raw_value, data.data(), size); + + return kstd::units::bytes{raw_value}; + } + + auto table_header::oem_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::oem_id); + constexpr auto offset = offsetof(common_table_header_data, oem_id); + + auto const base = reinterpret_cast<char const *>(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto table_header::oem_table_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::oem_table_id); + constexpr auto offset = offsetof(common_table_header_data, oem_table_id); + + auto const base = reinterpret_cast<char const *>(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto table_header::oem_revision() const noexcept -> decltype(common_table_header_data::oem_revision) + { + using type = decltype(common_table_header_data::oem_revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, oem_revision); + + auto const data = std::span<std::byte const, size>{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto table_header::revision() const noexcept -> decltype(common_table_header_data::revision) + { + using type = decltype(common_table_header_data::revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, revision); + + auto const data = std::span<std::byte const, size>{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto table_header::signature() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::signature); + constexpr auto offset = offsetof(common_table_header_data, signature); + + auto const base = reinterpret_cast<char const *>(m_data.data()); + + return std::string_view{base + offset, size}; + } + +} // namespace acpi
\ No newline at end of file diff --git a/libs/acpi/acpi/common/table_header.hpp b/libs/acpi/acpi/common/table_header.hpp new file mode 100644 index 0000000..471fed8 --- /dev/null +++ b/libs/acpi/acpi/common/table_header.hpp @@ -0,0 +1,56 @@ +#ifndef ACPI_COMMON_TABLE_HEADER_HPP +#define ACPI_COMMON_TABLE_HEADER_HPP + +// IWYU pragma: private, include <acpi/acpi.hpp> + +#include <kstd/units> + +#include <array> +#include <cstddef> +#include <cstdint> +#include <string_view> + +namespace acpi +{ + + //! The size of the common table header as defined in the ACPI specification. + constexpr auto common_table_header_size = 36; + + //! The common header for all ACPI tables, except the FACS. + //! + //! Multibyte fields in the header are not guaranteed to be naturally aligned by the firmware. Therefore, no direct + //! access to multibyte fields is provided. Instead, the provided member functions handle alignment of the multibyte + //! values and thus provide a safe interface to the header fields. + struct table_header + { + //! Get the revision of the utility used to create this table. + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + + //! Get the vendor ID of the utility used to create this table. + [[nodiscard]] auto creator_id() const noexcept -> std::string_view; + + //! Get the length of the entire table, including this header. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the ID of the OEM. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the OEMs revision number of this table. + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + + //! Get the OEMs ID of this table. + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + + //! Get the revision number of the structure of this table. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this table. + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + private: + std::array<std::byte, common_table_header_size> m_data; + }; + +} // namespace acpi + +#endif
\ No newline at end of file diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp new file mode 100644 index 0000000..d6976b3 --- /dev/null +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -0,0 +1,58 @@ +#include <kstd/units> + +#include <acpi/common/table_header.hpp> +#include <catch2/catch_test_macros.hpp> +#include <test_data/tables.hpp> + +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<acpi::table_header const *>(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<acpi::table_header>); + } + + 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_signature.hpp b/libs/acpi/acpi/common/table_signature.hpp new file mode 100644 index 0000000..f0c2d7a --- /dev/null +++ b/libs/acpi/acpi/common/table_signature.hpp @@ -0,0 +1,17 @@ +#ifndef ACPI_COMMON_TABLE_SIGNATURE_HPP +#define ACPI_COMMON_TABLE_SIGNATURE_HPP + +// IWYU pragma: private, include <acpi/acpi.hpp> + +namespace acpi +{ + + template<typename TableType> + struct table_signature; + + template<typename TableType> + constexpr auto table_signature_v = table_signature<TableType>::value; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/table_type.hpp b/libs/acpi/acpi/common/table_type.hpp index 7fdd7e3..bc427c7 100644 --- a/libs/acpi/acpi/table_type.hpp +++ b/libs/acpi/acpi/common/table_type.hpp @@ -1,5 +1,5 @@ -#ifndef ACPI_TABLE_TYPE_HPP -#define ACPI_TABLE_TYPE_HPP +#ifndef ACPI_COMMON_TABLE_TYPE_HPP +#define ACPI_COMMON_TABLE_TYPE_HPP // IWYU pragma: private, include <acpi/acpi.hpp> diff --git a/libs/acpi/acpi/common/vla_table.hpp b/libs/acpi/acpi/common/vla_table.hpp new file mode 100644 index 0000000..a65a28e --- /dev/null +++ b/libs/acpi/acpi/common/vla_table.hpp @@ -0,0 +1,115 @@ +#ifndef ACPI_COMMON_VLA_TABLE_HPP +#define ACPI_COMMON_VLA_TABLE_HPP + +#include <acpi/common/basic_table.hpp> + +#include <cstddef> +#include <iterator> + +namespace acpi +{ + + template<typename EntryType> + struct vla_table_iterator + { + using iterator_category = std::forward_iterator_tag; + using value_type = EntryType; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + constexpr vla_table_iterator() noexcept = default; + + constexpr vla_table_iterator(pointer entry, pointer limit) noexcept + : m_entry{entry} + , m_limit{limit} + {} + + constexpr auto operator++() noexcept -> vla_table_iterator & + { + auto decayed = std::bit_cast<std::byte *>(m_entry); + decayed += m_entry->length(); + m_entry = std::bit_cast<pointer>(decayed); + return *this; + } + + constexpr auto operator++(int) noexcept -> vla_table_iterator + { + auto copy = *this; + ++*this; + return copy; + } + + constexpr auto operator*() const noexcept -> reference + { + return *m_entry; + } + + constexpr auto operator->() const noexcept -> pointer + { + return m_entry; + } + + constexpr auto operator==(vla_table_iterator const & other) const noexcept -> bool + { + return m_entry == other.m_entry || (is_end() && other.is_end()); + } + + private: + [[nodiscard]] constexpr auto is_end() const noexcept -> bool + { + return m_entry == m_limit; + } + + pointer m_entry{}; + pointer m_limit{}; + }; + + template<typename EntryType, typename TableType> + struct vla_table : basic_table<TableType> + { + using entry_type = EntryType; + using iterator = vla_table_iterator<EntryType>; + using const_iterator = vla_table_iterator<EntryType const>; + + [[nodiscard]] auto begin() noexcept -> iterator + { + auto base = std::bit_cast<std::byte *>(this); + base += sizeof(TableType); + auto limit = std::bit_cast<std::byte *>(this); + limit += this->length().value; + return iterator{std::bit_cast<entry_type *>(base), std::bit_cast<entry_type *>(limit)}; + } + + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{}; + } + + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + auto base = std::bit_cast<std::byte *>(this); + base += sizeof(TableType); + |
