blob: 59b084b5ab77787b6ee124d4e373734684e6f058 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CONTAINERS_HEAP_ARRAY_H_
#define BASE_CONTAINERS_HEAP_ARRAY_H_
#include <stddef.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"
namespace base {
// HeapArray<T> is a replacement for std::unique_ptr<T[]> that keeps track
// of its size. It is intended to provide easy conversion to span<T> for most
// usage, but it also provides bounds-checked indexing.
//
// By default, elements in the array are either value-initialized (i.e. zeroed
// for primitive types) when the array is created using the WithSize()
// static method, or uninitialized when the array is created via the Uninit()
// static method.
template <typename T>
class TRIVIAL_ABI GSL_OWNER HeapArray {
public:
static_assert(!std::is_const_v<T>, "HeapArray cannot hold const types");
static_assert(!std::is_reference_v<T>,
"HeapArray cannot hold reference types");
using iterator = base::span<T>::iterator;
using const_iterator = base::span<const T>::iterator;
// Allocates initialized memory capable of holding `size` elements. No memory
// is allocated for zero-sized arrays.
static HeapArray WithSize(size_t size)
requires(std::constructible_from<T>)
{
if (!size) {
return HeapArray();
}
return HeapArray(std::unique_ptr<T[]>(new T[size]()), size);
}
// Allocates uninitialized memory capable of holding `size` elements. T must
// be trivially constructible and destructible. No memory is allocated for
// zero-sized arrays.
static HeapArray Uninit(size_t size)
requires(std::is_trivially_constructible_v<T> &&
std::is_trivially_destructible_v<T>)
{
if (!size) {
return HeapArray();
}
return HeapArray(std::unique_ptr<T[]>(new T[size]), size);
}
// Constructs an empty array and does not allocate any memory.
HeapArray()
requires(std::constructible_from<T>)
= default;
// Move-only type since the memory is owned.
HeapArray(const HeapArray&) = delete;
HeapArray& operator=(const HeapArray&) = delete;
// Move-construction leaves the moved-from object empty and containing
// no allocated memory.
HeapArray(HeapArray&& that)
: data_(std::move(that.data_)), size_(std::exchange(that.size_, 0u)) {}
// Move-assigment leaves the moved-from object empty and containing
// no allocated memory.
HeapArray& operator=(HeapArray&& that) {
data_ = std::move(that.data_);
size_ = std::exchange(that.size_, 0u);
return *this;
}
~HeapArray() = default;
bool empty() const { return size_ == 0u; }
size_t size() const { return size_; }
// Prefer span-based methods below over data() where possible. The data()
// method exists primarily to allow implicit constructions of spans.
// Returns nullptr for a zero-sized (or moved-from) array.
T* data() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data_.get(); }
const T* data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data_.get(); }
iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return as_span().begin(); }
const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().begin();
}
iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return as_span().end(); }
const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().end();
}
T& operator[](size_t idx) ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span()[idx];
}
const T& operator[](size_t idx) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span()[idx];
}
// Access the HeapArray via spans. Note that span<T> is implicilty
// constructible from HeapArray<T>, so an explicit call to .as_span() is
// most useful, say, when the compiler can't deduce a template
// argument type.
base::span<T> as_span() ABSL_ATTRIBUTE_LIFETIME_BOUND {
return base::span<T>(data_.get(), size_);
}
base::span<const T> as_span() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return base::span<const T>(data_.get(), size_);
}
// Convenience method to copy the contents of the entire array from a
// span<>. Hard CHECK occurs in span<>::copy_from() if the HeapArray and
// the span have different sizes.
void copy_from(base::span<const T> other) { as_span().copy_from(other); }
// Convenience methods to slice the vector into spans.
// Returns a span over the HeapArray starting at `offset` of `count` elements.
// If `count` is unspecified, all remaining elements are included. A CHECK()
// occurs if any of the parameters results in an out-of-range position in
// the HeapArray.
base::span<T> subspan(size_t offset, size_t count = base::dynamic_extent)
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().subspan(offset, count);
}
base::span<const T> subspan(size_t offset,
size_t count = base::dynamic_extent) const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().subspan(offset, count);
}
// Returns a span over the first `count` elements of the HeapArray. A CHECK()
// occurs if the `count` is larger than size of the HeapArray.
base::span<T> first(size_t count) ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().first(count);
}
base::span<const T> first(size_t count) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().first(count);
}
// Returns a span over the last `count` elements of the HeapArray. A CHECK()
// occurs if the `count` is larger than size of the HeapArray.
base::span<T> last(size_t count) ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().last(count);
}
base::span<const T> last(size_t count) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return as_span().last(count);
}
private:
HeapArray(std::unique_ptr<T[]> data, size_t size)
: data_(std::move(data)), size_(size) {}
std::unique_ptr<T[]> data_;
size_t size_ = 0u;
};
} // namespace base
#endif // BASE_CONTAINERS_HEAP_ARRAY_H_