blob: 58f93e4ea970ae77384b87c1d6e05e68247ddd58 [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005 Filip Pizlo. 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.
*
* THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``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 FILIP PIZLO OR
* CONTRIBUTORS 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 "tsf_internal.h"
#include "tsf_format.h"
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
tsf_buf_rdr_t *tsf_buf_reader_create(int fd,
uint32_t buf_size,
tsf_bool_t keep_fd) {
tsf_buf_rdr_t *result;
result=malloc(sizeof(tsf_buf_rdr_t));
if (result==NULL) {
tsf_set_errno("Could not malloc tsf_buf_rdr_t");
return NULL;
}
result->buf=malloc(buf_size);
if (result->buf==NULL) {
free(result);
tsf_set_errno("Could not malloc buffer");
return NULL;
}
result->fd=fd;
result->keep_fd=keep_fd;
result->size=buf_size;
result->puti=0;
result->geti=0;
return result;
}
void tsf_buf_reader_destroy(tsf_buf_rdr_t *buf) {
/* uh, close the fd if keep_fd is set? */
free(buf->buf);
free(buf);
}
tsf_bool_t tsf_buf_reader_read(void *_buf,
void *data,
uint32_t len) {
tsf_bool_t read_some=tsf_false;
#if defined(HAVE_READV)
struct iovec iov[2];
#endif
tsf_buf_rdr_t *buf=_buf;
/* get as much as possible from the buffer */
if (buf->puti>buf->geti) {
uint32_t avail=buf->puti-buf->geti;
read_some=tsf_true;
if (avail>len) {
/* can be satisfied entirely from the buffer */
memcpy(data,buf->buf+buf->geti,len);
buf->geti+=len;
return tsf_true;
}
memcpy(data,buf->buf+buf->geti,avail);
data=((uint8_t*)data)+avail;
len-=avail;
}
buf->puti=0;
buf->geti=0;
if (!len) {
return tsf_true;
}
/* now satisfy the rest of the request by issuing a new read */
#if defined(HAVE_READV)
iov[0].iov_base=data;
iov[0].iov_len=len;
iov[1].iov_base=(void*)buf->buf;
iov[1].iov_len=buf->size;
for (;;) {
int res=readv(buf->fd,iov,2);
if (res<0) {
if (errno==EINTR) {
continue;
}
tsf_set_errno("Calling readv() in tsf_buf_reader_read()");
return tsf_false;
}
if (res==0) {
tsf_set_error(read_some?
TSF_E_ERRONEOUS_EOF:TSF_E_UNEXPECTED_EOF,
"Calling readv() in tsf_buf_reader_read()");
return tsf_false;
}
read_some=tsf_true;
if ((unsigned)res>=iov[0].iov_len) {
buf->puti=res-iov[0].iov_len;
break;
}
iov[0].iov_base+=res;
iov[0].iov_len-=res;
}
#else
if (len>=buf->size) {
tsf_bool_t res=tsf_fd_reader(&buf->fd,data,len);
if (read_some && !res &&
tsf_get_error_code()==TSF_E_UNEXPECTED_EOF) {
tsf_set_error(TSF_E_ERRONEOUS_EOF,
"tsf_fd_reader() experienced an EOF");
}
return res;
}
/* idea is this: we keep reading until we have enough to satisfy the
* current request, but we allow the read to overflow for as many bytes
* as we have available in the buffer. */
while (buf->puti<len) {
int res=read(buf->fd,
buf->buf+buf->puti,
buf->size-buf->puti);
if (res<0) {
if (errno==EINTR) {
continue;
}
tsf_set_errno("Calling read() in tsf_buf_reader_read()");
return tsf_false;
}
if (res==0) {
tsf_set_error(read_some?
TSF_E_ERRONEOUS_EOF:TSF_E_UNEXPECTED_EOF,
"Calling read() in tsf_buf_reader_read()");
return tsf_false;
}
read_some=tsf_true;
buf->puti+=res;
}
memcpy(data,buf->buf,len);
buf->geti=len;
#endif
return tsf_true;
}
uint32_t tsf_buf_reader_get_remaining(tsf_buf_rdr_t *buf) {
return buf->puti - buf->geti;
}
void tsf_buf_reader_reset(tsf_buf_rdr_t *buf) {
buf->puti=0;
buf->geti=0;
}