Coverage Report

Created: 2024-06-18 06:05

/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
}