/* $Id: buffer.c,v 1.1 2001/01/23 19:59:36 steve Exp $ */ /*- * Copyright (c) 2001 Steve C. Woodford. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Steve C. Woodford. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "buffer.h" #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #define BUFFER_INIT_SIZE 128 int buffer_init(struct buffer_ctx **bcp, int fd) { struct buffer_ctx *bc; if ((bc = calloc(1, sizeof(*bc))) == NULL) return (-1); if ((bc->bc_buffp = malloc(BUFFER_INIT_SIZE)) == NULL) { (void) free(bc); return (-1); } bc->bc_buffsize = BUFFER_INIT_SIZE; bc->bc_bufflen = 0; bc->bc_buffhead = 0; bc->bc_bufftail = 0; bc->bc_fd = fd; *bcp = bc; return (0); } void buffer_destroy(struct buffer_ctx *bc) { (void) free(bc->bc_buffp); (void) free(bc); } ssize_t buffer_fill(struct buffer_ctx *bc, const char *src, size_t bytes) { size_t copy; ssize_t rv; /* * If the ring buffer is currently empty, or we manage to * drain what was left, write as much of this new data as * we can... */ if (bc->bc_bufflen == 0 || (rv = buffer_drain(bc) == 0)) { if ((rv = write(bc->bc_fd, src, bytes)) == bytes) return (0); if (rv > 0) { bytes -= rv; src = &src[rv]; } } if (rv < 0 && errno != EAGAIN) return (rv); /* * Check if we need to grow the ring-buffer to accomodate this data... */ if ((bc->bc_bufflen + bytes) > bc->bc_buffsize) { size_t newsize; char *newbuff; for (newsize = bc->bc_buffsize * 2; newsize < (bc->bc_bufflen + bytes); newsize *= 2) ; /* Nothing */ if ((newbuff = malloc(newsize)) == NULL) return (-1); /* Copy the data over */ if (bc->bc_buffhead < bc->bc_bufftail) { copy = bc->bc_buffsize - bc->bc_bufftail; memcpy(newbuff, &bc->bc_buffp[bc->bc_bufftail], copy); bc->bc_bufftail += copy; } else bc->bc_bufftail = copy = 0; if (bc->bc_bufftail < bc->bc_buffhead) { copy += (bc->bc_buffhead - bc->bc_bufftail); memcpy(&newbuff[copy], &bc->bc_buffp[bc->bc_bufftail], bc->bc_buffhead - bc->bc_bufftail); } bc->bc_buffhead = copy; bc->bc_bufftail = 0; bc->bc_buffsize = newsize; (void) free(bc->bc_buffp); bc->bc_buffp = newbuff; /* Copying the new data into the buffer is trivial ... */ memcpy(&bc->bc_buffp[bc->bc_buffhead], src, bytes); bc->bc_buffhead += bytes; bc->bc_bufflen += bytes; } else { if (bc->bc_buffhead > bc->bc_bufftail) { copy = MIN(bc->bc_buffsize - bc->bc_buffhead, bytes); bytes -= copy; bc->bc_bufflen += copy; if (copy == 1) bc->bc_buffp[bc->bc_buffhead] = *src; else { memcpy(&bc->bc_buffp[bc->bc_buffhead], src, copy); } bc->bc_buffhead += copy; if (bc->bc_buffhead == bc->bc_buffsize) bc->bc_buffhead = 0; } if (bytes && bc->bc_buffhead < bc->bc_bufftail) { copy = MIN(bc->bc_bufftail - bc->bc_buffhead, bytes); bytes -= copy; bc->bc_bufflen += copy; if (copy == 1) bc->bc_buffp[bc->bc_buffhead] = *src; else { memcpy(&bc->bc_buffp[bc->bc_buffhead], src, copy); } bc->bc_buffhead += copy; if (bc->bc_buffhead == bc->bc_buffsize) bc->bc_buffhead = 0; } } return (bc->bc_bufflen); } ssize_t buffer_drain(struct buffer_ctx *bc) { size_t bytes; ssize_t written; if (bc->bc_buffhead < bc->bc_bufftail) { bytes = bc->bc_buffsize - bc->bc_bufftail; written = write(bc->bc_fd, &bc->bc_buffp[bc->bc_bufftail], bytes); if (written < bytes) { if (written < 0) { if (errno != EAGAIN) return (-1); written = 0; } bc->bc_bufftail += written; } else bc->bc_bufftail = 0; bc->bc_bufflen -= written; } if (bc->bc_bufftail < bc->bc_buffhead) { bytes = bc->bc_buffhead - bc->bc_bufftail; written = write(bc->bc_fd, &bc->bc_buffp[bc->bc_bufftail], bytes); if (written < 0) { if (errno != EAGAIN) return (-1); written = 0; } bc->bc_bufftail += written; bc->bc_bufflen -= written; } return (bc->bc_bufflen); }