From f3d87430be8c130ad36cba032f5375f571e4cc26 Mon Sep 17 00:00:00 2001 From: The Fern Date: Fri, 2 Dec 2022 13:51:37 +0100 Subject: [PATCH] Update README Add constness to various methods in unordered_probe_map --- README.md | 47 ++++++++++++++------ include/ktl/containers/trivial_array.h | 2 +- include/ktl/containers/unordered_probe_map.h | 16 ++++--- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index cc7193b..e44b8db 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ![MacOS untested](https://img.shields.io/badge/MacOS-Untested-red?style=flat-square) A library containing various composite memory allocators and containers.
-Mostly based on a [CppCon talk by Andrei Alexandrescu](https://www.youtube.com/watch?v=LIb3L4vKZ7U). +Mostly based on a [Talk by Andrei Alexandrescu](https://www.youtube.com/watch?v=LIb3L4vKZ7U). [![Release](https://img.shields.io/github/v/release/KredeGC/KTL?display_name=tag&style=flat-square)](https://github.com/KredeGC/KTL/releases) [![License](https://img.shields.io/github/license/KredeGC/KTL?style=flat-square)](https://github.com/KredeGC/KTL/blob/master/LICENSE) @@ -52,27 +52,28 @@ Exceptions are not used with any of the allocators above. This means that upon f ## Allocator interface The allocators roughly follow the standard for STL allocators, except that they are not typed, so use void* instead. +`type_allocator` is the only allocator that uses T* for allocations. Some additional methods have also been added. | Method | Description | | --- | --- | -| void* allocate(size_type size) | Attempts to allocate a chunk of memory defined by `size`. For non-typed allocators the size is in bytes, but for typed allocators it's the amount of objects of the given type. | -| void deallocate(void* ptr, size_type size) | Attempts to deallocate the memory at location `ptr` with the given size, `size`. | -| void construct(T* ptr, Args&& args) | Calls the constructor of a specific type at the location `ptr` with `args`.
Not all allocators define this method. | -| void destroy(T* ptr) | Calls the destructor of a specific type at the location `ptr`.
Not all allocators define this method. | -| size_type max_size() | Returns the maximum size this allocator could possibly allocate.
Not all allocators define this method. | -| bool owns(void* ptr) | Returns whether or not the given memory at location `ptr` is owned by this allocator.
Not all allocators define this method, such as `mallocator`. | -| Alloc get_allocator() | Returns the allocator that this allocator wraps around.
Most composite allocators define this method. | +| `void* allocate(size_type size)` | Attempts to allocate a chunk of memory defined by `size`. For non-typed allocators the size is in bytes, but for typed allocators it's the amount of objects of the given type. | +| `void deallocate(void* ptr, size_type size)` | Attempts to deallocate the memory at location `ptr` with the given size, `size`. | +| `void construct(T* ptr, Args&& args)` | Calls the constructor of a specific type at the location `ptr` with `args`.
Not all allocators define this method. | +| `void destroy(T* ptr)` | Calls the destructor of a specific type at the location `ptr`.
Not all allocators define this method. | +| `size_type max_size()` | Returns the maximum size this allocator could possibly allocate.
Not all allocators define this method. | +| `bool owns(void* ptr)` | Returns whether or not the given memory at location `ptr` is owned by this allocator.
Not all allocators define this method, such as `mallocator`. | +| `Alloc get_allocator()` | Returns the allocator that this allocator wraps around.
Most composite allocators define this method. | # Containers This library also contains various containers that are STL compliant. | Signature | Description | Notes | | --- | --- | --- | -| binary_heap
\ | A binary heap, sorted using the `Comp` and allocated using the given `Alloc` allocator. | `Comp` can be either `std::greater` or `std::less` or some other custom implementation.
A shorthand version of both a min and a max heap can be used, via the `binary_min_heap` and `binary_max_heap` types. | -| trivial_array
\ | An array wrapper class, similar to `std::array`, but uses dynamic allocation and is optimized for trivial types. Takes a type `T` and an allocator `Alloc`. | The container uses a straight `memcpy` for most of its operations.
It's not recommended to use this with non-trivial types, eg. types that have custom default, copy or move constructors or custom destructors. | -| trivial_vector
\ | A vector class, similar to `std::vector`, but optimized for trivial types. Takes a type `T` and an allocator `Alloc`. | The container uses a straight `memcpy` for most of its operations.
It's not recommended to use this with non-trivial types, eg. types that have custom default, copy or move constructors or custom destructors. | -| unordered_probe_map
| An unordered map class similar to `std::unordered_map`, but optimized for cache locality. | Uses open addressing with linear probing for maximum cache locality.
The container uses a `Hash` struct, `EqualTo` struct and `Alloc` class passed in as template parameters. | +| [binary_heap
\](#binary_heap-interface) | A binary heap, sorted using the `Comp` and allocated using the given `Alloc` allocator. | `Comp` can be either `std::greater` or `std::less` or some other custom implementation.
A shorthand version of both a min and a max heap can be used, via the `binary_min_heap` and `binary_max_heap` types. | +| [trivial_array
\](#trivial_array-interface) | An array wrapper class, similar to `std::array`, but uses dynamic allocation and is optimized for trivial types. Takes a type `T` and an allocator `Alloc`. | The container uses a straight `memcpy` for most of its operations.
It's not recommended to use this with non-trivial types, eg. types that have custom default, copy or move constructors or custom destructors.
Just like with the standard array, the container may invalidate iterators and references on insertion, but not on erasure. | +| [trivial_vector
\](#trivial_vector-interface) | A vector class, similar to `std::vector`, but optimized for trivial types. Takes a type `T` and an allocator `Alloc`. | The container uses a straight `memcpy` for most of its operations.
It's not recommended to use this with non-trivial types, eg. types that have custom default, copy or move constructors or custom destructors.
Just like with the standard vector, the container may invalidate iterators and references on insertion, but not on erasure. | +| [unordered_probe_map
](#unordered_probe_map-interface) | An unordered map class similar to `std::unordered_map`, but optimized for cache locality using open addressing with linear probing. | The container uses a `Hash` struct, `EqualTo` struct and `Alloc` class passed in as template parameters, just like the standard unordered map.
Unlike `std::unordered_map` iterator and reference invalidation may happen on insertion. However, only iterators and references to the erased object are invalidated on erasure. | ## binary_heap interface | Method | Description | @@ -85,22 +86,42 @@ This library also contains various containers that are STL compliant. ## trivial_array interface | Method | Description | | --- | --- | +| `T& operator[size_t index]` | Returns a reference to the element at `index`. | | `void assign(const T* first, const T* last)` | Assigns the given values from `first` to `last`. It also resizes if the size doesn't match. | +| `bool empty()` | Returns true if the array has been initialized with no size. | | `void resize(size_t size)` | Resizes the array to the given size. | +| `size_t size()` | Returns the current size of the array. | ## trivial_vector interface | Method | Description | | --- | --- | +| `T& operator[size_t index]` | Returns a reference to the element at `index`. | +| `size_t capacity()` | Returns the curren capacity of the vector. | | `void clear()` | Clear all elements in the vector. | | `void emplace_back(Args&& args)` | Creates a new element and pushes it to the vector. | +| `bool empty()` | Returns whether or not the vector is empty. | | `void pop_back()` | Removes the last element from the vector. | | `void push_back(const T& value)` | Pushes a new value by copying it. | | `void push_back(T&& value)` | Pushes a new value by moving it. | | `void push_back(const T* first, const T* last)` | Pushes a range of values from `first` to `last`. | | `void reserve(size_t size)` | Reserves the size of the array to `size`, without initializing any elements. | | `void resize(size_t size)` | Resizes the vector to the given size. | +| `size_t size()` | Returns the current amount of elements in the vector. | -# Examples +## unordered_probe_map interface +| Method | Description | +| --- | --- | +| `V& operator[const K& index]` | Returns a reference to the element with the given key `index` and inserts one if it doesn't exist. | +| `size_t capacity()` | Returns the curren capacity of the map. Always a power of two. | +| `void clear()` | Clear all elements in the map. | +| `bool empty()` | Returns whether or not the map is empty. | +| `void erase(const K& index)` | Erase an element by its key. | +| `void erase(iterator iter)` | Erase an element by an iterator pointing to it. | +| `iterator insert(K&& index, V&& value)` | Inserts an element into the map with the key `index` and value `value`. Returns an iterator to the element. | +| `iterator find(const K& index)` | Attempts to find the value with the given key `index`. Returns `end()` if not found. | +| `size_t size()` | Returns the current amount of elements in the map. | + +# Allocator Examples Create an allocator which will attempt to use a pre allocator for allocation, but fall back on malloc when full. ```cpp // Create the allocator from some 16kb buffer and straight malloc diff --git a/include/ktl/containers/trivial_array.h b/include/ktl/containers/trivial_array.h index 4a5cffc..c95a844 100644 --- a/include/ktl/containers/trivial_array.h +++ b/include/ktl/containers/trivial_array.h @@ -143,7 +143,7 @@ namespace ktl size_t size() const noexcept { return m_End - m_Begin; } - bool empty() const noexcept { return m_Begin == nullptr; } + bool empty() const noexcept { return m_Begin == m_End; } T* data() noexcept { return m_Begin; } diff --git a/include/ktl/containers/unordered_probe_map.h b/include/ktl/containers/unordered_probe_map.h index 10df6e0..dd8cc12 100644 --- a/include/ktl/containers/unordered_probe_map.h +++ b/include/ktl/containers/unordered_probe_map.h @@ -265,6 +265,8 @@ namespace ktl bool empty() const noexcept { return m_Count == 0; } + float load_factor() const noexcept { return static_cast(m_Count) / static_cast(capacity()); } + template iterator insert(Key&& index, Value&& value) noexcept @@ -312,7 +314,7 @@ namespace ktl iter.m_End = nullptr; } - iterator find(const K& index) + iterator find(const K& index) const noexcept { if (m_Begin == nullptr) return iterator(nullptr, nullptr); @@ -344,7 +346,7 @@ namespace ktl } private: - void release() + void release() noexcept { if (m_Begin) { @@ -359,7 +361,7 @@ namespace ktl } } - void expand() + void expand() noexcept { if (m_Count >= capacity() / 2) { @@ -370,7 +372,7 @@ namespace ktl } } - void set_size(size_t n) + void set_size(size_t n) noexcept { m_Mask = n - 1; @@ -402,7 +404,7 @@ namespace ktl m_End = m_Begin + n; } - pair* find_empty(const K& index, pair* begin, size_t mask) + pair* find_empty(const K& index, pair* begin, size_t mask) const noexcept { size_t h = Hash()(index); @@ -421,7 +423,7 @@ namespace ktl return block; } - pair* get_pair(const K& index, pair* begin, size_t mask) + pair* get_pair(const K& index, pair* begin, size_t mask) const noexcept { size_t h = Hash()(index); @@ -441,7 +443,7 @@ namespace ktl return block; } - static constexpr size_t hash_collision_offet(size_t key, size_t counter, size_t size) + static constexpr size_t hash_collision_offet(size_t key, size_t counter, size_t size) noexcept { // Linear probing for best cache locality return (key + counter) & size;