blob: 3f87d2641b68fc869baf0f1426ce53a8dd635674 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* bbuffer.c
*
* Create/Destroy BBuffer
* BBUFFER *bbufferCreate()
* void *bbufferDestroy()
* l_uint8 *bbufferDestroyAndSaveData()
*
* Operations to read data TO a BBuffer
* l_int32 bbufferRead()
* l_int32 bbufferReadStream()
* l_int32 bbufferExtendArray()
*
* Operations to write data FROM a BBuffer
* l_int32 bbufferWrite()
* l_int32 bbufferWriteStream()
*
* Accessors
* l_int32 bbufferBytesToWrite()
*
*
* The bbuffer is an implementation of a byte queue.
* The bbuffer holds a byte array from which bytes are
* processed in a first-in/first-out fashion. As with
* any queue, bbuffer maintains two "pointers," one to the
* tail of the queue (where you read new bytes onto it)
* and one to the head of the queue (where you start from
* when writing bytes out of it.
*
* The queue can be visualized:
*
*
* byte 0 byte (nalloc - 1)
* | |
* --------------------------------------------------
* H T
* [ aw ][ bytes currently on queue ][ anr ]
*
* ---: all allocated data in bbuffer
* H: queue head (ptr to next byte to be written out)
* T: queue tail (ptr to first byte to be written to)
* aw: already written from queue
* anr: allocated but not yet read to
*
* The purpose of bbuffer is to allow you to safely read
* bytes in, and to sequentially write them out as well.
* In the process of writing bytes out, you don't actually
* remove the bytes in the array; you just move the pointer
* (nwritten) which points to the head of the queue. In
* the process of reading bytes in, you sometimes need to
* expand the array size. If a read is performed after a
* write, so that the head of the queue is not at the
* beginning of the array, the bytes already written are
* first removed by copying the others over them; then the
* new bytes are read onto the tail of the queue.
*
* Note that the meaning of "read into" and "write from"
* the bbuffer is OPPOSITE to that for a stream, where
* you read "from" a stream and write "into" a stream.
* As a mnemonic for remembering the direction:
* - to read bytes from a stream into the bbuffer,
* you call fread on the stream
* - to write bytes from the bbuffer into a stream,
* you call fwrite on the stream
*
* See zlibmem.c for an example use of bbuffer, where we
* compress and decompress an array of bytes in memory.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "allheaders.h"
static const l_int32 INITIAL_BUFFER_ARRAYSIZE = 1024; /* n'importe quoi */
/*--------------------------------------------------------------------------*
* BBuffer create/destroy *
*--------------------------------------------------------------------------*/
/*!
* bbufferCreate()
*
* Input: buffer address in memory (<optional>)
* size of byte array to be alloc'd (0 for default)
* Return: bbuffer, or null on error
*
* Notes:
* (1) If a buffer address is given, you should read all the data in.
* (2) Allocates a bbuffer with associated byte array of
* the given size. If a buffer address is given,
* it then reads the number of bytes into the byte array.
*/
BBUFFER *
bbufferCreate(l_uint8 *indata,
l_int32 nalloc)
{
BBUFFER *bb;
PROCNAME("bbufferCreate");
if (nalloc <= 0)
nalloc = INITIAL_BUFFER_ARRAYSIZE;
if ((bb = (BBUFFER *)CALLOC(1, sizeof(BBUFFER))) == NULL)
return (BBUFFER *)ERROR_PTR("bb not made", procName, NULL);
if ((bb->array = (l_uint8 *)CALLOC(nalloc, sizeof(l_uint8))) == NULL)
return (BBUFFER *)ERROR_PTR("byte array not made", procName, NULL);
bb->nalloc = nalloc;
bb->nwritten = 0;
if (indata) {
memcpy((l_uint8 *)bb->array, indata, nalloc);
bb->n = nalloc;
}
else
bb->n = 0;
return bb;
}
/*!
* bbufferDestroy()
*
* Input: &bbuffer (<to be nulled>)
* Return: void
*
* Notes:
* (1) Destroys the byte array in the bbuffer and then the bbuffer;
* then nulls the contents of the input ptr.
*/
void
bbufferDestroy(BBUFFER **pbb)
{
BBUFFER *bb;
PROCNAME("bbufferDestroy");
if (pbb == NULL) {
L_WARNING("ptr address is NULL", procName);
return;
}
if ((bb = *pbb) == NULL)
return;
if (bb->array)
FREE(bb->array);
FREE(bb);
*pbb = NULL;
return;
}
/*!
* bbufferDestroyAndSaveData()
*
* Input: &bbuffer (<to be nulled>)
* &nbytes (<return> number of bytes saved in array)
* Return: barray (newly allocated array of data)
*
* Notes:
* (1) Copies data to newly allocated array; then destroys the bbuffer.
*/
l_uint8 *
bbufferDestroyAndSaveData(BBUFFER **pbb,
l_int32 *pnbytes)
{
l_uint8 *array;
l_int32 nbytes;
BBUFFER *bb;
PROCNAME("bbufferDestroyAndSaveData");
if (pbb == NULL) {
L_WARNING("ptr address is NULL", procName);
return NULL;
}
if (pnbytes == NULL) {
L_WARNING("&nbytes is NULL", procName);
bbufferDestroy(pbb);
return NULL;
}
if ((bb = *pbb) == NULL)
return NULL;
/* write all unwritten bytes out to a new array */
nbytes = bb->n - bb->nwritten;
*pnbytes = nbytes;
if ((array = (l_uint8 *)CALLOC(nbytes, sizeof(l_uint8))) == NULL) {
L_WARNING("calloc failure for array", procName);
return NULL;
}
memcpy((void *)array, (void *)(bb->array + bb->nwritten), nbytes);
bbufferDestroy(pbb);
return array;
}
/*--------------------------------------------------------------------------*
* Operations to read data INTO a BBuffer *
*--------------------------------------------------------------------------*/
/*!
* bbufferRead()
*
* Input: bbuffer
* src (source memory buffer from which bytes are read)
* nbytes (bytes to be read)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) For a read after write, first remove the written
* bytes by shifting the unwritten bytes in the array,
* then check if there is enough room to add the new bytes.
* If not, realloc with bbufferExpandArray(), resulting
* in a second writing of the unwritten bytes. While less
* efficient, this is simpler than making a special case
* of reallocNew().
*/
l_int32
bbufferRead(BBUFFER *bb,
l_uint8 *src,
l_int32 nbytes)
{
l_int32 navail, nadd, nwritten;
PROCNAME("bbufferRead");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if (!src)
return ERROR_INT("src not defined", procName, 1);
if (nbytes == 0)
return ERROR_INT("no bytes to read", procName, 1);
if ((nwritten = bb->nwritten)) { /* move the unwritten bytes over */
memmove((l_uint8 *)bb->array, (l_uint8 *)(bb->array + nwritten),
bb->n - nwritten);
bb->nwritten = 0;
bb->n -= nwritten;
}
/* If necessary, expand the allocated array. Do so by
* by at least a factor of two. */
navail = bb->nalloc - bb->n;
if (nbytes > navail) {
nadd = L_MAX(bb->nalloc, nbytes);
bbufferExtendArray(bb, nadd);
}
/* Read in the new bytes */
memcpy((l_uint8 *)(bb->array + bb->n), src, nbytes);
bb->n += nbytes;
return 0;
}
/*!
* bbufferReadStream()
*
* Input: bbuffer
* fp (source stream from which bytes are read)
* nbytes (bytes to be read)
* Return: 0 if OK, 1 on error
*/
l_int32
bbufferReadStream(BBUFFER *bb,
FILE *fp,
l_int32 nbytes)
{
l_int32 navail, nadd, nread, nwritten;
PROCNAME("bbufferReadStream");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if (!fp)
return ERROR_INT("fp not defined", procName, 1);
if (nbytes == 0)
return ERROR_INT("no bytes to read", procName, 1);
if ((nwritten = bb->nwritten)) { /* move any unwritten bytes over */
memmove((l_uint8 *)bb->array, (l_uint8 *)(bb->array + nwritten),
bb->n - nwritten);
bb->nwritten = 0;
bb->n -= nwritten;
}
/* If necessary, expand the allocated array. Do so by
* by at least a factor of two. */
navail = bb->nalloc - bb->n;
if (nbytes > navail) {
nadd = L_MAX(bb->nalloc, nbytes);
bbufferExtendArray(bb, nadd);
}
/* Read in the new bytes */
nread = fread((void *)(bb->array + bb->n), 1, nbytes, fp);
bb->n += nread;
return 0;
}
/*!
* bbufferExtendArray()
*
* Input: bbuffer
* nbytes (number of bytes to extend array size)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) reallocNew() copies all bb->nalloc bytes, even though
* only bb->n are data.
*/
l_int32
bbufferExtendArray(BBUFFER *bb,
l_int32 nbytes)
{
PROCNAME("bbufferExtendArray");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if ((bb->array = (l_uint8 *)reallocNew((void **)&bb->array,
bb->nalloc,
bb->nalloc + nbytes)) == NULL)
return ERROR_INT("new ptr array not returned", procName, 1);
bb->nalloc += nbytes;
return 0;
}
/*--------------------------------------------------------------------------*
* Operations to write data FROM a BBuffer *
*--------------------------------------------------------------------------*/
/*!
* bbufferWrite()
*
* Input: bbuffer
* dest (dest memory buffer to which bytes are written)
* nbytes (bytes requested to be written)
* &nout (<return> bytes actually written)
* Return: 0 if OK, 1 on error
*/
l_int32
bbufferWrite(BBUFFER *bb,
l_uint8 *dest,
l_int32 nbytes,
l_int32 *pnout)
{
l_int32 nleft, nout;
PROCNAME("bbufferWrite");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if (!dest)
return ERROR_INT("dest not defined", procName, 1);
if (nbytes <= 0)
return ERROR_INT("no bytes requested to write", procName, 1);
if (!pnout)
return ERROR_INT("&nout not defined", procName, 1);
nleft = bb->n - bb->nwritten;
nout = L_MIN(nleft, nbytes);
*pnout = nout;
if (nleft == 0) { /* nothing to write; reinitialize the buffer */
bb->n = 0;
bb->nwritten = 0;
return 0;
}
/* nout > 0; transfer the data out */
memcpy(dest, (l_uint8 *)(bb->array + bb->nwritten), nout);
bb->nwritten += nout;
/* If all written; "empty" the buffer */
if (nout == nleft) {
bb->n = 0;
bb->nwritten = 0;
}
return 0;
}
/*!
* bbufferWriteStream()
*
* Input: bbuffer
* fp (dest stream to which bytes are written)
* nbytes (bytes requested to be written)
* &nout (<return> bytes actually written)
* Return: 0 if OK, 1 on error
*/
l_int32
bbufferWriteStream(BBUFFER *bb,
FILE *fp,
l_int32 nbytes,
l_int32 *pnout)
{
l_int32 nleft, nout;
PROCNAME("bbufferWriteStream");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if (!fp)
return ERROR_INT("output stream not defined", procName, 1);
if (nbytes <= 0)
return ERROR_INT("no bytes requested to write", procName, 1);
if (!pnout)
return ERROR_INT("&nout not defined", procName, 1);
nleft = bb->n - bb->nwritten;
nout = L_MIN(nleft, nbytes);
*pnout = nout;
if (nleft == 0) { /* nothing to write; reinitialize the buffer */
bb->n = 0;
bb->nwritten = 0;
return 0;
}
/* nout > 0; transfer the data out */
fwrite((void *)(bb->array + bb->nwritten), 1, nout, fp);
bb->nwritten += nout;
/* If all written; "empty" the buffer */
if (nout == nleft) {
bb->n = 0;
bb->nwritten = 0;
}
return 0;
}
/*--------------------------------------------------------------------------*
* Accessors *
*--------------------------------------------------------------------------*/
/*!
* bbufferBytesToWrite()
*
* Input: bbuffer
* &nbytes (<return>)
* Return: 0 if OK; 1 on error
*/
l_int32
bbufferBytesToWrite(BBUFFER *bb,
l_int32 *pnbytes)
{
PROCNAME("bbufferBytesToWrite");
if (!bb)
return ERROR_INT("bb not defined", procName, 1);
if (!pnbytes)
return ERROR_INT("&nbytes not defined", procName, 1);
*pnbytes = bb->n - bb->nwritten;
return 0;
}