273 lines
5.4 KiB
C
273 lines
5.4 KiB
C
#include "iocache.h"
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
struct iocache {
|
|
char *ioc_buf; /* the data */
|
|
unsigned long ioc_offset; /* where we're reading in the buffer */
|
|
unsigned long ioc_buflen; /* the amount of data read into the buffer */
|
|
unsigned long ioc_bufsize; /* size of the buffer */
|
|
};
|
|
|
|
void iocache_destroy(iocache *ioc)
|
|
{
|
|
if (!ioc)
|
|
return;
|
|
|
|
if (ioc->ioc_buf)
|
|
free(ioc->ioc_buf);
|
|
|
|
free(ioc);
|
|
}
|
|
|
|
/**
|
|
* Attempts to move data from the end to the beginning
|
|
* of the ioc_buf, expelling old data to make more room
|
|
* for new reads.
|
|
*/
|
|
static inline void iocache_move_data(iocache *ioc)
|
|
{
|
|
unsigned long available;
|
|
|
|
if (!ioc->ioc_offset)
|
|
return; /* nothing to do */
|
|
|
|
/* if we're fully read, we only have to reset the counters */
|
|
if (ioc->ioc_buflen <= ioc->ioc_offset) {
|
|
iocache_reset(ioc);
|
|
return;
|
|
}
|
|
available = ioc->ioc_buflen - ioc->ioc_offset;
|
|
memmove(ioc->ioc_buf, ioc->ioc_buf + ioc->ioc_offset, available);
|
|
ioc->ioc_offset = 0;
|
|
ioc->ioc_buflen = available;
|
|
}
|
|
|
|
void iocache_reset(iocache *ioc)
|
|
{
|
|
if (ioc)
|
|
ioc->ioc_offset = ioc->ioc_buflen = 0;
|
|
}
|
|
|
|
int iocache_resize(iocache *ioc, unsigned long new_size)
|
|
{
|
|
char *buf;
|
|
|
|
if (!ioc)
|
|
return -1;
|
|
|
|
iocache_move_data(ioc);
|
|
|
|
buf = realloc(ioc->ioc_buf, new_size);
|
|
if (!buf)
|
|
return -1;
|
|
ioc->ioc_buf = buf;
|
|
ioc->ioc_bufsize = new_size;
|
|
return 0;
|
|
}
|
|
|
|
int iocache_grow(iocache *ioc, unsigned long increment)
|
|
{
|
|
return iocache_resize(ioc, iocache_size(ioc) + increment);
|
|
}
|
|
|
|
unsigned long iocache_size(iocache *ioc)
|
|
{
|
|
return ioc ? ioc->ioc_bufsize : 0;
|
|
}
|
|
|
|
unsigned long iocache_capacity(iocache *ioc)
|
|
{
|
|
if (!ioc)
|
|
return -1;
|
|
|
|
if (!ioc->ioc_buf)
|
|
return -2;
|
|
|
|
if (!ioc->ioc_bufsize)
|
|
return -3;
|
|
|
|
iocache_move_data(ioc);
|
|
|
|
return ioc->ioc_bufsize - ioc->ioc_buflen;
|
|
}
|
|
|
|
unsigned long iocache_available(iocache *ioc)
|
|
{
|
|
if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen)
|
|
return 0;
|
|
|
|
return ioc->ioc_buflen - ioc->ioc_offset;
|
|
}
|
|
|
|
char *iocache_use_size(iocache *ioc, unsigned long size)
|
|
{
|
|
char *ret;
|
|
|
|
if (!ioc || !ioc->ioc_buf)
|
|
return NULL;
|
|
if (ioc->ioc_bufsize < size || iocache_available(ioc) < size)
|
|
return NULL;
|
|
|
|
ret = ioc->ioc_buf + ioc->ioc_offset;
|
|
ioc->ioc_offset += size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int iocache_unuse_size(iocache *ioc, unsigned long size)
|
|
{
|
|
if (!ioc || !ioc->ioc_buf)
|
|
return -1;
|
|
if (size > ioc->ioc_offset)
|
|
return -1;
|
|
|
|
ioc->ioc_offset -= size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
char *iocache_use_delim(iocache *ioc, const char *delim, size_t delim_len, unsigned long *size)
|
|
{
|
|
char *ptr = NULL;
|
|
char *buf;
|
|
unsigned long remains;
|
|
|
|
if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen)
|
|
return NULL;
|
|
|
|
*size = 0;
|
|
if (ioc->ioc_offset >= ioc->ioc_buflen) {
|
|
iocache_move_data(ioc);
|
|
return NULL;
|
|
}
|
|
|
|
buf = &ioc->ioc_buf[ioc->ioc_offset];
|
|
remains = iocache_available(ioc);
|
|
while (remains >= delim_len) {
|
|
unsigned long jump;
|
|
ptr = memchr(buf, *delim, remains - (delim_len - 1));
|
|
if (!ptr) {
|
|
return NULL;
|
|
}
|
|
if (delim_len == 1 || !memcmp(ptr, delim, delim_len)) {
|
|
unsigned long ioc_start;
|
|
|
|
ioc_start = (unsigned long)ioc->ioc_buf + ioc->ioc_offset;
|
|
*size = (unsigned long)ptr - ioc_start;
|
|
|
|
/* make sure we use up all of the delimiter as well */
|
|
return iocache_use_size(ioc, delim_len + *size);
|
|
}
|
|
jump = 1 + (unsigned long)ptr - (unsigned long)buf;
|
|
remains -= jump;
|
|
buf += jump;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
iocache *iocache_create(unsigned long size)
|
|
{
|
|
iocache *ioc;
|
|
|
|
ioc = calloc(1, sizeof(*ioc));
|
|
if (ioc && size) {
|
|
ioc->ioc_buf = calloc(1, size);
|
|
if (!ioc->ioc_buf) {
|
|
free(ioc);
|
|
return NULL;
|
|
}
|
|
ioc->ioc_bufsize = size;
|
|
}
|
|
|
|
return ioc;
|
|
}
|
|
|
|
int iocache_read(iocache *ioc, int fd)
|
|
{
|
|
int to_read, bytes_read, ret;
|
|
|
|
if (!ioc || !ioc->ioc_buf || fd < 0)
|
|
return -1;
|
|
|
|
/* we make sure we've got as much room as possible */
|
|
iocache_move_data(ioc);
|
|
|
|
/* if we've maxed out our buflen, grow by 2x to be safe */
|
|
if (ioc->ioc_buflen >= ioc->ioc_bufsize) {
|
|
|
|
to_read = ioc->ioc_buflen + ioc->ioc_bufsize;
|
|
ret = iocache_grow(ioc, ioc->ioc_bufsize);
|
|
if (ret == -1) {
|
|
to_read = ioc->ioc_buflen;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* calculate the size we should read */
|
|
to_read = ioc->ioc_bufsize - ioc->ioc_buflen;
|
|
}
|
|
|
|
bytes_read = read(fd, ioc->ioc_buf + ioc->ioc_buflen, to_read);
|
|
if (bytes_read > 0) {
|
|
ioc->ioc_buflen += bytes_read;
|
|
}
|
|
|
|
return bytes_read;
|
|
}
|
|
|
|
|
|
int iocache_add(iocache *ioc, char *buf, unsigned int len)
|
|
{
|
|
if (!ioc || iocache_capacity(ioc) < len)
|
|
return -1;
|
|
|
|
memcpy(ioc->ioc_buf + ioc->ioc_offset, buf, len);
|
|
ioc->ioc_buflen += len;
|
|
return ioc->ioc_buflen - ioc->ioc_offset;
|
|
}
|
|
|
|
/*
|
|
* Three cases to handle:
|
|
* - buf has data, iocache doesn't.
|
|
* - iocache has data, buf doesn't.
|
|
* - both buf and iocache has data.
|
|
*/
|
|
int iocache_sendto(iocache *ioc, int fd, char *buf, unsigned int len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
|
|
{
|
|
int sent;
|
|
|
|
errno = 0;
|
|
if (!ioc)
|
|
return -1;
|
|
|
|
if (!ioc->ioc_buflen && !len)
|
|
return 0;
|
|
|
|
if (ioc->ioc_buf && iocache_available(ioc)) {
|
|
if (buf && len) {
|
|
/* copy buf and len to iocache buffer to use just one write */
|
|
if (iocache_capacity(ioc) < len) {
|
|
if (iocache_grow(ioc, iocache_size(ioc)) < 0)
|
|
return -1;
|
|
}
|
|
if (iocache_add(ioc, buf, len) < 0)
|
|
return -1;
|
|
}
|
|
buf = ioc->ioc_buf;
|
|
len = iocache_available(ioc);
|
|
}
|
|
|
|
sent = sendto(fd, buf, len, flags, dest_addr, addrlen);
|
|
if (sent < 1)
|
|
return -errno;
|
|
|
|
if (iocache_available(ioc))
|
|
iocache_use_size(ioc, sent);
|
|
|
|
return sent;
|
|
}
|