FTL: Add SmallVector<T, N>
SmallVector is a StaticVector that promotes to std::vector when full.
It will serve as a base for SmallMap.
Bug: 160012986
Test: ftl_test
Change-Id: Id223a6fc169d792746220d2dde1f9f1a0e87a0cc
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
new file mode 100644
index 0000000..28f717a
--- /dev/null
+++ b/include/ftl/ArrayTraits.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <new>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+ using value_type = T;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator = pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+
+ using const_pointer = const value_type*;
+ using const_reference = const value_type&;
+ using const_iterator = const_pointer;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // TODO: Replace with std::construct_at in C++20.
+ template <typename... Args>
+ static pointer construct_at(const_iterator it, Args&&... args) {
+ void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+ return new (ptr) value_type{std::forward<Args>(args)...};
+ }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+ FTL_ARRAY_TRAIT(T, size_type);
+
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+public:
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return self().begin(); }
+
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return self().end(); }
+
+ reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+ const_reverse_iterator rbegin() const { return crbegin(); }
+ const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+ reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+ const_reverse_iterator rend() const { return crend(); }
+ const_reverse_iterator crend() const { return self().rend(); }
+
+ iterator last() { return self().end() - 1; }
+ const_iterator last() const { return self().last(); }
+
+ reference front() { return *self().begin(); }
+ const_reference front() const { return self().front(); }
+
+ reference back() { return *last(); }
+ const_reference back() const { return self().back(); }
+
+ reference operator[](size_type i) { return *(self().begin() + i); }
+ const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, size_t> class Array>
+struct ArrayComparators {
+ template <typename T, size_t N, size_t M>
+ friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return rhs < lhs;
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs < rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs > rhs);
+ }
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
new file mode 100644
index 0000000..b632bb2
--- /dev/null
+++ b/include/ftl/SmallVector.h
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/ArrayTraits.h>
+#include <ftl/StaticVector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct IsSmallVector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+// ftl::SmallVector<char, 3> vector;
+// assert(vector.empty());
+// assert(!vector.dynamic());
+//
+// vector = {'a', 'b', 'c'};
+// assert(vector.size() == 3u);
+// assert(!vector.dynamic());
+//
+// vector.push_back('d');
+// assert(vector.dynamic());
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'b');
+// assert(vector.dynamic());
+//
+// const char array[] = "hi";
+// vector = ftl::SmallVector(array);
+// assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+// assert(!vector.dynamic());
+//
+template <typename T, size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+ using Static = StaticVector<T, N>;
+ using Dynamic = SmallVector<T, 0>;
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename U>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ SmallVector() = default;
+
+ // Constructs at most N elements. See StaticVector for underlying constructors.
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>>
+ SmallVector(Arg&& arg, Args&&... args)
+ : mVector(std::in_place_type<Static>, std::forward<Arg>(arg),
+ std::forward<Args>(args)...) {}
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+ SmallVector(const SmallVector<U, M>& other)
+ : SmallVector(IteratorRange, other.begin(), other.end()) {}
+
+ void swap(SmallVector& other) { mVector.swap(other.mVector); }
+
+ // Returns whether the vector is backed by static or dynamic storage.
+ bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); }
+
+#define VISITOR(T, F, ...) \
+ T F() __VA_ARGS__ { \
+ return std::visit([](auto&& v) -> T { return v.F(); }, mVector); \
+ }
+
+ VISITOR(size_type, max_size, const)
+ VISITOR(size_type, size, const)
+ VISITOR(bool, empty, const)
+
+ // noexcept to suppress warning about zero variadic macro arguments.
+ VISITOR(iterator, begin, noexcept)
+ VISITOR(const_iterator, begin, const)
+ VISITOR(const_iterator, cbegin, const)
+
+ VISITOR(iterator, end, noexcept)
+ VISITOR(const_iterator, end, const)
+ VISITOR(const_iterator, cend, const)
+
+ VISITOR(reverse_iterator, rbegin, noexcept)
+ VISITOR(const_reverse_iterator, rbegin, const)
+ VISITOR(const_reverse_iterator, crbegin, const)
+
+ VISITOR(reverse_iterator, rend, noexcept)
+ VISITOR(const_reverse_iterator, rend, const)
+ VISITOR(const_reverse_iterator, crend, const)
+
+ VISITOR(iterator, last, noexcept)
+ VISITOR(const_iterator, last, const)
+
+ VISITOR(reference, front, noexcept)
+ VISITOR(const_reference, front, const)
+
+ VISITOR(reference, back, noexcept)
+ VISITOR(const_reference, back, const)
+
+#undef VISITOR
+
+ reference operator[](size_type i) {
+ return std::visit([i](auto& v) -> reference { return v[i]; }, mVector);
+ }
+
+ const_reference operator[](size_type i) const {
+ return std::visit([i](const auto& v) -> const_reference { return v[i]; }, mVector);
+ }
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ return std::
+ visit([it, &args...](auto& v)
+ -> reference { return v.replace(it, std::forward<Args>(args)...); },
+ mVector);
+ }
+
+ // Appends an element, and returns a reference to it.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ constexpr auto insertStatic = &Static::template emplace_back<Args...>;
+ constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>;
+ return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...);
+ }
+
+ // Appends an element.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ void push_back(const value_type& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(v);
+ }
+
+ void push_back(value_type&& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(value_type&&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(std::move(v));
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() {
+ std::visit([](auto& v) { v.pop_back(); }, mVector);
+ }
+
+ // 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.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(iterator it) {
+ std::visit([it](auto& v) { v.unstable_erase(it); }, mVector);
+ }
+
+private:
+ template <typename... Vs>
+ struct Visitor : Vs... {};
+
+ // TODO: Remove this deduction guide in C++20.
+ template <typename... Vs>
+ Visitor(Vs...) -> Visitor<Vs...>;
+
+ template <auto insertStatic, auto insertDynamic, typename... Args>
+ auto insert(Args&&... args) {
+ return std::visit(Visitor{[this, &args...](Static& vector) {
+ if (vector.full()) {
+ return (promote(vector).*
+ insertDynamic)(std::forward<Args>(args)...);
+ }
+
+ return (vector.*insertStatic)(std::forward<Args>(args)...);
+ },
+ [&args...](Dynamic& vector) {
+ return (vector.*insertDynamic)(std::forward<Args>(args)...);
+ }},
+ mVector);
+ }
+
+ Dynamic& promote(Static& staticVector) {
+ assert(staticVector.full());
+
+ // Allocate double capacity to reduce probability of reallocation.
+ Dynamic vector;
+ vector.reserve(Static::max_size() * 2);
+ std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector));
+
+ return mVector.template emplace<Dynamic>(std::move(vector));
+ }
+
+ std::variant<Static, Dynamic> mVector;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+ ArrayIterators<SmallVector<T, 0>, T>,
+ std::vector<T> {
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<SmallVector, T>;
+ using Impl = std::vector<T>;
+
+ friend Iter;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ using Impl::Impl;
+
+ using Impl::empty;
+ using Impl::max_size;
+ using Impl::size;
+
+ using Impl::reserve;
+
+ // std::vector iterators are not necessarily raw pointers.
+ iterator begin() { return Impl::data(); }
+ iterator end() { return Impl::data() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ return &Impl::emplace_back(std::forward<Args>(args)...);
+ }
+
+ bool push_back(const value_type& v) {
+ Impl::push_back(v);
+ return true;
+ }
+
+ bool push_back(value_type&& v) {
+ Impl::push_back(std::move(v));
+ return true;
+ }
+
+ using Impl::pop_back;
+
+ void unstable_erase(iterator it) {
+ if (it != last()) std::iter_swap(it, last());
+ pop_back();
+ }
+
+ void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct IsSmallVector : std::false_type {};
+
+template <typename T, size_t N>
+struct IsSmallVector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
index c0cdd11..457095d 100644
--- a/include/ftl/StaticVector.h
+++ b/include/ftl/StaticVector.h
@@ -16,11 +16,12 @@
#pragma once
+#include <ftl/ArrayTraits.h>
+
#include <algorithm>
#include <cassert>
#include <iterator>
#include <memory>
-#include <new>
#include <type_traits>
#include <utility>
@@ -63,9 +64,16 @@
// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
//
template <typename T, size_t N>
-class StaticVector final {
+class StaticVector final : ArrayTraits<T>,
+ ArrayIterators<StaticVector<T, N>, T>,
+ ArrayComparators<StaticVector> {
static_assert(N > 0);
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<StaticVector, T>;
+ friend Iter;
+
// There is ambiguity when constructing from two iterator-like elements like pointers:
// they could be an iterator range, or arguments for in-place construction. Assume the
// latter unless they are input iterators and cannot be used to construct elements. If
@@ -76,19 +84,19 @@
std::negation<std::is_constructible<T, I>>>;
public:
- using value_type = T;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
- using pointer = value_type*;
- using reference = value_type&;
- using iterator = pointer;
- using reverse_iterator = std::reverse_iterator<iterator>;
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
- using const_pointer = const value_type*;
- using const_reference = const value_type&;
- using const_iterator = const_pointer;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
// Creates an empty vector.
StaticVector() = default;
@@ -180,32 +188,26 @@
bool full() const { return size() == max_size(); }
iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
- const_iterator begin() const { return cbegin(); }
- const_iterator cbegin() const { return mut().begin(); }
-
iterator end() { return begin() + size(); }
- const_iterator end() const { return cend(); }
- const_iterator cend() const { return mut().end(); }
- reverse_iterator rbegin() { return std::make_reverse_iterator(end()); }
- const_reverse_iterator rbegin() const { return crbegin(); }
- const_reverse_iterator crbegin() const { return mut().rbegin(); }
+ using Iter::begin;
+ using Iter::end;
- reverse_iterator rend() { return std::make_reverse_iterator(begin()); }
- const_reverse_iterator rend() const { return crend(); }
- const_reverse_iterator crend() const { return mut().rend(); }
+ using Iter::cbegin;
+ using Iter::cend;
- iterator last() { return end() - 1; }
- const_iterator last() const { return mut().last(); }
+ using Iter::rbegin;
+ using Iter::rend;
- reference front() { return *begin(); }
- const_reference front() const { return mut().front(); }
+ using Iter::crbegin;
+ using Iter::crend;
- reference back() { return *last(); }
- const_reference back() const { return mut().back(); }
+ using Iter::last;
- reference operator[](size_type i) { return *(begin() + i); }
- const_reference operator[](size_type i) const { return mut()[i]; }
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
// Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
// replacing at end() is erroneous.
@@ -242,7 +244,13 @@
//
// On success, the end() iterator is invalidated.
//
- bool push_back(value_type v) {
+ bool push_back(const value_type& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(v);
+ return it != end();
+ }
+
+ bool push_back(value_type&& v) {
// Two statements for sequence point.
const iterator it = emplace_back(std::move(v));
return it != end();
@@ -273,8 +281,6 @@
private:
struct Empty {};
- StaticVector& mut() const { return *const_cast<StaticVector*>(this); }
-
// Recursion for variadic constructor.
template <size_t I, typename E, typename... Es>
StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
@@ -286,13 +292,6 @@
template <size_t I>
explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
- // TODO: Replace with std::construct_at in C++20.
- template <typename... Args>
- static pointer construct_at(const_iterator it, Args&&... args) {
- void* const ptr = const_cast<void*>(static_cast<const void*>(it));
- return new (ptr) value_type{std::forward<Args>(args)...};
- }
-
size_type mSize = 0;
std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
};
@@ -348,35 +347,4 @@
lhs.swap(rhs);
}
-// TODO: Replace with operator<=> in C++20.
-template <typename T, size_t N, size_t M>
-inline bool operator==(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator<(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator>(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return rhs < lhs;
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator!=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(lhs == rhs);
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator>=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(lhs < rhs);
-}
-
-template <typename T, size_t N, size_t M>
-inline bool operator<=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
- return !(rhs < lhs);
-}
-
} // namespace android::ftl