1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/utils/bufchain.c

194 lines
4.7 KiB
C
Raw Normal View History

/*
* Generic routines to deal with send buffers: a linked list of
* smallish blocks, with the operations
*
* - add an arbitrary amount of data to the end of the list
* - remove the first N bytes from the list
* - return a (pointer,length) pair giving some initial data in
* the list, suitable for passing to a send or write system
* call
* - retrieve a larger amount of initial data from the list
* - return the current size of the buffer chain in bytes
*/
#include "defs.h"
#include "misc.h"
#define BUFFER_MIN_GRANULE 512
struct bufchain_granule {
struct bufchain_granule *next;
char *bufpos, *bufend, *bufmax;
};
static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
{
unreachable("bufchain callback used while uninitialised");
}
void bufchain_init(bufchain *ch)
{
ch->head = ch->tail = NULL;
ch->buffersize = 0;
ch->ic = NULL;
ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
}
void bufchain_clear(bufchain *ch)
{
struct bufchain_granule *b;
while (ch->head) {
b = ch->head;
ch->head = ch->head->next;
smemclr(b, sizeof(*b));
sfree(b);
}
ch->tail = NULL;
ch->buffersize = 0;
}
size_t bufchain_size(bufchain *ch)
{
return ch->buffersize;
}
void bufchain_set_callback_inner(
bufchain *ch, IdempotentCallback *ic,
void (*queue_idempotent_callback)(IdempotentCallback *ic))
{
ch->queue_idempotent_callback = queue_idempotent_callback;
ch->ic = ic;
}
void bufchain_add(bufchain *ch, const void *data, size_t len)
{
const char *buf = (const char *)data;
if (len == 0) return;
ch->buffersize += len;
while (len > 0) {
if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
memcpy(ch->tail->bufend, buf, copylen);
buf += copylen;
len -= copylen;
ch->tail->bufend += copylen;
}
if (len > 0) {
size_t grainlen =
max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
struct bufchain_granule *newbuf;
newbuf = smalloc(grainlen);
newbuf->bufpos = newbuf->bufend =
(char *)newbuf + sizeof(struct bufchain_granule);
newbuf->bufmax = (char *)newbuf + grainlen;
newbuf->next = NULL;
if (ch->tail)
ch->tail->next = newbuf;
else
ch->head = newbuf;
ch->tail = newbuf;
}
}
if (ch->ic)
ch->queue_idempotent_callback(ch->ic);
}
void bufchain_consume(bufchain *ch, size_t len)
{
struct bufchain_granule *tmp;
assert(ch->buffersize >= len);
while (len > 0) {
int remlen = len;
assert(ch->head != NULL);
if (remlen >= ch->head->bufend - ch->head->bufpos) {
remlen = ch->head->bufend - ch->head->bufpos;
tmp = ch->head;
ch->head = tmp->next;
if (!ch->head)
ch->tail = NULL;
smemclr(tmp, sizeof(*tmp));
sfree(tmp);
} else
ch->head->bufpos += remlen;
ch->buffersize -= remlen;
len -= remlen;
}
}
ptrlen bufchain_prefix(bufchain *ch)
{
return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
}
void bufchain_fetch(bufchain *ch, void *data, size_t len)
{
struct bufchain_granule *tmp;
char *data_c = (char *)data;
tmp = ch->head;
assert(ch->buffersize >= len);
while (len > 0) {
int remlen = len;
assert(tmp != NULL);
if (remlen >= tmp->bufend - tmp->bufpos)
remlen = tmp->bufend - tmp->bufpos;
memcpy(data_c, tmp->bufpos, remlen);
tmp = tmp->next;
len -= remlen;
data_c += remlen;
}
}
void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
{
bufchain_fetch(ch, data, len);
bufchain_consume(ch, len);
}
bool bufchain_try_fetch(bufchain *ch, void *data, size_t len)
{
if (ch->buffersize >= len) {
bufchain_fetch(ch, data, len);
return true;
} else {
return false;
}
}
bool bufchain_try_consume(bufchain *ch, size_t len)
{
if (ch->buffersize >= len) {
bufchain_consume(ch, len);
return true;
} else {
return false;
}
}
bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
{
if (ch->buffersize >= len) {
bufchain_fetch_consume(ch, data, len);
return true;
} else {
return false;
}
}
size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
{
if (len > ch->buffersize)
len = ch->buffersize;
if (len)
bufchain_fetch_consume(ch, data, len);
return len;
}