Loading include/ftl/initializer_list.h +7 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <functional> #include <tuple> #include <utility> Loading Loading @@ -65,18 +66,18 @@ struct InitializerList<T, std::index_sequence<Sizes...>, Types...> { std::tuple<Types...> tuple; }; template <typename K, typename V> template <typename K, typename V, typename KeyEqual = std::equal_to<K>> struct KeyValue {}; // Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the // value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works // with the latter. template <typename K, typename V, std::size_t... Sizes, typename... Types> struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> { template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> { // Accumulate the three arguments to std::pair's piecewise constructor. template <typename... Args> [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList< KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, std::tuple<K&&>, std::tuple<Args&&...>> { return {std::tuple_cat( std::move(tuple), Loading @@ -94,9 +95,9 @@ template <typename T, typename... Args> return InitializerList<T>{}(std::forward<Args>(args)...); } template <typename K, typename V, typename... Args> template <typename K, typename V, typename E = std::equal_to<K>, typename... Args> [[nodiscard]] constexpr auto map(Args&&... args) { return list<KeyValue<K, V>>(std::forward<Args>(args)...); return list<KeyValue<K, V, E>>(std::forward<Args>(args)...); } template <typename K, typename V> Loading include/ftl/small_map.h +17 −10 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ namespace android::ftl { // // assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); // template <typename K, typename V, std::size_t N> template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>> class SmallMap final { using Map = SmallVector<std::pair<const K, V>, N>; Loading Loading @@ -153,7 +153,7 @@ class SmallMap final { auto get(const key_type& key, F f) const -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> { for (auto& [k, v] : *this) { if (k == key) { if (KeyEqual{}(k, key)) { if constexpr (std::is_void_v<R>) { f(v); return true; Loading Loading @@ -237,9 +237,16 @@ class SmallMap final { // bool erase(const key_type& key) { return erase(key, begin()); } // Removes all mappings. // // All iterators are invalidated. // void clear() { map_.clear(); } private: iterator find(const key_type& key, iterator first) { return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; }); return std::find_if(first, end(), [&key](const auto& pair) { return KeyEqual{}(pair.first, key); }); } bool erase(const key_type& key, iterator first) { Loading @@ -261,13 +268,13 @@ class SmallMap final { }; // Deduction guide for in-place constructor. template <typename K, typename V, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&) -> SmallMap<K, V, sizeof...(Sizes)>; template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&) -> SmallMap<K, V, sizeof...(Sizes), E>; // Returns whether the key-value pairs of two maps are equal. template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { if (lhs.size() != rhs.size()) return false; for (const auto& [k, v] : lhs) { Loading @@ -281,8 +288,8 @@ bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { } // TODO: Remove in C++20. template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { return !(lhs == rhs); } Loading include/ftl/small_vector.h +10 −9 Original line number Diff line number Diff line Loading @@ -151,8 +151,6 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { DISPATCH(reference, back, noexcept) DISPATCH(const_reference, back, const) #undef DISPATCH reference operator[](size_type i) { return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i]; } Loading Loading @@ -214,13 +212,15 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { // // The last() and end() iterators are invalidated. // void pop_back() { if (dynamic()) { std::get<Dynamic>(vector_).pop_back(); } else { std::get<Static>(vector_).pop_back(); } } DISPATCH(void, pop_back, noexcept) // Removes all elements. // // All iterators are invalidated. // DISPATCH(void, clear, noexcept) #undef DISPATCH // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. Loading Loading @@ -345,6 +345,7 @@ class SmallVector<T, 0> final : ArrayTraits<T>, return true; } using Impl::clear; using Impl::pop_back; void unstable_erase(iterator it) { Loading include/ftl/static_vector.h +10 −2 Original line number Diff line number Diff line Loading @@ -189,8 +189,7 @@ class StaticVector final : ArrayTraits<T>, } StaticVector& operator=(StaticVector&& other) { std::destroy(begin(), end()); size_ = 0; clear(); swap<true>(other); return *this; } Loading Loading @@ -280,6 +279,15 @@ class StaticVector final : ArrayTraits<T>, // void pop_back() { unstable_erase(last()); } // Removes all elements. // // All iterators are invalidated. // void clear() { std::destroy(begin(), end()); size_ = 0; } // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. // Loading libs/ftl/small_map_test.cpp +37 −0 Original line number Diff line number Diff line Loading @@ -345,4 +345,41 @@ TEST(SmallMap, Erase) { } } TEST(SmallMap, Clear) { SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); map.clear(); EXPECT_TRUE(map.empty()); EXPECT_FALSE(map.dynamic()); map = ftl::init::map(1, '1')(2, '2')(3, '3'); map.try_emplace(4, '4'); map.clear(); EXPECT_TRUE(map.empty()); EXPECT_TRUE(map.dynamic()); } TEST(SmallMap, KeyEqual) { struct KeyEqual { bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; } }; SmallMap<int, char, 1, KeyEqual> map; EXPECT_TRUE(map.try_emplace(3, '3').second); EXPECT_FALSE(map.try_emplace(13, '3').second); EXPECT_TRUE(map.try_emplace(22, '2').second); EXPECT_TRUE(map.contains(42)); EXPECT_TRUE(map.try_emplace(111, '1').second); EXPECT_EQ(map.get(321), '1'); map.erase(123); EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2'))); } } // namespace android::test Loading
include/ftl/initializer_list.h +7 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <functional> #include <tuple> #include <utility> Loading Loading @@ -65,18 +66,18 @@ struct InitializerList<T, std::index_sequence<Sizes...>, Types...> { std::tuple<Types...> tuple; }; template <typename K, typename V> template <typename K, typename V, typename KeyEqual = std::equal_to<K>> struct KeyValue {}; // Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the // value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works // with the latter. template <typename K, typename V, std::size_t... Sizes, typename... Types> struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> { template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> { // Accumulate the three arguments to std::pair's piecewise constructor. template <typename... Args> [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList< KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, std::tuple<K&&>, std::tuple<Args&&...>> { return {std::tuple_cat( std::move(tuple), Loading @@ -94,9 +95,9 @@ template <typename T, typename... Args> return InitializerList<T>{}(std::forward<Args>(args)...); } template <typename K, typename V, typename... Args> template <typename K, typename V, typename E = std::equal_to<K>, typename... Args> [[nodiscard]] constexpr auto map(Args&&... args) { return list<KeyValue<K, V>>(std::forward<Args>(args)...); return list<KeyValue<K, V, E>>(std::forward<Args>(args)...); } template <typename K, typename V> Loading
include/ftl/small_map.h +17 −10 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ namespace android::ftl { // // assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); // template <typename K, typename V, std::size_t N> template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>> class SmallMap final { using Map = SmallVector<std::pair<const K, V>, N>; Loading Loading @@ -153,7 +153,7 @@ class SmallMap final { auto get(const key_type& key, F f) const -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> { for (auto& [k, v] : *this) { if (k == key) { if (KeyEqual{}(k, key)) { if constexpr (std::is_void_v<R>) { f(v); return true; Loading Loading @@ -237,9 +237,16 @@ class SmallMap final { // bool erase(const key_type& key) { return erase(key, begin()); } // Removes all mappings. // // All iterators are invalidated. // void clear() { map_.clear(); } private: iterator find(const key_type& key, iterator first) { return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; }); return std::find_if(first, end(), [&key](const auto& pair) { return KeyEqual{}(pair.first, key); }); } bool erase(const key_type& key, iterator first) { Loading @@ -261,13 +268,13 @@ class SmallMap final { }; // Deduction guide for in-place constructor. template <typename K, typename V, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&) -> SmallMap<K, V, sizeof...(Sizes)>; template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&) -> SmallMap<K, V, sizeof...(Sizes), E>; // Returns whether the key-value pairs of two maps are equal. template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { if (lhs.size() != rhs.size()) return false; for (const auto& [k, v] : lhs) { Loading @@ -281,8 +288,8 @@ bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { } // TODO: Remove in C++20. template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { return !(lhs == rhs); } Loading
include/ftl/small_vector.h +10 −9 Original line number Diff line number Diff line Loading @@ -151,8 +151,6 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { DISPATCH(reference, back, noexcept) DISPATCH(const_reference, back, const) #undef DISPATCH reference operator[](size_type i) { return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i]; } Loading Loading @@ -214,13 +212,15 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { // // The last() and end() iterators are invalidated. // void pop_back() { if (dynamic()) { std::get<Dynamic>(vector_).pop_back(); } else { std::get<Static>(vector_).pop_back(); } } DISPATCH(void, pop_back, noexcept) // Removes all elements. // // All iterators are invalidated. // DISPATCH(void, clear, noexcept) #undef DISPATCH // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. Loading Loading @@ -345,6 +345,7 @@ class SmallVector<T, 0> final : ArrayTraits<T>, return true; } using Impl::clear; using Impl::pop_back; void unstable_erase(iterator it) { Loading
include/ftl/static_vector.h +10 −2 Original line number Diff line number Diff line Loading @@ -189,8 +189,7 @@ class StaticVector final : ArrayTraits<T>, } StaticVector& operator=(StaticVector&& other) { std::destroy(begin(), end()); size_ = 0; clear(); swap<true>(other); return *this; } Loading Loading @@ -280,6 +279,15 @@ class StaticVector final : ArrayTraits<T>, // void pop_back() { unstable_erase(last()); } // Removes all elements. // // All iterators are invalidated. // void clear() { std::destroy(begin(), end()); size_ = 0; } // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. // Loading
libs/ftl/small_map_test.cpp +37 −0 Original line number Diff line number Diff line Loading @@ -345,4 +345,41 @@ TEST(SmallMap, Erase) { } } TEST(SmallMap, Clear) { SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); map.clear(); EXPECT_TRUE(map.empty()); EXPECT_FALSE(map.dynamic()); map = ftl::init::map(1, '1')(2, '2')(3, '3'); map.try_emplace(4, '4'); map.clear(); EXPECT_TRUE(map.empty()); EXPECT_TRUE(map.dynamic()); } TEST(SmallMap, KeyEqual) { struct KeyEqual { bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; } }; SmallMap<int, char, 1, KeyEqual> map; EXPECT_TRUE(map.try_emplace(3, '3').second); EXPECT_FALSE(map.try_emplace(13, '3').second); EXPECT_TRUE(map.try_emplace(22, '2').second); EXPECT_TRUE(map.contains(42)); EXPECT_TRUE(map.try_emplace(111, '1').second); EXPECT_EQ(map.get(321), '1'); map.erase(123); EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2'))); } } // namespace android::test