NCBI C++ ToolKit
ncbi_buffer.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_buffer.c 92853 2021-02-19 05:24:50Z lavr $
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, Anton Lavrentiev
27  *
28  * File Description:
29  * Memory-resident FIFO storage area (to be used e.g. in I/O buffering)
30  *
31  */
32 
33 #include "ncbi_assert.h"
34 #include <connect/ncbi_buffer.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 
39 #define _BUF_ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
40 #define BUF_ALIGN(s) _BUF_ALIGN((s), sizeof(double))
41 
42 
43 /* Buffer chunk
44  */
45 typedef struct SBufChunkTag {
46  struct SBufChunkTag* next;
47  void* base; /* base pointer of the chunk data if to be free()'d */
48  char* data; /* data stored in this chunk */
49  size_t skip; /* # of bytes already discarded(read) from the chunk */
50  size_t size; /* of data (including the discarded "skip" bytes) */
51  size_t extent; /* total allocated size of "data" (0 if none/RO) */
53 
54 
55 /* Buffer
56  */
57 struct SNcbiBuf {
58  SBufChunk* list; /* the singly-linked list of chunks */
59  SBufChunk* last; /* shortcut to the last chunk in the list */
60  size_t unit; /* chunk size unit */
61  size_t size; /* total buffer size; must be consistent at all times*/
62 };
63 
64 
65 extern size_t BUF_SetChunkSize(BUF* buf, size_t chunk_size)
66 {
67  assert(!(sizeof(double) & (sizeof(double) - 1)));
69 
70  /* create the buffer internals, if not done yet */
71  if (!*buf) {
72  if (!(*buf = (struct SNcbiBuf*) malloc(sizeof(**buf))))
73  return 0;
74  (*buf)->list = (*buf)->last = 0;
75  (*buf)->size = 0;
76  }
77 
78  /* and set the minimal memory chunk unit size */
80  return (*buf)->unit;
81 }
82 
83 
84 extern size_t BUF_Size(BUF buf)
85 {
86 #if defined(_DEBUG) && !defined(NDEBUG)
87  size_t size;
88  SBufChunk* chunk;
89 
90  if (!buf)
91  return 0;
92  assert(!buf->list == !buf->last
93  && !buf->size == !(buf->list || buf->last));
94 
95  for (size = 0, chunk = buf->list; chunk; chunk = chunk->next) {
96  /* NB: no empty chunks allowed within the list */
97  assert(chunk->size > chunk->skip);
98  size += chunk->size - chunk->skip;
99  assert(chunk != buf->last || !chunk->next);
100  }
101  assert(size == buf->size);
102  return size;
103 #else
104  return buf ? buf->size : 0;
105 #endif /*_DEBUG && !NDEBUG*/
106 }
107 
108 
109 /* Create a new buffer chunk.
110  * Allocate at least "unit_size" bytes, but no less than "data_size" bytes.
111  * Special case: "data_size" == 0 results in no data storage allocation.
112  */
113 static SBufChunk* s_BUF_AllocChunk(size_t data_size, size_t unit_size)
114 {
115  size_t chunk_size, alloc_size;
116  SBufChunk* chunk;
117  if (!data_size) {
118  chunk_size = sizeof(*chunk);
119  alloc_size = 0;
120  } else {
121  chunk_size = BUF_ALIGN(sizeof(*chunk));
122  alloc_size = ((data_size + unit_size - 1) / unit_size) * unit_size;
123  }
124  assert(!data_size == !alloc_size);
125  if (!(chunk = (SBufChunk*) malloc(chunk_size + alloc_size)))
126  return 0/*failure*/;
127 
128  /* NB: chunk->next is left uninited! */
129  chunk->base = 0;
130  chunk->data = alloc_size ? (char*) chunk + chunk_size : 0;
131  chunk->skip = 0;
132  chunk->size = 0;
133  chunk->extent = alloc_size;
134  return chunk/*success*/;
135 }
136 
137 
138 extern int/*bool*/ BUF_AppendEx(BUF* buf, void* base, size_t alloc_size,
139  void* data, size_t size)
140 {
141  SBufChunk* chunk;
142 
143  if (!size) {
144  if (base)
145  free(base);
146  return 1/*success*/;
147  }
148  if (!data)
149  return 0/*failure*/;
150 
151  /* init the buffer internals, if not init'd yet */
152  if (!*buf && !BUF_SetChunkSize(buf, 0))
153  return 0/*failure*/;
154 
155  if (!(chunk = s_BUF_AllocChunk(0, 0)))
156  return 0/*failure*/;
157 
158  assert(!chunk->data);
159  chunk->next = 0;
160  chunk->base = base;
161  chunk->data = (char*) data;
162  chunk->size = size;
163  chunk->extent = alloc_size;
164 
165  if ((*buf)->last)
166  (*buf)->last->next = chunk;
167  else
168  (*buf)->list = chunk;
169  (*buf)->last = chunk;
170  (*buf)->size += size;
171  return 1/*success*/;
172 }
173 
174 
175 extern int/*bool*/ BUF_Append(BUF* buf, const void* data, size_t size)
176 {
177  return BUF_AppendEx(buf, 0, 0, (void*) data, size);
178 }
179 
180 
181 extern int/*bool*/ BUF_PrependEx(BUF* buf, void* base, size_t alloc_size,
182  void* data, size_t size)
183 {
184  SBufChunk* chunk;
185 
186  if (!size) {
187  if (base)
188  free(base);
189  return 1/*success*/;
190  }
191  if (!data)
192  return 0/*failure*/;
193 
194  /* init the buffer internals, if not init'd yet */
195  if (!*buf && !BUF_SetChunkSize(buf, 0))
196  return 0/*failure*/;
197 
198  if (!(chunk = s_BUF_AllocChunk(0, 0)))
199  return 0/*failure*/;
200 
201  assert(!chunk->data);
202  chunk->next = (*buf)->list;
203  chunk->base = base;
204  chunk->data = (char*) data;
205  chunk->size = size;
206  chunk->extent = alloc_size;
207 
208  if (!(*buf)->last) {
209  assert(!chunk->next);
210  (*buf)->last = chunk;
211  }
212  (*buf)->list = chunk;
213  (*buf)->size += size;
214  return 1/*success*/;
215 }
216 
217 
218 extern int/*bool*/ BUF_Prepend(BUF* buf, const void* data, size_t size)
219 {
220  return BUF_PrependEx(buf, 0, 0, (void*) data, size);
221 }
222 
223 
224 extern int/*bool*/ BUF_Write(BUF* buf, const void* src, size_t size)
225 {
226  SBufChunk* tail;
227  size_t pending;
228 
229  if (!size)
230  return 1/*success*/;
231  if (!src)
232  return 0/*failure*/;
233 
234  /* init the buffer internals, if not init'd yet */
235  if (!*buf && !BUF_SetChunkSize(buf, 0))
236  return 0/*failure*/;
237 
238  /* find the last allocated chunk */
239  tail = (*buf)->last;
240  assert(!(*buf)->list == !tail && !(*buf)->size == !tail);
241 
242  /* schedule to write to an unfilled space of the last allocated chunk */
243  if (tail && tail->extent > tail->size) {
244  pending = tail->extent - tail->size;
245  if (pending > size)
246  pending = size;
247  size -= pending;
248  } else
249  pending = 0;
250 
251  /* if necessary, allocate a new chunk and write remaining data into it */
252  if (size) {
253  SBufChunk* next;
254  if (!(next = s_BUF_AllocChunk(size, (*buf)->unit)))
255  return 0/*false*/;
256  assert(next->data);
257  memcpy(next->data, (const char*) src + pending, size);
258  next->size = size;
259  next->next = 0;
260 
261  /* append the new chunk to the list */
262  if (tail) {
263  tail->next = next;
264  assert( (*buf)->list);
265  } else {
266  assert(!(*buf)->list);
267  (*buf)->list = next;
268  }
269  (*buf)->last = next;
270  }
271 
272  /* finish up with the pending portion of the data (from the beginning) */
273  if (pending) {
274  void* dst = tail->data + tail->size;
275  if (dst != src)
276  memmove(dst, src, pending);
277  tail->size += pending;
278  }
279  (*buf)->size += pending + size;
280  assert((*buf)->size && (*buf)->list && (*buf)->last);
281  return 1/*success*/;
282 }
283 
284 
285 extern int/*bool*/ BUF_Pushback(BUF* buf, const void* src, size_t size)
286 {
287  SBufChunk* head;
288  void* dst;
289 
290  if (!size)
291  return 1/*success*/;
292  if (!src)
293  return 0/*failure*/;
294 
295  /* init the buffer internals, if not init'd yet */
296  if (!*buf && !BUF_SetChunkSize(buf, 0))
297  return 0/*failure*/;
298 
299  head = (*buf)->list;
300 
301  /* allocate and link a new chunk at the beginning of the chunk list */
302  if (!head || !head->extent || size > head->skip) {
303  size_t skip = head && head->extent ? head->skip : 0;
304  SBufChunk* next = head;
305  size -= skip;
306  assert(size);
307  if (!(head = s_BUF_AllocChunk(size, (*buf)->unit)))
308  return 0/*failure*/;
309  if (skip) {
310  /* fill up the skip area */
311  memcpy(next->data, (const char*) src + size, skip);
312  (*buf)->size += skip;
313  next->skip = 0;
314  }
315  head->skip = head->size = head->extent;
316  if (!(head->next = next)) {
317  assert(!(*buf)->last);
318  (*buf)->last = head;
319  } else
320  assert( (*buf)->last);
321  (*buf)->list = head;
322  }
323 
324  /* write remaining data */
325  assert(head->data && head->skip >= size);
326  head->skip -= size;
327  dst = head->data + head->skip;
328  if (dst != src)
329  memmove(dst, src, size);
330  (*buf)->size += size;
331  assert((*buf)->size && (*buf)->list && (*buf)->last);
332  return 1/*success*/;
333 }
334 
335 
336 extern size_t BUF_PeekAtCB(BUF buf,
337  size_t pos,
338  size_t (*callback)(void*, const void*, size_t),
339  void* cbdata,
340  size_t size)
341 {
342  size_t todo;
343  SBufChunk* chunk;
344 
345  assert(!buf || (!buf->list == !buf->last
346  && !buf->size == !(buf->list || buf->last)));
347 
348  if (!size || !buf || buf->size <= pos/*NB: includes !buf->size*/)
349  return 0/*nothing*/;
350  assert(buf->list && buf->last);
351 
352  /* special treatment for NULL callback */
353  if (!callback) {
354  todo = buf->size - pos;
355  return todo < size ? todo : size;
356  }
357 
358  /* skip "pos" bytes, by fast tracking for last chunk first if possible */
359  chunk = buf->last;
360  assert(chunk->size > chunk->skip/*i.e. chunk->size > 0*/);
361  if (pos + (todo = chunk->size - chunk->skip) < buf->size) {
362  for (chunk = buf->list; chunk; chunk = chunk->next) {
363  todo = chunk->size - chunk->skip;
364  assert(chunk->size > chunk->skip/*i.e. chunk->size > 0*/);
365  if (todo > pos)
366  break;
367  pos -= todo;
368  }
369  assert(chunk != buf->last);
370  } else
371  pos -= buf->size - todo;
372 
373  /* process the data being "peeked into" */
374  for (todo = size; todo && chunk; chunk = chunk->next, pos = 0) {
375  size_t skip = chunk->skip + pos;
376  size_t peek = chunk->size - skip;
377  assert(chunk->size > skip/*i.e. chunk->size > 0*/);
378  if (peek > todo)
379  peek = todo;
380  assert(peek);
381  skip = callback(cbdata, (const char*) chunk->data + skip, peek);
382  assert(skip <= peek);
383  todo -= skip;
384  if (skip < peek)
385  break;
386  }
387 
388  assert(size >= todo);
389  return size - todo;
390 }
391 
392 
393 static size_t x_BUF_MemcpyCB(void* cbdata, const void* data, size_t size)
394 {
395  char** dst = (char**) cbdata;
396  memcpy(*dst, data, size);
397  *dst += size;
398  return size;
399 }
400 
401 
402 extern size_t BUF_PeekAt(BUF buf, size_t pos, void* dst, size_t size)
403 {
404  return BUF_PeekAtCB(buf, pos, dst ? x_BUF_MemcpyCB : 0, &dst, size);
405 }
406 
407 
408 extern size_t BUF_Peek(BUF buf, void* dst, size_t size)
409 {
410  return BUF_PeekAt(buf, 0, dst, size);
411 }
412 
413 
414 extern size_t BUF_Read(BUF buf, void* dst, size_t size)
415 {
416  size_t todo = size;
417 
418  assert(!buf || (!buf->list == !buf->last
419  && !buf->size == !(buf->list || buf->last)));
420 
421  /* first, peek to the caller's data buffer, if non-NULL */
422  if (dst)
423  size = BUF_Peek(buf, dst, todo);
424  else if (!buf || !buf->size)
425  return 0/*nothing*/;
426  if (!size)
427  return 0/*nothing*/;
428 
429  /* then safely remove the peeked data (making it "read") from the buffer */
430  assert(size <= todo);
431  todo = size;
432 
433  assert(buf && buf->list && buf->last);
434  do {
435  SBufChunk* head = buf->list;
436  size_t avail = head->size - head->skip;
437  if (todo < avail) {
438  /* discard some of the chunk data */
439  head->skip += todo;
440  buf->size -= todo;
441  todo = 0;
442  break;
443  }
444  /* discard the entire chunk */
445  if (!(buf->list = head->next))
446  buf->last = 0;
447  if (head->base)
448  free(head->base);
449  free(head);
450  buf->size -= avail;
451  todo -= avail;
452  } while (todo && buf->list);
453 
454  assert(!buf->list == !buf->last
455  && !buf->size == !(buf->list || buf->last));
456  assert(size >= todo);
457  return size - todo;
458 }
459 
460 
461 extern void BUF_Erase(BUF buf)
462 {
463  if (buf) {
464  while (buf->list) {
465  SBufChunk* head = buf->list;
466  buf->list = head->next;
467  if (head->base)
468  free(head->base);
469  free(head);
470  }
471  buf->last = 0;
472  buf->size = 0;
473  }
474 }
475 
476 
477 extern int/*bool*/ BUF_Splice(BUF* dst, BUF src)
478 {
479  if (!src || !src->size)
480  return 1/*success*/;
481  assert(src->list && src->last);
482  /* init the buffer internals, if not init'd yet */
483  if (!*dst && !BUF_SetChunkSize(dst, 0))
484  return 0/*failure*/;
485  assert(!(*dst)->list == !(*dst)->last
486  && !(*dst)->size == !((*dst)->list || (*dst)->last));
487  if ((*dst)->last)
488  (*dst)->last->next = src->list;
489  else
490  (*dst)->list = src->list;
491  (*dst)->last = src->last;
492  (*dst)->size += src->size;
493  src->list = src->last = 0;
494  src->size = 0;
495  assert((*dst)->size && (*dst)->list && (*dst)->last);
496  return 1/*success*/;
497 }
498 
499 
500 extern void BUF_Destroy(BUF buf)
501 {
502  if (buf) {
503  BUF_Erase(buf);
504  free(buf);
505  }
506 }
static const int chunk_size
#define head
Definition: ct_nlmzip_i.h:138
static DLIST_TYPE *DLIST_NAME() next(DLIST_LIST_TYPE *list, DLIST_TYPE *item)
Definition: dlist.tmpl.h:56
char data[12]
Definition: iconv.c:80
void BUF_Erase(BUF buf)
Definition: ncbi_buffer.c:461
int BUF_Write(BUF *buf, const void *src, size_t size)
Definition: ncbi_buffer.c:224
int BUF_Append(BUF *buf, const void *data, size_t size)
Definition: ncbi_buffer.c:175
int BUF_Prepend(BUF *buf, const void *data, size_t size)
Definition: ncbi_buffer.c:218
size_t BUF_PeekAt(BUF buf, size_t pos, void *dst, size_t size)
Definition: ncbi_buffer.c:402
size_t BUF_Size(BUF buf)
Definition: ncbi_buffer.c:84
size_t BUF_SetChunkSize(BUF *buf, size_t chunk_size)
Definition: ncbi_buffer.c:65
int BUF_PrependEx(BUF *buf, void *base, size_t alloc_size, void *data, size_t size)
Definition: ncbi_buffer.c:181
int BUF_Pushback(BUF *buf, const void *src, size_t size)
Definition: ncbi_buffer.c:285
int BUF_AppendEx(BUF *buf, void *base, size_t alloc_size, void *data, size_t size)
Definition: ncbi_buffer.c:138
size_t BUF_Read(BUF buf, void *dst, size_t size)
Definition: ncbi_buffer.c:414
int BUF_Splice(BUF *dst, BUF src)
Definition: ncbi_buffer.c:477
size_t BUF_Peek(BUF buf, void *dst, size_t size)
Definition: ncbi_buffer.c:408
void BUF_Destroy(BUF buf)
Definition: ncbi_buffer.c:500
char * buf
const struct ncbi::grid::netcache::search::fields::SIZE size
#define BUF_ALIGN(s)
Definition: ncbi_buffer.c:40
static size_t x_BUF_MemcpyCB(void *cbdata, const void *data, size_t size)
Definition: ncbi_buffer.c:393
struct SBufChunkTag SBufChunk
static SBufChunk * s_BUF_AllocChunk(size_t data_size, size_t unit_size)
Definition: ncbi_buffer.c:113
size_t BUF_PeekAtCB(BUF buf, size_t pos, size_t(*callback)(void *, const void *, size_t), void *cbdata, size_t size)
Definition: ncbi_buffer.c:336
#define BUF_DEF_CHUNK_SIZE
Definition: ncbi_buffer.h:57
#define memmove(a, b, c)
#define assert(x)
Definition: srv_diag.hpp:58
size_t extent
Definition: ncbi_buffer.c:51
struct SBufChunkTag * next
Definition: ncbi_buffer.c:46
size_t size
Definition: ncbi_buffer.c:50
char * data
Definition: ncbi_buffer.c:48
size_t skip
Definition: ncbi_buffer.c:49
void * base
Definition: ncbi_buffer.c:47
SBufChunk * list
Definition: ncbi_buffer.c:58
SBufChunk * last
Definition: ncbi_buffer.c:59
size_t unit
Definition: ncbi_buffer.c:60
size_t size
Definition: ncbi_buffer.c:61
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Fri Sep 20 14:58:13 2024 by modify_doxy.py rev. 669887