Browse Source

Change outdated chobo-shl containers to the ones from itlib (#402)

Borislav Stanimirov 2 năm trước cách đây
mục cha
commit
c1e1f83321

+ 2 - 2
CMake/FileList.cmake

@@ -120,8 +120,8 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ComputedValues.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/chobo/flat_map.hpp
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/chobo/flat_set.hpp
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/itlib/flat_map.hpp
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/itlib/flat_set.hpp
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/robin_hood.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Context.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ContextInstancer.h

+ 7 - 7
Include/RmlUi/Config/Config.h

@@ -33,7 +33,7 @@
  * This file provides the means to configure various types used across RmlUi. It is possible to override container
  * types with your own, provided they are compatible with STL, or customize STL containers, for example by setting
  * custom allocators. This file may be edited directly, or can be copied to an alternate location, modified, and
- * included by setting the CMake option CUSTOM_CONFIGURATION_FILE (RMLUI_CUSTOM_CONFIGURATION_FILE preprocessor 
+ * included by setting the CMake option CUSTOM_CONFIGURATION_FILE (RMLUI_CUSTOM_CONFIGURATION_FILE preprocessor
  * define) to the path of that file.
  */
 
@@ -55,8 +55,8 @@
 #include <set>
 #include <unordered_set>
 #else
-#include "../Core/Containers/chobo/flat_map.hpp"
-#include "../Core/Containers/chobo/flat_set.hpp"
+#include "../Core/Containers/itlib/flat_map.hpp"
+#include "../Core/Containers/itlib/flat_set.hpp"
 #include "../Core/Containers/robin_hood.h"
 #endif	// RMLUI_NO_THIRDPARTY_CONTAINERS
 
@@ -103,13 +103,13 @@ using SmallOrderedSet = std::set< T >;
 template < typename Key, typename Value>
 using UnorderedMap = robin_hood::unordered_flat_map< Key, Value >;
 template <typename Key, typename Value>
-using SmallUnorderedMap = chobo::flat_map< Key, Value >;
+using SmallUnorderedMap = itlib::flat_map< Key, Value >;
 template <typename T>
 using UnorderedSet = robin_hood::unordered_flat_set< T >;
 template <typename T>
-using SmallUnorderedSet = chobo::flat_set< T >;
+using SmallUnorderedSet = itlib::flat_set< T >;
 template <typename T>
-using SmallOrderedSet = chobo::flat_set< T >;
+using SmallOrderedSet = itlib::flat_set< T >;
 #endif	// RMLUI_NO_THIRDPARTY_CONTAINERS
 template<typename Iterator>
 inline std::move_iterator<Iterator> MakeMoveIterator(Iterator it) { return std::make_move_iterator(it); }
@@ -149,7 +149,7 @@ inline UniquePtr<T> MakeUnique(Args&&... args) { return std::make_unique<T, Args
 // should be done using SFINAE as in example below.
 
 // Extra code to be inserted into RmlUi::Color<> class body. Note: be mindful of colorspaces used by different
-// color types. RmlUi assumes that float colors are interpreted in linear colorspace while byte colors are 
+// color types. RmlUi assumes that float colors are interpreted in linear colorspace while byte colors are
 // interpreted as sRGB.
 
 #define RMLUI_COLOUR_USER_EXTRA                                                                         \

+ 5 - 36
Include/RmlUi/Core/Containers/LICENSE.txt

@@ -30,44 +30,13 @@ SOFTWARE.
 
 ---
 
-chobo-flat-map
-https://github.com/Chobolabs/chobo-shl
+itlib
+https://github.com/iboB/itlib
 
-std::map-like class with an underlying vector
+MIT License
 
-MIT License:
-Copyright(c) 2016 Chobolabs Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files(the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and / or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions :
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-chobo-flat-set
-
-std::set-like class with an underlying vector
-
-Unofficial, chobo-like container, largely based on chobo::flat_map
-
-MIT License:
-Copyright(c) 2019 Michael R. P. Ragazzon 
-Copyright(c) 2016 Chobolabs Inc.
+Copyright(c) 2016-2019 Chobolabs Inc.
+Copyright(c) 2020-2023 Borislav Stanimirov
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files(the

+ 0 - 779
Include/RmlUi/Core/Containers/chobo/flat_map.hpp

@@ -1,779 +0,0 @@
-// chobo-flat-map v1.01
-//
-// std::map-like class with an underlying vector
-//
-// MIT License:
-// Copyright(c) 2016 Chobolabs Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files(the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and / or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions :
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-//
-//                  VERSION HISTORY
-//
-//  1.01 (2016-09-27) Fix for keys with no operator==. Clean up of assignment.
-//                    Added swap method.
-//  1.00 (2016-09-23) First public release
-//
-//
-//                  DOCUMENTATION
-//
-// Simply include this file wherever you need.
-// It defines the class chobo::flat_map, which is an almsot drop-in replacement
-// of std::map. Flat map has an optional underlying container which by default
-// is std::vector. Thus the items in the map are in a continuous block of
-// memory. Thus iterating over the map is cache friendly, at the cost of
-// O(n) for insert and erase.
-//
-// The elements inside (like in std::map) are kept in an order sorted by key.
-// Getting a value by key is O(log2 n)
-//
-// It generally performs much faster than std::map for smaller sets of elements
-//
-// The difference with std::map, which makes flat_map an not-exactly-drop-in
-// replacement is the last template argument:
-// * std::map has <key, value, compare, allocator>
-// * chobo::flat_map has <key, value, compare, container>
-// The container must be an std::vector compatible type (chobo::static_vector
-// and chobo::vector_ptr are, for example, viable). The container value type
-// must be std::pair<key, value>.
-//
-//                  Changing the allocator.
-//
-// If you want to change the allocator of flat map, you'll have to provide a
-// container with the appriate one. Example:
-//
-// chobo::flat_map<
-//      string,
-//      int,
-//      less<string>,
-//      std::vector<pair<string, int>, MyAllocator<pair<string, int>>
-//  > mymap
-//
-//
-//                  Configuration
-//
-// chobo::flat_map has two configurable settings:
-//
-// 1. Throw
-// Whether to throw exceptions: when `at` is called with a non-existent key.
-// By default, like std::map, it throws an std::out_of_range exception. If you define
-// CHOBO_FLAT_MAP_NO_THROW before including this header, the exception will
-// be substituted by an assertion.
-//
-// 2. const char* overloads
-// By default chobo::flat_map provides overloads for the access methods
-// (at, operator[], find, lower_bound, count) for const char* for cases when
-// std::string is the key, so that no allocations happen when accessing with
-// a C-string of a string literal.
-// However if const char* or any other class with implicit conversion from
-// const char* is the key, they won't compile.
-// If you plan on using flat_map with such keys, you'll need to define
-// CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS before including the header
-//
-//
-//                  TESTS
-//
-// The tests are included in the header file and use doctest (https://github.com/onqtam/doctest).
-// To run them, define CHOBO_FLAT_MAP_TEST_WITH_DOCTEST before including
-// the header in a file which has doctest.h already included.
-//
-// Additionally if chobo::static_vector is also available you may define
-// CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST to test flat_map with an
-// unrelying static_vector
-//
-// Additionally if chobo::vector_ptr is also available you may define
-// CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST to test flat_map with an
-// unrelying vector_ptr
-//
-#pragma once
-
-#include <vector>
-#include <algorithm>
-#include <type_traits>
-
-#if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
-#include <cstring>
-#endif
-
-#if !defined(CHOBO_FLAT_MAP_NO_THROW)
-#   include <stdexcept>
-#   define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() throw std::out_of_range("chobo::flat_map out of range")
-#else
-#   include <cassert>
-#   define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() assert(false && "chobo::flat_map out of range")
-#endif
-
-namespace chobo
-{
-namespace detail {
-    template <typename key_type, typename key_compare, typename value_type>
-    struct pair_compare : key_compare {
-        pair_compare() = default;
-        pair_compare(const key_compare& kc) : key_compare(kc) {}
-        bool operator()(const value_type& a, const key_type& b) const { return static_cast<const key_compare&>(*this)(a.first, b); }
-        bool operator()(const key_type& a, const value_type& b) const { return static_cast<const key_compare&>(*this)(a, b.first); }
-    };
-}
-
-template <typename Key, typename T, typename Compare = std::less<Key>, typename Container = std::vector<std::pair<Key, T>>>
-class flat_map : private detail::pair_compare<Key, Compare, std::pair<Key, T>>
-{
-public:
-    typedef Key key_type;
-    typedef T mapped_type;
-    typedef std::pair<Key, T> value_type;
-    typedef Container container_type;
-    typedef Compare key_compare;
-    typedef value_type& reference;
-    typedef const value_type& const_reference;
-    typedef typename container_type::allocator_type allocator_type;
-    typedef typename std::allocator_traits<allocator_type>::pointer pointer;
-    typedef typename std::allocator_traits<allocator_type>::pointer const_pointer;
-    typedef typename container_type::iterator iterator;
-    typedef typename container_type::const_iterator const_iterator;
-    typedef typename container_type::reverse_iterator reverse_iterator;
-    typedef typename container_type::const_reverse_iterator const_reverse_iterator;
-    typedef typename container_type::difference_type difference_type;
-    typedef typename container_type::size_type size_type;
-    typedef detail::pair_compare<Key, Compare, std::pair<Key, T>> pair_compare_t;
-
-    flat_map()
-    {}
-
-    explicit flat_map(const key_compare& comp, const allocator_type& alloc = allocator_type())
-        : pair_compare_t(comp), m_container(alloc)
-    {}
-
-    flat_map(const flat_map& x) = default;
-    flat_map(flat_map&& x) noexcept = default;
-
-    flat_map(std::initializer_list<value_type> ilist) : pair_compare_t(Compare())
-    {
-        m_container.reserve(ilist.size());
-        for (auto&& il : ilist)
-            emplace(il);
-    }
-
-    flat_map& operator=(const flat_map& x)
-    {
-        get_cmp() = x.get_cmp();
-        m_container = x.m_container;
-        return *this;
-    }
-    flat_map& operator=(flat_map&& x) noexcept
-    {
-        get_cmp() = std::move(x.get_cmp());
-        m_container = std::move(x.m_container);
-        return *this;
-    }
-
-    iterator begin() noexcept { return m_container.begin(); }
-    const_iterator begin() const noexcept { return m_container.begin(); }
-    iterator end() noexcept { return m_container.end(); }
-    const_iterator end() const noexcept { return m_container.end(); }
-    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
-    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
-    reverse_iterator rend() noexcept { return m_container.rend(); }
-    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
-    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
-    const_iterator cend() const noexcept { return m_container.cend(); }
-
-    bool empty() const noexcept { return m_container.empty(); }
-    size_type size() const noexcept { return m_container.size(); }
-    size_type max_size() const noexcept { return m_container.max_size(); }
-
-    void reserve(size_type count) { return m_container.reserve(count); }
-    size_type capacity() const noexcept { return m_container.capacity(); }
-
-    void clear() noexcept { m_container.clear(); }
-
-    iterator lower_bound(const key_type& k)
-    {
-        return std::lower_bound(m_container.begin(), m_container.end(), k, get_cmp());
-    }
-
-    const_iterator lower_bound(const key_type& k) const
-    {
-        return std::lower_bound(m_container.begin(), m_container.end(), k, get_cmp());
-    }
-
-    iterator find(const key_type& k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-            return i;
-
-        return end();
-    }
-
-    const_iterator find(const key_type& k) const
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-            return i;
-
-        return end();
-    }
-
-    size_t count(const key_type& k) const
-    {
-        return find(k) == end() ? 0 : 1;
-    }
-
-    template <typename P>
-    std::pair<iterator, bool> insert(P&& val)
-    {
-        auto i = lower_bound(val.first);
-        if (i != end() && !get_cmp()(val.first, *i))
-        {
-            return { i, false };
-        }
-
-        return{ m_container.emplace(i, std::forward<P>(val)), true };
-    }
-
-    std::pair<iterator, bool> insert(const value_type& val)
-    {
-        auto i = lower_bound(val.first);
-        if (i != end() && !get_cmp()(val.first, *i))
-        {
-            return { i, false };
-        }
-
-        return{ m_container.emplace(i, val), true };
-    }
-
-    template <class... Args>
-    std::pair<iterator, bool> emplace(Args&&... args)
-    {
-        value_type val(std::forward<Args>(args)...);
-        return insert(std::move(val));
-    }
-
-    iterator erase(const_iterator pos)
-    {
-        return m_container.erase(pos);
-    }
-
-    size_type erase(const key_type& k)
-    {
-        auto i = find(k);
-        if (i == end())
-        {
-            return 0;
-        }
-
-        erase(i);
-        return 1;
-    }
-
-    mapped_type& operator[](const key_type& k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-        {
-            return i->second;
-        }
-
-        i = m_container.emplace(i, k, mapped_type());
-        return i->second;
-    }
-
-    mapped_type& operator[](key_type&& k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-        {
-            return i->second;
-        }
-
-        i = m_container.emplace(i, std::forward<key_type>(k), mapped_type());
-        return i->second;
-    }
-
-    mapped_type& at(const key_type& k)
-    {
-        auto i = lower_bound(k);
-        if (i == end() || get_cmp()(*i, k))
-        {
-            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
-        }
-
-        return i->second;
-    }
-
-    const mapped_type& at(const key_type& k) const
-    {
-        auto i = lower_bound(k);
-        if (i == end() || get_cmp()(*i, k))
-        {
-            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
-        }
-
-        return i->second;
-    }
-
-    void swap(flat_map& x) noexcept
-    {
-        std::swap(get_cmp(), x.get_cmp());
-        m_container.swap(x.m_container);
-    }
-
-    const container_type& container() const noexcept
-    {
-        return m_container;
-    }
-
-    // DANGER! If you're not careful with this function, you may irreversably break the map
-    container_type& modify_container() noexcept
-    {
-        return m_container;
-    }
-
-#if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
-    ///////////////////////////////////////////////////////////////////////////////////
-    // const char* overloads for maps with an std::string key to avoid allocs
-    iterator lower_bound(const char* k)
-    {
-        static_assert(std::is_same<std::string, key_type>::value, "flat_map::lower_bound(const char*) works only for std::strings");
-        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
-        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
-        {
-            return strcmp(a.first.c_str(), b) < 0;
-        });
-    }
-
-    const_iterator lower_bound(const char* k) const
-    {
-        static_assert(std::is_same<std::string, key_type>::value, "flat_map::lower_bound(const char*) works only for std::strings");
-        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
-        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
-        {
-            return strcmp(a.first.c_str(), b) < 0;
-        });
-    }
-
-    mapped_type& operator[](const char* k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && i->first == k)
-        {
-            return i->second;
-        }
-
-        i = m_container.emplace(i, k, mapped_type());
-        return i->second;
-    }
-
-    mapped_type& at(const char* k)
-    {
-        auto i = lower_bound(k);
-        if (i == end() || i->first != k)
-        {
-            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
-        }
-
-        return i->second;
-    }
-
-    const mapped_type& at(const char* k) const
-    {
-        auto i = lower_bound(k);
-        if (i == end() || i->first != k)
-        {
-            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
-        }
-
-        return i->second;
-    }
-
-    iterator find(const char* k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && i->first == k)
-            return i;
-
-        return end();
-    }
-
-    const_iterator find(const char* k) const
-    {
-        auto i = lower_bound(k);
-        if (i != end() && i->first == k)
-            return i;
-
-        return end();
-    }
-
-    size_t count(const char* k) const
-    {
-        return find(k) == end() ? 0 : 1;
-    }
-
-#endif // !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
-
-private:
-    const pair_compare_t& get_cmp() const { return static_cast<const pair_compare_t&>(*this); }
-    pair_compare_t& get_cmp() { return static_cast<pair_compare_t&>(*this); }
-
-    container_type m_container;
-};
-
-template <typename Key, typename T, typename Compare, typename Container>
-bool operator==(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
-{
-    return a.container() == b.container();
-}
-
-template <typename Key, typename T, typename Compare, typename Container>
-bool operator!=(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
-{
-    return a.container() != b.container();
-}
-template <typename Key, typename T, typename Compare, typename Container>
-bool operator<(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
-{
-    return a.container() < b.container();
-}
-
-}
-
-#if defined(CHOBO_FLAT_MAP_TEST_WITH_DOCTEST)
-
-#include <string>
-
-namespace chobo_flat_map_test
-{
-
-// struct with no operator==
-struct int_wrap
-{
-    int_wrap() = default;
-    int_wrap(int i) : val(i) {}
-    int val;
-
-    struct compare
-    {
-        bool operator()(const int_wrap& a, const int_wrap& b) const
-        {
-            return a.val < b.val;
-        }
-    };
-};
-
-}
-
-TEST_CASE("[flat_map] test")
-{
-    using namespace chobo;
-    using namespace chobo_flat_map_test;
-
-    flat_map<int, float> ifmap;
-    CHECK(ifmap.empty());
-    CHECK(ifmap.size() == 0);
-    CHECK(ifmap.capacity() == 0);
-    CHECK(ifmap.begin() == ifmap.end());
-
-    ifmap[1] = 3.2f;
-    CHECK(ifmap.size() == 1);
-
-    auto ifit = ifmap.begin();
-    CHECK(ifit->first == 1);
-    CHECK(ifit->second == 3.2f);
-    CHECK(ifmap[1] == 3.2f);
-    CHECK(ifmap.at(1) == 3.2f);
-    CHECK(ifmap.count(1) == 1);
-    CHECK(ifmap.count(5) == 0);
-
-    ++ifit;
-    CHECK(ifit == ifmap.end());
-
-    auto res = ifmap.insert(std::make_pair(6, 3.14f));
-    CHECK(res.second);
-    CHECK(res.first == ifmap.begin() + 1);
-
-    res = ifmap.emplace(3, 5.5f);
-    CHECK(res.second);
-    CHECK(res.first == ifmap.begin() + 1);
-
-    res = ifmap.emplace(6, 8.f);
-    CHECK(!res.second);
-    CHECK(res.first == ifmap.begin() + 2);
-
-    ifmap[2] = 5;
-    ifmap[52] = 15;
-    ifmap[12] = 1;
-    CHECK(ifmap.size() == 6);
-
-    auto cmp = [](const flat_map<int, float>::value_type& a, const flat_map<int, float>::value_type& b) -> bool
-    {
-        return a.first < b.first;
-    };
-
-    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
-
-    ifmap.erase(12);
-    CHECK(ifmap.size() == 5);
-
-    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
-
-    ifit = ifmap.find(12);
-    CHECK(ifit == ifmap.end());
-
-    ifit = ifmap.find(6);
-    CHECK(ifit != ifmap.end());
-    ifmap.erase(ifit);
-
-    CHECK(ifmap.size() == 4);
-    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
-    ifit = ifmap.find(6);
-    CHECK(ifit == ifmap.end());
-
-    //
-
-    flat_map<std::string, int> simap;
-
-    CHECK(simap["123"] == 0);
-
-    CHECK(simap.begin()->first.c_str() == "123");
-
-    ++simap["asd"];
-
-    auto siit = simap.find("asd");
-    CHECK(siit != simap.end());
-    CHECK(siit->second == 1);
-    CHECK(siit == simap.begin() + 1);
-
-    CHECK(simap.count("bababa") == 0);
-    CHECK(simap.count("asd") == 1);
-
-    std::string asd = "asd";
-    CHECK(simap.at(asd) == simap.at("asd"));
-
-    simap["0The quick brown fox jumps over the lazy dog"] = 555;
-    CHECK(simap.begin()->first[1] == 'T');
-    const void* cstr = simap.begin()->first.c_str();
-
-    auto simap2 = std::move(simap);
-    CHECK(simap.empty());
-    CHECK(simap2.begin()->first.c_str() == cstr);
-
-    simap = std::move(simap2);
-    CHECK(simap2.empty());
-    CHECK(simap.begin()->first.c_str() == cstr);
-
-    CHECK(simap2 != simap);
-    simap2 = simap;
-    CHECK(simap2 == simap);
-
-    // no == comparable tests
-    flat_map<int_wrap, int, int_wrap::compare> iwmap;
-    iwmap[5] = 1;
-    iwmap[20] = 15;
-    iwmap[10] = 5;
-
-    auto iwi = iwmap.emplace(3, 4);
-    CHECK(iwi.second == true);
-    CHECK(iwi.first == iwmap.begin());
-
-    CHECK(iwmap.begin()->first.val == 3);
-    CHECK(iwmap.begin()->second == 4);
-    CHECK(iwmap.rbegin()->first.val == 20);
-    CHECK(iwmap.rbegin()->second == 15);
-    CHECK(iwmap.at(10) == 5);
-
-    iwi = iwmap.insert(std::pair<int_wrap, int>(11, 6));
-    CHECK(iwi.second == true);
-    CHECK(iwi.first + 2 == iwmap.end());
-
-    CHECK(iwmap[11] == 6);
-
-    iwi = iwmap.emplace(10, 55);
-    CHECK(iwi.second == false);
-    CHECK(iwi.first->second == 5);
-
-    CHECK(iwmap.find(18) == iwmap.end());
-    CHECK(iwmap.find(11) != iwmap.end());
-
-    const auto ciwmap = iwmap;
-
-    CHECK(ciwmap.begin()->first.val == 3);
-    CHECK(ciwmap.begin()->second == 4);
-    CHECK(ciwmap.rbegin()->first.val == 20);
-    CHECK(ciwmap.rbegin()->second == 15);
-    CHECK(ciwmap.at(10) == 5);
-
-    CHECK(ciwmap.find(18) == ciwmap.end());
-    CHECK(ciwmap.find(11) != ciwmap.end());
-
-    // swap
-    flat_map<int, int> m1, m2;
-    m1.reserve(10);
-    m1[1] = 2;
-    m1[2] = 5;
-    auto m1c = m1.capacity();
-
-    CHECK(m2.capacity() == 0);
-    m1.swap(m2);
-
-    CHECK(m2.size() == 2);
-    CHECK(m2.capacity() == m1c);
-    CHECK(m1.capacity() == 0);
-
-    // self usurp
-    m2 = m2;
-    CHECK(m2.size() == 2);
-    CHECK(m2.capacity() == m1c);
-
-    // stateful comparator
-    struct distance_from_constant {
-        int middle = 0;
-        bool operator()(const int& a, const int& b) const { return std::abs(a - middle) < std::abs(b - middle); }
-    };
-
-    flat_map<int, char, distance_from_constant> distmapx(distance_from_constant{10}, {});
-    for (auto v : {0, 9, 10, 11, 12, 20})
-        distmapx.emplace(v, 'x');
-
-    const std::vector<std::pair<int, char>> distmapx_equiv = {{10, 'x'}, {9, 'x'}, {12, 'x'}, {0, 'x'}};
-    CHECK(distmapx.container() == distmapx_equiv);
-
-    flat_map<int, char, distance_from_constant> distmapy(distance_from_constant{5}, {});
-    for (auto v : {5, 10})
-        distmapy.emplace(v, 'y');
-
-    const std::vector<std::pair<int, char>> distmapy_equiv = {{5, 'y'}, {10, 'y'}};
-    CHECK(distmapy.container() == distmapy_equiv);
-
-    // swap should also swap comparator state
-    distmapy.swap(distmapx);
-    distmapy.clear();
-    for (auto v : {5, 10})
-        distmapy.emplace(v, 'z');
-
-    const std::vector<std::pair<int, char>> distmapz_equiv = {{10, 'z'}, {5, 'z'}};
-    CHECK(distmapy.container() == distmapz_equiv);
-}
-
-#if defined(CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST)
-
-TEST_CASE("[flat_map] static_vector test")
-{
-    using namespace chobo;
-
-    flat_map<int, char, std::less<int>, static_vector<std::pair<int, char>, 10>> smap;
-    CHECK(smap.empty());
-    CHECK(smap.size() == 0);
-    CHECK(smap.capacity() == 10);
-    CHECK(smap.begin() == smap.end());
-
-    smap[1] = 3;
-    CHECK(smap.size() == 1);
-
-    auto ifit = smap.begin();
-    CHECK(ifit->first == 1);
-    CHECK(ifit->second == 3);
-    CHECK(smap[1] == 3);
-    CHECK(smap.at(1) == 3);
-    CHECK(smap.count(1) == 1);
-    CHECK(smap.count(5) == 0);
-
-    ++ifit;
-    CHECK(ifit == smap.end());
-
-    auto res = smap.insert(std::make_pair(6, 3));
-    CHECK(res.second);
-    CHECK(res.first == smap.begin() + 1);
-
-    res = smap.emplace(3, 5);
-    CHECK(res.second);
-    CHECK(res.first == smap.begin() + 1);
-
-    res = smap.emplace(6, 8);
-    CHECK(!res.second);
-    CHECK(res.first == smap.begin() + 2);
-
-    smap[2] = 5;
-    smap[52] = 15;
-    smap[12] = 1;
-    CHECK(smap.size() == 6);
-
-    auto cmp = [](const flat_map<int, float>::value_type& a, const flat_map<int, float>::value_type& b) -> bool
-    {
-        return a.first < b.first;
-    };
-
-    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
-
-    smap.erase(12);
-    CHECK(smap.size() == 5);
-
-    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
-
-    ifit = smap.find(12);
-    CHECK(ifit == smap.end());
-
-    ifit = smap.find(6);
-    CHECK(ifit != smap.end());
-    smap.erase(ifit);
-
-    CHECK(smap.size() == 4);
-    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
-    ifit = smap.find(6);
-    CHECK(ifit == smap.end());
-}
-
-#endif
-
-#if defined(CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST)
-
-TEST_CASE("[flat_map] vector_ptr test")
-{
-    using namespace chobo;
-    flat_map<int, char, std::less<int>, vector_ptr<std::pair<int, char>>> smap;
-
-    std::vector<std::pair<int, char>> vec;
-    smap.modify_container().reset(&vec);
-
-    smap[1] = '1';
-    smap[3] = '3';
-
-    CHECK(smap.at(3) == '3');
-
-    auto smap2 = smap;
-    CHECK(smap2.size() == 2);
-    CHECK(smap2[1] == '1');
-    CHECK(smap2.at(3) == '3');
-
-    smap2[0] = '0';
-
-    CHECK(smap.size() == 3);
-    CHECK(smap[0] == '0');
-
-    smap.clear();
-
-    CHECK(smap2.empty());
-}
-
-#endif
-
-
-#endif
-

+ 0 - 591
Include/RmlUi/Core/Containers/chobo/flat_set.hpp

@@ -1,591 +0,0 @@
-// chobo-flat-set v1.00
-//
-// std::set-like class with an underlying vector
-//
-// Unofficial, chobo-like container, largely based on chobo::flat_set
-//
-// MIT License:
-// Copyright(c) 2019 Michael R. P. Ragazzon 
-// Copyright(c) 2016 Chobolabs Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files(the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and / or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions :
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-//
-//                  VERSION HISTORY
-//
-//  1.00 (2019-05-04) First public release
-//
-//
-//                  DOCUMENTATION
-//
-// Simply include this file wherever you need.
-// It defines the class chobo::flat_set, which is an almsot drop-in replacement
-// of std::set. Flat set has an optional underlying container which by default
-// is std::vector. Thus the items in the set are in a continuous block of
-// memory. Thus iterating over the set is cache friendly, at the cost of
-// O(n) for insert and erase.
-//
-// The elements inside (like in std::set) are kept in an order sorted by key.
-// Getting a value by key is O(log2 n)
-//
-// It generally performs much faster than std::set for smaller sets of elements
-//
-// The difference with std::set, which makes flat_set an not-exactly-drop-in
-// replacement is the last template argument:
-// * std::set has <value, compare, allocator>
-// * chobo::flat_set has <value, compare, container>
-// The container must be an std::vector compatible type (chobo::static_vector
-// and chobo::vector_ptr are, for example, viable). The container value type
-// must be 'value'.
-//
-//                  Changing the allocator.
-//
-// If you want to change the allocator of flat set, you'll have to provide a
-// container with the appriate one. Example:
-//
-// chobo::flat_set<
-//      string,
-//      less<string>,
-//      std::vector<<string>, MyAllocator<string>>
-//  > myset
-//
-//
-//                  Configuration
-//
-// chobo::flat_set has one configurable setting:
-//
-// 1. const char* overloads
-// By default chobo::flat_set provides overloads for the access methods
-// (at, operator[], find, lower_bound, count) for const char* for cases when
-// std::string is the key, so that no allocations happen when accessing with
-// a C-string of a string literal.
-// However if const char* or any other class with implicit conversion from
-// const char* is the key, they won't compile.
-// If you plan on using flat_set with such keys, you'll need to define
-// CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS before including the header
-//
-//
-//                  TESTS
-//
-// The tests are included in the header file and use doctest (https://github.com/onqtam/doctest).
-// To run them, define CHOBO_FLAT_SET_TEST_WITH_DOCTEST before including
-// the header in a file which has doctest.h already included.
-//
-// Additionally if chobo::static_vector is also available you may define
-// CHOBO_FLAT_SET_TEST_STATIC_VECTOR_WITH_DOCTEST to test flat_set with an
-// unrelying static_vector
-//
-// Additionally if chobo::vector_ptr is also available you may define
-// CHOBO_FLAT_SET_TEST_VECTOR_PTR_WITH_DOCTEST to test flat_set with an
-// unrelying vector_ptr
-//
-#pragma once
-
-#include <vector>
-#include <algorithm>
-#include <type_traits>
-
-#if !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
-#include <cstring>
-#endif
-
-namespace chobo
-{
-
-template <typename T, typename Compare = std::less<T>, typename Container = std::vector<T>>
-class flat_set : private Compare
-{
-public:
-    typedef T key_type;
-    typedef T value_type;
-    typedef Container container_type;
-    typedef Compare key_compare;
-    typedef value_type& reference;
-    typedef const value_type& const_reference;
-    typedef typename container_type::allocator_type allocator_type;
-    typedef typename std::allocator_traits<allocator_type>::pointer pointer;
-    typedef typename std::allocator_traits<allocator_type>::pointer const_pointer;
-    typedef typename container_type::iterator iterator;
-    typedef typename container_type::const_iterator const_iterator;
-    typedef typename container_type::reverse_iterator reverse_iterator;
-    typedef typename container_type::const_reverse_iterator const_reverse_iterator;
-    typedef typename container_type::difference_type difference_type;
-    typedef typename container_type::size_type size_type;
-
-    flat_set()
-    {}
-
-    explicit flat_set(const key_compare& comp, const allocator_type& alloc = allocator_type())
-        : Compare(comp)
-        , m_container(alloc)
-    {}
-
-    flat_set(const flat_set& x) = default;
-    flat_set(flat_set&& x) noexcept = default;
-
-
-    flat_set(std::initializer_list<value_type> ilist)
-    {
-        m_container.reserve(ilist.size());
-        for (auto&& il : ilist)
-            emplace(il);
-    }
-
-    flat_set& operator=(const flat_set& x)
-    {
-        get_cmp() = x.get_cmp();
-        m_container = x.m_container;
-        return *this;
-    }
-    flat_set& operator=(flat_set&& x) noexcept
-    {
-        get_cmp() = std::move(x.get_cmp());
-        m_container = std::move(x.m_container);
-        return *this;
-    }
-
-    iterator begin() noexcept { return m_container.begin(); }
-    const_iterator begin() const noexcept { return m_container.begin(); }
-    iterator end() noexcept { return m_container.end(); }
-    const_iterator end() const noexcept { return m_container.end(); }
-    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
-    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
-    reverse_iterator rend() noexcept { return m_container.rend(); }
-    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
-    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
-    const_iterator cend() const noexcept { return m_container.cend(); }
-
-    bool empty() const noexcept { return m_container.empty(); }
-    size_type size() const noexcept { return m_container.size(); }
-    size_type max_size() const noexcept { return m_container.max_size(); }
-
-    void reserve(size_type count) { return m_container.reserve(count); }
-    size_type capacity() const noexcept { return m_container.capacity(); }
-
-    void clear() noexcept { m_container.clear(); }
-
-    iterator lower_bound(const key_type& k)
-    {
-        return std::lower_bound(m_container.begin(), m_container.end(), k, get_cmp());
-    }
-
-    const_iterator lower_bound(const key_type& k) const
-    {
-        return std::lower_bound(m_container.begin(), m_container.end(), k, get_cmp());
-    }
-
-    iterator find(const key_type& k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-            return i;
-
-        return end();
-    }
-
-    const_iterator find(const key_type& k) const
-    {
-        auto i = lower_bound(k);
-        if (i != end() && !get_cmp()(k, *i))
-            return i;
-
-        return end();
-    }
-
-    size_t count(const key_type& k) const
-    {
-        return find(k) == end() ? 0 : 1;
-    }
-
-    template <typename P>
-    std::pair<iterator, bool> insert(P&& val)
-    {
-        auto i = lower_bound(val);
-        if (i != end() && !get_cmp()(val, *i))
-        {
-            return { i, false };
-        }
-
-        return{ m_container.emplace(i, std::forward<P>(val)), true };
-    }
-
-    template <typename InputIt >
-    void insert(InputIt first, InputIt last)
-    {
-        difference_type diff = std::distance(first, last);
-        if(diff > 0) reserve(size() + (size_t)diff);
-        for (auto it = first; it != last; ++it)
-            emplace(*it);
-    }
-
-    template <class... Args>
-    std::pair<iterator, bool> emplace(Args&&... args)
-    {
-        value_type val(std::forward<Args>(args)...);
-        return insert(std::move(val));
-    }
-
-    iterator erase(const_iterator pos)
-    {
-        return m_container.erase(pos);
-    }
-
-    size_type erase(const key_type& k)
-    {
-        auto i = find(k);
-        if (i == end())
-        {
-            return 0;
-        }
-
-        erase(i);
-        return 1;
-    }
-
-    void swap(flat_set& x) noexcept
-    {
-        std::swap(get_cmp(), x.get_cmp());
-        m_container.swap(x.m_container);
-    }
-
-    const container_type& container() const noexcept
-    {
-        return m_container;
-    }
-
-    // DANGER! If you're not careful with this function, you may irreversably break the set
-    container_type& modify_container() noexcept
-    {
-        return m_container;
-    }
-
-#if !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
-    ///////////////////////////////////////////////////////////////////////////////////
-    // const char* overloads for sets with an std::string key to avoid allocs
-    iterator lower_bound(const char* k)
-    {
-        static_assert(std::is_same<std::string, key_type>::value, "flat_set::lower_bound(const char*) works only for std::strings");
-        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_set::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
-        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
-        {
-            return strcmp(a.c_str(), b) < 0;
-        });
-    }
-
-    const_iterator lower_bound(const char* k) const
-    {
-        static_assert(std::is_same<std::string, key_type>::value, "flat_set::lower_bound(const char*) works only for std::strings");
-        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_set::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
-        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
-        {
-            return strcmp(a.c_str(), b) < 0;
-        });
-    }
-
-    iterator find(const char* k)
-    {
-        auto i = lower_bound(k);
-        if (i != end() && *i == k)
-            return i;
-
-        return end();
-    }
-
-    const_iterator find(const char* k) const
-    {
-        auto i = lower_bound(k);
-        if (i != end() && *i == k)
-            return i;
-
-        return end();
-    }
-
-    size_t count(const char* k) const
-    {
-        return find(k) == end() ? 0 : 1;
-    }
-
-#endif // !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
-
-private:
-    const Compare& get_cmp() const { return static_cast<const Compare&>(*this); }
-    Compare& get_cmp() { return static_cast<Compare&>(*this); }
-
-    container_type m_container;
-};
-
-template <typename T, typename Compare, typename Container>
-bool operator==(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
-{
-    return a.container() == b.container();
-}
-template <typename T, typename Compare, typename Container>
-bool operator!=(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
-{
-    return a.container() != b.container();
-}
-template <typename T, typename Compare, typename Container>
-bool operator<(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
-{
-    return a.container() < b.container();
-}
-
-}
-
-#if defined(CHOBO_FLAT_SET_TEST_WITH_DOCTEST)
-
-#include <string>
-
-namespace chobo_flat_set_test
-{
-
-// struct with no operator==
-struct int_wrap
-{
-    int_wrap() = default;
-    int_wrap(int i) : val(i) {}
-    int val;
-
-    struct compare
-    {
-        bool operator()(const int_wrap& a, const int_wrap& b) const
-        {
-            return a.val < b.val;
-        }
-    };
-};
-
-}
-
-TEST_CASE("[flat_set] test")
-{
-    using namespace chobo;
-    using namespace chobo_flat_set_test;
-
-    flat_set<int> iset;
-    CHECK(iset.empty());
-    CHECK(iset.size() == 0);
-    CHECK(iset.capacity() == 0);
-    CHECK(iset.begin() == iset.end());
-
-    iset.insert(3);
-    CHECK(iset.size() == 1);
-
-    auto iit = iset.begin();
-    CHECK(*iit == 3);
-    CHECK(iset.count(3) == 1);
-    CHECK(iset.count(5) == 0);
-
-    ++iit;
-    CHECK(iit == iset.end());
-
-    auto res = iset.insert(6);
-    CHECK(res.second);
-    CHECK(res.first == iset.begin() + 1);
-
-    res = iset.emplace(4);
-    CHECK(res.second);
-    CHECK(res.first == iset.begin() + 1);
-
-    res = iset.emplace(6);
-    CHECK(!res.second);
-    CHECK(res.first == iset.begin() + 2);
-
-    iset.emplace(3);
-    CHECK(iset.size() == 3);
-    iset.emplace(9);
-    iset.insert(9);
-    iset.emplace(12);
-    CHECK(iset.size() == 5);
-
-    auto cmp = [](const flat_set<int>::value_type& a, const flat_set<int>::value_type& b) -> bool
-    {
-        return a < b;
-    };
-
-    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
-
-    iset.erase(12);
-    CHECK(iset.size() == 4);
-
-    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
-
-    iit = iset.find(11);
-    CHECK(iit == iset.end());
-
-    iit = iset.find(6);
-    CHECK(iit != iset.end());
-    iset.erase(iit);
-
-    CHECK(iset.size() == 3);
-    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
-    iit = iset.find(6);
-    CHECK(iit == iset.end());
-
-    //
-
-    flat_set<std::string> sset;
-
-    CHECK(sset.find("123") == sset.end());
-    sset.emplace("123");
-    CHECK(*sset.begin() == "123");
-
-    sset.emplace("asd");
-
-    auto sit = sset.find("asd");
-    CHECK(sit != sset.end());
-    CHECK(sit == sset.begin() + 1);
-
-    CHECK(sset.count("bababa") == 0);
-    CHECK(sset.count("asd") == 1);
-
-    std::string asd = "asd";
-    CHECK(sset.find(asd) == sset.find("asd"));
-
-    sset.emplace("0The quick brown fox jumps over the lazy dog");
-    CHECK(sset.begin()->at(1) == 'T');
-    const void* cstr = sset.begin()->c_str();
-
-    auto sset2 = std::move(sset);
-    CHECK(sset.empty());
-    CHECK(sset2.begin()->c_str() == cstr);
-
-    sset = std::move(sset2);
-    CHECK(sset2.empty());
-    CHECK(sset.begin()->c_str() == cstr);
-
-    CHECK(sset2 != sset);
-    sset2 = sset;
-    CHECK(sset2 == sset);
-
-    // no == comparable tests
-    flat_set<int_wrap, int_wrap::compare> iwset;
-    iwset.emplace(5);
-    iwset.emplace(20);
-    iwset.emplace(10);
-
-    auto iwi = iwset.emplace(3);
-    CHECK(iwi.second == true);
-    CHECK(iwi.first == iwset.begin());
-
-    CHECK(iwset.begin()->val == 3);
-    CHECK(iwset.rbegin()->val == 20);
-
-    iwi = iwset.insert(int_wrap(11));
-    CHECK(iwi.second == true);
-    CHECK(iwi.first + 2 == iwset.end());
-
-    iwi = iwset.emplace(int_wrap(10));
-    CHECK(iwi.second == false);
-
-    CHECK(iwset.find(18) == iwset.end());
-    CHECK(iwset.find(11) != iwset.end());
-
-    const auto ciwset = iwset;
-
-    CHECK(ciwset.begin()->val == 3);
-    CHECK(ciwset.rbegin()->val == 20);
-
-    CHECK(ciwset.find(18) == ciwset.end());
-    CHECK(ciwset.find(11) != ciwset.end());
-
-    // swap
-    flat_set<int> m1, m2;
-    m1.reserve(10);
-    m1.emplace(1);
-    m1.emplace(2);
-    auto m1c = m1.capacity();
-
-    CHECK(m2.capacity() == 0);
-    m1.swap(m2);
-
-    CHECK(m2.size() == 2);
-    CHECK(m2.capacity() == m1c);
-    CHECK(m1.capacity() == 0);
-
-    // self usurp
-    m2 = m2;
-    CHECK(m2.size() == 2);
-    CHECK(m2.capacity() == m1c);
-
-    // initializer list
-    flat_set<std::string> ilset = { "hello", "great", "magnificent", "world", "hello", "again" };
-    CHECK(ilset.size() == 5);
-    CHECK(std::is_sorted(ilset.begin(), ilset.end()));
-
-    ilset = { "b", "a" };
-    CHECK(ilset.size() == 2);
-    CHECK(std::is_sorted(ilset.begin(), ilset.end()));
-
-    // stateful comparator
-    struct distance_from_constant {
-        int middle = 0;
-        bool operator()(const int& a, const int& b) const { return std::abs(a - middle) < std::abs(b - middle); }
-    };
-
-    flat_set<int, distance_from_constant> distmapx(distance_from_constant{10}, {});
-    for (auto v : {0, 9, 10, 11, 12, 20})
-        distmapx.emplace(v);
-
-    const std::vector<int> distmapx_equiv = {10, 9, 12, 0};
-    CHECK(distmapx.container() == distmapx_equiv);
-
-    flat_set<int, distance_from_constant> distmapy(distance_from_constant{5}, {});
-    for (auto v : {5, 10})
-        distmapy.emplace(v);
-
-    const std::vector<int> distmapy_equiv = {5, 10};
-    CHECK(distmapy.container() == distmapy_equiv);
-
-    // swap should also swap comparator state
-    distmapy.swap(distmapx);
-    distmapy.clear();
-    for (auto v : {5, 10})
-        distmapy.emplace(v);
-
-    const std::vector<int> distmapz_equiv = {10, 5};
-    CHECK(distmapy.container() == distmapz_equiv);
-}
-
-#if defined(CHOBO_FLAT_SET_TEST_STATIC_VECTOR_WITH_DOCTEST)
-
-TEST_CASE("[flat_set] static_vector test")
-{
-    using namespace chobo;
-    
-    // Not implemented
-}
-
-#endif
-
-#if defined(CHOBO_FLAT_SET_TEST_VECTOR_PTR_WITH_DOCTEST)
-
-TEST_CASE("[flat_set] vector_ptr test")
-{
-    using namespace chobo;
-
-    // Not implemented
-}
-
-#endif
-
-
-#endif
-

+ 387 - 0
Include/RmlUi/Core/Containers/itlib/flat_map.hpp

@@ -0,0 +1,387 @@
+// itlib-flat-map v1.07
+//
+// std::map-like class with an underlying vector
+//
+// SPDX-License-Identifier: MIT
+// MIT License:
+// Copyright(c) 2016-2019 Chobolabs Inc.
+// Copyright(c) 2020-2023 Borislav Stanimirov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files(the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and / or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions :
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+//                  VERSION HISTORY
+//
+//  1.07 (2023-01-14) Inherit from Compare to enable empty base optimization
+//  1.06 (2023-01-09) Fixed transparency for std::string_view
+//  1.05 (2022-09-17) upper_bound and equal_range
+//  1.04 (2022-07-07) Transparent lookups (C++14 style)
+//                    Transparent construction
+//  1.03 (2022-04-14) Noxcept move construct and assign
+//  1.02 (2021-09-28) Fixed construction from std::initializer_list which
+//                    allowed duplicate keys to find their wey in the map
+//  1.01 (2021-09-15) Constructors from std::initializer_list
+//  1.00 (2020-10-14) Rebranded release from chobo-flat-map
+//
+//
+//                  DOCUMENTATION
+//
+// Simply include this file wherever you need.
+// It defines the class itlib::flat_map, which is an almsot drop-in replacement
+// of std::map. Flat map has an optional underlying container which by default
+// is std::vector. Thus the items in the map are in a continuous block of
+// memory. Thus iterating over the map is cache friendly, at the cost of
+// O(n) for insert and erase.
+//
+// The elements inside (like in std::map) are kept in an order sorted by key.
+// Getting a value by key is O(log2 n)
+//
+// It generally performs much faster than std::map for smaller sets of elements
+//
+// The difference with std::map, which makes flat_map an not-exactly-drop-in
+// replacement is the last template argument:
+// * std::map has <key, value, compare, allocator>
+// * itlib::flat_map has <key, value, compare, container>
+// The container must be an std::vector compatible type (itlib::static_vector
+// is, for example, viable). The container value type must be
+// std::pair<key, value>.
+//
+//                  Changing the allocator.
+//
+// If you want to change the allocator of flat map, you'll have to provide a
+// container with the appropriate one. Example:
+//
+// itlib::flat_map<
+//      string,
+//      int,
+//      less<string>,
+//      std::vector<pair<string, int>, MyAllocator<pair<string, int>>
+//  > mymap
+//
+//
+//                  Configuration
+//
+// Throw
+// Whether to throw exceptions: when `at` is called with a non-existent key.
+// By default, like std::map, it throws an std::out_of_range exception. If you define
+// ITLIB_FLAT_MAP_NO_THROW before including this header, the exception will
+// be substituted by an assertion.
+//
+//
+//                  TESTS
+//
+// You can find unit tests for static_vector in its official repo:
+// https://github.com/iboB/itlib/blob/master/test/
+//
+#pragma once
+
+#include <vector>
+#include <algorithm>
+#include <type_traits>
+
+#if !defined(ITLIB_FLAT_MAP_NO_THROW)
+#   include <stdexcept>
+#   define I_ITLIB_THROW_FLAT_MAP_OUT_OF_RANGE() throw std::out_of_range("itlib::flat_map out of range")
+#else
+#   include <cassert>
+#   define I_ITLIB_THROW_FLAT_MAP_OUT_OF_RANGE() assert(false && "itlib::flat_map out of range")
+#endif
+
+namespace itlib
+{
+
+namespace fmimpl
+{
+struct less
+{
+    template <typename T, typename U>
+    auto operator()(const T& t, const U& u) const -> decltype(t < u)
+    {
+        return t < u;
+    }
+};
+
+template <typename Key, typename T, typename Compare>
+struct pair_compare : public Compare
+{
+    using value_type = std::pair<Key, T>;
+    pair_compare() = default;
+    pair_compare(const Compare& kc) : Compare(kc) {}
+    bool operator()(const value_type& a, const value_type& b) const { return Compare::operator()(a.first, b.first); }
+    template <typename K> bool operator()(const value_type& a, const K& b) const { return Compare::operator()(a.first, b); }
+    template <typename K> bool operator()(const K& a, const value_type& b) const { return Compare::operator()(a, b.first); }
+};
+}
+
+template <typename Key, typename T, typename Compare = fmimpl::less, typename Container = std::vector<std::pair<Key, T>>>
+class flat_map : private fmimpl::pair_compare<Key, T, Compare>
+{
+    Container m_container;
+    using pair_compare = fmimpl::pair_compare<Key, T, Compare>;
+    pair_compare& cmp() { return *this; }
+    const pair_compare& cmp() const { return *this; }
+public:
+    typedef Key key_type;
+    typedef T mapped_type;
+    typedef std::pair<Key, T> value_type;
+    typedef Container container_type;
+    typedef Compare key_compare;
+    typedef value_type& reference;
+    typedef const value_type& const_reference;
+    typedef typename container_type::allocator_type allocator_type;
+    typedef typename std::allocator_traits<allocator_type>::pointer pointer;
+    typedef typename std::allocator_traits<allocator_type>::pointer const_pointer;
+    typedef typename container_type::iterator iterator;
+    typedef typename container_type::const_iterator const_iterator;
+    typedef typename container_type::reverse_iterator reverse_iterator;
+    typedef typename container_type::const_reverse_iterator const_reverse_iterator;
+    typedef typename container_type::difference_type difference_type;
+    typedef typename container_type::size_type size_type;
+
+    flat_map() = default;
+
+    explicit flat_map(const key_compare& comp, const allocator_type& alloc = allocator_type())
+        : pair_compare(comp)
+        , m_container(alloc)
+    {}
+
+    flat_map(std::initializer_list<value_type> init, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type())
+        : pair_compare(comp)
+        , m_container(std::move(init), alloc)
+    {
+        std::sort(m_container.begin(), m_container.end(), cmp());
+        auto new_end = std::unique(m_container.begin(), m_container.end(), [this](const value_type& a, const value_type& b) {
+            return !cmp()(a, b) && !cmp()(b, a);
+        });
+        m_container.erase(new_end, m_container.end());
+    }
+
+    flat_map(std::initializer_list<value_type> init, const allocator_type& alloc)
+        : flat_map(std::move(init), key_compare(), alloc)
+    {}
+
+    flat_map(const flat_map& x) = default;
+    flat_map& operator=(const flat_map& x) = default;
+
+    flat_map(flat_map&& x) noexcept = default;
+    flat_map& operator=(flat_map&& x) noexcept = default;
+
+    iterator begin() noexcept { return m_container.begin(); }
+    const_iterator begin() const noexcept { return m_container.begin(); }
+    iterator end() noexcept { return m_container.end(); }
+    const_iterator end() const noexcept { return m_container.end(); }
+    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
+    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
+    reverse_iterator rend() noexcept { return m_container.rend(); }
+    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
+    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
+    const_iterator cend() const noexcept { return m_container.cend(); }
+
+    bool empty() const noexcept { return m_container.empty(); }
+    size_type size() const noexcept { return m_container.size(); }
+    size_type max_size() const noexcept { return m_container.max_size(); }
+
+    void reserve(size_type count) { return m_container.reserve(count); }
+    size_type capacity() const noexcept { return m_container.capacity(); }
+
+    void clear() noexcept { m_container.clear(); }
+
+    template <typename K>
+    iterator lower_bound(const K& k)
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    const_iterator lower_bound(const K& k) const
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    iterator upper_bound(const K& k)
+    {
+        return std::upper_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    const_iterator upper_bound(const K& k) const
+    {
+        return std::upper_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    std::pair<iterator, iterator> equal_range(const K& k)
+    {
+        return std::equal_range(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    std::pair<const_iterator, const_iterator> equal_range(const K& k) const
+    {
+        return std::equal_range(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    iterator find(const K& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !cmp()(k, *i))
+            return i;
+
+        return end();
+    }
+
+    template <typename K>
+    const_iterator find(const K& k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !cmp()(k, *i))
+            return i;
+
+        return end();
+    }
+
+    template <typename K>
+    size_t count(const K& k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+    template <typename P>
+    std::pair<iterator, bool> insert(P&& val)
+    {
+        auto i = lower_bound(val.first);
+        if (i != end() && !cmp()(val.first, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, std::forward<P>(val)), true };
+    }
+
+    std::pair<iterator, bool> insert(const value_type& val)
+    {
+        auto i = lower_bound(val.first);
+        if (i != end() && !cmp()(val.first, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, val), true };
+    }
+
+    template <typename... Args>
+    std::pair<iterator, bool> emplace(Args&&... args)
+    {
+        value_type val(std::forward<Args>(args)...);
+        return insert(std::move(val));
+    }
+
+    iterator erase(const_iterator pos)
+    {
+        return m_container.erase(pos);
+    }
+
+    iterator erase(iterator pos)
+    {
+        return m_container.erase(const_iterator(pos));
+    }
+
+    template <typename K>
+    size_type erase(const K& k)
+    {
+        auto i = find(k);
+        if (i == end())
+        {
+            return 0;
+        }
+
+        erase(i);
+        return 1;
+    }
+
+    template <typename K>
+    typename std::enable_if<std::is_constructible<key_type, K>::value,
+    mapped_type&>::type operator[](K&& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !cmp()(k, *i))
+        {
+            return i->second;
+        }
+
+        i = m_container.emplace(i, std::forward<K>(k), mapped_type());
+        return i->second;
+    }
+
+    mapped_type& at(const key_type& k)
+    {
+        auto i = lower_bound(k);
+        if (i == end() || cmp()(*i, k))
+        {
+            I_ITLIB_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    const mapped_type& at(const key_type& k) const
+    {
+        auto i = lower_bound(k);
+        if (i == end() || cmp()(*i, k))
+        {
+            I_ITLIB_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    void swap(flat_map& x)
+    {
+        std::swap(cmp(), x.cmp());
+        m_container.swap(x.m_container);
+    }
+
+    const container_type& container() const noexcept
+    {
+        return m_container;
+    }
+
+    // DANGER! If you're not careful with this function, you may irreversably break the map
+    container_type& modify_container() noexcept
+    {
+        return m_container;
+    }
+};
+
+template <typename Key, typename T, typename Compare, typename Container>
+bool operator==(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
+{
+    return a.container() == b.container();
+}
+
+template <typename Key, typename T, typename Compare, typename Container>
+bool operator!=(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
+{
+    return a.container() != b.container();
+}
+
+}

+ 325 - 0
Include/RmlUi/Core/Containers/itlib/flat_set.hpp

@@ -0,0 +1,325 @@
+// itlib-flat-set v1.06
+//
+// std::set-like class with an underlying vector
+//
+// SPDX-License-Identifier: MIT
+// MIT License:
+// Copyright(c) 2021-2023 Borislav Stanimirov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files(the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and / or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions :
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+//                  VERSION HISTORY
+//
+//  1.06 (2023-01-14) Fixed initialization with custom Compare when equivalence
+//                    is not the same as `==`.
+//                    Inherit from Compare to enable empty base optimization
+//  1.05 (2022-09-17) upper_bound and equal_range
+//  1.04 (2022-06-23) Transparent lookups (C++14 style)
+//  1.03 (2022-04-14) Noxcept move construct and assign
+//  1.02 (2021-09-28) Fixed construction from std::initializer_list which
+//                    allowed duplicate elements to find their wey in the set
+//  1.01 (2021-09-15) Constructors from std::initializer_list
+//  1.00 (2021-08-10) Initial-release
+//
+//
+//                  DOCUMENTATION
+//
+// Simply include this file wherever you need.
+// It defines the class itlib::flat_set, which is an almsot drop-in replacement
+// of std::set. Flat set has an optional underlying container which by default
+// is std::vector. Thus the items in the set are in a continuous block of
+// memory. Thus iterating over the set is cache friendly, at the cost of
+// O(n) for insert and erase.
+//
+// The elements inside (like in std::set) are kept in an order sorted by key.
+// Getting a value by key is O(log2 n)
+//
+// It generally performs much faster than std::set for smaller sets of elements
+//
+// The difference with std::set, which makes flat_set an not-exactly-drop-in
+// replacement is the last template argument:
+// * std::set has <key, compare, allocator>
+// * itlib::flat_set has <key, compare, container>
+// The container must be an std::vector compatible type (itlib::static_vector
+// is, for example, viable). The container value type must be `key`
+//
+//                  Changing the allocator.
+//
+// If you want to change the allocator of flat set, you'll have to provide a
+// container with the appropriate one. Example:
+//
+// itlib::flat_set<
+//      string,
+//      less<string>,
+//      std::vector<string, MyAllocator<string>>
+//  > myset
+//
+//
+//                  TESTS
+//
+// You can find unit tests for static_vector in its official repo:
+// https://github.com/iboB/itlib/blob/master/test/
+//
+#pragma once
+
+#include <vector>
+#include <algorithm>
+#include <type_traits>
+
+namespace itlib
+{
+
+namespace fsimpl
+{
+struct less // so as not to clash with map_less
+{
+    template <typename T, typename U>
+    auto operator()(const T& t, const U& u) const -> decltype(t < u)
+    {
+        return t < u;
+    }
+};
+}
+
+template <typename Key, typename Compare = fsimpl::less, typename Container = std::vector<Key>>
+class flat_set : private /*EBO*/ Compare
+{
+    Container m_container;
+    Compare& cmp() { return *this; }
+    const Compare& cmp() const { return *this; }
+public:
+    using key_type = Key;
+    using value_type = Key;
+    using container_type = Container;
+    using key_compare = Compare;
+    using reference = value_type&;
+    using const_reference = const value_type& ;
+    using allocator_type = typename container_type::allocator_type;
+    using pointer = typename std::allocator_traits<allocator_type>::pointer;
+    using const_pointer = typename std::allocator_traits<allocator_type>::pointer;
+    using iterator = typename container_type::iterator;
+    using const_iterator = typename container_type::const_iterator;
+    using reverse_iterator = typename container_type::reverse_iterator;
+    using const_reverse_iterator = typename container_type::const_reverse_iterator;
+    using difference_type = typename container_type::difference_type;
+    using size_type = typename container_type::size_type;
+
+    flat_set()
+    {}
+
+    explicit flat_set(const key_compare& comp, const allocator_type& alloc = allocator_type())
+        : Compare(comp)
+        , m_container(alloc)
+    {}
+
+    explicit flat_set(container_type container, const key_compare& comp = key_compare())
+        : Compare(comp)
+        , m_container(std::move(container))
+    {
+        std::sort(m_container.begin(), m_container.end(), cmp());
+        auto new_end = std::unique(m_container.begin(), m_container.end(), [this](const value_type& a, const value_type& b) -> bool {
+            return !cmp()(a, b) && !cmp()(b, a);
+        });
+        m_container.erase(new_end, m_container.end());
+    }
+
+    flat_set(std::initializer_list<value_type> init, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type())
+        : flat_set(container_type(std::move(init), alloc), comp)
+    {}
+
+    flat_set(std::initializer_list<value_type> init, const allocator_type& alloc)
+        : flat_set(std::move(init), key_compare(), alloc)
+    {}
+
+    flat_set(const flat_set& x) = default;
+    flat_set& operator=(const flat_set& x) = default;
+
+    flat_set(flat_set&& x) noexcept = default;
+    flat_set& operator=(flat_set&& x) noexcept = default;
+
+    key_compare key_comp() const { return *this; }
+
+    iterator begin() noexcept { return m_container.begin(); }
+    const_iterator begin() const noexcept { return m_container.begin(); }
+    iterator end() noexcept { return m_container.end(); }
+    const_iterator end() const noexcept { return m_container.end(); }
+    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
+    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
+    reverse_iterator rend() noexcept { return m_container.rend(); }
+    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
+    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
+    const_iterator cend() const noexcept { return m_container.cend(); }
+
+    bool empty() const noexcept { return m_container.empty(); }
+    size_type size() const noexcept { return m_container.size(); }
+    size_type max_size() const noexcept { return m_container.max_size(); }
+
+    void reserve(size_type count) { return m_container.reserve(count); }
+    size_type capacity() const noexcept { return m_container.capacity(); }
+
+    void clear() noexcept { m_container.clear(); }
+
+    template <typename F>
+    iterator lower_bound(const F& k)
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename F>
+    const_iterator lower_bound(const F& k) const
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    iterator upper_bound(const K& k)
+    {
+        return std::upper_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    const_iterator upper_bound(const K& k) const
+    {
+        return std::upper_bound(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    std::pair<iterator, iterator> equal_range(const K& k)
+    {
+        return std::equal_range(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename K>
+    std::pair<const_iterator, const_iterator> equal_range(const K& k) const
+    {
+        return std::equal_range(m_container.begin(), m_container.end(), k, cmp());
+    }
+
+    template <typename F>
+    iterator find(const F& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !cmp()(k, *i))
+            return i;
+
+        return end();
+    }
+
+    template <typename F>
+    const_iterator find(const F& k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !cmp()(k, *i))
+            return i;
+
+        return end();
+    }
+
+    template <typename F>
+    size_t count(const F& k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+    template <typename P>
+    std::pair<iterator, bool> insert(P&& val)
+    {
+        auto i = lower_bound(val);
+        if (i != end() && !cmp()(val, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, std::forward<P>(val)), true };
+    }
+
+    std::pair<iterator, bool> insert(const value_type& val)
+    {
+        auto i = lower_bound(val);
+        if (i != end() && !cmp()(val, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, val), true };
+    }
+
+    template <typename... Args>
+    std::pair<iterator, bool> emplace(Args&&... args)
+    {
+        value_type val(std::forward<Args>(args)...);
+        return insert(std::move(val));
+    }
+
+    iterator erase(const_iterator pos)
+    {
+        return m_container.erase(pos);
+    }
+
+    iterator erase(iterator pos)
+    {
+        return erase(const_iterator(pos));
+    }
+
+    template <typename F>
+    size_type erase(const F& k)
+    {
+        auto i = find(k);
+        if (i == end())
+        {
+            return 0;
+        }
+
+        erase(i);
+        return 1;
+    }
+
+    void swap(flat_set& x)
+    {
+        std::swap(cmp(), x.cmp());
+        m_container.swap(x.m_container);
+    }
+
+    const container_type& container() const noexcept
+    {
+        return m_container;
+    }
+
+    // DANGER! If you're not careful with this function, you may irreversably break the set
+    container_type& modify_container() noexcept
+    {
+        return m_container;
+    }
+};
+
+template <typename Key, typename Compare, typename Container>
+bool operator==(const flat_set<Key, Compare, Container>& a, const flat_set<Key, Compare, Container>& b)
+{
+    return a.container() == b.container();
+}
+
+template <typename Key, typename Compare, typename Container>
+bool operator!=(const flat_set<Key, Compare, Container>& a, const flat_set<Key, Compare, Container>& b)
+{
+    return a.container() != b.container();
+}
+
+}