blob: 9869f838ab2630067788d69291e32b1370afd066 [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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#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, typename Deleter = void>
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;
// We don't put this default value in the template parameter list to allow the
// static_assert on is_reference_v to give a nicer error message.
using deleter_type = std::
conditional_t<std::is_void_v<Deleter>, std::default_delete<T[]>, Deleter>;
// 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[], deleter_type>(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[], deleter_type>(new T[size]), size);
}
static HeapArray CopiedFrom(base::span<const T> that) {
auto result = HeapArray::Uninit(that.size());
result.copy_from(that);
return result;
}
// Constructs a HeapArray from an existing pointer, taking ownership of the
// pointer.
//
// # Safety
// The pointer must be correctly aligned for type `T` and able to be deleted
// through the `deleter_type`, which defaults to the `delete[]` operation. The
// `ptr` must point to an array of at least `size` many elements. If these are
// not met, then Undefined Behaviour can result.
//
// # Checks
// When the `size` is zero, the `ptr` must be null.
UNSAFE_BUFFER_USAGE static HeapArray FromOwningPointer(T* ptr, size_t size) {
if (!size) {
CHECK_EQ(ptr, nullptr);
return HeapArray();
}
return HeapArray(std::unique_ptr<T[], deleter_type>(ptr), 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);
}
// Leaks the memory in the HeapArray so that it will never be freed, and
// consumes the HeapArray, returning an unowning span that points to the
// memory.
base::span<T> leak() && {
HeapArray<T> dropped = std::move(*this);
T* leaked = dropped.data_.release();
return make_span(leaked, dropped.size_);
}
// Delete the memory previously obtained from leak(). Argument is a pointer
// rather than a span to facilitate use by callers that have lost track of
// size information, as may happen when being passed through a C-style
// function callback. The void* argument type makes its signature compatible
// with typical void (*cb)(void*) C-style deletion callback.
static void DeleteLeakedData(void* ptr) {
// Memory is freed by unique ptr going out of scope.
std::unique_ptr<T[], deleter_type> deleter(static_cast<T*>(ptr));
}
private:
HeapArray(std::unique_ptr<T[], deleter_type> data, size_t size)
: data_(std::move(data)), size_(size) {}
std::unique_ptr<T[], deleter_type> data_;
size_t size_ = 0u;
};
} // namespace base
#endif // BASE_CONTAINERS_HEAP_ARRAY_H_