1 // Copyright (c) 2002-2023 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <NCollection_IncAllocator.hxx>
16 #include <Standard_Mutex.hxx>
17 #include <Standard_OutOfMemory.hxx>
21 IMPLEMENT_STANDARD_RTTIEXT(NCollection_IncAllocator, NCollection_BaseAllocator)
25 // Bounds for checking block size level
26 static constexpr unsigned THE_SMALL_BOUND_BLOCK_SIZE = NCollection_IncAllocator::THE_DEFAULT_BLOCK_SIZE * 16; // 196 KB
27 static constexpr unsigned THE_MEDIUM_BOUND_BLOCK_SIZE = NCollection_IncAllocator::THE_DEFAULT_BLOCK_SIZE * 64; // 786 KB
28 static constexpr unsigned THE_LARGE_BOUND_BLOCK_SIZE = NCollection_IncAllocator::THE_DEFAULT_BLOCK_SIZE * 1024; // 12 MB
30 //=======================================================================
31 //function : computeLevel
33 //=======================================================================
34 NCollection_IncAllocator::IBlockSizeLevel computeLevel(const unsigned int theSize)
36 if (theSize < NCollection_IncAllocator::THE_DEFAULT_BLOCK_SIZE)
38 return NCollection_IncAllocator::IBlockSizeLevel::Min;
40 else if (theSize < THE_SMALL_BOUND_BLOCK_SIZE)
42 return NCollection_IncAllocator::IBlockSizeLevel::Small;
44 else if (theSize < THE_MEDIUM_BOUND_BLOCK_SIZE)
46 return NCollection_IncAllocator::IBlockSizeLevel::Medium;
48 else if (theSize < THE_LARGE_BOUND_BLOCK_SIZE)
50 return NCollection_IncAllocator::IBlockSizeLevel::Large;
54 return NCollection_IncAllocator::IBlockSizeLevel::Max;
59 //=======================================================================
60 //function : NCollection_IncAllocator
61 //purpose : Constructor
62 //=======================================================================
63 NCollection_IncAllocator::NCollection_IncAllocator(const size_t theDefaultSize) :
64 myBlockSize(static_cast<unsigned>(theDefaultSize < THE_MINIMUM_BLOCK_SIZE ? THE_DEFAULT_BLOCK_SIZE : theDefaultSize))
67 //=======================================================================
68 //function : SetThreadSafe
69 //purpose : Constructor
70 //=======================================================================
71 void NCollection_IncAllocator::SetThreadSafe (const bool theIsThreadSafe)
77 myMutex = new Standard_Mutex;
87 //=======================================================================
88 //function : ~NCollection_IncAllocator
89 //purpose : Destructor
90 //=======================================================================
91 NCollection_IncAllocator::~NCollection_IncAllocator()
97 //=======================================================================
98 //function : AllocateOptimal
99 //purpose : allocate a memory
100 //=======================================================================
101 void* NCollection_IncAllocator::AllocateOptimal(const size_t theSize)
103 Standard_Mutex::Sentry aLock(myMutex);
104 // Allocating using general block
105 IBlock* aBlock = nullptr;
106 // Use allocated blocks
107 if (myAllocationHeap && myAllocationHeap->AvailableSize >= theSize)
109 aBlock = myAllocationHeap;
111 else // Allocate new general block
113 if (++myBlockCount % 5 == 0) // increase count before checking
117 if (myBlockSize < theSize)
119 myBlockSize = static_cast<unsigned>(theSize);
121 void* aBufferBlock = Standard::AllocateOptimal(myBlockSize + sizeof(IBlock));
122 aBlock = new (aBufferBlock) IBlock(aBufferBlock, myBlockSize);
123 aBlock->NextBlock = myAllocationHeap;
124 aBlock->NextOrderedBlock = myOrderedBlocks;
125 myOrderedBlocks = aBlock;
126 myAllocationHeap = aBlock;
128 void* aRes = aBlock->CurPointer;
129 aBlock->CurPointer += theSize;
130 aBlock->AvailableSize -= theSize;
131 if (aBlock->AvailableSize < 16)
133 myAllocationHeap = aBlock->NextBlock;
134 aBlock->NextBlock = myUsedHeap;
139 IBlock* aBlockIter = aBlock->NextBlock;
140 IBlock* aBlockToReplaceAfter = nullptr;
141 while (aBlockIter) // Search new sorted position
143 if (aBlockIter->AvailableSize > aBlock->AvailableSize)
145 aBlockToReplaceAfter = aBlockIter;
146 aBlockIter = aBlockIter->NextBlock;
151 if (aBlockToReplaceAfter) // Update list order
153 IBlock* aNext = aBlockToReplaceAfter->NextBlock;
154 aBlockToReplaceAfter->NextBlock = aBlock;
155 myAllocationHeap = aBlock->NextBlock;
156 aBlock->NextBlock = aNext;
162 //=======================================================================
163 //function : Allocate
164 //purpose : Allocate a memory
165 //=======================================================================
166 void* NCollection_IncAllocator::Allocate(const size_t theSize)
168 return AllocateOptimal(theSize);
171 //=======================================================================
174 //=======================================================================
175 void NCollection_IncAllocator::clean()
177 Standard_Mutex::Sentry aLock(myMutex);
178 IBlock* aHeapIter = myOrderedBlocks;
181 IBlock* aCur = aHeapIter;
182 aHeapIter = aHeapIter->NextOrderedBlock;
183 Standard::Free(aCur);
185 myOrderedBlocks = nullptr;
186 myAllocationHeap = nullptr;
187 myUsedHeap = nullptr;
189 myBlockSize = THE_DEFAULT_BLOCK_SIZE;
192 //=======================================================================
193 //function : increaseBlockSize
195 //=======================================================================
196 void NCollection_IncAllocator::increaseBlockSize()
198 switch (computeLevel(myBlockSize))
200 case NCollection_IncAllocator::IBlockSizeLevel::Min:
203 case NCollection_IncAllocator::IBlockSizeLevel::Small:
206 case NCollection_IncAllocator::IBlockSizeLevel::Medium:
209 case NCollection_IncAllocator::IBlockSizeLevel::Large:
210 myBlockSize = static_cast<unsigned>(std::lround(myBlockSize * 1.5));
212 case NCollection_IncAllocator::IBlockSizeLevel::Max:
217 //=======================================================================
218 //function : resetBlock
220 //=======================================================================
221 void NCollection_IncAllocator::resetBlock(IBlock* theBlock) const
223 theBlock->AvailableSize = theBlock->AvailableSize + (theBlock->CurPointer - (reinterpret_cast<char*>(theBlock) + sizeof(IBlock)));
224 theBlock->CurPointer = reinterpret_cast<char*>(theBlock) + sizeof(IBlock);
227 //=======================================================================
230 //=======================================================================
231 void NCollection_IncAllocator::Reset(const Standard_Boolean theReleaseMemory)
233 if (theReleaseMemory)
238 Standard_Mutex::Sentry aLock(myMutex);
239 IBlock* aHeapIter = myOrderedBlocks;
242 IBlock* aCur = aHeapIter;
243 aHeapIter = aHeapIter->NextOrderedBlock;
244 aCur->NextBlock = aHeapIter;
245 resetBlock(aCur); // reset size and pointer
247 myAllocationHeap = myOrderedBlocks;
248 myUsedHeap = nullptr;
251 //=======================================================================
252 //function : IBlockSmall::IBlockSmall
254 //=======================================================================
255 NCollection_IncAllocator::IBlock::IBlock(void* thePointer,
256 const size_t theSize) :
257 CurPointer(static_cast<char*>(thePointer) + sizeof(IBlock)),
258 AvailableSize(theSize)