|
NCBI Home IEB Home C Toolkit docs C++ Toolkit source browser C Toolkit source browser (2) |
NCBI C Toolkit Cross ReferenceC/connect/ncbi_buffer.c |
source navigation diff markup identifier search freetext search file search |
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 |
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more information. |