aboutsummaryrefslogtreecommitdiff
path: root/libs/kstd/kstd/flat_map
diff options
context:
space:
mode:
Diffstat (limited to 'libs/kstd/kstd/flat_map')
-rw-r--r--libs/kstd/kstd/flat_map365
1 files changed, 365 insertions, 0 deletions
diff --git a/libs/kstd/kstd/flat_map b/libs/kstd/kstd/flat_map
new file mode 100644
index 0000000..f12b1b5
--- /dev/null
+++ b/libs/kstd/kstd/flat_map
@@ -0,0 +1,365 @@
+#ifndef KSTD_FLAT_MAP_HPP
+#define KSTD_FLAT_MAP_HPP
+
+#include <kstd/bits/flat_map.hpp>
+#include <kstd/os/error.hpp>
+#include <kstd/vector>
+
+#include <algorithm>
+#include <concepts>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <utility>
+
+namespace kstd
+{
+
+ template<typename KeyType, typename MappedType, typename KeyCompare = std::less<KeyType>,
+ typename KeyContainerType = kstd::vector<KeyType>, typename MappedContainerType = kstd::vector<MappedType>>
+ struct flat_map
+ {
+ using key_container_type = KeyContainerType;
+ using mapped_container_type = MappedContainerType;
+ using key_type = KeyType;
+ using mapped_type = MappedType;
+ using value_type = std::pair<key_type, mapped_type>;
+ using key_compare = KeyCompare;
+ using reference = std::pair<key_type const &, mapped_type &>;
+ using const_reference = std::pair<key_type const &, mapped_type const &>;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using iterator = bits::flat_map_iterator<KeyType, MappedType, typename key_container_type::iterator,
+ typename mapped_container_type::iterator>;
+ using const_iterator =
+ bits::flat_map_iterator<KeyType, MappedType const, typename key_container_type::const_iterator,
+ typename mapped_container_type::const_iterator>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using containers = struct
+ {
+ key_container_type keys;
+ mapped_container_type values;
+ };
+
+ //! Compare two object of type value_type.
+ struct value_compare
+ {
+ constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool
+ {
+ return lhs.first < rhs.first;
+ }
+ };
+
+ //! Construct an empty flat map.
+ constexpr flat_map()
+ : flat_map{key_compare{}}
+ {}
+
+ //! Construct an empty flat map using the given custom comparator.
+ //!
+ //! @param comparator The comparator to use for comparing keys.
+ constexpr explicit flat_map(key_compare const & comparator)
+ : m_containers{}
+ , m_comparator{comparator}
+ {}
+
+ //! Get a reference to the mapped value associated with the given key.
+ //!
+ //! @warning This function will panic if the key is not found.
+ //! @param key The key to look up.
+ //! @return A reference to the mapped value.
+ [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type &
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key))
+ {
+ auto offset = std::distance(m_containers.keys.begin(), found);
+ return *(m_containers.values.begin() + offset);
+ }
+ os::panic("[kstd::flat_map] Key not found");
+ }
+
+ //! Get a reference to the mapped value associated with the given key.
+ //!
+ //! @warning This function will panic if the key is not found.
+ //! @param key The key to look up.
+ //! @return A const reference to the mapped value.
+ [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const &
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key))
+ {
+ auto offset = std::distance(m_containers.keys.cbegin(), found);
+ return *(m_containers.values.cbegin() + offset);
+ }
+ os::panic("[kstd::flat_map] Key not found");
+ }
+
+ //! Get a reference to the mapped value associated with the given key.
+ //!
+ //! @note This overload only participates in overload resolution if the key compare type is transparent.
+ //! @warning This function will panic if the key is not found.
+ //! @param x The key to look up.
+ //! @return A reference to the mapped value.
+ template<typename K>
+ requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; }
+ [[nodiscard]] constexpr auto at(K const & x) -> mapped_type &
+ {
+ auto found = find(x);
+ if (found != end())
+ {
+ auto offset = std::distance(m_containers.keys.begin(), found.key_iterator());
+ return *(m_containers.values.begin() + offset);
+ }
+ os::panic("[kstd::flat_map] Key not found");
+ }
+
+ //! Get a reference to the mapped value associated with the given key.
+ //! @note This overload only participates in overload resolution if the key compare type is transparent.
+ //! @warning This function will panic if the key is not found.
+ //! @param x The key to look up.
+ //! @return A const reference to the mapped value.
+ template<typename K>
+ requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; }
+ [[nodiscard]] auto at(K const & x) const -> mapped_type const &
+ {
+ auto found = find(x);
+ if (found != end())
+ {
+ auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator());
+ return *(m_containers.values.cbegin() + offset);
+ }
+ os::panic("[kstd::flat_map] Key not found");
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto begin() noexcept -> iterator
+ {
+ return iterator{m_containers.keys.begin(), m_containers.values.begin()};
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto begin() const noexcept -> const_iterator
+ {
+ return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()};
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto cbegin() const noexcept -> const_iterator
+ {
+ return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto end() noexcept -> iterator
+ {
+ return iterator{m_containers.keys.end(), m_containers.values.end()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto end() const noexcept -> const_iterator
+ {
+ return const_iterator{m_containers.keys.cend(), m_containers.values.cend()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto cend() const noexcept -> const_iterator
+ {
+ return const_iterator{m_containers.keys.cend(), m_containers.values.cend()};
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto rbegin() noexcept -> reverse_iterator
+ {
+ return reverse_iterator{end()};
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator
+ {
+ return const_reverse_iterator{cend()};
+ }
+
+ //! Get an iterator to the first element.
+ [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator
+ {
+ return const_reverse_iterator{cend()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto rend() noexcept -> reverse_iterator
+ {
+ return reverse_iterator{begin()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator
+ {
+ return const_reverse_iterator{cbegin()};
+ }
+
+ //! Get an iterator to the element past the last element.
+ [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator
+ {
+ return const_reverse_iterator{cbegin()};
+ }
+
+ //! Check whether this flat map is empty or not
+ [[nodiscard]] auto empty() const noexcept -> bool
+ {
+ return m_containers.keys.empty();
+ }
+
+ //! Get the number of elements in this flat map.
+ [[nodiscard]] auto size() const noexcept -> size_type
+ {
+ return m_containers.keys.size();
+ }
+
+ //! Get the maximum number of elements possible in this flat map
+ [[nodiscard]] auto max_size() const noexcept -> size_type
+ {
+ return std::min(m_containers.keys.max_size(), m_containers.values.max_size());
+ }
+
+ //! Try to insert a new key-value pair into the map.
+ //!
+ //! @param args Arguments to use for constructing the key-value pair.
+ //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place.
+ template<typename... Args>
+ auto emplace(Args &&... args) -> std::pair<iterator, bool>
+ requires std::constructible_from<value_type, Args...>
+ {
+ auto value = value_type{std::forward<Args>(args)...};
+ auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator);
+
+ if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first))
+ {
+ auto offset = std::distance(m_containers.keys.begin(), found);
+ return {
+ iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset},
+ false
+ };
+ }
+
+ auto offset = std::distance(m_containers.keys.begin(), found);
+ auto key_iterator = m_containers.keys.begin() + offset;
+ auto mapped_iterator = m_containers.values.begin() + offset;
+
+ auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first));
+ auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second));
+
+ return {
+ iterator{inserted_key, inserted_mapped},
+ true
+ };
+ }
+
+ //! Try to insert a element for the given key into this map.
+ //!
+ //! This function does nothing if the key is already present.
+ //!
+ //! @param key The key to insert a value for.
+ //! @param args The arguments to use to construct the mapped value.
+ template<typename... Args>
+ auto try_emplace(key_type const & key, Args &&... args) -> std::pair<iterator, bool>
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found))
+ {
+ return {found, false};
+ }
+
+ auto offset = std::distance(m_containers.keys.cbegin(), found);
+ auto intersertion_point = m_containers.value.begin() + offset;
+
+ auto inserted_key = m_containers.keys.emplace(key);
+ auto inserted_mapped = m_containers.values.emplace(std::forward<Args>(args)...);
+
+ return {
+ iterator{inserted_key, inserted_mapped},
+ true
+ };
+ }
+
+ //! Find an element with an equivalent key.
+ //!
+ //! @param key The key to look up.
+ //! @return An iterator to the element with the equivalent key, or end() if no such element is found.
+ [[nodiscard]] auto find(key_type const & key) noexcept -> iterator
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key))
+ {
+ auto offset = std::distance(m_containers.keys.begin(), found);
+ return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset};
+ }
+ return end();
+ }
+
+ //! Find an element with an equivalent key.
+ //!
+ //! @param key The key to look up.
+ //! @return An iterator to the element with the equivalent key, or end() if no such element is found.
+ [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key))
+ {
+ auto offset = std::distance(m_containers.keys.cbegin(), found);
+ return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset};
+ }
+ return cend();
+ }
+
+ //! Find an element with an equivalent key.
+ //! @note This overload only participates in overload resolution if the key compare type is transparent.
+ //! @param x The key to look up.
+ //! @return An iterator to the element with the equivalent key, or end() if no such element is found.
+ template<typename K>
+ requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; }
+ [[nodiscard]] auto find(K const & x) noexcept -> iterator
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x))
+ {
+ auto offset = std::distance(m_containers.keys.begin(), found);
+ return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset};
+ }
+ return end();
+ }
+
+ //! Find an element with an equivalent key.
+ //! @note This overload only participates in overload resolution if the key compare type is transparent.
+ //! @param x The key to look up.
+ //! @return An iterator to the element with the equivalent key, or end() if no such element is found.
+ template<typename K>
+ requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; }
+ [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator
+ {
+ auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator);
+ if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x))
+ {
+ auto offset = std::distance(m_containers.keys.cbegin(), found);
+ return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset};
+ }
+ return cend();
+ }
+
+ //! Check if the map contains the given key.
+ //!
+ //! @param key The key to check.
+ //! @return true iff. the key is found, false otherwise.
+ [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool
+ {
+ return find(key) != cend();
+ }
+
+ private:
+ containers m_containers;
+ key_compare m_comparator;
+ };
+} // namespace kstd
+
+#endif \ No newline at end of file