/*
* circ_buf.c - circular buffer module - implementation
*
* nc6 - an advanced netcat clone
* Copyright (C) 2001-2006 Mauro Tortonesi <mauro _at_ deepspace6.net>
* Copyright (C) 2002-2006 Chris Leishman <chris _at_ leishman.org>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "system.h"
#include "circ_buf.h"
#include "misc.h"
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
RCSID("@(#) $Header: /ds6/cvs/nc6/src/circ_buf.c,v 1.26 2006/01/19 22:46:23 chris Exp $");
#ifndef NDEBUG
static void cb_assert(const circ_buf_t *cb)
{
if (cb == NULL ||
cb->buf == NULL ||
cb->ptr == NULL ||
cb->buf_size < cb->data_size)
{
fatal_internal("circular buffer assertion failed");
}
}
#else
#define cb_assert(CB) do {} while(0)
#endif
void cb_init(circ_buf_t *cb, size_t size)
{
assert(cb != NULL);
assert(size > 0);
memset(cb, 0, sizeof(circ_buf_t));
cb->buf = (uint8_t *)xmalloc(size);
cb->ptr = cb->buf;
cb->data_size = 0;
cb->buf_size = size;
cb_assert(cb);
}
void cb_destroy(circ_buf_t *cb)
{
cb_assert(cb);
free(cb->buf);
cb->buf = NULL;
}
void cb_resize(circ_buf_t *cb, size_t size)
{
uint8_t *new_buf;
cb_assert(cb);
assert(size > 0);
/* create a new buffer and copy the existing data into it */
new_buf = (uint8_t *)xmalloc(size);
cb_extract(cb, new_buf, size);
/* replace buffer */
free(cb->buf);
cb->buf = new_buf;
/* adjust pointers and sizes */
cb->ptr = cb->buf;
cb->buf_size = size;
if (cb->data_size > size)
cb->data_size = size;
}
ssize_t cb_read(circ_buf_t *cb, int fd, size_t nbytes)
{
ssize_t rr;
int count;
struct iovec iov[2];
size_t len;
cb_assert(cb);
assert(fd >= 0);
/* buffer is full, return an error condition */
if (cb_is_full(cb)) return -1;
/* set nbytes appropriately */
if (nbytes == 0 || (size_t)nbytes > cb_space(cb))
nbytes = cb_space(cb);
/* prepare for writing to buffer */
if (cb->ptr == cb->buf) {
/* space only at end of buf */
iov->iov_base = cb->ptr + cb->data_size;
iov->iov_len = nbytes;
count = 1;
} else if (cb->ptr + cb->data_size >= cb->buf + cb->buf_size) {
/* space only before cb->ptr */
iov->iov_base = cb->ptr + cb->data_size - cb->buf_size;
iov->iov_len = nbytes;
count = 1;
} else {
/* space at end and begining of buf */
iov[0].iov_base = cb->ptr + cb->data_size;
len = (cb->buf_size - cb->data_size) - (cb->ptr - cb->buf);
if (len >= nbytes) {
/* first space provides enough */
iov[0].iov_len = nbytes;
count = 1;
} else {
/* need to use both free spaces */
iov[0].iov_len = len;
iov[1].iov_base = cb->buf;
iov[1].iov_len = nbytes - len;
count = 2;
}
}
/* do the actual read */
do {
errno = 0;
rr = readv(fd, iov, count);
} while (errno == EINTR);
/* if rr < 0 an error has occured,
* if rr = 0 nothing needs to be changed.
* update internal stuff only if rr > 0 */
if (rr > 0) {
cb->data_size += rr;
/* sanity check */
cb_assert(cb);
}
return rr;
}
ssize_t cb_recv(circ_buf_t *cb, int fd, size_t nbytes,
struct sockaddr *from, size_t *fromlen)
{
ssize_t rr;
int count;
struct iovec iov[2];
struct msghdr msg;
size_t len;
cb_assert(cb);
assert(fd >= 0);
/* buffer is full, return an error condition */
if (cb_is_full(cb)) return -1;
/* set nbytes appropriately */
if (nbytes == 0 || nbytes > cb_space(cb))
nbytes = cb_space(cb);
/* prepare for writing to buffer */
if (cb->ptr == cb->buf) {
/* space only at end of buf */
iov->iov_base = cb->ptr + cb->data_size;
iov->iov_len = nbytes;
count = 1;
} else if (cb->ptr + cb->data_size >= cb->buf + cb->buf_size) {
/* space only before cb->ptr */
iov->iov_base = cb->ptr + cb->data_size - cb->buf_size;
iov->iov_len = nbytes;
count = 1;
} else {
/* space at end and begining of buf */
iov[0].iov_base = cb->ptr + cb->data_size;
len = (cb->buf_size - cb->data_size) - (cb->ptr - cb->buf);
if (len >= nbytes) {
/* first space provides enough */
iov[0].iov_len = nbytes;
count = 1;
} else {
/* need to use both free spaces */
iov[0].iov_len = len;
iov[1].iov_base = cb->buf;
iov[1].iov_len = nbytes - len;
count = 2;
}
}
/* setup msg structure */
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)from;
msg.msg_namelen = (from != NULL && fromlen != 0)? *fromlen : 0;
msg.msg_iov = iov;
msg.msg_iovlen = count;
/* do the actual recv */
do {
errno = 0;
rr = recvmsg(fd, &msg, 0);
/* copy out updated namelen */
if (from != NULL && fromlen != 0)
*fromlen = msg.msg_namelen;
} while (errno == EINTR);
/* if rr < 0 an error has occured,
* if rr = 0 nothing needs to be changed.
* update internal stuff only if rr > 0 */
if (rr > 0) {
cb->data_size += rr;
/* sanity check */
cb_assert(cb);
}
return rr;
}
ssize_t cb_append(circ_buf_t *cb, const uint8_t *buf, size_t len)
{
ssize_t rr;
int i, count;
struct iovec iov[2];
const uint8_t *tmp;
cb_assert(cb);
assert(buf != NULL);
/* buffer is full, return an error condition */
if (cb_is_full(cb)) return -1;
/* return if len is zero */
if (len == 0) return 0;
/* setup initial values for tmp and rr */
tmp = (const uint8_t *)buf;
rr = 0;
/* prepare for writing to buffer */
if (cb->ptr == cb->buf) {
/* space only at end of buf */
iov->iov_base = cb->ptr + cb->data_size;
iov->iov_len = cb->buf_size - cb->data_size;
count = 1;
} else if (cb->ptr + cb->data_size >= cb->buf + cb->buf_size) {
/* space only before cb->ptr */
iov->iov_base = cb->ptr + cb->data_size - cb->buf_size;
iov->iov_len = cb->buf_size - cb->data_size;
count = 1;
} else {
/* space at end and begining of buf */
iov[0].iov_base = cb->ptr + cb->data_size;
iov[0].iov_len = (cb->buf_size - cb->data_size)
- (cb->ptr - cb->buf);
iov[1].iov_base = cb->buf;
iov[1].iov_len = cb->ptr - cb->buf;
count = 2;
}
/* do the actual copy */
for (i = 0; i < count; ++i) {
size_t chunk_size;
chunk_size = MIN((size_t)iov[i].iov_len, len);
assert(chunk_size > 0);
memcpy((void *)iov[i].iov_base, (const void *)tmp, chunk_size);
tmp = tmp + chunk_size;
len -= chunk_size;
cb->data_size += chunk_size;
rr += chunk_size;
/* sanity check */
cb_assert(cb);
if (len == 0) break;
}
return rr;
}
ssize_t cb_write(circ_buf_t *cb, int fd, size_t nbytes)
{
ssize_t rr;
int count;
struct iovec iov[2];
size_t len;
cb_assert(cb);
assert(fd >= 0);
/* buffer is empty, return immediately */
if (cb_is_empty(cb)) return 0;
/* set nbytes appropriately */
if (nbytes == 0 || nbytes > cb_used(cb))
nbytes = cb_used(cb);
/* prepare for reading from buffer */
if (cb->ptr + cb->data_size > cb->buf + cb->buf_size) {
/* data after ptr and at beginning of buffer */
iov[0].iov_base = cb->ptr;
len = cb->buf_size - (cb->ptr - cb->buf);
if (len >= nbytes) {
/* data after ptr is enough */
iov[0].iov_len = nbytes;
count = 1;
} else {
iov[0].iov_len = len;
iov[1].iov_base = cb->buf;
iov[1].iov_len = nbytes - len;
count = 2;
}
} else {
/* data only after ptr */
iov[0].iov_base = cb->ptr;
iov[0].iov_len = nbytes;
count = 1;
}
/* do the actual write */
do {
errno = 0;
rr = writev(fd, iov, count);
} while (errno == EINTR);
/* if rr < 0 an error has occured,
* if rr = 0 nothing needs to be changed.
* update internal stuff only if rr > 0 */
if (rr > 0) {
assert((size_t)rr <= cb->data_size);
cb->data_size -= rr;
/* update value of cb->ptr */
cb->ptr += rr;
if (cb->ptr >= cb->buf + cb->buf_size)
cb->ptr -= cb->buf_size;
/* sanity check */
cb_assert(cb);
}
return rr;
}
ssize_t cb_send(circ_buf_t *cb, int fd, size_t nbytes,
struct sockaddr *dest, size_t destlen)
{
ssize_t rr;
int count;
struct iovec iov[2];
struct msghdr msg;
size_t len;
cb_assert(cb);
assert(fd >= 0);
/* buffer is empty, return immediately */
if (cb_is_empty(cb)) return 0;
/* set nbytes appropriately */
if (nbytes == 0 || nbytes > cb_used(cb))
nbytes = cb_used(cb);
/* prepare for reading from buffer */
if (cb->ptr + cb->data_size > cb->buf + cb->buf_size) {
/* data after ptr and at beginning of buffer */
iov[0].iov_base = cb->ptr;
len = cb->buf_size - (cb->ptr - cb->buf);
if (len >= nbytes) {
/* data after ptr is enough */
iov[0].iov_len = nbytes;
count = 1;
} else {
iov[0].iov_len = len;
iov[1].iov_base = cb->buf;
iov[1].iov_len = nbytes - len;
count = 2;
}
} else {
/* data only after ptr */
iov[0].iov_base = cb->ptr;
iov[0].iov_len = nbytes;
count = 1;
}
/* setup msg structure */
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)dest;
msg.msg_namelen = destlen;
msg.msg_iov = iov;
msg.msg_iovlen = count;
/* do the actual send */
do {
errno = 0;
rr = sendmsg(fd, &msg, 0);
} while (errno == EINTR);
/* if rr < 0 an error has occured,
* if rr = 0 nothing needs to be changed.
* update internal stuff only if rr > 0 */
if (rr > 0) {
assert((size_t)rr <= cb->data_size);
cb->data_size -= rr;
/* update value of cb->ptr */
cb->ptr += rr;
if (cb->ptr >= cb->buf + cb->buf_size)
cb->ptr -= cb->buf_size;
/* sanity check */
cb_assert(cb);
}
return rr;
}
ssize_t cb_extract(circ_buf_t *cb, uint8_t *buf, size_t len)
{
ssize_t rr;
int i, count;
struct iovec iov[2];
cb_assert(cb);
assert(buf != NULL);
/* buffer is empty, return immediately */
if (cb_is_empty(cb)) return 0;
/* return if len is zero */
if (len == 0) return 0;
/* setup initial value for rr */
rr = 0;
/* prepare for reading from buffer */
if (cb->ptr + cb->data_size > cb->buf + cb->buf_size) {
iov[0].iov_base = cb->ptr;
iov[0].iov_len = cb->buf_size - (cb->ptr - cb->buf);
iov[1].iov_base = cb->buf;
iov[1].iov_len = cb->data_size - iov[0].iov_len;
count = 2;
} else {
iov[0].iov_base = cb->ptr;
iov[0].iov_len = cb->data_size;
count = 1;
}
/* do the actual copy */
for (i = 0; i < count; ++i) {
size_t chunk_size;
chunk_size = MIN((size_t)iov[i].iov_len, len);
assert(chunk_size > 0);
memcpy((void *)buf, (const void *)iov[i].iov_base, chunk_size);
buf = buf + chunk_size;
len -= chunk_size;
rr += chunk_size;
/* sanity check */
cb_assert(cb);
if (len == 0) break;
}
/* if rr = 0 nothing needs to be changed,
* update internal stuff only if rr > 0 */
if (rr > 0) {
assert((size_t)rr <= cb->data_size);
cb->data_size -= rr;
/* update value of cb->ptr */
cb->ptr += rr;
if (cb->ptr >= cb->buf + cb->buf_size)
cb->ptr -= cb->buf_size;
/* sanity check */
cb_assert(cb);
}
return rr;
}
void cb_clear(circ_buf_t *cb)
{
cb_assert(cb);
cb->ptr = cb->buf;
cb->data_size = 0;
}
syntax highlighted by Code2HTML, v. 0.9.1