/src/leptonica/src/bbuffer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file bbuffer.c |
29 | | * <pre> |
30 | | * |
31 | | * Create/Destroy BBuffer |
32 | | * L_BBUFFER *bbufferCreate() |
33 | | * void *bbufferDestroy() |
34 | | * l_uint8 *bbufferDestroyAndSaveData() |
35 | | * |
36 | | * Operations to read data TO a BBuffer |
37 | | * l_int32 bbufferRead() |
38 | | * l_int32 bbufferReadStream() |
39 | | * l_int32 bbufferExtendArray() |
40 | | * |
41 | | * Operations to write data FROM a BBuffer |
42 | | * l_int32 bbufferWrite() |
43 | | * l_int32 bbufferWriteStream() |
44 | | * |
45 | | * The bbuffer is an implementation of a byte queue. |
46 | | * The bbuffer holds a byte array from which bytes are |
47 | | * processed in a first-in/first-out fashion. As with |
48 | | * any queue, bbuffer maintains two "pointers," one to the |
49 | | * tail of the queue (where you read new bytes onto it) |
50 | | * and one to the head of the queue (where you start from |
51 | | * when writing bytes out of it. |
52 | | * |
53 | | * The queue can be visualized: |
54 | | * |
55 | | * \code |
56 | | * byte 0 byte (nalloc - 1) |
57 | | * | | |
58 | | * -------------------------------------------------- |
59 | | * H T |
60 | | * [ aw ][ bytes currently on queue ][ anr ] |
61 | | * |
62 | | * ---: all allocated data in bbuffer |
63 | | * H: queue head (ptr to next byte to be written out) |
64 | | * T: queue tail (ptr to first byte to be written to) |
65 | | * aw: already written from queue |
66 | | * anr: allocated but not yet read to |
67 | | * \endcode |
68 | | * The purpose of bbuffer is to allow you to safely read |
69 | | * bytes in, and to sequentially write them out as well. |
70 | | * In the process of writing bytes out, you don't actually |
71 | | * remove the bytes in the array; you just move the pointer |
72 | | * (nwritten) which points to the head of the queue. In |
73 | | * the process of reading bytes in, you sometimes need to |
74 | | * expand the array size. If a read is performed after a |
75 | | * write, so that the head of the queue is not at the |
76 | | * beginning of the array, the bytes already written are |
77 | | * first removed by copying the others over them; then the |
78 | | * new bytes are read onto the tail of the queue. |
79 | | * |
80 | | * Note that the meaning of "read into" and "write from" |
81 | | * the bbuffer is OPPOSITE to that for a stream, where |
82 | | * you read "from" a stream and write "into" a stream. |
83 | | * As a mnemonic for remembering the direction: |
84 | | * ~ to read bytes from a stream into the bbuffer, |
85 | | * you call fread on the stream |
86 | | * ~ to write bytes from the bbuffer into a stream, |
87 | | * you call fwrite on the stream |
88 | | * |
89 | | * See zlibmem.c for an example use of bbuffer, where we |
90 | | * compress and decompress an array of bytes in memory. |
91 | | * |
92 | | * We can also use the bbuffer trivially to read from stdin |
93 | | * into memory; e.g., to capture bytes piped from the stdout |
94 | | * of another program. This is equivalent to repeatedly |
95 | | * calling bbufferReadStream() until the input queue is empty. |
96 | | * This is implemented in l_binaryReadStream(). |
97 | | * </pre> |
98 | | */ |
99 | | |
100 | | #ifdef HAVE_CONFIG_H |
101 | | #include <config_auto.h> |
102 | | #endif /* HAVE_CONFIG_H */ |
103 | | |
104 | | #include <string.h> |
105 | | #include "allheaders.h" |
106 | | |
107 | | /* Bounds on array size */ |
108 | | static const l_uint32 MaxArraySize = 1000000000; /* 10^9 bytes */ |
109 | | static const l_int32 InitialArraySize = 1024; /*!< n'importe quoi */ |
110 | | |
111 | | /*--------------------------------------------------------------------------* |
112 | | * BBuffer create/destroy * |
113 | | *--------------------------------------------------------------------------*/ |
114 | | /*! |
115 | | * \brief bbufferCreate() |
116 | | * |
117 | | * \param[in] indata address in memory [optional] |
118 | | * \param[in] nalloc size of byte array to be alloc'd 0 for default |
119 | | * \return bbuffer, or NULL on error |
120 | | * |
121 | | * <pre> |
122 | | * Notes: |
123 | | * (1) If a buffer address is given, you should read all the data in. |
124 | | * (2) Allocates a bbuffer with associated byte array of |
125 | | * the given size. If a buffer address is given, |
126 | | * it then reads the number of bytes into the byte array. |
127 | | * </pre> |
128 | | */ |
129 | | L_BBUFFER * |
130 | | bbufferCreate(const l_uint8 *indata, |
131 | | l_int32 nalloc) |
132 | 0 | { |
133 | 0 | L_BBUFFER *bb; |
134 | |
|
135 | 0 | if (nalloc <= 0 || nalloc > MaxArraySize) |
136 | 0 | nalloc = InitialArraySize; |
137 | |
|
138 | 0 | bb = (L_BBUFFER *)LEPT_CALLOC(1, sizeof(L_BBUFFER)); |
139 | 0 | if ((bb->array = (l_uint8 *)LEPT_CALLOC(nalloc, sizeof(l_uint8))) == NULL) { |
140 | 0 | LEPT_FREE(bb); |
141 | 0 | return (L_BBUFFER *)ERROR_PTR("byte array not made", __func__, NULL); |
142 | 0 | } |
143 | 0 | bb->nalloc = nalloc; |
144 | 0 | bb->nwritten = 0; |
145 | |
|
146 | 0 | if (indata) { |
147 | 0 | memcpy(bb->array, indata, nalloc); |
148 | 0 | bb->n = nalloc; |
149 | 0 | } else { |
150 | 0 | bb->n = 0; |
151 | 0 | } |
152 | |
|
153 | 0 | return bb; |
154 | 0 | } |
155 | | |
156 | | |
157 | | /*! |
158 | | * \brief bbufferDestroy() |
159 | | * |
160 | | * \param[in,out] pbb will be set to null before returning |
161 | | * \return void |
162 | | * |
163 | | * <pre> |
164 | | * Notes: |
165 | | * (1) Destroys the byte array in the bbuffer and then the bbuffer; |
166 | | * then nulls the contents of the input ptr. |
167 | | * </pre> |
168 | | */ |
169 | | void |
170 | | bbufferDestroy(L_BBUFFER **pbb) |
171 | 0 | { |
172 | 0 | L_BBUFFER *bb; |
173 | |
|
174 | 0 | if (pbb == NULL) { |
175 | 0 | L_WARNING("ptr address is NULL\n", __func__); |
176 | 0 | return; |
177 | 0 | } |
178 | | |
179 | 0 | if ((bb = *pbb) == NULL) |
180 | 0 | return; |
181 | | |
182 | 0 | if (bb->array) |
183 | 0 | LEPT_FREE(bb->array); |
184 | 0 | LEPT_FREE(bb); |
185 | 0 | *pbb = NULL; |
186 | 0 | } |
187 | | |
188 | | |
189 | | /*! |
190 | | * \brief bbufferDestroyAndSaveData() |
191 | | * |
192 | | * \param[in,out] pbb input data buffer; will be nulled |
193 | | * \param[out] pnbytes number of bytes saved in array |
194 | | * \return barray newly allocated array of data |
195 | | * |
196 | | * <pre> |
197 | | * Notes: |
198 | | * (1) Copies data to newly allocated array; then destroys the bbuffer. |
199 | | * </pre> |
200 | | */ |
201 | | l_uint8 * |
202 | | bbufferDestroyAndSaveData(L_BBUFFER **pbb, |
203 | | size_t *pnbytes) |
204 | 0 | { |
205 | 0 | l_uint8 *array; |
206 | 0 | size_t nbytes; |
207 | 0 | L_BBUFFER *bb; |
208 | |
|
209 | 0 | if (pbb == NULL) { |
210 | 0 | L_WARNING("ptr address is NULL\n", __func__); |
211 | 0 | return NULL; |
212 | 0 | } |
213 | 0 | if (pnbytes == NULL) { |
214 | 0 | L_WARNING("&nbytes is NULL\n", __func__); |
215 | 0 | bbufferDestroy(pbb); |
216 | 0 | return NULL; |
217 | 0 | } |
218 | | |
219 | 0 | if ((bb = *pbb) == NULL) |
220 | 0 | return NULL; |
221 | | |
222 | | /* write all unwritten bytes out to a new array */ |
223 | 0 | nbytes = bb->n - bb->nwritten; |
224 | 0 | *pnbytes = nbytes; |
225 | 0 | if ((array = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) { |
226 | 0 | L_WARNING("calloc failure for array\n", __func__); |
227 | 0 | return NULL; |
228 | 0 | } |
229 | 0 | memcpy(array, bb->array + bb->nwritten, nbytes); |
230 | |
|
231 | 0 | bbufferDestroy(pbb); |
232 | 0 | return array; |
233 | 0 | } |
234 | | |
235 | | |
236 | | /*--------------------------------------------------------------------------* |
237 | | * Operations to read data INTO a BBuffer * |
238 | | *--------------------------------------------------------------------------*/ |
239 | | /*! |
240 | | * \brief bbufferRead() |
241 | | * |
242 | | * \param[in] bb bbuffer |
243 | | * \param[in] src source memory buffer from which bytes are read |
244 | | * \param[in] nbytes bytes to be read |
245 | | * \return 0 if OK, 1 on error |
246 | | * |
247 | | * <pre> |
248 | | * Notes: |
249 | | * (1) For a read after write, first remove the written |
250 | | * bytes by shifting the unwritten bytes in the array, |
251 | | * then check if there is enough room to add the new bytes. |
252 | | * If not, realloc with bbufferExpandArray(), resulting |
253 | | * in a second writing of the unwritten bytes. While less |
254 | | * efficient, this is simpler than making a special case |
255 | | * of reallocNew(). |
256 | | * </pre> |
257 | | */ |
258 | | l_ok |
259 | | bbufferRead(L_BBUFFER *bb, |
260 | | l_uint8 *src, |
261 | | l_int32 nbytes) |
262 | 0 | { |
263 | 0 | l_int32 navail, nadd, nwritten; |
264 | |
|
265 | 0 | if (!bb) |
266 | 0 | return ERROR_INT("bb not defined", __func__, 1); |
267 | 0 | if (!src) |
268 | 0 | return ERROR_INT("src not defined", __func__, 1); |
269 | 0 | if (nbytes == 0) |
270 | 0 | return ERROR_INT("no bytes to read", __func__, 1); |
271 | | |
272 | 0 | if ((nwritten = bb->nwritten)) { /* move the unwritten bytes over */ |
273 | 0 | memmove(bb->array, bb->array + nwritten, bb->n - nwritten); |
274 | 0 | bb->nwritten = 0; |
275 | 0 | bb->n -= nwritten; |
276 | 0 | } |
277 | | |
278 | | /* If necessary, expand the allocated array. Do so by |
279 | | * by at least a factor of two. */ |
280 | 0 | navail = bb->nalloc - bb->n; |
281 | 0 | if (nbytes > navail) { |
282 | 0 | nadd = L_MAX(bb->nalloc, nbytes); |
283 | 0 | if (bbufferExtendArray(bb, nadd)) |
284 | 0 | return ERROR_INT("extension failed", __func__, 1); |
285 | 0 | } |
286 | | |
287 | | /* Read in the new bytes */ |
288 | 0 | memcpy(bb->array + bb->n, src, nbytes); |
289 | 0 | bb->n += nbytes; |
290 | 0 | return 0; |
291 | 0 | } |
292 | | |
293 | | |
294 | | /*! |
295 | | * \brief bbufferReadStream() |
296 | | * |
297 | | * \param[in] bb bbuffer |
298 | | * \param[in] fp source stream from which bytes are read |
299 | | * \param[in] nbytes bytes to be read |
300 | | * \return 0 if OK, 1 on error |
301 | | */ |
302 | | l_ok |
303 | | bbufferReadStream(L_BBUFFER *bb, |
304 | | FILE *fp, |
305 | | l_int32 nbytes) |
306 | 0 | { |
307 | 0 | l_int32 navail, nadd, nread, nwritten; |
308 | |
|
309 | 0 | if (!bb) |
310 | 0 | return ERROR_INT("bb not defined", __func__, 1); |
311 | 0 | if (!fp) |
312 | 0 | return ERROR_INT("fp not defined", __func__, 1); |
313 | 0 | if (nbytes == 0) |
314 | 0 | return ERROR_INT("no bytes to read", __func__, 1); |
315 | | |
316 | 0 | if ((nwritten = bb->nwritten)) { /* move any unwritten bytes over */ |
317 | 0 | memmove(bb->array, bb->array + nwritten, bb->n - nwritten); |
318 | 0 | bb->nwritten = 0; |
319 | 0 | bb->n -= nwritten; |
320 | 0 | } |
321 | | |
322 | | /* If necessary, expand the allocated array. Do so by |
323 | | * by at least a factor of two. */ |
324 | 0 | navail = bb->nalloc - bb->n; |
325 | 0 | if (nbytes > navail) { |
326 | 0 | nadd = L_MAX(bb->nalloc, nbytes); |
327 | 0 | if (bbufferExtendArray(bb, nadd)) |
328 | 0 | return ERROR_INT("extension failed", __func__, 1); |
329 | 0 | } |
330 | | |
331 | | /* Read in the new bytes */ |
332 | 0 | nread = fread(bb->array + bb->n, 1, nbytes, fp); |
333 | 0 | bb->n += nread; |
334 | |
|
335 | 0 | return 0; |
336 | 0 | } |
337 | | |
338 | | |
339 | | /*! |
340 | | * \brief bbufferExtendArray() |
341 | | * |
342 | | * \param[in] bb bbuffer |
343 | | * \param[in] nbytes number of bytes to extend array size |
344 | | * \return 0 if OK, 1 on error |
345 | | * |
346 | | * <pre> |
347 | | * Notes: |
348 | | * (1) reallocNew() copies all bb->nalloc bytes, even though |
349 | | * only bb->n are data. |
350 | | * </pre> |
351 | | */ |
352 | | l_ok |
353 | | bbufferExtendArray(L_BBUFFER *bb, |
354 | | l_int32 nbytes) |
355 | 0 | { |
356 | 0 | if (!bb) |
357 | 0 | return ERROR_INT("bb not defined", __func__, 1); |
358 | | |
359 | 0 | if ((bb->array = (l_uint8 *)reallocNew((void **)&bb->array, |
360 | 0 | bb->nalloc, |
361 | 0 | bb->nalloc + nbytes)) == NULL) |
362 | 0 | return ERROR_INT("new ptr array not returned", __func__, 1); |
363 | | |
364 | 0 | bb->nalloc += nbytes; |
365 | 0 | return 0; |
366 | 0 | } |
367 | | |
368 | | |
369 | | /*--------------------------------------------------------------------------* |
370 | | * Operations to write data FROM a BBuffer * |
371 | | *--------------------------------------------------------------------------*/ |
372 | | /*! |
373 | | * \brief bbufferWrite() |
374 | | * |
375 | | * \param[in] bb bbuffer |
376 | | * \param[in] dest dest memory buffer to which bytes are written |
377 | | * \param[in] nbytes bytes requested to be written |
378 | | * \param[out] pnout bytes actually written |
379 | | * \return 0 if OK, 1 on error |
380 | | */ |
381 | | l_ok |
382 | | bbufferWrite(L_BBUFFER *bb, |
383 | | l_uint8 *dest, |
384 | | size_t nbytes, |
385 | | size_t *pnout) |
386 | 0 | { |
387 | 0 | size_t nleft, nout; |
388 | |
|
389 | 0 | if (!bb) |
390 | 0 | return ERROR_INT("bb not defined", __func__, 1); |
391 | 0 | if (!dest) |
392 | 0 | return ERROR_INT("dest not defined", __func__, 1); |
393 | 0 | if (nbytes <= 0) |
394 | 0 | return ERROR_INT("no bytes requested to write", __func__, 1); |
395 | 0 | if (!pnout) |
396 | 0 | return ERROR_INT("&nout not defined", __func__, 1); |
397 | | |
398 | 0 | nleft = bb->n - bb->nwritten; |
399 | 0 | nout = L_MIN(nleft, nbytes); |
400 | 0 | *pnout = nout; |
401 | |
|
402 | 0 | if (nleft == 0) { /* nothing to write; reinitialize the buffer */ |
403 | 0 | bb->n = 0; |
404 | 0 | bb->nwritten = 0; |
405 | 0 | return 0; |
406 | 0 | } |
407 | | |
408 | | /* nout > 0; transfer the data out */ |
409 | 0 | memcpy(dest, bb->array + bb->nwritten, nout); |
410 | 0 | bb->nwritten += nout; |
411 | | |
412 | | /* If all written; "empty" the buffer */ |
413 | 0 | if (nout == nleft) { |
414 | 0 | bb->n = 0; |
415 | 0 | bb->nwritten = 0; |
416 | 0 | } |
417 | |
|
418 | 0 | return 0; |
419 | 0 | } |
420 | | |
421 | | |
422 | | /*! |
423 | | * \brief bbufferWriteStream() |
424 | | * |
425 | | * \param[in] bb bbuffer |
426 | | * \param[in] fp dest stream to which bytes are written |
427 | | * \param[in] nbytes bytes requested to be written |
428 | | * \param[out] pnout bytes actually written |
429 | | * \return 0 if OK, 1 on error |
430 | | */ |
431 | | l_ok |
432 | | bbufferWriteStream(L_BBUFFER *bb, |
433 | | FILE *fp, |
434 | | size_t nbytes, |
435 | | size_t *pnout) |
436 | 0 | { |
437 | 0 | size_t nleft, nout; |
438 | |
|
439 | 0 | if (!bb) |
440 | 0 | return ERROR_INT("bb not defined", __func__, 1); |
441 | 0 | if (!fp) |
442 | 0 | return ERROR_INT("output stream not defined", __func__, 1); |
443 | 0 | if (nbytes <= 0) |
444 | 0 | return ERROR_INT("no bytes requested to write", __func__, 1); |
445 | 0 | if (!pnout) |
446 | 0 | return ERROR_INT("&nout not defined", __func__, 1); |
447 | | |
448 | 0 | nleft = bb->n - bb->nwritten; |
449 | 0 | nout = L_MIN(nleft, nbytes); |
450 | 0 | *pnout = nout; |
451 | |
|
452 | 0 | if (nleft == 0) { /* nothing to write; reinitialize the buffer */ |
453 | 0 | bb->n = 0; |
454 | 0 | bb->nwritten = 0; |
455 | 0 | return 0; |
456 | 0 | } |
457 | | |
458 | | /* nout > 0; transfer the data out */ |
459 | 0 | fwrite(bb->array + bb->nwritten, 1, nout, fp); |
460 | 0 | bb->nwritten += nout; |
461 | | |
462 | | /* If all written; "empty" the buffer */ |
463 | 0 | if (nout == nleft) { |
464 | 0 | bb->n = 0; |
465 | 0 | bb->nwritten = 0; |
466 | 0 | } |
467 | |
|
468 | 0 | return 0; |
469 | 0 | } |