From b7a37a1899772e16ce5550c6be3ff9cfd9825fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20Gm=C3=BCr?= Date: Thu, 27 Feb 2025 08:20:21 +0000 Subject: Add inital empty files for GDT --- .../include/arch/context_switching/descriptor_table/access_bytes.hpp | 0 arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp | 0 .../arch/context_switching/descriptor_table/global_descriptor_table.hpp | 0 .../arch/context_switching/descriptor_table/segment_descriptor.hpp | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp new file mode 100644 index 0000000..e69de29 diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp new file mode 100644 index 0000000..e69de29 diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/global_descriptor_table.hpp new file mode 100644 index 0000000..e69de29 diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3 From 8d14c729c43ee555c240a043e3909617e4fa5043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20Gm=C3=BCr?= Date: Thu, 27 Feb 2025 09:24:17 +0000 Subject: Add files to cmake and implement gdt flags --- arch/x86_64/CMakeLists.txt | 9 +++ .../descriptor_table/gdt_flags.hpp | 68 ++++++++++++++++++++++ .../descriptor_table/gdt_flags.cpp | 6 ++ 3 files changed, 83 insertions(+) create mode 100644 arch/x86_64/src/context_switching/descriptor_table/gdt_flags.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index e9f8d5f..8cb1ca2 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -81,6 +81,15 @@ target_sources("_exception" PRIVATE "src/exception_handling/pure_virtual.cpp" ) +#[============================================================================[ +# The Context switching Library +#]============================================================================] + +target_sources("_context" PRIVATE + "src/context_switching/descriptor_table/gdt_flags.cpp" + "src/context_switching/descriptor_table/access_byte.cpp" +) + #[============================================================================[ # The Bootable ISO Image #]============================================================================] diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp index e69de29..83a7c06 100644 --- a/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/gdt_flags.hpp @@ -0,0 +1,68 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP + +#include +#include + +namespace teachos::arch::context_switching::descriptor_table +{ + /** + * @brief Defines helper function for all states that the global descriptor flags of a segment descriptor can + * have. + * + * @note See https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-94076/index.html for more information. + */ + struct gdt_flags + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint8_t + { + GRANULARITY = 1U << 0U, ///< Indicates the size the Limit value in the segement descriptor is scaled by 1 Byte + ///< blocks if the bit is not set or by 4 KiB blocks if the bit is set. + PROTECTED_MODE_SEGMENT_16_OR_32_BIT_SIZE = 1U << 1U, ///< Descriptor defines either a 16-bit protected mdoe + ///< segment if bit is not set or 32-bit if the bit is set. + ENABLE_LONG_MODE = 1U << 2U ///< If set the descriptor defines a 64-bit code segment, when set + ///< PROTECTED_MODE_SEGMENT_16_OR_32_BIT_SIZE should always be clear. For other types + ///< of segments bit should not be set. + }; + + /** + * @brief Constructor. + * + * @param flags Actual value read from the elf section header, which should be converted into a std::bitset, to + * allow reading the state of single bits more easily. Only the first 3 bit of the value will actually be converted + * into the std::bitset, because the latter 4 bits are not relevant and the 4th bit is reserved. + */ + explicit gdt_flags(uint8_t flags) + : flags(flags >> 5U) + { + // Nothing to do + } + + /** + * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. + * + * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng + * std::bitset. Any additional bits that are set are not relevant. + * + * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. + * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. + */ + auto contains_flags(std::bitset<4U> other) const -> bool; + + /** + * @brief Allows to compare the underlying std::bitset of two instances. + * + * @param other Other instance that we want to compare with. + * @return Whether the underlying std::bitset of both types is the same. + */ + auto operator==(gdt_flags const & other) const -> bool = default; + + private: + std::bitset<4U> flags; ///< Underlying bitset used to read the flags from. + }; +} // namespace teachos::arch::context_switching::descriptor_table + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP diff --git a/arch/x86_64/src/context_switching/descriptor_table/gdt_flags.cpp b/arch/x86_64/src/context_switching/descriptor_table/gdt_flags.cpp new file mode 100644 index 0000000..8a05956 --- /dev/null +++ b/arch/x86_64/src/context_switching/descriptor_table/gdt_flags.cpp @@ -0,0 +1,6 @@ +#include "arch/context_switching/descriptor_table/gdt_flags.hpp" + +namespace teachos::arch::context_switching::descriptor_table +{ + auto gdt_flags::contains_flags(std::bitset<4U> other) const -> bool { return (flags & other) == other; } +} // namespace teachos::arch::context_switching::descriptor_table -- cgit v1.2.3 From d8a8efe3e8d90ec83069d1c934ff319626e87a2d Mon Sep 17 00:00:00 2001 From: Fabian Imhof Date: Thu, 27 Feb 2025 10:13:35 +0000 Subject: add descriptor_table access_byte --- .../descriptor_table/access_byte.hpp | 87 ++++++++++++++++++++++ .../descriptor_table/access_bytes.hpp | 0 arch/x86_64/src/boot/boot.s | 4 +- .../descriptor_table/access_byte.cpp | 14 ++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp delete mode 100644 arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp create mode 100644 arch/x86_64/src/context_switching/descriptor_table/access_byte.cpp (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp new file mode 100644 index 0000000..b3bce11 --- /dev/null +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp @@ -0,0 +1,87 @@ + +#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP +#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP + +#include +#include +#include + +namespace teachos::arch::context_switching::descriptor_table +{ + enum class privilege_level + { + KERNEL = 0, + ADMIN = 1, + PRIVILEGED_USER = 2, + USER = 3 + }; + + /// @brief Defines the access byte of a Descriptor Table + struct access_byte + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint8_t + { + PRESENT = 1U << 0U, ///< Present bit; Allows an entry to refer to a valid segment. + ///< Must be set (1) for any valid segment. + PRIVILEGE_LEVEL = 1U << 1U | 1 << 2U, ///< Descriptor privilege level field. + ///< Contains the CPU Privilege level of the segment. + DESCRIPTOR_TYPE = 1U << 3U, ///< Defines a system segment (if 0) or a code/data segment (if 1). + EXECUTABLE = 1U << 4U, ///< Defines a data segment (if 0) or a code segment which can be executed from (if 1). + CONFORMING = 1U << 5U, ///< For data selectors: Direction bit + ///< Grows up (if 0), grows down (if 1) + ///< For code selectors: Conforming bit + ///< Code can only be executed from the DPL ring (if 0) + ///< Code can be executed from equal or lower DPL ring (if 1) + READ_WRITE = 1U << 6U, ///< For code segments: Readable bit + ///< For data segments: Writeable bit (read is always allowed) + ACCESSED = 1U << 7U ///< Set, when the segment is accessed. If GDT descriptor is stored in read only pages and + ///< this bit is set to 0, the CPU trying to set this bit will trigger a page fault. + }; + + /** + * @brief Constructor. + * + * @param flags Actual value read from the elf section header, which should be converted into a std::bitset, to + * allow reading the state of single bits more easily. + */ + explicit access_byte(uint8_t flags) + : flags(flags) + { + // Nothing to do + } + + /** + * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset. + * + * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng + * std::bitset. Any additional bits that are set are not relevant. + * + * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. + * @return Whether the given flags are a subset or equivalent with the underlying std::bitset. + */ + auto contains_flags(std::bitset<8U> other) const -> bool; + + /** + * @brief Returns the privilege level of the current access_byte + * + * @return privilege_level + */ + auto get_privilege_level() const -> privilege_level; + + /** + * @brief Allows to compare the underlying std::bitset of two instances. + * + * @param other Other instance that we want to compare with. + * @return Whether the underlying std::bitset of both types is the same. + */ + auto operator==(access_byte const & other) const -> bool = default; + + private: + std::bitset<8U> flags; ///< Underlying bitset used to read the flags from + }; +} // namespace teachos::arch::context_switching::descriptor_table + +#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/access_bytes.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s index 2197dce..dbea42a 100644 --- a/arch/x86_64/src/boot/boot.s +++ b/arch/x86_64/src/boot/boot.s @@ -197,10 +197,10 @@ _start: call enable_paging call enable_sse - cli // Clears the interrupt flag during the GDT setup + cli /* Clears the interrupt flag during the GDT setup */ lgdt (global_descriptor_table_pointer) jmp $global_descriptor_table_code,$_transition_to_long_mode - // The interrupt flag is set in cpp after setting up the GDT + /* The interrupt flag is set in cpp after setting up the GDT */ call halt diff --git a/arch/x86_64/src/context_switching/descriptor_table/access_byte.cpp b/arch/x86_64/src/context_switching/descriptor_table/access_byte.cpp new file mode 100644 index 0000000..15a0947 --- /dev/null +++ b/arch/x86_64/src/context_switching/descriptor_table/access_byte.cpp @@ -0,0 +1,14 @@ +#include "arch/context_switching/descriptor_table/access_byte.hpp" + +#include + +namespace teachos::arch::context_switching::descriptor_table +{ + auto access_byte::contains_flags(std::bitset<8U> other) const -> bool { return (flags & other) == other; } + + auto access_byte::get_privilege_level() const -> privilege_level + { + constexpr uint8_t PRIVILEGE_MASK = 0b0110'0000; + return static_cast(flags.to_ulong() & PRIVILEGE_MASK); + } +} // namespace teachos::arch::context_switching::descriptor_table -- cgit v1.2.3 From 2f7db60472629dbd191c00e6010d120a046f5ab9 Mon Sep 17 00:00:00 2001 From: Fabian Imhof Date: Thu, 27 Feb 2025 13:09:55 +0000 Subject: start segment_descriptor --- .../descriptor_table/access_byte.hpp | 6 ++-- .../descriptor_table/segment_descriptor.hpp | 41 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp index b3bce11..36b22ce 100644 --- a/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/access_byte.hpp @@ -1,6 +1,6 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP +#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP +#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP #include #include @@ -84,4 +84,4 @@ namespace teachos::arch::context_switching::descriptor_table }; } // namespace teachos::arch::context_switching::descriptor_table -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_POINTERS_HPP \ No newline at end of file +#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_ACCESS_BYTE_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp index e69de29..ced0d89 100644 --- a/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp @@ -0,0 +1,41 @@ +#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_HPP +#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_HPP + +#include "arch/context_switching/descriptor_table/gdt_flags.hpp" + +#include +#include +#include + +namespace teachos::arch::context_switching::descriptor_table +{ + struct segment_descriptor + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint64_t + { + BASE = 1U << 0U, + FLAGS = 1U << 8U, + LIMIT = 1U << 12U, + ACCESS_BYTE = 1U << 16U, + BASE = 1U << 32U, + BASE = 1U << 40U, + LIMIT = 1U << 56U + }; + + explicit segment_descriptor(uint64_t flags) + : flags(flags) + { + // Nothing to do + } + + private: + std::uint8_t base1; + gdt_flags flags; + std::bitset<64U> flags; ///< Underlying bitset used to read the flags from + }; +} // namespace teachos::arch::context_switching::descriptor_table + +#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_DESCRIPTOR_TABLE_SEGMENT_DESCRIPTOR_HPP \ No newline at end of file -- cgit v1.2.3 From 56ee767e37cdccae333b292f2fd4c7b2123a7108 Mon Sep 17 00:00:00 2001 From: Fabian Imhof Date: Thu, 27 Feb 2025 13:12:06 +0000 Subject: add comment --- .../arch/context_switching/descriptor_table/segment_descriptor.hpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp b/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp index ced0d89..8428dfa 100644 --- a/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp +++ b/arch/x86_64/include/arch/context_switching/descriptor_table/segment_descriptor.hpp @@ -9,6 +9,11 @@ namespace teachos::arch::context_switching::descriptor_table { + + /* + TODO: Lookup segment_descriptor in intel manual chapter 3.4.5 + */ + struct segment_descriptor { /** -- cgit v1.2.3 From 051307f49f4cdfb86c527a475ab21feea4a664d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20Gm=C3=BCr?= Date: Tue, 4 Mar 2025 18:01:49 +0000 Subject: Add more methods to vector to mimic stl interface partially. --- arch/x86_64/include/arch/stl/shared_pointer.hpp | 2 + arch/x86_64/include/arch/stl/vector.hpp | 381 ++++++++++++++++++------ arch/x86_64/src/memory/main.cpp | 9 + 3 files changed, 306 insertions(+), 86 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/shared_pointer.hpp b/arch/x86_64/include/arch/stl/shared_pointer.hpp index 79a9f82..4a9dec5 100644 --- a/arch/x86_64/include/arch/stl/shared_pointer.hpp +++ b/arch/x86_64/include/arch/stl/shared_pointer.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP #define TEACHOS_ARCH_X86_64_STL_SHARED_POINTER_HPP +#include + namespace teachos::arch::stl { template diff --git a/arch/x86_64/include/arch/stl/vector.hpp b/arch/x86_64/include/arch/stl/vector.hpp index 62be704..5bebf62 100644 --- a/arch/x86_64/include/arch/stl/vector.hpp +++ b/arch/x86_64/include/arch/stl/vector.hpp @@ -2,190 +2,399 @@ #define TEACHOS_ARCH_X86_64_STL_VECTOR_HPP #include "arch/exception_handling/panic.hpp" -#include "arch/shared/container.hpp" -#include "arch/shared/contiguous_pointer_iterator.hpp" #include +#include namespace teachos::arch::stl { /** * @brief Custom vector implementation mirroring the std::vector to allow for the usage of STL functionality with our - * custom memory management + * custom memory management. * - * @tparam T Element the vector instance should contain + * @tparam T Element the vector instance should contain. */ template struct vector { /** - * @brief Defaulted constructor. Initalizes empty vector + * @brief Defaulted constructor. Initalizes empty vector. */ vector() = default; /** * @brief Constructs data with the given amount of elements containg the given value or alterantively the default - * constructed value + * constructed value. * - * @param capacity 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 + * @param capacity 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. */ - vector(size_t capacity, T initial = T{}) - : capacity(capacity) - , size(capacity) - , data(new T[capacity]{}) + explicit vector(size_t capacity, T initial = T{}) + : _size(capacity) + , _capacity(capacity) + , _data(new T[_capacity]{}) { - auto const begin = data; - auto const end = data + size; - vector_container container{begin, end}; - std::ranges::fill(container, inital); + std::ranges::fill(begin(), end(), initial); } /** - * @brief Copy constructor + * @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 vector(InputIterator first, InputIterator last) + : _size(std::distance(first, last)) + , _capacity(std::distance(first, last)) + , _data(new T[_capacity]{}) + { + std::copy(first, last, _data); + } + + /** + * @brief Construct data by copying all elements from the initializer list. + * + * @param initalizer_list List we want to copy all elements from. + */ + explicit vector(std::initializer_list initalizer_list) + : _size(initalizer_list.size()) + , _capacity(initalizer_list.size()) + , _data(new T[_capacity]{}) + { + std::copy(initalizer_list.begin(), initalizer_list.end(), _data); + } + + /** + * @brief Copy constructor. * * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it + * elements from it. * - * @param other Other instance of vector we want to copy the data from + * @param other Other instance of vector we want to copy the data from. */ vector(vector const & other) - : size(size) - , capacity(capacity) + : _size(other._size) + , _capacity(other._capacity) { - delete[] data; - data = new T[capacity]{}; - - auto const begin = other.data; - auto const end = other.data + size; - vector_container container{begin, end}; - std::ranges::copy(container, data); + delete[] _data; + _data = new T[_capacity]{}; + std::copy(other.begin(), other.end(), _data); } /** - * @brief Copy assignment operator + * @brief Copy assignment operator. * * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it + * elements from it. * - * @param other Other instance of vector we want to copy the data from - * @return Newly created copy + * @param other Other instance of vector we want to copy the data from. + * @return Newly created copy. */ vector & operator=(vector const & other) { - delete[] data; - size = other.size(); - capacity = other.capacity(); - data = new T[capacity]{}; - - auto const begin = other.data; - auto const end = other.data + size; - vector_container container{begin, end}; - std::ranges::copy(container, data); + delete[] _data; + _size = other._size; + _capacity = other._capacity; + _data = new T[_capacity]{}; + std::copy(other.begin(), other.end(), _data); return *this; } /** - * @brief Destructor + * @brief Destructor. */ - ~vector() { delete[] data; } + ~vector() { delete[] _data; } /** * @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 + * that is the case the capacity is increased automatically. * - * @return Current amount of elements + * @return Current amount of elements. */ - size_t size() const { return size; } + size_t size() const { return _size; } /** * @brief Amount of space the vector currently has, can be different than the size, because we allocate more than we - * exactly require to decrease the amount of allocations and deallocation to improve speed + * exactly require to decrease the amount of allocations and deallocation to improve speed. * - * @return Current amount of space the vector has for elements + * @return Current amount of space the vector has for elements. */ - size_t capacity() const { return capacity; } + size_t capacity() const { return _capacity; } /** - * @brief Array indexing operator. Allowing to access element at the given index + * @brief Array indexing operator. Allowing to access element at the given index. * - * @note Does not do any bounds checks use at() for that + * @note Does not do any bounds checks use at() for that. * - * @param index Index we want to access elements at - * @return Reference to the underlying element + * @param index Index we want to access elements at. + * @return Reference to the underlying element. */ - T & operator[](size_t index) { return data[index]; } + T & operator[](size_t index) { return _data[index]; } /** - * @brief Array indexing operator. Allowing to access element at the given index + * @brief Array indexing operator. Allowing to access element at the given index. * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted + * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted. * - * @param index Index we want to access elements at - * @return Reference to the underlying element + * @param index Index we want to access elements at. + * @return Reference to the underlying element. */ T & at(size_t index) { - if (index >= size) + if (index >= _size) { exception_handling::panic("[Vector] Attempted to read element at invalid index"); } - return this->operator[](size); + return this->operator[](index); } - void push_back(T & const element) {} + /** + * @brief Appends the given element value to the end of the container. The new element is initalized as a copy of + * value. + * + * @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. + * + * @param value The value of the element to append. + */ + void push_back(T const & value) + { + _data[_size] = value; + _size++; - void emplace_back(T && const element) + if (_size == _capacity) + { + reserve(_capacity * 2); + } + } + + /** + * @brief Appends the given element value to the end of the container. Value is moved into the new element. + * + * @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. + * + * @param value The value of the element to append. + */ + void push_back(T && value) { - // If no cacacity, increase capacity - if (size == capacity) + _data[_size] = std::move(value); + _size++; + + if (_size == _capacity) { - reserve(capacity * 2); + reserve(_capacity * 2); } + } - data[size] = element; - size++; + /** + * @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. + * + * @tparam Args + * @param args Arguments to forward to the constructor of the element + * @return T& + */ + template + auto emplace_back(Args &&... args) -> T & + { + _data[_size] = T{std::forward(args)...}; + auto const index = _size++; + + if (_size == _capacity) + { + reserve(_capacity * 2); + } + return _data[index]; } + /** + * @brief Removes the last element of the container. 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. + */ void pop_back() { - if (size <= 0) + if (_size <= 0) { exception_handling::panic("[Vector] Attempted to pop back last element of already empty vector"); } - size--; + _size--; } - T & front() { return *data; } + /** + * @brief Returns an iterator to the first element of the vector. + * If the vector is empty, the returned iterator will be equal to end(). + * + * @return Iterator to the first element. + */ + T * begin() noexcept { return _data; } + + /** + * @brief Returns an iterator to the first element of the vector. + * If the vector is empty, the returned iterator will be equal to end(). + * + * @return Iterator to the first element. + */ + T const * begin() const noexcept { return _data; } + + /** + * @brief Returns an iterator to the first element of the vector. + * If the vector is empty, the returned iterator will be equal to end(). + * + * @return Iterator to the first element. + */ + T const * cbegin() const noexcept { return begin(); } + + /** + * @brief Returns an iterator to the element following the last element of the vector. This element acts as a + * placeholder, attempting to access it results in undefined behavior. + * + * @return Iterator to the element following the last element. + */ + T * end() noexcept { return _data + _size; } + + /** + * @brief Returns an iterator to the element following the last element of the vector. This element acts as a + * placeholder, attempting to access it results in undefined behavior. + * + * @return Iterator to the element following the last element. + */ + T const * end() const noexcept { return _data + _size; } + + /** + * @brief Returns an iterator to the element following the last element of the vector. This element acts as a + * placeholder, attempting to access it results in undefined behavior. + * + * @return Iterator to the element following the last element. + */ + T const * cend() const noexcept { return end(); } + + /** + * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range + * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable + * in that case). + * + * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal + * to the address of the first element. + */ + T * data() { return _data; } + + /** + * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range + * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable + * in that case). + * + * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal + * to the address of the first element. + */ + T const * data() const { return _data; } - T & back() { return *(data + size); } + /** + * @brief Returns a reference to the first element in the container. Calling front on an empty container causes + * undefined behavior. + * + * @return Reference to the first element. + */ + T & front() { return *begin(); } + + /** + * @brief Returns a reference to the first element in the container. Calling front on an empty container causes + * undefined behavior. + * + * @return Reference to the first element. + */ + T const & front() const { return *begin(); } + /** + * @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. + */ + T & back() { return *end(); } + + /** + * @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. + */ + T const & back() const { return *end(); } + + /** + * @brief Increase the capacity of the vector (the total number of elements that the vector can hold without + * requiring reallocation) to a value that's greater or equal to new_cap. If new_cap is greater than the current + * capacity(), new storage is allocated, otherwise the function does nothing. + * + * reserve() does not change the size of the vector. + * + * If new_cap is greater than capacity(), all iterators (including the end() iterator) and all references to the + * elements are invalidated. Otherwise, no iterators or references are invalidated. + * + * After a call to reserve(), insertions will not trigger reallocation unless the insertion would make the size of + * the vector greater than the value of capacity(). + * + * @note Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for + * instance, calling it before every push_back() call) may actually increase the number of reallocations (by causing + * the capacity to grow linearly rather than exponentially) and result in increased computational complexity and + * decreased performance. For example, a function that receives an arbitrary vector by reference and appends + * elements to it should usually not call reserve() on the vector, since it does not know of the vector's usage + * characteristics. + * + * When inserting a range, the range version of insert() is generally preferable as it preserves the correct + * capacity growth behavior, unlike reserve() followed by a series of push_back()s. + * + * reserve() cannot be used to reduce the capacity of the container; to that end shrink_to_fit() is provided. + * + * @param new_capacity New capacity of the vector, in number of elements + */ void reserve(size_t new_capacity) { - if (new_capacity < size) + if (new_capacity <= _capacity) { - // Creating new array with less capacity than is required to keep all current elements makes no sense return; } - T * temp = new T[new_capacity]; + _capacity = new_capacity; + T * temp = new T[_capacity]{}; + std::copy(begin(), end(), temp); + delete[] _data; + _data = temp; + } - auto const begin = other.data; - auto const end = other.data + capacity; - vector_container container{begin, end}; - std::ranges::copy(container, temp); + /** + * @brief Requests the removal of unused capacity. Meaning it requests to reduce capacity() to size(). + * + * If reallocation occurs, all iterators (including the end() iterator) and all references to the elements are + * invalidated. If no reallocation occurs, no iterators or references are invalidated. + */ + void shrink_to_fit() + { + if (_size == _capacity) + { + return; + } - delete[] data; - capacity = new_capacity; - data = temp; + _capacity = _size; + T * temp = new T[_capacity]{}; + std::copy(begin(), end(), temp); + delete[] _data; + _data = temp; } private: - T * data = {}; ///< Pointer to the first element in the underlying data container - size_t capacity = {}; ///< Amount of space for elements in the underlying data container - size_t size = {}; ///< Amount of elements in the underlying data container - - typedef shared::container> vector_container; + size_t _size = {}; ///< Amount of elements in the underlying data container + size_t _capacity = {}; ///< Amount of space for elements in the underlying data container + T * _data = {}; ///< Pointer to the first element in the underlying data container }; } // namespace teachos::arch::stl diff --git a/arch/x86_64/src/memory/main.cpp b/arch/x86_64/src/memory/main.cpp index a6f91d9..08308db 100644 --- a/arch/x86_64/src/memory/main.cpp +++ b/arch/x86_64/src/memory/main.cpp @@ -7,6 +7,9 @@ #include "arch/memory/heap/heap_allocator.hpp" #include "arch/memory/paging/active_page_table.hpp" #include "arch/memory/paging/kernel_mapper.hpp" +#include "arch/stl/shared_pointer.hpp" +#include "arch/stl/unique_pointer.hpp" +#include "arch/stl/vector.hpp" namespace teachos::arch::memory { @@ -49,5 +52,11 @@ namespace teachos::arch::memory remap_heap(allocator, active_table); video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black); video::vga::text::newline(); + + auto test2 = stl::make_unique(0); + auto test3 = stl::make_shared(0); + if (test2 && test3) + { + } } } // namespace teachos::arch::memory -- cgit v1.2.3 From 06d5e5872838bd1528493b62b4dc28d44b54aa47 Mon Sep 17 00:00:00 2001 From: Fabian Imhof Date: Sun, 9 Mar 2025 09:30:01 +0000 Subject: add doxygen comments to shared and unique pointer --- arch/x86_64/include/arch/stl/shared_pointer.hpp | 93 ++++++++++++++++++++++++- arch/x86_64/include/arch/stl/unique_pointer.hpp | 71 ++++++++++++++++++- arch/x86_64/src/memory/main.cpp | 8 +-- 3 files changed, 162 insertions(+), 10 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/shared_pointer.hpp b/arch/x86_64/include/arch/stl/shared_pointer.hpp index 4a9dec5..80ca7fe 100644 --- a/arch/x86_64/include/arch/stl/shared_pointer.hpp +++ b/arch/x86_64/include/arch/stl/shared_pointer.hpp @@ -5,15 +5,33 @@ namespace teachos::arch::stl { + /** + * @brief A simple implementation of a shared pointer. + * + * This class provides reference counting and automatic resource management + * for dynamically allocated objects. + * + * @tparam T The type of the managed object. + */ template struct shared_pointer { + /** + * @brief Constructs a shared pointer. + * + * @param pointer Raw pointer to manage (default is nullptr). + */ explicit shared_pointer(T * pointer = nullptr) : pointer(pointer) , ref_count(new std::atomic(pointer != nullptr ? 1 : 0)) { } + /** + * @brief Copy constructor. + * + * @param other The shared_pointer to copy from. + */ shared_pointer(const shared_pointer & other) : pointer(other.pointer) , ref_count(other.ref_count) @@ -24,6 +42,11 @@ namespace teachos::arch::stl } } + /** + * @brief Move constructor. + * + * @param other The shared_pointer to move from. + */ shared_pointer(shared_pointer && other) noexcept : pointer(other.pointer) , ref_count(other.ref_count) @@ -32,6 +55,12 @@ namespace teachos::arch::stl other.ref_count = nullptr; } + /** + * @brief Copy assignment operator. + * + * @param other The shared_pointer to copy from. + * @return Reference to this shared_pointer. + */ shared_pointer & operator=(const shared_pointer & other) { if (this != &other) @@ -49,6 +78,12 @@ namespace teachos::arch::stl return *this; } + /** + * @brief Move assignment operator. + * + * @param other The shared_pointer to move from. + * @return Reference to this shared_pointer. + */ shared_pointer & operator=(shared_pointer && other) noexcept { if (this != &other) @@ -63,8 +98,14 @@ namespace teachos::arch::stl return *this; } + /** + * @brief Destructor. Cleans up resources if necessary. + */ ~shared_pointer() { cleanup(); } + /** + * @brief Releases ownership and deletes the object if necessary. + */ void cleanup() { if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) @@ -74,6 +115,11 @@ namespace teachos::arch::stl } } + /** + * @brief Resets the shared pointer with a new raw pointer. + * + * @param p New raw pointer (default is nullptr). + */ void reset(T * p = nullptr) { cleanup(); @@ -81,18 +127,43 @@ namespace teachos::arch::stl ref_count = new std::atomic(p != nullptr ? 1 : 0); } + /** + * @brief Swaps the contents of this shared pointer with another. + * + * @param other The shared_pointer to swap with. + */ void swap(shared_pointer & other) { std::swap(pointer, other.pointer); std::swap(ref_count, other.ref_count); } + /** + * @brief Dereference operator. + * + * @return Reference to the managed object. + */ T & operator*() const { return *pointer; } + /** + * @brief Member access operator. + * + * @return Pointer to the managed object. + */ T * operator->() const { return pointer; } + /** + * @brief Returns the raw pointer. + * + * @return Pointer to the managed object. + */ T * get() const { return pointer; } + /** + * @brief Returns the reference count. + * + * @return Number of shared_pointer instances managing the same object. + */ int use_count() const { if (pointer != nullptr) @@ -103,15 +174,33 @@ namespace teachos::arch::stl return 0; } + /** + * @brief Checks if this is the only shared pointer managing the object. + * + * @return True if the use count is 1, otherwise false. + */ bool unique() const { return use_count() == 1; } + /** + * @brief Checks if the shared pointer is not empty. + * + * @return True if the pointer is not null, otherwise false. + */ explicit operator bool() const { return pointer != nullptr; } private: - T * pointer; - std::atomic * ref_count; + T * pointer; ///< The managed object. + std::atomic * ref_count; ///< Reference count. }; + /** + * @brief Creates a shared pointer instance. + * + * @tparam T The type of object to allocate. + * @tparam Args Argument types for the constructor of T. + * @param args Arguments for the constructor of T. + * @return A shared_pointer instance managing a newly created object. + */ template shared_pointer make_shared(Args &&... args) { diff --git a/arch/x86_64/include/arch/stl/unique_pointer.hpp b/arch/x86_64/include/arch/stl/unique_pointer.hpp index 0ec3c38..08c862d 100644 --- a/arch/x86_64/include/arch/stl/unique_pointer.hpp +++ b/arch/x86_64/include/arch/stl/unique_pointer.hpp @@ -3,25 +3,56 @@ namespace teachos::arch::stl { + /** + * @brief A simple unique pointer implementation. + * + * @tparam T Type of the managed object. + */ template struct unique_pointer { + /** + * @brief Constructs a unique pointer. + * + * @param ptr Pointer to manage, default is nullptr. + */ explicit unique_pointer(T * ptr = nullptr) : pointer(ptr) { } + /** + * @brief Destructor that deletes the managed object. + */ ~unique_pointer() { delete pointer; } + /** + * @brief Deleted copy constructor to enforce unique ownership. + */ unique_pointer(const unique_pointer &) = delete; + + /** + * @brief Deleted copy assignment operator to enforce unique ownership. + */ unique_pointer & operator=(const unique_pointer &) = delete; + /** + * @brief Move constructor. + * + * @param other Unique pointer to move from. + */ unique_pointer(unique_pointer && other) noexcept : pointer(other.pointer) { other.pointer = nullptr; } + /** + * @brief Move assignment operator. + * + * @param other Unique pointer to move from. + * @return Reference to this unique pointer. + */ unique_pointer & operator=(unique_pointer && other) noexcept { if (this != &other) @@ -33,12 +64,32 @@ namespace teachos::arch::stl return *this; } + /** + * @brief Dereference operator. + * + * @return Reference to the managed object. + */ T & operator*() const { return *pointer; } + /** + * @brief Member access operator. + * + * @return Pointer to the managed object. + */ T * operator->() const { return pointer; } + /** + * @brief Gets the raw pointer. + * + * @return The managed pointer. + */ T * get() const { return pointer; } + /** + * @brief Releases ownership of the managed object. + * + * @return The raw pointer and sets internal pointer to nullptr. + */ T * release() { T * temp = pointer; @@ -46,18 +97,36 @@ namespace teachos::arch::stl return temp; } + /** + * @brief Resets the managed object. + * + * @param ptr New pointer to manage (default is nullptr). + */ void reset(T * ptr = nullptr) { delete pointer; pointer = ptr; } + /** + * @brief Swaps the managed object with another unique pointer. + * + * @param other Unique pointer to swap with. + */ void swap(unique_pointer & other) { std::swap(pointer, other.pointer); } private: - T * pointer; + T * pointer; ///< The managed pointer. }; + /** + * @brief Creates a unique pointer instance. + * + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args Arguments for T's constructor. + * @return A unique pointer managing a newly created T object. + */ template unique_pointer make_unique(Args &&... args) { diff --git a/arch/x86_64/src/memory/main.cpp b/arch/x86_64/src/memory/main.cpp index 08308db..15e89c0 100644 --- a/arch/x86_64/src/memory/main.cpp +++ b/arch/x86_64/src/memory/main.cpp @@ -52,11 +52,5 @@ namespace teachos::arch::memory remap_heap(allocator, active_table); video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black); video::vga::text::newline(); - - auto test2 = stl::make_unique(0); - auto test3 = stl::make_shared(0); - if (test2 && test3) - { - } } -} // namespace teachos::arch::memory +} // namespace teachos::arch::memory \ No newline at end of file -- cgit v1.2.3 From 1a6d41362447531a2ea5ee344c15b9aaa6c2090a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20Gm=C3=BCr?= Date: Sun, 9 Mar 2025 16:52:17 +0000 Subject: Adjust comments and implement remaining interface for STL classes. --- arch/x86_64/include/arch/stl/shared_pointer.hpp | 142 ++++++++++++++---------- arch/x86_64/include/arch/stl/unique_pointer.hpp | 96 +++++++++++----- arch/x86_64/include/arch/stl/vector.hpp | 18 +-- arch/x86_64/src/memory/main.cpp | 5 +- 4 files changed, 163 insertions(+), 98 deletions(-) (limited to 'arch/x86_64') diff --git a/arch/x86_64/include/arch/stl/shared_pointer.hpp b/arch/x86_64/include/arch/stl/shared_pointer.hpp index 80ca7fe..1ddc182 100644 --- a/arch/x86_64/include/arch/stl/shared_pointer.hpp +++ b/arch/x86_64/include/arch/stl/shared_pointer.hpp @@ -6,10 +6,14 @@ namespace teachos::arch::stl { /** - * @brief A simple implementation of a shared pointer. - * - * This class provides reference counting and automatic resource management - * for dynamically allocated objects. + * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several + * shared_pointer objects may own the same object. The object is destroyed and its memory deallocated when either of + * the following happens: the last remaining shared_pointer owning the object is destroyed; the last remaining + * shared_pointer owning the object is assigned another pointer via operator= or reset(). A + * shared_pointer can share ownership of an object while storing a pointer to another object. This feature can be used + * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), + * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count + * reaches zero. * * @tparam T The type of the managed object. */ @@ -17,14 +21,15 @@ namespace teachos::arch::stl struct shared_pointer { /** - * @brief Constructs a shared pointer. + * @brief Constructor. * - * @param pointer Raw pointer to manage (default is nullptr). + * @param pointer A pointer to an object to manage (default is nullptr). */ explicit shared_pointer(T * pointer = nullptr) : pointer(pointer) - , ref_count(new std::atomic(pointer != nullptr ? 1 : 0)) + , ref_count(new std::atomic(pointer != nullptr ? 1 : 0)) { + // Nothing to do. } /** @@ -56,10 +61,12 @@ namespace teachos::arch::stl } /** - * @brief Copy assignment operator. + * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the + * object managed by r. If r manages no object, *this manages no object too. Equivalent to + * shared_ptr(r).swap(*this). * - * @param other The shared_pointer to copy from. - * @return Reference to this shared_pointer. + * @param other Another smart pointer to share the ownership with. + * @return Reference to this shared pointer. */ shared_pointer & operator=(const shared_pointer & other) { @@ -79,10 +86,11 @@ namespace teachos::arch::stl } /** - * @brief Move assignment operator. + * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of + * the previous state of r, and r is empty. Equivalent to shared_ptr(std::move(r)).swap(*this). * - * @param other The shared_pointer to move from. - * @return Reference to this shared_pointer. + * @param other Another smart pointer to acquire the ownership from. + * @return Reference to this shared pointer. */ shared_pointer & operator=(shared_pointer && other) noexcept { @@ -104,31 +112,20 @@ namespace teachos::arch::stl ~shared_pointer() { cleanup(); } /** - * @brief Releases ownership and deletes the object if necessary. - */ - void cleanup() - { - if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) - { - delete pointer; - delete ref_count; - } - } - - /** - * @brief Resets the shared pointer with a new raw pointer. + * @brief Replaces the managed object. * - * @param p New raw pointer (default is nullptr). + * @param ptr Pointer to a new object to manage (default = nullptr). */ - void reset(T * p = nullptr) + void reset(T * ptr = nullptr) { cleanup(); - pointer = p; - ref_count = new std::atomic(p != nullptr ? 1 : 0); + pointer = ptr; + ref_count = new std::atomic(ptr != nullptr ? 1 : 0); } /** - * @brief Swaps the contents of this shared pointer with another. + * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not + * adjusted. * * @param other The shared_pointer to swap with. */ @@ -139,32 +136,38 @@ namespace teachos::arch::stl } /** - * @brief Dereference operator. + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. * - * @return Reference to the managed object. + * @return Returns the object owned by *this, equivalent to *get(). */ - T & operator*() const { return *pointer; } + auto operator*() const -> T & { return *pointer; } /** * @brief Member access operator. * - * @return Pointer to the managed object. + * @return Returns a pointer to the object owned by *this, i.e. get(). */ - T * operator->() const { return pointer; } + auto operator->() const -> T * { return pointer; } /** - * @brief Returns the raw pointer. + * @brief Returns a pointer to the managed object or nullptr if no object is owned. * - * @return Pointer to the managed object. + * @return Pointer to the managed object or nullptr if no object is owned. */ - T * get() const { return pointer; } + auto get() const -> T * { return pointer; } /** - * @brief Returns the reference count. + * @brief Returns the number of different shared_pointer instances (*this included) managing the current object. If + * there is no managed object, ​0​ is returned. * - * @return Number of shared_pointer instances managing the same object. + * @note Common use cases include comparison with ​0​. If use_count returns zero, the shared pointer is empty + * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1, + * there are no other owners. + * + * @return The number of Shared_pointer instances managing the current object or ​0​ if there is no managed + * object. */ - int use_count() const + auto use_count() const -> std::size_t { if (pointer != nullptr) { @@ -175,34 +178,59 @@ namespace teachos::arch::stl } /** - * @brief Checks if this is the only shared pointer managing the object. + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. * - * @return True if the use count is 1, otherwise false. + * @return true if *this owns an object, false otherwise. */ - bool unique() const { return use_count() == 1; } + explicit operator bool() const { return pointer != nullptr; } /** - * @brief Checks if the shared pointer is not empty. - * - * @return True if the pointer is not null, otherwise false. + * @brief Defaulted three-way comparator operator. */ - explicit operator bool() const { return pointer != nullptr; } + auto operator<=>(const shared_pointer & other) const = default; private: - T * pointer; ///< The managed object. - std::atomic * ref_count; ///< Reference count. + /** + * @brief Releases ownership and deletes the object if this was the last ereference to the owned managed object. + */ + auto cleanup() -> void + { + if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) + { + delete pointer; + delete ref_count; + } + } + + T * pointer; ///< The managed object. + std::atomic * ref_count; ///< Reference count. }; /** - * @brief Creates a shared pointer instance. + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template + auto swap(shared_pointer & lhs, shared_pointer & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a shared_pointer. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: shared_pointer(new T(std::forward(args)...)). * - * @tparam T The type of object to allocate. - * @tparam Args Argument types for the constructor of T. - * @param args Arguments for the constructor of T. - * @return A shared_pointer instance managing a newly created object. + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Shared_pointer of an instance of type T. */ template - shared_pointer make_shared(Args &&... args) + auto make_shared(Args &&... args) -> shared_pointer { return shared_pointer(new T(std::forward(args)...)); } diff --git a/arch/x86_64/include/arch/stl/unique_pointer.hpp b/arch/x86_64/include/arch/stl/unique_pointer.hpp index 08c862d..899a35b 100644 --- a/arch/x86_64/include/arch/stl/unique_pointer.hpp +++ b/arch/x86_64/include/arch/stl/unique_pointer.hpp @@ -4,7 +4,8 @@ namespace teachos::arch::stl { /** - * @brief A simple unique pointer implementation. + * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer + * and subsequently disposes of that object when the unique_pointer goes out of scope. * * @tparam T Type of the managed object. */ @@ -12,13 +13,14 @@ namespace teachos::arch::stl struct unique_pointer { /** - * @brief Constructs a unique pointer. + * @brief Constructor. * - * @param ptr Pointer to manage, default is nullptr. + * @param ptr A pointer to an object to manage (default is nullptr). */ explicit unique_pointer(T * ptr = nullptr) : pointer(ptr) { + // Nothing to do. } /** @@ -34,7 +36,7 @@ namespace teachos::arch::stl /** * @brief Deleted copy assignment operator to enforce unique ownership. */ - unique_pointer & operator=(const unique_pointer &) = delete; + auto operator=(const unique_pointer &) -> unique_pointer & = delete; /** * @brief Move constructor. @@ -48,12 +50,12 @@ namespace teachos::arch::stl } /** - * @brief Move assignment operator. + * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). * - * @param other Unique pointer to move from. + * @param other Smart pointer from which ownership will be transferred. * @return Reference to this unique pointer. */ - unique_pointer & operator=(unique_pointer && other) noexcept + auto operator=(unique_pointer && other) noexcept -> unique_pointer & { if (this != &other) { @@ -65,32 +67,42 @@ namespace teachos::arch::stl } /** - * @brief Dereference operator. + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. * - * @return Reference to the managed object. + * @return Returns the object owned by *this, equivalent to *get(). */ - T & operator*() const { return *pointer; } + auto operator*() const -> T & { return *pointer; } /** * @brief Member access operator. * - * @return Pointer to the managed object. + * @return Returns a pointer to the object owned by *this, i.e. get(). + */ + auto operator->() const -> T * { return pointer; } + + /** + * @brief Returns a pointer to the managed object or nullptr if no object is owned. + * + * @return Pointer to the managed object or nullptr if no object is owned. */ - T * operator->() const { return pointer; } + auto get() const -> T * { return pointer; } /** - * @brief Gets the raw pointer. + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. * - * @return The managed pointer. + * @return true if *this owns an object, false otherwise. */ - T * get() const { return pointer; } + explicit operator bool() const noexcept { return pointer != nullptr; } /** - * @brief Releases ownership of the managed object. + * @brief Releases the ownership of the managed object, if any. + * get() returns nullptr after the call. + * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()). * - * @return The raw pointer and sets internal pointer to nullptr. + * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be + * returned by get() before the call. */ - T * release() + auto release() -> T * { T * temp = pointer; pointer = nullptr; @@ -98,37 +110,65 @@ namespace teachos::arch::stl } /** - * @brief Resets the managed object. + * @brief Replaces the managed object. * - * @param ptr New pointer to manage (default is nullptr). + * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed, + * except where provided as a compiler extension or as a debugging assert. Note that code such as + * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does. + * + * @param ptr Pointer to a new object to manage (default = nullptr). */ - void reset(T * ptr = nullptr) + auto reset(T * ptr = nullptr) -> void { delete pointer; pointer = ptr; } /** - * @brief Swaps the managed object with another unique pointer. + * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other. * - * @param other Unique pointer to swap with. + * @param other Another unique_ptr object to swap the managed object and the deleter with. */ - void swap(unique_pointer & other) { std::swap(pointer, other.pointer); } + auto swap(unique_pointer & other) -> void + { + using std::swap; + swap(pointer, other.pointer); + } + + /** + * @brief Defaulted three-way comparator operator. + */ + auto operator<=>(const unique_pointer & other) const = default; private: T * pointer; ///< The managed pointer. }; /** - * @brief Creates a unique pointer instance. + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template + auto swap(unique_pointer & lhs, unique_pointer & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a unique_pointer. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: unique_pointer(new T(std::forward(args)...)). * * @tparam T Type of the managed object. * @tparam Args Argument types for T's constructor. - * @param args Arguments for T's constructor. - * @return A unique pointer managing a newly created T object. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Unique_pointer of an instance of type T. */