Telodendria/src/Stream.c
Jordan Bancino 92da3542a6 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.
2023-03-15 16:47:34 +00:00

319 lines
5.1 KiB
C

#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
{
Io *io;
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 *
StreamOpen(Io * io)
{
Stream *stream;
if (!io)
{
return NULL;
}
stream = Malloc(sizeof(Stream));
if (!stream)
{
return NULL;
}
memset(stream, 0, sizeof(Stream));
stream->io = io;
return stream;
}
int
StreamClose(Stream *stream)
{
int ret = 0;
if (!stream)
{
errno = EBADF;
return EOF;
}
if (stream->rBuf)
{
Free(stream->rBuf);
}
if (stream->wBuf)
{
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;
}
int
StreamVprintf(Stream * stream, const char *fmt, va_list ap)
{
if (!stream)
{
return -1;
}
StreamFlush(stream); /* Flush the buffer out before doing the printf */
/* 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);
}
int
StreamPrintf(Stream * stream, const char *fmt,...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = StreamVprintf(stream, fmt, ap);
va_end(ap);
return ret;
}
int
StreamGetc(Stream * stream)
{
int c;
if (!stream)
{
errno = EBADF;
return EOF;
}
/* Empty the ungetc stack first */
if (stream->ugLen)
{
c = stream->ugBuf[stream->ugLen - 1];
stream->ugLen--;
return c;
}
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;
}