| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | #include <stdint.h> |
| 6 | #include <windows.h> |
| 7 | #include "debugmacros.h" |
| 8 | #include "iallocator.h" |
| 9 | #include "gcinfoarraylist.h" |
| 10 | #include "safemath.h" |
| 11 | |
| 12 | inline size_t roundUp(size_t size, size_t alignment) |
| 13 | { |
| 14 | // `alignment` must be a power of two |
| 15 | assert(alignment != 0); |
| 16 | assert((alignment & (alignment - 1)) == 0); |
| 17 | |
| 18 | return (size + (alignment - 1)) & ~(alignment - 1); |
| 19 | } |
| 20 | |
| 21 | GcInfoArrayListBase::GcInfoArrayListBase(IAllocator* allocator) |
| 22 | : m_allocator(allocator), |
| 23 | m_firstChunk(nullptr), |
| 24 | m_lastChunk(nullptr), |
| 25 | m_lastChunkCount(0), |
| 26 | m_lastChunkCapacity(0), |
| 27 | m_itemCount(0) |
| 28 | { |
| 29 | assert(m_allocator != nullptr); |
| 30 | } |
| 31 | |
| 32 | GcInfoArrayListBase::~GcInfoArrayListBase() |
| 33 | { |
| 34 | for (ChunkBase* list = m_firstChunk, *chunk; list != nullptr; list = chunk) |
| 35 | { |
| 36 | chunk = list->m_next; |
| 37 | m_allocator->Free(list); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | void GcInfoArrayListBase::AppendNewChunk(size_t firstChunkCapacity, size_t elementSize, size_t chunkAlignment) |
| 42 | { |
| 43 | size_t chunkCapacity = (m_firstChunk == nullptr) ? firstChunkCapacity : (m_lastChunkCapacity * GrowthFactor); |
| 44 | |
| 45 | S_SIZE_T chunkSize = S_SIZE_T(roundUp(sizeof(ChunkBase), chunkAlignment)) + (S_SIZE_T(elementSize) * S_SIZE_T(chunkCapacity)); |
| 46 | assert(!chunkSize.IsOverflow()); |
| 47 | |
| 48 | ChunkBase* chunk = reinterpret_cast<ChunkBase*>(m_allocator->Alloc(chunkSize.Value())); |
| 49 | chunk->m_next = nullptr; |
| 50 | |
| 51 | if (m_lastChunk != nullptr) |
| 52 | { |
| 53 | assert(m_firstChunk != nullptr); |
| 54 | m_lastChunk->m_next = chunk; |
| 55 | } |
| 56 | else |
| 57 | { |
| 58 | assert(m_lastChunk == nullptr); |
| 59 | m_firstChunk = chunk; |
| 60 | } |
| 61 | |
| 62 | m_lastChunk = chunk; |
| 63 | m_lastChunkCount = 0; |
| 64 | m_lastChunkCapacity = chunkCapacity; |
| 65 | } |
| 66 | |
| 67 | GcInfoArrayListBase::IteratorBase::IteratorBase(GcInfoArrayListBase* list, size_t firstChunkCapacity) |
| 68 | : m_list(list) |
| 69 | { |
| 70 | assert(m_list != nullptr); |
| 71 | |
| 72 | // Note: if the list is empty, m_list->firstChunk == nullptr == m_list->lastChunk and m_lastChunkCount == 0. |
| 73 | // In that case, the next two lines will set m_currentChunk to nullptr and m_currentChunkCount to 0. |
| 74 | m_currentChunk = m_list->m_firstChunk; |
| 75 | m_currentChunkCount = (m_currentChunk == m_list->m_lastChunk) ? m_list->m_lastChunkCount : firstChunkCapacity; |
| 76 | } |
| 77 | |
| 78 | GcInfoArrayListBase::ChunkBase* GcInfoArrayListBase::IteratorBase::GetNextChunk(size_t& elementCount) |
| 79 | { |
| 80 | if (m_currentChunk == nullptr) |
| 81 | { |
| 82 | elementCount = 0; |
| 83 | return nullptr; |
| 84 | } |
| 85 | |
| 86 | ChunkBase* chunk = m_currentChunk; |
| 87 | elementCount = m_currentChunkCount; |
| 88 | |
| 89 | m_currentChunk = m_currentChunk->m_next; |
| 90 | if (m_currentChunk == nullptr) |
| 91 | { |
| 92 | m_currentChunkCount = 0; |
| 93 | } |
| 94 | else if (m_currentChunk == m_list->m_lastChunk) |
| 95 | { |
| 96 | m_currentChunkCount = m_list->m_lastChunkCount; |
| 97 | } |
| 98 | else |
| 99 | { |
| 100 | m_currentChunkCount *= GrowthFactor; |
| 101 | } |
| 102 | |
| 103 | return chunk; |
| 104 | } |
| 105 | |