/* Copyright 2017-2024 * Kaz Kylheku * Vancouver, Canada * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "config.h" #if HAVE_ZLIB #include #endif #include "lib.h" #include "gc.h" #include "itypes.h" #include "signal.h" #include "unwind.h" #include "eval.h" #include "stream.h" #include "arith.h" #include "utf8.h" #include "txr.h" #include "buf.h" static cnum buf_check_len(val len, val self) { cnum l = c_num(len, self); if (l < 0) uw_throwf(error_s, lit("~a: negative length ~s specified"), self, len, nao); return l; } static cnum buf_check_alloc_size(val alloc_size, cnum len, val self) { cnum ah = c_num(alloc_size, self); if (ah < len) uw_throwf(error_s, lit("~a: alloc size size ~s lower than length"), self, alloc_size, nao); return ah; } static cnum buf_check_index(struct buf *b, val index, val self) { cnum ix = c_num(index, self); if (ix < 0) ix = c_num(plus(b->len, index), self); if (ix < 0) uw_throwf(error_s, lit("~a: negative byte index ~s specified"), self, index, nao); return ix; } val make_buf(val len, val init_val, val alloc_size) { val self = lit("make-buf"); cnum blen = buf_check_len(len, self); val alloc = if3(null_or_missing_p(alloc_size), len, alloc_size); cnum size = buf_check_alloc_size(alloc, blen, self); cnum iv = c_u8(default_arg(init_val, zero), self); mem_t *data = if3(iv == 0 && size == blen, chk_calloc(size, 1), chk_malloc(size)); val obj = make_obj(); obj->b.type = BUF; obj->b.data = data; obj->b.len = len; obj->b.size = num(size); if (iv != 0 || size != blen) memset(data, convert(unsigned char, iv), blen); return obj; } val bufp(val object) { return tnil(type(object) == BUF); } val init_borrowed_buf(obj_t *obj, val len, mem_t *data) { obj->b.type = BUF; obj->b.data = data; obj->b.len = len; obj->b.size = nil; return obj; } val make_borrowed_buf(val len, mem_t *data) { return init_borrowed_buf(make_obj(), len, data); } val make_duplicate_buf(val len, mem_t *data) { val self = lit("make-duplicate-buf"); val obj = make_obj(); obj->b.type = BUF; obj->b.data = chk_copy_obj(data, c_num(len, self)); obj->b.len = len; obj->b.size = len; return obj; } val make_owned_buf(val len, mem_t *data) { val buf = make_borrowed_buf(len, data); buf->b.size = len; return buf; } static struct buf *buf_handle(val buf, val ctx) { if (type(buf) == BUF) return coerce(struct buf *, buf); uw_throwf(error_s, lit("~a: ~s isn't a buffer"), ctx, buf, nao); } val copy_buf(val buf) { struct buf *b = buf_handle(buf, lit("copy-buf")); return if3(b->size, make_duplicate_buf(b->len, b->data), make_borrowed_buf(b->len, b->data)); } static void buf_shrink(struct buf *b) { val self = lit("buf-trim"); val len = b->len; if (len == zero) len = succ(len); /* avoid reallocing to zero length; i.e. freeing */ if (len != b->size) { b->data = chk_realloc(b->data, c_unum(len, self)); b->size = b->len; } } val buf_trim(val buf) { val self = lit("buf-trim"); struct buf *b = buf_handle(buf, self); val oldsize = b->size; if (!oldsize) uw_throwf(error_s, lit("~a: ~s is a fixed buffer"), self, buf, nao); buf_shrink(b); return oldsize; } static val buf_do_set_len(val buf, struct buf *b, val newlen, val init_val, val self) { val oldlen = b->len; cnum olen = c_num(oldlen, self), len = c_num(newlen, self); cnum oldsize = c_num(b->size, self), size = oldsize; cnum iv = c_u8(default_arg(init_val, zero), self); if (!b->size) uw_throwf(error_s, lit("~a: ~s is a fixed buffer"), self, buf, nao); (void) buf_check_len(newlen, self); b->len = newlen; if (size < len) { if (size > INT_PTR_MAX - INT_PTR_MAX / 5) { size = INT_PTR_MAX; } else { size = size + size / 4; if (size < len) size = len; } } if (size > oldsize) { b->data = chk_realloc(b->data, size); b->size = num(size); } if (len > olen) memset(b->data + olen, convert(unsigned char, iv), len - olen); return oldlen; } val buf_set_length(val buf, val len, val init_val) { val self = lit("buf-set-len"); struct buf *b = buf_handle(buf, self); return buf_do_set_len(buf, b, len, init_val, self); } val buf_free(val buf) { val self = lit("buf-free"); struct buf *b = buf_handle(buf, self); if (b->size) { free(b->data); b->data = 0; b->len = b->size = zero; return t; } return nil; } val length_buf(val buf) { val self = lit("length-buf"); struct buf *b = buf_handle(buf, self); return b->len; } val buf_alloc_size(val buf) { val self = lit("buf-alloc-size"); struct buf *b = buf_handle(buf, self); return b->size; } mem_t *buf_get(val buf, val self) { struct buf *b = buf_handle(buf, self); return b->data; } val sub_buf(val buf, val from, val to) { val self = lit("sub-buf"); struct buf *b = buf_handle(buf, lit("sub")); val len = b->len; if (null_or_missing_p(from)) from = zero; else if (from == t) from = len; else if (minusp(from)) { from = plus(from, len); if (to == zero) to = len; } if (null_or_missing_p(to) || to == t) to = len; else if (minusp(to)) to = plus(to, len); from = max2(zero, min2(from, len)); to = max2(zero, min2(to, len)); if (ge(from, to)) { return make_buf(zero, nil, zero); } else if (from == 0 && to == len) { return buf; } else { return make_duplicate_buf(minus(to, from), b->data + c_num(from, self)); } } val replace_buf(val buf, val items, val from, val to) { val self = lit("replace"); val len = length_buf(buf); if (missingp(from)) { from = zero; } else if (from == t) { from = len; } else if (!integerp(from)) { seq_iter_t wh_iter, item_iter; cnum offs = 0; cnum l = c_num(len, self), ol = l; val wh, item; seq_iter_init(self, &wh_iter, from); seq_iter_init(self, &item_iter, items); if (!missingp(to)) uw_throwf(error_s, lit("~a: to-arg not applicable when from-arg is a list"), self, nao); while (seq_get(&item_iter, &item) && seq_get(&wh_iter, &wh)) { if (ge(wh, len)) break; buf_put_uchar(buf, wh, item); } if (!opt_compat || opt_compat > 289) { while (seq_get(&wh_iter, &wh)) { cnum w = c_num(wh, self); if (w < 0) w += ol; if (w < 0) break; w -= offs; if (w >= l) break; memmove(buf->b.data + w, buf->b.data + w + 1, l - w - 1); l--; offs++; } if (offs > 0) buf_set_length(buf, num_fast(l), zero); } return buf; } else if (minusp(from)) { from = plus(from, len); if (to == zero) to = len; } if (null_or_missing_p(to) || to == t) to = len; else if (minusp(to)) to = plus(to, len); from = max2(zero, min2(from, len)); to = max2(zero, min2(to, len)); { val len_rep = minus(to, from); val len_it = length(items); if (gt(len_rep, len_it)) { val len_diff = minus(len_rep, len_it); cnum t = c_num(to, self); cnum l = c_num(len, self); memmove(buf->b.data + t - c_num(len_diff, self), buf->b.data + t, l - t); buf_set_length(buf, minus(len, len_diff), zero); to = plus(from, len_it); } else if (lt(len_rep, len_it)) { val len_diff = minus(len_it, len_rep); cnum t = c_num(to, self); cnum l = c_num(len, self); buf_set_length(buf, plus(len, len_diff), zero); memmove(buf->b.data + t + c_num(len_diff, self), buf->b.data + t, l - t); to = plus(from, len_it); } if (zerop(len_it)) return buf; if (bufp(items)) { memmove(buf->b.data + c_num(from, self), items->b.data, c_num(len_it, self)); } else { seq_iter_t item_iter; cnum f = c_num(from, self); cnum t = c_num(to, self); seq_iter_init(self, &item_iter, items); for (; f != t; f++) { val item = seq_geti(&item_iter); buf_put_uchar(buf, num(f), item); } } } return buf; } val buf_list(val list) { val self = lit("buf-list"); val len = length(list); val buf = make_buf(zero, zero, len); seq_iter_t iter; val elem; cnum i; for (i = 0, seq_iter_init(self, &iter, list); seq_get(&iter, &elem); i++) buf->b.data[i] = c_uchar(elem, self); buf->b.len = len; return buf; } static void buf_move_bytes(val buf, val pos, mem_t *ptr, cnum size, val self) { struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); val req_len = plus(num(p), num(size)); if (gt(req_len, b->len)) buf_do_set_len(buf, b, req_len, nil, self); memmove(b->data + p, ptr, size); } val buf_put_buf(val dbuf, val sbuf, val pos) { val self = lit("buf-put-buf"); struct buf *sb = buf_handle(sbuf, self); buf_move_bytes(dbuf, pos, sb->data, c_num(sb->len, self), self); return sbuf; } void buf_put_bytes(val buf, val pos, mem_t *ptr, cnum size, val self) { struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); val req_len = plus(num(p), num(size)); if (gt(req_len, b->len)) buf_do_set_len(buf, b, req_len, nil, self); memcpy(b->data + p, ptr, size); } #if HAVE_I8 val buf_put_i8(val buf, val pos, val num) { val self = lit("buf-put-i8"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); i8_t v = c_i8(num, self); if (p >= c_num(b->len, self)) buf_do_set_len(buf, b, succ(pos), nil, self); b->data[p] = v; return num; } val buf_put_u8(val buf, val pos, val num) { val self = lit("buf-put-u8"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); cnum v = c_u8(num, self); if (p >= c_num(b->len, self)) buf_do_set_len(buf, b, succ(pos), nil, self); b->data[p] = v; return num; } #endif #if HAVE_I16 val buf_put_i16(val buf, val pos, val num) { val self = lit("buf-put-i16"); i16_t n = c_i16(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_u16(val buf, val pos, val num) { val self = lit("buf-put-u16"); u16_t n = c_u16(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } #endif #if HAVE_I32 val buf_put_i32(val buf, val pos, val num) { val self = lit("buf-put-i32"); i32_t n = c_i32(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_u32(val buf, val pos, val num) { val self = lit("buf-put-u32"); u32_t n = c_u32(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } #endif #if HAVE_I64 val buf_put_i64(val buf, val pos, val num) { val self = lit("buf-put-i64"); i64_t n = c_i64(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_u64(val buf, val pos, val num) { val self = lit("buf-put-u64"); u64_t n = c_u64(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } #endif val buf_put_char(val buf, val pos, val num) { val self = lit("buf-put-char"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); char v = c_char(num, self); if (p >= c_num(b->len, self)) buf_do_set_len(buf, b, succ(pos), nil, self); b->data[p] = v; return num; } val buf_put_uchar(val buf, val pos, val num) { val self = lit("buf-put-uchar"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); unsigned char v = c_uchar(num, self); if (p >= c_num(b->len, self)) buf_do_set_len(buf, b, succ(pos), nil, self); b->data[p] = v; return num; } val buf_put_short(val buf, val pos, val num) { val self = lit("buf-put-short"); short n = c_short(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_ushort(val buf, val pos, val num) { val self = lit("buf-put-ushort"); unsigned short n = c_short(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_int(val buf, val pos, val num) { val self = lit("buf-put-int"); int n = c_int(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_uint(val buf, val pos, val num) { val self = lit("buf-put-uint"); unsigned n = c_uint(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_long(val buf, val pos, val num) { val self = lit("buf-put-long"); long n = c_long(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_ulong(val buf, val pos, val num) { val self = lit("buf-put-ulong"); unsigned long n = c_ulong(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_float(val buf, val pos, val num) { val self = lit("buf-put-float"); double n; double f = c_flo(num, self); if (f > FLT_MAX || f < FLT_MIN) uw_throwf(error_s, lit("~a: ~s is out of float range"), self, num, nao); n = f; buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_double(val buf, val pos, val num) { val self = lit("buf-put-double"); double n = c_flo(num, self); buf_put_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num; } val buf_put_cptr(val buf, val pos, val cptr) { val self = lit("buf-put-cptr"); mem_t *p = cptr_get(cptr); buf_put_bytes(buf, pos, coerce(mem_t *, &p), sizeof p, self); return cptr; } void buf_get_bytes(val buf, val pos, mem_t *ptr, cnum size, val self) { struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); cnum e = p + size; cnum l = c_num(b->len, self); if (e > l || e < 0) uw_throwf(error_s, lit("~a: attempted read past buffer end"), self, nao); memcpy(ptr, b->data + p, size); } #if HAVE_I8 val buf_get_i8(val buf, val pos) { val self = lit("buf-get-i8"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); if (p >= c_num(b->len, self)) uw_throwf(error_s, lit("~a: attempted read past buffer end"), self, nao); return num_fast(convert(i8_t, b->data[p])); } val buf_get_u8(val buf, val pos) { val self = lit("buf-get-u8"); struct buf *b = buf_handle(buf, self); cnum p = buf_check_index(b, pos, self); if (p >= c_num(b->len, self)) uw_throwf(error_s, lit("~a: attempted read past buffer end"), self, nao); return num_fast(convert(u8_t, b->data[p])); } #endif #if HAVE_I16 val buf_get_i16(val buf, val pos) { val self = lit("buf-get-i16"); i16_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num_fast(n); } val buf_get_u16(val buf, val pos) { val self = lit("buf-get-u16"); u16_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num_fast(n); } #endif #if HAVE_I32 val buf_get_i32(val buf, val pos) { val self = lit("buf-get-i32"); i32_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num(n); } val buf_get_u32(val buf, val pos) { val self = lit("buf-get-u32"); u32_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return unum(n); } #endif #if HAVE_I64 val buf_get_i64(val buf, val pos) { val self = lit("buf-get-i64"); i64_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); if (sizeof (i64_t) <= sizeof (cnum)) { return num(n); } else { val high = num(n >> 32); val low = unum(n & 0xFFFFFFFF); return logior(ash(high, num_fast(32)), low); } } val buf_get_u64(val buf, val pos) { val self = lit("buf-get-u64"); u64_t n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); if (sizeof (u64_t) <= sizeof (uint_ptr_t)) { return unum(n); } else { val high = unum(n >> 32); val low = unum(n & 0xFFFFFFFF); return logior(ash(high, num_fast(32)), low); } } #endif val buf_get_char(val buf, val pos) { #if CHAR_MAX == UCHAR_MAX return buf_get_u8(buf, pos); #else return buf_get_i8(buf, pos); #endif } val buf_get_uchar(val buf, val pos) { return buf_get_u8(buf, pos); } val buf_get_short(val buf, val pos) { val self = lit("buf-get-short"); short n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); #if SIZEOF_SHORT < SIZEOF_PTR return num_fast(n); #else return num(n); #endif } val buf_get_ushort(val buf, val pos) { val self = lit("buf-get-ushort"); unsigned short n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); #if SIZEOF_SHORT < SIZEOF_PTR return num_fast(n); #else return unum(n); #endif } val buf_get_int(val buf, val pos) { val self = lit("buf-get-int"); int n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num(n); } val buf_get_uint(val buf, val pos) { val self = lit("buf-get-uint"); unsigned n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return unum(n); } val buf_get_long(val buf, val pos) { val self = lit("buf-get-long"); long n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return num(n); } val buf_get_ulong(val buf, val pos) { val self = lit("buf-get-long"); unsigned long n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return unum(n); } val buf_get_float(val buf, val pos) { val self = lit("buf-get-float"); float n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return flo(n); } val buf_get_double(val buf, val pos) { val self = lit("buf-get-double"); double n; buf_get_bytes(buf, pos, coerce(mem_t *, &n), sizeof n, self); return flo(n); } val buf_get_cptr(val buf, val pos) { val self = lit("buf-get-cptr"); mem_t *p; buf_get_bytes(buf, pos, coerce(mem_t *, &p), sizeof p, self); return cptr(p); } val buf_print(val buf, val stream_in) { val self = lit("buf-print"); val stream = default_arg(stream_in, std_output); struct buf *b = buf_handle(buf, self); cnum len = c_num(b->len, self), count = 0; mem_t *data = b->data; val save_mode = test_neq_set_indent_mode(stream, num_fast(indent_foff), num_fast(indent_data)); val save_indent; int fb = 0; put_string(lit("#b'"), stream); save_indent = inc_indent(stream, zero); while (len-- > 0) { format(stream, lit("~,02x"), num_fast(*data++), nao); if ((++count & 7) == 0 && len) if (width_check(stream, chr(' '))) fb = 1; } set_indent(stream, save_indent); set_indent_mode(stream, save_mode); if (fb) force_break(stream); return put_char(chr('\''), stream); } val buf_pprint(val buf, val stream_in) { val self = lit("buf-pprint"); val stream = default_arg(stream_in, std_output); struct buf *b = buf_handle(buf, self); cnum len = c_num(b->len, self); mem_t *data = b->data; if (opt_compat && opt_compat <= 294) { while (len-- > 0) put_byte(num_fast(*data++), stream); } else { while (len-- > 0) format(stream, lit("~,02x"), num_fast(*data++), nao); } return t; } val buf_str_sep(val buf, val sep, val self) { struct buf *b = buf_handle(buf, self); ucnum len = c_unum(b->len, self); val ret = null_string; if (len > 0) { val stream = make_string_output_stream(); ucnum i = 0; goto first; while (i < len) { put_string(sep, stream); first: format(stream, lit("~,02x"), num_fast(b->data[i++]), nao); } ret = get_string_from_stream(stream); } return ret; } void buf_hex(val buf, char *hex, size_t sz, int caps) { val self = lit("buf-hex"); struct buf *b = buf_handle(buf, self); size_t i; unsigned char *data = b->data; const char *fmt = caps ? "%02X" : "%02x"; *hex = 0; for (i = 0; i < sz - 1; i += 2) hex += sprintf(hex, fmt, *data++); } struct buf_strm { struct strm_base a; utf8_decoder_t ud; int is_byte_oriented; val buf; val pos; val unget_c; }; static void buf_strm_mark(val stream) { struct buf_strm *b = coerce(struct buf_strm *, stream->co.handle); strm_base_mark(&b->a); gc_mark(b->buf); gc_mark(b->pos); gc_mark(b->unget_c); } static int buf_strm_put_byte_callback(int b, mem_t *ctx) { struct buf_strm *s = coerce(struct buf_strm *, ctx); (void) buf_put_uchar(s->buf, s->pos, num_fast(b)); s->pos = succ(s->pos); return 1; } static val buf_strm_put_string(val stream, val str) { val self = lit("put-string"); struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); const wchar_t *p = c_str(str, self); while (*p) { (void) utf8_encode(*p++, buf_strm_put_byte_callback, coerce(mem_t *, s)); } return t; } static val buf_strm_put_char(val stream, val ch) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); (void) utf8_encode(c_chr(ch), buf_strm_put_byte_callback, coerce(mem_t *, s)); return t; } static val buf_strm_put_byte(val stream, int b) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); (void) buf_strm_put_byte_callback(b, coerce(mem_t *, s)); return t; } static int buf_strm_get_byte_callback(mem_t *ctx) { val self = lit("get-byte"); struct buf_strm *s = coerce(struct buf_strm *, ctx); struct buf *b = buf_handle(s->buf, self); cnum p = buf_check_index(b, s->pos, self); s->pos = num(p + 1); return (p >= c_num(b->len, self)) ? EOF : b->data[p]; } static val buf_strm_get_char(val stream) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); if (s->unget_c) { return rcyc_pop(&s->unget_c); } else { wint_t ch; if (s->is_byte_oriented) { ch = buf_strm_get_byte_callback(coerce(mem_t *, s)); if (ch == 0) ch = 0xDC00; } else { ch = utf8_decode(&s->ud, buf_strm_get_byte_callback, coerce(mem_t *, s)); } return (ch != WEOF) ? chr(ch) : nil; } } static val buf_strm_get_byte(val stream) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); int byte = buf_strm_get_byte_callback(coerce(mem_t *, s)); return byte == EOF ? nil : num_fast(byte); } static val buf_strm_unget_char(val stream, val ch) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); mpush(ch, mkloc(s->unget_c, stream)); return ch; } static val buf_strm_unget_byte(val stream, int byte) { val self = lit("unget-byte"); struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); struct buf *b = buf_handle(s->buf, self); cnum p = c_num(s->pos, self); if (p <= 0) { uw_throwf(file_error_s, lit("~a: cannot push back past start of stream ~s"), self, stream, nao); } b->data[--p] = byte; s->pos = num(p); return num_fast(byte); } static val buf_strm_seek(val stream, val offset, enum strm_whence whence) { val self = lit("seek-stream"); struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); struct buf *b = buf_handle(s->buf, self); val npos; switch (whence) { case strm_start: npos = offset; break; case strm_cur: if (offset == zero) return s->pos; npos = plus(s->pos, offset); break; case strm_end: npos = minus(b->len, offset); break; default: internal_error("invalid whence value"); } if (minusp(npos)) uw_throwf(error_s, lit("~a: cannot seek to negative position ~s"), self, npos, nao); s->pos = npos; return t; } static val buf_strm_truncate(val stream, val len) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); buf_set_length(s->buf, len, zero); return t; } static val buf_strm_get_prop(val stream, val ind) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); if (ind == name_k) { return lit("buf-stream"); } else if (ind == byte_oriented_k) { return tnil(s->is_byte_oriented); } return nil; } static val buf_strm_set_prop(val stream, val ind, val prop) { struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); if (ind == byte_oriented_k) { s->is_byte_oriented = prop ? 1 : 0; return t; } return nil; } static val buf_strm_get_error(val stream) { val self = lit("get-error"); struct buf_strm *s = coerce(struct buf_strm *, stream->co.handle); struct buf *b = buf_handle(s->buf, self); return ge(s->pos, b->len); } static val buf_strm_get_error_str(val stream) { return errno_to_string(buf_strm_get_error(stream)); } static struct strm_ops buf_strm_ops = strm_ops_init(cobj_ops_init(eq, stream_print_op, stream_destroy_op, buf_strm_mark, cobj_eq_hash_op, 0), wli("buf-stream"), buf_strm_put_string, buf_strm_put_char, buf_strm_put_byte, generic_get_line, buf_strm_get_char, buf_strm_get_byte, buf_strm_unget_char, buf_strm_unget_byte, 0, 0, 0, 0, buf_strm_seek, buf_strm_truncate, buf_strm_get_prop, buf_strm_set_prop, buf_strm_get_error, buf_strm_get_error_str, 0, 0); static struct buf_strm *buf_strm(val stream, val self) { struct buf_strm *s = coerce(struct buf_strm *, cobj_handle(self, stream, stream_cls)); type_assert (stream->co.ops == &buf_strm_ops.cobj_ops, (lit("~a: ~a is not a buffer stream"), self, stream, nao)); return s; } val make_buf_stream(val buf_opt) { val stream; val buf = default_arg(buf_opt, make_buf(zero, zero, num_fast(64))); struct buf_strm *s = coerce(struct buf_strm *, chk_malloc(sizeof *s)); strm_base_init(&s->a); utf8_decoder_init(&s->ud); s->buf = nil; s->pos = zero; s->is_byte_oriented = 0; s->unget_c = nil; stream = cobj(coerce(mem_t *, s), stream_cls, &buf_strm_ops.cobj_ops); s->buf = buf; return stream; } val get_buf_from_stream(val stream) { val self = lit("get-buf-from-stream"); struct buf_strm *s = buf_strm(stream, self); return s->buf; } void buf_swap32(val buf) { val self = lit("buf-swap32"); struct buf *b = buf_handle(buf, self); mem_t *data = b->data, *end = data + c_num(b->len, self); for (; data + 3 < end; data += 4) { u32_t sw32 = *coerce(u32_t *, data); sw32 = ((sw32 & 0xFF00FF00U) >> 8) | ((sw32 & 0x00FF00FFU) << 8); sw32 = ((sw32 & 0xFFFF0000U) >> 16) | ((sw32 & 0x0000FFFFU) << 16); *coerce(u32_t *, data) = sw32; } } static val buf_str(val str, val null_term) { val self = lit("buf-str"); size_t sz; val nt = default_null_arg(null_term); unsigned char *u8 = utf8_dup_to_buf(c_str(str, self), &sz, nt != nil); return make_owned_buf(unum(sz), u8); } static val str_buf(val buf, val null_term) { val self = lit("str-buf"); struct buf *b = buf_handle(buf, self); val nt = default_null_arg(null_term); size_t blen = c_unum(b->len, self); size_t len = (nt && blen > 0 && !b->data[blen-1]) ? blen - 1 : blen; wchar_t *str = utf8_dup_from_buf(coerce(const char *, b->data), len); return string_own(str); } static val buf_int(val num) { val self = lit("buf-int"); switch (type(num)) { case CHR: return buf_int(num_fast(c_ch(num))); case NUM: num = bignum(c_num(num, self)); /* fallthrough */ case BGNUM: { val wi = width(num); val bits = succ(wi); val bytes = ash(plus(bits, num_fast(7)), num_fast(-3)); val bitsround = ash(bytes, num_fast(3)); val un = logtrunc(num, bitsround); val ube = if3(bignump(un), un, bignum(c_num(un, self))); mp_int *m = mp(ube); size_t numsize = mp_unsigned_bin_size(m); size_t bufsize = c_unum(bytes, self); mem_t *data = chk_malloc(bufsize); data[0] = 0; mp_to_unsigned_bin(m, data + (bufsize - numsize)); return make_owned_buf(unum(bufsize), data); } default: uw_throwf(type_error_s, lit("~a: ~s isn't an integer or character"), self, num, nao); } } static val buf_uint(val num) { val self = lit("buf-uint"); switch (type(num)) { case CHR: return buf_uint(num_fast(c_ch(num))); case NUM: num = bignum(c_num(num, self)); /* fallthrough */ case BGNUM: { mp_int *m = mp(num); if (!mp_isneg(m)) { size_t size = mp_unsigned_bin_size(m); mem_t *data = chk_malloc(size); mp_to_unsigned_bin(m, data); return make_owned_buf(unum(size), data); } } uw_throwf(type_error_s, lit("~a: ~s isn't a non-negative integer"), self, num, nao); default: uw_throwf(type_error_s, lit("~a: ~s isn't an integer or character"), self, num, nao); } } static val int_buf(val buf) { val self = lit("int-buf"); struct buf *b = buf_handle(buf, self); ucnum size = c_unum(b->len, self); ucnum bits = size * 8; val ubn = make_bignum(); mp_err mpe = mp_read_unsigned_bin(mp(ubn), b->data, size); if (mpe != MP_OKAY) do_mp_error(self, mpe); return sign_extend(normalize(ubn), unum(bits)); } static val uint_buf(val buf) { val self = lit("uint-buf"); struct buf *b = buf_handle(buf, self); ucnum size = c_unum(b->len, self); val ubn = make_bignum(); mp_err mpe = mp_read_unsigned_bin(mp(ubn), b->data, size); if (mpe != MP_OKAY) do_mp_error(self, mpe); return normalize(ubn); } #if HAVE_ZLIB static val buf_compress(val buf, val level_opt) { val self = lit("buf-compress"); val level = default_arg(level_opt, negone); int lev = c_int(level, self); struct buf *b = buf_handle(buf, self); ucnum size = c_unum(b->len, self); uLong bound = compressBound(size), zsize = bound; mem_t *zdata = chk_malloc(bound); if (convert(uLong, size) != size) { free(zdata); uw_throwf(error_s, lit("~a: array size overflow"), self, nao); } if (compress2(zdata, &zsize, b->data, size, lev) != Z_OK) { free(zdata); uw_throwf(error_s, lit("~a: compression failed"), self, nao); } zdata = chk_realloc(zdata, zsize); return make_owned_buf(unum(zsize), zdata); } static val buf_decompress(val buf) { val self = lit("buf-decompress"); struct buf *b = buf_handle(buf, self); ucnum zsize = c_unum(b->len, self); uLong zsz10 = 10 * zsize; uLong size = if3(zsz10 > zsize, zsz10, convert(uLong, -1)); mem_t *data = chk_malloc(size); for (;;) { switch (uncompress(data, &size, b->data, zsize)) { case Z_OK: data = chk_realloc(data, size); return make_owned_buf(unum(size), data); case Z_BUF_ERROR: if (size == convert(uLong, -1)) break; if (size * 2 > size) size = size * 2; else if (size == convert(uLong, -1)) break; else size = convert(uLong, -1); data = chk_realloc(data, size); continue; default: break; } break; } free(data); uw_throwf(error_s, lit("~a: decompression failed"), self, nao); } #endif void buf_init(void) { reg_fun(intern(lit("make-buf"), user_package), func_n3o(make_buf, 1)); reg_fun(intern(lit("bufp"), user_package), func_n1(bufp)); reg_fun(intern(lit("buf-trim"), user_package), func_n1(buf_trim)); reg_fun(intern(lit("buf-set-length"), user_package), func_n3o(buf_set_length, 2)); reg_fun(intern(lit("length-buf"), user_package), func_n1(length_buf)); reg_fun(intern(lit("buf-alloc-size"), user_package), func_n1(buf_alloc_size)); reg_fun(intern(lit("copy-buf"), user_package), func_n1(copy_buf)); reg_fun(intern(lit("sub-buf"), user_package), func_n3(sub_buf)); reg_fun(intern(lit("replace-buf"), user_package), func_n4(replace_buf)); reg_fun(intern(lit("buf-list"), user_package), func_n1(buf_list)); reg_fun(intern(lit("buf-put-buf"), user_package), func_n3(buf_put_buf)); #if HAVE_I8 reg_fun(intern(lit("buf-put-i8"), user_package), func_n3(buf_put_i8)); reg_fun(intern(lit("buf-put-u8"), user_package), func_n3(buf_put_u8)); #endif #if HAVE_I16 reg_fun(intern(lit("buf-put-i16"), user_package), func_n3(buf_put_i16)); reg_fun(intern(lit("buf-put-u16"), user_package), func_n3(buf_put_u16)); #endif #if HAVE_I32 reg_fun(intern(lit("buf-put-i32"), user_package), func_n3(buf_put_i32)); reg_fun(intern(lit("buf-put-u32"), user_package), func_n3(buf_put_u32)); #endif #if HAVE_I64 reg_fun(intern(lit("buf-put-i64"), user_package), func_n3(buf_put_i64)); reg_fun(intern(lit("buf-put-u64"), user_package), func_n3(buf_put_u64)); #endif reg_fun(intern(lit("buf-put-char"), user_package), func_n3(buf_put_char)); reg_fun(intern(lit("buf-put-uchar"), user_package), func_n3(buf_put_uchar)); reg_fun(intern(lit("buf-put-short"), user_package), func_n3(buf_put_short)); reg_fun(intern(lit("buf-put-ushort"), user_package), func_n3(buf_put_ushort)); reg_fun(intern(lit("buf-put-int"), user_package), func_n3(buf_put_int)); reg_fun(intern(lit("buf-put-uint"), user_package), func_n3(buf_put_uint)); reg_fun(intern(lit("buf-put-long"), user_package), func_n3(buf_put_long)); reg_fun(intern(lit("buf-put-ulong"), user_package), func_n3(buf_put_ulong)); reg_fun(intern(lit("buf-put-float"), user_package), func_n3(buf_put_float)); reg_fun(intern(lit("buf-put-double"), user_package), func_n3(buf_put_double)); reg_fun(intern(lit("buf-put-cptr"), user_package), func_n3(buf_put_cptr)); #if HAVE_I8 reg_fun(intern(lit("buf-get-i8"), user_package), func_n2(buf_get_i8)); reg_fun(intern(lit("buf-get-u8"), user_package), func_n2(buf_get_u8)); #endif #if HAVE_I16 reg_fun(intern(lit("buf-get-i16"), user_package), func_n2(buf_get_i16)); reg_fun(intern(lit("buf-get-u16"), user_package), func_n2(buf_get_u16)); #endif #if HAVE_I32 reg_fun(intern(lit("buf-get-i32"), user_package), func_n2(buf_get_i32)); reg_fun(intern(lit("buf-get-u32"), user_package), func_n2(buf_get_u32)); #endif #if HAVE_I64 reg_fun(intern(lit("buf-get-i64"), user_package), func_n2(buf_get_i64)); reg_fun(intern(lit("buf-get-u64"), user_package), func_n2(buf_get_u64)); #endif reg_fun(intern(lit("buf-get-char"), user_package), func_n2(buf_get_char)); reg_fun(intern(lit("buf-get-uchar"), user_package), func_n2(buf_get_uchar)); reg_fun(intern(lit("buf-get-short"), user_package), func_n2(buf_get_short)); reg_fun(intern(lit("buf-get-ushort"), user_package), func_n2(buf_get_ushort)); reg_fun(intern(lit("buf-get-int"), user_package), func_n2(buf_get_int)); reg_fun(intern(lit("buf-get-uint"), user_package), func_n2(buf_get_uint)); reg_fun(intern(lit("buf-get-long"), user_package), func_n2(buf_get_long)); reg_fun(intern(lit("buf-get-ulong"), user_package), func_n2(buf_get_ulong)); reg_fun(intern(lit("buf-get-float"), user_package), func_n2(buf_get_float)); reg_fun(intern(lit("buf-get-double"), user_package), func_n2(buf_get_double)); reg_fun(intern(lit("buf-get-cptr"), user_package), func_n2(buf_get_cptr)); reg_fun(intern(lit("make-buf-stream"), user_package), func_n1o(make_buf_stream, 0)); reg_fun(intern(lit("get-buf-from-stream"), user_package), func_n1(get_buf_from_stream)); reg_fun(intern(lit("buf-str"), user_package), func_n2o(buf_str, 1)); reg_fun(intern(lit("str-buf"), user_package), func_n2o(str_buf, 1)); reg_fun(intern(lit("buf-int"), user_package), func_n1(buf_int)); reg_fun(intern(lit("buf-uint"), user_package), func_n1(buf_uint)); reg_fun(intern(lit("int-buf"), user_package), func_n1(int_buf)); reg_fun(intern(lit("uint-buf"), user_package), func_n1(uint_buf)); #if HAVE_ZLIB reg_fun(intern(lit("buf-compress"), user_package), func_n2o(buf_compress, 1)); reg_fun(intern(lit("buf-decompress"), user_package), func_n1(buf_decompress)); #endif fill_stream_ops(&buf_strm_ops); }