0024023: Revamp the OCCT Handle -- general
[occt.git] / src / NCollection / NCollection_IncAllocator.cxx
CommitLineData
b311480e 1// Created on: 2002-04-12
2// Created by: Alexander GRIGORIEV
973c2be1 3// Copyright (c) 2002-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
b311480e 15
7fd59977 16#include <NCollection_IncAllocator.hxx>
23be7421
M
17#include <NCollection_DataMap.hxx>
18#include <NCollection_Map.hxx>
19#include <Standard_Mutex.hxx>
7fd59977 20#include <Standard_OutOfMemory.hxx>
21#include <stdio.h>
64531d9c 22#include <fstream>
23#include <iomanip>
7fd59977 24
7fd59977 25
c99551fa
K
26namespace
27{
28
29 inline size_t IMEM_SIZE (const size_t theSize)
30 {
31 return (theSize - 1) / sizeof(NCollection_IncAllocator::aligned_t) + 1;
32 }
33
34 inline size_t IMEM_ALIGN (const void* theAddress)
35 {
36 return sizeof(NCollection_IncAllocator::aligned_t) * IMEM_SIZE (size_t(theAddress));
37 }
38
39 #define IMEM_FREE(p_bl) (size_t(p_bl->p_end_block - p_bl->p_free_space))
40
0797d9d3 41#ifdef OCCT_DEBUG
302f96fb 42 // auxiliary dummy function used to get a place where break point can be set
43 inline void place_for_breakpoint() {}
74d80fb9 44#endif
5640d653 45}
7fd59977 46
47#define MaxLookup 16
48
23be7421
M
49static Standard_Boolean IS_DEBUG = Standard_False;
50
51//=======================================================================
52/**
53 * Static data map (address -> AllocatorID)
54 */
55//=======================================================================
56static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
57{
58 static NCollection_DataMap<Standard_Address, Standard_Size> TheMap;
59 return TheMap;
60}
61
62//=======================================================================
63/**
64 * Static map (AllocatorID)
65 */
66//=======================================================================
67static NCollection_Map<Standard_Size>& StorageIDSet()
68{
69 static NCollection_Map<Standard_Size> TheMap;
70 return TheMap;
71}
72
73//=======================================================================
74//function : IncAllocator_SetDebugFlag
75//purpose : Turn on/off debugging of memory allocation
76//=======================================================================
77
78Standard_EXPORT void IncAllocator_SetDebugFlag(const Standard_Boolean theDebug)
79{
80 IS_DEBUG = theDebug;
81}
82
0797d9d3 83#ifdef OCCT_DEBUG
302f96fb 84
23be7421
M
85//=======================================================================
86/**
87 * Static value of the current allocation ID. It provides unique
88 * numbering of allocators.
89 */
90//=======================================================================
91static Standard_Size CurrentID = 0;
92static Standard_Size CATCH_ID = 0;
93
94//=======================================================================
95//function : Debug_Create
96//purpose : Store the allocator address in the internal maps
97//=======================================================================
98
99static void Debug_Create(Standard_Address theAlloc)
100{
101 static Standard_Mutex aMutex;
bd0c22ce 102 aMutex.Lock();
23be7421
M
103 StorageIDMap().Bind(theAlloc, ++CurrentID);
104 StorageIDSet().Add(CurrentID);
23be7421 105 if (CurrentID == CATCH_ID)
302f96fb 106 place_for_breakpoint();
107 aMutex.Unlock();
23be7421
M
108}
109
110//=======================================================================
111//function : Debug_Destroy
112//purpose : Forget the allocator address from the internal maps
113//=======================================================================
114
115static void Debug_Destroy(Standard_Address theAlloc)
116{
117 static Standard_Mutex aMutex;
bd0c22ce 118 aMutex.Lock();
23be7421
M
119 if (StorageIDMap().IsBound(theAlloc))
120 {
121 Standard_Size anID = StorageIDMap()(theAlloc);
122 StorageIDSet().Remove(anID);
123 StorageIDMap().UnBind(theAlloc);
124 }
bd0c22ce 125 aMutex.Unlock();
23be7421
M
126}
127
0797d9d3 128#endif /* OCCT_DEBUG */
302f96fb 129
23be7421
M
130//=======================================================================
131//function : IncAllocator_PrintAlive
132//purpose : Outputs the alive numbers to the file inc_alive.d
133//=======================================================================
134
135Standard_EXPORT void IncAllocator_PrintAlive()
136{
64531d9c 137 if (StorageIDSet().IsEmpty())
138 {
139 return;
140 }
141
142 std::ofstream aFileOut ("inc_alive.d", std::ios_base::trunc | std::ios_base::out);
143 if (!aFileOut.is_open())
144 {
145 std::cout << "failure writing file inc_alive.d" << std::endl;
146 return;
147 }
148 aFileOut.imbue (std::locale ("C"));
149 aFileOut << std::fixed << std::setprecision(1);
150
151 aFileOut << "Alive IncAllocators (number, size in Kb)\n";
152 Standard_Size aTotSize = 0;
153 Standard_Integer nbAlloc = 0;
154 for (NCollection_DataMap<Standard_Address, Standard_Size>::Iterator itMap (StorageIDMap());
155 itMap.More(); itMap.Next())
23be7421 156 {
64531d9c 157 const NCollection_IncAllocator* anAlloc = static_cast<NCollection_IncAllocator*>(itMap.Key());
158 Standard_Size anID = itMap.Value();
159 Standard_Size aSize = anAlloc->GetMemSize();
160 aTotSize += aSize;
161 nbAlloc++;
162 aFileOut << std::setw(20) << anID << ' '
163 << std::setw(20) << (double(aSize) / 1024.0)
164 << '\n';
23be7421 165 }
64531d9c 166 aFileOut << "Total:\n"
167 << std::setw(20) << nbAlloc << ' '
168 << std::setw(20) << (double(aTotSize) / 1024.0)
169 << '\n';
170 aFileOut.close();
23be7421
M
171}
172
7fd59977 173//=======================================================================
174//function : NCollection_IncAllocator()
175//purpose : Constructor
176//=======================================================================
177
178NCollection_IncAllocator::NCollection_IncAllocator (const size_t theBlockSize)
179{
180#ifdef ALLOC_TRACK_USAGE
181 printf ("\n..NCollection_IncAllocator: Created (%x)\n",this);
23be7421 182#endif
0797d9d3 183#ifdef OCCT_DEBUG
23be7421
M
184 if (IS_DEBUG)
185 Debug_Create(this);
7fd59977 186#endif
8ba3c5e0 187 const size_t aDefault = DefaultBlockSize;
7fd59977 188 const size_t aSize = IMEM_SIZE(sizeof(IBlock)) +
8ba3c5e0 189 IMEM_SIZE((theBlockSize > 2*sizeof(IBlock)) ? theBlockSize : aDefault);
7fd59977 190 IBlock * const aBlock = (IBlock *) malloc (aSize * sizeof(aligned_t));
191 myFirstBlock = aBlock;
db56cc2d 192 mySize = aSize - IMEM_SIZE(sizeof(IBlock));
23be7421 193 myMemSize = aSize * sizeof(aligned_t);
7fd59977 194 if (aBlock == NULL)
195 Standard_OutOfMemory::Raise("NCollection_IncAllocator: out of memory");
196 aBlock -> p_free_space = (aligned_t *) IMEM_ALIGN (&aBlock[1]);
197 aBlock -> p_end_block = ((aligned_t *) aBlock) + aSize;
198 aBlock -> p_next = NULL;
199}
200
201//=======================================================================
202//function : ~NCollection_IncAllocator
203//purpose : Destructor
204//=======================================================================
205
206NCollection_IncAllocator::~NCollection_IncAllocator ()
207{
0797d9d3 208#ifdef OCCT_DEBUG
23be7421
M
209 if (IS_DEBUG)
210 Debug_Destroy(this);
211#endif
7fd59977 212 Clean();
213 free (myFirstBlock);
214}
215
216//=======================================================================
217//function : Allocate
218//purpose : allocate a memory
219//remark : returns NULL if allocation fails
220//=======================================================================
221
222void * NCollection_IncAllocator::Allocate (const size_t aSize)
223{
224 aligned_t * aResult = NULL;
225 const size_t cSize = aSize ? IMEM_SIZE(aSize) : 0;
226
227 if (cSize > mySize) {
228 /* If the requested size exceeds normal allocation size, allocate
229 a separate block and place it as the head of the list */
230 aResult = (aligned_t *) allocateNewBlock (cSize+1);
231 if (aResult)
232 myFirstBlock -> p_free_space = myFirstBlock -> p_end_block;
db56cc2d 233 else
234 Standard_OutOfMemory::Raise("NCollection_IncAllocator: out of memory");
7fd59977 235 } else
236 if (cSize <= IMEM_FREE(myFirstBlock)) {
237 /* If the requested size fits into the free space in the 1st block */
238 aResult = myFirstBlock -> allocateInBlock (cSize);
239 } else {
240 /* Search for a block in the list with enough free space */
241 int aMaxLookup = MaxLookup; /* limit the number of blocks to query */
242 IBlock * aCurrentBlock = myFirstBlock -> p_next;
243 while (aCurrentBlock && aMaxLookup--) {
244 if (cSize <= IMEM_FREE(aCurrentBlock)) {
245 aResult = aCurrentBlock -> allocateInBlock (cSize);
246 break;
247 }
248 aCurrentBlock = aCurrentBlock -> p_next;
249 }
250 if (aResult == NULL) {
251 /* There is no available block with enough free space. Create a new
252 one and place it in the head of the list */
253 aResult = (aligned_t *) allocateNewBlock (mySize);
254 if (aResult)
255 myFirstBlock -> p_free_space = aResult + cSize;
db56cc2d 256 else
257 {
258 const size_t aDefault = IMEM_SIZE(DefaultBlockSize);
259 if (cSize > aDefault)
260 Standard_OutOfMemory::Raise("NCollection_IncAllocator: out of memory");
261 else
262 {
263 aResult = (aligned_t *) allocateNewBlock (aDefault);
264 if (aResult)
265 myFirstBlock -> p_free_space = aResult + cSize;
266 else
267 Standard_OutOfMemory::Raise("NCollection_IncAllocator: out of memory");
268 }
269 }
7fd59977 270 }
271 }
272 return aResult;
273}
274
275//=======================================================================
276//function : Reallocate
277//purpose :
278//=======================================================================
279
280void * NCollection_IncAllocator::Reallocate (void * theAddress,
281 const size_t oldSize,
282 const size_t newSize)
283{
284// Check that the dummy parameters are OK
285 if (theAddress == NULL || oldSize == 0)
286 return Allocate (newSize);
287 const size_t cOldSize = IMEM_SIZE(oldSize);
288 const size_t cNewSize = newSize ? IMEM_SIZE(newSize) : 0;
289 aligned_t * anAddress = (aligned_t *) theAddress;
290
291// We check only the LAST allocation to do the real extension/contraction
292 if (anAddress + cOldSize == myFirstBlock -> p_free_space) {
293 myFirstBlock -> p_free_space = anAddress;
294// If the new size fits into the memory block => OK
295// This also includes any case of contraction
296 if (cNewSize <= IMEM_FREE(myFirstBlock)) {
297 myFirstBlock -> p_free_space += cNewSize;
298 return anAddress;
299 }
300 }
301// In case of contraction of non-terminating allocation, do nothing
302 else if (cOldSize >= cNewSize)
303 return anAddress;
304// Extension of non-terminated allocation if there is enough room in the
305// current memory block
306 if (cNewSize <= IMEM_FREE(myFirstBlock)) {
307 aligned_t * aResult = myFirstBlock -> allocateInBlock (cNewSize);
308 if (aResult)
309 for (unsigned i = 0; i < cOldSize; i++)
310 aResult[i] = anAddress[i];
311 return aResult;
312 }
313
314// This is either of the cases:
315// - extension of non-terminating allocation, or
316// - extension of terminating allocation when the new size is too big
317// In both cases create a new memory block, allocate memory and copy there
318// the reallocated memory.
db56cc2d 319 size_t cMaxSize = mySize > cNewSize ? mySize : cNewSize;
320 aligned_t * aResult = (aligned_t *) allocateNewBlock (cMaxSize);
7fd59977 321 if (aResult) {
322 myFirstBlock -> p_free_space = aResult + cNewSize;
323 for (unsigned i = 0; i < cOldSize; i++)
324 aResult[i] = anAddress[i];
325 }
db56cc2d 326 else
327 {
328 Standard_OutOfMemory::Raise("NCollection_IncAllocator: out of memory");
329 }
7fd59977 330 return aResult;
331}
332
333//=======================================================================
334//function : Free
335//purpose :
336//=======================================================================
337
338void NCollection_IncAllocator::Free (void *)
339{}
340
341//=======================================================================
342//function : Clean
343//purpose :
344//=======================================================================
345
346void NCollection_IncAllocator::Clean ()
347{
348#ifdef ALLOC_TRACK_USAGE
349 printf ("\n..NCollection_IncAllocator: Memory size to clean:%8.1f kB (%x)\n",
350 double(GetMemSize())/1024, this);
351#endif
352 IBlock * aBlock = myFirstBlock;
353 if (aBlock) {
354 aBlock -> p_free_space = (aligned_t *) &aBlock[1];
355 aBlock = aBlock -> p_next;
356 while (aBlock) {
357 IBlock * aNext = aBlock -> p_next;
358 free (aBlock);
359 aBlock = aNext;
360 }
361 myFirstBlock -> p_next = NULL;
362 }
23be7421 363 myMemSize = 0;
7fd59977 364}
365
366//=======================================================================
367//function : Reset
368//purpose :
369//=======================================================================
370
371void NCollection_IncAllocator::Reset (const Standard_Boolean doReleaseMem)
372{
373 if (doReleaseMem)
374 Clean();
375 else {
376 Standard_Integer aBlockCount(0);
377 IBlock * aBlock = myFirstBlock;
378 while (aBlock)
379 if (aBlockCount++ < MaxLookup) {
380 aBlock -> p_free_space = (aligned_t *) &aBlock[1];
381 if (aBlockCount < MaxLookup)
382 aBlock = aBlock -> p_next;
383 else {
384 IBlock * aNext = aBlock -> p_next;
385 aBlock -> p_next = NULL;
386 aBlock = aNext;
387 }
388 } else {
389 IBlock * aNext = aBlock -> p_next;
23be7421 390 myMemSize -= (aBlock -> p_end_block - (aligned_t *) aBlock) * sizeof (aligned_t);
7fd59977 391 free (aBlock);
392 aBlock = aNext;
393 }
394 }
395}
396
397//=======================================================================
398//function : GetMemSize
399//purpose : diagnostic utility
400//=======================================================================
401
402size_t NCollection_IncAllocator::GetMemSize () const
403{
23be7421
M
404// size_t aResult = 0;
405// IBlock * aBlock = myFirstBlock;
406// while (aBlock) {
407// aResult += (aBlock -> p_end_block - (aligned_t *) aBlock);
408// aBlock = aBlock -> p_next;
409// }
410// return aResult * sizeof (aligned_t);
411 return myMemSize;
7fd59977 412}
413
414//=======================================================================
415//function : allocateNewBlock
416//purpose :
417//=======================================================================
418
419void * NCollection_IncAllocator::allocateNewBlock (const size_t cSize)
420{
421 aligned_t * aResult = 0L;
422 const size_t aSz = cSize + IMEM_SIZE(sizeof(IBlock));
423 IBlock * aBlock = (IBlock *) malloc (aSz * sizeof(aligned_t));
424 if (aBlock) {
425 aBlock -> p_end_block = ((aligned_t *)aBlock) + aSz;
426 aBlock -> p_next = myFirstBlock;
427 myFirstBlock = aBlock;
428 aResult = (aligned_t *) IMEM_ALIGN(&aBlock[1]);
23be7421 429 myMemSize += aSz * sizeof(aligned_t);
7fd59977 430 }
7fd59977 431 return aResult;
432}