freetype-config might not be available, allow pkg-config to work
[imager.git] / io.c
1 #include "imager.h"
2 #include "imageri.h"
3 #include <stdlib.h>
4 #ifndef _MSC_VER
5 #include <unistd.h>
6 #endif
7
8
9 /* FIXME: make allocation dynamic */
10
11
12 #ifdef IMAGER_DEBUG_MALLOC
13
14 #define MAXMAL 102400
15 #define MAXDESC 65
16
17 #define UNDRRNVAL 10
18 #define OVERRNVAL 10
19
20 #define PADBYTE 0xaa
21
22
23 static int malloc_need_init = 1;
24
25 typedef struct {
26   void* ptr;
27   size_t size;
28   const char *file;
29   int line;
30 } malloc_entry;
31
32 malloc_entry malloc_pointers[MAXMAL];
33
34
35
36
37 /* Utility functions */
38
39
40 static
41 void
42 malloc_init(void) {
43   int i;
44   for(i=0; i<MAXMAL; i++) malloc_pointers[i].ptr = NULL;
45   malloc_need_init = 0;
46   atexit(malloc_state);
47 }
48
49
50 static
51 int 
52 find_ptr(void *p) {
53   int i;
54   for(i=0;i<MAXMAL;i++)
55     if (malloc_pointers[i].ptr == p)
56       return i;
57   return -1;
58 }
59
60
61 /* Takes a pointer to real start of array,
62  * sets the entries in the table, returns
63  * the offset corrected pointer */
64
65 static
66 void *
67 set_entry(int i, char *buf, size_t size, char *file, int line) {
68   memset( buf, PADBYTE, UNDRRNVAL );
69   memset( &buf[UNDRRNVAL+size], PADBYTE, OVERRNVAL );
70   buf += UNDRRNVAL;
71   malloc_pointers[i].ptr  = buf;
72   malloc_pointers[i].size = size;
73   malloc_pointers[i].file = file;
74   malloc_pointers[i].line = line;
75   return buf;
76 }
77
78 void
79 malloc_state(void) {
80   int i;
81   size_t total = 0;
82
83   i_clear_error();
84   mm_log((0,"malloc_state()\n"));
85   bndcheck_all();
86   for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr != NULL) {
87       mm_log((0,"%d: %lu (%p) : %s (%d)\n", i, (unsigned long)malloc_pointers[i].size, malloc_pointers[i].ptr, malloc_pointers[i].file, malloc_pointers[i].line));
88     total += malloc_pointers[i].size;
89   }
90   if (total == 0) mm_log((0,"No memory currently used!\n"))
91     else mm_log((0,"total: %lu\n", (unsigned long)total));
92 }
93
94
95
96 void*
97 mymalloc_file_line(size_t size, char* file, int line) {
98   char *buf;
99   int i;
100   if (malloc_need_init) malloc_init();
101   
102   /* bndcheck_all(); Uncomment for LOTS OF THRASHING */
103   
104   if ( (i = find_ptr(NULL)) < 0 ) {
105     mm_log((0,"more than %d segments allocated at %s (%d)\n", MAXMAL, file, line));
106     exit(3);
107   }
108
109   if ( (buf = malloc(size+UNDRRNVAL+OVERRNVAL)) == NULL ) {
110     mm_log((1,"Unable to allocate %ld for %s (%i)\n", (long)size, file, line));
111     exit(3);
112   }
113   
114   buf = set_entry(i, buf, size, file, line);
115   mm_log((1,"mymalloc_file_line: slot <%d> %ld bytes allocated at %p for %s (%d)\n", i, (long)size, buf, file, line));
116   return buf;
117 }
118
119 void *
120 (mymalloc)(size_t size) {
121   return mymalloc_file_line(size, "unknown", 0);
122 }
123
124 void*
125 myrealloc_file_line(void *ptr, size_t newsize, char* file, int line) {
126   char *buf;
127   int i;
128
129   if (malloc_need_init) malloc_init();
130   /* bndcheck_all(); ACTIVATE FOR LOTS OF THRASHING */
131   
132   if (!ptr) {
133     mm_log((1, "realloc called with ptr = NULL, sending request to malloc\n"));
134     return mymalloc_file_line(newsize, file, line);
135   }
136   
137   if (!newsize) {
138     mm_log((1, "newsize = 0, sending request to free\n"));
139     myfree_file_line(ptr, file, line);
140     return NULL;
141   }
142
143   if ( (i = find_ptr(ptr)) == -1) {
144     mm_log((0, "Unable to find %p in realloc for %s (%i)\n", ptr, file, line));
145     exit(3);
146   }
147   
148   if ( (buf = realloc(((char *)ptr)-UNDRRNVAL, UNDRRNVAL+OVERRNVAL+newsize)) == NULL ) {
149     mm_log((1,"Unable to reallocate %ld bytes at %p for %s (%i)\n", (long)
150             newsize, ptr, file, line));
151     exit(3); 
152   }
153   
154   buf = set_entry(i, buf, newsize, file, line);
155   mm_log((1,"realloc_file_line: slot <%d> %ld bytes allocated at %p for %s (%d)\n", i, (long)newsize, buf, file, line));
156   return buf;
157 }
158
159 void *
160 (myrealloc)(void *ptr, size_t newsize) {
161   return myrealloc_file_line(ptr, newsize, "unknown", 0);
162 }
163
164 static
165 void
166 bndcheck(int idx) {
167   int i;
168   size_t s = malloc_pointers[idx].size;
169   unsigned char *pp = malloc_pointers[idx].ptr;
170   if (!pp) {
171     mm_log((1, "bndcheck: No pointer in slot %d\n", idx));
172     return;
173   }
174   
175   for(i=0;i<UNDRRNVAL;i++) {
176     if (pp[-(1+i)] != PADBYTE)
177       mm_log((1,"bndcheck: UNDERRUN OF %d bytes detected: slot = %d, point = %p, size = %ld\n", i+1, idx, pp, (long)s ));
178   }
179   
180   for(i=0;i<OVERRNVAL;i++) {
181     if (pp[s+i] != PADBYTE)
182       mm_log((1,"bndcheck: OVERRUN OF %d bytes detected: slot = %d, point = %p, size = %ld\n", i+1, idx, pp, (long)s ));
183   }
184 }
185
186 void
187 bndcheck_all() {
188   int idx;
189   mm_log((1, "bndcheck_all()\n"));
190   for(idx=0; idx<MAXMAL; idx++)
191     if (malloc_pointers[idx].ptr)
192       bndcheck(idx);
193 }
194
195 void
196 myfree_file_line(void *p, char *file, int line) {
197   char  *pp = p;
198   int match = 0;
199   int i;
200
201   if (p == NULL)
202     return;
203   
204   for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr == p) {
205       mm_log((1,"myfree_file_line: pointer %i (%s (%d)) freed at %s (%i)\n", i, malloc_pointers[i].file, malloc_pointers[i].line, file, line));
206     bndcheck(i);
207     malloc_pointers[i].ptr = NULL;
208     match++;
209   }
210
211   mm_log((1, "myfree_file_line: freeing address %p (real %p)\n", pp, pp-UNDRRNVAL));
212   
213   if (match != 1) {
214     mm_log((1, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line));
215     fprintf(stderr, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line);
216                 exit(255);
217   }
218   
219   
220   free(pp-UNDRRNVAL);
221 }
222
223 void
224 (myfree)(void *block) {
225   myfree_file_line(block, "unknown", 0);
226 }
227
228 #else 
229
230 void
231 malloc_state() {
232 }
233
234 void*
235 mymalloc(size_t size) {
236   void *buf;
237
238   if ( (buf = malloc(size)) == NULL ) {
239     mm_log((1, "mymalloc: unable to malloc %ld\n", (long)size));
240     fprintf(stderr,"Unable to malloc %ld.\n", (long)size); exit(3);
241   }
242   mm_log((1, "mymalloc(size %ld) -> %p\n", (long)size, buf));
243   return buf;
244 }
245
246 void *
247 mymalloc_file_line(size_t size, char *file, int line) {
248   return mymalloc(size);
249 }
250
251 void
252 myfree(void *p) {
253   mm_log((1, "myfree(p %p)\n", p));
254   free(p);
255 }
256
257 void
258 myfree_file_line(void *p, char *file, int line) {
259   myfree(p);
260 }
261
262 void *
263 myrealloc(void *block, size_t size) {
264   void *result;
265
266   mm_log((1, "myrealloc(block %p, size %ld)\n", block, (long)size));
267   if ((result = realloc(block, size)) == NULL) {
268     mm_log((1, "myrealloc: out of memory\n"));
269     fprintf(stderr, "Out of memory.\n");
270     exit(3);
271   }
272   return result;
273 }
274
275 void *
276 myrealloc_file_line(void *block, size_t newsize, char *file, int size) {
277   return myrealloc(block, newsize);
278 }
279
280 #endif /* IMAGER_MALLOC_DEBUG */
281
282
283
284
285 /* memory pool implementation */
286
287 void
288 i_mempool_init(i_mempool *mp) {
289   mp->alloc = 10;
290   mp->used  = 0;
291   mp->p = mymalloc(sizeof(void*)*mp->alloc);
292 }
293
294 void
295 i_mempool_extend(i_mempool *mp) {
296   mp->p = myrealloc(mp->p, mp->alloc * 2);
297   mp->alloc *=2;
298 }
299
300 void *
301 i_mempool_alloc(i_mempool *mp, size_t size) {
302   if (mp->used == mp->alloc) i_mempool_extend(mp);
303   mp->p[mp->used] = mymalloc(size);
304   mp->used++;
305   return mp->p[mp->used-1];
306 }
307
308
309 void
310 i_mempool_destroy(i_mempool *mp) {
311   unsigned int i;
312   for(i=0; i<mp->used; i++) myfree(mp->p[i]);
313   myfree(mp->p);
314 }
315
316
317
318 /* Should these really be here? */
319
320 #undef min
321 #undef max
322
323 i_img_dim
324 i_minx(i_img_dim a, i_img_dim b) {
325   if (a<b) return a; else return b;
326 }
327
328 i_img_dim
329 i_maxx(i_img_dim a, i_img_dim b) {
330   if (a>b) return a; else return b;
331 }
332
333
334 struct utf8_size {
335   int mask, expect;
336   int size;
337 };
338
339 struct utf8_size utf8_sizes[] =
340 {
341   { 0x80, 0x00, 1 },
342   { 0xE0, 0xC0, 2 },
343   { 0xF0, 0xE0, 3 },
344   { 0xF8, 0xF0, 4 },
345 };
346
347 /*
348 =item i_utf8_advance(char **p, size_t *len)
349
350 Retrieve a C<UTF-8> character from the stream.
351
352 Modifies *p and *len to indicate the consumed characters.
353
354 This doesn't support the extended C<UTF-8> encoding used by later
355 versions of Perl.  Since this is typically used to implement text
356 output by font drivers, the strings supplied shouldn't have such out
357 of range characters.
358
359 This doesn't check that the C<UTF-8> character is using the shortest
360 possible representation.
361
362 Returns ~0UL on failure.
363
364 =cut
365 */
366
367 unsigned long 
368 i_utf8_advance(char const **p, size_t *len) {
369   unsigned char c;
370   int i, ci, clen = 0;
371   unsigned char codes[3];
372   if (*len == 0)
373     return ~0UL;
374   c = *(*p)++; --*len;
375
376   for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
377     if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
378       clen = utf8_sizes[i].size;
379       break;
380     }
381   }
382   if (clen == 0 || *len < clen-1) {
383     --*p; ++*len;
384     return ~0UL;
385   }
386
387   /* check that each character is well formed */
388   i = 1;
389   ci = 0;
390   while (i < clen) {
391     if (((*p)[ci] & 0xC0) != 0x80) {
392       --*p; ++*len;
393       return ~0UL;
394     }
395     codes[ci] = (*p)[ci];
396     ++ci; ++i;
397   }
398   *p += clen-1; *len -= clen-1;
399   if (c & 0x80) {
400     if ((c & 0xE0) == 0xC0) {
401       return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
402     }
403     else if ((c & 0xF0) == 0xE0) {
404       return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
405     }
406     else if ((c & 0xF8) == 0xF0) {
407       return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12) 
408               | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
409     }
410     else {
411       *p -= clen; *len += clen;
412       return ~0UL;
413     }
414   }
415   else {
416     return c;
417   }
418 }
419