1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % M M EEEEE M M OOO RRRR Y Y %
7 % MM MM E MM MM O O R R Y Y %
8 % M M M EEE M M M O O RRRR Y %
9 % M M E M M O O R R Y %
10 % M M EEEEE M M OOO R R Y %
11 % %
12 % %
13 % MagickCore Memory Allocation Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1998 %
18 % %
19 % %
20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % http://www.imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Segregate our memory requirements from any program that calls our API. This
37 % should help reduce the risk of others changing our program state or causing
38 % memory corruption.
39 %
40 % Our custom memory allocation manager implements a best-fit allocation policy
41 % using segregated free lists. It uses a linear distribution of size classes
42 % for lower sizes and a power of two distribution of size classes at higher
43 % sizes. It is based on the paper, "Fast Memory Allocation using Lazy Fits."
44 % written by Yoo C. Chung.
45 %
46 % By default, ANSI memory methods are called (e.g. malloc). Use the
47 % custom memory allocator by defining MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT
48 % to allocate memory with private anonymous mapping rather than from the
49 % heap.
50 %
51 */
52
53 /*
54 Include declarations.
55 */
56 #include "MagickCore/studio.h"
57 #include "MagickCore/blob.h"
58 #include "MagickCore/blob-private.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/memory-private.h"
63 #include "MagickCore/resource_.h"
64 #include "MagickCore/semaphore.h"
65 #include "MagickCore/string_.h"
66 #include "MagickCore/utility-private.h"
67
68 /*
69 Define declarations.
70 */
71 #define BlockFooter(block,size) \
72 ((size_t *) ((char *) (block)+(size)-2*sizeof(size_t)))
73 #define BlockHeader(block) ((size_t *) (block)-1)
74 #define BlockSize 4096
75 #define BlockThreshold 1024
76 #define MaxBlockExponent 16
77 #define MaxBlocks ((BlockThreshold/(4*sizeof(size_t)))+MaxBlockExponent+1)
78 #define MaxSegments 1024
79 #define MemoryGuard ((0xdeadbeef << 31)+0xdeafdeed)
80 #define NextBlock(block) ((char *) (block)+SizeOfBlock(block))
81 #define NextBlockInList(block) (*(void **) (block))
82 #define PreviousBlock(block) ((char *) (block)-(*((size_t *) (block)-2)))
83 #define PreviousBlockBit 0x01
84 #define PreviousBlockInList(block) (*((void **) (block)+1))
85 #define SegmentSize (2*1024*1024)
86 #define SizeMask (~0x01)
87 #define SizeOfBlock(block) (*BlockHeader(block) & SizeMask)
88
89 /*
90 Typedef declarations.
91 */
92 typedef enum
93 {
94 UndefinedVirtualMemory,
95 AlignedVirtualMemory,
96 MapVirtualMemory,
97 UnalignedVirtualMemory
98 } VirtualMemoryType;
99
100 typedef struct _DataSegmentInfo
101 {
102 void
103 *allocation,
104 *bound;
105
106 MagickBooleanType
107 mapped;
108
109 size_t
110 length;
111
112 struct _DataSegmentInfo
113 *previous,
114 *next;
115 } DataSegmentInfo;
116
117 typedef struct _MagickMemoryMethods
118 {
119 AcquireMemoryHandler
120 acquire_memory_handler;
121
122 ResizeMemoryHandler
123 resize_memory_handler;
124
125 DestroyMemoryHandler
126 destroy_memory_handler;
127 } MagickMemoryMethods;
128
129 struct _MemoryInfo
130 {
131 char
132 filename[MagickPathExtent];
133
134 VirtualMemoryType
135 type;
136
137 size_t
138 length;
139
140 void
141 *blob;
142
143 size_t
144 signature;
145 };
146
147 typedef struct _MemoryPool
148 {
149 size_t
150 allocation;
151
152 void
153 *blocks[MaxBlocks+1];
154
155 size_t
156 number_segments;
157
158 DataSegmentInfo
159 *segments[MaxSegments],
160 segment_pool[MaxSegments];
161 } MemoryPool;
162
163 /*
164 Global declarations.
165 */
166 #if defined _MSC_VER
MSCMalloc(size_t size)167 static void* MSCMalloc(size_t size)
168 {
169 return malloc(size);
170 }
MSCRealloc(void * ptr,size_t size)171 static void* MSCRealloc(void* ptr, size_t size)
172 {
173 return realloc(ptr, size);
174 }
MSCFree(void * ptr)175 static void MSCFree(void* ptr)
176 {
177 free(ptr);
178 }
179 #endif
180
181 static MagickMemoryMethods
182 memory_methods =
183 {
184 #if defined _MSC_VER
185 (AcquireMemoryHandler) MSCMalloc,
186 (ResizeMemoryHandler) MSCRealloc,
187 (DestroyMemoryHandler) MSCFree
188 #else
189 (AcquireMemoryHandler) malloc,
190 (ResizeMemoryHandler) realloc,
191 (DestroyMemoryHandler) free
192 #endif
193 };
194 #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
195 static MemoryPool
196 memory_pool;
197
198 static SemaphoreInfo
199 *memory_semaphore = (SemaphoreInfo *) NULL;
200
201 static volatile DataSegmentInfo
202 *free_segments = (DataSegmentInfo *) NULL;
203
204 /*
205 Forward declarations.
206 */
207 static MagickBooleanType
208 ExpandHeap(size_t);
209 #endif
210
211 /*
212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213 % %
214 % %
215 % %
216 % A c q u i r e A l i g n e d M e m o r y %
217 % %
218 % %
219 % %
220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 %
222 % AcquireAlignedMemory() returns a pointer to a block of memory at least size
223 % bytes whose address is a multiple of 16*sizeof(void *).
224 %
225 % The format of the AcquireAlignedMemory method is:
226 %
227 % void *AcquireAlignedMemory(const size_t count,const size_t quantum)
228 %
229 % A description of each parameter follows:
230 %
231 % o count: the number of quantum elements to allocate.
232 %
233 % o quantum: the number of bytes in each quantum.
234 %
235 */
AcquireAlignedMemory(const size_t count,const size_t quantum)236 MagickExport void *AcquireAlignedMemory(const size_t count,const size_t quantum)
237 {
238 #define AlignedExtent(size,alignment) \
239 (((size)+((alignment)-1)) & ~((alignment)-1))
240
241 size_t
242 alignment,
243 extent,
244 size;
245
246 void
247 *memory;
248
249 if (HeapOverflowSanityCheck(count,quantum) != MagickFalse)
250 return((void *) NULL);
251 memory=NULL;
252 alignment=CACHE_LINE_SIZE;
253 size=count*quantum;
254 extent=AlignedExtent(size,alignment);
255 if ((size == 0) || (alignment < sizeof(void *)) || (extent < size))
256 return((void *) NULL);
257 #if defined(MAGICKCORE_HAVE_POSIX_MEMALIGN)
258 if (posix_memalign(&memory,alignment,extent) != 0)
259 memory=NULL;
260 #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC)
261 memory=_aligned_malloc(extent,alignment);
262 #else
263 {
264 void
265 *p;
266
267 extent=(size+alignment-1)+sizeof(void *);
268 if (extent > size)
269 {
270 p=malloc(extent);
271 if (p != NULL)
272 {
273 memory=(void *) AlignedExtent((size_t) p+sizeof(void *),alignment);
274 *((void **) memory-1)=p;
275 }
276 }
277 }
278 #endif
279 return(memory);
280 }
281
282 #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
283 /*
284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 % %
286 % %
287 % %
288 + A c q u i r e B l o c k %
289 % %
290 % %
291 % %
292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
293 %
294 % AcquireBlock() returns a pointer to a block of memory at least size bytes
295 % suitably aligned for any use.
296 %
297 % The format of the AcquireBlock method is:
298 %
299 % void *AcquireBlock(const size_t size)
300 %
301 % A description of each parameter follows:
302 %
303 % o size: the size of the memory in bytes to allocate.
304 %
305 */
306
AllocationPolicy(size_t size)307 static inline size_t AllocationPolicy(size_t size)
308 {
309 register size_t
310 blocksize;
311
312 /*
313 The linear distribution.
314 */
315 assert(size != 0);
316 assert(size % (4*sizeof(size_t)) == 0);
317 if (size <= BlockThreshold)
318 return(size/(4*sizeof(size_t)));
319 /*
320 Check for the largest block size.
321 */
322 if (size > (size_t) (BlockThreshold*(1L << (MaxBlockExponent-1L))))
323 return(MaxBlocks-1L);
324 /*
325 Otherwise use a power of two distribution.
326 */
327 blocksize=BlockThreshold/(4*sizeof(size_t));
328 for ( ; size > BlockThreshold; size/=2)
329 blocksize++;
330 assert(blocksize > (BlockThreshold/(4*sizeof(size_t))));
331 assert(blocksize < (MaxBlocks-1L));
332 return(blocksize);
333 }
334
InsertFreeBlock(void * block,const size_t i)335 static inline void InsertFreeBlock(void *block,const size_t i)
336 {
337 register void
338 *next,
339 *previous;
340
341 size_t
342 size;
343
344 size=SizeOfBlock(block);
345 previous=(void *) NULL;
346 next=memory_pool.blocks[i];
347 while ((next != (void *) NULL) && (SizeOfBlock(next) < size))
348 {
349 previous=next;
350 next=NextBlockInList(next);
351 }
352 PreviousBlockInList(block)=previous;
353 NextBlockInList(block)=next;
354 if (previous != (void *) NULL)
355 NextBlockInList(previous)=block;
356 else
357 memory_pool.blocks[i]=block;
358 if (next != (void *) NULL)
359 PreviousBlockInList(next)=block;
360 }
361
RemoveFreeBlock(void * block,const size_t i)362 static inline void RemoveFreeBlock(void *block,const size_t i)
363 {
364 register void
365 *next,
366 *previous;
367
368 next=NextBlockInList(block);
369 previous=PreviousBlockInList(block);
370 if (previous == (void *) NULL)
371 memory_pool.blocks[i]=next;
372 else
373 NextBlockInList(previous)=next;
374 if (next != (void *) NULL)
375 PreviousBlockInList(next)=previous;
376 }
377
AcquireBlock(size_t size)378 static void *AcquireBlock(size_t size)
379 {
380 register size_t
381 i;
382
383 register void
384 *block;
385
386 /*
387 Find free block.
388 */
389 size=(size_t) (size+sizeof(size_t)+6*sizeof(size_t)-1) & -(4U*sizeof(size_t));
390 i=AllocationPolicy(size);
391 block=memory_pool.blocks[i];
392 while ((block != (void *) NULL) && (SizeOfBlock(block) < size))
393 block=NextBlockInList(block);
394 if (block == (void *) NULL)
395 {
396 i++;
397 while (memory_pool.blocks[i] == (void *) NULL)
398 i++;
399 block=memory_pool.blocks[i];
400 if (i >= MaxBlocks)
401 return((void *) NULL);
402 }
403 assert((*BlockHeader(NextBlock(block)) & PreviousBlockBit) == 0);
404 assert(SizeOfBlock(block) >= size);
405 RemoveFreeBlock(block,AllocationPolicy(SizeOfBlock(block)));
406 if (SizeOfBlock(block) > size)
407 {
408 size_t
409 blocksize;
410
411 void
412 *next;
413
414 /*
415 Split block.
416 */
417 next=(char *) block+size;
418 blocksize=SizeOfBlock(block)-size;
419 *BlockHeader(next)=blocksize;
420 *BlockFooter(next,blocksize)=blocksize;
421 InsertFreeBlock(next,AllocationPolicy(blocksize));
422 *BlockHeader(block)=size | (*BlockHeader(block) & ~SizeMask);
423 }
424 assert(size == SizeOfBlock(block));
425 *BlockHeader(NextBlock(block))|=PreviousBlockBit;
426 memory_pool.allocation+=size;
427 return(block);
428 }
429 #endif
430
431 /*
432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 % %
434 % %
435 % %
436 % A c q u i r e M a g i c k M e m o r y %
437 % %
438 % %
439 % %
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 %
442 % AcquireMagickMemory() returns a pointer to a block of memory at least size
443 % bytes suitably aligned for any use.
444 %
445 % The format of the AcquireMagickMemory method is:
446 %
447 % void *AcquireMagickMemory(const size_t size)
448 %
449 % A description of each parameter follows:
450 %
451 % o size: the size of the memory in bytes to allocate.
452 %
453 */
AcquireMagickMemory(const size_t size)454 MagickExport void *AcquireMagickMemory(const size_t size)
455 {
456 register void
457 *memory;
458
459 #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
460 memory=memory_methods.acquire_memory_handler(size == 0 ? 1UL : size);
461 #else
462 if (memory_semaphore == (SemaphoreInfo *) NULL)
463 ActivateSemaphoreInfo(&memory_semaphore);
464 if (free_segments == (DataSegmentInfo *) NULL)
465 {
466 LockSemaphoreInfo(memory_semaphore);
467 if (free_segments == (DataSegmentInfo *) NULL)
468 {
469 register ssize_t
470 i;
471
472 assert(2*sizeof(size_t) > (size_t) (~SizeMask));
473 (void) ResetMagickMemory(&memory_pool,0,sizeof(memory_pool));
474 memory_pool.allocation=SegmentSize;
475 memory_pool.blocks[MaxBlocks]=(void *) (-1);
476 for (i=0; i < MaxSegments; i++)
477 {
478 if (i != 0)
479 memory_pool.segment_pool[i].previous=
480 (&memory_pool.segment_pool[i-1]);
481 if (i != (MaxSegments-1))
482 memory_pool.segment_pool[i].next=(&memory_pool.segment_pool[i+1]);
483 }
484 free_segments=(&memory_pool.segment_pool[0]);
485 }
486 UnlockSemaphoreInfo(memory_semaphore);
487 }
488 LockSemaphoreInfo(memory_semaphore);
489 memory=AcquireBlock(size == 0 ? 1UL : size);
490 if (memory == (void *) NULL)
491 {
492 if (ExpandHeap(size == 0 ? 1UL : size) != MagickFalse)
493 memory=AcquireBlock(size == 0 ? 1UL : size);
494 }
495 UnlockSemaphoreInfo(memory_semaphore);
496 #endif
497 return(memory);
498 }
499
500 /*
501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
502 % %
503 % %
504 % %
505 % A c q u i r e Q u a n t u m M e m o r y %
506 % %
507 % %
508 % %
509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 %
511 % AcquireQuantumMemory() returns a pointer to a block of memory at least
512 % count * quantum bytes suitably aligned for any use.
513 %
514 % The format of the AcquireQuantumMemory method is:
515 %
516 % void *AcquireQuantumMemory(const size_t count,const size_t quantum)
517 %
518 % A description of each parameter follows:
519 %
520 % o count: the number of quantum elements to allocate.
521 %
522 % o quantum: the number of bytes in each quantum.
523 %
524 */
AcquireQuantumMemory(const size_t count,const size_t quantum)525 MagickExport void *AcquireQuantumMemory(const size_t count,const size_t quantum)
526 {
527 size_t
528 extent;
529
530 if (HeapOverflowSanityCheck(count,quantum) != MagickFalse)
531 return((void *) NULL);
532 extent=count*quantum;
533 return(AcquireMagickMemory(extent));
534 }
535
536 /*
537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538 % %
539 % %
540 % %
541 % A c q u i r e V i r t u a l M e m o r y %
542 % %
543 % %
544 % %
545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546 %
547 % AcquireVirtualMemory() allocates a pointer to a block of memory at least size
548 % bytes suitably aligned for any use.
549 %
550 % The format of the AcquireVirtualMemory method is:
551 %
552 % MemoryInfo *AcquireVirtualMemory(const size_t count,const size_t quantum)
553 %
554 % A description of each parameter follows:
555 %
556 % o count: the number of quantum elements to allocate.
557 %
558 % o quantum: the number of bytes in each quantum.
559 %
560 */
AcquireVirtualMemory(const size_t count,const size_t quantum)561 MagickExport MemoryInfo *AcquireVirtualMemory(const size_t count,
562 const size_t quantum)
563 {
564 MemoryInfo
565 *memory_info;
566
567 size_t
568 extent;
569
570 if (HeapOverflowSanityCheck(count,quantum) != MagickFalse)
571 return((MemoryInfo *) NULL);
572 memory_info=(MemoryInfo *) MagickAssumeAligned(AcquireAlignedMemory(1,
573 sizeof(*memory_info)));
574 if (memory_info == (MemoryInfo *) NULL)
575 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
576 (void) ResetMagickMemory(memory_info,0,sizeof(*memory_info));
577 extent=count*quantum;
578 memory_info->length=extent;
579 memory_info->signature=MagickCoreSignature;
580 if (AcquireMagickResource(MemoryResource,extent) != MagickFalse)
581 {
582 memory_info->blob=AcquireAlignedMemory(1,extent);
583 if (memory_info->blob != NULL)
584 {
585 memory_info->type=AlignedVirtualMemory;
586 return(memory_info);
587 }
588 }
589 RelinquishMagickResource(MemoryResource,extent);
590 if (AcquireMagickResource(MapResource,extent) != MagickFalse)
591 {
592 /*
593 Heap memory failed, try anonymous memory mapping.
594 */
595 memory_info->blob=MapBlob(-1,IOMode,0,extent);
596 if (memory_info->blob != NULL)
597 {
598 memory_info->type=MapVirtualMemory;
599 return(memory_info);
600 }
601 if (AcquireMagickResource(DiskResource,extent) != MagickFalse)
602 {
603 int
604 file;
605
606 /*
607 Anonymous memory mapping failed, try file-backed memory mapping.
608 If the MapResource request failed, there is no point in trying
609 file-backed memory mapping.
610 */
611 file=AcquireUniqueFileResource(memory_info->filename);
612 if (file != -1)
613 {
614 MagickOffsetType
615 offset;
616
617 offset=(MagickOffsetType) lseek(file,extent-1,SEEK_SET);
618 if ((offset == (MagickOffsetType) (extent-1)) &&
619 (write(file,"",1) == 1))
620 {
621 memory_info->blob=MapBlob(file,IOMode,0,extent);
622 if (memory_info->blob != NULL)
623 {
624 (void) close(file);
625 memory_info->type=MapVirtualMemory;
626 return(memory_info);
627 }
628 }
629 /*
630 File-backed memory mapping failed, delete the temporary file.
631 */
632 (void) close(file);
633 (void) RelinquishUniqueFileResource(memory_info->filename);
634 *memory_info->filename = '\0';
635 }
636 }
637 RelinquishMagickResource(DiskResource,extent);
638 }
639 RelinquishMagickResource(MapResource,extent);
640 if (memory_info->blob == NULL)
641 {
642 memory_info->blob=AcquireMagickMemory(extent);
643 if (memory_info->blob != NULL)
644 memory_info->type=UnalignedVirtualMemory;
645 }
646 if (memory_info->blob == NULL)
647 memory_info=RelinquishVirtualMemory(memory_info);
648 return(memory_info);
649 }
650
651 /*
652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653 % %
654 % %
655 % %
656 % C o p y M a g i c k M e m o r y %
657 % %
658 % %
659 % %
660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 %
662 % CopyMagickMemory() copies size bytes from memory area source to the
663 % destination. Copying between objects that overlap will take place
664 % correctly. It returns destination.
665 %
666 % The format of the CopyMagickMemory method is:
667 %
668 % void *CopyMagickMemory(void *destination,const void *source,
669 % const size_t size)
670 %
671 % A description of each parameter follows:
672 %
673 % o destination: the destination.
674 %
675 % o source: the source.
676 %
677 % o size: the size of the memory in bytes to allocate.
678 %
679 */
CopyMagickMemory(void * destination,const void * source,const size_t size)680 MagickExport void *CopyMagickMemory(void *destination,const void *source,
681 const size_t size)
682 {
683 register const unsigned char
684 *p;
685
686 register unsigned char
687 *q;
688
689 assert(destination != (void *) NULL);
690 assert(source != (const void *) NULL);
691 p=(const unsigned char *) source;
692 q=(unsigned char *) destination;
693 if (((q+size) < p) || (q > (p+size)))
694 switch (size)
695 {
696 default: return(memcpy(destination,source,size));
697 case 8: *q++=(*p++);
698 case 7: *q++=(*p++);
699 case 6: *q++=(*p++);
700 case 5: *q++=(*p++);
701 case 4: *q++=(*p++);
702 case 3: *q++=(*p++);
703 case 2: *q++=(*p++);
704 case 1: *q++=(*p++);
705 case 0: return(destination);
706 }
707 return(memmove(destination,source,size));
708 }
709
710 /*
711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
712 % %
713 % %
714 % %
715 + D e s t r o y M a g i c k M e m o r y %
716 % %
717 % %
718 % %
719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
720 %
721 % DestroyMagickMemory() deallocates memory associated with the memory manager.
722 %
723 % The format of the DestroyMagickMemory method is:
724 %
725 % DestroyMagickMemory(void)
726 %
727 */
DestroyMagickMemory(void)728 MagickExport void DestroyMagickMemory(void)
729 {
730 #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
731 register ssize_t
732 i;
733
734 if (memory_semaphore == (SemaphoreInfo *) NULL)
735 ActivateSemaphoreInfo(&memory_semaphore);
736 LockSemaphoreInfo(memory_semaphore);
737 for (i=0; i < (ssize_t) memory_pool.number_segments; i++)
738 if (memory_pool.segments[i]->mapped == MagickFalse)
739 memory_methods.destroy_memory_handler(
740 memory_pool.segments[i]->allocation);
741 else
742 (void) UnmapBlob(memory_pool.segments[i]->allocation,
743 memory_pool.segments[i]->length);
744 free_segments=(DataSegmentInfo *) NULL;
745 (void) ResetMagickMemory(&memory_pool,0,sizeof(memory_pool));
746 UnlockSemaphoreInfo(memory_semaphore);
747 RelinquishSemaphoreInfo(&memory_semaphore);
748 #endif
749 }
750
751 #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
752 /*
753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754 % %
755 % %
756 % %
757 + E x p a n d H e a p %
758 % %
759 % %
760 % %
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 %
763 % ExpandHeap() get more memory from the system. It returns MagickTrue on
764 % success otherwise MagickFalse.
765 %
766 % The format of the ExpandHeap method is:
767 %
768 % MagickBooleanType ExpandHeap(size_t size)
769 %
770 % A description of each parameter follows:
771 %
772 % o size: the size of the memory in bytes we require.
773 %
774 */
ExpandHeap(size_t size)775 static MagickBooleanType ExpandHeap(size_t size)
776 {
777 DataSegmentInfo
778 *segment_info;
779
780 MagickBooleanType
781 mapped;
782
783 register ssize_t
784 i;
785
786 register void
787 *block;
788
789 size_t
790 blocksize;
791
792 void
793 *segment;
794
795 blocksize=((size+12*sizeof(size_t))+SegmentSize-1) & -SegmentSize;
796 assert(memory_pool.number_segments < MaxSegments);
797 segment=MapBlob(-1,IOMode,0,blocksize);
798 mapped=segment != (void *) NULL ? MagickTrue : MagickFalse;
799 if (segment == (void *) NULL)
800 segment=(void *) memory_methods.acquire_memory_handler(blocksize);
801 if (segment == (void *) NULL)
802 return(MagickFalse);
803 segment_info=(DataSegmentInfo *) free_segments;
804 free_segments=segment_info->next;
805 segment_info->mapped=mapped;
806 segment_info->length=blocksize;
807 segment_info->allocation=segment;
808 segment_info->bound=(char *) segment+blocksize;
809 i=(ssize_t) memory_pool.number_segments-1;
810 for ( ; (i >= 0) && (memory_pool.segments[i]->allocation > segment); i--)
811 memory_pool.segments[i+1]=memory_pool.segments[i];
812 memory_pool.segments[i+1]=segment_info;
813 memory_pool.number_segments++;
814 size=blocksize-12*sizeof(size_t);
815 block=(char *) segment_info->allocation+4*sizeof(size_t);
816 *BlockHeader(block)=size | PreviousBlockBit;
817 *BlockFooter(block,size)=size;
818 InsertFreeBlock(block,AllocationPolicy(size));
819 block=NextBlock(block);
820 assert(block < segment_info->bound);
821 *BlockHeader(block)=2*sizeof(size_t);
822 *BlockHeader(NextBlock(block))=PreviousBlockBit;
823 return(MagickTrue);
824 }
825 #endif
826
827 /*
828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829 % %
830 % %
831 % %
832 % G e t M a g i c k M e m o r y M e t h o d s %
833 % %
834 % %
835 % %
836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837 %
838 % GetMagickMemoryMethods() gets the methods to acquire, resize, and destroy
839 % memory.
840 %
841 % The format of the GetMagickMemoryMethods() method is:
842 %
843 % void GetMagickMemoryMethods(AcquireMemoryHandler *acquire_memory_handler,
844 % ResizeMemoryHandler *resize_memory_handler,
845 % DestroyMemoryHandler *destroy_memory_handler)
846 %
847 % A description of each parameter follows:
848 %
849 % o acquire_memory_handler: method to acquire memory (e.g. malloc).
850 %
851 % o resize_memory_handler: method to resize memory (e.g. realloc).
852 %
853 % o destroy_memory_handler: method to destroy memory (e.g. free).
854 %
855 */
GetMagickMemoryMethods(AcquireMemoryHandler * acquire_memory_handler,ResizeMemoryHandler * resize_memory_handler,DestroyMemoryHandler * destroy_memory_handler)856 MagickExport void GetMagickMemoryMethods(
857 AcquireMemoryHandler *acquire_memory_handler,
858 ResizeMemoryHandler *resize_memory_handler,
859 DestroyMemoryHandler *destroy_memory_handler)
860 {
861 assert(acquire_memory_handler != (AcquireMemoryHandler *) NULL);
862 assert(resize_memory_handler != (ResizeMemoryHandler *) NULL);
863 assert(destroy_memory_handler != (DestroyMemoryHandler *) NULL);
864 *acquire_memory_handler=memory_methods.acquire_memory_handler;
865 *resize_memory_handler=memory_methods.resize_memory_handler;
866 *destroy_memory_handler=memory_methods.destroy_memory_handler;
867 }
868
869 /*
870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
871 % %
872 % %
873 % %
874 % G e t V i r t u a l M e m o r y B l o b %
875 % %
876 % %
877 % %
878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 %
880 % GetVirtualMemoryBlob() returns the virtual memory blob associated with the
881 % specified MemoryInfo structure.
882 %
883 % The format of the GetVirtualMemoryBlob method is:
884 %
885 % void *GetVirtualMemoryBlob(const MemoryInfo *memory_info)
886 %
887 % A description of each parameter follows:
888 %
889 % o memory_info: The MemoryInfo structure.
890 */
GetVirtualMemoryBlob(const MemoryInfo * memory_info)891 MagickExport void *GetVirtualMemoryBlob(const MemoryInfo *memory_info)
892 {
893 assert(memory_info != (const MemoryInfo *) NULL);
894 assert(memory_info->signature == MagickCoreSignature);
895 return(memory_info->blob);
896 }
897
898 /*
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % %
901 % %
902 % %
903 + H e a p O v e r f l o w S a n i t y C h e c k %
904 % %
905 % %
906 % %
907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
908 %
909 % HeapOverflowSanityCheck() returns MagickTrue if the heap allocation request
910 % does not exceed the maximum limits of a size_t otherwise MagickFalse.
911 %
912 % The format of the HeapOverflowSanityCheck method is:
913 %
914 % MagickBooleanType HeapOverflowSanityCheck(const size_t count,
915 % const size_t quantum)
916 %
917 % A description of each parameter follows:
918 %
919 % o size: the size of the memory in bytes we require.
920 %
921 */
HeapOverflowSanityCheck(const size_t count,const size_t quantum)922 MagickExport MagickBooleanType HeapOverflowSanityCheck(const size_t count,
923 const size_t quantum)
924 {
925 size_t
926 size;
927
928 size=count*quantum;
929 if ((count == 0) || (quantum != (size/count)))
930 {
931 errno=ENOMEM;
932 return(MagickTrue);
933 }
934 return(MagickFalse);
935 }
936
937 /*
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 % %
940 % %
941 % %
942 % R e l i n q u i s h A l i g n e d M e m o r y %
943 % %
944 % %
945 % %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %
948 % RelinquishAlignedMemory() frees memory acquired with AcquireAlignedMemory()
949 % or reuse.
950 %
951 % The format of the RelinquishAlignedMemory method is:
952 %
953 % void *RelinquishAlignedMemory(void *memory)
954 %
955 % A description of each parameter follows:
956 %
957 % o memory: A pointer to a block of memory to free for reuse.
958 %
959 */
RelinquishAlignedMemory(void * memory)960 MagickExport void *RelinquishAlignedMemory(void *memory)
961 {
962 if (memory == (void *) NULL)
963 return((void *) NULL);
964 #if defined(MAGICKCORE_HAVE_POSIX_MEMALIGN)
965 free(memory);
966 #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC)
967 _aligned_free(memory);
968 #else
969 free(*((void **) memory-1));
970 #endif
971 return(NULL);
972 }
973
974 /*
975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
976 % %
977 % %
978 % %
979 % R e l i n q u i s h M a g i c k M e m o r y %
980 % %
981 % %
982 % %
983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 %
985 % RelinquishMagickMemory() frees memory acquired with AcquireMagickMemory()
986 % or AcquireQuantumMemory() for reuse.
987 %
988 % The format of the RelinquishMagickMemory method is:
989 %
990 % void *RelinquishMagickMemory(void *memory)
991 %
992 % A description of each parameter follows:
993 %
994 % o memory: A pointer to a block of memory to free for reuse.
995 %
996 */
RelinquishMagickMemory(void * memory)997 MagickExport void *RelinquishMagickMemory(void *memory)
998 {
999 if (memory == (void *) NULL)
1000 return((void *) NULL);
1001 #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
1002 memory_methods.destroy_memory_handler(memory);
1003 #else
1004 LockSemaphoreInfo(memory_semaphore);
1005 assert((SizeOfBlock(memory) % (4*sizeof(size_t))) == 0);
1006 assert((*BlockHeader(NextBlock(memory)) & PreviousBlockBit) != 0);
1007 if ((*BlockHeader(memory) & PreviousBlockBit) == 0)
1008 {
1009 void
1010 *previous;
1011
1012 /*
1013 Coalesce with previous adjacent block.
1014 */
1015 previous=PreviousBlock(memory);
1016 RemoveFreeBlock(previous,AllocationPolicy(SizeOfBlock(previous)));
1017 *BlockHeader(previous)=(SizeOfBlock(previous)+SizeOfBlock(memory)) |
1018 (*BlockHeader(previous) & ~SizeMask);
1019 memory=previous;
1020 }
1021 if ((*BlockHeader(NextBlock(NextBlock(memory))) & PreviousBlockBit) == 0)
1022 {
1023 void
1024 *next;
1025
1026 /*
1027 Coalesce with next adjacent block.
1028 */
1029 next=NextBlock(memory);
1030 RemoveFreeBlock(next,AllocationPolicy(SizeOfBlock(next)));
1031 *BlockHeader(memory)=(SizeOfBlock(memory)+SizeOfBlock(next)) |
1032 (*BlockHeader(memory) & ~SizeMask);
1033 }
1034 *BlockFooter(memory,SizeOfBlock(memory))=SizeOfBlock(memory);
1035 *BlockHeader(NextBlock(memory))&=(~PreviousBlockBit);
1036 InsertFreeBlock(memory,AllocationPolicy(SizeOfBlock(memory)));
1037 UnlockSemaphoreInfo(memory_semaphore);
1038 #endif
1039 return((void *) NULL);
1040 }
1041
1042 /*
1043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044 % %
1045 % %
1046 % %
1047 % R e l i n q u i s h V i r t u a l M e m o r y %
1048 % %
1049 % %
1050 % %
1051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1052 %
1053 % RelinquishVirtualMemory() frees memory acquired with AcquireVirtualMemory().
1054 %
1055 % The format of the RelinquishVirtualMemory method is:
1056 %
1057 % MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info)
1058 %
1059 % A description of each parameter follows:
1060 %
1061 % o memory_info: A pointer to a block of memory to free for reuse.
1062 %
1063 */
RelinquishVirtualMemory(MemoryInfo * memory_info)1064 MagickExport MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info)
1065 {
1066 assert(memory_info != (MemoryInfo *) NULL);
1067 assert(memory_info->signature == MagickCoreSignature);
1068 if (memory_info->blob != (void *) NULL)
1069 switch (memory_info->type)
1070 {
1071 case AlignedVirtualMemory:
1072 {
1073 memory_info->blob=RelinquishAlignedMemory(memory_info->blob);
1074 RelinquishMagickResource(MemoryResource,memory_info->length);
1075 break;
1076 }
1077 case MapVirtualMemory:
1078 {
1079 (void) UnmapBlob(memory_info->blob,memory_info->length);
1080 memory_info->blob=NULL;
1081 RelinquishMagickResource(MapResource,memory_info->length);
1082 if (*memory_info->filename != '\0')
1083 {
1084 (void) RelinquishUniqueFileResource(memory_info->filename);
1085 RelinquishMagickResource(DiskResource,memory_info->length);
1086 }
1087 break;
1088 }
1089 case UnalignedVirtualMemory:
1090 default:
1091 {
1092 memory_info->blob=RelinquishMagickMemory(memory_info->blob);
1093 break;
1094 }
1095 }
1096 memory_info->signature=(~MagickCoreSignature);
1097 memory_info=(MemoryInfo *) RelinquishAlignedMemory(memory_info);
1098 return(memory_info);
1099 }
1100
1101 /*
1102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 % %
1104 % %
1105 % %
1106 % R e s e t M a g i c k M e m o r y %
1107 % %
1108 % %
1109 % %
1110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1111 %
1112 % ResetMagickMemory() fills the first size bytes of the memory area pointed to
1113 % by memory with the constant byte c.
1114 %
1115 % The format of the ResetMagickMemory method is:
1116 %
1117 % void *ResetMagickMemory(void *memory,int byte,const size_t size)
1118 %
1119 % A description of each parameter follows:
1120 %
1121 % o memory: a pointer to a memory allocation.
1122 %
1123 % o byte: set the memory to this value.
1124 %
1125 % o size: size of the memory to reset.
1126 %
1127 */
ResetMagickMemory(void * memory,int byte,const size_t size)1128 MagickExport void *ResetMagickMemory(void *memory,int byte,const size_t size)
1129 {
1130 assert(memory != (void *) NULL);
1131 return(memset(memory,byte,size));
1132 }
1133
1134 /*
1135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1136 % %
1137 % %
1138 % %
1139 % R e s i z e M a g i c k M e m o r y %
1140 % %
1141 % %
1142 % %
1143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1144 %
1145 % ResizeMagickMemory() changes the size of the memory and returns a pointer to
1146 % the (possibly moved) block. The contents will be unchanged up to the
1147 % lesser of the new and old sizes.
1148 %
1149 % The format of the ResizeMagickMemory method is:
1150 %
1151 % void *ResizeMagickMemory(void *memory,const size_t size)
1152 %
1153 % A description of each parameter follows:
1154 %
1155 % o memory: A pointer to a memory allocation.
1156 %
1157 % o size: the new size of the allocated memory.
1158 %
1159 */
1160
1161 #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
ResizeBlock(void * block,size_t size)1162 static inline void *ResizeBlock(void *block,size_t size)
1163 {
1164 register void
1165 *memory;
1166
1167 if (block == (void *) NULL)
1168 return(AcquireBlock(size));
1169 memory=AcquireBlock(size);
1170 if (memory == (void *) NULL)
1171 return((void *) NULL);
1172 if (size <= (SizeOfBlock(block)-sizeof(size_t)))
1173 (void) memcpy(memory,block,size);
1174 else
1175 (void) memcpy(memory,block,SizeOfBlock(block)-sizeof(size_t));
1176 memory_pool.allocation+=size;
1177 return(memory);
1178 }
1179 #endif
1180
ResizeMagickMemory(void * memory,const size_t size)1181 MagickExport void *ResizeMagickMemory(void *memory,const size_t size)
1182 {
1183 register void
1184 *block;
1185
1186 if (memory == (void *) NULL)
1187 return(AcquireMagickMemory(size));
1188 #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT)
1189 block=memory_methods.resize_memory_handler(memory,size == 0 ? 1UL : size);
1190 if (block == (void *) NULL)
1191 memory=RelinquishMagickMemory(memory);
1192 #else
1193 LockSemaphoreInfo(memory_semaphore);
1194 block=ResizeBlock(memory,size == 0 ? 1UL : size);
1195 if (block == (void *) NULL)
1196 {
1197 if (ExpandHeap(size == 0 ? 1UL : size) == MagickFalse)
1198 {
1199 UnlockSemaphoreInfo(memory_semaphore);
1200 memory=RelinquishMagickMemory(memory);
1201 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1202 }
1203 block=ResizeBlock(memory,size == 0 ? 1UL : size);
1204 assert(block != (void *) NULL);
1205 }
1206 UnlockSemaphoreInfo(memory_semaphore);
1207 memory=RelinquishMagickMemory(memory);
1208 #endif
1209 return(block);
1210 }
1211
1212 /*
1213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 % %
1215 % %
1216 % %
1217 % R e s i z e Q u a n t u m M e m o r y %
1218 % %
1219 % %
1220 % %
1221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222 %
1223 % ResizeQuantumMemory() changes the size of the memory and returns a pointer
1224 % to the (possibly moved) block. The contents will be unchanged up to the
1225 % lesser of the new and old sizes.
1226 %
1227 % The format of the ResizeQuantumMemory method is:
1228 %
1229 % void *ResizeQuantumMemory(void *memory,const size_t count,
1230 % const size_t quantum)
1231 %
1232 % A description of each parameter follows:
1233 %
1234 % o memory: A pointer to a memory allocation.
1235 %
1236 % o count: the number of quantum elements to allocate.
1237 %
1238 % o quantum: the number of bytes in each quantum.
1239 %
1240 */
ResizeQuantumMemory(void * memory,const size_t count,const size_t quantum)1241 MagickExport void *ResizeQuantumMemory(void *memory,const size_t count,
1242 const size_t quantum)
1243 {
1244 size_t
1245 extent;
1246
1247 if (HeapOverflowSanityCheck(count,quantum) != MagickFalse)
1248 {
1249 memory=RelinquishMagickMemory(memory);
1250 return((void *) NULL);
1251 }
1252 extent=count*quantum;
1253 return(ResizeMagickMemory(memory,extent));
1254 }
1255
1256 /*
1257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258 % %
1259 % %
1260 % %
1261 % S e t M a g i c k M e m o r y M e t h o d s %
1262 % %
1263 % %
1264 % %
1265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1266 %
1267 % SetMagickMemoryMethods() sets the methods to acquire, resize, and destroy
1268 % memory. Your custom memory methods must be set prior to the
1269 % MagickCoreGenesis() method.
1270 %
1271 % The format of the SetMagickMemoryMethods() method is:
1272 %
1273 % SetMagickMemoryMethods(AcquireMemoryHandler acquire_memory_handler,
1274 % ResizeMemoryHandler resize_memory_handler,
1275 % DestroyMemoryHandler destroy_memory_handler)
1276 %
1277 % A description of each parameter follows:
1278 %
1279 % o acquire_memory_handler: method to acquire memory (e.g. malloc).
1280 %
1281 % o resize_memory_handler: method to resize memory (e.g. realloc).
1282 %
1283 % o destroy_memory_handler: method to destroy memory (e.g. free).
1284 %
1285 */
SetMagickMemoryMethods(AcquireMemoryHandler acquire_memory_handler,ResizeMemoryHandler resize_memory_handler,DestroyMemoryHandler destroy_memory_handler)1286 MagickExport void SetMagickMemoryMethods(
1287 AcquireMemoryHandler acquire_memory_handler,
1288 ResizeMemoryHandler resize_memory_handler,
1289 DestroyMemoryHandler destroy_memory_handler)
1290 {
1291 /*
1292 Set memory methods.
1293 */
1294 if (acquire_memory_handler != (AcquireMemoryHandler) NULL)
1295 memory_methods.acquire_memory_handler=acquire_memory_handler;
1296 if (resize_memory_handler != (ResizeMemoryHandler) NULL)
1297 memory_methods.resize_memory_handler=resize_memory_handler;
1298 if (destroy_memory_handler != (DestroyMemoryHandler) NULL)
1299 memory_methods.destroy_memory_handler=destroy_memory_handler;
1300 }
1301