1
0
forked from GitHub/gf-core
Files
gf-core/src/runtime/c/gu/seq.c
2012-01-20 13:41:10 +00:00

246 lines
5.0 KiB
C

#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;
}