diff options
-rw-r--r-- | ChangeLog | 28 | ||||
-rw-r--r-- | stream.c | 85 |
2 files changed, 99 insertions, 14 deletions
@@ -1,5 +1,33 @@ 2012-03-12 Kaz Kylheku <kaz@kylheku.com> + Implementing put_byte for string output stream. + This does the Right Thing with a mixture of bytes and characters. + Incomplete byte sequences. + + * stream.c (struct strm_ops): Changing byte argument of put_byte + to int, since the put_byte API function can just pass down that + value after validating it. + (stdio_handle): Use available typedef. + (stdio_put_byte): Follow interface change in strm_ops. + Do not validate the range of a byte; the put_byte higher + level function does that now. + (struct string_output): New members: ud, byte_buf, head, tail. + (string_out_byte_callback, string_out_byte_flush): New static + functions. + (string_out_put_string): Flush any UTF-8 bytes in the byte buffer + before putting the string. + (string_out_put_byte): New static function, implementation for + put_byte on string output streams. + (string_out_ops): string_out_put_byte wired in. + (make_string_output_stream): Initialize new members of + of struct string_output. + (get_string_from_stream): Flush any UTF-8 bytes in the byte buffer + before retrieving the string. + (put_byte): Validate that the byte is in range. Pass byte + as C int down to the put_byte virtual. + +2012-03-12 Kaz Kylheku <kaz@kylheku.com> + Plugging memory leak. * stream.c (byte_in_stream_destroy): New function. @@ -51,7 +51,7 @@ struct strm_ops { struct cobj_ops cobj_ops; val (*put_string)(val, val); val (*put_char)(val, val); - val (*put_byte)(val, val); + val (*put_byte)(val, int); val (*get_line)(val); val (*get_char)(val); val (*get_byte)(val); @@ -67,7 +67,7 @@ static void common_destroy(val obj) struct stdio_handle { FILE *f; val descr; - struct utf8_decoder ud; + utf8_decoder_t ud; }; static void stdio_stream_print(val stream, val out) @@ -152,18 +152,13 @@ static val stdio_put_char(val stream, val ch) ? t : stdio_maybe_write_error(stream); } -static val stdio_put_byte(val stream, val byte) +static val stdio_put_byte(val stream, int b) { - cnum b = c_num(byte); struct stdio_handle *h = (struct stdio_handle *) stream->co.handle; if (stream != std_debug) output_produced = t; - if (b < 0 || b > 255) - uw_throwf(file_error_s, lit("put-byte on ~a: byte value ~a out of range"), - stream, byte, nao); - return h->f != 0 && putc(b, (FILE *) h->f) != EOF ? t : stdio_maybe_write_error(stream); } @@ -442,6 +437,9 @@ struct string_output { wchar_t *buf; size_t size; size_t fill; + utf8_decoder_t ud; + unsigned char byte_buf[4]; + int head, tail; }; static void string_out_stream_destroy(val stream) @@ -456,13 +454,46 @@ static void string_out_stream_destroy(val stream) } } +static int string_out_byte_callback(mem_t *ctx) +{ + struct string_output *so = (struct string_output *) ctx; + if (so->tail >= so->head) + return EOF; + return so->byte_buf[so->tail++]; +} + +static val string_out_put_char(val stream, val ch); + +static val string_out_byte_flush(struct string_output *so, val stream) +{ + val result = nil; + + while (so->tail < so->head) { + wint_t ch = utf8_decode(&so->ud, string_out_byte_callback, (mem_t *) so); + int remaining = so->head - so->tail; + if (remaining != 0) + memmove(so->byte_buf, so->byte_buf + so->tail, remaining); + so->head = so->tail = remaining; + utf8_decoder_init(&so->ud); + if (ch == WEOF) + internal_error("unexpected WEOF from utf8_decode"); + result = string_out_put_char(stream, chr(ch)); + so->tail = 0; + } + return nil; +} + static val string_out_put_string(val stream, val str) { struct string_output *so = (struct string_output *) stream->co.handle; - if (so == 0) { + if (so == 0) return nil; - } else { + + if (so->head != 0) + string_out_byte_flush(so, stream); + + { const wchar_t *s = c_str(str); size_t len = c_num(length_str(str)); size_t old_size = so->size; @@ -493,6 +524,21 @@ static val string_out_put_char(val stream, val ch) return string_out_put_string(stream, auto_str((const wchli_t *) wref(onech))); } +static val string_out_put_byte(val stream, int ch) +{ + struct string_output *so = (struct string_output *) stream->co.handle; + + if (so == 0) + return nil; + + so->byte_buf[so->head++] = ch; + + if (so->head >= (int) sizeof so->byte_buf) + return string_out_byte_flush(so, stream); + + return t; +} + static struct strm_ops string_out_ops = { { cobj_equal_op, cobj_print_op, @@ -501,7 +547,7 @@ static struct strm_ops string_out_ops = { cobj_hash_op }, string_out_put_string, string_out_put_char, - 0, + string_out_put_byte, 0, 0, 0, @@ -690,6 +736,8 @@ val make_string_output_stream(void) so->buf = (wchar_t *) chk_malloc(so->size * sizeof so->buf); so->fill = 0; so->buf[0] = 0; + utf8_decoder_init(&so->ud); + so->head = so->tail = 0; return cobj((mem_t *) so, stream_s, &string_out_ops.cobj_ops); } @@ -703,11 +751,14 @@ val get_string_from_stream(val stream) struct string_output *so = (struct string_output *) stream->co.handle; val out = nil; - stream->co.handle = 0; - if (!so) return out; + if (so->head != 0) + out = string_out_byte_flush(so, stream); + + stream->co.handle = 0; + so->buf = (wchar_t *) chk_realloc((mem_t *) so->buf, (so->fill + 1) * sizeof *so->buf); out = string_own(so->buf); @@ -1227,6 +1278,8 @@ val put_char(val ch, val stream) val put_byte(val byte, val stream) { + cnum b = c_num(byte); + if (!stream) stream = std_output; @@ -1234,9 +1287,13 @@ val put_byte(val byte, val stream) type_assert (stream->co.cls == stream_s, (lit("~a is not a stream"), stream, nao)); + if (b < 0 || b > 255) + uw_throwf(file_error_s, lit("put-byte on ~a: byte value ~a out of range"), + stream, byte, nao); + { struct strm_ops *ops = (struct strm_ops *) stream->co.ops; - return ops->put_char ? ops->put_byte(stream, byte) : nil; + return ops->put_char ? ops->put_byte(stream, b) : nil; } } |