diff --git a/src/file.c b/src/file.c index 5c21eb2..350b100 100644 --- a/src/file.c +++ b/src/file.c @@ -144,8 +144,8 @@ ctor(void *ptr, va_list args) const char *mode = va_arg(args, const char*); int flags; - /* Make sure we have a valid pointer in case we error out */ - file->stream.ops = NULL; + /* Make sure we have a valid file in case we error out */ + cfw_stream->ctor(ptr, args); file->eof = false; if ((flags = parse_mode(mode)) == -1) @@ -162,7 +162,7 @@ ctor(void *ptr, va_list args) static void dtor(void *ptr) { - cfw_stream_close(ptr); + cfw_stream->dtor(ptr); } static CFWClass class = { diff --git a/src/stream.c b/src/stream.c index 754476e..b04c400 100644 --- a/src/stream.c +++ b/src/stream.c @@ -24,16 +24,21 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include "stream.h" +#define BUFFER_SIZE 4096 + static bool ctor(void *ptr, va_list args) { CFWStream *stream = ptr; stream->ops = NULL; + stream->cache = NULL; + stream->cache_len = 0; return true; } @@ -53,10 +58,178 @@ cfw_stream_read(void *ptr, void *buf, size_t len) if (stream == NULL || stream->ops == NULL) return -1; - if ((ret = stream->ops->read(stream, buf, len)) < -1) - ret = -1; + if (stream->cache == NULL) { + if ((ret = stream->ops->read(stream, buf, len)) < -1) + ret = -1; - return ret; + return ret; + } + + if (len >= stream->cache_len) { + ret = stream->cache_len; + + memcpy(buf, stream->cache, stream->cache_len); + + free(stream->cache); + stream->cache = NULL; + stream->cache_len = 0; + + return ret; + } else { + char *tmp; + + if ((tmp = malloc(stream->cache_len - len)) == NULL) + return -1; + memcpy(tmp, stream->cache + len, stream->cache_len - len); + memcpy(buf, stream->cache, len); + + free(stream->cache); + stream->cache = tmp; + stream->cache_len -= len; + + return len; + } +} + +CFWString* +cfw_stream_read_line(void *ptr) +{ + CFWStream *stream = ptr; + CFWString *ret; + char *buf, *ret_str, *new_cache; + ssize_t buf_len; + size_t i, ret_len; + + /* Look if there is a line or \0 in our cache */ + if (stream->cache != NULL) { + for (i = 0; i < stream->cache_len; i++) { + if (stream->cache[i] == '\n' || + stream->cache[i] == '\0') { + ret_len = i; + if (i > 0 && stream->cache[i - 1] == '\r') + ret_len--; + + ret_str = cfw_strndup(stream->cache, ret_len); + if (ret_str == NULL) + return NULL; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + new_cache = malloc(stream->cache_len - i - 1); + if (new_cache == NULL) + return NULL; + memcpy(new_cache, stream->cache + i + 1, + stream->cache_len - i - 1); + + free(stream->cache); + stream->cache = new_cache; + stream->cache_len -= i + 1; + + return ret; + } + } + } + + /* Read and see if we get a newline or \0 */ + + if ((buf = malloc(BUFFER_SIZE)) == NULL) + return NULL; + + for (;;) { + if (stream->ops->eof(stream)) { + free(buf); + + if (stream->cache == NULL) + return NULL; + + ret_len = stream->cache_len; + + if (ret_len > 0 && stream->cache[ret_len - 1] == '\r') + ret_len--; + + ret_str = cfw_strndup(stream->cache, ret_len); + if (ret_str == NULL) + return NULL; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + free(stream->cache); + stream->cache = NULL; + stream->cache_len = 0; + + return ret; + } + + buf_len = stream->ops->read(stream, buf, BUFFER_SIZE); + if (buf_len == -1) { + free(buf); + return NULL; + } + + /* Look if there's a newline or \0 */ + for (i = 0; i < buf_len; i++) { + if (buf[i] == '\n' || buf[i] == '\0') { + ret_len = stream->cache_len + i; + + if ((ret_str = malloc(ret_len + 1)) == NULL) { + /* + * FIXME: We lost the current buffer. + * Mark the stream as broken? + */ + free(buf); + return NULL; + } + memcpy(ret_str, stream->cache, + stream->cache_len); + memcpy(ret_str + stream->cache_len, buf, i); + if (ret_len > 0 && ret_str[ret_len - 1] == '\r') + ret_len--; + ret_str[ret_len] = '\0'; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(buf); + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + new_cache = malloc(buf_len - i - 1); + if (new_cache == NULL) { + free(buf); + return NULL; + } + memcpy(new_cache, buf + i + 1, buf_len - i - 1); + + free(stream->cache); + stream->cache = new_cache; + stream->cache_len = buf_len - i - 1; + + free(buf); + return ret; + } + } + + /* There was no newline or \0 */ + new_cache = realloc(stream->cache, stream->cache_len + buf_len); + if (new_cache == NULL) { + free(buf); + return NULL; + } + memcpy(new_cache + stream->cache_len, buf, buf_len); + stream->cache = new_cache; + stream->cache_len += buf_len; + } } bool @@ -84,6 +257,9 @@ cfw_stream_eof(void *ptr) if (stream == NULL || stream->ops == NULL) return true; + if (stream->cache != NULL) + return false; + return stream->ops->eof(stream); } diff --git a/src/stream.h b/src/stream.h index c5693de..4b29bd8 100644 --- a/src/stream.h +++ b/src/stream.h @@ -31,6 +31,7 @@ #include "class.h" #include "object.h" +#include "string.h" struct cfw_stream_ops { ssize_t (*read)(void*, void*, size_t); @@ -42,10 +43,13 @@ struct cfw_stream_ops { typedef struct CFWStream { CFWObject obj; struct cfw_stream_ops *ops; + char *cache; + size_t cache_len; } CFWStream; extern CFWClass *cfw_stream; extern ssize_t cfw_stream_read(void*, void*, size_t); +extern CFWString* cfw_stream_read_line(void*); extern bool cfw_stream_write(void*, const void*, size_t); extern bool cfw_stream_write_string(void*, const char*); extern bool cfw_stream_eof(void*); diff --git a/src/tcpsocket.c b/src/tcpsocket.c index ce78121..2666170 100644 --- a/src/tcpsocket.c +++ b/src/tcpsocket.c @@ -96,6 +96,8 @@ ctor(void *ptr, va_list args) { CFWTCPSocket *sock = ptr; + cfw_stream->ctor(ptr, args); + sock->fd = -1; sock->stream.ops = &stream_ops; sock->eof = false; @@ -106,7 +108,7 @@ ctor(void *ptr, va_list args) static void dtor(void *ptr) { - cfw_stream_close(ptr); + cfw_stream->dtor(ptr); } bool