#ifndef COMPACT_VECTOR_INL_H_ #error "Direct inclusion of this file is not allowed, include compact_vector.h" // For the sake of sane code completion. #include "compact_vector.h" #endif #undef COMPACT_VECTOR_INL_H_ #include #include #include #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// static_assert(sizeof(uintptr_t) == 8); static_assert(std::endian::native == std::endian::little); //////////////////////////////////////////////////////////////////////////////// template struct TCompactVectorOnHeapStorage { T* End; T* Capacity; T Elements[0]; }; //////////////////////////////////////////////////////////////////////////////// template class TCompactVectorReallocationPtrAdjuster { public: TCompactVectorReallocationPtrAdjuster(TVector* vector, TPtr& ptr) : Vector_(vector) , Ptr_(ptr) , Index_(ptr >= Vector_->begin() && ptr <= Vector_->end() ? std::distance(Vector_->begin(), const_cast(ptr)) : -1) { } ~TCompactVectorReallocationPtrAdjuster() { if (Index_ >= 0) { Ptr_ = Vector_->begin() + Index_; } } private: TVector* const Vector_; TPtr& Ptr_; const ptrdiff_t Index_; }; template class TCompactVectorReallocationPtrAdjuster { public: TCompactVectorReallocationPtrAdjuster(TVector* /*vector*/, std::nullptr_t /*ptr*/) { } }; //////////////////////////////////////////////////////////////////////////////// template TCompactVector::TCompactVector() noexcept { InlineMeta_.SizePlusOne = 1; } template TCompactVector::TCompactVector(const TCompactVector& other) : TCompactVector() { assign(other.begin(), other.end()); } template template TCompactVector::TCompactVector(const TCompactVector& other) : TCompactVector() { assign(other.begin(), other.end()); } template TCompactVector::TCompactVector(TCompactVector&& other) noexcept(std::is_nothrow_move_constructible_v) : TCompactVector() { swap(other); } template template TCompactVector::TCompactVector(TCompactVector&& other) : TCompactVector() { swap(other); } template TCompactVector::TCompactVector(size_type count) : TCompactVector() { assign(count, T()); } template TCompactVector::TCompactVector(size_type count, const T& value) : TCompactVector() { assign(count, value); } template template TCompactVector::TCompactVector(TIterator first, TIterator last) : TCompactVector() { assign(first, last); } template TCompactVector::TCompactVector(std::initializer_list list) : TCompactVector() { assign(list.begin(), list.end()); } template TCompactVector::~TCompactVector() { if (Y_LIKELY(IsInline())) { Destroy(&InlineElements_[0], &InlineElements_[InlineMeta_.SizePlusOne - 1]); } else { auto* storage = OnHeapMeta_.Storage; Destroy(storage->Elements, storage->End); ::free(storage); } } template bool TCompactVector::empty() const { if (Y_LIKELY(IsInline())) { return InlineMeta_.SizePlusOne == 1; } else { const auto* storage = OnHeapMeta_.Storage; return storage->Elements == storage->End; } } template auto TCompactVector::begin() -> iterator { return Y_LIKELY(IsInline()) ? &InlineElements_[0] : OnHeapMeta_.Storage->Elements; } template auto TCompactVector::begin() const -> const_iterator { return const_cast(this)->begin(); } template auto TCompactVector::end() -> iterator { return Y_LIKELY(IsInline()) ? &InlineElements_[InlineMeta_.SizePlusOne - 1] : OnHeapMeta_.Storage->End; } template auto TCompactVector::end() const -> const_iterator { return const_cast(this)->end(); } template auto TCompactVector::rbegin() -> reverse_iterator { return static_cast(end()); } template auto TCompactVector::rbegin() const -> const_reverse_iterator { return static_cast(end()); } template auto TCompactVector::rend() -> reverse_iterator { return static_cast(begin()); } template auto TCompactVector::rend() const -> const_reverse_iterator { return static_cast(begin()); } template auto TCompactVector::size() const -> size_type { if (Y_LIKELY(IsInline())) { return InlineMeta_.SizePlusOne - 1; } else { const auto* storage = OnHeapMeta_.Storage; return storage->End - storage->Elements; } } template auto TCompactVector::capacity() const -> size_type { if (Y_LIKELY(IsInline())) { return N; } else { const auto* storage = OnHeapMeta_.Storage; return storage->Capacity - storage->Elements; } } template auto TCompactVector::max_size() const -> size_type { return static_cast(-1) / sizeof(T); } template auto TCompactVector::data() -> pointer { return static_cast(begin()); } template auto TCompactVector::data() const -> const_pointer { return static_cast(begin()); } template auto TCompactVector::operator[](size_type index) -> reference { YT_ASSERT(index < size()); return begin()[index]; } template auto TCompactVector::operator[](size_type index) const -> const_reference { return const_cast(this)->operator[](index); } template auto TCompactVector::front() -> reference { YT_ASSERT(!empty()); return begin()[0]; } template auto TCompactVector::front() const -> const_reference { return const_cast(this)->front(); } template auto TCompactVector::back() -> reference { YT_ASSERT(!empty()); return end()[-1]; } template auto TCompactVector::back() const -> const_reference { return const_cast(this)->back(); } template void TCompactVector::push_back(const T& value) { PushBackImpl( &value, [&] (T* dst, const T* value) { ::new(dst) T(*value); }); } template void TCompactVector::push_back(T&& value) { PushBackImpl( &value, [&] (T* dst, T* value) { ::new(dst) T(std::move(*value)); }); } template template auto TCompactVector::emplace(const_iterator pos, TArgs&&... args) -> iterator { return InsertOneImpl( pos, nullptr, [&] (auto* dst, std::nullptr_t) { ::new(dst) T(std::forward(args)...); }, [&] (auto* dst, std::nullptr_t) { *dst = T(std::forward(args)...); }); } template template auto TCompactVector::emplace_back(TArgs&&... args) -> reference { return PushBackImpl( nullptr, [&] (T* dst, std::nullptr_t) { ::new(dst) T(std::forward(args)...); }); } template void TCompactVector::pop_back() { YT_ASSERT(!empty()); if (Y_LIKELY(IsInline())) { InlineElements_[InlineMeta_.SizePlusOne - 2].T::~T(); --InlineMeta_.SizePlusOne; } else { auto* storage = OnHeapMeta_.Storage; storage->End[-1].T::~T(); --storage->End; } } template auto TCompactVector::erase(const_iterator pos) -> iterator { YT_ASSERT(pos >= begin()); YT_ASSERT(pos < end()); auto* mutablePos = const_cast(pos); Move(mutablePos + 1, end(), mutablePos); pop_back(); return mutablePos; } template auto TCompactVector::erase(const_iterator first, const_iterator last) -> iterator { YT_ASSERT(first >= begin()); YT_ASSERT(last <= end()); auto* mutableFirst = const_cast(first); auto* mutableLast = const_cast(last); auto count = std::distance(mutableFirst, mutableLast); if (Y_LIKELY(IsInline())) { auto* end = &InlineElements_[0] + InlineMeta_.SizePlusOne - 1; Move(mutableLast, end, mutableFirst); Destroy(end - count, end); InlineMeta_.SizePlusOne -= count; } else { auto* storage = OnHeapMeta_.Storage; auto* end = storage->End; Move(mutableLast, storage->End, mutableFirst); Destroy(end - count, end); storage->End -= count; } return mutableFirst; } template void TCompactVector::clear() { if (Y_LIKELY(IsInline())) { Destroy(&InlineElements_[0], &InlineElements_[InlineMeta_.SizePlusOne - 1]); InlineMeta_.SizePlusOne = 1; } else { auto* storage = OnHeapMeta_.Storage; Destroy(storage->Elements, storage->End); storage->End = storage->Elements; } } template void TCompactVector::resize(size_type newSize) { ResizeImpl( newSize, [] (auto* dst) { ::new(dst) T(); }); } template void TCompactVector::resize(size_type newSize, const T& value) { ResizeImpl( newSize, [&] (auto* dst) { ::new(dst) T(value); }); } template void TCompactVector::reserve(size_t newCapacity) { if (Y_UNLIKELY(newCapacity > N)) { EnsureOnHeapCapacity(newCapacity, /*incremental*/ false); } } template void TCompactVector::swap(TCompactVector& other) { if (this == &other) { return; } if (!IsInline() && !other.IsInline()) { std::swap(OnHeapMeta_.Storage, other.OnHeapMeta_.Storage); return; } auto* lhs = this; auto* rhs = &other; if (lhs->size() < rhs->size()) { std::swap(lhs, rhs); } size_t rhsSize = rhs->size(); size_t lhsSize = lhs->size(); if (lhsSize > rhs->capacity()) { rhs->EnsureOnHeapCapacity(lhs->size(), /*incremental*/ false); } for (size_t index = 0; index < rhsSize; ++index) { std::swap((*lhs)[index], (*rhs)[index]); } UninitializedMove(lhs->begin() + rhsSize, lhs->end(), rhs->end()); Destroy(lhs->begin() + rhsSize, lhs->end()); rhs->SetSize(lhsSize); lhs->SetSize(rhsSize); } template void TCompactVector::assign(size_type count, const T& value) { clear(); if (Y_UNLIKELY(count > capacity())) { EnsureOnHeapCapacity(count, /*incremental*/ false); } auto* dst = begin(); std::uninitialized_fill(dst, dst + count, value); SetSize(count); } template template void TCompactVector::assign(TIterator first, TIterator last) { clear(); auto count = std::distance(first, last); if (Y_UNLIKELY(count > static_cast(capacity()))) { EnsureOnHeapCapacity(count, /*incremental*/ false); } std::uninitialized_copy(first, last, begin()); SetSize(count); } template void TCompactVector::assign(std::initializer_list list) { assign(list.begin(), list.end()); } template template void TCompactVector::assign(const TCompactVector& other) { if constexpr(N == OtherN) { if (this == &other) { return; } } auto otherSize = other.size(); auto otherBegin = other.begin(); if (capacity() >= otherSize) { const auto* src = other.begin(); auto* dst = begin(); auto thisSize = size(); auto copySize = std::min(thisSize, otherSize); Copy(src, src + copySize, dst); src += copySize; dst += copySize; auto uninitializedCopySize = otherSize - copySize; UninitializedCopy(src, src + uninitializedCopySize, dst); // NB: src += uninitializedCopySize is not needed. dst += uninitializedCopySize; if (thisSize > otherSize) { Destroy(dst, end()); } SetSize(otherSize); return; } clear(); EnsureOnHeapCapacity(otherSize, /*incremental*/ false); YT_ASSERT(!IsInline()); auto* storage = OnHeapMeta_.Storage; UninitializedCopy(otherBegin, otherBegin + otherSize, storage->Elements); storage->End = storage->Elements + otherSize; } template template void TCompactVector::assign(TCompactVector&& other) { if constexpr(N == OtherN) { if (this == &other) { return; } } clear(); if (!other.IsInline()) { if (Y_UNLIKELY(!IsInline())) { ::free(OnHeapMeta_.Storage); } OnHeapMeta_.Storage = other.OnHeapMeta_.Storage; other.InlineMeta_.SizePlusOne = 1; return; } auto otherSize = other.size(); if (Y_UNLIKELY(otherSize > capacity())) { EnsureOnHeapCapacity(otherSize, /*incremental*/ false); } auto* otherBegin = other.begin(); UninitializedMove(otherBegin, otherBegin + otherSize, begin()); SetSize(otherSize); other.clear(); } template auto TCompactVector::operator=(const TCompactVector& other) -> TCompactVector& { assign(other); return *this; } template template auto TCompactVector::operator=(const TCompactVector& other) -> TCompactVector& { assign(other); return *this; } template auto TCompactVector::operator=(TCompactVector&& other) -> TCompactVector& { assign(std::move(other)); return *this; } template template auto TCompactVector::operator=(TCompactVector&& other) -> TCompactVector& { assign(std::move(other)); return *this; } template auto TCompactVector::operator=(std::initializer_list list) -> TCompactVector& { assign(list); return *this; } template auto TCompactVector::insert(const_iterator pos, const T& value) -> iterator { return InsertOneImpl( pos, &value, [&] (auto* dst, const auto* value) { ::new(dst) T(*value); }, [&] (auto* dst, const auto* value) { *dst = *value; }); } template auto TCompactVector::insert(const_iterator pos, T&& value) -> iterator { return InsertOneImpl( pos, &value, [&] (auto* dst, auto* value) { ::new(dst) T(std::move(*value)); }, [&] (auto* dst, auto* value) { *dst = std::move(*value); }); } template auto TCompactVector::insert(const_iterator pos, size_type count, const T& value) -> iterator { return InsertManyImpl( pos, count, [&] (auto* dstFirst, auto* dstLast) { for (auto* dst = dstFirst; dst != dstLast; ++dst) { ::new(dst) T(value); } }, [&] (auto* dstFirst, auto* dstLast) { for (auto* dst = dstFirst; dst != dstLast; ++dst) { *dst = value; } }); } template template auto TCompactVector::insert(const_iterator pos, TIterator first, TIterator last) -> iterator { auto current = first; return InsertManyImpl( pos, std::distance(first, last), [&] (auto* dstFirst, auto* dstLast) { for (auto* dst = dstFirst; dst != dstLast; ++dst) { ::new(dst) T(*current++); } }, [&] (auto* dstFirst, auto* dstLast) { for (auto* dst = dstFirst; dst != dstLast; ++dst) { *dst = *current++; } }); } template auto TCompactVector::insert(const_iterator pos, std::initializer_list list) -> iterator { return insert(pos, list.begin(), list.end()); } template void TCompactVector::shrink_to_small() { if (Y_LIKELY(IsInline())) { return; } auto size = this->size(); if (size > N) { return; } auto* storage = OnHeapMeta_.Storage; UninitializedMove(storage->Elements, storage->End, &InlineElements_[0]); Destroy(storage->Elements, storage->End); ::free(storage); InlineMeta_.SizePlusOne = size + 1; } template bool TCompactVector::IsInline() const { return InlineMeta_.SizePlusOne != 0; } template void TCompactVector::SetSize(size_t newSize) { if (Y_LIKELY(IsInline())) { InlineMeta_.SizePlusOne = newSize + 1; } else { auto* storage = OnHeapMeta_.Storage; storage->End = storage->Elements + newSize; } } template void TCompactVector::EnsureOnHeapCapacity(size_t newCapacity, bool incremental) { newCapacity = std::max(newCapacity, N + 1); if (incremental) { newCapacity = std::max(newCapacity, capacity() * 2); } auto byteSize = sizeof(TOnHeapStorage) + newCapacity * sizeof(T); byteSize = nallocx(byteSize, 0); newCapacity = (byteSize - sizeof(TOnHeapStorage)) / sizeof(T); auto* newStorage = static_cast(::malloc(byteSize)); YT_VERIFY((reinterpret_cast(newStorage) >> 56) == 0); newStorage->Capacity = newStorage->Elements + newCapacity; size_t size; if (IsInline()) { size = InlineMeta_.SizePlusOne - 1; UninitializedMove(&InlineElements_[0], &InlineElements_[0] + size, newStorage->Elements); Destroy(&InlineElements_[0], &InlineElements_[0] + size); } else { auto* storage = OnHeapMeta_.Storage; size = storage->End - storage->Elements; UninitializedMove(storage->Elements, storage->End, newStorage->Elements); Destroy(storage->Elements, storage->End); ::free(storage); } newStorage->End = newStorage->Elements + size; OnHeapMeta_.Storage = newStorage; } template template auto TCompactVector::PushBackImpl(TPtr valuePtr, F&& func) -> reference { auto sizePlusOne = InlineMeta_.SizePlusOne; if (Y_LIKELY(sizePlusOne != 0 && sizePlusOne != N + 1)) { auto* dst = &InlineElements_[sizePlusOne - 1]; func(dst, valuePtr); ++InlineMeta_.SizePlusOne; return *dst; } auto hasSpareOnHeapCapacity = [&] { if (sizePlusOne != 0) { return false; } auto* storage = OnHeapMeta_.Storage; return storage->End < storage->Capacity; }; if (Y_UNLIKELY(!hasSpareOnHeapCapacity())) { TCompactVectorReallocationPtrAdjuster valuePtrAdjuster(this, valuePtr); EnsureOnHeapCapacity(0, /*incremental*/ true); } YT_ASSERT(!IsInline()); auto* storage = OnHeapMeta_.Storage; auto* dst = storage->End++; func(dst, valuePtr); return *dst; } template template void TCompactVector::ResizeImpl(size_type newSize, F&& func) { auto size = this->size(); if (newSize > size) { if (Y_UNLIKELY(newSize > capacity())) { EnsureOnHeapCapacity(newSize, /*incremental*/ false); } auto* first = end(); auto* last = first + newSize - size; for (auto* current = first; current != last; ++current) { func(current); } } else if (newSize < size) { Destroy(begin() + newSize, end()); } SetSize(newSize); } template template auto TCompactVector::InsertOneImpl(const_iterator pos, TPtr valuePtr, UninitializedF&& uninitializedFunc, InitializedF&& initializedFunc) -> iterator { YT_ASSERT(pos >= begin()); YT_ASSERT(pos <= end()); auto* mutablePos = const_cast(pos); auto newSize = size() + 1; if (Y_UNLIKELY(newSize > capacity())) { TCompactVectorReallocationPtrAdjuster mutablePosAdjuster(this, mutablePos); TCompactVectorReallocationPtrAdjuster valuePtrAdjuster(this, valuePtr); EnsureOnHeapCapacity(newSize, /*incremental*/ true); } auto* end = this->end(); if constexpr(!std::is_same_v) { if (valuePtr >= mutablePos && valuePtr < end) { ++valuePtr; } } auto moveCount = std::distance(mutablePos, end); if (moveCount == 0) { uninitializedFunc(end, valuePtr); } else { if constexpr(std::is_trivially_copyable_v) { ::memmove(mutablePos + 1, mutablePos, moveCount * sizeof(T)); } else { ::new(end) T(std::move(end[-1])); MoveBackward(mutablePos, end - 1, mutablePos + 1); } initializedFunc(mutablePos, valuePtr); } SetSize(newSize); return mutablePos; } template template auto TCompactVector::InsertManyImpl(const_iterator pos, size_t insertCount, UninitializedF&& uninitializedFunc, InitializedF&& initializedFunc) -> iterator { YT_ASSERT(pos >= begin()); YT_ASSERT(pos <= end()); auto* mutablePos = const_cast(pos); if (insertCount == 0) { return mutablePos; } auto size = this->size(); auto newSize = size + insertCount; if (Y_UNLIKELY(newSize > capacity())) { auto index = std::distance(begin(), mutablePos); EnsureOnHeapCapacity(newSize, /*incremental*/ true); mutablePos = begin() + index; } auto* end = this->end(); auto moveCount = std::distance(mutablePos, end); if constexpr(std::is_trivially_copyable_v) { ::memmove(mutablePos + insertCount, mutablePos, moveCount * sizeof(T)); initializedFunc(mutablePos, mutablePos + insertCount); } else { if (static_cast(insertCount) >= moveCount) { UninitializedMove(mutablePos, end, mutablePos + insertCount); initializedFunc(mutablePos, end); uninitializedFunc(end, end + insertCount - moveCount); } else { auto overlapCount = moveCount - insertCount; UninitializedMove(mutablePos + overlapCount, end, mutablePos + overlapCount + insertCount); MoveBackward(mutablePos, mutablePos + overlapCount, mutablePos + insertCount); initializedFunc(mutablePos, mutablePos + insertCount); } } SetSize(newSize); return mutablePos; } template void TCompactVector::Destroy(T* first, T* last) { if constexpr(!std::is_trivially_destructible_v) { for (auto* current = first; current != last; ++current) { current->T::~T(); } } } template template void TCompactVector::Copy(const T1* srcFirst, const T1* srcLast, T2* dst) { if constexpr(std::is_trivially_copyable_v && std::is_same_v) { ::memcpy(dst, srcFirst, (srcLast - srcFirst) * sizeof(T)); } else { std::copy(srcFirst, srcLast, dst); } } template template void TCompactVector::UninitializedCopy(const T1* srcFirst, const T1* srcLast, T2* dst) { if constexpr(std::is_trivially_copyable_v && std::is_same_v) { ::memcpy(dst, srcFirst, (srcLast - srcFirst) * sizeof(T)); } else { std::uninitialized_copy(srcFirst, srcLast, dst); } } template void TCompactVector::Move(T* srcFirst, T* srcLast, T* dst) { if constexpr(std::is_trivially_copyable_v) { ::memmove(dst, srcFirst, (srcLast - srcFirst) * sizeof(T)); } else { std::move(srcFirst, srcLast, dst); } } template void TCompactVector::UninitializedMove(T* srcFirst, T* srcLast, T* dst) { if constexpr(std::is_trivially_copyable_v) { ::memcpy(dst, srcFirst, (srcLast - srcFirst) * sizeof(T)); } else { std::uninitialized_move(srcFirst, srcLast, dst); } } template void TCompactVector::MoveBackward(T* srcFirst, T* srcLast, T* dst) { auto* src = srcLast; dst += std::distance(srcFirst, srcLast); while (src > srcFirst) { *--dst = std::move(*--src); } } ///////////////////////////////////////////////////////////////////////////// template bool operator==(const TCompactVector& lhs, const TCompactVector& rhs) { if constexpr(LhsN == RhsN) { if (&lhs == &rhs) { return true; } } if (lhs.size() != rhs.size()) { return false; } return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template bool operator!=(const TCompactVector& lhs, const TCompactVector& rhs) { return !(lhs == rhs); } template bool operator<(const TCompactVector& lhs, const TCompactVector& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } //////////////////////////////////////////////////////////////////////////////// template void swap(TCompactVector& lhs, TCompactVector& rhs) // NOLINT { lhs.swap(rhs); } ///////////////////////////////////////////////////////////////////////////// } // namespace NYT namespace std { //////////////////////////////////////////////////////////////////////////////// template struct hash> { size_t operator()(const NYT::TCompactVector& container) const { size_t result = 0; for (const auto& element : container) { NYT::HashCombine(result, element); } return result; } }; //////////////////////////////////////////////////////////////////////////////// } // namespace std