Add cfw_stream_read_line().

This also adds a caching infrastructure to cfw_stream.
This commit is contained in:
Jonathan Schleifer 2012-09-30 03:01:37 +02:00
parent 8f0ffd2483
commit 977f3a0a44
4 changed files with 189 additions and 7 deletions

View file

@ -24,16 +24,21 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#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);
}