chiark / gitweb /
debugging for thing that crashed
[innduct.git] / innfeed / buffer.c
1 /*  $Id: buffer.c 7285 2005-06-07 06:38:24Z eagle $
2 **
3 **  The Buffer class for innfeed.
4 **
5 **  Written by James Brister <brister@vix.com>
6 **
7 **  The implementation of the Buffer class.  Buffers are reference counted
8 **  objects that abstract memory regions in a way similar to struct iovec.
9 */
10
11 #include "innfeed.h"
12 #include "config.h"
13 #include "clibrary.h"
14 #include <assert.h>
15
16 #include "inn/messages.h"
17 #include "libinn.h"
18
19 #include "buffer.h"
20
21
22 static Buffer gBufferList = NULL ;
23 static Buffer bufferPool = NULL ;
24 static unsigned int bufferCount = 0 ;
25 static unsigned int bufferByteCount = 0 ;
26
27
28 struct buffer_s 
29 {
30     int refCount ;
31     char *mem ;
32     size_t memSize ;            /* the length of mem */
33     size_t dataSize ;           /* amount that has actual data in it. */
34     bool deletable ;
35     void (*bufferDeletedCbk)(void *);
36     void *bufferDeletedCbkData;
37     struct buffer_s *next ;
38     struct buffer_s *prev ;
39 };
40
41 #define BUFFER_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (struct buffer_s)))
42
43 static void fillBufferPool (void)
44 {
45   unsigned int i ;
46
47   bufferPool = xmalloc (sizeof(struct buffer_s) * BUFFER_POOL_SIZE) ;
48
49   for (i = 0; i < BUFFER_POOL_SIZE - 1; i++)
50     bufferPool[i] . next = &(bufferPool [i + 1]) ;
51   bufferPool [BUFFER_POOL_SIZE-1] . next = NULL ;
52 }
53
54
55 Buffer newBuffer (size_t size)
56 {
57   Buffer nb ;
58
59   if (bufferPool == NULL)
60     fillBufferPool() ;
61
62   nb = bufferPool;
63   ASSERT (nb != NULL) ;
64   bufferPool = bufferPool->next ;
65
66   nb->refCount = 1 ;
67
68   nb->mem = xmalloc (size + 1) ;
69   
70   nb->mem [size] = '\0' ;
71   nb->memSize = size ;
72   nb->dataSize = 0 ;
73   nb->deletable = true ;
74   nb->bufferDeletedCbk = NULL;
75   nb->bufferDeletedCbkData = NULL;
76
77   bufferByteCount += size + 1 ;
78   bufferCount++ ;
79
80   nb->next = gBufferList ;
81   nb->prev = NULL;
82   if (gBufferList != NULL)
83      gBufferList->prev = nb ;
84   gBufferList = nb ;
85   
86 #if 0
87   d_printf (1,"Creating a DELETABLE buffer %p\n",nb) ;
88 #endif
89
90   return nb ;
91 }
92
93
94 Buffer newBufferByCharP (const char *ptr, size_t size, size_t dataSize)
95 {
96   Buffer nb ;
97
98   if (bufferPool == NULL)
99     fillBufferPool() ;
100
101   nb = bufferPool;
102   ASSERT (nb != NULL) ;
103   bufferPool = bufferPool->next ;
104   
105   nb->refCount = 1 ;
106   nb->mem = (char *) ptr ;      /* cast away const */
107   nb->memSize = size ;
108   nb->dataSize = dataSize ;
109   nb->deletable = false ;
110   nb->bufferDeletedCbk = NULL;
111   nb->bufferDeletedCbkData = NULL;
112
113   nb->next = gBufferList ;
114   nb->prev = NULL;
115   if (gBufferList != NULL)
116      gBufferList->prev = nb ;
117   gBufferList = nb ;
118
119   bufferCount++ ;
120 #if 0
121   d_printf (1,"Creating a NON-DELETABLE buffer %p\n",nb) ;
122 #endif
123   
124   return nb ;
125 }
126
127
128 void delBuffer (Buffer buff) 
129 {
130   if (buff != NULL && --(buff->refCount) == 0)
131     {
132 #if 0
133       d_printf (1,"Freeing a %s buffer (%p)\n",
134                (buff->deletable ? "DELETABLE" : "NON-DELETABLE"), buff) ;
135 #endif
136
137       bufferCount-- ;
138       if (buff->deletable)
139         {
140           bufferByteCount -= (buff->memSize + 1) ;
141           free (buff->mem) ;
142           buff->mem = NULL ;
143         }
144
145       if (buff->bufferDeletedCbk) {
146         (buff->bufferDeletedCbk)(buff->bufferDeletedCbkData);
147         buff->bufferDeletedCbk = NULL;
148         buff->bufferDeletedCbkData = NULL;
149       }
150
151       if (buff->next != NULL)
152         buff->next->prev = buff->prev ;
153       if (buff->prev != NULL)
154         buff->prev->next = buff->next ;
155       else
156         {
157           ASSERT(gBufferList == buff) ;
158           gBufferList = buff->next ;
159         }
160
161       buff->next = bufferPool ;
162       bufferPool = buff ;
163     }
164 }
165
166 Buffer bufferTakeRef (Buffer buff) 
167 {
168   ASSERT (buff != NULL) ;
169   
170   if (buff != NULL)
171     buff->refCount++ ;
172
173   return buff ;
174 }
175
176
177 void gPrintBufferInfo (FILE *fp, unsigned int indentAmt)
178 {
179   Buffer b ;
180   char indent [INDENT_BUFFER_SIZE] ;
181   unsigned int i ;
182
183   for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
184     indent [i] = ' ' ;
185   indent [i] = '\0' ;
186   
187   fprintf (fp,"%sGlobal Buffer List : (count %d) {\n",indent,bufferCount) ;
188   
189   for (b = gBufferList ; b != NULL ; b = b->next)
190     printBufferInfo (b,fp,indentAmt + INDENT_INCR) ;
191
192   fprintf (fp,"%s}\n",indent) ;
193 }
194
195 void printBufferInfo (Buffer buffer, FILE *fp, unsigned int indentAmt)
196 {
197   char indent [INDENT_BUFFER_SIZE] ;
198   char bufferStart [256] ;
199   unsigned int i ;
200
201   for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
202     indent [i] = ' ' ;
203   indent [i] = '\0' ;
204   
205   fprintf (fp,"%sBuffer : %p {\n",indent,(void *) buffer) ;
206
207   if (buffer == NULL)
208     {
209       fprintf (fp,"%s}\n",indent) ;
210       return ;
211     }
212
213   i = MIN(sizeof (bufferStart) - 1,buffer->dataSize) ;
214   memcpy (bufferStart,buffer->mem,i) ;
215   bufferStart[i] = '\0';
216   
217   fprintf (fp,"%s    refcount : %d\n",indent,buffer->refCount) ;
218   fprintf (fp,"%s    data-size : %ld\n",indent,(long) buffer->dataSize) ;
219   fprintf (fp,"%s    mem-size : %ld\n",indent,(long) buffer->memSize) ;
220   fprintf (fp,"%s    base : %p\n", indent,(void *) buffer->mem) ;
221   fprintf (fp,"%s    deletable : %s\n",indent,boolToString(buffer->deletable));
222   fprintf (fp,"%s    buffer [0:%ld] : \"%s\"\n",
223            indent, (long) i, bufferStart) ;
224   fprintf (fp,"%s}\n",indent) ;
225 }
226
227
228 void *bufferBase (Buffer buff) 
229 {
230   return buff->mem ;
231 }
232
233 size_t bufferSize (Buffer buff) 
234 {
235   return buff->memSize ;
236 }
237
238 size_t bufferDataSize (Buffer buff) 
239 {
240   return buff->dataSize ;
241 }
242
243 void bufferIncrDataSize (Buffer buff, size_t size) 
244 {
245   if (buff->dataSize + size > buff->memSize)
246     die ("Trying to make a buffer data size bigger than its memory alloc");
247   else
248     buff->dataSize += size ;
249 }
250
251 void bufferDecrDataSize (Buffer buff, size_t size) 
252 {
253   ASSERT (size > buff->dataSize) ;
254   
255   buff->dataSize -= size ;
256 }
257
258 void bufferSetDataSize (Buffer buff, size_t size) 
259 {
260   buff->dataSize = size ;
261
262   ASSERT (buff->dataSize <= buff->memSize) ;
263 }
264
265 void bufferSetDeletedCbk (Buffer buff, void (*cbk)(void *), void *data)
266 {
267   ASSERT(buff->bufferDeletedCbk == NULL &&
268          buff->bufferDeletedCbk != cbk);
269   ASSERT(buff->bufferDeletedCbkData == NULL &&
270          buff->bufferDeletedCbkData != data);
271   buff->bufferDeletedCbk = cbk;
272   buff->bufferDeletedCbkData = data;
273 }
274
275 void freeBufferArray (Buffer *buffs)
276 {
277   Buffer *b = buffs ;
278   
279   while (b && *b)
280     {
281       delBuffer (*b) ;
282       b++ ;
283     }
284
285   if (buffs)
286     free (buffs) ;
287 }
288
289
290   /* Allocate an array and put all the arguments (the last of which must be
291      NULL) into it. The terminating NULL is put in the returned array. */
292 Buffer *makeBufferArray (Buffer buff, ...)
293 {
294   va_list ap ;
295   size_t cLen = 10, idx = 0 ;
296   Buffer *ptr, p ;
297
298   ptr = xcalloc (cLen, sizeof(Buffer)) ;
299
300   ptr [idx++] = buff ;
301
302   va_start (ap, buff) ;
303   do
304     {
305       p = va_arg (ap, Buffer) ;
306       if (idx == cLen)
307         {
308           cLen += 10 ;
309           ptr = xrealloc (ptr, sizeof(Buffer) * cLen) ;
310         }
311       ptr [idx++] = p ;
312     }
313   while (p != NULL) ;
314   va_end (ap) ;
315
316   return ptr ;
317 }
318
319
320 bool isDeletable (Buffer buff)
321 {
322   return buff->deletable ;
323 }
324
325
326
327   /* Dups the array including taking out references on the Buffers inside */
328 Buffer *dupBufferArray (Buffer *array)
329 {
330   Buffer *newArr ;
331   int count = 0 ;
332
333   while (array && array [count] != NULL)
334     count++ ;
335
336   newArr = xmalloc (sizeof(Buffer) * (count + 1)) ;
337
338   for (count = 0 ; array [count] != NULL ; count++)
339     newArr [count] = bufferTakeRef (array [count]) ;
340
341   newArr [count] = NULL ;
342
343   return newArr ;
344 }
345
346
347 unsigned int bufferArrayLen (Buffer *array)
348 {
349   unsigned int count = 0 ;
350
351   if (array != NULL)
352     while (*array != NULL)
353       {
354         count++ ;
355         array++ ;
356       }
357
358   return count ;
359 }
360
361
362 bool copyBuffer (Buffer dest, Buffer src)
363 {
364   char *baseDest = bufferBase (dest) ;
365   char *baseSrc = bufferBase (src) ;
366   unsigned int amt = bufferDataSize (src) ;
367
368   if (amt > bufferSize (dest))
369     return false ;
370
371   memcpy (baseDest, baseSrc, amt) ;
372
373   bufferSetDataSize (dest,amt) ;
374
375   return true ;
376 }
377
378
379 unsigned int bufferRefCount (Buffer buf)
380 {
381   return buf->refCount ;
382 }
383
384
385 void bufferAddNullByte (Buffer buff) 
386 {
387   char *p = bufferBase (buff) ;
388
389   p [buff->dataSize] = '\0' ;
390 }
391
392
393   /* append the src buffer to the dest buffer growing the dest as needed.
394      Can only be done to deletable buffers. */
395 bool concatBuffer (Buffer dest, Buffer src)
396 {
397   ASSERT (dest->deletable) ;
398
399   if ( !dest->deletable )
400     return false ;              /* yeah, i know this is taken care of above */
401       
402   if ((dest->dataSize + src->dataSize) > dest->memSize)
403     {
404       char *newMem = xcalloc (dest->dataSize + src->dataSize + 1, 1) ;
405
406       bufferByteCount += ((dest->dataSize + src->dataSize) - dest->memSize) ;
407       
408       memcpy (newMem, dest->mem, dest->dataSize) ;
409
410       ASSERT (dest->mem != NULL) ;
411       free (dest->mem) ;
412
413       dest->mem = newMem ;
414       dest->memSize = dest->dataSize + src->dataSize ; /* yep. 1 less */
415     }
416
417   memcpy (&dest->mem[dest->dataSize], src->mem, dest->dataSize) ;
418
419   dest->dataSize += src->dataSize ;
420
421   return true ;
422 }
423
424
425   /* realloc the buffer's memory to increase the size by AMT */
426 bool expandBuffer (Buffer buff, size_t amt)
427 {
428   d_printf (2,"Expanding buffer....\n") ;
429   
430   if (!buff->deletable)
431     return false ;
432
433   bufferByteCount += amt ;
434   buff->memSize += amt ;
435
436   buff->mem = xrealloc (buff->mem, buff->memSize + 1) ;
437
438   return true ;
439 }
440
441
442   /* Take a buffer and shift the contents around to add the necessary CR
443      before every line feed and a '.' before every '.' at the start of a
444      line. */
445 bool nntpPrepareBuffer (Buffer buffer)
446 {
447   int msize, newDsize, dsize, extra ;
448   char *base, p, *src, *dst ;
449   bool needfinal = false ;
450
451   ASSERT (buffer != NULL) ;
452
453   dsize = buffer->dataSize ;
454   msize = buffer->memSize - 1 ;
455   base = buffer->mem ;
456
457   extra = 3 ;
458   p = '\0' ;
459   for (src = base + dsize - 1 ; src > base ; )
460     {
461       if (*src == '\n')
462         {
463           extra++ ;
464           if (p == '.')
465             extra++ ;
466         }
467       p = *src-- ;
468     }
469   if (*src == '\n')
470     {
471       extra++ ;
472       if (p == '.')
473         extra++ ;
474     }
475
476   if (dsize > 0 && base [dsize - 1] != '\n')
477     {
478       needfinal = true ;
479       extra += 2 ;
480     }
481     
482   newDsize = dsize + extra ;
483   
484   if (msize - dsize < extra)
485     {
486       d_printf (2,"Expanding buffer in nntpPrepareBuffer (from %d to %d)\n",
487                msize, msize + (extra - (msize - dsize))) ;
488
489       if ( !expandBuffer (buffer, extra - (msize - dsize)) )
490         {
491           d_printf (1,"Expand failed...\n") ;
492           return false ;
493         }
494       
495       ASSERT (dsize == (int) buffer->dataSize) ;
496
497       base = buffer->mem ;
498     }
499
500   base [newDsize] = '\0' ;
501   base [newDsize - 1] = '\n' ;
502   base [newDsize - 2] = '\r' ;
503   base [newDsize - 3] = '.' ;
504   newDsize -= 3 ;
505   extra -= 3 ;
506   
507   if (needfinal)
508     {
509       base [newDsize - 1] = '\n' ;
510       base [newDsize - 2] = '\r' ;
511       newDsize -= 2 ;
512       extra -= 2 ;
513     }
514   
515   if (extra)
516     {
517       p = '\0';
518       src = base + dsize - 1 ;
519       dst = base + newDsize - 1 ;
520       while (1)
521         {
522           if (*src == '\n')
523             {
524               if (p == '.')
525                 {
526                   *dst-- = '.' ;
527                   extra-- ;
528                 }
529               *dst-- = '\n' ;
530               *dst = '\r' ;
531               if (--extra <= 0)
532                  break ;
533               p = '\0' ;
534               dst-- ;
535               src-- ;
536             }
537           else
538             p = *dst-- = *src-- ;
539         }
540       ASSERT(dst >= base && src >= base) ; 
541     }
542
543   newDsize += 3;
544   if (needfinal)
545     newDsize += 2 ;
546   
547   bufferSetDataSize (buffer,newDsize) ;
548   
549   return true ;
550 }