diff --git a/AK/Vector.h b/AK/Vector.h index 6a4b3ebc5de..f09692dd32c 100644 --- a/AK/Vector.h +++ b/AK/Vector.h @@ -40,11 +40,6 @@ public: { } - ~Vector() - { - clear(); - } - #ifndef SERENITY_LIBC_BUILD Vector(std::initializer_list list) { @@ -87,9 +82,184 @@ public: m_size = other.size(); } + ~Vector() + { + clear(); + } + Span span() { return { data(), size() }; } Span span() const { return { data(), size() }; } + operator Span() { return span(); } + operator Span() const { return span(); } + + bool is_empty() const { return size() == 0; } + ALWAYS_INLINE size_t size() const { return m_size; } + size_t capacity() const { return m_capacity; } + + T* data() + { + if constexpr (inline_capacity > 0) + return m_outline_buffer ? m_outline_buffer : inline_buffer(); + return m_outline_buffer; + } + + const T* data() const + { + if constexpr (inline_capacity > 0) + return m_outline_buffer ? m_outline_buffer : inline_buffer(); + return m_outline_buffer; + } + + ALWAYS_INLINE const T& at(size_t i) const + { + VERIFY(i < m_size); + return data()[i]; + } + + ALWAYS_INLINE T& at(size_t i) + { + VERIFY(i < m_size); + return data()[i]; + } + + ALWAYS_INLINE const T& operator[](size_t i) const { return at(i); } + ALWAYS_INLINE T& operator[](size_t i) { return at(i); } + + const T& first() const { return at(0); } + T& first() { return at(0); } + + const T& last() const { return at(size() - 1); } + T& last() { return at(size() - 1); } + + template + Optional first_matching(Callback callback) + { + for (size_t i = 0; i < size(); ++i) { + if (callback(at(i))) { + return at(i); + } + } + return {}; + } + + template + Optional last_matching(Callback callback) + { + for (ssize_t i = size() - 1; i >= 0; --i) { + if (callback(at(i))) { + return at(i); + } + } + return {}; + } + + template + bool operator==(const V& other) const + { + if (m_size != other.size()) + return false; + return TypedTransfer::compare(data(), other.data(), size()); + } + + bool contains_slow(const T& value) const + { + for (size_t i = 0; i < size(); ++i) { + if (Traits::equals(at(i), value)) + return true; + } + return false; + } + + bool contains_in_range(const T& value, const size_t start, const size_t end) const + { + VERIFY(start <= end); + VERIFY(end < size()); + for (size_t i = start; i <= end; ++i) { + if (Traits::equals(at(i), value)) + return true; + } + return false; + } + + template + void insert(size_t index, U&& value) + { + auto did_allocate = try_insert(index, forward(value)); + VERIFY(did_allocate); + } + + template + void insert_before_matching(U&& value, C callback, size_t first_index = 0, size_t* inserted_index = nullptr) + { + auto did_allocate = try_insert_before_matching(forward(value), callback, first_index, inserted_index); + VERIFY(did_allocate); + } + + void append(Vector&& other) + { + auto did_allocate = try_append(move(other)); + VERIFY(did_allocate); + } + + void append(const Vector& other) + { + auto did_allocate = try_append(other); + VERIFY(did_allocate); + } + + ALWAYS_INLINE void append(T&& value) + { + auto did_allocate = try_append(move(value)); + VERIFY(did_allocate); + } + + ALWAYS_INLINE void append(const T& value) + { + auto did_allocate = try_append(T(value)); + VERIFY(did_allocate); + } + + void append(const T* values, size_t count) + { + auto did_allocate = try_append(values, count); + VERIFY(did_allocate); + } + + template + ALWAYS_INLINE void unchecked_append(U&& value) + { + VERIFY((size() + 1) <= capacity()); + new (slot(m_size)) T(forward(value)); + ++m_size; + } + + template + void empend(Args&&... args) + { + auto did_allocate = try_empend(forward(args)...); + VERIFY(did_allocate); + } + + template + void prepend(U&& value) + { + auto did_allocate = try_insert(0, forward(value)); + VERIFY(did_allocate); + } + + void prepend(Vector&& other) + { + auto did_allocate = try_prepend(move(other)); + VERIFY(did_allocate); + } + + void prepend(const T* values, size_t count) + { + auto did_allocate = try_prepend(values, count); + VERIFY(did_allocate); + } + // FIXME: What about assigning from a vector with lower inline capacity? Vector& operator=(Vector&& other) { @@ -113,6 +283,27 @@ public: return *this; } + Vector& operator=(const Vector& other) + { + if (this != &other) { + clear(); + ensure_capacity(other.size()); + TypedTransfer::copy(data(), other.data(), other.size()); + m_size = other.size(); + } + return *this; + } + + template + Vector& operator=(const Vector& other) + { + clear(); + ensure_capacity(other.size()); + TypedTransfer::copy(data(), other.data(), other.size()); + m_size = other.size(); + return *this; + } + void clear() { clear_with_capacity(); @@ -130,105 +321,6 @@ public: m_size = 0; } - template - bool operator==(const V& other) const - { - if (m_size != other.size()) - return false; - return TypedTransfer::compare(data(), other.data(), size()); - } - - operator Span() { return span(); } - operator Span() const { return span(); } - - bool contains_slow(const T& value) const - { - for (size_t i = 0; i < size(); ++i) { - if (Traits::equals(at(i), value)) - return true; - } - return false; - } - - bool contains_in_range(const T& value, const size_t start, const size_t end) const - { - VERIFY(start <= end); - VERIFY(end < size()); - for (size_t i = start; i <= end; ++i) { - if (Traits::equals(at(i), value)) - return true; - } - return false; - } - - bool is_empty() const { return size() == 0; } - ALWAYS_INLINE size_t size() const { return m_size; } - size_t capacity() const { return m_capacity; } - - T* data() - { - if constexpr (inline_capacity > 0) - return m_outline_buffer ? m_outline_buffer : inline_buffer(); - return m_outline_buffer; - } - const T* data() const - { - if constexpr (inline_capacity > 0) - return m_outline_buffer ? m_outline_buffer : inline_buffer(); - return m_outline_buffer; - } - - ALWAYS_INLINE const T& at(size_t i) const - { - VERIFY(i < m_size); - return data()[i]; - } - ALWAYS_INLINE T& at(size_t i) - { - VERIFY(i < m_size); - return data()[i]; - } - - ALWAYS_INLINE const T& operator[](size_t i) const { return at(i); } - ALWAYS_INLINE T& operator[](size_t i) { return at(i); } - - const T& first() const { return at(0); } - T& first() { return at(0); } - - const T& last() const { return at(size() - 1); } - T& last() { return at(size() - 1); } - - T take_last() - { - VERIFY(!is_empty()); - T value = move(last()); - last().~T(); - --m_size; - return value; - } - - T take_first() - { - VERIFY(!is_empty()); - T value = move(first()); - remove(0); - return value; - } - - T take(size_t index) - { - T value = move(at(index)); - remove(index); - return value; - } - - T unstable_take(size_t index) - { - VERIFY(index < m_size); - swap(at(index), at(m_size - 1)); - return take_last(); - } - void remove(size_t index) { VERIFY(index < m_size); @@ -267,140 +359,6 @@ public: m_size -= count; } - template - [[nodiscard]] bool try_insert(size_t index, U&& value) - { - if (index > size()) - return false; - if (index == size()) - return try_append(forward(value)); - if (!try_grow_capacity(size() + 1)) - return false; - ++m_size; - if constexpr (Traits::is_trivial()) { - TypedTransfer::move(slot(index + 1), slot(index), m_size - index - 1); - } else { - for (size_t i = size() - 1; i > index; --i) { - new (slot(i)) T(move(at(i - 1))); - at(i - 1).~T(); - } - } - new (slot(index)) T(forward(value)); - return true; - } - - template - void insert(size_t index, U&& value) - { - auto did_allocate = try_insert(index, forward(value)); - VERIFY(did_allocate); - } - - template - [[nodiscard]] bool try_insert_before_matching(U&& value, C callback, size_t first_index = 0, size_t* inserted_index = nullptr) - { - for (size_t i = first_index; i < size(); ++i) { - if (callback(at(i))) { - if (!try_insert(i, forward(value))) - return false; - if (inserted_index) - *inserted_index = i; - return true; - } - } - if (!try_append(forward(value))) - return false; - if (inserted_index) - *inserted_index = size() - 1; - return true; - } - - template - void insert_before_matching(U&& value, C callback, size_t first_index = 0, size_t* inserted_index = nullptr) - { - auto did_allocate = try_insert_before_matching(forward(value), callback, first_index, inserted_index); - VERIFY(did_allocate); - } - - Vector& operator=(const Vector& other) - { - if (this != &other) { - clear(); - ensure_capacity(other.size()); - TypedTransfer::copy(data(), other.data(), other.size()); - m_size = other.size(); - } - return *this; - } - - template - Vector& operator=(const Vector& other) - { - clear(); - ensure_capacity(other.size()); - TypedTransfer::copy(data(), other.data(), other.size()); - m_size = other.size(); - return *this; - } - - [[nodiscard]] bool try_append(Vector&& other) - { - if (is_empty()) { - *this = move(other); - return true; - } - auto other_size = other.size(); - Vector tmp = move(other); - if (!try_grow_capacity(size() + other_size)) - return false; - TypedTransfer::move(data() + m_size, tmp.data(), other_size); - m_size += other_size; - return true; - } - - void append(Vector&& other) - { - auto did_allocate = try_append(move(other)); - VERIFY(did_allocate); - } - - [[nodiscard]] bool try_append(const Vector& other) - { - if (!try_grow_capacity(size() + other.size())) - return false; - TypedTransfer::copy(data() + m_size, other.data(), other.size()); - m_size += other.m_size; - return true; - } - - void append(const Vector& other) - { - auto did_allocate = try_append(other); - VERIFY(did_allocate); - } - - template - Optional first_matching(Callback callback) - { - for (size_t i = 0; i < size(); ++i) { - if (callback(at(i))) { - return at(i); - } - } - return {}; - } - - template - Optional last_matching(Callback callback) - { - for (ssize_t i = size() - 1; i >= 0; --i) { - if (callback(at(i))) { - return at(i); - } - } - return {}; - } - template bool remove_first_matching(Callback callback) { @@ -425,12 +383,125 @@ public: } } - template - ALWAYS_INLINE void unchecked_append(U&& value) + T take_last() { - VERIFY((size() + 1) <= capacity()); - new (slot(m_size)) T(forward(value)); + VERIFY(!is_empty()); + T value = move(last()); + last().~T(); + --m_size; + return value; + } + + T take_first() + { + VERIFY(!is_empty()); + T value = move(first()); + remove(0); + return value; + } + + T take(size_t index) + { + T value = move(at(index)); + remove(index); + return value; + } + + T unstable_take(size_t index) + { + VERIFY(index < m_size); + swap(at(index), at(m_size - 1)); + return take_last(); + } + + template + [[nodiscard]] bool try_insert(size_t index, U&& value) + { + if (index > size()) + return false; + if (index == size()) + return try_append(forward(value)); + if (!try_grow_capacity(size() + 1)) + return false; ++m_size; + if constexpr (Traits::is_trivial()) { + TypedTransfer::move(slot(index + 1), slot(index), m_size - index - 1); + } else { + for (size_t i = size() - 1; i > index; --i) { + new (slot(i)) T(move(at(i - 1))); + at(i - 1).~T(); + } + } + new (slot(index)) T(forward(value)); + return true; + } + + template + [[nodiscard]] bool try_insert_before_matching(U&& value, C callback, size_t first_index = 0, size_t* inserted_index = nullptr) + { + for (size_t i = first_index; i < size(); ++i) { + if (callback(at(i))) { + if (!try_insert(i, forward(value))) + return false; + if (inserted_index) + *inserted_index = i; + return true; + } + } + if (!try_append(forward(value))) + return false; + if (inserted_index) + *inserted_index = size() - 1; + return true; + } + + [[nodiscard]] bool try_append(Vector&& other) + { + if (is_empty()) { + *this = move(other); + return true; + } + auto other_size = other.size(); + Vector tmp = move(other); + if (!try_grow_capacity(size() + other_size)) + return false; + TypedTransfer::move(data() + m_size, tmp.data(), other_size); + m_size += other_size; + return true; + } + + [[nodiscard]] bool try_append(const Vector& other) + { + if (!try_grow_capacity(size() + other.size())) + return false; + TypedTransfer::copy(data() + m_size, other.data(), other.size()); + m_size += other.m_size; + return true; + } + + [[nodiscard]] ALWAYS_INLINE bool try_append(T&& value) + { + if (!try_grow_capacity(size() + 1)) + return false; + new (slot(m_size)) T(move(value)); + ++m_size; + return true; + } + + [[nodiscard]] ALWAYS_INLINE bool try_append(const T& value) + { + return try_append(T(value)); + } + + [[nodiscard]] bool try_append(const T* values, size_t count) + { + if (!count) + return true; + if (!try_grow_capacity(size() + count)) + return false; + TypedTransfer::copy(slot(m_size), values, count); + m_size += count; + return true; } template @@ -443,52 +514,12 @@ public: return true; } - template - void empend(Args&&... args) - { - auto did_allocate = try_empend(forward(args)...); - VERIFY(did_allocate); - } - - [[nodiscard]] ALWAYS_INLINE bool try_append(T&& value) - { - if (!try_grow_capacity(size() + 1)) - return false; - new (slot(m_size)) T(move(value)); - ++m_size; - return true; - } - - ALWAYS_INLINE void append(T&& value) - { - auto did_allocate = try_append(move(value)); - VERIFY(did_allocate); - } - - [[nodiscard]] ALWAYS_INLINE bool try_append(const T& value) - { - return try_append(T(value)); - } - - ALWAYS_INLINE void append(const T& value) - { - auto did_allocate = try_append(T(value)); - VERIFY(did_allocate); - } - template [[nodiscard]] bool try_prepend(U&& value) { return try_insert(0, forward(value)); } - template - void prepend(U&& value) - { - auto did_allocate = try_insert(0, forward(value)); - VERIFY(did_allocate); - } - [[nodiscard]] bool try_prepend(Vector&& other) { if (other.is_empty()) @@ -514,12 +545,6 @@ public: return true; } - void prepend(Vector&& other) - { - auto did_allocate = try_prepend(move(other)); - VERIFY(did_allocate); - } - [[nodiscard]] bool try_prepend(const T* values, size_t count) { if (!count) @@ -532,29 +557,6 @@ public: return true; } - void prepend(const T* values, size_t count) - { - auto did_allocate = try_prepend(values, count); - VERIFY(did_allocate); - } - - [[nodiscard]] bool try_append(const T* values, size_t count) - { - if (!count) - return true; - if (!try_grow_capacity(size() + count)) - return false; - TypedTransfer::copy(slot(m_size), values, count); - m_size += count; - return true; - } - - void append(const T* values, size_t count) - { - auto did_allocate = try_append(values, count); - VERIFY(did_allocate); - } - [[nodiscard]] bool try_grow_capacity(size_t needed_capacity) { if (m_capacity >= needed_capacity) @@ -562,12 +564,6 @@ public: return try_ensure_capacity(padded_capacity(needed_capacity)); } - void grow_capacity(size_t needed_capacity) - { - auto did_allocate = try_grow_capacity(needed_capacity); - VERIFY(did_allocate); - } - [[nodiscard]] bool try_ensure_capacity(size_t needed_capacity) { if (m_capacity >= needed_capacity) @@ -592,6 +588,33 @@ public: return true; } + [[nodiscard]] bool try_resize(size_t new_size, bool keep_capacity = false) + { + if (new_size <= size()) { + shrink(new_size, keep_capacity); + return true; + } + + if (!try_ensure_capacity(new_size)) + return false; + + for (size_t i = size(); i < new_size; ++i) + new (slot(i)) T {}; + m_size = new_size; + return true; + } + + [[nodiscard]] bool try_resize_and_keep_capacity(size_t new_size) + { + return try_resize(new_size, true); + } + + void grow_capacity(size_t needed_capacity) + { + auto did_allocate = try_grow_capacity(needed_capacity); + VERIFY(did_allocate); + } + void ensure_capacity(size_t needed_capacity) { auto did_allocate = try_ensure_capacity(needed_capacity); @@ -617,33 +640,12 @@ public: m_size = new_size; } - [[nodiscard]] bool try_resize(size_t new_size, bool keep_capacity = false) - { - if (new_size <= size()) { - shrink(new_size, keep_capacity); - return true; - } - - if (!try_ensure_capacity(new_size)) - return false; - - for (size_t i = size(); i < new_size; ++i) - new (slot(i)) T {}; - m_size = new_size; - return true; - } - void resize(size_t new_size, bool keep_capacity = false) { auto did_allocate = try_resize(new_size, keep_capacity); VERIFY(did_allocate); } - [[nodiscard]] bool try_resize_and_keep_capacity(size_t new_size) - { - return try_resize(new_size, true); - } - void resize_and_keep_capacity(size_t new_size) { auto did_allocate = try_resize_and_keep_capacity(new_size);