diff options
Diffstat (limited to 'lib/qiovec.c')
-rw-r--r-- | lib/qiovec.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/lib/qiovec.c b/lib/qiovec.c new file mode 100644 index 00000000..546dfcb0 --- /dev/null +++ b/lib/qiovec.c @@ -0,0 +1,261 @@ +/* Flexible iovec handler + * Copyright (C) 2010 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "zebra.h" + +#include "memory.h" +#include "zassert.h" +#include "miyagi.h" + +#include "qiovec.h" + +/*============================================================================== + * Initialise, allocate and reset qiovec + */ + +/*------------------------------------------------------------------------------ + * Initialise new qiovec -- allocate if required. + * + * This is for initialising a new structure. Any pre-exiting contents are + * lost. + * + * Returns: address of qiovec + */ +extern qiovec +qiovec_init_new(qiovec viov) +{ + if (viov == NULL) + viov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ; + else + memset(viov, 0, sizeof(struct qiovec)) ; + + /* Zeroising has set: + * + * vec = NULL - no array, yet + * writing = false -- no writing going on + * + * i_get = 0 -- next entry to get + * i_put = 0 -- next entry to put + * + * i_alloc = 0; -- no entries allocated + * + * Nothing more is required. + */ + + return viov ; +} ; + +/*------------------------------------------------------------------------------ + * Reset qiovec (if any) -- release body and (if required) the structure. + * + * Returns: address of qiovec (if any) -- NULL if structure released + */ +extern qiovec +qiovec_reset(qiovec viov, bool free_structure) +{ + if (viov != NULL) + { + if (viov->vec != NULL) + XFREE(MTYPE_QIOVEC_VEC, viov->vec) ; + + if (free_structure) + XFREE(MTYPE_QIOVEC, viov) ; /* sets viov = NULL */ + else + qiovec_init_new(viov) ; /* re-initialise */ + } ; + + return viov ; +} ; + +/*------------------------------------------------------------------------------ + * Clear given qiovec. + */ +extern void +qiovec_clear(qiovec viov) +{ + viov->i_get = 0 ; + viov->i_put = 0 ; + viov->writing = 0 ; +} ; + +/*------------------------------------------------------------------------------ + * Push item to given qiovec + * + * NB: avoids pushing zero length items. + */ +extern void +qiovec_push(qiovec viov, const void* base, size_t len) +{ + struct iovec* p_iov ; + + if (len == 0) + return ; + + if (viov->i_put >= viov->i_alloc) + { + size_t size ; + assert(viov->i_put == viov->i_alloc) ; + + assert( ((viov->i_alloc == 0) && (viov->vec == NULL)) + || ((viov->i_alloc != 0) && (viov->vec != NULL)) ) ; + + if (viov->i_get > 200) /* keep in check */ + { + size = (viov->i_put - viov->i_get) * sizeof(struct iovec) ; + if (size != 0) + memmove(viov->vec, &viov->vec[viov->i_get], size) ; + viov->i_put -= viov->i_get ; + viov->i_get = 0 ; + } + else + { + viov->i_alloc += 100 ; /* a sizable chunk */ + + size = viov->i_alloc * sizeof(struct iovec) ; + if (viov->vec == NULL) + viov->vec = XMALLOC(MTYPE_QIOVEC_VEC, size) ; + else + viov->vec = XREALLOC(MTYPE_QIOVEC_VEC, viov->vec, size) ; + } ; + } ; + + p_iov = &viov->vec[viov->i_put++] ; + + p_iov->iov_base = miyagi(base) ; + p_iov->iov_len = len ; +} ; + +/*------------------------------------------------------------------------------ + * Write given qiovec -- assuming NON-BLOCKING. + * + * Does nothing if the qiovec is empty. + * + * Loops internally if gets EINTR. + * + * When there is nothing left to output, resets the i_put & i_get to zero. + * + * Returns: > 0 => one or more bytes left to output + * 0 => all done -- zero bytes left to output + * -1 => failed -- see errno + */ +extern int +qiovec_write_nb(int fd, qiovec viov) +{ + int n ; + int l ; + + n = viov->i_put - viov->i_get ; + + l = iovec_write_nb(fd, &viov->vec[viov->i_get], n) ; + + if (l == 0) + { + viov->writing = 0 ; + viov->i_get = viov->i_put = 0 ; + } + else + { + viov->writing = 1 ; + viov->i_get += (n - l) ; + } ; + + return l ; +} ; + +/*------------------------------------------------------------------------------ + * Write given iovec -- assuming NON-BLOCKING. + * + * Does nothing if given zero iovec entries (and array may be NULL). + * + * Loops internally if gets EINTR. + * + * If does not manage to write everything, then: + * + * -- updates the length field of all entries up to and including the + * last one for which data has been written. + * + * -- updates the address field of the first entry that still has some + * data to be output. + * + * Can call this again with the same 'p_iov' and the same 'n' -- the entries + * which have zero lengths will be stepped over. Output will continue from + * where it left off. + * + * Alternatively, if this returns 'l', then do "p_iov += n - l", and set + * "n = l" before calling this again. + * + * Returns: > 0 => number of entries left to output + * 0 => all done -- nothing left to output + * -1 => failed -- see errno + */ +extern int +iovec_write_nb(int fd, struct iovec p_iov[], int n) +{ + ssize_t ret ; + + assert(n >= 0) ; + + /* Skip past any leading zero length entries */ + while ((n > 0) && (p_iov->iov_len == 0)) + { + ++p_iov ; + --n ; + } ; + + while (n > 0) + { + ret = writev(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ; + + if (ret > 0) + { + while (ret > 0) + { + if (ret >= (ssize_t)p_iov->iov_len) + { + assert(n > 0) ; + ret -= p_iov->iov_len ; + p_iov->iov_len = 0 ; + ++p_iov ; + --n ; + } + else + { + p_iov->iov_base = (char*)p_iov->iov_base + ret ; + p_iov->iov_len -= ret ; + ret = 0 ; + } ; + } ; + } + else if (ret == 0) + break ; /* not sure can happen... but + cannot assume will go away */ + else + { + int err = errno ; + if ((err == EAGAIN) || (err == EWOULDBLOCK)) + break ; + if (err != EINTR) + return -1 ; /* failed */ + } ; + } ; + + return n ; +} ; |