]> granicus.if.org Git - imagemagick/blob - MagickCore/memory.c
https://github.com/ImageMagick/ImageMagick/issues/82
[imagemagick] / MagickCore / memory.c
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_ZERO_CONFIGURATION_SUPPORT
48 %  to allocate memory with private anonymous mapping rather than from the
49 %  heap.
50 %
51 */
52 \f
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 \f
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 \f
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 \f
163 /*
164   Global declarations.
165 */
166 #if defined _MSC_VER
167 static void* MSCMalloc(size_t size)
168 {
169   return malloc(size);
170 }
171 static void* MSCRealloc(void* ptr, size_t size)
172 {
173   return realloc(ptr, size);
174 }
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_ZERO_CONFIGURATION_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 \f
204 /*
205   Forward declarations.
206 */
207 static MagickBooleanType
208   ExpandHeap(size_t);
209 #endif
210 \f
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 */
236 static MagickBooleanType CheckMemoryOverflow(const size_t count,
237   const size_t quantum)
238 {
239   size_t
240     size;
241
242   size=count*quantum;
243   if ((count == 0) || (quantum != (size/count)))
244     {
245       errno=ENOMEM;
246       return(MagickTrue);
247     }
248   return(MagickFalse);
249 }
250
251 MagickExport void *AcquireAlignedMemory(const size_t count,const size_t quantum)
252 {
253 #define AlignedExtent(size,alignment) \
254   (((size)+((alignment)-1)) & ~((alignment)-1))
255
256   size_t
257     alignment,
258     extent,
259     size;
260
261   void
262     *memory;
263
264   if (CheckMemoryOverflow(count,quantum) != MagickFalse)
265     return((void *) NULL);
266   memory=NULL;
267   alignment=CACHE_LINE_SIZE;
268   size=count*quantum;
269   extent=AlignedExtent(size,alignment);
270   if ((size == 0) || (alignment < sizeof(void *)) || (extent < size))
271     return((void *) NULL);
272 #if defined(MAGICKCORE_HAVE_POSIX_MEMALIGN)
273   if (posix_memalign(&memory,alignment,extent) != 0)
274     memory=NULL;
275 #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC)
276   memory=_aligned_malloc(extent,alignment);
277 #else
278   {
279     void
280       *p;
281
282     extent=(size+alignment-1)+sizeof(void *);
283     if (extent > size)
284       {
285         p=malloc(extent);
286         if (p != NULL)
287           {
288             memory=(void *) AlignedExtent((size_t) p+sizeof(void *),alignment);
289             *((void **) memory-1)=p;
290           }
291       }
292   }
293 #endif
294   return(memory);
295 }
296 \f
297 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
298 /*
299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300 %                                                                             %
301 %                                                                             %
302 %                                                                             %
303 +   A c q u i r e B l o c k                                                   %
304 %                                                                             %
305 %                                                                             %
306 %                                                                             %
307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308 %
309 %  AcquireBlock() returns a pointer to a block of memory at least size bytes
310 %  suitably aligned for any use.
311 %
312 %  The format of the AcquireBlock method is:
313 %
314 %      void *AcquireBlock(const size_t size)
315 %
316 %  A description of each parameter follows:
317 %
318 %    o size: the size of the memory in bytes to allocate.
319 %
320 */
321
322 static inline size_t AllocationPolicy(size_t size)
323 {
324   register size_t
325     blocksize;
326
327   /*
328     The linear distribution.
329   */
330   assert(size != 0);
331   assert(size % (4*sizeof(size_t)) == 0);
332   if (size <= BlockThreshold)
333     return(size/(4*sizeof(size_t)));
334   /*
335     Check for the largest block size.
336   */
337   if (size > (size_t) (BlockThreshold*(1L << (MaxBlockExponent-1L))))
338     return(MaxBlocks-1L);
339   /*
340     Otherwise use a power of two distribution.
341   */
342   blocksize=BlockThreshold/(4*sizeof(size_t));
343   for ( ; size > BlockThreshold; size/=2)
344     blocksize++;
345   assert(blocksize > (BlockThreshold/(4*sizeof(size_t))));
346   assert(blocksize < (MaxBlocks-1L));
347   return(blocksize);
348 }
349
350 static inline void InsertFreeBlock(void *block,const size_t i)
351 {
352   register void
353     *next,
354     *previous;
355
356   size_t
357     size;
358
359   size=SizeOfBlock(block);
360   previous=(void *) NULL;
361   next=memory_pool.blocks[i];
362   while ((next != (void *) NULL) && (SizeOfBlock(next) < size))
363   {
364     previous=next;
365     next=NextBlockInList(next);
366   }
367   PreviousBlockInList(block)=previous;
368   NextBlockInList(block)=next;
369   if (previous != (void *) NULL)
370     NextBlockInList(previous)=block;
371   else
372     memory_pool.blocks[i]=block;
373   if (next != (void *) NULL)
374     PreviousBlockInList(next)=block;
375 }
376
377 static inline void RemoveFreeBlock(void *block,const size_t i)
378 {
379   register void
380     *next,
381     *previous;
382
383   next=NextBlockInList(block);
384   previous=PreviousBlockInList(block);
385   if (previous == (void *) NULL)
386     memory_pool.blocks[i]=next;
387   else
388     NextBlockInList(previous)=next;
389   if (next != (void *) NULL)
390     PreviousBlockInList(next)=previous;
391 }
392
393 static void *AcquireBlock(size_t size)
394 {
395   register size_t
396     i;
397
398   register void
399     *block;
400
401   /*
402     Find free block.
403   */
404   size=(size_t) (size+sizeof(size_t)+6*sizeof(size_t)-1) & -(4U*sizeof(size_t));
405   i=AllocationPolicy(size);
406   block=memory_pool.blocks[i];
407   while ((block != (void *) NULL) && (SizeOfBlock(block) < size))
408     block=NextBlockInList(block);
409   if (block == (void *) NULL)
410     {
411       i++;
412       while (memory_pool.blocks[i] == (void *) NULL)
413         i++;
414       block=memory_pool.blocks[i];
415       if (i >= MaxBlocks)
416         return((void *) NULL);
417     }
418   assert((*BlockHeader(NextBlock(block)) & PreviousBlockBit) == 0);
419   assert(SizeOfBlock(block) >= size);
420   RemoveFreeBlock(block,AllocationPolicy(SizeOfBlock(block)));
421   if (SizeOfBlock(block) > size)
422     {
423       size_t
424         blocksize;
425
426       void
427         *next;
428
429       /*
430         Split block.
431       */
432       next=(char *) block+size;
433       blocksize=SizeOfBlock(block)-size;
434       *BlockHeader(next)=blocksize;
435       *BlockFooter(next,blocksize)=blocksize;
436       InsertFreeBlock(next,AllocationPolicy(blocksize));
437       *BlockHeader(block)=size | (*BlockHeader(block) & ~SizeMask);
438     }
439   assert(size == SizeOfBlock(block));
440   *BlockHeader(NextBlock(block))|=PreviousBlockBit;
441   memory_pool.allocation+=size;
442   return(block);
443 }
444 #endif
445 \f
446 /*
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 %                                                                             %
449 %                                                                             %
450 %                                                                             %
451 %   A c q u i r e M a g i c k M e m o r y                                     %
452 %                                                                             %
453 %                                                                             %
454 %                                                                             %
455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456 %
457 %  AcquireMagickMemory() returns a pointer to a block of memory at least size
458 %  bytes suitably aligned for any use.
459 %
460 %  The format of the AcquireMagickMemory method is:
461 %
462 %      void *AcquireMagickMemory(const size_t size)
463 %
464 %  A description of each parameter follows:
465 %
466 %    o size: the size of the memory in bytes to allocate.
467 %
468 */
469 MagickExport void *AcquireMagickMemory(const size_t size)
470 {
471   register void
472     *memory;
473
474 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
475   memory=memory_methods.acquire_memory_handler(size == 0 ? 1UL : size);
476 #else
477   if (memory_semaphore == (SemaphoreInfo *) NULL)
478     ActivateSemaphoreInfo(&memory_semaphore);
479   if (free_segments == (DataSegmentInfo *) NULL)
480     {
481       LockSemaphoreInfo(memory_semaphore);
482       if (free_segments == (DataSegmentInfo *) NULL)
483         {
484           register ssize_t
485             i;
486
487           assert(2*sizeof(size_t) > (size_t) (~SizeMask));
488           (void) ResetMagickMemory(&memory_pool,0,sizeof(memory_pool));
489           memory_pool.allocation=SegmentSize;
490           memory_pool.blocks[MaxBlocks]=(void *) (-1);
491           for (i=0; i < MaxSegments; i++)
492           {
493             if (i != 0)
494               memory_pool.segment_pool[i].previous=
495                 (&memory_pool.segment_pool[i-1]);
496             if (i != (MaxSegments-1))
497               memory_pool.segment_pool[i].next=(&memory_pool.segment_pool[i+1]);
498           }
499           free_segments=(&memory_pool.segment_pool[0]);
500         }
501       UnlockSemaphoreInfo(memory_semaphore);
502     }
503   LockSemaphoreInfo(memory_semaphore);
504   memory=AcquireBlock(size == 0 ? 1UL : size);
505   if (memory == (void *) NULL)
506     {
507       if (ExpandHeap(size == 0 ? 1UL : size) != MagickFalse)
508         memory=AcquireBlock(size == 0 ? 1UL : size);
509     }
510   UnlockSemaphoreInfo(memory_semaphore);
511 #endif
512   return(memory);
513 }
514 \f
515 /*
516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 %                                                                             %
518 %                                                                             %
519 %                                                                             %
520 %   A c q u i r e Q u a n t u m M e m o r y                                   %
521 %                                                                             %
522 %                                                                             %
523 %                                                                             %
524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525 %
526 %  AcquireQuantumMemory() returns a pointer to a block of memory at least
527 %  count * quantum bytes suitably aligned for any use.
528 %
529 %  The format of the AcquireQuantumMemory method is:
530 %
531 %      void *AcquireQuantumMemory(const size_t count,const size_t quantum)
532 %
533 %  A description of each parameter follows:
534 %
535 %    o count: the number of quantum elements to allocate.
536 %
537 %    o quantum: the number of bytes in each quantum.
538 %
539 */
540 MagickExport void *AcquireQuantumMemory(const size_t count,const size_t quantum)
541 {
542   size_t
543     extent;
544
545   if (CheckMemoryOverflow(count,quantum) != MagickFalse)
546     return((void *) NULL);
547   extent=count*quantum;
548   return(AcquireMagickMemory(extent));
549 }
550 \f
551 /*
552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553 %                                                                             %
554 %                                                                             %
555 %                                                                             %
556 %   A c q u i r e V i r t u a l M e m o r y                                   %
557 %                                                                             %
558 %                                                                             %
559 %                                                                             %
560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561 %
562 %  AcquireVirtualMemory() allocates a pointer to a block of memory at least size
563 %  bytes suitably aligned for any use.
564 %
565 %  The format of the AcquireVirtualMemory method is:
566 %
567 %      MemoryInfo *AcquireVirtualMemory(const size_t count,const size_t quantum)
568 %
569 %  A description of each parameter follows:
570 %
571 %    o count: the number of quantum elements to allocate.
572 %
573 %    o quantum: the number of bytes in each quantum.
574 %
575 */
576 MagickExport MemoryInfo *AcquireVirtualMemory(const size_t count,
577   const size_t quantum)
578 {
579   MemoryInfo
580     *memory_info;
581
582   size_t
583     extent;
584
585   if (CheckMemoryOverflow(count,quantum) != MagickFalse)
586     return((MemoryInfo *) NULL);
587   memory_info=(MemoryInfo *) MagickAssumeAligned(AcquireAlignedMemory(1,
588     sizeof(*memory_info)));
589   if (memory_info == (MemoryInfo *) NULL)
590     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
591   (void) ResetMagickMemory(memory_info,0,sizeof(*memory_info));
592   extent=count*quantum;
593   memory_info->length=extent;
594   memory_info->signature=MagickCoreSignature;
595   if (AcquireMagickResource(MemoryResource,extent) != MagickFalse)
596     {
597       memory_info->blob=AcquireAlignedMemory(1,extent);
598       if (memory_info->blob != NULL)
599         {
600           memory_info->type=AlignedVirtualMemory;
601           return(memory_info);
602         }
603     }
604   RelinquishMagickResource(MemoryResource,extent);
605   if (AcquireMagickResource(MapResource,extent) != MagickFalse)
606     {
607       /*
608         Heap memory failed, try anonymous memory mapping.
609       */
610       memory_info->blob=MapBlob(-1,IOMode,0,extent);
611       if (memory_info->blob != NULL)
612         {
613           memory_info->type=MapVirtualMemory;
614           return(memory_info);
615         }
616       if (AcquireMagickResource(DiskResource,extent) != MagickFalse)
617         {
618           int
619             file;
620
621           /*
622             Anonymous memory mapping failed, try file-backed memory mapping.
623             If the MapResource request failed, there is no point in trying
624             file-backed memory mapping.
625           */
626           file=AcquireUniqueFileResource(memory_info->filename);
627           if (file != -1)
628             {
629               if ((lseek(file,extent-1,SEEK_SET) == (extent-1)) &&
630                   (write(file,"",1) == 1))
631                 {
632                   memory_info->blob=MapBlob(file,IOMode,0,extent);
633                   if (memory_info->blob != NULL)
634                     {
635                       (void) close(file);
636                       memory_info->type=MapVirtualMemory;
637                       return(memory_info);
638                     }
639                 }
640               /*
641                 File-backed memory mapping failed, delete the temporary file.
642               */
643               (void) close(file);
644               (void) RelinquishUniqueFileResource(memory_info->filename);
645               *memory_info->filename = '\0';
646             }
647         }
648       RelinquishMagickResource(DiskResource,extent);
649     }
650   RelinquishMagickResource(MapResource,extent);
651   if (memory_info->blob == NULL)
652     {
653       memory_info->blob=AcquireMagickMemory(extent);
654       if (memory_info->blob != NULL)
655         memory_info->type=UnalignedVirtualMemory;
656     }
657   if (memory_info->blob == NULL)
658     memory_info=RelinquishVirtualMemory(memory_info);
659   return(memory_info);
660 }
661 \f
662 /*
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664 %                                                                             %
665 %                                                                             %
666 %                                                                             %
667 %   C o p y M a g i c k M e m o r y                                           %
668 %                                                                             %
669 %                                                                             %
670 %                                                                             %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 %
673 %  CopyMagickMemory() copies size bytes from memory area source to the
674 %  destination.  Copying between objects that overlap will take place
675 %  correctly.  It returns destination.
676 %
677 %  The format of the CopyMagickMemory method is:
678 %
679 %      void *CopyMagickMemory(void *destination,const void *source,
680 %        const size_t size)
681 %
682 %  A description of each parameter follows:
683 %
684 %    o destination: the destination.
685 %
686 %    o source: the source.
687 %
688 %    o size: the size of the memory in bytes to allocate.
689 %
690 */
691 MagickExport void *CopyMagickMemory(void *destination,const void *source,
692   const size_t size)
693 {
694   register const unsigned char
695     *p;
696
697   register unsigned char
698     *q;
699
700   assert(destination != (void *) NULL);
701   assert(source != (const void *) NULL);
702   p=(const unsigned char *) source;
703   q=(unsigned char *) destination;
704   if (((q+size) < p) || (q > (p+size)))
705     switch (size)
706     {
707       default: return(memcpy(destination,source,size));
708       case 8: *q++=(*p++);
709       case 7: *q++=(*p++);
710       case 6: *q++=(*p++);
711       case 5: *q++=(*p++);
712       case 4: *q++=(*p++);
713       case 3: *q++=(*p++);
714       case 2: *q++=(*p++);
715       case 1: *q++=(*p++);
716       case 0: return(destination);
717     }
718   return(memmove(destination,source,size));
719 }
720 \f
721 /*
722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723 %                                                                             %
724 %                                                                             %
725 %                                                                             %
726 +   D e s t r o y M a g i c k M e m o r y                                     %
727 %                                                                             %
728 %                                                                             %
729 %                                                                             %
730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
731 %
732 %  DestroyMagickMemory() deallocates memory associated with the memory manager.
733 %
734 %  The format of the DestroyMagickMemory method is:
735 %
736 %      DestroyMagickMemory(void)
737 %
738 */
739 MagickExport void DestroyMagickMemory(void)
740 {
741 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
742   register ssize_t
743     i;
744
745   if (memory_semaphore == (SemaphoreInfo *) NULL)
746     ActivateSemaphoreInfo(&memory_semaphore);
747   LockSemaphoreInfo(memory_semaphore);
748   for (i=0; i < (ssize_t) memory_pool.number_segments; i++)
749     if (memory_pool.segments[i]->mapped == MagickFalse)
750       memory_methods.destroy_memory_handler(
751         memory_pool.segments[i]->allocation);
752     else
753       (void) UnmapBlob(memory_pool.segments[i]->allocation,
754         memory_pool.segments[i]->length);
755   free_segments=(DataSegmentInfo *) NULL;
756   (void) ResetMagickMemory(&memory_pool,0,sizeof(memory_pool));
757   UnlockSemaphoreInfo(memory_semaphore);
758   RelinquishSemaphoreInfo(&memory_semaphore);
759 #endif
760 }
761 \f
762 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
763 /*
764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765 %                                                                             %
766 %                                                                             %
767 %                                                                             %
768 +   E x p a n d H e a p                                                       %
769 %                                                                             %
770 %                                                                             %
771 %                                                                             %
772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773 %
774 %  ExpandHeap() get more memory from the system.  It returns MagickTrue on
775 %  success otherwise MagickFalse.
776 %
777 %  The format of the ExpandHeap method is:
778 %
779 %      MagickBooleanType ExpandHeap(size_t size)
780 %
781 %  A description of each parameter follows:
782 %
783 %    o size: the size of the memory in bytes we require.
784 %
785 */
786 static MagickBooleanType ExpandHeap(size_t size)
787 {
788   DataSegmentInfo
789     *segment_info;
790
791   MagickBooleanType
792     mapped;
793
794   register ssize_t
795     i;
796
797   register void
798     *block;
799
800   size_t
801     blocksize;
802
803   void
804     *segment;
805
806   blocksize=((size+12*sizeof(size_t))+SegmentSize-1) & -SegmentSize;
807   assert(memory_pool.number_segments < MaxSegments);
808   segment=MapBlob(-1,IOMode,0,blocksize);
809   mapped=segment != (void *) NULL ? MagickTrue : MagickFalse;
810   if (segment == (void *) NULL)
811     segment=(void *) memory_methods.acquire_memory_handler(blocksize);
812   if (segment == (void *) NULL)
813     return(MagickFalse);
814   segment_info=(DataSegmentInfo *) free_segments;
815   free_segments=segment_info->next;
816   segment_info->mapped=mapped;
817   segment_info->length=blocksize;
818   segment_info->allocation=segment;
819   segment_info->bound=(char *) segment+blocksize;
820   i=(ssize_t) memory_pool.number_segments-1;
821   for ( ; (i >= 0) && (memory_pool.segments[i]->allocation > segment); i--)
822     memory_pool.segments[i+1]=memory_pool.segments[i];
823   memory_pool.segments[i+1]=segment_info;
824   memory_pool.number_segments++;
825   size=blocksize-12*sizeof(size_t);
826   block=(char *) segment_info->allocation+4*sizeof(size_t);
827   *BlockHeader(block)=size | PreviousBlockBit;
828   *BlockFooter(block,size)=size;
829   InsertFreeBlock(block,AllocationPolicy(size));
830   block=NextBlock(block);
831   assert(block < segment_info->bound);
832   *BlockHeader(block)=2*sizeof(size_t);
833   *BlockHeader(NextBlock(block))=PreviousBlockBit;
834   return(MagickTrue);
835 }
836 #endif
837 \f
838 /*
839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
840 %                                                                             %
841 %                                                                             %
842 %                                                                             %
843 %   G e t M a g i c k M e m o r y M e t h o d s                               %
844 %                                                                             %
845 %                                                                             %
846 %                                                                             %
847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848 %
849 %  GetMagickMemoryMethods() gets the methods to acquire, resize, and destroy
850 %  memory.
851 %
852 %  The format of the GetMagickMemoryMethods() method is:
853 %
854 %      void GetMagickMemoryMethods(AcquireMemoryHandler *acquire_memory_handler,
855 %        ResizeMemoryHandler *resize_memory_handler,
856 %        DestroyMemoryHandler *destroy_memory_handler)
857 %
858 %  A description of each parameter follows:
859 %
860 %    o acquire_memory_handler: method to acquire memory (e.g. malloc).
861 %
862 %    o resize_memory_handler: method to resize memory (e.g. realloc).
863 %
864 %    o destroy_memory_handler: method to destroy memory (e.g. free).
865 %
866 */
867 MagickExport void GetMagickMemoryMethods(
868   AcquireMemoryHandler *acquire_memory_handler,
869   ResizeMemoryHandler *resize_memory_handler,
870   DestroyMemoryHandler *destroy_memory_handler)
871 {
872   assert(acquire_memory_handler != (AcquireMemoryHandler *) NULL);
873   assert(resize_memory_handler != (ResizeMemoryHandler *) NULL);
874   assert(destroy_memory_handler != (DestroyMemoryHandler *) NULL);
875   *acquire_memory_handler=memory_methods.acquire_memory_handler;
876   *resize_memory_handler=memory_methods.resize_memory_handler;
877   *destroy_memory_handler=memory_methods.destroy_memory_handler;
878 }
879 \f
880 /*
881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882 %                                                                             %
883 %                                                                             %
884 %                                                                             %
885 %   G e t V i r t u a l M e m o r y B l o b                                   %
886 %                                                                             %
887 %                                                                             %
888 %                                                                             %
889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
890 %
891 %  GetVirtualMemoryBlob() returns the virtual memory blob associated with the
892 %  specified MemoryInfo structure.
893 %
894 %  The format of the GetVirtualMemoryBlob method is:
895 %
896 %      void *GetVirtualMemoryBlob(const MemoryInfo *memory_info)
897 %
898 %  A description of each parameter follows:
899 %
900 %    o memory_info: The MemoryInfo structure.
901 */
902 MagickExport void *GetVirtualMemoryBlob(const MemoryInfo *memory_info)
903 {
904   assert(memory_info != (const MemoryInfo *) NULL);
905   assert(memory_info->signature == MagickCoreSignature);
906   return(memory_info->blob);
907 }
908 \f
909 /*
910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911 %                                                                             %
912 %                                                                             %
913 %                                                                             %
914 %   R e l i n q u i s h A l i g n e d M e m o r y                             %
915 %                                                                             %
916 %                                                                             %
917 %                                                                             %
918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
919 %
920 %  RelinquishAlignedMemory() frees memory acquired with AcquireAlignedMemory()
921 %  or reuse.
922 %
923 %  The format of the RelinquishAlignedMemory method is:
924 %
925 %      void *RelinquishAlignedMemory(void *memory)
926 %
927 %  A description of each parameter follows:
928 %
929 %    o memory: A pointer to a block of memory to free for reuse.
930 %
931 */
932 MagickExport void *RelinquishAlignedMemory(void *memory)
933 {
934   if (memory == (void *) NULL)
935     return((void *) NULL);
936 #if defined(MAGICKCORE_HAVE_POSIX_MEMALIGN)
937   free(memory);
938 #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC)
939   _aligned_free(memory);
940 #else
941   free(*((void **) memory-1));
942 #endif
943   return(NULL);
944 }
945 \f
946 /*
947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948 %                                                                             %
949 %                                                                             %
950 %                                                                             %
951 %   R e l i n q u i s h M a g i c k M e m o r y                               %
952 %                                                                             %
953 %                                                                             %
954 %                                                                             %
955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956 %
957 %  RelinquishMagickMemory() frees memory acquired with AcquireMagickMemory()
958 %  or AcquireQuantumMemory() for reuse.
959 %
960 %  The format of the RelinquishMagickMemory method is:
961 %
962 %      void *RelinquishMagickMemory(void *memory)
963 %
964 %  A description of each parameter follows:
965 %
966 %    o memory: A pointer to a block of memory to free for reuse.
967 %
968 */
969 MagickExport void *RelinquishMagickMemory(void *memory)
970 {
971   if (memory == (void *) NULL)
972     return((void *) NULL);
973 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
974   memory_methods.destroy_memory_handler(memory);
975 #else
976   LockSemaphoreInfo(memory_semaphore);
977   assert((SizeOfBlock(memory) % (4*sizeof(size_t))) == 0);
978   assert((*BlockHeader(NextBlock(memory)) & PreviousBlockBit) != 0);
979   if ((*BlockHeader(memory) & PreviousBlockBit) == 0)
980     {
981       void
982         *previous;
983
984       /*
985         Coalesce with previous adjacent block.
986       */
987       previous=PreviousBlock(memory);
988       RemoveFreeBlock(previous,AllocationPolicy(SizeOfBlock(previous)));
989       *BlockHeader(previous)=(SizeOfBlock(previous)+SizeOfBlock(memory)) |
990         (*BlockHeader(previous) & ~SizeMask);
991       memory=previous;
992     }
993   if ((*BlockHeader(NextBlock(NextBlock(memory))) & PreviousBlockBit) == 0)
994     {
995       void
996         *next;
997
998       /*
999         Coalesce with next adjacent block.
1000       */
1001       next=NextBlock(memory);
1002       RemoveFreeBlock(next,AllocationPolicy(SizeOfBlock(next)));
1003       *BlockHeader(memory)=(SizeOfBlock(memory)+SizeOfBlock(next)) |
1004         (*BlockHeader(memory) & ~SizeMask);
1005     }
1006   *BlockFooter(memory,SizeOfBlock(memory))=SizeOfBlock(memory);
1007   *BlockHeader(NextBlock(memory))&=(~PreviousBlockBit);
1008   InsertFreeBlock(memory,AllocationPolicy(SizeOfBlock(memory)));
1009   UnlockSemaphoreInfo(memory_semaphore);
1010 #endif
1011   return((void *) NULL);
1012 }
1013 \f
1014 /*
1015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1016 %                                                                             %
1017 %                                                                             %
1018 %                                                                             %
1019 %   R e l i n q u i s h V i r t u a l M e m o r y                             %
1020 %                                                                             %
1021 %                                                                             %
1022 %                                                                             %
1023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1024 %
1025 %  RelinquishVirtualMemory() frees memory acquired with AcquireVirtualMemory().
1026 %
1027 %  The format of the RelinquishVirtualMemory method is:
1028 %
1029 %      MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info)
1030 %
1031 %  A description of each parameter follows:
1032 %
1033 %    o memory_info: A pointer to a block of memory to free for reuse.
1034 %
1035 */
1036 MagickExport MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info)
1037 {
1038   assert(memory_info != (MemoryInfo *) NULL);
1039   assert(memory_info->signature == MagickCoreSignature);
1040   if (memory_info->blob != (void *) NULL)
1041     switch (memory_info->type)
1042     {
1043       case AlignedVirtualMemory:
1044       {
1045         memory_info->blob=RelinquishAlignedMemory(memory_info->blob);
1046         RelinquishMagickResource(MemoryResource,memory_info->length);
1047         break;
1048       }
1049       case MapVirtualMemory:
1050       {
1051         (void) UnmapBlob(memory_info->blob,memory_info->length);
1052         memory_info->blob=NULL;
1053         RelinquishMagickResource(MapResource,memory_info->length);
1054         if (*memory_info->filename != '\0')
1055           {
1056             (void) RelinquishUniqueFileResource(memory_info->filename);
1057             RelinquishMagickResource(DiskResource,memory_info->length);
1058           }
1059         break;
1060       }
1061       case UnalignedVirtualMemory:
1062       default:
1063       {
1064         memory_info->blob=RelinquishMagickMemory(memory_info->blob);
1065         break;
1066       }
1067     }
1068   memory_info->signature=(~MagickCoreSignature);
1069   memory_info=(MemoryInfo *) RelinquishAlignedMemory(memory_info);
1070   return(memory_info);
1071 }
1072 \f
1073 /*
1074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1075 %                                                                             %
1076 %                                                                             %
1077 %                                                                             %
1078 %   R e s e t M a g i c k M e m o r y                                         %
1079 %                                                                             %
1080 %                                                                             %
1081 %                                                                             %
1082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083 %
1084 %  ResetMagickMemory() fills the first size bytes of the memory area pointed to
1085 %  by memory with the constant byte c.
1086 %
1087 %  The format of the ResetMagickMemory method is:
1088 %
1089 %      void *ResetMagickMemory(void *memory,int byte,const size_t size)
1090 %
1091 %  A description of each parameter follows:
1092 %
1093 %    o memory: a pointer to a memory allocation.
1094 %
1095 %    o byte: set the memory to this value.
1096 %
1097 %    o size: size of the memory to reset.
1098 %
1099 */
1100 MagickExport void *ResetMagickMemory(void *memory,int byte,const size_t size)
1101 {
1102   assert(memory != (void *) NULL);
1103   return(memset(memory,byte,size));
1104 }
1105 \f
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 %                                                                             %
1109 %                                                                             %
1110 %                                                                             %
1111 %   R e s i z e M a g i c k M e m o r y                                       %
1112 %                                                                             %
1113 %                                                                             %
1114 %                                                                             %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 %  ResizeMagickMemory() changes the size of the memory and returns a pointer to
1118 %  the (possibly moved) block.  The contents will be unchanged up to the
1119 %  lesser of the new and old sizes.
1120 %
1121 %  The format of the ResizeMagickMemory method is:
1122 %
1123 %      void *ResizeMagickMemory(void *memory,const size_t size)
1124 %
1125 %  A description of each parameter follows:
1126 %
1127 %    o memory: A pointer to a memory allocation.
1128 %
1129 %    o size: the new size of the allocated memory.
1130 %
1131 */
1132
1133 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1134 static inline void *ResizeBlock(void *block,size_t size)
1135 {
1136   register void
1137     *memory;
1138
1139   if (block == (void *) NULL)
1140     return(AcquireBlock(size));
1141   memory=AcquireBlock(size);
1142   if (memory == (void *) NULL)
1143     return((void *) NULL);
1144   if (size <= (SizeOfBlock(block)-sizeof(size_t)))
1145     (void) memcpy(memory,block,size);
1146   else
1147     (void) memcpy(memory,block,SizeOfBlock(block)-sizeof(size_t));
1148   memory_pool.allocation+=size;
1149   return(memory);
1150 }
1151 #endif
1152
1153 MagickExport void *ResizeMagickMemory(void *memory,const size_t size)
1154 {
1155   register void
1156     *block;
1157
1158   if (memory == (void *) NULL)
1159     return(AcquireMagickMemory(size));
1160 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1161   block=memory_methods.resize_memory_handler(memory,size == 0 ? 1UL : size);
1162   if (block == (void *) NULL)
1163     memory=RelinquishMagickMemory(memory);
1164 #else
1165   LockSemaphoreInfo(memory_semaphore);
1166   block=ResizeBlock(memory,size == 0 ? 1UL : size);
1167   if (block == (void *) NULL)
1168     {
1169       if (ExpandHeap(size == 0 ? 1UL : size) == MagickFalse)
1170         {
1171           UnlockSemaphoreInfo(memory_semaphore);
1172           memory=RelinquishMagickMemory(memory);
1173           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1174         }
1175       block=ResizeBlock(memory,size == 0 ? 1UL : size);
1176       assert(block != (void *) NULL);
1177     }
1178   UnlockSemaphoreInfo(memory_semaphore);
1179   memory=RelinquishMagickMemory(memory);
1180 #endif
1181   return(block);
1182 }
1183 \f
1184 /*
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1186 %                                                                             %
1187 %                                                                             %
1188 %                                                                             %
1189 %   R e s i z e Q u a n t u m M e m o r y                                     %
1190 %                                                                             %
1191 %                                                                             %
1192 %                                                                             %
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 %
1195 %  ResizeQuantumMemory() changes the size of the memory and returns a pointer
1196 %  to the (possibly moved) block.  The contents will be unchanged up to the
1197 %  lesser of the new and old sizes.
1198 %
1199 %  The format of the ResizeQuantumMemory method is:
1200 %
1201 %      void *ResizeQuantumMemory(void *memory,const size_t count,
1202 %        const size_t quantum)
1203 %
1204 %  A description of each parameter follows:
1205 %
1206 %    o memory: A pointer to a memory allocation.
1207 %
1208 %    o count: the number of quantum elements to allocate.
1209 %
1210 %    o quantum: the number of bytes in each quantum.
1211 %
1212 */
1213 MagickExport void *ResizeQuantumMemory(void *memory,const size_t count,
1214   const size_t quantum)
1215 {
1216   size_t
1217     extent;
1218
1219   if (CheckMemoryOverflow(count,quantum) != MagickFalse)
1220     {
1221       memory=RelinquishMagickMemory(memory);
1222       return((void *) NULL);
1223     }
1224   extent=count*quantum;
1225   return(ResizeMagickMemory(memory,extent));
1226 }
1227 \f
1228 /*
1229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230 %                                                                             %
1231 %                                                                             %
1232 %                                                                             %
1233 %   S e t M a g i c k M e m o r y M e t h o d s                               %
1234 %                                                                             %
1235 %                                                                             %
1236 %                                                                             %
1237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238 %
1239 %  SetMagickMemoryMethods() sets the methods to acquire, resize, and destroy
1240 %  memory. Your custom memory methods must be set prior to the
1241 %  MagickCoreGenesis() method.
1242 %
1243 %  The format of the SetMagickMemoryMethods() method is:
1244 %
1245 %      SetMagickMemoryMethods(AcquireMemoryHandler acquire_memory_handler,
1246 %        ResizeMemoryHandler resize_memory_handler,
1247 %        DestroyMemoryHandler destroy_memory_handler)
1248 %
1249 %  A description of each parameter follows:
1250 %
1251 %    o acquire_memory_handler: method to acquire memory (e.g. malloc).
1252 %
1253 %    o resize_memory_handler: method to resize memory (e.g. realloc).
1254 %
1255 %    o destroy_memory_handler: method to destroy memory (e.g. free).
1256 %
1257 */
1258 MagickExport void SetMagickMemoryMethods(
1259   AcquireMemoryHandler acquire_memory_handler,
1260   ResizeMemoryHandler resize_memory_handler,
1261   DestroyMemoryHandler destroy_memory_handler)
1262 {
1263   /*
1264     Set memory methods.
1265   */
1266   if (acquire_memory_handler != (AcquireMemoryHandler) NULL)
1267     memory_methods.acquire_memory_handler=acquire_memory_handler;
1268   if (resize_memory_handler != (ResizeMemoryHandler) NULL)
1269     memory_methods.resize_memory_handler=resize_memory_handler;
1270   if (destroy_memory_handler != (DestroyMemoryHandler) NULL)
1271     memory_methods.destroy_memory_handler=destroy_memory_handler;
1272 }