1 /*******************************************************************
2  *
3  *  hugemem.c
4  *
5  *    Memory management component (body)
6  *    for dealing with "huge" objects with 16-bit Windows.
7  *
8  *  Written by Antoine Leca based on ideas from Dave Hoo.
9  *  Copyright 1999 by Dave Hoo, Antoine Leca,
10  *  David Turner, Robert Wilhelm, and Werner Lemberg.
11  *
12  *  This file is part of the FreeType project, and may only be used
13  *  modified and distributed under the terms of the FreeType project
14  *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
15  *  this file you indicate that you have read the license and
16  *  understand and accept it fully.
17  *
18  ******************************************************************/
19 
20 #include <limits.h>
21 #include <windows.h>
22 
23 #include "ttdebug.h"
24 #include "ttmemory.h"
25 #include "ttengine.h"
26 
27 #ifndef TT_HUGE_PTR
28 #error  "This component needs TT_HUGE_PTR to be #defined."
29 #endif
30 
31 #ifdef  TT_CONFIG_OPTION_THREAD_SAFE
32 #error  "This component needs static allocation and is not re-entrant."
33 #endif
34 
35   /* If the memory reclaimed is abobve this limit, alloc directly from */
36   /* global heap. Else, alloc using malloc (using suballocation).      */
37 #ifndef MEMORY_MIN_GLOBAL
38 #define MEMORY_MIN_GLOBAL   4096
39 #endif
40 
41 /* required by the tracing mode */
42 #undef  TT_COMPONENT
43 #define TT_COMPONENT  trace_memory
44 
45 
46 #ifdef DEBUG_MEMORY
47 
48 #include <stdio.h>
49 
50 #define MAX_TRACKED_BLOCKS  1024
51 
52   struct  TMemRec_
53   {
54     void*  base;
55     Long   size;
56   };
57 
58   typedef struct TMemRec_  TMemRec;
59 
60   static TMemRec  pointers[MAX_TRACKED_BLOCKS + 1];
61 
62   static Int  num_alloc;
63   static Int  num_free;
64   static Int  num_realloc; /* counts only `real' reallocations
65                               (i.e., an existing buffer will be resized
66                               to a value larger than zero */
67 
68   static Int  fail_alloc;
69   static Int  fail_realloc;
70   static Int  fail_free;
71 
72 #else
73 
74   /* We need a tracing stack of the calls to big chunks of memory,   */
75   /* in order to call the matching version of free().                */
76 
77 #define MAX_TRACKED_BIGCHUNKS    64
78 
79   struct  TMemRec_
80   {
81     void*  base;
82   };
83 
84   typedef struct TMemRec_  TMemRec;
85 
86   static TMemRec  pointers[MAX_TRACKED_BIGCHUNKS + 1];
87 
88 #endif /* DEBUG_MEMORY */
89 
90 
91 #ifndef TT_CONFIG_REENTRANT
92   Long  TTMemory_Allocated;
93   Long  TTMemory_MaxAllocated;
94 #endif
95 
96 
97 /*******************************************************************
98  *
99  *  Function    :  TT_Alloc
100  *
101  *  Description :  Allocates memory from the heap buffer.
102  *
103  *  Input  :  Size      size of the memory to be allocated
104  *            P         pointer to a buffer pointer
105  *
106  *  Output :  Error code.
107  *
108  *  NOTE :  The newly allocated block should _always_ be zeroed
109  *          on return.  Many parts of the engine rely on this to
110  *          work properly.
111  *
112  ******************************************************************/
113 
114   EXPORT_FUNC
TT_Alloc(ULong Size,void ** P)115   TT_Error  TT_Alloc( ULong  Size, void**  P )
116   {
117     Int  i;
118 
119 
120     if ( !P )
121       return TT_Err_Invalid_Argument;
122         /* Also see below for another case of "invalid argument". */
123 
124     if ( Size > 0 )
125     {
126       if ( Size >= MEMORY_MIN_GLOBAL )
127       {
128         HANDLE  hMem;
129 
130         hMem = GlobalAlloc( GMEM_ZEROINIT, Size );
131         if ( !hMem )
132           return TT_Err_Out_Of_Memory;
133 
134         *P = (void*)GlobalLock( hMem );
135       }
136       else
137         *P = (void*)malloc( Size );
138 
139       if ( !*P )
140         return TT_Err_Out_Of_Memory;
141 
142 #ifndef TT_CONFIG_REENTRANT
143       TTMemory_MaxAllocated += Size;
144       TTMemory_Allocated    += Size;
145 #endif
146 
147 #ifdef DEBUG_MEMORY
148 
149       num_alloc++;
150 
151       i = 0;
152       while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != NULL )
153         i++;
154 
155       if ( i >= MAX_TRACKED_BLOCKS )
156         fail_alloc++;
157       else
158       {
159         pointers[i].base = *P;
160         pointers[i].size = Size;
161       }
162 
163 #else
164 
165       if ( Size >= MEMORY_MIN_GLOBAL )
166       {
167         i = 0;
168         while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != NULL )
169           i++;
170 
171         if ( i >= MAX_TRACKED_BIGCHUNKS )
172           /* We fail badly here. Increase MAX_TRACKED_BIGCHUNKS if needed. */
173           return TT_Err_Invalid_Argument;
174         else
175           pointers[i].base = *P;
176       }
177 
178 #endif /* DEBUG_MEMORY */
179 
180       /* The nice thing about GlobalAlloc is that it zeroes the memory. */
181 
182       if ( Size < MEMORY_MIN_GLOBAL )
183         MEM_Set( *P, 0, Size );
184 
185     }
186     else
187       *P = NULL;
188 
189     return TT_Err_Ok;
190   }
191 
192 
193 #ifdef TT_CONFIG_OPTION_EXTEND_ENGINE
194 
195 
196 /*******************************************************************
197  *
198  *  Function    :  TT_Realloc
199  *
200  *  Description :  Reallocates memory from the heap buffer.
201  *
202  *  Input  :  Size      new size of the memory to be allocated;
203  *                      if zero, TT_Free() will be called
204  *            P         pointer to a buffer pointer; if *P == NULL,
205  *                      TT_Alloc() will be called
206  *
207  *  Output :  Error code.
208  *
209  *  NOTES :  It's not necessary to zero the memory in case the
210  *           reallocated buffer is larger than before -- the
211  *           application has to take care of this.
212  *
213  *           If the memory request fails, TT_Free() will be
214  *           called on *P, and TT_Err_Out_Of_Memory returned.
215  *
216  ******************************************************************/
217 
218   EXPORT_FUNC
TT_Realloc(ULong Size,void ** P)219   TT_Error  TT_Realloc( ULong  Size, void**  P )
220   {
221     ULong   oldSize;
222     void*   Q;
223     Int  i;
224 
225 
226     if ( !P )
227       return TT_Err_Invalid_Argument;
228 
229     if ( !*P )
230       return TT_Alloc( Size, P );
231 
232     if ( Size == 0 )
233       return TT_Free( P );
234 
235 #ifdef DEBUG_MEMORY
236 
237     num_realloc++;
238 
239     i = 0;
240     while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != *P )
241       i++;
242 
243     if ( i >= MAX_TRACKED_BLOCKS )
244       fail_realloc++;
245     else
246       oldSize = pointers[i].size;
247 
248 #else
249 
250     i = 0;
251     while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != *P )
252       i++;
253 
254     /* If we did not found the pointer, then this is a "small" chunk. */
255 
256     if ( i < MAX_TRACKED_BIGCHUNKS )
257     {
258         /* Signal we found a big one. Real size does not matter. */
259       oldSize = MEMORY_MIN_GLOBAL;
260     }
261 
262 #endif /* DEBUG_MEMORY */
263 
264     if ( oldSize >= MEMORY_MIN_GLOBAL )
265     {
266       /* Deal with a big chunk. */
267       HANDLE hMem, hNewMem;
268 
269       hMem = GlobalHandle ( (ULong)*P >> 16 ) & 0xFFFF;
270       if ( !hMem )  /* Bad call... */
271         return TT_Err_Invalid_Argument;
272 
273       GlobalUnlock( hMem );
274 	  hNewMem = GlobalReAlloc( hMem, Size, 0 );
275       if ( hNewMem )
276         *P = (void*)GlobalLock( hNewMem );
277     }
278     if ( Size >= MEMORY_MIN_GLOBAL )
279     {
280       /* A small chunk crosses the limit... */
281 
282       if( TT_Alloc( Size, &Q ) != TT_Err_Ok )
283         Q = NULL;  /* Failed to create the new block. */
284       else
285         MEM_Copy( Q, *P, oldSize );
286 
287           /* We need to register the new entry. */
288 #ifndef DEBUG_MEMORY
289 
290       i = 0;
291       while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != NULL )
292         i++;
293 
294       if ( i >= MAX_TRACKED_BIGCHUNKS )
295         /* We fail badly here. Increase MAX_TRACKED_BIGCHUNKS if needed. */
296         return TT_Err_Invalid_Argument;
297 #endif /* DEBUG_MEMORY */
298     }
299     else
300       Q = (void*)realloc( *P, Size );
301 
302     if ( !Q )
303     {
304       TT_Free( *P );
305       return TT_Err_Out_Of_Memory;
306     }
307 
308 #ifdef DEBUG_MEMORY
309 
310     if ( i < MAX_TRACKED_BLOCKS )
311     {
312 #ifndef TT_CONFIG_REENTRANT
313       TTMemory_Allocated += Size - pointers[i].size;
314       if ( Size > pointers[i].size )
315         TTMemory_MaxAllocated += Size - pointers[i].size;
316 #endif
317 
318       pointers[i].base = Q;
319       pointers[i].size = Size;
320     }
321 #else
322     if ( i < MAX_TRACKED_BIGCHUNKS )
323     {
324       pointers[i].base = Q;
325     }
326 #endif /* DEBUG_MEMORY */
327 
328     *P = Q;
329 
330     return TT_Err_Ok;
331   }
332 
333 
334 #endif /* TT_CONFIG_OPTION_EXTEND_ENGINE */
335 
336 
337 /*******************************************************************
338  *
339  *  Function    :  TT_Free
340  *
341  *  Description :  Releases a previously allocated block of memory.
342  *
343  *  Input  :  P    pointer to memory block
344  *
345  *  Output :  Always SUCCESS.
346  *
347  *  Note : The pointer must _always_ be set to NULL by this function.
348  *
349  ******************************************************************/
350 
351   EXPORT_FUNC
TT_Free(void ** P)352   TT_Error  TT_Free( void**  P )
353   {
354     Int  i;
355     Long Size = 0;
356 
357 
358     if ( !P || !*P )
359       return TT_Err_Ok;
360 
361 #ifdef DEBUG_MEMORY
362 
363     num_free++;
364 
365     i = 0;
366     while ( i < MAX_TRACKED_BLOCKS && pointers[i].base != *P )
367       i++;
368 
369     if ( i >= MAX_TRACKED_BLOCKS )
370       fail_free++;
371     else
372     {
373 #ifndef TT_CONFIG_REENTRANT
374       TTMemory_Allocated -= pointers[i].size;
375 #endif
376 
377       Size = pointers[i].size;
378       pointers[i].base = NULL;
379       pointers[i].size = 0;
380     }
381 
382 #else
383 
384     i = 0;
385     while ( i < MAX_TRACKED_BIGCHUNKS && pointers[i].base != *P )
386       i++;
387 
388     /* If we did not found the pointer, then this is a "small" chunk. */
389 
390     if ( i < MAX_TRACKED_BIGCHUNKS )
391     {
392       pointers[i].base = NULL;
393         /* Signal we found a big one. Real size does not matter. */
394       Size = MEMORY_MIN_GLOBAL;
395     }
396 
397 #endif /* DEBUG_MEMORY */
398 
399     if ( Size >= MEMORY_MIN_GLOBAL )
400     {
401       HANDLE hMem;
402 
403       hMem = GlobalHandle ( (ULong)*P >> 16 ) & 0xFFFF;
404       if ( !hMem )  /* Bad call... */
405         return TT_Err_Invalid_Argument;
406 
407       GlobalUnlock( hMem );
408       GlobalFree  ( hMem );
409     }
410     else
411       free( *P );
412 
413     *P = NULL;
414 
415     return TT_Err_Ok;
416   }
417 
418 
419 /*******************************************************************
420  *
421  *  Function    :  TTMemory_Init
422  *
423  *  Description :  Initializes the memory.
424  *
425  *  Output :  Always SUCCESS.
426  *
427  ******************************************************************/
428 
429   LOCAL_FUNC
TTMemory_Init(void)430   TT_Error  TTMemory_Init( void )
431   {
432 #ifdef DEBUG_MEMORY
433     Int  i;
434 
435 
436     for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ )
437     {
438       pointers[i].base = NULL;
439       pointers[i].size = 0;
440     }
441 
442     num_alloc   = 0;
443     num_realloc = 0;
444     num_free    = 0;
445 
446     fail_alloc   = 0;
447     fail_realloc = 0;
448     fail_free    = 0;
449 #else
450     Int  i;
451 
452     for ( i = 0; i < MAX_TRACKED_BIGCHUNKS; i++ )
453     {
454       pointers[i].base = NULL;
455     }
456 #endif
457 
458 
459 #ifndef TT_CONFIG_REENTRANT
460     TTMemory_Allocated    = 0;
461     TTMemory_MaxAllocated = 0;
462 #endif
463 
464     return TT_Err_Ok;
465   }
466 
467 
468 /*******************************************************************
469  *
470  *  Function    :  TTMemory_Done
471  *
472  *  Description :  Finalizes memory usage.
473  *
474  *  Output :  Always SUCCESS.
475  *
476  ******************************************************************/
477 
478   LOCAL_FUNC
TTMemory_Done(void)479   TT_Error  TTMemory_Done( void )
480   {
481 #ifdef DEBUG_MEMORY
482     Int  i, num_leaked, tot_leaked;
483 
484 
485     num_leaked = 0;
486     tot_leaked = 0;
487 
488     for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ )
489     {
490       if ( pointers[i].base )
491       {
492         num_leaked ++;
493         tot_leaked += pointers[i].size;
494       }
495     }
496 
497     fprintf( stderr,
498              "%d memory allocations, of which %d failed\n",
499              num_alloc,
500              fail_alloc );
501 
502     fprintf( stderr,
503              "%d memory reallocations, of which %d failed\n",
504              num_realloc,
505              fail_realloc );
506 
507     fprintf( stderr,
508              "%d memory frees, of which %d failed\n",
509              num_free,
510              fail_free );
511 
512     if ( num_leaked > 0 )
513     {
514       fprintf( stderr,
515                "There are %d leaked memory blocks, totalizing %d bytes\n",
516                num_leaked, tot_leaked );
517 
518       for ( i = 0; i < MAX_TRACKED_BLOCKS; i++ )
519       {
520         if ( pointers[i].base )
521         {
522           fprintf( stderr,
523                    "index: %4d (base: $%08lx, size: %08ld)\n",
524                    i,
525                    (long)pointers[i].base,
526                    pointers[i].size );
527         }
528       }
529     }
530     else
531       fprintf( stderr, "No memory leaks !\n" );
532 
533 #endif /* DEBUG_MEMORY */
534 
535     return TT_Err_Ok;
536   }
537 
538 
539 /* END */
540