/*
* $LynxId: HTChunk.c,v 1.29 2023/04/10 22:58:51 tom Exp $
*
* Chunk handling: Flexible arrays
* ===============================
*
*/
#include <HTUtils.h>
#include <HTChunk.h>
#include <LYLeaks.h>
/*
* Initialize a chunk with a certain allocation unit
*/
void HTChunkInit(HTChunk *ch, int grow)
{
ch->data = 0;
ch->growby = grow;
ch->size = 0;
ch->allocated = 0;
}
/* Create a chunk with a certain allocation unit
* --------------
*/
HTChunk *HTChunkCreate(int grow)
{
HTChunk *ch = typecalloc(HTChunk);
if (ch == NULL)
outofmem(__FILE__, "creation of chunk");
HTChunkInit(ch, grow);
return ch;
}
HTChunk *HTChunkCreateMayFail(int grow, int failok)
{
HTChunk *ch = typecalloc(HTChunk);
if (ch == NULL) {
if (!failok) {
outofmem(__FILE__, "creation of chunk");
} else {
return ch;
}
}
HTChunkInit(ch, grow);
ch->failok = failok;
return ch;
}
/* Create a chunk with a certain allocation unit and ensured size
* --------------
*/
HTChunk *HTChunkCreate2(int grow, size_t needed)
{
HTChunk *ch = typecalloc(HTChunk);
if (ch == NULL)
outofmem(__FILE__, "HTChunkCreate2");
HTChunkInit(ch, grow);
if (needed-- > 0) {
/* Round up */
ch->allocated = (int) (needed - (needed % (size_t) ch->growby)
+ (unsigned) ch->growby);
CTRACE((tfp, "HTChunkCreate2: requested %d, allocate %u\n",
(int) needed, (unsigned) ch->allocated));
ch->data = typecallocn(char, (unsigned) ch->allocated);
if (!ch->data)
outofmem(__FILE__, "HTChunkCreate2 data");
}
return ch;
}
/* Clear a chunk of all data
* --------------------------
*/
void HTChunkClear(HTChunk *ch)
{
FREE(ch->data);
ch->size = 0;
ch->allocated = 0;
}
/* Free a chunk (and it's chain, if any)
* -------------------------------------
*/
void HTChunkFree(HTChunk *ch)
{
HTChunk *next;
do {
next = ch->next;
FREE(ch->data);
FREE(ch);
ch = next;
} while (ch != NULL);
}
/* Realloc the chunk
* -----------------
*/
BOOL HTChunkRealloc(HTChunk *ch, int growby)
{
char *data;
ch->allocated = ch->allocated + growby;
data = (ch->data
? typeRealloc(char, ch->data, ch->allocated)
: typecallocn(char, ch->allocated));
if (data) {
ch->data = data;
} else if (ch->failok) {
HTChunkClear(ch); /* allocation failed, clear all data - kw */
return FALSE; /* caller should check ch->allocated - kw */
} else {
outofmem(__FILE__, "HTChunkRealloc");
}
return TRUE;
}
/* Append a character
* ------------------
*/
void HTChunkPutc(HTChunk *ch, unsigned c)
{
if (ch->size >= ch->allocated) {
if (!HTChunkRealloc(ch, ch->growby))
return;
}
ch->data[ch->size++] = (char) c;
}
/* like above but no realloc: extend to another chunk if necessary */
HTChunk *HTChunkPutc2(HTChunk *ch, int c)
{
if (ch->size >= ch->allocated) {
HTChunk *chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
ch->next = chunk;
ch = chunk;
HTChunkPutc(ch, UCH(c));
} else {
ch->data[ch->size++] = (char) c;
}
return ch;
}
/* Ensure a certain size
* ---------------------
*/
void HTChunkEnsure(HTChunk *ch, int needed)
{
if (needed <= ch->allocated)
return;
ch->allocated = needed - 1 - ((needed - 1) % ch->growby)
+ ch->growby; /* Round up */
ch->data = (ch->data
? typeRealloc(char, ch->data, ch->allocated)
: typecallocn(char, ch->allocated));
if (ch->data == NULL)
outofmem(__FILE__, "HTChunkEnsure");
}
/*
* Append a block of characters.
*/
void HTChunkPutb(HTChunk *ch, const char *b, int l)
{
if (l <= 0)
return;
if (ch->size + l > ch->allocated) {
int growby = l - (l % ch->growby) + ch->growby; /* Round up */
if (!HTChunkRealloc(ch, growby))
return;
}
MemCpy(ch->data + ch->size, b, l);
ch->size += l;
}
/* like above but no realloc: extend to another chunk if necessary */
HTChunk *HTChunkPutb2(HTChunk *ch, const char *b, int l)
{
if (l <= 0)
return ch;
if (ch->size + l > ch->allocated) {
HTChunk *chunk;
int m = ch->allocated - ch->size;
if (m != 0 && b != 0) {
MemCpy(ch->data + ch->size, b, (unsigned) m);
ch->size += m;
}
chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
ch->next = chunk;
ch = chunk;
if (b != 0)
HTChunkPutb(ch, b + m, l - m);
} else {
MemCpy(ch->data + ch->size, b, (unsigned) l);
ch->size += l;
}
return ch;
}
#define PUTC(code) ch->data[ch->size++] = (char)(code)
#define PUTC2(code) ch->data[ch->size++] = (char)(0x80|(0x3f &(code)))
/*
* Append a character encoded as UTF-8.
*/
void HTChunkPutUtf8Char(HTChunk *ch, UCode_t code)
{
int utflen;
if (TOASCII(code) < 128)
utflen = 1;
else if (code < 0x800L) {
utflen = 2;
} else if (code < 0x10000L) {
utflen = 3;
} else if (code < 0x200000L) {
utflen = 4;
} else if (code < 0x4000000L) {
utflen = 5;
} else if (code <= 0x7fffffffL) {
utflen = 6;
} else
utflen = 0;
if (ch->size + utflen > ch->allocated) {
int growby = (ch->growby >= utflen) ? ch->growby : utflen;
if (!HTChunkRealloc(ch, growby))
return;
}
switch (utflen) {
case 0:
return;
case 1:
ch->data[ch->size++] = (char) code;
return;
case 2:
PUTC(0xc0 | (code >> 6));
break;
case 3:
PUTC(0xe0 | (code >> 12));
break;
case 4:
PUTC(0xf0 | (code >> 18));
break;
case 5:
PUTC(0xf8 | (code >> 24));
break;
case 6:
PUTC(0xfc | (code >> 30));
break;
}
switch (utflen) {
case 6:
PUTC2(code >> 24);
/* FALLTHRU */
case 5:
PUTC2(code >> 18);
/* FALLTHRU */
case 4:
PUTC2(code >> 12);
/* FALLTHRU */
case 3:
PUTC2(code >> 6);
/* FALLTHRU */
case 2:
PUTC2(code);
break;
}
}
/* Terminate a chunk
* -----------------
*/
void HTChunkTerminate(HTChunk *ch)
{
HTChunkPutc(ch, (char) 0);
}
/* Append a string
* ---------------
*/
void HTChunkPuts(HTChunk *ch, const char *s)
{
const char *p;
if (s != NULL) {
for (p = s; *p; p++) {
if (ch->size >= ch->allocated) {
if (!HTChunkRealloc(ch, ch->growby))
break;
}
ch->data[ch->size++] = *p;
}
}
}
/* like above but no realloc: extend to another chunk if necessary */
HTChunk *HTChunkPuts2(HTChunk *ch, const char *s)
{
const char *p;
if (s != NULL) {
for (p = s; *p; p++) {
if (ch->size >= ch->allocated) {
HTChunk *chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
ch->next = chunk;
ch = chunk;
HTChunkPuts(ch, p);
break;
}
ch->data[ch->size++] = *p;
}
}
return ch;
}