1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - This software is distributed in the hope that it will be
4 - useful, but with NO WARRANTY OF ANY KIND.
5 - No author or distributor accepts responsibility to anyone for the
6 - consequences of using this software, or for whether it serves any
7 - particular purpose or works at all, unless he or she says so in
8 - writing. Everyone is granted permission to copy, modify and
9 - redistribute this source code, for commercial or non-commercial
10 - purposes, with the following restrictions: (1) the origin of this
11 - source code must not be misrepresented; (2) modified versions must
12 - be plainly marked as such; and (3) this notice may not be removed
13 - or altered from any source or modified source distribution.
14 *====================================================================*/
15
16 /*
17 * bbuffer.c
18 *
19 * Create/Destroy BBuffer
20 * BBUFFER *bbufferCreate()
21 * void *bbufferDestroy()
22 * l_uint8 *bbufferDestroyAndSaveData()
23 *
24 * Operations to read data TO a BBuffer
25 * l_int32 bbufferRead()
26 * l_int32 bbufferReadStream()
27 * l_int32 bbufferExtendArray()
28 *
29 * Operations to write data FROM a BBuffer
30 * l_int32 bbufferWrite()
31 * l_int32 bbufferWriteStream()
32 *
33 * Accessors
34 * l_int32 bbufferBytesToWrite()
35 *
36 *
37 * The bbuffer is an implementation of a byte queue.
38 * The bbuffer holds a byte array from which bytes are
39 * processed in a first-in/first-out fashion. As with
40 * any queue, bbuffer maintains two "pointers," one to the
41 * tail of the queue (where you read new bytes onto it)
42 * and one to the head of the queue (where you start from
43 * when writing bytes out of it.
44 *
45 * The queue can be visualized:
46 *
47 *
48 * byte 0 byte (nalloc - 1)
49 * | |
50 * --------------------------------------------------
51 * H T
52 * [ aw ][ bytes currently on queue ][ anr ]
53 *
54 * ---: all allocated data in bbuffer
55 * H: queue head (ptr to next byte to be written out)
56 * T: queue tail (ptr to first byte to be written to)
57 * aw: already written from queue
58 * anr: allocated but not yet read to
59 *
60 * The purpose of bbuffer is to allow you to safely read
61 * bytes in, and to sequentially write them out as well.
62 * In the process of writing bytes out, you don't actually
63 * remove the bytes in the array; you just move the pointer
64 * (nwritten) which points to the head of the queue. In
65 * the process of reading bytes in, you sometimes need to
66 * expand the array size. If a read is performed after a
67 * write, so that the head of the queue is not at the
68 * beginning of the array, the bytes already written are
69 * first removed by copying the others over them; then the
70 * new bytes are read onto the tail of the queue.
71 *
72 * Note that the meaning of "read into" and "write from"
73 * the bbuffer is OPPOSITE to that for a stream, where
74 * you read "from" a stream and write "into" a stream.
75 * As a mnemonic for remembering the direction:
76 * - to read bytes from a stream into the bbuffer,
77 * you call fread on the stream
78 * - to write bytes from the bbuffer into a stream,
79 * you call fwrite on the stream
80 *
81 * See zlibmem.c for an example use of bbuffer, where we
82 * compress and decompress an array of bytes in memory.
83 */
84
85 #include <stdio.h>
86 #include <string.h>
87 #include <stdlib.h>
88 #include "allheaders.h"
89
90 static const l_int32 INITIAL_BUFFER_ARRAYSIZE = 1024; /* n'importe quoi */
91
92
93 /*--------------------------------------------------------------------------*
94 * BBuffer create/destroy *
95 *--------------------------------------------------------------------------*/
96 /*!
97 * bbufferCreate()
98 *
99 * Input: buffer address in memory (<optional>)
100 * size of byte array to be alloc'd (0 for default)
101 * Return: bbuffer, or null on error
102 *
103 * Notes:
104 * (1) If a buffer address is given, you should read all the data in.
105 * (2) Allocates a bbuffer with associated byte array of
106 * the given size. If a buffer address is given,
107 * it then reads the number of bytes into the byte array.
108 */
109 BBUFFER *
bbufferCreate(l_uint8 * indata,l_int32 nalloc)110 bbufferCreate(l_uint8 *indata,
111 l_int32 nalloc)
112 {
113 BBUFFER *bb;
114
115 PROCNAME("bbufferCreate");
116
117 if (nalloc <= 0)
118 nalloc = INITIAL_BUFFER_ARRAYSIZE;
119
120 if ((bb = (BBUFFER *)CALLOC(1, sizeof(BBUFFER))) == NULL)
121 return (BBUFFER *)ERROR_PTR("bb not made", procName, NULL);
122 if ((bb->array = (l_uint8 *)CALLOC(nalloc, sizeof(l_uint8))) == NULL)
123 return (BBUFFER *)ERROR_PTR("byte array not made", procName, NULL);
124 bb->nalloc = nalloc;
125 bb->nwritten = 0;
126
127 if (indata) {
128 memcpy((l_uint8 *)bb->array, indata, nalloc);
129 bb->n = nalloc;
130 }
131 else
132 bb->n = 0;
133
134 return bb;
135 }
136
137
138 /*!
139 * bbufferDestroy()
140 *
141 * Input: &bbuffer (<to be nulled>)
142 * Return: void
143 *
144 * Notes:
145 * (1) Destroys the byte array in the bbuffer and then the bbuffer;
146 * then nulls the contents of the input ptr.
147 */
148 void
bbufferDestroy(BBUFFER ** pbb)149 bbufferDestroy(BBUFFER **pbb)
150 {
151 BBUFFER *bb;
152
153 PROCNAME("bbufferDestroy");
154
155 if (pbb == NULL) {
156 L_WARNING("ptr address is NULL", procName);
157 return;
158 }
159
160 if ((bb = *pbb) == NULL)
161 return;
162
163 if (bb->array)
164 FREE(bb->array);
165 FREE(bb);
166 *pbb = NULL;
167
168 return;
169 }
170
171
172 /*!
173 * bbufferDestroyAndSaveData()
174 *
175 * Input: &bbuffer (<to be nulled>)
176 * &nbytes (<return> number of bytes saved in array)
177 * Return: barray (newly allocated array of data)
178 *
179 * Notes:
180 * (1) Copies data to newly allocated array; then destroys the bbuffer.
181 */
182 l_uint8 *
bbufferDestroyAndSaveData(BBUFFER ** pbb,l_int32 * pnbytes)183 bbufferDestroyAndSaveData(BBUFFER **pbb,
184 l_int32 *pnbytes)
185 {
186 l_uint8 *array;
187 l_int32 nbytes;
188 BBUFFER *bb;
189
190 PROCNAME("bbufferDestroyAndSaveData");
191
192 if (pbb == NULL) {
193 L_WARNING("ptr address is NULL", procName);
194 return NULL;
195 }
196 if (pnbytes == NULL) {
197 L_WARNING("&nbytes is NULL", procName);
198 bbufferDestroy(pbb);
199 return NULL;
200 }
201
202 if ((bb = *pbb) == NULL)
203 return NULL;
204
205 /* write all unwritten bytes out to a new array */
206 nbytes = bb->n - bb->nwritten;
207 *pnbytes = nbytes;
208 if ((array = (l_uint8 *)CALLOC(nbytes, sizeof(l_uint8))) == NULL) {
209 L_WARNING("calloc failure for array", procName);
210 return NULL;
211 }
212 memcpy((void *)array, (void *)(bb->array + bb->nwritten), nbytes);
213
214 bbufferDestroy(pbb);
215 return array;
216 }
217
218
219
220 /*--------------------------------------------------------------------------*
221 * Operations to read data INTO a BBuffer *
222 *--------------------------------------------------------------------------*/
223 /*!
224 * bbufferRead()
225 *
226 * Input: bbuffer
227 * src (source memory buffer from which bytes are read)
228 * nbytes (bytes to be read)
229 * Return: 0 if OK, 1 on error
230 *
231 * Notes:
232 * (1) For a read after write, first remove the written
233 * bytes by shifting the unwritten bytes in the array,
234 * then check if there is enough room to add the new bytes.
235 * If not, realloc with bbufferExpandArray(), resulting
236 * in a second writing of the unwritten bytes. While less
237 * efficient, this is simpler than making a special case
238 * of reallocNew().
239 */
240 l_int32
bbufferRead(BBUFFER * bb,l_uint8 * src,l_int32 nbytes)241 bbufferRead(BBUFFER *bb,
242 l_uint8 *src,
243 l_int32 nbytes)
244 {
245 l_int32 navail, nadd, nwritten;
246
247 PROCNAME("bbufferRead");
248
249 if (!bb)
250 return ERROR_INT("bb not defined", procName, 1);
251 if (!src)
252 return ERROR_INT("src not defined", procName, 1);
253 if (nbytes == 0)
254 return ERROR_INT("no bytes to read", procName, 1);
255
256 if ((nwritten = bb->nwritten)) { /* move the unwritten bytes over */
257 memmove((l_uint8 *)bb->array, (l_uint8 *)(bb->array + nwritten),
258 bb->n - nwritten);
259 bb->nwritten = 0;
260 bb->n -= nwritten;
261 }
262
263 /* If necessary, expand the allocated array. Do so by
264 * by at least a factor of two. */
265 navail = bb->nalloc - bb->n;
266 if (nbytes > navail) {
267 nadd = L_MAX(bb->nalloc, nbytes);
268 bbufferExtendArray(bb, nadd);
269 }
270
271 /* Read in the new bytes */
272 memcpy((l_uint8 *)(bb->array + bb->n), src, nbytes);
273 bb->n += nbytes;
274
275 return 0;
276 }
277
278
279 /*!
280 * bbufferReadStream()
281 *
282 * Input: bbuffer
283 * fp (source stream from which bytes are read)
284 * nbytes (bytes to be read)
285 * Return: 0 if OK, 1 on error
286 */
287 l_int32
bbufferReadStream(BBUFFER * bb,FILE * fp,l_int32 nbytes)288 bbufferReadStream(BBUFFER *bb,
289 FILE *fp,
290 l_int32 nbytes)
291 {
292 l_int32 navail, nadd, nread, nwritten;
293
294 PROCNAME("bbufferReadStream");
295
296 if (!bb)
297 return ERROR_INT("bb not defined", procName, 1);
298 if (!fp)
299 return ERROR_INT("fp not defined", procName, 1);
300 if (nbytes == 0)
301 return ERROR_INT("no bytes to read", procName, 1);
302
303 if ((nwritten = bb->nwritten)) { /* move any unwritten bytes over */
304 memmove((l_uint8 *)bb->array, (l_uint8 *)(bb->array + nwritten),
305 bb->n - nwritten);
306 bb->nwritten = 0;
307 bb->n -= nwritten;
308 }
309
310 /* If necessary, expand the allocated array. Do so by
311 * by at least a factor of two. */
312 navail = bb->nalloc - bb->n;
313 if (nbytes > navail) {
314 nadd = L_MAX(bb->nalloc, nbytes);
315 bbufferExtendArray(bb, nadd);
316 }
317
318 /* Read in the new bytes */
319 nread = fread((void *)(bb->array + bb->n), 1, nbytes, fp);
320 bb->n += nread;
321
322 return 0;
323 }
324
325
326 /*!
327 * bbufferExtendArray()
328 *
329 * Input: bbuffer
330 * nbytes (number of bytes to extend array size)
331 * Return: 0 if OK, 1 on error
332 *
333 * Notes:
334 * (1) reallocNew() copies all bb->nalloc bytes, even though
335 * only bb->n are data.
336 */
337 l_int32
bbufferExtendArray(BBUFFER * bb,l_int32 nbytes)338 bbufferExtendArray(BBUFFER *bb,
339 l_int32 nbytes)
340 {
341 PROCNAME("bbufferExtendArray");
342
343 if (!bb)
344 return ERROR_INT("bb not defined", procName, 1);
345
346 if ((bb->array = (l_uint8 *)reallocNew((void **)&bb->array,
347 bb->nalloc,
348 bb->nalloc + nbytes)) == NULL)
349 return ERROR_INT("new ptr array not returned", procName, 1);
350
351 bb->nalloc += nbytes;
352 return 0;
353 }
354
355
356
357 /*--------------------------------------------------------------------------*
358 * Operations to write data FROM a BBuffer *
359 *--------------------------------------------------------------------------*/
360 /*!
361 * bbufferWrite()
362 *
363 * Input: bbuffer
364 * dest (dest memory buffer to which bytes are written)
365 * nbytes (bytes requested to be written)
366 * &nout (<return> bytes actually written)
367 * Return: 0 if OK, 1 on error
368 */
369 l_int32
bbufferWrite(BBUFFER * bb,l_uint8 * dest,l_int32 nbytes,l_int32 * pnout)370 bbufferWrite(BBUFFER *bb,
371 l_uint8 *dest,
372 l_int32 nbytes,
373 l_int32 *pnout)
374 {
375 l_int32 nleft, nout;
376
377 PROCNAME("bbufferWrite");
378
379 if (!bb)
380 return ERROR_INT("bb not defined", procName, 1);
381 if (!dest)
382 return ERROR_INT("dest not defined", procName, 1);
383 if (nbytes <= 0)
384 return ERROR_INT("no bytes requested to write", procName, 1);
385 if (!pnout)
386 return ERROR_INT("&nout not defined", procName, 1);
387
388 nleft = bb->n - bb->nwritten;
389 nout = L_MIN(nleft, nbytes);
390 *pnout = nout;
391
392 if (nleft == 0) { /* nothing to write; reinitialize the buffer */
393 bb->n = 0;
394 bb->nwritten = 0;
395 return 0;
396 }
397
398 /* nout > 0; transfer the data out */
399 memcpy(dest, (l_uint8 *)(bb->array + bb->nwritten), nout);
400 bb->nwritten += nout;
401
402 /* If all written; "empty" the buffer */
403 if (nout == nleft) {
404 bb->n = 0;
405 bb->nwritten = 0;
406 }
407
408 return 0;
409 }
410
411
412 /*!
413 * bbufferWriteStream()
414 *
415 * Input: bbuffer
416 * fp (dest stream to which bytes are written)
417 * nbytes (bytes requested to be written)
418 * &nout (<return> bytes actually written)
419 * Return: 0 if OK, 1 on error
420 */
421 l_int32
bbufferWriteStream(BBUFFER * bb,FILE * fp,l_int32 nbytes,l_int32 * pnout)422 bbufferWriteStream(BBUFFER *bb,
423 FILE *fp,
424 l_int32 nbytes,
425 l_int32 *pnout)
426 {
427 l_int32 nleft, nout;
428
429 PROCNAME("bbufferWriteStream");
430
431 if (!bb)
432 return ERROR_INT("bb not defined", procName, 1);
433 if (!fp)
434 return ERROR_INT("output stream not defined", procName, 1);
435 if (nbytes <= 0)
436 return ERROR_INT("no bytes requested to write", procName, 1);
437 if (!pnout)
438 return ERROR_INT("&nout not defined", procName, 1);
439
440 nleft = bb->n - bb->nwritten;
441 nout = L_MIN(nleft, nbytes);
442 *pnout = nout;
443
444 if (nleft == 0) { /* nothing to write; reinitialize the buffer */
445 bb->n = 0;
446 bb->nwritten = 0;
447 return 0;
448 }
449
450 /* nout > 0; transfer the data out */
451 fwrite((void *)(bb->array + bb->nwritten), 1, nout, fp);
452 bb->nwritten += nout;
453
454 /* If all written; "empty" the buffer */
455 if (nout == nleft) {
456 bb->n = 0;
457 bb->nwritten = 0;
458 }
459
460 return 0;
461 }
462
463
464
465 /*--------------------------------------------------------------------------*
466 * Accessors *
467 *--------------------------------------------------------------------------*/
468 /*!
469 * bbufferBytesToWrite()
470 *
471 * Input: bbuffer
472 * &nbytes (<return>)
473 * Return: 0 if OK; 1 on error
474 */
475 l_int32
bbufferBytesToWrite(BBUFFER * bb,l_int32 * pnbytes)476 bbufferBytesToWrite(BBUFFER *bb,
477 l_int32 *pnbytes)
478 {
479 PROCNAME("bbufferBytesToWrite");
480
481 if (!bb)
482 return ERROR_INT("bb not defined", procName, 1);
483 if (!pnbytes)
484 return ERROR_INT("&nbytes not defined", procName, 1);
485
486 *pnbytes = bb->n - bb->nwritten;
487 return 0;
488 }
489
490
491