Add IoCopy() and StreamCopy()

Both do buffered reads and writes, but IoCopy() uses IoRead() and
IoWrite() directly, whereas StreamCopy() relies on StreamGetc() and
StreamPutc(), which manipulate the stream buffers.
This commit is contained in:
Jordan Bancino 2023-03-15 17:14:16 +00:00
parent 92da3542a6
commit ab4755240a
4 changed files with 115 additions and 10 deletions

View file

@ -5,8 +5,8 @@
#include <errno.h>
#include <stdio.h>
#ifndef IO_PRINTF_BUFFER
#define IO_PRINTF_BUFFER 1024
#ifndef IO_BUFFER
#define IO_BUFFER 4096
#endif
struct Io
@ -177,13 +177,13 @@ IoVprintf(Io *io, const char *fmt, va_list ap)
return -1;
}
buf = Malloc(IO_PRINTF_BUFFER);
buf = Malloc(IO_BUFFER);
if (!buf)
{
return -1;
}
write = vsnprintf(buf, IO_PRINTF_BUFFER, fmt, ap);
write = vsnprintf(buf, IO_BUFFER, fmt, ap);
if (write < 0)
{
@ -195,7 +195,7 @@ IoVprintf(Io *io, const char *fmt, va_list ap)
* be rare, but may occasionally happen. If it does, realloc to
* the correct size and try again.
*/
if (write >= IO_PRINTF_BUFFER)
if (write >= IO_BUFFER)
{
char *new = Realloc(buf, write + 1);
if (!new)
@ -228,3 +228,37 @@ IoPrintf(Io *io, const char *fmt, ...)
return ret;
}
ssize_t
IoCopy(Io *in, Io *out)
{
ssize_t nBytes = 0;
char buf[IO_BUFFER];
ssize_t rRes;
ssize_t wRes;
if (!in || !out)
{
errno = EBADF;
return -1;
}
while ((rRes = IoRead(in, &buf, IO_BUFFER)) != 0)
{
if (rRes == -1)
{
return -1;
}
wRes = IoWrite(out, &buf, rRes);
if (wRes == -1)
{
return -1;
}
nBytes += wRes;
}
return nBytes;
}

View file

@ -4,11 +4,20 @@
#define STREAM_BUFFER 4096
#endif
#ifndef STREAM_RETRIES
#define STREAM_RETRIES 10
#endif
#ifndef STREAM_DELAY
#define STREAM_DELAY 2
#endif
#define STREAM_EOF (1 << 0)
#define STREAM_ERR (1 << 1)
#include <Io.h>
#include <Memory.h>
#include <Util.h>
#include <stdio.h>
#include <errno.h>
@ -29,7 +38,7 @@ struct Stream
size_t ugSize;
size_t ugLen;
int flags : 2;
int flags:2;
};
Stream *
@ -55,7 +64,7 @@ StreamOpen(Io * io)
}
int
StreamClose(Stream *stream)
StreamClose(Stream * stream)
{
int ret = 0;
@ -73,6 +82,7 @@ StreamClose(Stream *stream)
if (stream->wBuf)
{
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
Free(stream->wBuf);
if (writeRes == -1)
@ -100,12 +110,12 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
return -1;
}
StreamFlush(stream); /* Flush the buffer out before doing the printf */
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.
*/
* flush the buffer. */
return IoVprintf(stream->io, fmt, ap);
}
@ -317,3 +327,58 @@ StreamFlush(Stream * stream)
return 0;
}
ssize_t
StreamCopy(Stream * in, Stream * out)
{
ssize_t nBytes = 0;
int c;
int tries = 0;
int readFlg = 0;
while (1)
{
c = StreamGetc(in);
if (StreamEof(in))
{
break;
}
if (StreamError(in))
{
if (errno == EAGAIN)
{
StreamClearError(in);
tries++;
if (tries >= STREAM_RETRIES || readFlg)
{
break;
}
else
{
UtilSleepMillis(STREAM_DELAY);
continue;
}
}
else
{
break;
}
}
/* As soon as we've successfully read a byte, treat future
* EAGAINs as EOF, because somebody might have forgotten to
* close their stream. */
readFlg = 1;
tries = 0;
StreamPutc(out, c);
nBytes++;
}
StreamFlush(out);
return nBytes;
}

View file

@ -43,4 +43,7 @@ IoVprintf(Io *, const char *, va_list);
extern int
IoPrintf(Io *, const char *, ...);
extern ssize_t
IoCopy(Io *, Io *);
#endif /* TELODENDRIA_IO_H */

View file

@ -40,4 +40,7 @@ StreamClearError(Stream *);
extern int
StreamFlush(Stream *);
extern ssize_t
StreamCopy(Stream *, Stream *);
#endif /* TELODENDRIA_STREAM_H */