mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
174 lines
4.4 KiB
C
174 lines
4.4 KiB
C
|
/*
|
||
|
* 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_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;
|
||
|
}
|