%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/zone/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/zone/zone-chunk-list.h |
// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <algorithm> #include "src/base/iterator.h" #include "src/common/globals.h" #include "src/utils/memcopy.h" #include "src/zone/zone.h" #ifndef V8_ZONE_ZONE_CHUNK_LIST_H_ #define V8_ZONE_ZONE_CHUNK_LIST_H_ namespace v8 { namespace internal { template <typename T, bool backwards, bool modifiable> class ZoneChunkListIterator; // A zone-backed hybrid of a vector and a linked list. Use it if you need a // collection that // * needs to grow indefinitely, // * will mostly grow at the back, but may sometimes grow in front as well // (preferably in batches), // * needs to have very low overhead, // * offers forward- and backwards-iteration, // * offers relatively fast seeking, // * offers bidirectional iterators, // * can be rewound without freeing the backing store, // * can be split and joined again efficiently. // This list will maintain a doubly-linked list of chunks. When a chunk is // filled up, a new one gets appended. New chunks appended at the end will // grow in size up to a certain limit to avoid over-allocation and to keep // the zone clean. Chunks may be partially filled. In particular, chunks may // be empty after rewinding, such that they can be reused when inserting // again at a later point in time. template <typename T> class ZoneChunkList : public ZoneObject { public: using iterator = ZoneChunkListIterator<T, false, true>; using const_iterator = ZoneChunkListIterator<T, false, false>; using reverse_iterator = ZoneChunkListIterator<T, true, true>; using const_reverse_iterator = ZoneChunkListIterator<T, true, false>; static constexpr uint32_t kInitialChunkCapacity = 8; static constexpr uint32_t kMaxChunkCapacity = 256; explicit ZoneChunkList(Zone* zone) : zone_(zone) {} MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ZoneChunkList); size_t size() const { return size_; } bool empty() const { return size() == 0; } T& front(); const T& front() const; T& back(); const T& back() const; void push_back(const T& item); // If the first chunk has space, inserts into it at the front. Otherwise // allocate a new chunk with the same growth strategy as `push_back`. // This limits the amount of copying to O(`kMaxChunkCapacity`). void push_front(const T& item); // Cuts the last list elements so at most 'limit' many remain. Does not // free the actual memory, since it is zone allocated. void Rewind(const size_t limit = 0); // Quickly scans the list to retrieve the element at the given index. Will // *not* check bounds. iterator Find(const size_t index); const_iterator Find(const size_t index) const; // TODO(heimbuef): Add 'rFind', seeking from the end and returning a // reverse iterator. // Splits off a new list that contains the elements from `split_begin` to // `end()`. The current list is truncated to end just before `split_begin`. // This naturally invalidates all iterators, including `split_begin`. ZoneChunkList<T> SplitAt(iterator split_begin); void Append(ZoneChunkList<T>& other); void CopyTo(T* ptr); iterator begin() { return iterator::Begin(this); } iterator end() { return iterator::End(this); } reverse_iterator rbegin() { return reverse_iterator::Begin(this); } reverse_iterator rend() { return reverse_iterator::End(this); } const_iterator begin() const { return const_iterator::Begin(this); } const_iterator end() const { return const_iterator::End(this); } const_reverse_iterator rbegin() const { return const_reverse_iterator::Begin(this); } const_reverse_iterator rend() const { return const_reverse_iterator::End(this); } void swap(ZoneChunkList<T>& other) { DCHECK_EQ(zone_, other.zone_); std::swap(size_, other.size_); std::swap(front_, other.front_); std::swap(last_nonempty_, other.last_nonempty_); } private: template <typename S, bool backwards, bool modifiable> friend class ZoneChunkListIterator; struct Chunk { uint32_t capacity_ = 0; uint32_t position_ = 0; Chunk* next_ = nullptr; Chunk* previous_ = nullptr; T* items() { return reinterpret_cast<T*>(this + 1); } const T* items() const { return reinterpret_cast<const T*>(this + 1); } uint32_t size() const { DCHECK_LE(position_, capacity_); return position_; } bool empty() const { return size() == 0; } bool full() const { return size() == capacity_; } }; Chunk* NewChunk(const uint32_t capacity) { void* memory = zone_->Allocate<Chunk>(sizeof(Chunk) + capacity * sizeof(T)); Chunk* chunk = new (memory) Chunk(); chunk->capacity_ = capacity; return chunk; } static uint32_t NextChunkCapacity(uint32_t previous_capacity) { return std::min(previous_capacity * 2, kMaxChunkCapacity); } struct SeekResult { Chunk* chunk_; uint32_t chunk_index_; }; // Returns the chunk and relative index of the element at the given global // index. Will skip entire chunks and is therefore faster than iterating. SeekResult SeekIndex(size_t index) const; #ifdef DEBUG // Check the invariants. void Verify() const { if (front_ == nullptr) { // Initial empty state. DCHECK_NULL(last_nonempty_); DCHECK_EQ(0, size()); } else if (empty()) { // Special case: Fully rewound list, with only empty chunks. DCHECK_EQ(front_, last_nonempty_); DCHECK_EQ(0, size()); for (Chunk* chunk = front_; chunk != nullptr; chunk = chunk->next_) { DCHECK(chunk->empty()); } } else { // Normal state: Somewhat filled and (partially) rewound. DCHECK_NOT_NULL(last_nonempty_); size_t size_check = 0; bool in_empty_tail = false; for (Chunk* chunk = front_; chunk != nullptr; chunk = chunk->next_) { // Chunks from `front_` to `last_nonempty_` (inclusive) are non-empty. DCHECK_EQ(in_empty_tail, chunk->empty()); size_check += chunk->size(); if (chunk == last_nonempty_) { in_empty_tail = true; } } DCHECK_EQ(size_check, size()); } } #endif Zone* zone_; size_t size_ = 0; Chunk* front_ = nullptr; Chunk* last_nonempty_ = nullptr; }; template <typename T, bool backwards, bool modifiable> class ZoneChunkListIterator : public base::iterator<std::bidirectional_iterator_tag, T> { private: template <typename S> using maybe_const = typename std::conditional<modifiable, S, typename std::add_const<S>::type>::type; using Chunk = maybe_const<typename ZoneChunkList<T>::Chunk>; using ChunkList = maybe_const<ZoneChunkList<T>>; public: maybe_const<T>& operator*() const { return current_->items()[position_]; } maybe_const<T>* operator->() const { return ¤t_->items()[position_]; } bool operator==(const ZoneChunkListIterator& other) const { return other.current_ == current_ && other.position_ == position_; } bool operator!=(const ZoneChunkListIterator& other) const { return !operator==(other); } ZoneChunkListIterator& operator++() { Move<backwards>(); return *this; } ZoneChunkListIterator operator++(int) { ZoneChunkListIterator clone(*this); Move<backwards>(); return clone; } ZoneChunkListIterator& operator--() { Move<!backwards>(); return *this; } ZoneChunkListIterator operator--(int) { ZoneChunkListIterator clone(*this); Move<!backwards>(); return clone; } void Advance(uint32_t amount) { static_assert(!backwards, "Advance only works on forward iterators"); #ifdef DEBUG ZoneChunkListIterator clone(*this); for (uint32_t i = 0; i < amount; ++i) { ++clone; } #endif CHECK(!base::bits::UnsignedAddOverflow32(position_, amount, &position_)); while (position_ > 0 && position_ >= current_->position_) { auto overshoot = position_ - current_->position_; current_ = current_->next_; position_ = overshoot; DCHECK(position_ == 0 || current_); } #ifdef DEBUG DCHECK_EQ(clone, *this); #endif } private: friend class ZoneChunkList<T>; static ZoneChunkListIterator Begin(ChunkList* list) { // Forward iterator: if (!backwards) return ZoneChunkListIterator(list->front_, 0); // Backward iterator: if (list->empty()) return End(list); DCHECK(!list->last_nonempty_->empty()); return ZoneChunkListIterator(list->last_nonempty_, list->last_nonempty_->position_ - 1); } static ZoneChunkListIterator End(ChunkList* list) { // Backward iterator: if (backwards) return ZoneChunkListIterator(nullptr, 0); // Forward iterator: if (list->empty()) return Begin(list); // NOTE: Decrementing `end()` is not supported if `last_nonempty_->next_` // is nullptr (in that case `Move` will crash on dereference). return ZoneChunkListIterator(list->last_nonempty_->next_, 0); } ZoneChunkListIterator(Chunk* current, uint32_t position) : current_(current), position_(position) { DCHECK(current == nullptr || position < current->capacity_); } template <bool move_backward> void Move() { if (move_backward) { // Move backwards. if (position_ == 0) { current_ = current_->previous_; position_ = current_ ? current_->position_ - 1 : 0; } else { --position_; } } else { // Move forwards. ++position_; if (position_ >= current_->position_) { current_ = current_->next_; position_ = 0; } } } Chunk* current_; uint32_t position_; }; template <typename T> T& ZoneChunkList<T>::front() { DCHECK(!empty()); return *begin(); } template <typename T> const T& ZoneChunkList<T>::front() const { DCHECK(!empty()); return *begin(); } template <typename T> T& ZoneChunkList<T>::back() { DCHECK(!empty()); // Avoid the branch in `ZoneChunkListIterator::Begin()`. V8_ASSUME(size_ != 0); return *rbegin(); } template <typename T> const T& ZoneChunkList<T>::back() const { DCHECK(!empty()); // Avoid the branch in `ZoneChunkListIterator::Begin()`. V8_ASSUME(size_ != 0); return *rbegin(); } template <typename T> void ZoneChunkList<T>::push_back(const T& item) { if (last_nonempty_ == nullptr) { // Initially empty chunk list. front_ = NewChunk(kInitialChunkCapacity); last_nonempty_ = front_; } else if (last_nonempty_->full()) { // If there is an empty chunk following, reuse that, otherwise allocate. if (last_nonempty_->next_ == nullptr) { Chunk* chunk = NewChunk(NextChunkCapacity(last_nonempty_->capacity_)); last_nonempty_->next_ = chunk; chunk->previous_ = last_nonempty_; } last_nonempty_ = last_nonempty_->next_; DCHECK(!last_nonempty_->full()); } last_nonempty_->items()[last_nonempty_->position_] = item; ++last_nonempty_->position_; ++size_; DCHECK_LE(last_nonempty_->position_, last_nonempty_->capacity_); } template <typename T> void ZoneChunkList<T>::push_front(const T& item) { if (front_ == nullptr) { // Initially empty chunk list. front_ = NewChunk(kInitialChunkCapacity); last_nonempty_ = front_; } else if (front_->full()) { // First chunk at capacity, so prepend a new chunk. DCHECK_NULL(front_->previous_); Chunk* chunk = NewChunk(NextChunkCapacity(front_->capacity_)); front_->previous_ = chunk; chunk->next_ = front_; front_ = chunk; } DCHECK(!front_->full()); T* end = front_->items() + front_->position_; std::move_backward(front_->items(), end, end + 1); front_->items()[0] = item; ++front_->position_; ++size_; DCHECK_LE(front_->position_, front_->capacity_); } template <typename T> typename ZoneChunkList<T>::SeekResult ZoneChunkList<T>::SeekIndex( size_t index) const { DCHECK_LT(index, size()); Chunk* current = front_; while (index >= current->capacity_) { index -= current->capacity_; current = current->next_; } DCHECK_LT(index, current->capacity_); return {current, static_cast<uint32_t>(index)}; } template <typename T> void ZoneChunkList<T>::Rewind(const size_t limit) { if (limit >= size()) return; SeekResult seek_result = SeekIndex(limit); DCHECK_NOT_NULL(seek_result.chunk_); // Do a partial rewind of the chunk containing the index. seek_result.chunk_->position_ = seek_result.chunk_index_; // Set last_nonempty_ so iterators will work correctly. last_nonempty_ = seek_result.chunk_; // Do full rewind of all subsequent chunks. for (Chunk* current = seek_result.chunk_->next_; current != nullptr; current = current->next_) { current->position_ = 0; } size_ = limit; #ifdef DEBUG Verify(); #endif } template <typename T> typename ZoneChunkList<T>::iterator ZoneChunkList<T>::Find(const size_t index) { SeekResult seek_result = SeekIndex(index); return typename ZoneChunkList<T>::iterator(seek_result.chunk_, seek_result.chunk_index_); } template <typename T> typename ZoneChunkList<T>::const_iterator ZoneChunkList<T>::Find( const size_t index) const { SeekResult seek_result = SeekIndex(index); return typename ZoneChunkList<T>::const_iterator(seek_result.chunk_, seek_result.chunk_index_); } template <typename T> ZoneChunkList<T> ZoneChunkList<T>::SplitAt(iterator split_begin) { ZoneChunkList<T> result(zone_); // `result` is an empty freshly-constructed list. if (split_begin == end()) return result; // `this` is empty after the split and `result` contains everything. if (split_begin == begin()) { this->swap(result); return result; } // There is at least one element in both `this` and `result`. // Split the chunk. Chunk* split_chunk = split_begin.current_; DCHECK_LE(split_begin.position_, split_chunk->position_); T* chunk_split_begin = split_chunk->items() + split_begin.position_; T* chunk_split_end = split_chunk->items() + split_chunk->position_; uint32_t new_chunk_size = static_cast<uint32_t>(chunk_split_end - chunk_split_begin); uint32_t new_chunk_capacity = std::max( kInitialChunkCapacity, base::bits::RoundUpToPowerOfTwo32(new_chunk_size)); CHECK_LE(new_chunk_size, new_chunk_capacity); Chunk* new_chunk = NewChunk(new_chunk_capacity); std::copy(chunk_split_begin, chunk_split_end, new_chunk->items()); new_chunk->position_ = new_chunk_size; split_chunk->position_ = split_begin.position_; // Split the linked list. result.front_ = new_chunk; result.last_nonempty_ = (last_nonempty_ == split_chunk) ? new_chunk : last_nonempty_; new_chunk->next_ = split_chunk->next_; if (new_chunk->next_) { new_chunk->next_->previous_ = new_chunk; } last_nonempty_ = split_chunk; split_chunk->next_ = nullptr; // Compute the new size. size_t new_size = 0; for (Chunk* chunk = front_; chunk != split_chunk; chunk = chunk->next_) { DCHECK(!chunk->empty()); new_size += chunk->size(); } new_size += split_chunk->size(); DCHECK_LT(new_size, size()); result.size_ = size() - new_size; size_ = new_size; #ifdef DEBUG Verify(); result.Verify(); #endif return result; } template <typename T> void ZoneChunkList<T>::Append(ZoneChunkList<T>& other) { DCHECK_EQ(zone_, other.zone_); if (other.front_ == nullptr) return; last_nonempty_->next_ = other.front_; other.front_->previous_ = last_nonempty_; last_nonempty_ = other.last_nonempty_; size_ += other.size_; #ifdef DEBUG Verify(); #endif // Leave `other` in empty, but valid state. other.front_ = nullptr; other.last_nonempty_ = nullptr; other.size_ = 0; } template <typename T> void ZoneChunkList<T>::CopyTo(T* ptr) { for (Chunk* current = front_; current != nullptr; current = current->next_) { void* start = current->items(); void* end = current->items() + current->position_; size_t bytes = static_cast<size_t>(reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start)); MemCopy(ptr, current->items(), bytes); ptr += current->position_; } } } // namespace internal } // namespace v8 #endif // V8_ZONE_ZONE_CHUNK_LIST_H_