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