blob: 12c03b02c5f9bdb3856a5d1791450e246379ac8e [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_
#include <cstddef>
#include <iterator>
#include <memory>
#include <utility>
#include "base/compiler_specific.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// ContiguousContainer is a container which stores a list of heterogeneous
// items (in particular, of varying sizes), packed next to one another in
// memory. Items are never relocated, so it is safe to store pointers to them
// for the lifetime of the container (unless the item is removed).
//
// Memory is allocated in a series of buffers (with exponential growth). When an
// item is allocated, it is given only the space it requires (possibly with
// enough padding to preserve alignment), rather than the maximum possible size.
// This allows small and large items to coexist without wasting much space.
//
// Since it stores pointers to all of the items it allocates in a vector, it
// supports efficient iteration and indexing. However, for mutation the
// supported operations are limited to appending to the end of the list and
// replacing the last item.
//
// Clients should instantiate ContiguousContainer; ContiguousContainerBase is an
// artifact of the implementation.
class PLATFORM_EXPORT ContiguousContainerBase {
DISALLOW_NEW();
public:
ContiguousContainerBase(const ContiguousContainerBase&) = delete;
ContiguousContainerBase& operator=(const ContiguousContainerBase&) = delete;
ContiguousContainerBase(ContiguousContainerBase&&) = delete;
ContiguousContainerBase& operator=(ContiguousContainerBase&&) = delete;
protected:
// The initial capacity will be allocated when the first item is added.
ContiguousContainerBase(wtf_size_t max_item_size,
wtf_size_t initial_capacity_in_bytes);
~ContiguousContainerBase();
wtf_size_t size() const { return items_.size(); }
bool IsEmpty() const { return !size(); }
wtf_size_t CapacityInBytes() const;
wtf_size_t UsedCapacityInBytes() const;
wtf_size_t MemoryUsageInBytes() const;
// These do not invoke constructors or destructors.
uint8_t* Allocate(wtf_size_t item_size, const char* type_name);
wtf_size_t LastItemSize() const {
return static_cast<wtf_size_t>(buffers_.back().End() - items_.back());
}
using ItemVector = Vector<uint8_t*>;
ItemVector items_;
private:
class Buffer {
public:
Buffer(wtf_size_t buffer_size, const char* type_name)
: capacity_(static_cast<wtf_size_t>(
WTF::Partitions::BufferPotentialCapacity(buffer_size))),
begin_(static_cast<uint8_t*>(
WTF::Partitions::BufferMalloc(capacity_, type_name))),
end_(begin_) {
ANNOTATE_NEW_BUFFER(begin_, capacity_, 0);
}
~Buffer() {
ANNOTATE_DELETE_BUFFER(begin_, capacity_, UsedCapacity());
WTF::Partitions::BufferFree(begin_);
}
wtf_size_t Capacity() const { return capacity_; }
wtf_size_t UsedCapacity() const {
return static_cast<wtf_size_t>(end_ - begin_);
}
wtf_size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); }
bool IsEmpty() const { return UsedCapacity() == 0; }
uint8_t* Allocate(wtf_size_t item_size) {
DCHECK_GE(UnusedCapacity(), item_size);
ANNOTATE_CHANGE_SIZE(begin_, capacity_, UsedCapacity(),
UsedCapacity() + item_size);
uint8_t* result = end_;
end_ += item_size;
return result;
}
uint8_t* End() const { return end_; }
private:
// begin_ <= end_ <= begin_ + capacity_
wtf_size_t capacity_;
uint8_t* begin_;
uint8_t* end_;
};
Vector<Buffer> buffers_;
wtf_size_t max_item_size_;
wtf_size_t initial_capacity_in_bytes_;
};
// For most cases, no alignment stricter than pointer alignment is required. If
// one of the derived classes has stronger alignment requirements (and the
// static_assert fires), set alignment to the LCM of the derived class
// alignments. For small structs without pointers, it may be possible to reduce
// alignment for tighter packing.
template <class BaseItemType, unsigned alignment = sizeof(void*)>
class ContiguousContainer : public ContiguousContainerBase {
private:
// Declares itself as a forward iterator, but also supports a few more
// things. The whole random access iterator interface is a bit much.
template <typename BaseIterator, typename ValueType>
class IteratorWrapper
: public std::iterator<std::forward_iterator_tag, ValueType> {
DISALLOW_NEW();
public:
IteratorWrapper() = default;
bool operator==(const IteratorWrapper& other) const {
return it_ == other.it_;
}
bool operator!=(const IteratorWrapper& other) const {
return it_ != other.it_;
}
bool operator<(const IteratorWrapper& other) const {
return it_ < other.it_;
}
ValueType& operator*() const { return *reinterpret_cast<ValueType*>(*it_); }
ValueType* operator->() const { return &operator*(); }
IteratorWrapper operator+(std::ptrdiff_t n) const {
return IteratorWrapper(it_ + n);
}
IteratorWrapper operator++(int) {
IteratorWrapper tmp = *this;
++it_;
return tmp;
}
std::ptrdiff_t operator-(const IteratorWrapper& other) const {
return it_ - other.it_;
}
IteratorWrapper& operator++() {
++it_;
return *this;
}
private:
explicit IteratorWrapper(const BaseIterator& it) : it_(it) {}
BaseIterator it_;
friend class ContiguousContainer;
};
public:
using iterator = IteratorWrapper<ItemVector::iterator, BaseItemType>;
using const_iterator =
IteratorWrapper<ItemVector::const_iterator, const BaseItemType>;
using reverse_iterator =
IteratorWrapper<ItemVector::reverse_iterator, BaseItemType>;
using const_reverse_iterator =
IteratorWrapper<ItemVector::const_reverse_iterator, const BaseItemType>;
using value_type = BaseItemType;
ContiguousContainer(wtf_size_t max_item_size,
wtf_size_t initial_capacity_in_bytes)
: ContiguousContainerBase(Align(max_item_size),
initial_capacity_in_bytes) {}
~ContiguousContainer() {
for (auto& item : *this) {
(void)item; // MSVC incorrectly reports this variable as unused.
item.~BaseItemType();
}
}
using ContiguousContainerBase::CapacityInBytes;
using ContiguousContainerBase::IsEmpty;
using ContiguousContainerBase::MemoryUsageInBytes;
using ContiguousContainerBase::size;
using ContiguousContainerBase::UsedCapacityInBytes;
iterator begin() { return iterator(items_.begin()); }
iterator end() { return iterator(items_.end()); }
const_iterator begin() const { return const_iterator(items_.begin()); }
const_iterator end() const { return const_iterator(items_.end()); }
reverse_iterator rbegin() { return reverse_iterator(items_.rbegin()); }
reverse_iterator rend() { return reverse_iterator(items_.rend()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(items_.rbegin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(items_.rend());
}
BaseItemType& front() { return *begin(); }
const BaseItemType& front() const { return *begin(); }
BaseItemType& back() { return *rbegin(); }
const BaseItemType& back() const { return *rbegin(); }
BaseItemType& operator[](wtf_size_t index) { return *(begin() + index); }
const BaseItemType& operator[](wtf_size_t index) const {
return *(begin() + index);
}
template <class DerivedItemType, typename... Args>
DerivedItemType& AllocateAndConstruct(Args&&... args) {
static_assert(WTF::IsSubclass<DerivedItemType, BaseItemType>::value,
"Must use subclass of BaseItemType.");
static_assert(alignment % alignof(DerivedItemType) == 0,
"Derived type requires stronger alignment.");
return *new (AlignedAllocate(sizeof(DerivedItemType)))
DerivedItemType(std::forward<Args>(args)...);
}
// Appends a new item using memcpy, then default-constructs a base item
// in its place. Use with care.
BaseItemType& AppendByMoving(BaseItemType& item, wtf_size_t size) {
DCHECK_GE(size, sizeof(BaseItemType));
void* new_item = AlignedAllocate(size);
memcpy(new_item, static_cast<void*>(&item), size);
new (&item) BaseItemType;
return *static_cast<BaseItemType*>(new_item);
}
// The caller must ensure that |size| (the actual size of |item|) is the same
// as or smaller than the replaced item.
BaseItemType& ReplaceLastByMoving(BaseItemType& item, wtf_size_t size) {
DCHECK_GE(size, sizeof(BaseItemType));
DCHECK_GE(LastItemSize(), size);
back().~BaseItemType();
memcpy(static_cast<void*>(&back()), static_cast<void*>(&item), size);
new (&item) BaseItemType;
return back();
}
private:
void* AlignedAllocate(wtf_size_t size) {
void* result = ContiguousContainerBase::Allocate(
Align(size), WTF_HEAP_PROFILER_TYPE_NAME(BaseItemType));
DCHECK_EQ(reinterpret_cast<intptr_t>(result) & (alignment - 1), 0u);
return result;
}
static wtf_size_t Align(wtf_size_t size) {
wtf_size_t aligned_size = alignment * ((size + alignment - 1) / alignment);
DCHECK_EQ(aligned_size % alignment, 0u);
DCHECK_GE(aligned_size, size);
DCHECK_LT(aligned_size, size + alignment);
return aligned_size;
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_