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