blob: 98bce1957c4aa2692f746cff3ceb33aaef9a4653 [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 ContiguousContainer_h
#define ContiguousContainer_h
#include "platform/PlatformExport.h"
#include "wtf/Alignment.h"
#include "wtf/Allocator.h"
#include "wtf/Compiler.h"
#include "wtf/Noncopyable.h"
#include "wtf/TypeTraits.h"
#include "wtf/Vector.h"
#include <cstddef>
#include <iterator>
#include <memory>
#include <utility>
namespace blink {
// ContiguousContainer is a container which stores a list of heterogeneous
// objects (in particular, of varying sizes), packed next to one another in
// memory. Objects are never relocated, so it is safe to store pointers to them
// for the lifetime of the container (unless the object is removed).
//
// Memory is allocated in a series of buffers (with exponential growth). When an
// object 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 objects to coexist without wasting much space.
//
// Since it stores pointers to all of the objects it allocates in a vector, it
// supports efficient iteration and indexing. However, for mutation the
// supported operations are limited to appending to, and removing from, the end
// of the list.
//
// Clients should instantiate ContiguousContainer; ContiguousContainerBase is an
// artifact of the implementation.
class PLATFORM_EXPORT ContiguousContainerBase {
DISALLOW_NEW();
WTF_MAKE_NONCOPYABLE(ContiguousContainerBase);
protected:
explicit ContiguousContainerBase(size_t maxObjectSize);
ContiguousContainerBase(ContiguousContainerBase&&);
~ContiguousContainerBase();
ContiguousContainerBase& operator=(ContiguousContainerBase&&);
size_t size() const { return m_elements.size(); }
bool isEmpty() const { return !size(); }
size_t capacityInBytes() const;
size_t usedCapacityInBytes() const;
size_t memoryUsageInBytes() const;
// These do not invoke constructors or destructors.
void reserveInitialCapacity(size_t, const char* typeName);
void* allocate(size_t objectSize, const char* typeName);
void removeLast();
void clear();
void swap(ContiguousContainerBase&);
// Discards excess buffer capacity. Intended for use when no more appending
// is anticipated.
void shrinkToFit();
Vector<void*> m_elements;
private:
class Buffer;
Buffer* allocateNewBufferForNextAllocation(size_t, const char* typeName);
Vector<std::unique_ptr<Buffer>> m_buffers;
unsigned m_endIndex;
size_t m_maxObjectSize;
};
// 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 BaseElementType, 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() {}
bool operator==(const IteratorWrapper& other) const { return m_it == other.m_it; }
bool operator!=(const IteratorWrapper& other) const { return m_it != other.m_it; }
ValueType& operator*() const { return *static_cast<ValueType*>(*m_it); }
ValueType* operator->() const { return &operator*(); }
IteratorWrapper operator+(std::ptrdiff_t n) const { return IteratorWrapper(m_it + n); }
IteratorWrapper operator++(int) { IteratorWrapper tmp = *this; ++m_it; return tmp; }
std::ptrdiff_t operator-(const IteratorWrapper& other) const { return m_it - other.m_it; }
IteratorWrapper& operator++() { ++m_it; return *this; }
private:
explicit IteratorWrapper(const BaseIterator& it) : m_it(it) {}
BaseIterator m_it;
friend class ContiguousContainer;
};
public:
using iterator = IteratorWrapper<Vector<void*>::iterator, BaseElementType>;
using const_iterator = IteratorWrapper<Vector<void*>::const_iterator, const BaseElementType>;
using reverse_iterator = IteratorWrapper<Vector<void*>::reverse_iterator, BaseElementType>;
using const_reverse_iterator = IteratorWrapper<Vector<void*>::const_reverse_iterator, const BaseElementType>;
explicit ContiguousContainer(size_t maxObjectSize) : ContiguousContainerBase(align(maxObjectSize)) {}
ContiguousContainer(size_t maxObjectSize, size_t initialSizeBytes)
: ContiguousContainer(maxObjectSize)
{
reserveInitialCapacity(std::max(maxObjectSize, initialSizeBytes), WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType));
}
ContiguousContainer(ContiguousContainer&& source)
: ContiguousContainerBase(std::move(source)) {}
~ContiguousContainer()
{
for (auto& element : *this) {
(void)element; // MSVC incorrectly reports this variable as unused.
element.~BaseElementType();
}
}
ContiguousContainer& operator=(ContiguousContainer&& source)
{
// Must clear in the derived class to ensure that element destructors
// care called.
clear();
ContiguousContainerBase::operator=(std::move(source));
return *this;
}
using ContiguousContainerBase::size;
using ContiguousContainerBase::isEmpty;
using ContiguousContainerBase::capacityInBytes;
using ContiguousContainerBase::usedCapacityInBytes;
using ContiguousContainerBase::memoryUsageInBytes;
using ContiguousContainerBase::shrinkToFit;
iterator begin() { return iterator(m_elements.begin()); }
iterator end() { return iterator(m_elements.end()); }
const_iterator begin() const { return const_iterator(m_elements.begin()); }
const_iterator end() const { return const_iterator(m_elements.end()); }
reverse_iterator rbegin() { return reverse_iterator(m_elements.rbegin()); }
reverse_iterator rend() { return reverse_iterator(m_elements.rend()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(m_elements.rbegin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(m_elements.rend()); }
BaseElementType& first() { return *begin(); }
const BaseElementType& first() const { return *begin(); }
BaseElementType& last() { return *rbegin(); }
const BaseElementType& last() const { return *rbegin(); }
BaseElementType& operator[](size_t index) { return *(begin() + index); }
const BaseElementType& operator[](size_t index) const { return *(begin() + index); }
template <class DerivedElementType, typename... Args>
DerivedElementType& allocateAndConstruct(Args&&... args)
{
static_assert(WTF::IsSubclass<DerivedElementType, BaseElementType>::value,
"Must use subclass of BaseElementType.");
static_assert(alignment % WTF_ALIGN_OF(DerivedElementType) == 0,
"Derived type requires stronger alignment.");
return *new (alignedAllocate(sizeof(DerivedElementType))) DerivedElementType(std::forward<Args>(args)...);
}
void removeLast()
{
ASSERT(!isEmpty());
last().~BaseElementType();
ContiguousContainerBase::removeLast();
}
void clear()
{
for (auto& element : *this) {
(void)element; // MSVC incorrectly reports this variable as unused.
element.~BaseElementType();
}
ContiguousContainerBase::clear();
}
void swap(ContiguousContainer& other) { ContiguousContainerBase::swap(other); }
// Appends a new element using memcpy, then default-constructs a base
// element in its place. Use with care.
BaseElementType& appendByMoving(BaseElementType& item, size_t size)
{
ASSERT(size >= sizeof(BaseElementType));
void* newItem = alignedAllocate(size);
memcpy(newItem, static_cast<void*>(&item), size);
new (&item) BaseElementType;
return *static_cast<BaseElementType*>(newItem);
}
private:
void* alignedAllocate(size_t size)
{
void* result = ContiguousContainerBase::allocate(align(size), WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType));
DCHECK_EQ(reinterpret_cast<intptr_t>(result) & (alignment - 1), 0u);
return result;
}
static size_t align(size_t size)
{
size_t alignedSize = alignment * ((size + alignment - 1) / alignment);
DCHECK_EQ(alignedSize % alignment, 0u);
DCHECK_GE(alignedSize, size);
DCHECK_LT(alignedSize, size + alignment);
return alignedSize;
}
};
} // namespace blink
#endif // ContiguousContainer_h