summaryrefslogtreecommitdiff
path: root/src/runtime/c/gu/seq.c
diff options
context:
space:
mode:
authorkr.angelov <kr.angelov@gmail.com>2012-01-20 13:41:10 +0000
committerkr.angelov <kr.angelov@gmail.com>2012-01-20 13:41:10 +0000
commit2eee382a62a909d5a3f2f5eda94f30fe68fd5335 (patch)
treeb0b0d513535895f244214aebf6358e172b8dce6d /src/runtime/c/gu/seq.c
parentb9728357126f8b9a6311cca17d9f0dcc2a7bfb9b (diff)
initial import of the C runtime
Diffstat (limited to 'src/runtime/c/gu/seq.c')
-rw-r--r--src/runtime/c/gu/seq.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/runtime/c/gu/seq.c b/src/runtime/c/gu/seq.c
new file mode 100644
index 000000000..660cf5af6
--- /dev/null
+++ b/src/runtime/c/gu/seq.c
@@ -0,0 +1,245 @@
+#include <gu/out.h>
+#include <gu/seq.h>
+#include <gu/fun.h>
+#include <gu/assert.h>
+#include <string.h>
+
+
+struct GuBuf {
+ uint8_t* data;
+ size_t elem_size;
+ size_t avail_len;
+ GuFinalizer fin;
+};
+
+GuBuf*
+gu_seq_buf(GuSeq seq)
+{
+ gu_require(gu_tagged_tag(seq.w_) == 0);
+ return gu_word_ptr(seq.w_);
+}
+
+GuSeq
+gu_buf_seq(GuBuf* buf)
+{
+ return (GuSeq) { .w_ = gu_ptr_word(buf) };
+}
+
+size_t
+gu_buf_length(GuBuf* dyn)
+{
+ return (size_t)(((GuWord*)(void*)dyn)[-1] >> 1);
+}
+
+size_t
+gu_buf_avail(GuBuf* buf)
+{
+ return buf->avail_len;
+}
+
+
+static void
+gu_buf_set_length(GuBuf* dyn, size_t new_len)
+{
+ ((GuWord*)(void*)dyn)[-1] = ((GuWord) new_len) << 1 | 0x1;
+}
+
+static void
+gu_buf_fini(GuFinalizer* fin)
+{
+ GuBuf* buf = gu_container(fin, GuBuf, fin);
+ gu_mem_buf_free(buf->data);
+}
+
+GuBuf*
+gu_make_buf(size_t elem_size, GuPool* pool)
+{
+ GuBuf* buf = gu_new_prefixed(unsigned, GuBuf, pool);
+ gu_buf_set_length(buf, 0);
+ buf->elem_size = elem_size;
+ buf->data = NULL;
+ buf->avail_len = 0;
+ buf->fin.fn = gu_buf_fini;
+ gu_pool_finally(pool, &buf->fin);
+ gu_buf_set_length(buf, 0);
+ return buf;
+}
+
+static const GuWord gu_empty_seq[2] = {0, 0};
+
+GuSeq
+gu_make_seq(size_t elem_size, size_t length, GuPool* pool)
+{
+ size_t size = elem_size * length;
+ if (0 < length && length <= GU_TAG_MAX) {
+ void* buf = gu_malloc(pool, size);
+ return (GuSeq) { gu_tagged(buf, length) };
+ } else if (size == 0) {
+ return (GuSeq) { gu_tagged((void*)&gu_empty_seq[1], 0) };
+ } else {
+ void* buf = gu_malloc_prefixed(pool,
+ gu_alignof(GuWord),
+ sizeof(GuWord),
+ 0, size);
+ ((GuWord*) buf)[-1] = ((GuWord) length) << 1;
+ return (GuSeq) { gu_tagged(buf, 0) };
+ }
+}
+
+static void
+gu_buf_require(GuBuf* buf, size_t req_len)
+{
+ if (req_len <= buf->avail_len) {
+ return;
+ }
+ size_t req_size = buf->elem_size * req_len;
+ size_t real_size;
+ buf->data = gu_mem_buf_realloc(buf->data, req_size,
+ &real_size);
+ buf->avail_len = real_size / buf->elem_size;
+}
+
+void*
+gu_buf_data(GuBuf* buf)
+{
+ return buf->data;
+}
+
+void*
+gu_buf_extend_n(GuBuf* buf, size_t n_elems)
+{
+ size_t len = gu_buf_length(buf);
+ size_t new_len = len + n_elems;
+ gu_buf_require(buf, new_len);
+ gu_buf_set_length(buf, new_len);
+ return &buf->data[buf->elem_size * len];
+}
+
+void*
+gu_buf_extend(GuBuf* buf)
+{
+ return gu_buf_extend_n(buf, 1);
+}
+
+void
+gu_buf_push_n(GuBuf* buf, const void* data, size_t n_elems)
+{
+
+ void* p = gu_buf_extend_n(buf, n_elems);
+ memcpy(p, data, buf->elem_size * n_elems);
+}
+
+const void*
+gu_buf_trim_n(GuBuf* buf, size_t n_elems)
+{
+ gu_require(n_elems <= gu_buf_length(buf));
+ size_t new_len = gu_buf_length(buf) - n_elems;
+ gu_buf_set_length(buf, new_len);
+ return &buf->data[buf->elem_size * new_len];
+}
+
+const void*
+gu_buf_trim(GuBuf* buf)
+{
+ return gu_buf_trim_n(buf, 1);
+}
+
+void
+gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out)
+{
+ const void* p = gu_buf_trim_n(buf, n_elems);
+ memcpy(data_out, p, buf->elem_size * n_elems);
+}
+
+GuSeq
+gu_buf_freeze(GuBuf* buf, GuPool* pool)
+{
+ size_t len = gu_buf_length(buf);
+ GuSeq seq = gu_make_seq(buf->elem_size, len, pool);
+ void* bufdata = gu_buf_data(buf);
+ void* seqdata = gu_seq_data(seq);
+ memcpy(seqdata, bufdata, buf->elem_size * len);
+ return seq;
+}
+
+typedef struct GuBufOut GuBufOut;
+struct GuBufOut
+{
+ GuOutStream stream;
+ GuBuf* buf;
+};
+
+static size_t
+gu_buf_out_output(GuOutStream* stream, const uint8_t* src, size_t sz,
+ GuExn* err)
+{
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ gu_assert(sz % buf->elem_size == 0);
+ size_t len = sz / buf->elem_size;
+ gu_buf_push_n(bout->buf, src, len);
+ return len;
+}
+
+static uint8_t*
+gu_buf_outbuf_begin(GuOutStream* stream, size_t req, size_t* sz_out, GuExn* err)
+{
+ (void) req;
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ size_t esz = buf->elem_size;
+ size_t len = gu_buf_length(buf);
+ gu_buf_require(buf, len + (req + esz - 1) / esz);
+ size_t avail = buf->avail_len;
+ gu_assert(len < avail);
+ *sz_out = esz * (avail - len);
+ return &buf->data[len * esz];
+}
+
+static void
+gu_buf_outbuf_end(GuOutStream* stream, size_t sz, GuExn* err)
+{
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ size_t len = gu_buf_length(buf);
+ size_t elem_size = buf->elem_size;
+ gu_require(sz % elem_size == 0);
+ gu_require(sz < elem_size * (len - buf->avail_len));
+ gu_buf_set_length(buf, len + (sz / elem_size));
+}
+
+GuOut*
+gu_buf_out(GuBuf* buf, GuPool* pool)
+{
+ GuBufOut* bout = gu_new_i(pool, GuBufOut,
+ .stream.output = gu_buf_out_output,
+ .stream.begin_buf = gu_buf_outbuf_begin,
+ .stream.end_buf = gu_buf_outbuf_end,
+ .buf = buf);
+ return gu_new_out(&bout->stream, pool);
+}
+
+const GuSeq
+gu_null_seq = GU_NULL_SEQ;
+
+
+#include <gu/type.h>
+
+GU_DEFINE_KIND(GuSeq, GuOpaque);
+GU_DEFINE_KIND(GuBuf, abstract);
+
+GU_DEFINE_TYPE(GuChars, GuSeq, gu_type(char));
+GU_DEFINE_TYPE(GuBytes, GuSeq, gu_type(uint8_t));
+
+char*
+gu_chars_str(GuChars chars, GuPool* pool)
+{
+ size_t len = gu_seq_length(chars);
+ char* data = gu_seq_data(chars);
+ char* str = gu_new_str(len, pool);
+ memcpy(str, data, len);
+ return str;
+}