Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'utils/bufchain.c')
-rw-r--r--utils/bufchain.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/utils/bufchain.c b/utils/bufchain.c
new file mode 100644
index 00000000..9b02c65f
--- /dev/null
+++ b/utils/bufchain.c
@@ -0,0 +1,193 @@
+/*
+ * 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;
+}