/* * Copyright (c) 2018-2022, Andreas Kling * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace AK { // FixedArray is an Array with a size only known at run-time. // It guarantees to only allocate when being constructed, and to only deallocate when being destructed. template class FixedArray { public: FixedArray() = default; static ErrorOr> try_create(size_t size) { if (size == 0) return FixedArray(); T* elements = static_cast(kmalloc_array(size, sizeof(T))); if (!elements) return Error::from_errno(ENOMEM); for (size_t i = 0; i < size; ++i) new (&elements[i]) T(); return FixedArray(size, elements); } static FixedArray must_create_but_fixme_should_propagate_errors(size_t size) { return MUST(try_create(size)); } // NOTE: // Even though it may look like there will be a template instantiation of this function for every size, // the compiler will inline this anyway and therefore not generate any duplicate code. template static ErrorOr> try_create(T(&&array)[N]) { if (N == 0) return FixedArray(); T* elements = static_cast(kmalloc_array(N, sizeof(T))); if (!elements) return Error::from_errno(ENOMEM); for (size_t i = 0; i < N; ++i) new (&elements[i]) T(move(array[i])); return FixedArray(N, elements); } ErrorOr> try_clone() const { if (m_size == 0) return FixedArray(); T* elements = static_cast(kmalloc_array(m_size, sizeof(T))); if (!elements) return Error::from_errno(ENOMEM); for (size_t i = 0; i < m_size; ++i) new (&elements[i]) T(m_elements[i]); return FixedArray(m_size, elements); } FixedArray must_clone_but_fixme_should_propagate_errors() const { return MUST(try_clone()); } // Nobody can ever use these functions, since it would be impossible to make them OOM-safe due to their signatures. We just explicitly delete them. FixedArray(FixedArray const&) = delete; FixedArray& operator=(FixedArray const&) = delete; FixedArray(FixedArray&& other) : m_size(other.m_size) , m_elements(other.m_elements) { other.m_size = 0; other.m_elements = nullptr; } // This function would violate the contract, as it would need to deallocate this FixedArray. As it also has no use case, we delete it. FixedArray& operator=(FixedArray&&) = delete; ~FixedArray() { if (!m_elements) return; for (size_t i = 0; i < m_size; ++i) m_elements[i].~T(); kfree_sized(m_elements, sizeof(T) * m_size); // NOTE: should prevent use-after-free early m_size = 0; m_elements = nullptr; } size_t size() const { return m_size; } T* data() { return m_elements; } T const* data() const { return m_elements; } T& operator[](size_t index) { VERIFY(index < m_size); return m_elements[index]; } T const& operator[](size_t index) const { VERIFY(index < m_size); return m_elements[index]; } bool contains_slow(T const& value) const { for (size_t i = 0; i < m_size; ++i) { if (m_elements[i] == value) return true; } return false; } void swap(FixedArray& other) { ::swap(m_size, other.m_size); ::swap(m_elements, other.m_elements); } using Iterator = SimpleIterator; using ConstIterator = SimpleIterator; Iterator begin() { return Iterator::begin(*this); } ConstIterator begin() const { return ConstIterator::begin(*this); } Iterator end() { return Iterator::end(*this); } ConstIterator end() const { return ConstIterator::end(*this); } Span span() { return { data(), size() }; } Span span() const { return { data(), size() }; } private: FixedArray(size_t size, T* elements) : m_size(size) , m_elements(elements) { } size_t m_size { 0 }; T* m_elements { nullptr }; }; } using AK::FixedArray;