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