From 436fddaa5c6b52604602d7838a832c5d2dfeef10 Mon Sep 17 00:00:00 2001 Message-Id: <436fddaa5c6b52604602d7838a832c5d2dfeef10.1714822040.git.mdw@distorted.org.uk> From: Mark Wooding Date: Sat, 17 Jun 2000 10:39:19 +0000 Subject: [PATCH] Experimental new support for packet buffering. Organization: Straylight/Edgeware From: mdw --- pkbuf.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pkbuf.h | 195 +++++++++++++++++++++++++++++++++++++++++ selpk.c | 184 +++++++++++++++++++++++++++++++++++++++ selpk.h | 138 +++++++++++++++++++++++++++++ 4 files changed, 781 insertions(+) create mode 100644 pkbuf.c create mode 100644 pkbuf.h create mode 100644 selpk.c create mode 100644 selpk.h diff --git a/pkbuf.c b/pkbuf.c new file mode 100644 index 0000000..73e2ca5 --- /dev/null +++ b/pkbuf.c @@ -0,0 +1,264 @@ +/* -*-c-*- + * + * $Id: pkbuf.c,v 1.1 2000/06/17 10:39:19 mdw Exp $ + * + * Simple packet buffering + * + * (c) 2000 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: pkbuf.c,v $ + * Revision 1.1 2000/06/17 10:39:19 mdw + * Experimental new support for packet buffering. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include "alloc.h" +#include "arena.h" +#include "pkbuf.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @pkbuf_flush@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @octet *p@ = pointer to where to start searching + * @size_t len@ = length of new material added + * + * Returns: --- + * + * Use: Flushes any complete packets in a packet buffer. New + * material is assumed to have been added starting at @p@. If + * @p@ is null, then the scan starts at the beginning of the + * buffer, and the size of data already in the buffer is used in + * place of @len@. + * + * It is assumed that the buffer is initially enabled. You + * shouldn't be contributing data to a disabled buffer anyway. + * However, the buffer handler may at some point disable itself, + * and @pkbuf_flush@ can cope with this eventuality. Any + * pending data is left at the start of the buffer and can be + * flushed out by calling @pkbuf_flush(b, 0, 0)@ if the buffer + * is ever re-enabled. + */ + +void pkbuf_flush(pkbuf *pk, octet *p, size_t len) +{ + size_t l; + size_t keep; + + /* --- Initialize variables as necessary --- */ + + if (!p) { + p = pk->buf; + len = pk->len; + } + l = p + len - pk->buf; + p = pk->buf; + + /* --- Now grind through any packets which have accumulated --- */ + + while (l > pk->want) { + size_t sz = pk->want; + + /* --- Pass a packet to the user handler --- */ + + keep = 0; + pk->func(p, sz, pk, &keep, pk->p); + + /* --- Adjust all the pointers for the next packet --- */ + + sz -= keep; + p += sz; + l -= sz; + + /* --- Abort here if disabled --- */ + + if (!(pk->f & PKBUF_ENABLE)) + break; + } + + /* --- Shunt data around in the buffer --- */ + + if (p > pk->buf && l != 0) + memmove(pk->buf, p, l); + pk->len = l; +} + +/* --- @pkbuf_close@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Informs the client that no more data is likely to arrive. If + * there is a partial packet in the buffer, it is discarded. + */ + +void pkbuf_close(pkbuf *pk) +{ + if (pk->buf) { + fprintf(stderr, "*** destroying buffer, closing down\n"); + x_free(pk->a, pk->buf); + pk->buf = 0; + } + if (pk->f & PKBUF_ENABLE) + pk->func(0, 0, pk, 0, pk->p); +} + +/* --- @pkbuf_free@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @octet **p@ = output pointer to free space + * + * Returns: Free buffer size. + * + * Use: Returns the free portion of a packet buffer. Data can then + * be written to this portion, and split out into packets by + * calling @pkbuf_flush@. A buffer is allocated if none + * currently exists. + */ + +size_t pkbuf_free(pkbuf *pk, octet **p) +{ + if (!pk->buf) { + fprintf(stderr, "*** allocating new buffer\n"); + pk->buf = x_alloc(pk->a, pk->sz); + } + *p = pk->buf + pk->len; + return (pk->sz - pk->len); +} + +/* --- @pkbuf_snarf@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @const void *p@ = pointer to input data buffer + * @size_t sz@ = size of data in input buffer + * + * Returns: --- + * + * Use: Snarfs the data from the input buffer and spits it out as + * packets. This interface ignores the complexities of dealing + * with disablement: you should be using @pkbuf_free@ to + * contribute data if you want to cope with that. + */ + +void pkbuf_snarf(pkbuf *pk, const void *p, size_t sz) +{ + const octet *pp = p; + while (sz && (pk->f & PKBUF_ENABLE)) { + size_t bsz; + octet *bp; + + bsz = pkbuf_free(pk, &bp); + if (bsz > sz) + bsz = sz; + memcpy(bp, pp, bsz); + pkbuf_flush(pk, bp, bsz); + pp += bsz; + sz -= bsz; + } +} + +/* --- @pkbuf_want@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @size_t want@ = how many octets wanted for next packet + * + * Returns: --- + * + * Use: Sets the desired size for the next packet to be read. If + * it's larger than the current buffer, the buffer is extended. + */ + +void pkbuf_want(pkbuf *pk, size_t want) +{ + pk->want = want; + if (want > pk->sz) { + do pk->sz <<= 1; while (want < pk->sz); + if (pk->buf) { + if (pk->len) + pk->buf = x_realloc(pk->a, pk->buf, pk->sz); + else { + x_free(pk->a, pk->buf); + pk->buf = 0; + } + } + } +} + +/* --- @pkbuf_init@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @void (*func)(octet *b, size_t sz, pkbuf *pk,@ + * @size_t *keep, void *p)@ = + * handler function + * @void *p@ = argument pointer for @func@ + * + * Returns: --- + * + * Use: Initializes a packet buffer block. Any packets are passed to + * the provided function for handling. + */ + +void pkbuf_init(pkbuf *pk, + void (*func)(octet */*b*/, size_t /*sz*/, + pkbuf */*pk*/, size_t */*keep*/, void */*p*/), + void *p) +{ + pk->func = func; + pk->p = p; + pk->len = 0; + pk->f = PKBUF_ENABLE; + pk->buf = 0; + pk->sz = 256; + pk->want = 1; + pk->a = arena_global; +} + +/* --- @pkbuf_destroy@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Deallocates a line buffer and frees any resources it owned. + */ + +void pkbuf_destroy(pkbuf *pk) +{ + if (pk->buf) { + x_free(pk->a, pk->buf); + pk->buf = 0; + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/pkbuf.h b/pkbuf.h new file mode 100644 index 0000000..e6bbec3 --- /dev/null +++ b/pkbuf.h @@ -0,0 +1,195 @@ +/* -*-c-*- + * + * $Id: pkbuf.h,v 1.1 2000/06/17 10:39:19 mdw Exp $ + * + * Simple packet buffering + * + * (c) 2000 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: pkbuf.h,v $ + * Revision 1.1 2000/06/17 10:39:19 mdw + * Experimental new support for packet buffering. + * + */ + +#ifndef MLIB_PKBUF_H +#define MLIB_PKBUF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#ifndef MLIB_ARENA_H +# include "arena.h" +#endif + +#ifndef MLIB_BITS_H +# include "bits.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct pkbuf { + size_t sz; /* Size of current buffer */ + size_t len; /* Length of data in the buffer */ + size_t want; /* Want this many bytes for packet */ + unsigned f; /* Various state flags */ + void (*func)(octet */*b*/, size_t /*sz*/, + struct pkbuf */*pk*/, size_t */*keep*/, + void */*p*/); /* Handler function */ + void *p; /* Argument for handler */ + arena *a; /* Memory allocation arena */ + octet *buf; /* Actual buffer space */ +} pkbuf; + +enum { + PKBUF_ENABLE = 1 /* Buffer is currently enabled */ +}; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @pkbuf_flush@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @octet *p@ = pointer to where to start searching + * @size_t len@ = length of new material added + * + * Returns: --- + * + * Use: Flushes any complete packets in a packet buffer. New + * material is assumed to have been added starting at @p@. If + * @p@ is null, then the scan starts at the beginning of the + * buffer, and the size of data already in the buffer is used in + * place of @len@. + * + * It is assumed that the buffer is initially enabled. You + * shouldn't be contributing data to a disabled buffer anyway. + * However, the buffer handler may at some point disable itself, + * and @pkbuf_flush@ can cope with this eventuality. Any + * pending data is left at the start of the buffer and can be + * flushed out by calling @pkbuf_flush(b, 0, 0)@ if the buffer + * is ever re-enabled. + */ + +extern void pkbuf_flush(pkbuf */*pk*/, octet */*p*/, size_t /*len*/); + +/* --- @pkbuf_close@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Informs the client that no more data is likely to arrive. If + * there is a partial packet in the buffer, it is discarded. + */ + +extern void pkbuf_close(pkbuf */*pk*/); + +/* --- @pkbuf_free@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @octet **p@ = output pointer to free space + * + * Returns: Free buffer size. + * + * Use: Returns the free portion of a packet buffer. Data can then + * be written to this portion, and split out into packets by + * calling @pkbuf_flush@. + */ + +extern size_t pkbuf_free(pkbuf */*pk*/, octet **/*p*/); + +/* --- @pkbuf_snarf@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @const void *p@ = pointer to input data buffer + * @size_t sz@ = size of data in input buffer + * + * Returns: --- + * + * Use: Snarfs the data from the input buffer and spits it out as + * packets. This interface ignores the complexities of dealing + * with disablement: you should be using @pkbuf_free@ to + * contribute data if you want to cope with that. + */ + +extern void pkbuf_snarf(pkbuf */*pk*/, const void */*p*/, size_t /*sz*/); + +/* --- @pkbuf_want@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @size_t want@ = how many octets wanted for next packet + * + * Returns: --- + * + * Use: Sets the desired size for the next packet to be read. If + * it's larger than the current buffer, the buffer is extended. + */ + +extern void pkbuf_want(pkbuf */*pk*/, size_t /*want*/); + +/* --- @pkbuf_init@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * @void (*func)(octet *b, size_t sz, pkbuf *pk,@ + * @size_t *keep, void *p)@ = + * handler function + * @void *p@ = argument pointer for @func@ + * + * Returns: --- + * + * Use: Initializes a packet buffer block. Any packets are passed to + * the provided function for handling. + */ + +extern void pkbuf_init(pkbuf */*pk*/, + void (*/*func*/)(octet */*b*/, size_t /*sz*/, + pkbuf */*pk*/, size_t */*keep*/, + void */*p*/), + void */*p*/); + +/* --- @pkbuf_destroy@ --- * + * + * Arguments: @pkbuf *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Deallocates a packet buffer and frees any resources it owned. + */ + +extern void pkbuf_destroy(pkbuf */*pk*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/selpk.c b/selpk.c new file mode 100644 index 0000000..9d077b6 --- /dev/null +++ b/selpk.c @@ -0,0 +1,184 @@ +/* -*-c-*- + * + * $Id: selpk.c,v 1.1 2000/06/17 10:39:19 mdw Exp $ + * + * Packet-buffering select handler + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: selpk.c,v $ + * Revision 1.1 2000/06/17 10:39:19 mdw + * Experimental new support for packet buffering. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "pkbuf.h" +#include "sel.h" +#include "selpk.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @selpk_enable@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Enables a buffer for reading, and emits any queued packets + * to the buffer's owner. + */ + +void selpk_enable(selpk *pk) +{ + if (!(pk->pk.f & PKBUF_ENABLE)) { + pk->pk.f |= PKBUF_ENABLE; + sel_addfile(&pk->reader); + pkbuf_flush(&pk->pk, 0, 0); + } +} + +/* --- @selpk_disable@ --- * + * + * Arguments: @selpk *pk@ = pointer to a buffer block + * + * Returns: --- + * + * Use: Disables a buffer. It won't be read from until it's + * enabled again. + */ + +void selpk_disable(selpk *pk) +{ + if (pk->pk.f & PKBUF_ENABLE) { + pk->pk.f &= ~PKBUF_ENABLE; + sel_rmfile(&pk->reader); + } +} + +/* --- @selpk_read@ --- * + * + * Arguments: @int fd@ = file descriptor to read from + * @int mode@ = what we can do to the file + * @void *vp@ = pointer to buffer context + * + * Returns: --- + * + * Use: Acts on the result of a @select@ call. + */ + +static void selpk_read(int fd, unsigned mode, void *vp) +{ + selpk *pk = vp; + octet *p; + size_t sz; + int n; + + sz = pkbuf_free(&pk->pk, &p); + n = read(fd, p, sz); + if (n == 0) + pkbuf_close(&pk->pk); + else if (n > 0) + pkbuf_flush(&pk->pk, p, n); + else switch (errno) { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return; + default: + pkbuf_close(&pk->pk); + } +} + +/* --- @selpk_want@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * @size_t sz@ = size of buffer + * + * Returns: --- + * + * Use: Sets the size of the packet to be read next. + */ + +void selpk_want(selpk *pk, size_t sz) +{ + pkbuf_want(&pk->pk, sz); +} + +/* --- @selpk_init@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * @sel_state *s@ = pointer to select state to attach to + * @int fd@ = file descriptor to listen to + * @void (*func)(char *s, void *p)@ = function to call + * @void *p@ = argument for function + * + * Returns: --- + * + * Use: Initializes a buffer block. + */ + +void selpk_init(selpk *pk, + sel_state *s, + int fd, + void (*func)(octet */*b*/, size_t /*sz*/, pkbuf */*pk*/, + size_t */*keep*/, void */*p*/), + void *p) +{ + pkbuf_init(&pk->pk, func, p); + pk->pk.f &= ~PKBUF_ENABLE; + sel_initfile(s, &pk->reader, fd, SEL_READ, selpk_read, pk); + selpk_enable(pk); +} + +/* --- @selpk_destroy@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Deallocates a packet buffer and frees any resources it owned. + */ + +void selpk_destroy(selpk *pk) +{ + selpk_disable(pk); + pkbuf_destroy(&pk->pk); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/selpk.h b/selpk.h new file mode 100644 index 0000000..c1f5db3 --- /dev/null +++ b/selpk.h @@ -0,0 +1,138 @@ +/* -*-c-*- + * + * $Id: selpk.h,v 1.1 2000/06/17 10:39:19 mdw Exp $ + * + * Packet-buffering select handler + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: selpk.h,v $ + * Revision 1.1 2000/06/17 10:39:19 mdw + * Experimental new support for packet buffering. + * + */ + +#ifndef MLIB_SELPK_H +#define MLIB_SELPK_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifndef MLIB_PKBUF_H +# include "pkbuf.h" +#endif + +#ifndef MLIB_SEL_H +# include "sel.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct selpk { + sel_file reader; /* File selection object */ + pkbuf pk; /* Packet buffering object */ +} selpk; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @selpk_enable@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * + * Returns: --- + * + * Use: Enables a buffer for reading, and emits any queued packets + * to the buffer's owner. + */ + +extern void selpk_enable(selpk */*pk*/); + +/* --- @selpk_disable@ --- * + * + * Arguments: @selpk *pk@ = pointer to a buffer block + * + * Returns: --- + * + * Use: Disables a buffer. It won't be read from until it's + * enabled again. + */ + +extern void selpk_disable(selpk */*pk*/); + +/* --- @selpk_want@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * @size_t want@ = size of packet to read + * + * Returns: --- + * + * Use: Sets the size of the packet to be read next. + */ + +extern void selpk_setsize(selpk */*pk*/, size_t /*sz*/); + +/* --- @selpk_init@ --- * + * + * Arguments: @selpk *pk@ = pointer to buffer block + * @sel_state *s@ = pointer to select state to attach to + * @int fd@ = file descriptor to listen to + * @void (*func)(...)@ = function to call + * @void *p@ = argument for function + * + * Returns: --- + * + * Use: Initializes a buffer block. + */ + +extern void selpk_init(selpk */*pk*/, + sel_state */*s*/, + int /*fd*/, + void (*/*func*/)(octet */*b*/, size_t /*sz*/, + pkbuf */*pk*/, size_t */*keep*/, + void */*p*/), + void */*p*/); + +/* --- @selpk_destroy@ --- * + * + * Arguments: @lbuf *b@ = pointer to buffer block + * + * Returns: --- + * + * Use: Deallocates a packet buffer and frees any resources it owned. + */ + +extern void selpk_destroy(selpk */*pk*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif -- [mdw]