forked from Telodendria/Telodendria
Move low-level fopencookie()/funopen() functionality to Io API.
The Stream API now provides the buffered I/O functionality analogous to the C standard library.
This commit is contained in:
parent
5dbaf3c223
commit
92da3542a6
4 changed files with 546 additions and 151 deletions
230
src/Io.c
Normal file
230
src/Io.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
#include <Io.h>
|
||||
|
||||
#include <Memory.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef IO_PRINTF_BUFFER
|
||||
#define IO_PRINTF_BUFFER 1024
|
||||
#endif
|
||||
|
||||
struct Io
|
||||
{
|
||||
IoFunctions io;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
Io *
|
||||
IoCreate(void *cookie, IoFunctions funcs)
|
||||
{
|
||||
Io *io;
|
||||
|
||||
/* Must have at least read or write */
|
||||
if (!funcs.read && !funcs.write)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io = Malloc(sizeof(Io));
|
||||
|
||||
if (!io)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io->cookie = cookie;
|
||||
|
||||
io->io.read = funcs.read;
|
||||
io->io.write = funcs.write;
|
||||
io->io.seek = funcs.seek;
|
||||
io->io.close = funcs.close;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
IoRead(Io *io, void *buf, size_t nBytes)
|
||||
{
|
||||
if (!io || !io->io.read)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return io->io.read(io->cookie, buf, nBytes);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
IoWrite(Io *io, void *buf, size_t nBytes)
|
||||
{
|
||||
if (!io || !io->io.write)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return io->io.write(io->cookie, buf, nBytes);
|
||||
}
|
||||
|
||||
off_t
|
||||
IoSeek(Io *io, off_t offset, int whence)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!io->io.seek)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return io->io.seek(io->cookie, offset, whence);
|
||||
}
|
||||
|
||||
int
|
||||
IoClose(Io *io)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!io)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (io->io.close)
|
||||
{
|
||||
ret = io->io.close(io->cookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
Free(io);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
IoReadFd(void *cookie, void *buf, size_t nBytes)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
|
||||
return read(fd, buf, nBytes);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
IoWriteFd(void *cookie, void *buf, size_t nBytes)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
|
||||
return write(fd, buf, nBytes);
|
||||
}
|
||||
|
||||
static off_t
|
||||
IoSeekFd(void *cookie, off_t offset, int whence)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
|
||||
return lseek(fd, offset, whence);
|
||||
}
|
||||
|
||||
static int
|
||||
IoCloseFd(void *cookie)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
|
||||
Free(cookie);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
Io *
|
||||
IoOpen(int fd)
|
||||
{
|
||||
int *cookie = Malloc(sizeof(int));
|
||||
IoFunctions f;
|
||||
|
||||
if (!cookie)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*cookie = fd;
|
||||
|
||||
f.read = IoReadFd;
|
||||
f.write = IoWriteFd;
|
||||
f.seek = IoSeekFd;
|
||||
f.close = IoCloseFd;
|
||||
|
||||
return IoCreate(cookie, f);
|
||||
}
|
||||
|
||||
int
|
||||
IoVprintf(Io *io, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf;
|
||||
size_t write;
|
||||
|
||||
int ret;
|
||||
|
||||
if (!io || !fmt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = Malloc(IO_PRINTF_BUFFER);
|
||||
if (!buf)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
write = vsnprintf(buf, IO_PRINTF_BUFFER, fmt, ap);
|
||||
|
||||
if (write < 0)
|
||||
{
|
||||
Free(buf);
|
||||
return write;
|
||||
}
|
||||
|
||||
/* Number of bytes to write exceeded buffer size; this should
|
||||
* be rare, but may occasionally happen. If it does, realloc to
|
||||
* the correct size and try again.
|
||||
*/
|
||||
if (write >= IO_PRINTF_BUFFER)
|
||||
{
|
||||
char *new = Realloc(buf, write + 1);
|
||||
if (!new)
|
||||
{
|
||||
Free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = new;
|
||||
|
||||
/* This time we don't care about the return value */
|
||||
vsnprintf(buf, write, fmt, ap);
|
||||
}
|
||||
|
||||
ret = IoWrite(io, buf, write);
|
||||
|
||||
Free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
IoPrintf(Io *io, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = IoVprintf(io, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
355
src/Stream.c
355
src/Stream.c
|
@ -1,170 +1,319 @@
|
|||
#include <Stream.h>
|
||||
|
||||
#ifndef STREAM_BUFFER
|
||||
#define STREAM_BUFFER 4096
|
||||
#endif
|
||||
|
||||
#define STREAM_EOF (1 << 0)
|
||||
#define STREAM_ERR (1 << 1)
|
||||
|
||||
#include <Io.h>
|
||||
#include <Memory.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Stream
|
||||
{
|
||||
StreamFunctions io;
|
||||
void *cookie;
|
||||
Io *io;
|
||||
|
||||
int *ub;
|
||||
size_t ubSize;
|
||||
size_t ubLen;
|
||||
int *rBuf;
|
||||
size_t rLen;
|
||||
size_t rOff;
|
||||
|
||||
int *wBuf;
|
||||
size_t wLen;
|
||||
|
||||
int *ugBuf;
|
||||
size_t ugSize;
|
||||
size_t ugLen;
|
||||
|
||||
int flags : 2;
|
||||
};
|
||||
|
||||
Stream *
|
||||
StreamCreate(void *cookie, StreamFunctions funcs)
|
||||
StreamOpen(Io * io)
|
||||
{
|
||||
Stream *stream;
|
||||
|
||||
if (!funcs.read || !funcs.write)
|
||||
if (!io)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream = Malloc(sizeof(Stream));
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->cookie = cookie;
|
||||
|
||||
stream->io.read = funcs.read;
|
||||
stream->io.write = funcs.write;
|
||||
stream->io.seek = funcs.seek;
|
||||
stream->io.close = funcs.close;
|
||||
|
||||
stream->ubSize = 0;
|
||||
memset(stream, 0, sizeof(Stream));
|
||||
stream->io = io;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
StreamRead(Stream *stream, void *buf, size_t nBytes)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stream->io.read(stream->cookie, buf, nBytes);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
StreamWrite(Stream *stream, void *buf, size_t nBytes)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stream->io.write(stream->cookie, buf, nBytes);
|
||||
}
|
||||
|
||||
off_t
|
||||
StreamSeek(Stream *stream, off_t offset, int whence)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!stream->io.seek)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stream->io.seek(stream->cookie, offset, whence);
|
||||
}
|
||||
|
||||
int
|
||||
StreamClose(Stream *stream)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (stream->io.close)
|
||||
if (stream->rBuf)
|
||||
{
|
||||
ret = stream->io.close(stream->cookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 0;
|
||||
Free(stream->rBuf);
|
||||
}
|
||||
|
||||
if (stream->ubSize)
|
||||
if (stream->wBuf)
|
||||
{
|
||||
Free(stream->ub);
|
||||
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
||||
Free(stream->wBuf);
|
||||
|
||||
if (writeRes == -1)
|
||||
{
|
||||
ret = EOF;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->ugBuf)
|
||||
{
|
||||
Free(stream->ugBuf);
|
||||
}
|
||||
|
||||
ret = IoClose(stream->io);
|
||||
Free(stream);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
StreamReadFd(void *cookie, void *buf, size_t nBytes)
|
||||
int
|
||||
StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
|
||||
return read(fd, buf, nBytes);
|
||||
if (!stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
StreamWriteFd(void *cookie, void *buf, size_t nBytes)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
StreamFlush(stream); /* Flush the buffer out before doing the printf */
|
||||
|
||||
return write(fd, buf, nBytes);
|
||||
/* Defer printf to underlying Io. We probably should buffer the
|
||||
* printf operation just like StreamPutc() so we don't have to
|
||||
* flush the buffer.
|
||||
*/
|
||||
return IoVprintf(stream->io, fmt, ap);
|
||||
}
|
||||
|
||||
static off_t
|
||||
StreamSeekFd(void *cookie, off_t offset, int whence)
|
||||
int
|
||||
StreamPrintf(Stream * stream, const char *fmt,...)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
return lseek(fd, offset, whence);
|
||||
va_start(ap, fmt);
|
||||
ret = StreamVprintf(stream, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
StreamCloseFd(void *cookie)
|
||||
int
|
||||
StreamGetc(Stream * stream)
|
||||
{
|
||||
int fd = *((int *) cookie);
|
||||
int c;
|
||||
|
||||
return close(fd);
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
Stream *
|
||||
StreamOpen(int fd)
|
||||
/* Empty the ungetc stack first */
|
||||
if (stream->ugLen)
|
||||
{
|
||||
int *fdp = Malloc(sizeof(int));
|
||||
StreamFunctions f;
|
||||
|
||||
if (!fdp)
|
||||
{
|
||||
return NULL;
|
||||
c = stream->ugBuf[stream->ugLen - 1];
|
||||
stream->ugLen--;
|
||||
return c;
|
||||
}
|
||||
|
||||
*fdp = fd;
|
||||
|
||||
f.read = StreamReadFd;
|
||||
f.write = StreamWriteFd;
|
||||
f.seek = StreamSeekFd;
|
||||
f.close = StreamCloseFd;
|
||||
|
||||
return StreamCreate(fdp, f);
|
||||
|
||||
if (stream->flags & EOF)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (!stream->rBuf)
|
||||
{
|
||||
/* No buffer allocated yet */
|
||||
stream->rBuf = Malloc(STREAM_BUFFER * sizeof(int));
|
||||
if (!stream->rBuf)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->rOff = 0;
|
||||
stream->rLen = 0;
|
||||
}
|
||||
|
||||
if (stream->rOff >= stream->rLen)
|
||||
{
|
||||
/* We read through the entire buffer; get a new one */
|
||||
ssize_t readRes = IoRead(stream->io, stream->rBuf, STREAM_BUFFER);
|
||||
|
||||
if (readRes == 0)
|
||||
{
|
||||
stream->flags |= STREAM_EOF;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (readRes == -1)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->rOff = 0;
|
||||
stream->rLen = readRes;
|
||||
}
|
||||
|
||||
/* Read the character in the buffer and advance the offset */
|
||||
c = stream->rBuf[stream->rOff];
|
||||
stream->rOff++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
StreamUngetc(Stream * stream, int c)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (!stream->ugBuf)
|
||||
{
|
||||
stream->ugSize = STREAM_BUFFER;
|
||||
stream->ugBuf = Malloc(stream->ugSize);
|
||||
|
||||
if (!stream->ugBuf)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->ugLen >= stream->ugSize)
|
||||
{
|
||||
int *new;
|
||||
|
||||
stream->ugSize += STREAM_BUFFER;
|
||||
new = Realloc(stream->ugBuf, stream->ugSize);
|
||||
if (!new)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
Free(stream->ugBuf);
|
||||
stream->ugBuf = NULL;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
Free(stream->ugBuf);
|
||||
stream->ugBuf = new;
|
||||
}
|
||||
|
||||
stream->ugBuf[stream->ugLen - 1] = c;
|
||||
stream->ugLen++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
StreamPutc(Stream * stream, int c)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (!stream->wBuf)
|
||||
{
|
||||
stream->wBuf = Malloc(STREAM_BUFFER * sizeof(int));
|
||||
if (!stream->wBuf)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->wLen == STREAM_BUFFER)
|
||||
{
|
||||
/* Buffer full; write it */
|
||||
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
||||
|
||||
if (writeRes == -1)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->wLen = 0;
|
||||
}
|
||||
|
||||
stream->wBuf[stream->wLen] = c;
|
||||
stream->wLen++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
StreamEof(Stream * stream)
|
||||
{
|
||||
return stream && (stream->flags & STREAM_EOF);
|
||||
}
|
||||
|
||||
int
|
||||
StreamError(Stream * stream)
|
||||
{
|
||||
return stream && (stream->flags & STREAM_ERR);
|
||||
}
|
||||
|
||||
void
|
||||
StreamClearError(Stream * stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
stream->flags &= ~STREAM_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
StreamFlush(Stream * stream)
|
||||
{
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (stream->wLen)
|
||||
{
|
||||
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
||||
|
||||
if (writeRes == -1)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->wLen = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
46
src/include/Io.h
Normal file
46
src/include/Io.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef TELODENDRIA_IO_H
|
||||
#define TELODENDRIA_IO_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct Io Io;
|
||||
|
||||
typedef ssize_t (IoReadFunc) (void *, void *, size_t);
|
||||
typedef ssize_t (IoWriteFunc) (void *, void *, size_t);
|
||||
typedef off_t (IoSeekFunc) (void *, off_t, int);
|
||||
typedef int (IoCloseFunc) (void *);
|
||||
|
||||
typedef struct IoFunctions
|
||||
{
|
||||
IoReadFunc *read;
|
||||
IoWriteFunc *write;
|
||||
IoSeekFunc *seek;
|
||||
IoCloseFunc *close;
|
||||
} IoFunctions;
|
||||
|
||||
extern Io *
|
||||
IoCreate(void *, IoFunctions);
|
||||
|
||||
extern ssize_t
|
||||
IoRead(Io *, void *, size_t);
|
||||
|
||||
extern ssize_t
|
||||
IoWrite(Io *, void *, size_t);
|
||||
|
||||
extern off_t
|
||||
IoSeek(Io *, off_t, int);
|
||||
|
||||
extern int
|
||||
IoClose(Io *);
|
||||
|
||||
extern Io *
|
||||
IoOpen(int);
|
||||
|
||||
extern int
|
||||
IoVprintf(Io *, const char *, va_list);
|
||||
|
||||
extern int
|
||||
IoPrintf(Io *, const char *, ...);
|
||||
|
||||
#endif /* TELODENDRIA_IO_H */
|
|
@ -1,51 +1,18 @@
|
|||
#ifndef TELODENDRIA_STREAM_H
|
||||
#define TELODENDRIA_STREAM_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <Io.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct Stream Stream;
|
||||
|
||||
/*
|
||||
* Low-level Stream API in the style of POSIX system calls.
|
||||
* Heavily inspired by GNU fopencookie() and BSD funopen().
|
||||
*/
|
||||
|
||||
typedef ssize_t (StreamReadFunc) (void *, void *, size_t);
|
||||
typedef ssize_t (StreamWriteFunc) (void *, void *, size_t);
|
||||
typedef off_t (StreamSeekFunc) (void *, off_t, int);
|
||||
typedef int (StreamCloseFunc) (void *);
|
||||
|
||||
typedef struct StreamFunctions
|
||||
{
|
||||
StreamReadFunc *read;
|
||||
StreamWriteFunc *write;
|
||||
StreamSeekFunc *seek;
|
||||
StreamCloseFunc *close;
|
||||
} StreamFunctions;
|
||||
|
||||
extern Stream *
|
||||
StreamCreate(void *, StreamFunctions);
|
||||
|
||||
extern ssize_t
|
||||
StreamRead(Stream *, void *, size_t);
|
||||
|
||||
extern ssize_t
|
||||
StreamWrite(Stream *, void *, size_t);
|
||||
|
||||
extern off_t
|
||||
StreamSeek(Stream *, off_t, int);
|
||||
StreamOpen(Io *io);
|
||||
|
||||
extern int
|
||||
StreamClose(Stream *);
|
||||
|
||||
extern Stream *
|
||||
StreamOpen(int);
|
||||
|
||||
/*
|
||||
* High level Stream API in the style of C standard I/O.
|
||||
*/
|
||||
|
||||
extern int
|
||||
StreamVprintf(Stream *, const char *, va_list);
|
||||
|
||||
|
@ -58,6 +25,9 @@ StreamGetc(Stream *);
|
|||
extern int
|
||||
StreamUngetc(Stream *, int);
|
||||
|
||||
extern int
|
||||
StreamPutc(Stream *, int);
|
||||
|
||||
extern int
|
||||
StreamEof(Stream *);
|
||||
|
||||
|
|
Loading…
Reference in a new issue