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 | |