NCBI C Toolkit Cross Reference

C/connect/ncbi_buffer.c


  1 /* $Id: ncbi_buffer.c,v 6.18 2009/07/06 18:49:30 kazimird Exp $
  2  * ===========================================================================
  3  *
  4  *                            PUBLIC DOMAIN NOTICE
  5  *               National Center for Biotechnology Information
  6  *
  7  *  This software/database is a "United States Government Work" under the
  8  *  terms of the United States Copyright Act.  It was written as part of
  9  *  the author's official duties as a United States Government employee and
 10  *  thus cannot be copyrighted.  This software/database is freely available
 11  *  to the public for use. The National Library of Medicine and the U.S.
 12  *  Government have not placed any restriction on its use or reproduction.
 13  *
 14  *  Although all reasonable efforts have been taken to ensure the accuracy
 15  *  and reliability of the software and data, the NLM and the U.S.
 16  *  Government do not and cannot warrant the performance or results that
 17  *  may be obtained by using this software or data. The NLM and the U.S.
 18  *  Government disclaim all warranties, express or implied, including
 19  *  warranties of performance, merchantability or fitness for any particular
 20  *  purpose.
 21  *
 22  *  Please cite the author in any work or product based on this material.
 23  *
 24  * ===========================================================================
 25  *
 26  * Author:  Denis Vakatov
 27  *
 28  * File Description:
 29  *   Memory-resident FIFO storage area (to be used e.g. in I/O buffering)
 30  *
 31  */
 32 
 33 #include <connect/ncbi_buffer.h>
 34 #include <stdlib.h>
 35 #include <string.h>
 36 #include <assert.h>
 37 #ifndef NDEBUG
 38 /* NOTE: this conditional inclusion is only needed by assert.h on Darwin!
 39  * We do not want to include "ncbi_config.h" to additionally branch on
 40  * NCBI_OS_DAWRIN here because in C toolkit it in turn pulls ncbilcl.h,
 41  * which includes <stdio.h>, thus making this conditional unnecessary.
 42  */
 43 #  include <stdio.h>
 44 #endif
 45 
 46 
 47 /* Buffer chunk
 48  */
 49 typedef struct SBufChunkTag {
 50     struct SBufChunkTag* next;
 51     size_t size;       /* of data (including the discarded "n_skip" bytes)  */
 52     size_t alloc_size; /* maximum available (allocated) size of "data"      */
 53     size_t n_skip;     /* # of bytes already discarded(read) from the chunk */
 54     char*  data;       /* data stored in this chunk                         */
 55 } SBufChunk;
 56 
 57 
 58 /* Buffer
 59  */
 60 typedef struct BUF_tag {
 61     size_t     chunk_size; /* this is actually a chunk size unit             */
 62     SBufChunk* list;
 63     SBufChunk* last;
 64     size_t     size;       /* total buffer size; m.b.consistent at all times */
 65 } BUF_struct;
 66 
 67 
 68 
 69 extern size_t BUF_SetChunkSize(BUF* pBuf, size_t chunk_size)
 70 {
 71     /* create buffer internals, if not created yet */
 72     if ( !*pBuf ) {
 73         if ( !(*pBuf = (BUF_struct*) malloc(sizeof(BUF_struct))) )
 74             return 0;
 75         (*pBuf)->list = (*pBuf)->last = 0;
 76         (*pBuf)->size = 0;
 77     }
 78 
 79     /* and set the min. mem. chunk size */
 80     (*pBuf)->chunk_size = chunk_size ? chunk_size : BUF_DEF_CHUNK_SIZE;
 81     return (*pBuf)->chunk_size;
 82 }
 83 
 84 
 85 extern size_t BUF_Size(BUF buf)
 86 {
 87 #if defined(_DEBUG)  &&  !defined(NDEBUG)  
 88     size_t     size;
 89     SBufChunk* pChunk;
 90     if (!buf  ||  !buf->list)
 91         return 0;
 92 
 93     for (size = 0, pChunk = buf->list;  pChunk;  pChunk = pChunk->next) {
 94         /* NB: no empty blocks allowed within the list */
 95         assert(pChunk->size > pChunk->n_skip);
 96         size += pChunk->size - pChunk->n_skip;
 97     }
 98     assert(size == buf->size);
 99     return size;
100 #else
101     return buf ? buf->size : 0;
102 #endif /*_DEBUG && !NDEBUG*/
103 }
104 
105 
106 /* Create a new chunk.
107  * Allocate at least "chunk_size" bytes, but no less than "size" bytes.
108  * Special case: "size" == 0 results in no data storage allocation.
109  */
110 static SBufChunk* s_AllocChunk(size_t size, size_t chunk_size)
111 {
112     size_t alloc_size = ((size + chunk_size - 1) / chunk_size) * chunk_size;
113     SBufChunk* pChunk = (SBufChunk*) malloc(sizeof(*pChunk) + alloc_size);
114     if ( !pChunk )
115         return 0;
116 
117     /* leave pChunk->next uninited */
118     pChunk->size       = 0;
119     pChunk->alloc_size = alloc_size;
120     pChunk->n_skip     = 0;
121     pChunk->data       = alloc_size ? (char*) pChunk + sizeof(*pChunk) : 0;
122     return pChunk;
123 }
124 
125 
126 extern int/*bool*/ BUF_Append(BUF* pBuf, const void* data, size_t size)
127 {
128     SBufChunk* pChunk;
129     if ( !size )
130         return 1/*true*/;
131 
132     /* init the buffer internals, if not init'd yet */
133     if (!*pBuf  &&  !BUF_SetChunkSize(pBuf, 0))
134         return 0/*false*/;
135 
136     if ( !(pChunk = s_AllocChunk(0, (*pBuf)->chunk_size)) )
137         return 0/*false*/;
138 
139     assert( !pChunk->data );
140     pChunk->alloc_size = size;
141     pChunk->size       = size;
142     pChunk->data       = (char*) data;
143     pChunk->next       = 0;
144 
145     if ( (*pBuf)->last )
146         (*pBuf)->last->next = pChunk;
147     else
148         (*pBuf)->list       = pChunk;
149     (*pBuf)->last  = pChunk;
150     (*pBuf)->size += size;
151     return 1/*true*/;
152 }
153 
154 
155 extern int/*bool*/ BUF_Prepend(BUF* pBuf, const void* data, size_t size)
156 {
157     SBufChunk* pChunk;
158     if ( !size )
159         return 1/*true*/;
160 
161     /* init the buffer internals, if not init'd yet */
162     if (!*pBuf  &&  !BUF_SetChunkSize(pBuf, 0))
163         return 0/*false*/;
164 
165     if ( !(pChunk = s_AllocChunk(0, (*pBuf)->chunk_size)) )
166         return 0/*false*/;
167 
168     assert( !pChunk->data );
169     pChunk->alloc_size = size;
170     pChunk->size       = size;
171     pChunk->data       = (char*) data;
172     pChunk->next       = (*pBuf)->list;
173 
174     (*pBuf)->list  = pChunk;
175     (*pBuf)->size += size;
176     return 1/*true*/;
177 }
178 
179 
180 extern int/*bool*/ BUF_Write(BUF* pBuf, const void* data, size_t size)
181 {
182     SBufChunk* pChunk, *pTail;
183     if ( !size )
184         return 1/*true*/;
185 
186     /* init the buffer internals, if not init'd yet */
187     if (!*pBuf  &&  !BUF_SetChunkSize(pBuf, 0))
188         return 0/*false*/;
189 
190     /* find the last allocated chunk */
191     pTail = (*pBuf)->last;
192 
193     /* write to an unfilled space of the last allocated chunk, if any */
194     if (pTail  &&  pTail->alloc_size > pTail->size) {
195         size_t n_avail = pTail->alloc_size - pTail->size;
196         size_t n_write = n_avail < size ? n_avail : size;
197         memcpy(pTail->data + pTail->size, data, n_write);
198         pTail->size   += n_write;
199         (*pBuf)->size += n_write;
200         size          -= n_write;
201         data = (char*) data + n_write;
202     }
203 
204     /* allocate and write to the new chunk, if necessary */
205     if ( size ) {
206         if ( !(pChunk = s_AllocChunk(size, (*pBuf)->chunk_size)) )
207             return 0/*false*/;
208         memcpy(pChunk->data, data, size);
209         pChunk->size = size;
210         pChunk->next = 0;
211 
212         /* add the new chunk to the list */
213         if ( pTail )
214             pTail->next   = pChunk;
215         else
216             (*pBuf)->list = pChunk;
217         (*pBuf)->last  = pChunk;
218         (*pBuf)->size += size;
219     }
220     return 1/*true*/;
221 }
222 
223 
224 extern int/*bool*/ BUF_PushBack(BUF* pBuf, const void* data, size_t size)
225 {
226     SBufChunk* pChunk;
227     if ( !size )
228         return 1/*true*/;
229 
230     /* init the buffer internals, if not init'd yet */
231     if (!*pBuf  &&  !BUF_SetChunkSize(pBuf, 0) )
232         return 0/*false*/;
233 
234     pChunk = (*pBuf)->list;
235 
236     /* allocate and link a new chunk to the beginning of the chunk list */
237     if (!pChunk  ||  pChunk->n_skip < size) {
238         if ( !(pChunk = s_AllocChunk(size, (*pBuf)->chunk_size)) )
239             return 0/*false*/;
240         pChunk->n_skip = pChunk->size = pChunk->alloc_size;
241         pChunk->next   = (*pBuf)->list;
242         (*pBuf)->list  = pChunk;
243         if ( !(*pBuf)->last )
244             (*pBuf)->last = pChunk;
245     }
246 
247     /* write data */
248     assert(pChunk->n_skip >= size);
249     pChunk->n_skip -= size;
250     memcpy(pChunk->data + pChunk->n_skip, data, size);
251     (*pBuf)->size  += size;
252     return 1/*true*/;
253 }
254 
255 
256 extern size_t BUF_Peek(BUF buf, void* data, size_t size)
257 {
258     return BUF_PeekAt(buf, 0, data, size);
259 }
260 
261 
262 extern size_t BUF_PeekAt(BUF buf, size_t pos, void* data, size_t size)
263 {
264     size_t     n_todo;
265     SBufChunk* pChunk;
266 
267     if (!size  ||  !buf  ||  !buf->list)
268         return 0;
269 
270     /* special treatment for NULL data buffer */
271     if ( !data ) {
272         if (buf->size <= pos)
273             return 0;
274         n_todo = buf->size - pos;
275         return n_todo < size ? n_todo : size;
276     }
277 
278     /* skip "pos" bytes */
279     for (pChunk = buf->list;  pChunk;  pChunk = pChunk->next) {
280         size_t chunk_size = pChunk->size - pChunk->n_skip;
281         assert(pChunk->size > pChunk->n_skip);
282         if (chunk_size > pos)
283             break;
284         pos -= chunk_size;
285     }
286 
287     /* copy the peeked data to "data" */
288     for (n_todo = size;  n_todo  &&  pChunk;  pChunk = pChunk->next, pos = 0) {
289         size_t n_skip = pChunk->n_skip + pos;
290         size_t n_copy = pChunk->size - n_skip;
291         assert(pChunk->size > n_skip);
292         if (n_copy > n_todo)
293             n_copy = n_todo;
294 
295  
296         memcpy(data, (char*) pChunk->data + n_skip, n_copy);
297         data = (char*) data + n_copy;
298         n_todo -= n_copy;
299     }
300 
301     assert(size >= n_todo);
302     return size - n_todo;
303 }
304 
305 
306 extern size_t BUF_PeekAtCB(BUF buf,
307                            size_t pos,
308                            void (*callback)(void*, void*, size_t),
309                            void* data,
310                            size_t size)
311 {
312     size_t     n_todo;
313     SBufChunk* pChunk;
314 
315     if (!size  ||  !buf  ||  !buf->list)
316         return 0;
317 
318     /* special treatment for NULL callback */
319     if ( !callback ) {
320         if (buf->size <= pos)
321             return 0;
322         n_todo = buf->size - pos;
323         return n_todo < size ? n_todo : size;
324     }
325 
326     /* skip "pos" bytes */
327     for (pChunk = buf->list;  pChunk;  pChunk = pChunk->next) {
328         size_t chunk_size = pChunk->size - pChunk->n_skip;
329         assert(pChunk->size > pChunk->n_skip);
330         if (chunk_size > pos)
331             break;
332         pos -= chunk_size;
333     }
334 
335     /* process the peeked data */
336     for (n_todo = size;  n_todo  &&  pChunk;  pChunk = pChunk->next, pos = 0) {
337         size_t n_skip = pChunk->n_skip + pos;
338         size_t n_copy = pChunk->size - n_skip;
339         assert(pChunk->size > n_skip);
340         if (n_copy > n_todo)
341             n_copy = n_todo;
342 
343         callback(data, (char*) pChunk->data + n_skip, n_copy);
344         n_todo -= n_copy;
345     }
346 
347     assert(size >= n_todo);
348     return size - n_todo;
349 }
350 
351 
352 extern size_t BUF_Read(BUF buf, void* data, size_t size)
353 {
354     size_t n_todo;
355 
356     /* peek to the callers data buffer, if non-NULL */
357     if ( data )
358         size = BUF_PeekAt(buf, 0, data, size);
359     else if (!buf ||  !buf->list)
360         return 0;
361 
362     if (!size)
363         return 0;
364 
365     /* remove the read data from the buffer */ 
366     n_todo = size;
367     do {
368         SBufChunk* pHead = buf->list;
369         size_t     n_avail = pHead->size - pHead->n_skip;
370         if (n_todo < n_avail) {
371             /* discard some of the chunk data */
372             pHead->n_skip += n_todo;
373             buf->size     -= n_todo;
374             n_todo = 0;
375             break;
376         }
377         /* discard the whole chunk */
378         if ( !(buf->list = pHead->next) )
379             buf->last = 0;
380         free(pHead);
381         buf->size -= n_avail;
382         n_todo    -= n_avail;
383     } while (n_todo  &&  buf->list);
384 
385     assert(size >= n_todo);
386     return size - n_todo;
387 }
388 
389 
390 extern void BUF_Erase(BUF buf)
391 {
392     if ( !buf )
393         return;
394 
395     while ( buf->list ) {
396         SBufChunk* pHead = buf->list;
397         buf->list = pHead->next;
398         free(pHead);
399     }
400     buf->last = 0;
401     buf->size = 0;
402 }
403 
404 
405 extern void BUF_Destroy(BUF buf)
406 {
407     if (buf) {
408         BUF_Erase(buf);
409         free(buf);
410     }
411 }
412 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.