initial import of the C runtime

This commit is contained in:
kr.angelov
2012-01-20 13:41:10 +00:00
parent 2fd04e0797
commit a369d3bdac
94 changed files with 14344 additions and 1277 deletions

53
src/runtime/c/gu/assert.c Normal file
View File

@@ -0,0 +1,53 @@
#include <gu/assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
const char*
gu_assert_mode_descs[] = {
[GU_ASSERT_PRECOND] = "precondition failed",
[GU_ASSERT_POSTCOND] = "postcondition failed",
[GU_ASSERT_ASSERTION] = "assertion failed",
[GU_ASSERT_NEVER] = "control should not reach here",
};
void
gu_abort_v_(GuAssertMode mode,
const char* file, const char* func, int line,
const char* msg_fmt, va_list args)
{
const char* desc = gu_assert_mode_descs[mode];
(void) fprintf(stderr, "%s (%s:%d): %s\n", func, file, line, desc);
if (msg_fmt != NULL) {
(void) fputc('\t', stderr);
(void) vfprintf(stderr, msg_fmt, args);
(void) fputc('\n', stderr);
}
abort();
}
void
gu_abort_(GuAssertMode mode,
const char* file, const char* func, int line,
const char* msg_fmt, ...)
{
va_list args;
va_start(args, msg_fmt);
gu_abort_v_(mode, file, func, line, msg_fmt, args);
va_end(args);
}
void
gu_fatal(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
fputs("Fatal error", stderr);
if (fmt) {
fputs(": ", stderr);
(void) vfprintf(stderr, fmt, args);
}
fputc('\n', stderr);
abort();
}

61
src/runtime/c/gu/assert.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef GU_ASSERT_H_
#define GU_ASSERT_H_
#include <gu/defs.h>
typedef enum {
GU_ASSERT_PRECOND,
GU_ASSERT_ASSERTION,
GU_ASSERT_POSTCOND,
GU_ASSERT_NEVER
} GuAssertMode;
void
gu_abort_v_(GuAssertMode mode,
const char* file, const char* func, int line,
const char* msg_fmt, va_list args);
void
gu_abort_(GuAssertMode mode,
const char* file, const char* func, int line,
const char* msg_fmt, ...);
#ifndef NDEBUG
#define gu_assertion_(mode_, expr_, ...) \
GU_BEGIN \
if (!(expr_)) { \
gu_abort_(mode_, __FILE__, __func__, __LINE__, __VA_ARGS__); \
} \
GU_END
#else
// this should prevent unused variable warnings when a variable is only used
// in an assertion
#define gu_assertion_(mode_, expr_, ...) \
GU_BEGIN \
(void) (sizeof (expr_)); \
GU_END
#endif
#define gu_require(expr) \
gu_assertion_(GU_ASSERT_PRECOND, expr, "%s", #expr)
#define gu_assert_msg(expr, ...) \
gu_assertion_(GU_ASSERT_ASSERTION, expr, __VA_ARGS__)
#define gu_assert(expr) \
gu_assert_msg(expr, "%s", #expr)
#define gu_ensure(expr) \
gu_assertion_(GU_ASSERT_POSTCOND, expr, "%s", #expr)
#define gu_impossible_msg(...) \
gu_assertion_(GU_ASSERT_ASSERTION, false, __VA_ARGS__)
#define gu_impossible() \
gu_impossible_msg(NULL)
void
gu_fatal(const char* fmt, ...);
#endif /* GU_ASSERT_H_ */

53
src/runtime/c/gu/bits.c Normal file
View File

@@ -0,0 +1,53 @@
#include <gu/bits.h>
#include <limits.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
unsigned gu_ceil2e(unsigned u)
{
u--;
u |= u >> 1;
u |= u >> 2;
u |= u >> 4;
u |= u >> 8;
#if UINT_MAX > UINT16_MAX
u |= u >> 16;
#endif
#if UINT_MAX > UINT32_MAX
u |= u >> 32;
#endif
u++;
return u;
}
GU_DEFINE_TYPE(GuIntDecodeExn, abstract, _);
double
gu_decode_double(uint64_t u)
{
bool sign = u >> 63;
unsigned rawexp = u >> 52 & 0x7ff;
uint64_t mantissa = u & 0xfffffffffffff;
double ret;
if (rawexp == 0x7ff) {
if (mantissa == 0) {
ret = INFINITY;
} else {
// At least glibc supports specifying the
// mantissa like this.
int len = snprintf(NULL, 0, "0x%" PRIx64, mantissa);
char buf[len + 1];
snprintf(buf, len + 1, "0x%" PRIx64, mantissa);
ret = nan(buf);
}
} else {
uint64_t m = rawexp ? 1ULL << 52 | mantissa : mantissa << 1;
ret = ldexp((double) m, rawexp - 1075);
}
return sign ? copysign(ret, -1.0) : ret;
}

151
src/runtime/c/gu/bits.h Normal file
View File

@@ -0,0 +1,151 @@
#ifndef GU_BITS_H_
#define GU_BITS_H_
#include <gu/defs.h>
#include <gu/assert.h>
#define GU_WORD_BITS (sizeof(GuWord) * CHAR_BIT)
/*
* Based on the Bit Twiddling Hacks collection by Sean Eron Anderson
* <http://graphics.stanford.edu/~seander/bithacks.html>
*/
unsigned gu_ceil2e(unsigned i);
static inline int
gu_sign(int i) {
return (i > 0) - (i < 0);
}
static inline size_t
gu_ceildiv(size_t size, size_t div)
{
return (size + div - 1) / div;
}
static inline bool
gu_aligned(uintptr_t addr, size_t alignment)
{
gu_require(alignment == gu_ceil2e(alignment));
return (addr & (alignment - 1)) == 0;
}
static inline uintptr_t
gu_align_forward(uintptr_t addr, size_t alignment) {
gu_require(alignment == gu_ceil2e(alignment));
uintptr_t mask = alignment - 1;
return (addr + mask) & ~mask;
}
static inline uintptr_t
gu_align_backward(uintptr_t addr, size_t alignment) {
gu_require(alignment == gu_ceil2e(alignment));
return addr & ~(alignment - 1);
}
static inline bool
gu_bits_test(const GuWord* bitmap, int idx) {
return !!(bitmap[idx / GU_WORD_BITS] & 1 << (idx % GU_WORD_BITS));
}
static inline void
gu_bits_set(GuWord* bitmap, int idx) {
bitmap[idx / GU_WORD_BITS] |= ((GuWord) 1) << (idx % GU_WORD_BITS);
}
static inline void
gu_bits_clear(GuWord* bitmap, int idx) {
bitmap[idx / GU_WORD_BITS] &= ~(((GuWord) 1) << (idx % GU_WORD_BITS));
}
static inline size_t
gu_bits_size(size_t n_bits) {
return gu_ceildiv(n_bits, GU_WORD_BITS) * sizeof(GuWord);
}
static inline void*
gu_word_ptr(GuWord w)
{
return (void*) w;
}
static inline GuWord
gu_ptr_word(void* p)
{
return (GuWord) p;
}
#define GuOpaque() struct { GuWord w_; }
typedef GuWord GuTagged;
#define GU_TAG_MAX (sizeof(GuWord) - 1)
static inline size_t
gu_tagged_tag(GuTagged t) {
return (int) (t & (sizeof(GuWord) - 1));
}
static inline void*
gu_tagged_ptr(GuTagged w) {
return (void*) gu_align_backward(w, sizeof(GuWord));
}
static inline GuTagged
gu_tagged(void* ptr, size_t tag) {
gu_require(tag < sizeof(GuWord));
uintptr_t u = (uintptr_t) ptr;
gu_require(gu_align_backward(u, sizeof(GuWord)) == u);
return (GuWord) { u | tag };
}
#include <gu/exn.h>
#include <gu/type.h>
extern GU_DECLARE_TYPE(GuIntDecodeExn, abstract);
#define GU_DECODE_2C_(u_, t_, umax_, posmax_, tmin_, err_) \
(((u_) <= (posmax_)) \
? (t_) (u_) \
: (tmin_) + ((t_) ((umax_) - (u_))) < 0 \
? -1 - ((t_) ((umax_) - (u_))) \
: (gu_raise(err_, GuIntDecodeExn), -1))
static inline int8_t
gu_decode_2c8(uint8_t u, GuExn* err)
{
return GU_DECODE_2C_(u, int8_t, UINT8_C(0xff),
INT8_C(0x7f), INT8_MIN, err);
}
static inline int16_t
gu_decode_2c16(uint16_t u, GuExn* err)
{
return GU_DECODE_2C_(u, int16_t, UINT16_C(0xffff),
INT16_C(0x7fff), INT16_MIN, err);
}
static inline int32_t
gu_decode_2c32(uint32_t u, GuExn* err)
{
return GU_DECODE_2C_(u, int32_t, UINT32_C(0xffffffff),
INT32_C(0x7fffffff), INT32_MIN, err);
}
static inline int64_t
gu_decode_2c64(uint64_t u, GuExn* err)
{
return GU_DECODE_2C_(u, int64_t, UINT64_C(0xffffffffffffffff),
INT64_C(0x7fffffffffffffff), INT64_MIN, err);
}
double
gu_decode_double(uint64_t u);
#endif // GU_BITS_H_

73
src/runtime/c/gu/choice.c Normal file
View File

@@ -0,0 +1,73 @@
#include <gu/choice.h>
#include <gu/seq.h>
#include <gu/assert.h>
#include <gu/log.h>
struct GuChoice {
GuBuf* path;
size_t path_idx;
};
GuChoice*
gu_new_choice(GuPool* pool)
{
GuChoice* ch = gu_new(GuChoice, pool);
ch->path = gu_new_buf(uint8_t, pool);
ch->path_idx = 0;
return ch;
}
GuChoiceMark
gu_choice_mark(GuChoice* ch)
{
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
gu_debug("%p@%d: mark", ch, ch->path_idx);
return (GuChoiceMark){ch->path_idx};
}
void
gu_choice_reset(GuChoice* ch, GuChoiceMark mark)
{
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
gu_debug("%p@%d: reset %d", ch, ch->path_idx, mark.path_idx);
gu_require(mark.path_idx <= ch->path_idx );
ch->path_idx = mark.path_idx;
}
int
gu_choice_next(GuChoice* ch, int n_choices)
{
gu_assert(n_choices >= 0);
gu_require(n_choices <= UINT8_MAX);
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
if (n_choices == 0) {
return -1;
}
int i = 0;
if (gu_buf_length(ch->path) > ch->path_idx) {
i = (int) gu_buf_get(ch->path, uint8_t, ch->path_idx);
gu_assert(i <= n_choices);
} else {
gu_buf_push(ch->path, uint8_t, n_choices);
i = n_choices;
}
int ret = (i == 0) ? -1 : n_choices - i;
gu_debug("%p@%d: %d", ch, ch->path_idx, ret);
ch->path_idx++;
return ret;
}
bool
gu_choice_advance(GuChoice* ch)
{
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
while (gu_buf_length(ch->path) > ch->path_idx) {
uint8_t last = gu_buf_pop(ch->path, uint8_t);
if (last > 1) {
gu_buf_push(ch->path, uint8_t, last-1);
return true;
}
}
return false;
}

37
src/runtime/c/gu/choice.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef GU_CHOICE_H_
#define GU_CHOICE_H_
#include <gu/mem.h>
typedef struct GuChoice GuChoice;
typedef struct GuChoiceMark GuChoiceMark;
GuChoice*
gu_new_choice(GuPool* pool);
int
gu_choice_next(GuChoice* ch, int n_choices);
GuChoiceMark
gu_choice_mark(GuChoice* ch);
void
gu_choice_reset(GuChoice* ch, GuChoiceMark mark);
bool
gu_choice_advance(GuChoice* ch);
// private
struct GuChoiceMark {
size_t path_idx;
};
#endif // GU_CHOICE_H_

4
src/runtime/c/gu/defs.c Normal file
View File

@@ -0,0 +1,4 @@
#include <gu/defs.h>
void* const gu_null = NULL;
GuStruct* const gu_null_struct = NULL;

217
src/runtime/c/gu/defs.h Normal file
View File

@@ -0,0 +1,217 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file
*
* Miscellaneous macros.
*/
#ifndef GU_DEFS_H_
#define GU_DEFS_H_
#include <stddef.h>
#include <inttypes.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <stdarg.h>
#include <gu/sysdeps.h>
#define gu_container(mem_p, container_type, member) \
((container_type*)(((uint8_t*) (mem_p)) - offsetof(container_type, member)))
/**< Find the address of a containing structure.
*
* If @c s has type @c t*, where @c t is a struct or union type with a
* member @m, then <tt>GU_CONTAINER_P(&s->m, t, m) == s</tt>.
*
* @param mem_p Pointer to the member of a structure.
* @param container_type The type of the containing structure.
* @param member The name of the member of @a container_type
* @return The address of the containing structure.
*
* @hideinitializer */
#define gu_member_p(struct_p_, offset_) \
((void*)&((uint8_t*)(struct_p_))[offset_])
#define gu_member(t_, struct_p_, offset_) \
(*(t_*)gu_member_p(struct_p_, offset_))
#ifdef GU_ALIGNOF
# define gu_alignof GU_ALIGNOF
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
#else
# define gu_alignof(t_) \
((size_t)(offsetof(struct { char c_; t_ e_; }, e_)))
# ifdef GU_CAN_HAVE_FAM_IN_MEMBER
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
# endif
#endif
#define GU_PLIT(type, expr) \
((type[1]){ expr })
#define GU_LVALUE(type, expr) \
(*((type[1]){ expr }))
#define GU_COMMA ,
#define GU_ARRAY_LEN(t,a) (sizeof((const t[])a) / sizeof(t))
#define GU_ID(...) __VA_ARGS__
// This trick is by Laurent Deniau <laurent.deniau@cern.ch>
#define GU_N_ARGS(...) \
GU_N_ARGS_(__VA_ARGS__, \
31,30,29,28,27,26,25,24, \
23,22,21,20,19,18,17,16, \
15,14,13,12,11,10,9,8, \
7,6,5,4,3,2,1,0)
#define GU_N_ARGS_(...) GU_N_ARGS__(__VA_ARGS__)
#define GU_N_ARGS__(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, \
q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,N,...) \
N
#define GU_ARG1(a1, ...) a1
#define GU_ARG2(a1, a2, ...) a2
#define GU_BEGIN do {
#define GU_END } while (false)
#define GU_NOP GU_BEGIN (void) 0; GU_END
/**< @hideinitializer */
//
// Assert
//
#define GU_MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_))
#define GU_MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_))
static inline int
gu_max(int a, int b) {
return GU_MAX(a, b);
}
static inline int
gu_min(int a, int b) {
return GU_MIN(a, b);
}
#ifdef GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
#define gu_flex_alignof gu_alignof
#else
#define gu_flex_alignof(t) 0
#endif
static inline size_t
gu_flex_size(size_t ssize, size_t offset, int n_elems, size_t e_size)
{
return GU_MAX(ssize, offset + n_elems * e_size);
}
#define GU_FLEX_SIZE(type, flex_member, n_elems) \
gu_flex_size(sizeof(type), offsetof(type, flex_member), \
n_elems, sizeof(((type*)NULL)->flex_member[0]))
// The following are directly from gmacros.h in GLib
#define GU_PASTE_ARGS(id1_,id2_) \
id1_ ## id2_
#define GU_PASTE(id1_, id2_) \
GU_PASTE_ARGS(id1_, id2_)
#define GU_STATIC_ASSERT(expr_) \
typedef struct { \
char static_assert[(expr_) ? 1 : -1]; \
} GU_PASTE(GuStaticAssert_, __LINE__)
#define GU_ENSURE_TYPE(T, EXPR) \
((void)(sizeof(*(T*)NULL=(EXPR))),(EXPR))
#define GU_END_DECLS \
extern void gu_dummy_(void)
extern void* const gu_null;
// Dummy struct used for generic struct pointers
typedef struct GuStruct GuStruct;
extern GuStruct* const gu_null_struct;
typedef uintptr_t GuWord;
#define GU_WORD_MAX UINTPTR_MAX
// TODO: use max_align_t once C1X is supported
typedef union {
char c;
short s;
int i;
long l;
long long ll;
intmax_t im;
float f;
double d;
long double ld;
void* p;
void (*fp)();
} GuMaxAlign;
#define gu_alloca(N) \
(((union { GuMaxAlign align_; uint8_t buf_[N]; }){{0}}).buf_)
// For Doxygen
#define GU_PRIVATE /** @private */
#ifdef GU_GNUC
# define GU_LIKELY(EXPR) __builtin_expect(EXPR, 1)
# define GU_UNLIKELY(EXPR) __builtin_expect(EXPR, 0)
# define GU_IS_CONSTANT(EXPR) __builtin_constant_p(EXPR)
#else
# define GU_LIKELY(EXPR) (EXPR)
# define GU_UNLIKELY(EXPR) (EXPR)
# ifdef GU_OPTIMIZE_SIZE
# define GU_IS_CONSTANT(EXPR) false
# else
# define GU_IS_CONSTANT(EXPR) true
# endif
#endif
// Splint annotations
#define GU_ONLY GU_SPLINT(only)
#define GU_NULL GU_SPLINT(null)
#define GU_NOTNULL GU_SPLINT(notnull)
#define GU_RETURNED GU_SPLINT(returned)
#define GU_ABSTRACT GU_SPLINT(abstract)
#define GU_IMMUTABLE GU_SPLINT(immutable)
#define GU_NOTREACHED GU_SPLINT(notreached)
#define GU_UNUSED GU_SPLINT(unused) GU_GNUC_ATTR(unused)
#define GU_OUT GU_SPLINT(out)
#define GU_IN GU_SPLINT(in)
#define GU_NORETURN GU_SPLINT(noreturn) GU_GNUC_ATTR(noreturn)
#define GU_MODIFIES(x) GU_SPLINT(modifies x)
#endif // GU_DEFS_H_

411
src/runtime/c/gu/dump.c Normal file
View File

@@ -0,0 +1,411 @@
#include <gu/dump.h>
#include <gu/list.h>
#include <gu/variant.h>
#include <gu/seq.h>
#include <gu/assert.h>
#include <gu/str.h>
#include <gu/file.h>
GuDump*
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool)
{
GuDump* ctx = gu_new(GuDump, pool);
ctx->pool = pool;
if (dumpers == NULL) {
dumpers = &gu_dump_table;
}
ctx->dumpers = gu_new_type_map(dumpers, pool);
ctx->yaml = gu_new_yaml(wtr, err, pool);
ctx->data = gu_new_addr_map(void, void*, &gu_null, pool);
ctx->print_address = false;
return ctx;
}
void
gu_dump(GuType* type, const void* value, GuDump* ctx)
{
GuDumpFn* dumper = gu_type_map_get(ctx->dumpers, type);
if (ctx->print_address) {
GuPool* pool = gu_new_pool();
GuString s = gu_format_string(pool, "%p", value);
gu_yaml_comment(ctx->yaml, s);
gu_pool_free(pool);
}
(*dumper)(dumper, type, value, ctx);
}
void
gu_dump_stderr(GuType* type, const void* value, GuExn* err)
{
GuPool* pool = gu_new_pool();
GuOut* out = gu_file_out(stderr, pool);
#if 0
GuWriter* wtr = gu_locale_writer(out, pool);
#else
GuWriter* wtr = gu_new_utf8_writer(out, pool);
#endif
GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
gu_dump(type, value, ctx);
gu_pool_free(pool);
}
static void
gu_dump_scalar(GuDump* ctx, const char* fmt, ...)
{
GuPool* tmp_pool = gu_local_pool();
va_list args;
va_start(args, fmt);
GuString s = gu_format_string_v(fmt, args, tmp_pool);
va_end(args);
gu_yaml_scalar(ctx->yaml, s);
gu_pool_free(tmp_pool);
}
static void
gu_dump_str_scalar(GuDump* ctx, const char* str)
{
GuPool* tmp_pool = gu_local_pool();
GuString s = gu_str_string(str, tmp_pool);
gu_yaml_scalar(ctx->yaml, s);
gu_pool_free(tmp_pool);
}
static void
gu_dump_null(GuDump* ctx)
{
gu_yaml_tag_secondary(ctx->yaml, "null");
gu_yaml_scalar(ctx->yaml, gu_empty_string);
}
static void
gu_dump_int(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const int* ip = p;
gu_dump_scalar(ctx, "%d", *ip);
}
static void
gu_dump_uint16(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const uint16_t* ip = p;
gu_dump_scalar(ctx, "%" PRIu16, *ip);
}
static void
gu_dump_size(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) (dumper && type);
const size_t* zp = p;
gu_dump_scalar(ctx, "%zu", *zp);
}
static void
gu_dump_double(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const double* dp = p;
gu_dump_scalar(ctx, "%lf", *dp);
}
static const char gu_dump_length_key[] = "gu_dump_length_key";
static void
gu_dump_length(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const GuLength* ip = p;
gu_dump_scalar(ctx, "%d", *ip);
GuLength* lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
if (lenp != NULL) {
*lenp = *ip;
}
}
static void
gu_dump_str(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const GuStr* sp = p;
gu_dump_str_scalar(ctx, *sp);
}
static void
gu_dump_string(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
const GuString* sp = p;
gu_yaml_scalar(ctx->yaml, *sp);
}
// For _non-shared_ pointers.
static void
gu_dump_pointer(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuPointerType* ptype = (GuPointerType*) type;
void* const* pp = p;
if (*pp == NULL) {
gu_dump_null(ctx);
} else {
gu_dump(ptype->pointed_type, *pp, ctx);
}
}
typedef struct {
GuMapItor itor;
GuMapType* mtype;
GuDump* ctx;
} GuDumpMapFn;
static void
gu_dump_map_itor(GuMapItor* self, const void* key, void* value, GuExn* err)
{
(void) err;
GuDumpMapFn* clo = (GuDumpMapFn*) self;
gu_dump(clo->mtype->key_type, key, clo->ctx);
gu_dump(clo->mtype->value_type, value, clo->ctx);
}
static void
gu_dump_map(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuMapType* mtype = (GuMapType*) type;
GuMap* map = (GuMap*) p;
gu_yaml_begin_mapping(ctx->yaml);
GuDumpMapFn clo = { { gu_dump_map_itor }, mtype, ctx };
gu_map_iter(map, &clo.itor, NULL);
gu_yaml_end(ctx->yaml);
}
static void
gu_dump_struct(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuStructRepr* srepr = (GuStructRepr*) type;
gu_yaml_begin_mapping(ctx->yaml);
const uint8_t* data = p;
GuLength* old_lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
GuLength len = (GuLength)-1;
gu_map_put(ctx->data, gu_dump_length_key, void*, &len);
for (int i = 0; i < srepr->members.len; i++) {
const GuMember* member = &srepr->members.elems[i];
gu_dump_str_scalar(ctx, member->name);
const uint8_t* memp = &data[member->offset];
if (member->is_flex) {
// Flexible array member
gu_assert(len != (GuLength)-1);
size_t mem_s = gu_type_size(member->type);
gu_yaml_begin_sequence(ctx->yaml);
for (GuLength i = 0; i < len; i++) {
gu_dump(member->type, &memp[i * mem_s], ctx);
}
gu_yaml_end(ctx->yaml);
} else {
gu_dump(member->type, memp, ctx);
}
}
gu_yaml_end(ctx->yaml);
if (old_lenp) {
gu_map_set(ctx->data, gu_dump_length_key, void*, old_lenp);
}
}
static void
gu_dump_alias(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuTypeAlias* alias = gu_type_cast(type, alias);
gu_dump(alias->type, p, ctx);
}
static const char gu_dump_reference_key[] = "reference";
static bool
gu_dump_anchor(GuDump* ctx, const void* p)
{
GuMap* map = gu_map_get(ctx->data, gu_dump_reference_key, void*);
if (map == NULL) {
map = gu_new_addr_map(void, GuYamlAnchor,
&gu_yaml_null_anchor, ctx->pool);
gu_map_put(ctx->data, gu_dump_reference_key, void*, map);
}
GuYamlAnchor a = gu_map_get(map, p, GuYamlAnchor);
if (a == gu_yaml_null_anchor) {
a = gu_yaml_anchor(ctx->yaml);
gu_map_put(map, p, GuYamlAnchor, a);
return true;
} else {
gu_yaml_alias(ctx->yaml, a);
return false;
}
}
static void
gu_dump_referenced(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuTypeAlias* alias = gu_type_cast(type, alias);
bool created = gu_dump_anchor(ctx, p);
if (created) {
gu_dump(alias->type, p, ctx);
} else {
// gu_assert(false);
}
}
static void
gu_dump_reference(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
(void) type;
void* const* pp = p;
bool created = gu_dump_anchor(ctx, *pp);
if (created) {
// gu_assert(false);
GuPointerType* ptype = (GuPointerType*) type;
gu_dump(ptype->pointed_type, *pp, ctx);
}
}
static void
gu_dump_shared(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
void* const* pp = p;
if (*pp == NULL) {
gu_dump_null(ctx);
} else {
bool created = gu_dump_anchor(ctx, *pp);
if (created) {
GuPointerType* ptype = (GuPointerType*) type;
gu_dump(ptype->pointed_type, *pp, ctx);
}
}
}
static void
gu_dump_list(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuListType* ltype = (GuListType*) type;
const uint8_t* up = p;
int len = * (const int*) p;
size_t elem_size = gu_type_size(ltype->elem_type);
gu_yaml_begin_sequence(ctx->yaml);
for (int i = 0; i < len; i++) {
ptrdiff_t offset = ltype->elems_offset + i * elem_size;
gu_dump(ltype->elem_type, &up[offset], ctx);
}
gu_yaml_end(ctx->yaml);
}
static void
gu_dump_variant(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuVariantType* vtype = gu_type_cast(type, GuVariant);
const GuVariant* vp = p;
int tag = gu_variant_tag(*vp);
for (int i = 0; i < vtype->ctors.len; i++) {
GuConstructor* ctor = &vtype->ctors.elems[i];
if (ctor->c_tag == tag) {
gu_yaml_begin_mapping(ctx->yaml);
gu_dump_str_scalar(ctx, ctor->c_name);
void* data = gu_variant_data(*vp);
gu_dump(ctor->type, data, ctx);
gu_yaml_end(ctx->yaml);
return;
}
}
gu_assert(false);
}
static void
gu_dump_enum(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuEnumType* etype = gu_type_cast(type, enum);
GuEnumConstant* cp = gu_enum_value(etype, p);
gu_assert(cp != NULL);
gu_dump_str_scalar(ctx, cp->name);
}
static void
gu_dump_seq(GuDumpFn* dumper, GuType* type, const void* p,
GuDump* ctx)
{
(void) dumper;
GuSeqType* dtype = gu_type_cast(type, GuSeq);
size_t elem_size = gu_type_size(dtype->elem_type);
const GuSeq* seqp = p;
GuSeq seq = *seqp;
if (gu_seq_is_null(seq)) {
gu_dump_null(ctx);
return;
}
size_t len = gu_seq_length(seq);
const uint8_t* data = gu_seq_data(seq);
gu_yaml_begin_sequence(ctx->yaml);
for (size_t i = 0; i < len; i++) {
const void* elemp = &data[i * elem_size];
gu_dump(dtype->elem_type, elemp, ctx);
}
gu_yaml_end(ctx->yaml);
}
GuTypeTable
gu_dump_table = GU_TYPETABLE(
GU_SLIST_0,
{ gu_kind(int), gu_fn(gu_dump_int) },
{ gu_kind(uint16_t), gu_fn(gu_dump_uint16) },
{ gu_kind(size_t), gu_fn(gu_dump_size) },
{ gu_kind(GuStr), gu_fn(gu_dump_str) },
{ gu_kind(GuString), gu_fn(gu_dump_string) },
{ gu_kind(struct), gu_fn(gu_dump_struct) },
{ gu_kind(pointer), gu_fn(gu_dump_pointer) },
{ gu_kind(GuMap), gu_fn(gu_dump_map) },
{ gu_kind(alias), gu_fn(gu_dump_alias) },
{ gu_kind(reference), gu_fn(gu_dump_reference) },
{ gu_kind(referenced), gu_fn(gu_dump_referenced) },
{ gu_kind(shared), gu_fn(gu_dump_shared) },
{ gu_kind(GuList), gu_fn(gu_dump_list) },
{ gu_kind(GuSeq), gu_fn(gu_dump_seq) },
{ gu_kind(GuLength), gu_fn(gu_dump_length) },
{ gu_kind(GuVariant), gu_fn(gu_dump_variant) },
{ gu_kind(double), gu_fn(gu_dump_double) },
{ gu_kind(enum), gu_fn(gu_dump_enum) },
);

34
src/runtime/c/gu/dump.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef GU_DUMP_H_
#define GU_DUMP_H_
#include <gu/defs.h>
#include <gu/yaml.h>
#include <gu/type.h>
#include <gu/map.h>
typedef struct GuDump GuDump;
struct GuDump {
GuPool* pool;
GuYaml* yaml;
GuMap* data;
GuTypeMap* dumpers;
bool print_address;
};
typedef void (*GuDumpFn)(GuFn* self, GuType* type, const void* value, GuDump* ctx);
GuDump*
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool);
void
gu_dump(GuType* type, const void* value, GuDump* ctx);
void
gu_dump_stderr(GuType* type, const void* value, GuExn* err);
extern GuTypeTable
gu_dump_table;
#endif // GU_DUMP_H_

7
src/runtime/c/gu/enum.c Normal file
View File

@@ -0,0 +1,7 @@
#include <gu/enum.h>
void
gu_enum_next(GuEnum* en, void* to, GuPool* pool)
{
en->next(en, to, pool);
}

35
src/runtime/c/gu/enum.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef GU_ENUM_H_
#define GU_ENUM_H_
#include <gu/mem.h>
typedef struct GuEnum GuEnum;
struct GuEnum {
void (*next)(GuEnum* self, void* to, GuPool* pool);
};
void
gu_enum_next(GuEnum* en, void* to, GuPool* pool);
#ifdef GU_GNUC
#define gu_next(ENUM, T, POOL) \
({ \
T gu_next_tmp_; \
gu_enum_next((ENUM), &gu_next_tmp_, (POOL)); \
gu_next_tmp_; \
})
#else
static inline void*
gu_enum_next_(GuEnum* en, void* to, GuPool* pool)
{
gu_enum_next(en, to, pool);
return to;
}
#define gu_next(ENUM, T, POOL) \
(*(T*)gu_enum_next_((ENUM), &(T){0}, (POOL)))
#endif
#endif /* GU_ENUM_H_ */

72
src/runtime/c/gu/exn.c Normal file
View File

@@ -0,0 +1,72 @@
#include <gu/exn.h>
#include <gu/assert.h>
GuExn*
gu_new_exn(GuExn* parent, GuKind* catch, GuPool* pool)
{
return gu_new_s(pool, GuExn,
.state = GU_EXN_OK,
.parent = parent,
.catch = catch,
.caught = NULL,
.data.pool = pool,
.data.data = NULL);
}
void
gu_exn_block(GuExn* err)
{
if (err && err->state == GU_EXN_RAISED) {
err->state = GU_EXN_BLOCKED;
}
}
void
gu_exn_unblock(GuExn* err)
{
if (err && err->state == GU_EXN_BLOCKED) {
err->state = GU_EXN_RAISED;
}
}
GuExnData*
gu_exn_raise_debug_(GuExn* base, GuType* type,
const char* filename, const char* func, int lineno)
{
gu_require(type);
// TODO: log the error, once there's a system for dumping
// error objects.
GuExn* err = base;
while (err && !(err->catch && gu_type_has_kind(type, err->catch))) {
err->state = GU_EXN_RAISED;
err = err->parent;
}
if (!err) {
gu_abort_(GU_ASSERT_ASSERTION, filename, func, lineno,
"Unexpected error raised");
}
GuExnState old_state = err->state;
err->state = GU_EXN_RAISED;
if (old_state == GU_EXN_OK) {
err->caught = type;
if (err->data.pool) {
return &err->data;
}
}
// Exceptian had already been raised, possibly blocked, or no
// exception value is required.
return NULL;
}
GuExnData*
gu_exn_raise_(GuExn* base, GuType* type)
{
return gu_exn_raise_debug_(base, type, NULL, NULL, -1);
}
GU_DEFINE_TYPE(GuErrno, signed, _);

195
src/runtime/c/gu/exn.h Normal file
View File

@@ -0,0 +1,195 @@
#ifndef GU_EXN_H_
#define GU_EXN_H_
#include <gu/mem.h>
#include <gu/type.h>
/** @file
*
* @defgroup GuExn Exceptions
* Defined in <gu/exn.h>.
* @{
*/
/// An exception frame.
typedef struct GuExn GuExn;
/// @private
typedef enum {
GU_EXN_RAISED,
GU_EXN_OK,
GU_EXN_BLOCKED
} GuExnState;
typedef struct GuExnData GuExnData;
/// A structure for storing exception values.
struct GuExnData
/**
* When an exception is raised, if there is an associated value, it
* must be allocated from a pool that still exists when control
* returns to the handler of that exception. This structure is used to
* communicate the exception from the raiser to the handler: the
* handler sets #pool when setting up the exception frame, and the
* raiser uses that pool to allocate the value and stores that in
* #data. When control returns to the handler, it reads the value from
* there.
*/
{
/// The pool that the exception value should be allocated from.
GuPool* const pool;
/// The exception value.
const void* data;
};
struct GuExn {
/// @privatesection
GuExnState state;
GuExn* parent;
GuKind* catch;
GuType* caught;
GuExnData data;
};
/// @name Creating exception frames
//@{
/// Allocate a new local exception frame.
#define gu_exn(parent_, catch_, pool_) &(GuExn){ \
.state = GU_EXN_OK, \
.parent = parent_, \
.catch = gu_kind(catch_), \
.caught = NULL, \
.data.pool = pool_, \
.data.data = NULL \
}
/// Allocate a new exception frame.
GuExn*
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);
static inline bool
gu_exn_is_raised(GuExn* err) {
return err && (err->state == GU_EXN_RAISED);
}
static inline void
gu_exn_clear(GuExn* err) {
err->caught = NULL;
err->state = GU_EXN_OK;
}
GuType*
gu_exn_caught(GuExn* err);
const void*
gu_exn_caught_data(GuExn* err);
/// Temporarily block a raised exception.
void
gu_exn_block(GuExn* err);
/// Show again a blocked exception.
void
gu_exn_unblock(GuExn* err);
//@private
GuExnData*
gu_exn_raise_(GuExn* err, GuType* type);
//@private
GuExnData*
gu_exn_raise_debug_(GuExn* err, GuType* type,
const char* filename, const char* func, int lineno);
#ifdef NDEBUG
#define gu_exn_raise(err_, type_) \
gu_exn_raise_(err_, type_)
#else
#define gu_exn_raise(err_, type_) \
gu_exn_raise_debug_(err_, type_, \
__FILE__, __func__, __LINE__)
#endif
/// Raise an exception.
#define gu_raise(exn, T) \
gu_exn_raise(exn, gu_type(T))
/**<
* @param exn The current exception frame.
*
* @param T The C type of the exception to raise.
*
* @return A #GuExnData object that can be used to store the exception value, or
* \c NULL if no value is required.
*
* @note The associated #GuType object for type \p T must be visible.
*/
#define gu_raise_new(error_, t_, pool_, expr_) \
GU_BEGIN \
GuExnData* gu_raise_err_ = gu_raise(error_, t_); \
if (gu_raise_err_) { \
GuPool* pool_ = gu_raise_err_->pool; \
gu_raise_err_->data = expr_; \
} \
GU_END
#define gu_raise_i(error_, t_, ...) \
gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))
/// Check the status of the current exception frame
static inline bool
gu_ok(GuExn* exn) {
return !GU_UNLIKELY(gu_exn_is_raised(exn));
}
/**<
* @return \c false if an exception has been raised in the frame \p exn
* and it has not been blocked, \c true otherwise.
*/
/// Return from current function if an exception has been raised.
#define gu_return_on_exn(exn_, retval_) \
GU_BEGIN \
if (gu_exn_is_raised(exn_)) return retval_; \
GU_END
/**<
* @showinitializer
*/
#include <errno.h>
typedef int GuErrno;
extern GU_DECLARE_TYPE(GuErrno, signed);
#define gu_raise_errno(error_) \
gu_raise_i(error_, GuErrno, errno)
#if 0
typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);
extern GuTypeTable gu_exn_default_printer;
void
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);
#endif
/** @} */
#endif // GU_EXN_H_

73
src/runtime/c/gu/file.c Normal file
View File

@@ -0,0 +1,73 @@
#include <gu/file.h>
typedef struct GuFileOutStream GuFileOutStream;
struct GuFileOutStream {
GuOutStream stream;
FILE* file;
};
static size_t
gu_file_output(GuOutStream* stream, const uint8_t* buf, size_t len, GuExn* err)
{
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
errno = 0;
size_t wrote = fwrite(buf, 1, len, fos->file);
if (wrote < len) {
if (ferror(fos->file)) {
gu_raise_errno(err);
}
}
return wrote;
}
static void
gu_file_flush(GuOutStream* stream, GuExn* err)
{
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
errno = 0;
if (fflush(fos->file) != 0) {
gu_raise_errno(err);
}
}
GuOut*
gu_file_out(FILE* file, GuPool* pool)
{
GuFileOutStream* fos = gu_new_i(pool, GuFileOutStream,
.stream.output = gu_file_output,
.stream.flush = gu_file_flush,
.file = file);
return gu_new_out(&fos->stream, pool);
}
typedef struct GuFileInStream GuFileInStream;
struct GuFileInStream {
GuInStream stream;
FILE* file;
};
static size_t
gu_file_input(GuInStream* stream, uint8_t* buf, size_t sz, GuExn* err)
{
GuFileInStream* fis = gu_container(stream, GuFileInStream, stream);
errno = 0;
size_t got = fread(buf, 1, sz, fis->file);
if (got == 0) {
if (ferror(fis->file)) {
gu_raise_errno(err);
}
}
return got;
}
GuIn*
gu_file_in(FILE* file, GuPool* pool)
{
GuFileInStream* fis = gu_new_s(pool, GuFileInStream,
.stream.input = gu_file_input,
.file = file);
return gu_new_in(&fis->stream, pool);
}

14
src/runtime/c/gu/file.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef GU_FILE_H_
#define GU_FILE_H_
#include <gu/in.h>
#include <gu/out.h>
#include <stdio.h>
GuOut*
gu_file_out(FILE* file, GuPool* pool);
GuIn*
gu_file_in(FILE* file, GuPool* pool);
#endif // GU_FILE_H_

1
src/runtime/c/gu/fun.c Normal file
View File

@@ -0,0 +1 @@
#include <gu/fun.h>

65
src/runtime/c/gu/fun.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef GU_FUN_H_
#define GU_FUN_H_
#include <gu/defs.h>
typedef void (*GuFn)();
typedef void (*GuFn0)(GuFn* clo);
typedef void (*GuFn1)(GuFn* clo, void* arg1);
typedef void (*GuFn2)(GuFn* clo, void* arg1, void* arg2);
#define gu_fn(fn_) (&(GuFn){ fn_ })
static inline void
gu_apply0(GuFn* fn) {
(*fn)(fn);
}
static inline void
gu_apply1(GuFn* fn, void* arg1) {
(*fn)(fn, arg1);
}
static inline void
gu_apply2(GuFn* fn, void* arg1, void* arg2) {
(*fn)(fn, arg1, arg2);
}
#define gu_apply(fn_, ...) \
((fn_)->fn((fn_), __VA_ARGS__))
typedef struct GuClo0 GuClo0;
struct GuClo0 {
GuFn fn;
};
typedef struct GuClo1 GuClo1;
struct GuClo1 {
GuFn fn;
void *env1;
};
typedef struct GuClo2 GuClo2;
struct GuClo2 {
GuFn fn;
void *env1;
void *env2;
};
typedef struct GuClo3 GuClo3;
struct GuClo3 {
GuFn fn;
void *env1;
void *env2;
void *env3;
};
typedef const struct GuEquality GuEquality;
struct GuEquality {
bool (*is_equal)(GuEquality* self, const void* a, const void* b);
};
#endif // GU_FUN_H_

77
src/runtime/c/gu/hash.c Normal file
View File

@@ -0,0 +1,77 @@
#include <gu/hash.h>
GuHash
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len)
{
for (size_t n = 0; n < len; n++) {
h = gu_hash_byte(h, buf[n]);
}
return h;
}
static bool
gu_int_eq_fn(GuEquality* self, const void* p1, const void* p2)
{
(void) self;
const int* ip1 = p1;
const int* ip2 = p2;
return *ip1 == *ip2;
}
static GuHash
gu_int_hash_fn(GuHasher* self, const void* p)
{
(void) self;
return (GuHash) *(const int*) p;
}
GuHasher gu_int_hasher[1] = {
{
{ gu_int_eq_fn },
gu_int_hash_fn
}
};
static bool
gu_addr_eq_fn(GuEquality* self, const void* p1, const void* p2)
{
(void) self;
return (p1 == p2);
}
static GuHash
gu_addr_hash_fn(GuHasher* self, const void* p)
{
(void) self;
return (GuHash) (uintptr_t) p;
}
GuHasher gu_addr_hasher[1] = {
{
{ gu_addr_eq_fn },
gu_addr_hash_fn
}
};
static bool
gu_word_eq_fn(GuEquality* self, const void* p1, const void* p2)
{
(void) self;
const GuWord* wp1 = p1;
const GuWord* wp2 = p2;
return (*wp1 == *wp2);
}
static GuHash
gu_word_hash_fn(GuHasher* self, const void* p)
{
(void) self;
return (GuHash) (uintptr_t) p;
}
GuHasher gu_word_hasher[1] = {
{
{ gu_word_eq_fn },
gu_word_hash_fn
}
};

40
src/runtime/c/gu/hash.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef GU_HASH_H_
#define GU_HASH_H_
#include <gu/fun.h>
typedef GuWord GuHash;
static inline GuHash
gu_hash_ptr(void* ptr)
{
return (GuHash) ptr;
}
static inline GuHash
gu_hash_byte(GuHash h, uint8_t u)
{
// Paul Larson's simple byte hash
return h * 101 + u;
}
GuHash
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len);
typedef const struct GuHasher GuHasher;
struct GuHasher {
GuEquality eq;
GuHash (*hash)(GuHasher* self, const void* p);
};
extern GuHasher gu_int_hasher[1];
extern GuHasher gu_addr_hasher[1];
extern GuHasher gu_word_hasher[1];
#endif // GU_HASH_H_

421
src/runtime/c/gu/in.c Normal file
View File

@@ -0,0 +1,421 @@
#include <gu/in.h>
#include <gu/bits.h>
#include <math.h>
GU_DEFINE_TYPE(GuEOF, abstract, _);
static bool
gu_in_is_buffering(GuIn* in)
{
return (in->buf_end != NULL);
}
static void
gu_in_end_buffering(GuIn* in, GuExn* err)
{
if (!gu_in_is_buffering(in)) {
return;
}
if (in->stream->end_buffer) {
size_t len = ((ptrdiff_t) in->buf_size) + in->buf_curr;
in->stream->end_buffer(in->stream, len, err);
}
in->buf_curr = 0;
in->buf_size = 0;
in->buf_end = NULL;
}
static bool
gu_in_begin_buffering(GuIn* in, GuExn* err)
{
if (gu_in_is_buffering(in)) {
if (in->buf_curr < 0) {
return true;
} else {
gu_in_end_buffering(in, err);
if (!gu_ok(err)) return false;
}
}
if (!in->stream->begin_buffer) {
return false;
}
size_t sz = 0;
const uint8_t* new_buf =
in->stream->begin_buffer(in->stream, &sz, err);
if (new_buf) {
in->buf_end = &new_buf[sz];
in->buf_curr = -(ptrdiff_t) sz;
in->buf_size = sz;
return true;
}
return false;
}
static size_t
gu_in_input(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
{
if (sz == 0) {
return 0;
}
gu_in_end_buffering(in, err);
if (!gu_ok(err)) {
return 0;
}
GuInStream* stream = in->stream;
if (stream->input) {
return stream->input(stream, dst, sz, err);
}
gu_raise(err, GuEOF);
return 0;
}
size_t
gu_in_some(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
{
gu_require(sz <= PTRDIFF_MAX);
if (!gu_in_begin_buffering(in, err)) {
if (!gu_ok(err)) return 0;
return gu_in_input(in, dst, sz, err);
}
size_t real_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
memcpy(dst, &in->buf_end[in->buf_curr], real_sz);
in->buf_curr += real_sz;
return real_sz;
}
void
gu_in_bytes_(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
{
size_t avail_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
memcpy(dst, &in->buf_end[in->buf_curr], avail_sz);
in->buf_curr += avail_sz;
if (avail_sz < sz) {
gu_in_input(in, &dst[avail_sz], sz - avail_sz, err);
}
}
const uint8_t*
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err)
{
if (!gu_in_begin_buffering(in, err)) {
return NULL;
}
*sz_out = (size_t) -in->buf_curr;
return &in->buf_end[in->buf_curr];
}
void
gu_in_end_span(GuIn* in, size_t consumed)
{
gu_require(consumed <= (size_t) -in->buf_curr);
in->buf_curr += (ptrdiff_t) consumed;
}
uint8_t
gu_in_u8_(GuIn* in, GuExn* err)
{
if (gu_in_begin_buffering(in, err) && in->buf_curr < 0) {
return in->buf_end[in->buf_curr++];
}
uint8_t u = 0;
size_t r = gu_in_input(in, &u, 1, err);
if (r < 1) {
gu_raise(err, GuEOF);
return 0;
}
return u;
}
static uint64_t
gu_in_be(GuIn* in, GuExn* err, int n)
{
uint8_t buf[8];
gu_in_bytes(in, buf, n, err);
uint64_t u = 0;
for (int i = 0; i < n; i++) {
u = u << 8 | buf[i];
}
return u;
}
static uint64_t
gu_in_le(GuIn* in, GuExn* err, int n)
{
uint8_t buf[8];
gu_in_bytes(in, buf, n, err);
uint64_t u = 0;
for (int i = 0; i < n; i++) {
u = u << 8 | buf[i];
}
return u;
}
int8_t
gu_in_s8(GuIn* in, GuExn* err)
{
return gu_decode_2c8(gu_in_u8(in, err), err);
}
uint16_t
gu_in_u16le(GuIn* in, GuExn* err)
{
return gu_in_le(in, err, 2);
}
int16_t
gu_in_s16le(GuIn* in, GuExn* err)
{
return gu_decode_2c16(gu_in_u16le(in, err), err);
}
uint16_t
gu_in_u16be(GuIn* in, GuExn* err)
{
return gu_in_be(in, err, 2);
}
int16_t
gu_in_s16be(GuIn* in, GuExn* err)
{
return gu_decode_2c16(gu_in_u16be(in, err), err);
}
uint32_t
gu_in_u32le(GuIn* in, GuExn* err)
{
return gu_in_le(in, err, 4);
}
int32_t
gu_in_s32le(GuIn* in, GuExn* err)
{
return gu_decode_2c32(gu_in_u32le(in, err), err);
}
uint32_t
gu_in_u32be(GuIn* in, GuExn* err)
{
return gu_in_be(in, err, 4);
}
int32_t
gu_in_s32be(GuIn* in, GuExn* err)
{
return gu_decode_2c32(gu_in_u32be(in, err), err);
}
uint64_t
gu_in_u64le(GuIn* in, GuExn* err)
{
return gu_in_le(in, err, 8);
}
int64_t
gu_in_s64le(GuIn* in, GuExn* err)
{
return gu_decode_2c64(gu_in_u64le(in, err), err);
}
uint64_t
gu_in_u64be(GuIn* in, GuExn* err)
{
return gu_in_be(in, err, 8);
}
int64_t
gu_in_s64be(GuIn* in, GuExn* err)
{
return gu_decode_2c64(gu_in_u64be(in, err), err);
}
double
gu_in_f64le(GuIn* in, GuExn* err)
{
return gu_decode_double(gu_in_u64le(in, err));
}
double
gu_in_f64be(GuIn* in, GuExn* err)
{
return gu_decode_double(gu_in_u64le(in, err));
}
static void
gu_in_fini(GuFinalizer* fin)
{
GuIn* in = gu_container(fin, GuIn, fini);
GuPool* pool = gu_local_pool();
GuExn* err = gu_exn(NULL, type, pool);
gu_in_end_buffering(in, err);
gu_pool_free(pool);
}
GuIn
gu_init_in(GuInStream* stream)
{
return (GuIn) {
.buf_end = NULL,
.buf_curr = 0,
.buf_size = 0,
.stream = stream,
.fini.fn = gu_in_fini
};
}
GuIn*
gu_new_in(GuInStream* stream, GuPool* pool)
{
GuIn* in = gu_new(GuIn, pool);
*in = gu_init_in(stream);
return in;
}
typedef struct GuProxyInStream GuProxyInStream;
struct GuProxyInStream {
GuInStream stream;
GuIn* real_in;
};
static const uint8_t*
gu_proxy_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
{
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
return gu_in_begin_span(pis->real_in, sz_out, err);
}
static void
gu_proxy_in_end_buffer(GuInStream* self, size_t sz, GuExn* err)
{
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
gu_in_end_span(pis->real_in, sz);
}
static size_t
gu_proxy_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
{
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
return gu_in_some(pis->real_in, dst, sz, err);
}
GuInStream*
gu_in_proxy_stream(GuIn* in, GuPool* pool)
{
return &gu_new_s(
pool, GuProxyInStream,
.stream.begin_buffer = gu_proxy_in_begin_buffer,
.stream.end_buffer = gu_proxy_in_end_buffer,
.stream.input = gu_proxy_in_input,
.real_in = in)->stream;
}
enum {
GU_BUFFERED_IN_BUF_SIZE = 4096
};
typedef struct GuBufferedInStream GuBufferedInStream;
struct GuBufferedInStream {
GuInStream stream;
size_t alloc;
size_t have;
size_t curr;
GuIn* in;
uint8_t buf[];
};
static const uint8_t*
gu_buffered_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
{
GuBufferedInStream* bis =
gu_container(self, GuBufferedInStream, stream);
if (bis->curr == bis->have) {
bis->curr = 0;
bis->have = gu_in_some(bis->in, bis->buf, bis->alloc, err);
if (!gu_ok(err)) return NULL;
}
*sz_out = bis->have - bis->curr;
return &bis->buf[bis->curr];
}
static void
gu_buffered_in_end_buffer(GuInStream* self, size_t consumed, GuExn* err)
{
GuBufferedInStream* bis =
gu_container(self, GuBufferedInStream, stream);
gu_require(consumed < bis->have - bis->curr);
bis->curr += consumed;
}
static size_t
gu_buffered_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
{
GuBufferedInStream* bis =
gu_container(self, GuBufferedInStream, stream);
return gu_in_some(bis->in, dst, sz, err);
}
GuIn*
gu_buffered_in(GuIn* in, size_t buf_sz, GuPool* pool)
{
GuBufferedInStream* bis = gu_new_flex(pool, GuBufferedInStream,
buf, buf_sz);
bis->stream = (GuInStream) {
.begin_buffer = gu_buffered_in_begin_buffer,
.end_buffer = gu_buffered_in_end_buffer,
.input = gu_buffered_in_input
};
bis->have = bis->curr = 0;
bis->alloc = buf_sz;
return gu_new_in(&bis->stream, pool);
}
typedef struct GuDataIn GuDataIn;
struct GuDataIn {
GuInStream stream;
const uint8_t* data;
size_t sz;
};
static const uint8_t*
gu_data_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
{
(void) err;
GuDataIn* di = gu_container(self, GuDataIn, stream);
const uint8_t* buf = di->data;
if (buf) {
*sz_out = di->sz;
di->data = NULL;
di->sz = 0;
}
return buf;
}
GuIn*
gu_data_in(const uint8_t* data, size_t sz, GuPool* pool)
{
GuDataIn* di = gu_new_s(pool, GuDataIn,
.stream.begin_buffer = gu_data_in_begin_buffer,
.data = data,
.sz = sz);
return gu_new_in(&di->stream, pool);
}
extern inline uint8_t
gu_in_u8(GuIn* restrict in, GuExn* err);
extern inline void
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err);
extern inline int
gu_in_peek_u8(GuIn* restrict in);
extern inline void
gu_in_consume(GuIn* restrict in, size_t sz);

146
src/runtime/c/gu/in.h Normal file
View File

@@ -0,0 +1,146 @@
#ifndef GU_IN_H_
#define GU_IN_H_
#include <gu/defs.h>
#include <gu/exn.h>
#include <gu/assert.h>
typedef struct GuInStream GuInStream;
struct GuInStream {
const uint8_t* (*begin_buffer)(GuInStream* self, size_t* sz_out,
GuExn* err);
void (*end_buffer)(GuInStream* self, size_t consumed, GuExn* err);
size_t (*input)(GuInStream* self, uint8_t* buf, size_t max_sz,
GuExn* err);
};
typedef struct GuIn GuIn;
struct GuIn {
const uint8_t* restrict buf_end;
ptrdiff_t buf_curr;
size_t buf_size;
GuInStream* stream;
GuFinalizer fini;
};
GuIn
gu_init_in(GuInStream* stream);
GuIn*
gu_new_in(GuInStream* stream, GuPool* pool);
GuInStream*
gu_in_proxy_stream(GuIn* in, GuPool* pool);
const uint8_t*
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err);
void
gu_in_end_span(GuIn* in, size_t consumed);
size_t
gu_in_some(GuIn* in, uint8_t* buf, size_t max_len, GuExn* err);
inline void
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err)
{
gu_require(sz < PTRDIFF_MAX);
ptrdiff_t curr = in->buf_curr;
ptrdiff_t new_curr = curr + (ptrdiff_t) sz;
if (GU_UNLIKELY(new_curr > 0)) {
extern void gu_in_bytes_(GuIn* in, uint8_t* buf, size_t sz,
GuExn* err);
gu_in_bytes_(in, buf, sz, err);
return;
}
memcpy(buf, &in->buf_end[curr], sz);
in->buf_curr = new_curr;
}
inline int
gu_in_peek_u8(GuIn* restrict in)
{
if (GU_UNLIKELY(in->buf_curr == 0)) {
return -1;
}
return in->buf_end[in->buf_curr];
}
inline void
gu_in_consume(GuIn* restrict in, size_t sz)
{
gu_require((ptrdiff_t) sz + in->buf_curr <= 0);
in->buf_curr += sz;
}
inline uint8_t
gu_in_u8(GuIn* restrict in, GuExn* err)
{
if (GU_UNLIKELY(in->buf_curr == 0)) {
extern uint8_t gu_in_u8_(GuIn* restrict in, GuExn* err);
return gu_in_u8_(in, err);
}
return in->buf_end[in->buf_curr++];
}
int8_t
gu_in_s8(GuIn* in, GuExn* err);
uint16_t
gu_in_u16le(GuIn* in, GuExn* err);
uint16_t
gu_in_u16be(GuIn* in, GuExn* err);
int16_t
gu_in_s16le(GuIn* in, GuExn* err);
int16_t
gu_in_s16be(GuIn* in, GuExn* err);
uint32_t
gu_in_u32le(GuIn* in, GuExn* err);
uint32_t
gu_in_u32be(GuIn* in, GuExn* err);
int32_t
gu_in_s32le(GuIn* in, GuExn* err);
int32_t
gu_in_s32be(GuIn* in, GuExn* err);
uint64_t
gu_in_u64le(GuIn* in, GuExn* err);
uint64_t
gu_in_u64be(GuIn* in, GuExn* err);
int64_t
gu_in_s64le(GuIn* in, GuExn* err);
int64_t
gu_in_s64be(GuIn* in, GuExn* err);
double
gu_in_f64le(GuIn* in, GuExn* err);
double
gu_in_f64be(GuIn* in, GuExn* err);
GuIn*
gu_buffered_in(GuIn* in, size_t sz, GuPool* pool);
GuIn*
gu_data_in(const uint8_t* buf, size_t size, GuPool* pool);
extern GU_DECLARE_TYPE(GuEOF, abstract);
#include <gu/type.h>
#endif // GU_IN_H_

59
src/runtime/c/gu/intern.c Normal file
View File

@@ -0,0 +1,59 @@
#include "intern.h"
struct GuIntern {
GuPool* str_pool;
GuMap* map;
};
GuIntern*
gu_new_intern(GuPool* str_pool, GuPool* pool)
{
GuIntern* intern = gu_new(GuIntern, pool);
intern->str_pool = str_pool;
intern->map = gu_new_set(const char*, gu_str_hasher, pool);
return intern;
}
const char*
gu_intern_str(GuIntern* intern, const char* cstr)
{
const char* const* strp = gu_map_find_key(intern->map, &cstr);
if (strp) {
return *strp;
}
const char* str = gu_strdup(cstr, intern->str_pool);
gu_map_insert(intern->map, &str);
return str;
}
struct GuSymTable {
GuPool* sym_pool;
GuMap* map;
};
GuSymTable*
gu_new_symtable(GuPool* sym_pool, GuPool* pool)
{
GuSymTable* tab = gu_new(GuSymTable, pool);
tab->sym_pool = sym_pool;
tab->map = gu_new_set(GuSymbol, gu_string_hasher, pool);
return tab;
}
GuSymbol
gu_symtable_intern(GuSymTable* tab, GuString string)
{
if (gu_string_is_stable(string)) {
return string;
}
const GuSymbol* symp = gu_map_find_key(tab->map, &string);
if (symp) {
return *symp;
}
GuSymbol sym = gu_string_copy(string, tab->sym_pool);
gu_map_insert(tab->map, &sym);
return sym;
}

24
src/runtime/c/gu/intern.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef GU_INTERN_H_
#define GU_INTERN_H_
#include <gu/map.h>
#include <gu/str.h>
#include <gu/string.h>
typedef struct GuIntern GuIntern;
GuIntern* gu_new_intern(GuPool* str_pool, GuPool* pool);
const char* gu_intern_str(GuIntern* intern, const char* cstr);
typedef struct GuSymTable GuSymTable;
typedef GuString GuSymbol;
GuSymTable*
gu_new_symtable(GuPool* sym_pool, GuPool* pool);
GuSymbol
gu_symtable_intern(GuSymTable* symtab, GuString string);
#endif /* GU_INTERN_H_ */

59
src/runtime/c/gu/list.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gu/list.h>
#include <gu/assert.h>
#include <string.h>
static const int gu_list_empty = 0;
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
int n_elems, size_t alignment)
{
gu_assert(n_elems >= 0);
if (n_elems == 0) {
return (void*) &gu_list_empty;
}
// XXX: use gu_flex_size, use offset of elems
void* p = gu_malloc_aligned(pool, base_size + elem_size * n_elems,
alignment);
*(int*) p = n_elems;
return p;
}
GU_DEFINE_KIND(GuList, abstract);
// GU_DEFINE_TYPE(GuStrs, GuList, gu_type(GuStr));
// GU_DEFINE_TYPE(GuStrsP, pointer, gu_type(GuStrs));
void*
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool)
{
return gu_list_alloc(pool, ltype->size,
gu_type_size(ltype->elem_type),
n_elems, ltype->align);
}
void*
gu_list_type_index(GuListType* ltype, void* list, int i)
{
uint8_t* p = list;
return &p[ltype->elems_offset + i * gu_type_size(ltype->elem_type)];
}

140
src/runtime/c/gu/list.h Normal file
View File

@@ -0,0 +1,140 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file
*
* Lists.
*/
#ifndef GU_LIST_H_
#define GU_LIST_H_
#include <gu/mem.h>
#define GuList(t) \
struct { \
const int len; \
t elems[]; \
}
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
int n_elems, size_t alignment);
#define gu_new_list(t, pool, n) \
((t*) gu_list_alloc(pool, \
sizeof(t), \
sizeof(((t*)NULL)->elems[0]), \
(n), \
gu_flex_alignof(t)))
static inline int
gu_list_length(const void* list)
{
return *(const int*) list;
}
#define gu_list_elems(lst) \
((lst)->elems)
#define gu_list_index(lst, i) \
(gu_list_elems(lst)[i])
typedef GuList(void*) GuPointers;
//typedef GuList(uint8_t) GuBytes;
typedef GuList(int) GuInts;
#define GuListN(t_, len_) \
struct { \
int len; \
t elems[len_]; \
}
#define gu_list_(qual_, t_, ...) \
((qual_ GuList(t_) *) \
((qual_ GuListN(t_, (sizeof((t_[]){__VA_ARGS__}) / sizeof(t_)))[]){ \
__VA_ARGS__ \
}))
#define gu_list(t_, ...) \
gu_list_(, t_, __VA_ARGS__)
#define gu_clist(t_, ...) \
gu_list_(const, t_, __VA_ARGS__)
#define GuSList(t) \
const struct { \
int len; \
t* elems; \
}
#define GU_SLIST_0 { .len = 0, .elems = NULL }
#define GU_SLIST(t, ...) \
{ \
.len = GU_ARRAY_LEN(t,GU_ID({__VA_ARGS__})), \
.elems = ((t[]){__VA_ARGS__}) \
}
#include <gu/type.h>
//
// list
//
typedef const struct GuListType GuListType, GuType_GuList;
struct GuListType {
GuType_abstract abstract_base;
size_t size;
size_t align;
GuType* elem_type;
ptrdiff_t elems_offset;
};
#define GU_TYPE_INIT_GuList(k_, t_, elem_type_) { \
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
.size = sizeof(t_), \
.align = gu_alignof(t_), \
.elem_type = elem_type_, \
.elems_offset = offsetof(t_, elems) \
}
extern GU_DECLARE_KIND(GuList);
void*
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool);
void*
gu_list_type_index(GuListType* ltype, void* list, int i);
#include <gu/str.h>
typedef GuList(GuStr) GuStrs;
typedef GuStrs* GuStrsP;
extern GU_DECLARE_TYPE(GuStrs, GuList);
extern GU_DECLARE_TYPE(GuStrsP, pointer);
#endif // GU_LIST_H_

79
src/runtime/c/gu/log.c Normal file
View File

@@ -0,0 +1,79 @@
#include <gu/defs.h>
#include <gu/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
static int gu_log_depth = 0;
static bool
gu_log_match(const char* pat, size_t patlen, const char* str)
{
if (patlen > 0 && pat[patlen-1] == '*') {
return strncmp(pat, str, patlen-1) == 0;
} else if (strlen(str) == patlen) {
return strncmp(pat, str, patlen) == 0;
}
return false;
}
static bool
gu_log_enabled(const char* func, const char* file)
{
const char* cfg = getenv("GU_LOG");
if (cfg == NULL) {
return false;
}
const char* p = cfg;
while (true) {
size_t len = strcspn(p, ",");
if (gu_log_match(p, len, func)) {
return true;
}
if (gu_log_match(p, len, file)) {
return true;
}
if (p[len] == '\0') {
break;
}
p = &p[len + 1];
}
return false;
}
void
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
const char* fmt, va_list args)
{
(void) (kind && line);
if (!gu_log_enabled(func, file)) {
return;
}
if (kind == GU_LOG_KIND_EXIT) {
gu_log_depth--;
}
if (fmt) {
int indent = gu_min(32 + gu_log_depth, 48);
fprintf(stderr, "%-*s: ", indent, func);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
fflush(stderr);
}
if (kind == GU_LOG_KIND_ENTER) {
gu_log_depth++;
}
}
void
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
gu_log_full_v(kind, func, file, line, fmt, args);
va_end(args);
}

65
src/runtime/c/gu/log.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef GU_LOG_H_
#define GU_LOG_H_
#include <stdarg.h>
typedef enum GuLogKind {
GU_LOG_KIND_ENTER,
GU_LOG_KIND_EXIT,
GU_LOG_KIND_DEBUG,
GU_LOG_KIND_ERROR
} GuLogKind;
void
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
const char* fmt, ...);
void
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
const char* fmt, va_list args);
#ifndef NDEBUG
#define gu_logv(kind_, fmt_, args_) \
gu_log_full_v(kind_, __func__, __FILE__, __LINE__, fmt_, args_)
#define gu_log(kind_, ...) \
gu_log_full(kind_, __func__, __FILE__, __LINE__, __VA_ARGS__)
#else
static inline void
gu_logv(GuLogKind kind, const char* fmt, va_list args)
{
(void) kind;
(void) fmt;
(void) args;
}
static inline void
gu_log(GuLogKind kind, const char* fmt, ...)
{
(void) kind;
(void) fmt;
}
#endif
#define gu_enter(...) \
gu_log(GU_LOG_KIND_ENTER, __VA_ARGS__)
#define gu_exit(...) \
gu_log(GU_LOG_KIND_EXIT, __VA_ARGS__)
#define gu_debug(...) \
gu_log(GU_LOG_KIND_DEBUG, __VA_ARGS__)
#define gu_debugv(kind_, fmt_, args_) \
gu_logv(GU_LOG_KIND_DEBUG, fmt_, args_)
#endif // GU_LOG_H_

353
src/runtime/c/gu/map.c Normal file
View File

@@ -0,0 +1,353 @@
#include <gu/defs.h>
#include <gu/mem.h>
#include <gu/type.h>
#include <gu/map.h>
#include <gu/assert.h>
#include <gu/prime.h>
#include <gu/log.h>
typedef enum {
GU_MAP_GENERIC,
GU_MAP_ADDR,
GU_MAP_WORD
} GuMapKind;
typedef struct GuMapData GuMapData;
struct GuMapData {
uint8_t* keys;
uint8_t* values;
size_t n_occupied;
size_t n_entries;
size_t zero_idx;
};
struct GuMap {
GuMapKind const kind;
GuHasher* const hasher;
size_t const key_size;
size_t const value_size;
const void* default_value;
GuMapData data;
GuFinalizer fin;
};
static void
gu_map_finalize(GuFinalizer* fin)
{
GuMap* map = gu_container(fin, GuMap, fin);
gu_mem_buf_free(map->data.keys);
if (map->value_size) {
gu_mem_buf_free(map->data.values);
}
}
static const GuWord gu_map_empty_key = 0;
static bool
gu_map_buf_is_zero(const uint8_t* p, size_t sz) {
while (sz >= sizeof(GuWord)) {
sz -= sizeof(GuWord);
if (memcmp(&p[sz], &gu_map_empty_key, sizeof(GuWord)) != 0) {
return false;
}
}
return (memcmp(p, &gu_map_empty_key, sz) == 0);
}
static bool
gu_map_entry_is_free(GuMap* map, GuMapData* data, size_t idx)
{
if (idx == data->zero_idx) {
return false;
} else if (map->kind == GU_MAP_ADDR) {
const void* key = ((const void**)data->keys)[idx];
return key == NULL;
} else if (map->kind == GU_MAP_WORD) {
GuWord key = ((GuWord*)data->keys)[idx];
return key == 0;
}
gu_assert(map->kind == GU_MAP_GENERIC);
const void* key = &data->keys[idx * map->key_size];
return gu_map_buf_is_zero(key, map->key_size);
}
static bool
gu_map_lookup(GuMap* map, const void* key, size_t* idx_out)
{
size_t n = map->data.n_entries;
switch (map->kind) {
case GU_MAP_GENERIC: {
GuHasher* hasher = map->hasher;
GuEquality* eq = (GuEquality*) hasher;
GuHash hash = hasher->hash(hasher, key);
size_t idx = hash % n;
size_t offset = (hash % (n - 2)) + 1;
size_t key_size = map->key_size;
while (true) {
void* entry_key = &map->data.keys[idx * key_size];
if (gu_map_buf_is_zero(entry_key, key_size) &&
map->data.zero_idx != idx) {
*idx_out = idx;
return false;
} else if (eq->is_equal(eq, key, entry_key)) {
*idx_out = idx;
return true;
}
idx = (idx + offset) % n;
}
gu_impossible();
break;
}
case GU_MAP_ADDR: {
GuHash hash = (GuHash) key;
size_t idx = hash % n;
size_t offset = (hash % (n - 2)) + 1;
while (true) {
const void* entry_key =
((const void**)map->data.keys)[idx];
if (entry_key == NULL && map->data.zero_idx != idx) {
*idx_out = idx;
return false;
} else if (entry_key == key) {
*idx_out = idx;
return true;
}
idx = (idx + offset) % n;
}
gu_impossible();
break;
}
case GU_MAP_WORD: {
GuWord w = *(const GuWord*)key;
GuHash hash = (GuHash) w;
size_t idx = hash % n;
size_t offset = (hash % (n - 2)) + 1;
while (true) {
GuWord entry_key = ((GuWord*)map->data.keys)[idx];
if (entry_key == 0 && map->data.zero_idx != idx) {
*idx_out = idx;
return false;
} else if (entry_key == w) {
*idx_out = idx;
return true;
}
idx = (idx + offset) % n;
}
gu_impossible();
break;
}
default:
gu_impossible();
}
gu_impossible();
return false;
}
static void
gu_map_resize(GuMap* map)
{
GuMapData* data = &map->data;
GuMapData old_data = *data;
size_t req_entries =
gu_twin_prime_sup(GU_MAX(11, map->data.n_occupied * 4 / 3 + 1));
size_t key_size = map->key_size;
size_t key_alloc = 0;
data->keys = gu_mem_buf_alloc(req_entries * key_size, &key_alloc);
size_t value_size = map->value_size;
size_t value_alloc = 0;
if (value_size) {
data->values = gu_mem_buf_alloc(req_entries * value_size,
&value_alloc);
memset(data->values, 0, value_alloc);
}
data->n_entries = gu_twin_prime_inf(value_size ?
GU_MIN(key_alloc / key_size,
value_alloc / value_size)
: key_alloc / key_size);
switch (map->kind) {
case GU_MAP_GENERIC:
case GU_MAP_WORD:
memset(data->keys, 0, key_alloc);
break;
case GU_MAP_ADDR:
for (size_t i = 0; i < data->n_entries; i++) {
((const void**)data->keys)[i] = NULL;
}
break;
default:
gu_impossible();
}
gu_assert(data->n_entries > data->n_occupied);
gu_debug("Resized to %d entries", data->n_entries);
data->n_occupied = 0;
data->zero_idx = SIZE_MAX;
for (size_t i = 0; i < old_data.n_entries; i++) {
if (gu_map_entry_is_free(map, &old_data, i)) {
continue;
}
void* old_key = &old_data.keys[i * key_size];
if (map->kind == GU_MAP_ADDR) {
old_key = *(void**)old_key;
}
void* old_value = &old_data.values[i * value_size];
memcpy(gu_map_insert(map, old_key),
old_value, map->value_size);
}
gu_mem_buf_free(old_data.keys);
if (value_size) {
gu_mem_buf_free(old_data.values);
}
}
static bool
gu_map_maybe_resize(GuMap* map)
{
if (map->data.n_entries <=
map->data.n_occupied + (map->data.n_occupied / 4)) {
gu_map_resize(map);
return true;
}
return false;
}
void*
gu_map_find(GuMap* map, const void* key)
{
size_t idx;
bool found = gu_map_lookup(map, key, &idx);
if (found) {
return &map->data.values[idx * map->value_size];
}
return NULL;
}
const void*
gu_map_find_default(GuMap* map, const void* key)
{
void* p = gu_map_find(map, key);
return p ? p : map->default_value;
}
const void*
gu_map_find_key(GuMap* map, const void* key)
{
size_t idx;
bool found = gu_map_lookup(map, key, &idx);
if (found) {
return &map->data.keys[idx * map->key_size];
}
return NULL;
}
void*
gu_map_insert(GuMap* map, const void* key)
{
size_t idx;
bool found = gu_map_lookup(map, key, &idx);
if (!found) {
if (gu_map_maybe_resize(map)) {
found = gu_map_lookup(map, key, &idx);
gu_assert(!found);
}
if (map->kind == GU_MAP_ADDR) {
((const void**)map->data.keys)[idx] = key;
} else {
memcpy(&map->data.keys[idx * map->key_size],
key, map->key_size);
}
if (map->default_value) {
memcpy(&map->data.values[idx * map->value_size],
map->default_value, map->value_size);
}
if (gu_map_entry_is_free(map, &map->data, idx)) {
gu_assert(map->data.zero_idx == SIZE_MAX);
map->data.zero_idx = idx;
}
map->data.n_occupied++;
}
return &map->data.values[idx * map->value_size];
}
void
gu_map_iter(GuMap* map, GuMapItor* itor, GuExn* err)
{
for (size_t i = 0; i < map->data.n_entries && gu_ok(err); i++) {
if (gu_map_entry_is_free(map, &map->data, i)) {
continue;
}
const void* key = &map->data.keys[i * map->key_size];
void* value = &map->data.values[i * map->value_size];
if (map->kind == GU_MAP_ADDR) {
key = *(const void* const*) key;
}
itor->fn(itor, key, value, err);
}
}
static const uint8_t gu_map_no_values[1] = { 0 };
GuMap*
gu_make_map(size_t key_size, GuHasher* hasher,
size_t value_size, const void* default_value,
GuPool* pool)
{
GuMapKind kind =
((!hasher || hasher == gu_addr_hasher)
? GU_MAP_ADDR
: (key_size == sizeof(GuWord) && hasher == gu_word_hasher)
? GU_MAP_WORD
: GU_MAP_GENERIC);
if (kind == GU_MAP_ADDR) {
key_size = sizeof(GuWord);
}
GuMapData data = {
.n_occupied = 0,
.n_entries = 0,
.keys = NULL,
.values = value_size ? NULL : (uint8_t*) gu_map_no_values,
.zero_idx = SIZE_MAX
};
GuMap* map = gu_new_i(
pool, GuMap,
.default_value = default_value,
.hasher = hasher,
.data = data,
.key_size = key_size,
.value_size = value_size,
.fin.fn = gu_map_finalize,
.kind = kind
);
gu_pool_finally(pool, &map->fin);
gu_map_resize(map);
return map;
}
GuMap*
gu_map_type_make(GuMapType* mtype, GuPool* pool)
{
size_t key_size = 0;
if (mtype->hasher && mtype->hasher != gu_addr_hasher) {
key_size = gu_type_size(mtype->key_type);
}
size_t value_size = gu_type_size(mtype->value_type);
return gu_make_map(key_size, mtype->hasher,
value_size, mtype->default_value, pool);
}
GU_DEFINE_KIND(GuMap, abstract);
// GU_DEFINE_KIND(GuIntMap, GuMap);

121
src/runtime/c/gu/map.h Normal file
View File

@@ -0,0 +1,121 @@
#ifndef GU_MAP_H_
#define GU_MAP_H_
#include <gu/hash.h>
#include <gu/mem.h>
#include <gu/exn.h>
typedef const struct GuMapItor GuMapItor;
struct GuMapItor {
void (*fn)(GuMapItor* self, const void* key, void* value,
GuExn *err);
};
typedef struct GuMap GuMap;
GuMap*
gu_make_map(size_t key_size, GuHasher* hasher,
size_t value_size, const void* default_value,
GuPool* pool);
#define gu_new_map(K, HASHER, V, DV, POOL) \
(gu_make_map(sizeof(K), (HASHER), sizeof(V), (DV), (POOL)))
#define gu_new_set(K, HASHER, POOL) \
(gu_make_map(sizeof(K), (HASHER), 0, NULL, (POOL)))
#define gu_new_addr_map(K, V, DV, POOL) \
(gu_make_map(0, NULL, sizeof(V), (DV), (POOL)))
size_t
gu_map_count(GuMap* map);
void*
gu_map_find_full(GuMap* ht, void* key_inout);
const void*
gu_map_find_default(GuMap* ht, const void* key);
#define gu_map_get(MAP, KEYP, V) \
(*(V*)gu_map_find_default((MAP), (KEYP)))
void*
gu_map_find(GuMap* ht, const void* key);
#define gu_map_set(MAP, KEYP, V, VAL) \
GU_BEGIN \
V* gu_map_set_p_ = gu_map_find((MAP), (KEYP)); \
*gu_map_set_p_ = (VAL); \
GU_END
const void*
gu_map_find_key(GuMap* ht, const void* key);
static inline bool
gu_map_has(GuMap* ht, const void* key)
{
return gu_map_find_key(ht, key) != NULL;
}
void*
gu_map_insert(GuMap* ht, const void* key);
#define gu_map_put(MAP, KEYP, V, VAL) \
GU_BEGIN \
V* gu_map_put_p_ = gu_map_insert((MAP), (KEYP)); \
*gu_map_put_p_ = (VAL); \
GU_END
void
gu_map_iter(GuMap* ht, GuMapItor* itor, GuExn* err);
typedef GuMap GuIntMap;
#define gu_new_int_map(VAL_T, DEFAULT, POOL) \
gu_new_map(int, gu_int_hasher, VAL_T, DEFAULT, POOL)
#if defined(GU_TYPE_H_) && !defined(GU_MAP_H_TYPE_)
#define GU_MAP_H_TYPE_
extern GU_DECLARE_KIND(GuMap);
typedef const struct GuMapType GuMapType, GuType_GuMap;
struct GuMapType {
GuType_abstract abstract_base;
GuHasher* hasher;
GuType* key_type;
GuType* value_type;
const void* default_value;
};
GuMap*
gu_map_type_make(GuMapType* mtype, GuPool* pool);
#define gu_map_type_new(MAP_T, POOL) \
gu_map_type_make(gu_type_cast(gu_type(MAP_T), GuMap), (POOL))
#define GU_TYPE_INIT_GuMap(k_, t_, kt_, h_, vt_, dv_) \
{ \
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
.hasher = h_, \
.key_type = kt_, \
.value_type = vt_, \
.default_value = dv_ \
}
#define gu_type__GuIntMap gu_type__GuMap
typedef GuType_GuMap GuType_GuIntMap;
#define GU_TYPE_INIT_GuIntMap(KIND, MAP_T, VAL_T, DEFAULT) \
GU_TYPE_INIT_GuMap(KIND, MAP_T, gu_type(int), gu_int_hasher, \
VAL_T, DEFAULT)
#endif
#endif // GU_MAP_H_

346
src/runtime/c/gu/mem.c Normal file
View File

@@ -0,0 +1,346 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gu/mem.h>
#include <gu/fun.h>
#include <gu/bits.h>
#include <gu/assert.h>
#include <gu/log.h>
#include <string.h>
#include <stdlib.h>
#ifdef USE_VALGRIND
#include <valgrind/valgrind.h>
#define VG(X) X
#else
#define VG(X) GU_NOP
#endif
static const size_t
// Maximum request size for a chunk. The actual maximum chunk size
// may be somewhat larger.
gu_mem_chunk_max_size = 1024 * sizeof(void*),
// number of bytes to allocate in the pool when it is created
gu_mem_pool_initial_size = 24 * sizeof(void*),
// Pool allocations larger than this will get their own chunk if
// there's no room in the current one. Allocations smaller than this may trigger
// the creation of a new chunk, in which case the remaining space in
// the current chunk is left unused (internal fragmentation).
gu_mem_max_shared_alloc = 64 * sizeof(void*),
// Should not be smaller than the granularity for malloc
gu_mem_unit_size = 2 * sizeof(void*),
/* Malloc tuning: the additional memory used by malloc next to the
allocated object */
gu_malloc_overhead = sizeof(size_t);
static void*
gu_mem_realloc(void* p, size_t size)
{
void* buf = realloc(p, size);
if (size != 0 && buf == NULL) {
gu_fatal("Memory allocation failed");
}
gu_debug("%p %zu -> %p", p, size, buf); // strictly illegal
return buf;
}
static void*
gu_mem_alloc(size_t size)
{
void* buf = malloc(size);
if (buf == NULL) {
gu_fatal("Memory allocation failed");
}
gu_debug("%zu -> %p", size, buf);
return buf;
}
static void
gu_mem_free(void* p)
{
gu_debug("%p", p);
free(p);
}
static size_t
gu_mem_padovan(size_t min)
{
// This could in principle be done faster with Q-matrices for
// Padovan numbers, but not really worth it for our commonly
// small numbers.
if (min <= 5) {
return min;
}
size_t a = 7, b = 9, c = 12;
while (min > a) {
if (b < a) {
// overflow
return SIZE_MAX;
}
size_t tmp = a + b;
a = b;
b = c;
c = tmp;
}
return a;
}
void*
gu_mem_buf_realloc(void* old_buf, size_t min_size, size_t* real_size_out)
{
size_t min_blocks = ((min_size + gu_malloc_overhead - 1) /
gu_mem_unit_size) + 1;
size_t blocks = gu_mem_padovan(min_blocks);
size_t size = blocks * gu_mem_unit_size - gu_malloc_overhead;
void* buf = gu_mem_realloc(old_buf, size);
*real_size_out = buf ? size : 0;
return buf;
}
void*
gu_mem_buf_alloc(size_t min_size, size_t* real_size_out)
{
return gu_mem_buf_realloc(NULL, min_size, real_size_out);
}
void
gu_mem_buf_free(void* buf)
{
gu_mem_free(buf);
}
typedef struct GuMemChunk GuMemChunk;
struct GuMemChunk {
GuMemChunk* next;
uint8_t data[];
};
typedef struct GuFinalizerNode GuFinalizerNode;
struct GuFinalizerNode {
GuFinalizerNode* next;
GuFinalizer* fin;
};
enum GuPoolFlags {
GU_POOL_LOCAL = 1 << 0
};
struct GuPool {
uint8_t* curr_buf; // actually GuMemChunk*
GuMemChunk* chunks;
GuFinalizerNode* finalizers;
uint16_t flags;
uint16_t left_edge;
uint16_t right_edge;
uint16_t curr_size;
uint8_t init_buf[];
};
static GuPool*
gu_init_pool(uint8_t* buf, size_t sz)
{
gu_require(gu_aligned((uintptr_t) (void*) buf, gu_alignof(GuPool)));
gu_require(sz >= sizeof(GuPool));
GuPool* pool = (GuPool*) buf;
pool->flags = 0;
pool->curr_size = sz;
pool->curr_buf = (uint8_t*) pool;
pool->chunks = NULL;
pool->finalizers = NULL;
pool->left_edge = offsetof(GuPool, init_buf);
pool->right_edge = sz;
VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
return pool;
}
GuPool*
gu_local_pool_(uint8_t* buf, size_t sz)
{
GuPool* pool = gu_init_pool(buf, sz);
pool->flags |= GU_POOL_LOCAL;
gu_debug("%p", pool);
return pool;
}
GuPool*
gu_new_pool(void)
{
size_t sz = GU_FLEX_SIZE(GuPool, init_buf, gu_mem_pool_initial_size);
uint8_t* buf = gu_mem_buf_alloc(sz, &sz);
GuPool* pool = gu_init_pool(buf, sz);
gu_debug("%p", pool);
return pool;
}
static void
gu_pool_expand(GuPool* pool, size_t req)
{
size_t real_req = GU_MAX(req, GU_MIN(((size_t)pool->curr_size) + 1,
gu_mem_chunk_max_size));
gu_assert(real_req >= sizeof(GuMemChunk));
size_t size = 0;
GuMemChunk* chunk = gu_mem_buf_alloc(real_req, &size);
chunk->next = pool->chunks;
pool->chunks = chunk;
pool->curr_buf = (uint8_t*) chunk;
pool->left_edge = offsetof(GuMemChunk, data);
pool->right_edge = pool->curr_size = size;
// size should always fit in uint16_t
gu_assert((size_t) pool->right_edge == size);
}
static size_t
gu_mem_advance(size_t old_pos, size_t pre_align, size_t pre_size,
size_t align, size_t size)
{
size_t p = gu_align_forward(old_pos, pre_align);
p += pre_size;
p = gu_align_forward(p, align);
p += size;
return p;
}
static void*
gu_pool_malloc_aligned(GuPool* pool, size_t pre_align, size_t pre_size,
size_t align, size_t size)
{
gu_require(size <= gu_mem_max_shared_alloc);
size_t pos = gu_mem_advance(pool->left_edge, pre_align, pre_size,
align, size);
if (pos > (size_t) pool->right_edge) {
pos = gu_mem_advance(offsetof(GuMemChunk, data),
pre_align, pre_size, align, size);
gu_pool_expand(pool, pos);
gu_assert(pos <= pool->right_edge);
}
pool->left_edge = pos;
uint8_t* addr = &pool->curr_buf[pos - size];
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size, size + pre_size ));
return addr;
}
static size_t
gu_pool_avail(GuPool* pool)
{
return (size_t) pool->right_edge - (size_t) pool->left_edge;
}
void*
gu_pool_malloc_unaligned(GuPool* pool, size_t size)
{
if (size > gu_pool_avail(pool)) {
gu_pool_expand(pool, offsetof(GuMemChunk, data) + size);
gu_assert(size <= gu_pool_avail(pool));
}
pool->right_edge -= size;
void* addr = &pool->curr_buf[pool->right_edge];
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr, size));
return addr;
}
void*
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
size_t align, size_t size)
{
gu_enter("-> %p %zu %zu %zu %zu",
pool, pre_align, pre_size, align, size);
void* ret = NULL;
if (pre_align == 0) {
pre_align = gu_alignof(GuMaxAlign);
}
if (align == 0) {
align = gu_alignof(GuMaxAlign);
}
size_t full_size = gu_mem_advance(offsetof(GuMemChunk, data),
pre_align, pre_size, align, size);
if (full_size > gu_mem_max_shared_alloc) {
GuMemChunk* chunk = gu_mem_alloc(full_size);
chunk->next = pool->chunks;
pool->chunks = chunk;
uint8_t* addr = &chunk->data[full_size - size
- offsetof(GuMemChunk, data)];
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size,
pre_size + size));
ret = addr;
} else if (pre_align == 1 && align == 1) {
uint8_t* buf = gu_pool_malloc_unaligned(pool, pre_size + size);
ret = &buf[pre_size];
} else {
ret = gu_pool_malloc_aligned(pool, pre_align, pre_size,
align, size);
}
gu_exit("<- %p", ret);
return ret;
}
void*
gu_malloc_aligned(GuPool* pool, size_t size, size_t align)
{
if (align == 0) {
align = GU_MIN(size, gu_alignof(GuMaxAlign));
}
void* ret = gu_malloc_prefixed(pool, 1, 0, align, size);
return ret;
}
void
gu_pool_finally(GuPool* pool, GuFinalizer* finalizer)
{
GuFinalizerNode* node = gu_new(GuFinalizerNode, pool);
node->next = pool->finalizers;
node->fin = finalizer;
pool->finalizers = node;
}
void
gu_pool_free(GuPool* pool)
{
gu_debug("%p", pool);
GuFinalizerNode* node = pool->finalizers;
while (node) {
GuFinalizerNode* next = node->next;
node->fin->fn(node->fin);
node = next;
}
GuMemChunk* chunk = pool->chunks;
while (chunk) {
GuMemChunk* next = chunk->next;
gu_mem_buf_free(chunk);
chunk = next;
}
VG(VALGRIND_DESTROY_MEMPOOL(pool));
if (!pool->flags & GU_POOL_LOCAL) {
gu_mem_buf_free(pool);
}
}
extern inline void* gu_malloc(GuPool* pool, size_t size);
extern inline void*
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
const void* init);

276
src/runtime/c/gu/mem.h Normal file
View File

@@ -0,0 +1,276 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file
*
* Memory allocation tools.
*/
#ifndef GU_MEM_H_
#define GU_MEM_H_
#include <gu/defs.h>
#include <gu/fun.h>
/** @defgroup GuPool Memory pools */
//@{
/// A memory pool.
typedef struct GuPool GuPool;
/// @name Creating a pool
//@{
/// Create a new memory pool.
GU_ONLY GuPool*
gu_new_pool(void);
/**<
* @return A new memory pool.
*/
//@private
GuPool*
gu_local_pool_(uint8_t* init_buf, size_t sz);
//@private
#define GU_LOCAL_POOL_INIT_SIZE (16 * sizeof(GuWord))
/// Create a stack-allocated memory pool.
#define gu_local_pool() \
gu_local_pool_(gu_alloca(GU_LOCAL_POOL_INIT_SIZE), \
GU_LOCAL_POOL_INIT_SIZE)
/**<
* @return A memory pool whose first chunk is allocated directly from
* the stack. This makes its creation faster, and more suitable for
* functions that usually allocate only a little memory from the pool
* until it is freed.
*
* @note The pool created with #gu_local_pool \e must be freed with
* #gu_pool_free before the end of the block where #gu_local_pool was
* called.
*
* @note Because #gu_local_pool uses relatively much stack space, it
* should not be used in the bodies of recursive functions.
*/
//@}
/// @name Destroying a pool
//@{
/// Free a memory pool and all objects allocated from it.
void
gu_pool_free(GU_ONLY GuPool* pool);
/**<
* When the pool is freed, all finalizers registered by
* #gu_pool_finally on \p pool are invoked in reverse order of
* registration.
*
* @note After the pool is freed, all objects allocated from it become
* invalid and may no longer be used. */
//@}
/// @name Allocating from a pool
//@{
/// Allocate memory with a specified alignment.
void*
gu_malloc_aligned(GuPool* pool, size_t size, size_t alignment);
void*
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
size_t align, size_t size);
/// Allocate memory from a pool.
inline void*
gu_malloc(GuPool* pool, size_t size) {
return gu_malloc_aligned(pool, size, 0);
}
#include <string.h>
//@private
static inline void*
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
const void* init)
{
void* p = gu_malloc_aligned(pool, size, alignment);
memcpy(p, init, size);
return p;
}
//@private
static inline void*
gu_malloc_init(GuPool* pool, size_t size, const void* init)
{
return gu_malloc_init_aligned(pool, size, 0, init);
}
/** Allocate memory to store an array of objects of a given type. */
#define gu_new_n(type, n, pool) \
((type*)gu_malloc_aligned((pool), \
sizeof(type) * (n), \
gu_alignof(type)))
/**<
* @param type The C type of the objects to allocate.
*
* @param n The number of objects to allocate.
*
* @param pool The memory pool to allocate from.
*
* @return A pointer to a heap-allocated array of \p n uninitialized
* objects of type \p type.
*/
/** Allocate memory to store an object of a given type. */
#define gu_new(type, pool) \
gu_new_n(type, 1, pool)
/**<
* @param type The C type of the object to allocate.
*
* @param pool The memory pool to allocate from.
*
* @return A pointer to a heap-allocated uninitialized object of type
* \p type.
*/
#define gu_new_prefixed(pre_type, type, pool) \
((type*)(gu_malloc_prefixed((pool), \
gu_alignof(pre_type), sizeof(pre_type), \
gu_alignof(type), sizeof(type))))
#ifdef GU_HAVE_STATEMENT_EXPRESSIONS
#define gu_new_i(pool, type, ...) \
({ \
type *gu_new_p_ = gu_new(type, pool); \
memcpy((void*) gu_new_p_, &(type){ __VA_ARGS__ }, \
sizeof(type)); \
gu_new_p_; \
})
#else // GU_HAVE_STATEMENT_EXPRESSIONS
#define gu_new_i(pool, type, ...) \
((type*)gu_malloc_init_aligned((pool), sizeof(type), \
gu_alignof(type), \
&(type){ __VA_ARGS__ }))
#endif // GU_HAVE_STATEMENT_EXPRESSIONS
/** @def gu_new_i(pool, type, ...)
*
* Allocate and initialize an object.
*
* @param pool The pool to allocate from.
*
* @param type The C type of the object to allocate.
*
* @param ... An initializer list for the object to allocate.
*/
#define gu_new_s gu_new_i
// Alas, there's no portable way to get the alignment of flex structs.
#define gu_new_flex(pool_, type_, flex_member_, n_elems_) \
((type_ *)gu_malloc_aligned( \
(pool_), \
GU_FLEX_SIZE(type_, flex_member_, n_elems_), \
gu_flex_alignof(type_)))
//@}
/// @name Finalizers
//@{
typedef struct GuFinalizer GuFinalizer;
struct GuFinalizer {
void (*fn)(GuFinalizer* self);
///< @param self A pointer to this finalizer.
};
/// Register a finalizer.
void gu_pool_finally(GuPool* pool, GuFinalizer* fini);
/**< Register \p fini to be called when \p pool is destroyed. The
* finalizers are called in reverse order of registration.
*/
//@}
//@}
/** @defgroup GuMemBuf Memory buffers
*
* Resizable blocks of heap-allocated memory. These operations differ
* from standard \c malloc, \c realloc and \c free -functions in that
* memory buffers are not allocated by exact size. Instead, a minimum
* size is requested, and the returned buffer may be larger. This
* gives the memory allocator more flexibility when the client code
* can make use of larger buffers than requested.
* */
//@{
/// Allocate a new memory buffer.
GU_ONLY void*
gu_mem_buf_alloc(size_t min_size, size_t* real_size);
/**<
* @param min_size The minimum acceptable size for a returned memory block.
*
* @param[out] real_size The actual size of the returned memory
* block. This is never less than \p min_size.
*
* @return A pointer to the memory buffer.
*/
/// Allocate a new memory buffer to replace an old one.
GU_ONLY void*
gu_mem_buf_realloc(
GU_NULL GU_ONLY GU_RETURNED
void* buf,
size_t min_size,
size_t* real_size_out);
/// Free a memory buffer.
void
gu_mem_buf_free(GU_ONLY void* buf);
//@}
#endif // GU_MEM_H_

302
src/runtime/c/gu/out.c Normal file
View File

@@ -0,0 +1,302 @@
#include <gu/seq.h>
#include <gu/out.h>
GuOut
gu_init_out(GuOutStream* stream)
{
gu_require(stream != NULL);
GuOut out = {
.buf_end = NULL,
.buf_curr = 0,
.stream = stream,
.fini.fn = NULL
};
return out;
}
static bool
gu_out_is_buffering(GuOut* out)
{
return !!out->buf_end;
}
static void
gu_out_end_buf(GuOut* out, GuExn* err)
{
if (!gu_out_is_buffering(out)) {
return;
}
GuOutStream* stream = out->stream;
size_t curr_len = ((ptrdiff_t)out->buf_size) + out->buf_curr;
stream->end_buf(stream, curr_len, err);
out->buf_end = NULL;
out->buf_size = out->buf_curr = 0;
}
static bool
gu_out_begin_buf(GuOut* out, size_t req, GuExn* err)
{
GuOutStream* stream = out->stream;
if (gu_out_is_buffering(out)) {
if (out->buf_curr < 0) {
return true;
} else {
gu_out_end_buf(out, err);
if (!gu_ok(err)) {
return false;
}
}
}
if (stream->begin_buf) {
size_t sz = 0;
uint8_t* buf = stream->begin_buf(stream, req, &sz, err);
gu_assert(sz <= PTRDIFF_MAX);
if (buf) {
out->buf_end = &buf[sz];
out->buf_curr = -(ptrdiff_t) sz;
out->buf_size = sz;
return true;
}
}
return false;
}
static void
gu_out_fini(GuFinalizer* self)
{
GuOut* out = gu_container(self, GuOut, fini);
if (gu_out_is_buffering(out)) {
GuPool* pool = gu_local_pool();
GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
gu_out_end_buf(out, err);
gu_pool_free(pool);
}
}
GuOut*
gu_new_out(GuOutStream* stream, GuPool* pool)
{
GuOut* out = gu_new(GuOut, pool);
*out = gu_init_out(stream);
out->fini.fn = gu_out_fini;
gu_pool_finally(pool, &out->fini);
return out;
}
extern inline bool
gu_out_try_buf_(GuOut* out, const uint8_t* src, size_t len);
extern inline size_t
gu_out_bytes(GuOut* out, const uint8_t* buf, size_t len, GuExn* err);
static size_t
gu_out_output(GuOut* out, const uint8_t* src, size_t len, GuExn* err)
{
gu_out_end_buf(out, err);
if (!gu_ok(err)) {
return 0;
}
return out->stream->output(out->stream, src, len, err);
}
void
gu_out_flush(GuOut* out, GuExn* err)
{
GuOutStream* stream = out->stream;
if (out->buf_end) {
gu_out_end_buf(out, err);
if (!gu_ok(err)) {
return;
}
}
if (stream->flush) {
stream->flush(stream, err);
}
}
uint8_t*
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err)
{
if (!out->buf_end && !gu_out_begin_buf(out, req, err)) {
return NULL;
}
*sz_out = -out->buf_curr;
return &out->buf_end[out->buf_curr];
}
void
gu_out_end_span(GuOut* out, size_t sz)
{
ptrdiff_t new_curr = (ptrdiff_t) sz + out->buf_curr;
gu_require(new_curr <= 0);
out->buf_curr = new_curr;
}
size_t
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src, size_t len,
GuExn* err)
{
if (!gu_ok(err)) {
return 0;
} else if (gu_out_try_buf_(out, src, len)) {
return len;
}
if (gu_out_begin_buf(out, len, err)) {
if (gu_out_try_buf_(out, src, len)) {
return len;
}
}
return gu_out_output(out, src, len, err);
}
void gu_out_u8_(GuOut* restrict out, uint8_t u, GuExn* err)
{
if (gu_out_begin_buf(out, 1, err)) {
if (gu_out_try_u8_(out, u)) {
return;
}
}
gu_out_output(out, &u, 1, err);
}
extern inline void
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err);
extern inline void
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err);
extern inline bool
gu_out_is_buffered(GuOut* out);
extern inline bool
gu_out_try_u8_(GuOut* restrict out, uint8_t u);
typedef struct GuProxyOutStream GuProxyOutStream;
struct GuProxyOutStream {
GuOutStream stream;
GuOut* real_out;
};
static uint8_t*
gu_proxy_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
GuExn* err)
{
GuProxyOutStream* pos =
gu_container(self, GuProxyOutStream, stream);
return gu_out_begin_span(pos->real_out, req, sz_out, err);
}
static void
gu_proxy_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
{
GuProxyOutStream* pos =
gu_container(self, GuProxyOutStream, stream);
gu_out_end_span(pos->real_out, sz);
}
static size_t
gu_proxy_out_output(GuOutStream* self, const uint8_t* src, size_t sz,
GuExn* err)
{
GuProxyOutStream* pos =
gu_container(self, GuProxyOutStream, stream);
return gu_out_bytes(pos->real_out, src, sz, err);
}
static void
gu_proxy_out_flush(GuOutStream* self, GuExn* err)
{
GuProxyOutStream* pos =
gu_container(self, GuProxyOutStream, stream);
gu_out_flush(pos->real_out, err);
}
GuOutStream*
gu_out_proxy_stream(GuOut* out, GuPool* pool)
{
return &gu_new_s(pool, GuProxyOutStream,
.stream.begin_buf = gu_proxy_out_buf_begin,
.stream.end_buf = gu_proxy_out_buf_end,
.stream.output = gu_proxy_out_output,
.stream.flush = gu_proxy_out_flush,
.real_out = out)->stream;
}
typedef struct GuBufferedOutStream GuBufferedOutStream;
struct GuBufferedOutStream {
GuProxyOutStream pstream;
size_t sz;
uint8_t buf[];
};
static uint8_t*
gu_buffered_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
GuExn* err)
{
(void) (req && err);
GuBufferedOutStream* b =
gu_container(self, GuBufferedOutStream, pstream.stream);
*sz_out = b->sz;
return b->buf;
}
static void
gu_buffered_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
{
GuBufferedOutStream* b =
gu_container(self, GuBufferedOutStream, pstream.stream);
gu_require(sz <= b->sz);
gu_out_bytes(b->pstream.real_out, b->buf, sz, err);
}
GuOut*
gu_new_buffered_out(GuOut* out, size_t sz, GuPool* pool)
{
GuBufferedOutStream* b =
gu_new_flex(pool, GuBufferedOutStream, buf, sz);
b->pstream.stream = (GuOutStream) {
.begin_buf = gu_buffered_out_buf_begin,
.end_buf = gu_buffered_out_buf_end,
.output = gu_proxy_out_output,
.flush = gu_proxy_out_flush
};
b->pstream.real_out = out;
b->sz = sz;
return gu_new_out(&b->pstream.stream, pool);
}
GuOut*
gu_out_buffered(GuOut* out, GuPool* pool)
{
if (gu_out_is_buffered(out)) {
return out;
}
return gu_new_buffered_out(out, 4096, pool);
}

166
src/runtime/c/gu/out.h Normal file
View File

@@ -0,0 +1,166 @@
#ifndef GU_OUT_H_
#define GU_OUT_H_
#include <gu/defs.h>
#include <gu/assert.h>
#include <gu/exn.h>
typedef struct GuOut GuOut;
typedef struct GuOutStream GuOutStream;
struct GuOutStream {
uint8_t* (*begin_buf)(GuOutStream* self, size_t req, size_t* sz_out,
GuExn* err);
void (*end_buf)(GuOutStream* self, size_t span, GuExn* err);
size_t (*output)(GuOutStream* self, const uint8_t* buf, size_t size,
GuExn* err);
void (*flush)(GuOutStream* self, GuExn* err);
};
struct GuOut {
uint8_t* restrict buf_end;
ptrdiff_t buf_curr;
size_t buf_size;
GuOutStream* stream;
GuFinalizer fini;
};
GuOut
gu_init_out(GuOutStream* stream);
GuOut*
gu_new_out(GuOutStream* stream, GuPool* pool);
inline bool
gu_out_is_buffered(GuOut* out)
{
return !!out->stream->begin_buf;
}
GuOutStream*
gu_out_proxy_stream(GuOut* out, GuPool* pool);
GuOut*
gu_new_buffered_out(GuOut* out, size_t buf_sz, GuPool* pool);
GuOut*
gu_out_buffered(GuOut* out, GuPool* pool);
uint8_t*
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err);
uint8_t*
gu_out_force_span(GuOut* out, size_t min, size_t max, size_t* sz_out,
GuExn* err);
void
gu_out_end_span(GuOut* out, size_t sz);
size_t
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src,
size_t len, GuExn* err);
inline bool
gu_out_try_buf_(GuOut* restrict out, const uint8_t* restrict src, size_t len)
{
gu_require(len <= PTRDIFF_MAX);
ptrdiff_t curr = out->buf_curr;
ptrdiff_t new_curr = curr + (ptrdiff_t) len;
if (GU_UNLIKELY(new_curr > 0)) {
return false;
}
memcpy(&out->buf_end[curr], src, len);
out->buf_curr = new_curr;
return true;
}
inline size_t
gu_out_bytes(GuOut* restrict out, const uint8_t* restrict src, size_t len,
GuExn* err)
{
if (GU_LIKELY(gu_out_try_buf_(out, src, len))) {
return len;
}
return gu_out_bytes_(out, src, len, err);
}
void
gu_out_flush(GuOut* out, GuExn* err);
inline bool
gu_out_try_u8_(GuOut* restrict out, uint8_t u)
{
ptrdiff_t curr = out->buf_curr;
ptrdiff_t new_curr = curr + 1;
if (GU_UNLIKELY(new_curr > 0)) {
return false;
}
out->buf_end[curr] = u;
out->buf_curr = new_curr;
return true;
}
inline void
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err)
{
if (GU_UNLIKELY(!gu_out_try_u8_(out, u))) {
extern void gu_out_u8_(GuOut* restrict out, uint8_t u,
GuExn* err);
gu_out_u8_(out, u, err);
}
}
inline void
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err)
{
gu_out_u8(out, (uint8_t) i, err);
}
void
gu_out_u16le(GuOut* out, uint16_t u, GuExn* err);
void
gu_out_u16be(GuOut* out, uint16_t u, GuExn* err);
void
gu_out_s16le(GuOut* out, int16_t u, GuExn* err);
void
gu_out_s16be(GuOut* out, int16_t u, GuExn* err);
void
gu_out_u32le(GuOut* out, uint32_t u, GuExn* err);
void
gu_out_u32be(GuOut* out, uint32_t u, GuExn* err);
void
gu_out_s32le(GuOut* out, int32_t u, GuExn* err);
void
gu_out_s32be(GuOut* out, int32_t u, GuExn* err);
void
gu_out_u64le(GuOut* out, uint64_t u, GuExn* err);
void
gu_out_u64be(GuOut* out, uint64_t u, GuExn* err);
void
gu_out_s64le(GuOut* out, int64_t u, GuExn* err);
void
gu_out_s64be(GuOut* out, int64_t u, GuExn* err);
void
gu_out_f64le(GuOut* out, double d, GuExn* err);
void
gu_out_f64be(GuOut* out, double d, GuExn* err);
#endif // GU_OUT_H_

154
src/runtime/c/gu/prime.c Normal file
View File

@@ -0,0 +1,154 @@
#include <gu/defs.h>
#include <gu/assert.h>
static const uint32_t gu_prime_wheel_mask = 0UL
| 1 << 1
| 1 << 7
| 1 << 11
| 1 << 13
| 1 << 17
| 1 << 19
| 1 << 23
| 1 << 29;
static bool
gu_prime_wheel(int i)
{
gu_assert(i >= 0 && i < 30);
return !!(gu_prime_wheel_mask & (1 << i));
}
static const uint32_t gu_small_prime_mask = 0UL
| 1 << 2
| 1 << 3
| 1 << 5
| 1 << 7
| 1 << 11
| 1 << 13
| 1 << 17
| 1 << 19
| 1 << 23
| 1 << 29
| 1U << 31;
static bool
gu_is_wheel_prime(int u)
{
gu_assert(u > 30 && u % 2 != 0 && u % 3 != 0 && u % 5 != 0);
int d = 0;
int i = 7;
goto start;
while (d * d <= u) {
for (i = 1; i <= 29; i+=2) {
start:
if (gu_prime_wheel(i) && u % (d + i) == 0) {
return false;
}
}
d += 30;
}
return true;
}
int
gu_prime_inf(int i)
{
if (i < 2) {
return 0;
} else if (i < 32) {
while (!(gu_small_prime_mask & (1 << i))) {
i--;
}
return i;
}
int d = (i - 1) | 1;
int r = d % 30;
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
d -= 2;
r -= 2;
if (r < 0) {
r += 30;
}
}
return d;
}
int
gu_prime_sup(int i)
{
if (i <= 2) {
return 2;
} else if (i < 32) {
while (!(gu_small_prime_mask & (1 << i))) {
i++;
}
return i;
}
int d = i | 1;
int r = d % 30;
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
d += 2;
r += 2;
if (r > 30) {
r -= 30;
}
}
return d;
}
bool
gu_is_prime(int i)
{
if (i < 2) {
return false;
} else if (i < 30) {
return !!(gu_small_prime_mask & (1 << i));
} else if (!gu_prime_wheel(i % 30)) {
return false;
} else {
return gu_is_wheel_prime(i);
}
}
bool
gu_is_twin_prime(int i)
{
return gu_is_prime(i) && gu_is_prime(i - 2);
}
int
gu_twin_prime_inf(int i)
{
while (true) {
i = gu_prime_inf(i);
if (i == 0) {
return 0;
} else if (gu_is_prime(i - 2)) {
return i;
}
i = i - 4;
}
return i;
}
int
gu_twin_prime_sup(int i)
{
if (i <= 5) {
return 5;
}
i = i - 2;
while (true) {
i = gu_prime_sup(i);
if (gu_is_prime(i + 2)) {
return i + 2;
}
i = i + 4;
}
return i;
}

16
src/runtime/c/gu/prime.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef GU_PRIME_H_
#define GU_PRIME_H_
#include <gu/defs.h>
bool gu_is_prime(int i);
bool gu_is_twin_prime(int i);
int gu_prime_inf(int i);
int gu_twin_prime_inf(int i);
int gu_prime_sup(int i);
int gu_twin_prime_sup(int i);
#endif // GU_PRIME_H_

15
src/runtime/c/gu/read.c Normal file
View File

@@ -0,0 +1,15 @@
#include <gu/read.h>
extern inline GuUCS
gu_read_ucs(GuReader* rdr, GuExn* err);
extern inline char
gu_getc(GuReader* rdr, GuExn* err);
GuReader*
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool)
{
GuReader* rdr = gu_new(GuReader, pool);
rdr->in_ = gu_init_in(gu_in_proxy_stream(utf8_in, pool));
return rdr;
}

31
src/runtime/c/gu/read.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef GU_READ_H_
#define GU_READ_H_
#include <gu/in.h>
#include <gu/ucs.h>
#include <gu/utf8.h>
typedef struct GuReader GuReader;
struct GuReader {
GuIn in_;
};
inline GuUCS
gu_read_ucs(GuReader* rdr, GuExn* err)
{
return gu_in_utf8(&rdr->in_, err);
}
inline char
gu_getc(GuReader* rdr, GuExn* err)
{
return gu_in_utf8_char(&rdr->in_, err);
}
GuReader*
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool);
/**< @todo Implement. */
#endif // GU_READ_H_

245
src/runtime/c/gu/seq.c Normal file
View File

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

198
src/runtime/c/gu/seq.h Normal file
View File

@@ -0,0 +1,198 @@
#ifndef GU_SEQ_H_
#define GU_SEQ_H_
#include <gu/mem.h>
#include <gu/bits.h>
typedef struct GuBuf GuBuf;
typedef GuOpaque() GuSeq;
GuSeq
gu_make_seq(size_t elem_size, size_t len, GuPool* pool);
#define gu_new_seq(T, N, POOL) \
gu_make_seq(sizeof(T), (N), (POOL))
static inline size_t
gu_seq_length(GuSeq seq)
{
GuWord w = seq.w_;
size_t tag = gu_tagged_tag(w);
if (tag == 0) {
GuWord* p = gu_tagged_ptr(w);
return (size_t) (p[-1] >> 1);
}
return tag;
}
static inline void*
gu_seq_data(GuSeq seq)
{
GuWord w = seq.w_;
int tag = gu_tagged_tag(w);
void* ptr = gu_tagged_ptr(w);
if (tag == 0) {
GuWord* p = ptr;
if (p[-1] & 0x1) {
return *(uint8_t**) ptr;
}
}
return ptr;
}
static inline bool
gu_seq_is_null(GuSeq seq)
{
return (gu_tagged_ptr(seq.w_)) == NULL;
}
#define gu_seq_index(SEQ, T, I) \
(&((T*)gu_seq_data(SEQ))[I])
#define gu_seq_get(SEQ, T, I) \
(*gu_seq_index(SEQ, T, I))
#define gu_seq_set(SEQ, T, I, V) \
GU_BEGIN \
(*gu_seq_index(SEQ, T, I) = (V)); \
GU_END
GuBuf*
gu_seq_buf(GuSeq seq);
GuSeq
gu_buf_seq(GuBuf* buf);
GuBuf*
gu_make_buf(size_t elem_size, GuPool* pool);
#define gu_new_buf(T, POOL) \
gu_make_buf(sizeof(T), (POOL))
size_t
gu_buf_length(GuBuf* buf);
size_t
gu_buf_avail(GuBuf* buf);
void*
gu_buf_data(GuBuf* buf);
#define gu_buf_index(BUF, T, I) \
(&((T*)gu_buf_data(BUF))[I])
#define gu_buf_get(BUF, T, I) \
(*gu_buf_index(BUF, T, I))
#define gu_buf_set(BUF, T, I) \
GU_BEGIN \
(*gu_buf_index(BUF, T, I) = (V)); \
GU_END
void
gu_buf_push_n(GuBuf* buf, const void* elems, size_t n_elems);
void*
gu_buf_extend_n(GuBuf* buf, size_t n_elems);
void*
gu_buf_extend(GuBuf* buf);
#define gu_buf_push(BUF, T, VAL) \
GU_BEGIN \
((*(T*)gu_buf_extend(BUF)) = (VAL)); \
GU_END
void
gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out);
const void*
gu_buf_trim_n(GuBuf* buf, size_t n_elems);
const void*
gu_buf_trim(GuBuf* buf);
#define gu_buf_pop(BUF, T) \
(*(T*)gu_buf_trim(BUF))
void
gu_seq_resize_tail(GuSeq seq, ptrdiff_t change);
#if 0
void
gu_buf_resize_head(GuBuf* buf, ptrdiff_t change);
void
gu_buf_unshift(GuBuf* buf, const void* data, size_t size);
void
gu_buf_shift(GuBuf* buf, size_t size, void* data_out);
#endif
GuSeq
gu_buf_freeze(GuBuf* buf, GuPool* pool);
extern const GuSeq gu_null_seq;
#define GU_NULL_SEQ { .w_ = (GuWord)(void*)NULL }
typedef GuSeq GuChars;
typedef GuSeq GuBytes;
typedef GuBuf GuCharBuf;
typedef GuBuf GuByteBuf;
char*
gu_chars_str(GuChars chars, GuPool* pool);
#endif // GU_SEQ_H_
#if defined(GU_OUT_H_) && !defined(GU_SEQ_H_OUT_)
#define GU_SEQ_H_OUT_
GuOut*
gu_buf_out(GuBuf* buf, GuPool* pool);
#endif
#if defined(GU_TYPE_H_) && !defined(GU_SEQ_H_TYPE_)
#define GU_SEQ_H_TYPE_
extern GU_DECLARE_KIND(GuSeq);
extern GU_DECLARE_KIND(GuBuf);
struct GuSeqType {
GuType_GuOpaque opaque_base;
GuType* elem_type;
};
typedef const struct GuSeqType GuSeqType, GuType_GuSeq;
#define GU_TYPE_INIT_GuSeq(k_, t_, elem_type_) { \
.opaque_base = GU_TYPE_INIT_GuOpaque(k_, t_, _), \
.elem_type = elem_type_, \
}
typedef struct GuBufType GuBufType, GuType_GuBuf;
struct GuBufType {
GuType_abstract abstract_base;
GuType* elem_type;
};
#define GU_TYPE_INIT_GuBuf(KIND, BUF_T, ELEM_T) { \
.abstract_base = GU_TYPE_INIT_abstract(KIND, BUF_T, _), \
.elem_type = ELEM_T \
}
extern GU_DECLARE_TYPE(GuChars, GuSeq);
extern GU_DECLARE_TYPE(GuBytes, GuSeq);
#endif

85
src/runtime/c/gu/str.c Normal file
View File

@@ -0,0 +1,85 @@
#include <gu/assert.h>
#include <gu/str.h>
#include <string.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
const char gu_empty_str[] = "";
const char* const gu_null_str = NULL;
char*
gu_new_str(size_t size, GuPool* pool)
{
char* str = gu_new_n(char, size + 1, pool);
memset(str, '\0', size + 1);
return str;
}
char*
gu_strdup(const char* cstr, GuPool* pool)
{
int len = strlen(cstr);
char* str = gu_new_str(len, pool);
memcpy(str, cstr, len);
return str;
}
bool
gu_str_eq(GuStr s1, GuStr s2)
{
return (strcmp(s1, s2)) == 0;
}
static bool
gu_str_is_equal(GuEquality* self, const void* p1, const void* p2)
{
(void) self;
const GuStr* sp1 = p1;
const GuStr* sp2 = p2;
return gu_str_eq(*sp1, *sp2);
}
static GuHash
gu_str_hasher_hash(GuHasher* self, const void* p)
{
(void) self;
GuHash h = 0;
const GuStr* sp = p;
for (const char* s = *sp; *s != '\0'; s++) {
h = 101 * h + (unsigned char) *s;
}
return h;
}
GuHasher gu_str_hasher[1] = {
{
.eq = { .is_equal = gu_str_is_equal },
.hash = gu_str_hasher_hash
}
};
GU_DEFINE_TYPE(GuStr, repr, _);
char*
gu_vasprintf(const char* fmt, va_list args, GuPool* pool)
{
va_list args2;
va_copy(args2, args);
int len = vsnprintf(NULL, 0, fmt, args2);
gu_assert_msg(len >= 0, "Invalid format string: \"%s\"", fmt);
va_end(args2);
char* str = gu_new_str(len, pool);
vsnprintf(str, len + 1, fmt, args);
return str;
}
char*
gu_asprintf(GuPool* pool, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
char* str = gu_vasprintf(fmt, args, pool);
va_end(args);
return str;
}

29
src/runtime/c/gu/str.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef GU_STR_H_
#define GU_STR_H_
#include <gu/mem.h>
#include <gu/hash.h>
extern const char gu_empty_str[];
extern const char* const gu_null_str;
typedef const char* GuStr;
char* gu_new_str(size_t size, GuPool* pool);
char* gu_strdup(const char* str, GuPool* pool);
bool
gu_str_eq(GuStr s1, GuStr s2);
extern GuHasher gu_str_hasher[1];
#include <gu/type.h>
extern GU_DECLARE_TYPE(GuStr, repr);
char* gu_vasprintf(const char* fmt, va_list args, GuPool* pool);
char* gu_asprintf(GuPool* pool, const char* fmt, ...);
#endif // GU_STR_H_

270
src/runtime/c/gu/string.c Normal file
View File

@@ -0,0 +1,270 @@
#include <gu/type.h>
#include <gu/out.h>
#include <gu/seq.h>
#include <gu/map.h>
#include <gu/string.h>
#include <gu/utf8.h>
#include <gu/assert.h>
#include "config.h"
const GuString gu_empty_string = { 1 };
struct GuStringBuf {
GuByteBuf* bbuf;
GuWriter* wtr;
};
GuStringBuf*
gu_string_buf(GuPool* pool)
{
GuBuf* buf = gu_new_buf(uint8_t, pool);
GuOut* out = gu_buf_out(buf, pool);
GuWriter* wtr = gu_new_utf8_writer(out, pool);
return gu_new_s(pool, GuStringBuf,
.bbuf = buf,
.wtr = wtr);
}
GuWriter*
gu_string_buf_writer(GuStringBuf* sb)
{
return sb->wtr;
}
static GuString
gu_utf8_string(const uint8_t* buf, size_t sz, GuPool* pool)
{
if (sz < GU_MIN(sizeof(GuWord), 128)) {
GuWord w = 0;
for (size_t n = 0; n < sz; n++) {
w = w << 8 | buf[n];
}
w = w << 8 | (sz << 1) | 1;
return (GuString) { w };
}
uint8_t* p = NULL;
if (sz < 256) {
p = gu_malloc_aligned(pool, 1 + sz, 2);
p[0] = (uint8_t) sz;
} else {
uint8_t* p =
gu_malloc_prefixed(pool, gu_alignof(size_t),
sizeof(size_t), 1, 1 + sizeof(sz));
((size_t*) p)[-1] = sz;
p[0] = 0;
}
memcpy(&p[1], buf, sz);
return (GuString) { (GuWord) (void*) p };
}
GuString
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool)
{
gu_writer_flush(sb->wtr, NULL);
uint8_t* data = gu_buf_data(sb->bbuf);
size_t len = gu_buf_length(sb->bbuf);
return gu_utf8_string(data, len, pool);
}
GuReader*
gu_string_reader(GuString s, GuPool* pool)
{
GuWord w = s.w_;
uint8_t* buf = NULL;
size_t len = 0;
if (w & 1) {
len = (w & 0xff) >> 1;
buf = gu_new_n(uint8_t, len, pool);
for (int i = len - 1; i >= 0; i--) {
w >>= 8;
buf[i] = w & 0xff;
}
} else {
uint8_t* p = (void*) w;
len = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
buf = &p[1];
}
GuIn* in = gu_data_in(buf, len, pool);
GuReader* rdr = gu_new_utf8_reader(in, pool);
return rdr;
}
static bool
gu_string_is_long(GuString s)
{
return !(s.w_ & 1);
}
bool
gu_string_is_stable(GuString s)
{
return !gu_string_is_long(s);
}
static size_t
gu_string_long_length(GuString s)
{
gu_assert(gu_string_is_long(s));
uint8_t* p = (void*) s.w_;
uint8_t len = p[0];
if (len > 0) {
return len;
}
return ((size_t*) p)[-1];
}
size_t
gu_string_length(GuString s)
{
if (gu_string_is_long(s)) {
return gu_string_long_length(s);
}
return (s.w_ & 0xff) >> 1;
}
static uint8_t*
gu_string_long_data(GuString s)
{
gu_require(gu_string_is_long(s));
uint8_t* p = (void*) s.w_;
return &p[1];
}
GuString
gu_string_copy(GuString string, GuPool* pool)
{
if (gu_string_is_long(string)) {
uint8_t* data = gu_string_long_data(string);
size_t len = gu_string_long_length(string);
return gu_utf8_string(data, len, pool);
} else {
return string;
}
}
void
gu_string_write(GuString s, GuWriter* wtr, GuExn* err)
{
GuWord w = s.w_;
uint8_t buf[sizeof(GuWord)];
uint8_t* src;
size_t sz;
if (w & 1) {
sz = (w & 0xff) >> 1;
gu_assert(sz <= sizeof(GuWord));
size_t i = sz;
while (i > 0) {
w >>= 8;
buf[--i] = w & 0xff;
}
src = buf;
} else {
uint8_t* p = (void*) w;
sz = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
src = &p[1];
}
gu_utf8_write(src, sz, wtr, err);
}
GuString
gu_format_string_v(const char* fmt, va_list args, GuPool* pool)
{
GuPool* tmp_pool = gu_local_pool();
GuStringBuf* sb = gu_string_buf(tmp_pool);
GuWriter* wtr = gu_string_buf_writer(sb);
gu_vprintf(fmt, args, wtr, NULL);
gu_writer_flush(wtr, NULL);
GuString s = gu_string_buf_freeze(sb, pool);
gu_pool_free(tmp_pool);
return s;
}
GuString
gu_format_string(GuPool* pool, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
GuString s = gu_format_string_v(fmt, args, pool);
va_end(args);
return s;
}
GuString
gu_str_string(const char* str, GuPool* pool)
{
#ifdef GU_CHAR_ASCII
return gu_utf8_string((const uint8_t*) str, strlen(str), pool);
#else
GuPool* tmp_pool = gu_local_pool();
GuStringBuf* sb = gu_string_buf(tmp_pool);
GuWriter* wtr = gu_string_buf_writer(sb);
gu_puts(str, wtr, NULL);
gu_writer_flush(wtr, NULL);
GuString s = gu_string_buf_freeze(sb, pool);
gu_pool_free(tmp_pool);
return s;
#endif
}
GuWord
gu_string_hash(GuString s)
{
if (s.w_ & 1) {
return s.w_;
}
size_t len = gu_string_length(s);
uint8_t* data = gu_string_long_data(s);
return gu_hash_bytes(0, data, len);
}
bool
gu_string_eq(GuString s1, GuString s2)
{
if (s1.w_ == s2.w_) {
return true;
} else if (gu_string_is_long(s1) && gu_string_is_long(s2)) {
size_t len1 = gu_string_long_length(s1);
size_t len2 = gu_string_long_length(s2);
if (len1 != len2) {
return false;
}
uint8_t* data1 = gu_string_long_data(s1);
uint8_t* data2 = gu_string_long_data(s2);
return (memcmp(data1, data2, len1) == 0);
}
return false;
}
static GuHash
gu_string_hasher_hash(GuHasher* self, const void* p)
{
(void) self;
const GuString* sp = p;
return gu_string_hash(*sp);
}
static bool
gu_string_eq_fn(GuEquality* self, const void* p1, const void* p2)
{
(void) self;
const GuString* sp1 = p1;
const GuString* sp2 = p2;
return gu_string_eq(*sp1, *sp2);
}
GuHasher gu_string_hasher[1] = {
{
.eq = { gu_string_eq_fn },
.hash = gu_string_hasher_hash
}
};
GU_DEFINE_TYPE(GuString, GuOpaque, _);
GU_DEFINE_TYPE(GuStrings, GuSeq, gu_type(GuString));
GU_DEFINE_KIND(GuStringMap, GuMap);

125
src/runtime/c/gu/string.h Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright 2011 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GU_STRING_H_
#define GU_STRING_H_
#include <gu/bits.h>
#include <gu/read.h>
#include <gu/write.h>
typedef GuOpaque() GuString;
extern const GuString gu_empty_string;
GuString
gu_string_copy(GuString string, GuPool* pool);
void
gu_string_write(GuString string, GuWriter* wtr, GuExn* err);
GuReader*
gu_string_reader(GuString string, GuPool* pool);
bool
gu_string_is_stable(GuString string);
GuString
gu_ucs_string(const GuUCS* ubuf, size_t len, GuPool* pool);
typedef struct GuStringBuf GuStringBuf;
GuStringBuf*
gu_string_buf(GuPool* pool);
GuWriter*
gu_string_buf_writer(GuStringBuf* sb);
GuString
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool);
GuString
gu_format_string_v(const char* fmt, va_list args, GuPool* pool);
GuString
gu_format_string(GuPool* pool, const char* fmt, ...);
GuString
gu_str_string(const char* str, GuPool* pool);
#endif // GU_STRING_H_
#if defined(GU_HASH_H_) && !defined(GU_STRING_H_HASH_)
#define GU_STRING_H_HASH_
uintptr_t
gu_string_hash(GuString s);
extern GuHasher gu_string_hasher[1];
bool
gu_string_eq(GuString s1, GuString s2);
#endif
#ifdef GU_TYPE_H_
# ifndef GU_STRING_H_TYPE_
# define GU_STRING_H_TYPE_
extern GU_DECLARE_TYPE(GuString, GuOpaque);
# endif
# if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_TYPE_)
# define GU_STRING_H_SEQ_TYPE_
extern GU_DECLARE_TYPE(GuStrings, GuSeq);
# endif
# if defined(GU_MAP_H_TYPE_) && !defined(GU_STRING_H_MAP_TYPE_)
# define GU_STRING_H_MAP_TYPE_
extern GU_DECLARE_KIND(GuStringMap);
typedef GuType_GuMap GuType_GuStringMap;
#define GU_TYPE_INIT_GuStringMap(KIND, MAP_T, VAL_T, DEFAULT) \
GU_TYPE_INIT_GuMap(KIND, MAP_T, \
gu_type(GuString), gu_string_hasher, \
VAL_T, DEFAULT)
# endif
#endif
#if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_)
#define GU_STRING_H_SEQ_
typedef GuSeq GuStrings;
// typedef GuBuf GuStringBuf;
#endif
#if defined(GU_MAP_H_) && !defined(GU_STRING_H_MAP_)
#define GU_STRING_H_MAP_
typedef GuMap GuStringMap;
#define gu_new_string_map(VAL_T, DEFAULT, POOL) \
gu_new_map(GuString, gu_string_hasher, (VAL_T), (DEFAULT), (POOL))
#endif

View File

@@ -0,0 +1,30 @@
#ifndef GU_SYSDEPS_H_
#define GU_SYSDEPS_H_
#include <guconfig.h>
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
# define GU_GNUC
#endif
#ifdef GU_GNUC
# define GU_ALIGNOF __alignof
# define GU_HAVE_STATEMENT_EXPRESSIONS
# define GU_GNUC_ATTR(x) __attribute__(( x ))
# if defined(__OPTIMIZE_SIZE__)
# define GU_OPTIMIZE_SIZE
# elif defined(__OPTIMIZE__)
# define GU_OPTIMIZE_SPEED
# endif
#else
# define GU_GNUC_ATTR(x)
#endif
#ifdef S_SPLINT_S
# define GU_SPLINT(x) %{ x %}
#else
# define GU_SPLINT(x)
#endif
#endif // GU_SYSDEPS_H_

229
src/runtime/c/gu/type.c Normal file
View File

@@ -0,0 +1,229 @@
#include <gu/type.h>
#include <gu/assert.h>
#include <gu/map.h>
GuKind GU_TYPE_IDENT(type)[1] = {{ .super = NULL }};
GU_DEFINE_KIND(alias, type);
GU_DEFINE_KIND(typedef, alias);
GU_DEFINE_KIND(referenced, alias);
GU_DEFINE_KIND(repr, type);
GU_DEFINE_KIND(GuOpaque, repr);
GU_DEFINE_KIND(abstract, type);
GU_DEFINE_KIND(struct, repr);
GU_DEFINE_KIND(pointer, repr);
GU_DEFINE_KIND(reference, pointer);
GU_DEFINE_KIND(shared, pointer);
GU_DEFINE_KIND(primitive, repr);
// sizeof(void) is illegal, so do this manually
GuPrimType GU_TYPE_IDENT(void)[1] = {{
.repr_base = {
.type_base = {
.kind_base = {
.super = gu_kind(primitive),
},
},
.size = 0,
.align = 1,
},
.name = "void",
}};
GU_DEFINE_KIND(integer, primitive);
GU_DEFINE_TYPE(char, integer, _);
GU_DEFINE_KIND(signed, integer);
GU_DEFINE_TYPE(int, signed, _);
GU_DEFINE_TYPE(int8_t, signed, _);
GU_DEFINE_TYPE(int16_t, signed, _);
GU_DEFINE_TYPE(int32_t, signed, _);
GU_DEFINE_TYPE(int64_t, signed, _);
GU_DEFINE_TYPE(intptr_t, signed, _);
GU_DEFINE_TYPE(intmax_t, signed, _);
GU_DEFINE_KIND(unsigned, integer);
GU_DEFINE_TYPE(uint8_t, unsigned, _);
GU_DEFINE_TYPE(uint16_t, unsigned, _);
GU_DEFINE_TYPE(uint32_t, unsigned, _);
GU_DEFINE_TYPE(uint64_t, unsigned, _);
GU_DEFINE_TYPE(uintmax_t, unsigned, _);
GU_DEFINE_TYPE(size_t, unsigned, _);
GU_DEFINE_TYPE(GuLength, unsigned, _);
GU_DEFINE_KIND(GuFloating, primitive);
GU_DEFINE_TYPE(float, GuFloating, _);
GU_DEFINE_TYPE(double, GuFloating, _);
GU_DEFINE_TYPE(GuLongDouble, GuFloating, _);
GU_DEFINE_KIND(enum, repr);
bool gu_type_has_kind(GuType* type, GuKind* kind)
{
GuKind* k = (GuKind*)type;
while (k != NULL) {
if (k == kind) {
return true;
}
k = k->super;
}
return false;
}
struct GuTypeMap {
GuMap* map;
};
static void
gu_type_map_init(GuTypeMap* tmap, GuTypeTable* table)
{
for (int i = 0; i < table->parents.len; i++) {
gu_type_map_init(tmap, table->parents.elems[i]);
}
for (int i = 0; i < table->entries.len; i++) {
GuTypeTableEntry* e = &table->entries.elems[i];
gu_map_put(tmap->map, e->kind, void*, e->val);
}
}
GuTypeMap*
gu_new_type_map(GuTypeTable* table, GuPool* pool)
{
GuTypeMap* tmap =
gu_new_i(pool, GuTypeMap,
.map = gu_new_map(GuKind, NULL, void*, &gu_null, pool));
gu_type_map_init(tmap, table);
return tmap;
}
bool
gu_struct_has_flex(GuStructRepr* srepr)
{
for (int i = 0; i < srepr->members.len; i++) {
if (srepr->members.elems[i].is_flex) {
return true;
}
}
return false;
}
void*
gu_type_map_get(GuTypeMap* tmap, GuType* type)
{
GuKind* kind = (GuKind*)type;
while (kind != NULL) {
void* val = gu_map_get(tmap->map, kind, void*);
if (val != NULL) {
return val;
}
kind = kind->super;
}
return NULL;
}
const void*
gu_type_dyn_cast(GuType* type, GuKind* kind)
{
if (gu_type_has_kind(type, kind)) {
return type;
}
return NULL;
}
const void*
gu_type_check_cast(GuType* type, GuKind* kind)
{
gu_assert(gu_type_has_kind(type, kind));
return type;
}
GuTypeRepr*
gu_type_repr(GuType* type)
{
GuTypeAlias* alias;
while ((alias = gu_type_try_cast(type, alias))) {
type = alias->type;
}
return gu_type_try_cast(type, repr);
}
size_t
gu_type_size(GuType* type)
{
GuTypeRepr* repr = gu_type_repr(type);
return repr ? repr->size : 0;
}
GuEnumConstant*
gu_enum_value(GuEnumType* etype, const void* enump)
{
size_t esize = etype->repr_base.size;
#define CHECK_ENUM_TYPE(t_) do { \
if (esize == sizeof(t_)) { \
t_ c = *(const t_*)enump; \
for (int i = 0; i < etype->constants.len; i++) { \
GuEnumConstant* cp = &etype->constants.elems[i]; \
t_ d = *(const t_*)cp->enum_value; \
if (c == d) { \
return cp; \
} \
} \
return NULL; \
} \
} while (false)
CHECK_ENUM_TYPE(int);
CHECK_ENUM_TYPE(char);
CHECK_ENUM_TYPE(short);
CHECK_ENUM_TYPE(long);
CHECK_ENUM_TYPE(long long);
return NULL;
}
void*
gu_type_malloc(GuType* type, GuPool* pool)
{
GuTypeRepr* repr = gu_type_repr(type);
gu_assert(repr);
return gu_malloc_aligned(pool, repr->size, repr->align);
}
#if 0
typedef const struct GuPtrConvFns GuPtrConvFns;
struct GuPtrConvFns {
void* (*get)(const void* pp);
void (*set)(void** pp, void* p);
};
#define GU_TYPE_PTR_DEFINE_GETSET(name_, t_) \
static void* gu_type_##name_##_ptr_get(const void* pp) { \
return *(t_* const*) pp; \
} \
\
static void gu_type_##name_##_ptr_set(void* pp, void* p) { \
*(t_**) pp = p; \
} \
static GuPtrConvFns gu_ptr_conv_##name_ = { \
.get = gu_type_##name_##_ptr_get, \
.set = gu_type_##name_##_ptr_set \
}
GU_TYPE_PTR_DEFINE_GETSET(void, void);
GU_TYPE_PTR_DEFINE_GETSET(struct, GuStruct);
GU_TYPE_PTR_DEFINE_GETSET(int, int);
#endif

454
src/runtime/c/gu/type.h Normal file
View File

@@ -0,0 +1,454 @@
#ifndef GU_TYPE_H_
#define GU_TYPE_H_
#include <gu/defs.h>
//
// kind
//
typedef const struct GuKind GuKind;
struct GuKind {
GuKind* super;
};
// Use GU_PASTE here so k_ can be preprocessor-expanded
#define GU_TYPE_IDENT(k_) GU_PASTE(gu_type__,k_)
#define gu_kind(k_) ((GuKind*)GU_TYPE_IDENT(k_))
#define GU_DECLARE_KIND(k_) \
GuKind GU_TYPE_IDENT(k_)[1]
extern GU_DECLARE_KIND(kind);
#define GU_DEFINE_KIND(k_, super_k_) \
GuKind GU_TYPE_IDENT(k_)[1] = {{ .super = gu_kind(super_k_) }}
//
// type
//
typedef const struct GuType GuType;
struct GuType {
GuKind kind_base;
};
typedef GuType GuType_type;
extern GU_DECLARE_KIND(type);
#define GU_TYPE_INIT_type(k_, t_, _) { .kind_base = { .super = gu_kind(k_) } }
#define gu_type(t_) ((GuType*)gu_kind(t_))
#define GU_KIND_TYPE(k_) GU_PASTE(GuType_,k_)
// This cannot be used indirectly, since we don't want to pp-expand k_.
// We must inline the body into other macros.
#define GU_TYPE_INIT(k_, ...) \
GU_TYPE_INIT_##k_(k_, __VA_ARGS__)
//#define GU_TYPE_LIT(k_, ...)
// ((GuType*)(GuType_##k_[]){GU_TYPE_INIT(k_, __VA_ARGS__)})
#define GU_TYPE_LIT(k_, ...) \
((GuType*)&(GU_KIND_TYPE(k_)) GU_TYPE_INIT_##k_(k_, __VA_ARGS__))
#define GU_DECLARE_TYPE(t_, k_) \
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1]
//#define GU_DEFINE_TYPE(t_, k_, ...)
// GuType_##k_ GU_TYPE_IDENT(t_) = GU_TYPE_INIT(k_, t_, __VA_ARGS__)
#define GU_DEFINE_TYPE(t_, k_, ...) \
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1] = \
{ GU_TYPE_INIT_##k_(k_, t_, __VA_ARGS__) }
#define GU_DEFINE_TYPE_ALIAS(t1_, t2_) \
static GuType* const GU_TYPE_IDENT(t1_) = gu_type(t2_)
//
// abstract
//
typedef GuType GuType_abstract;
#define GU_TYPE_INIT_abstract(k_, t_, _) \
GU_TYPE_INIT_type(k_, t_, _)
extern GU_DECLARE_KIND(abstract);
//
// repr
//
typedef struct GuTypeRepr GuTypeRepr, GuType_repr;
struct GuTypeRepr {
GuType type_base;
uint16_t size;
uint16_t align;
};
#define GU_TYPE_INIT_repr(k_, t_, _) { \
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
.size = sizeof(t_), \
.align = gu_alignof(t_) \
}
extern GU_DECLARE_KIND(repr);
//
// GuOpaque
//
typedef GuType_repr GuType_GuOpaque;
#define GU_TYPE_INIT_GuOpaque GU_TYPE_INIT_repr
extern GU_DECLARE_KIND(GuOpaque);
//
// pointer
//
typedef const struct GuPointerType GuPointerType, GuType_pointer;
struct GuPointerType {
GuType_repr repr_base;
GuType* pointed_type;
};
#define GU_TYPE_INIT_pointer(k_, t_, pointed_) \
{ \
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
.pointed_type = pointed_ \
}
extern GU_DECLARE_KIND(pointer);
#define gu_ptr_type(t_) \
GU_TYPE_LIT(pointer, t_*, gu_type(t_))
//
// alias
//
typedef const struct GuTypeAlias GuTypeAlias, GuType_alias;
struct GuTypeAlias {
GuType type_base;
GuType* type;
};
#define GU_TYPE_INIT_alias(k_, t_, type_) { \
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
.type = type_ \
}
extern GU_DECLARE_KIND(alias);
//
// typedef
//
typedef const struct GuTypeDef GuTypeDef, GuType_typedef;
struct GuTypeDef {
GuType_alias alias_base;
const char* name;
};
#define GU_TYPE_INIT_typedef(k_, t_, type_) { \
.alias_base = GU_TYPE_INIT_alias(k_, t_, type_), \
.name = #t_, \
}
extern GU_DECLARE_KIND(typedef);
#define GU_DEFINE_TYPEDEF_X(t_, dk_, k_, ...) \
GU_DEFINE_TYPE(t_, dk_, GU_TYPE_LIT(k_, t_, __VA_ARGS__))
#define GU_DEFINE_TYPEDEF(t_, ...) \
GU_DEFINE_TYPEDEF_X(t_, typedef, __VA_ARGS__)
//
// referenced
//
extern GU_DECLARE_KIND(referenced);
typedef GuType_alias GuType_referenced;
#define GU_TYPE_INIT_referenced GU_TYPE_INIT_alias
#include <gu/list.h>
//
// struct
//
typedef const struct GuStructRepr GuStructRepr, GuType_struct;
typedef const struct GuMember GuMember;
struct GuMember {
ptrdiff_t offset;
const char* name;
GuType* type;
bool is_flex;
};
struct GuStructRepr {
GuType_repr repr_base;
const char* name;
GuSList(GuMember) members;
};
extern GU_DECLARE_KIND(struct);
#define GU_MEMBER_AUX_(struct_, member_, type_, is_flex_) \
{ \
.offset = offsetof(struct_, member_), \
.name = #member_, \
.type = type_, \
.is_flex = is_flex_, \
}
#define GU_MEMBER_V(struct_, member_, type_) \
GU_MEMBER_AUX_(struct_, member_, type_, false)
#define GU_MEMBER(s_, m_, t_) \
GU_MEMBER_V(s_, m_, gu_type(t_))
#define GU_MEMBER_P(s_, m_, t_) \
GU_MEMBER_V(s_, m_, gu_ptr_type(t_))
#define GU_MEMBER_S(s_, m_, t_) \
GU_MEMBER_V(s_, m_, gu_shared_ptr_type(t_))
#define GU_FLEX_MEMBER_V(struct_, member_, type_) \
GU_MEMBER_AUX_(struct_, member_, type_, true)
#define GU_FLEX_MEMBER(s_, m_, t_) \
GU_FLEX_MEMBER_V(s_, m_, gu_type(t_))
#define GU_FLEX_MEMBER_P(s_, m_, t_) \
GU_FLEX_MEMBER_V(s_, m_, gu_ptr_type(t_))
#define GU_TYPE_INIT_struct(k_, t_, ...) { \
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
.name = #t_, \
.members = GU_SLIST(GuMember, __VA_ARGS__) \
}
bool
gu_struct_has_flex(GuStructRepr* srepr);
//
// reference
//
typedef GuType_pointer GuType_reference;
#define GU_TYPE_INIT_reference GU_TYPE_INIT_pointer
extern GU_DECLARE_KIND(reference);
//
// shared
//
typedef GuType_pointer GuType_shared;
#define GU_TYPE_INIT_shared GU_TYPE_INIT_pointer
extern GU_DECLARE_KIND(shared);
#define gu_shared_ptr_type(t_) \
GU_TYPE_LIT(shared, t_*, gu_type(t_))
//
// primitives
//
typedef const struct GuPrimType GuPrimType, GuType_primitive;
struct GuPrimType {
GuType_repr repr_base;
const char* name;
};
#define GU_TYPE_INIT_primitive(k_, t_, _) { \
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
.name = #t_ \
}
extern GU_DECLARE_KIND(primitive);
extern GU_DECLARE_TYPE(void, primitive);
#define GU_TYPE_INIT_integer GU_TYPE_INIT_primitive
typedef GuType_primitive GuType_integer;
extern GU_DECLARE_KIND(integer);
extern GU_DECLARE_TYPE(char, integer);
#define GU_TYPE_INIT_signed GU_TYPE_INIT_integer
typedef GuType_integer GuType_signed;
extern GU_DECLARE_KIND(signed);
extern GU_DECLARE_TYPE(int, signed);
extern GU_DECLARE_TYPE(int8_t, signed);
extern GU_DECLARE_TYPE(int16_t, signed);
extern GU_DECLARE_TYPE(int32_t, signed);
extern GU_DECLARE_TYPE(int64_t, signed);
extern GU_DECLARE_TYPE(intptr_t, signed);
extern GU_DECLARE_TYPE(intmax_t, signed);
#define GU_TYPE_INIT_unsigned GU_TYPE_INIT_integer
typedef GuType_integer GuType_unsigned;
extern GU_DECLARE_KIND(unsigned);
extern GU_DECLARE_TYPE(uint8_t, unsigned);
extern GU_DECLARE_TYPE(uint16_t, unsigned);
extern GU_DECLARE_TYPE(uint32_t, unsigned);
extern GU_DECLARE_TYPE(uint64_t, unsigned);
extern GU_DECLARE_TYPE(uintmax_t, unsigned);
extern GU_DECLARE_TYPE(size_t, unsigned);
typedef size_t GuLength;
extern GU_DECLARE_TYPE(GuLength, unsigned); // TODO: get rid
#define GU_TYPE_INIT_GuFloating GU_TYPE_INIT_primitive
typedef GuType_primitive GuType_GuFloating;
extern GU_DECLARE_KIND(GuFloating);
extern GU_DECLARE_TYPE(float, GuFloating);
extern GU_DECLARE_TYPE(double, GuFloating);
typedef long double GuLongDouble;
extern GU_DECLARE_TYPE(GuLongDouble, GuFloating);
//
// enum
//
extern GU_DECLARE_KIND(enum);
typedef const struct GuEnumConstant GuEnumConstant;
struct GuEnumConstant {
const char* name;
int64_t value;
const void* enum_value;
};
typedef const struct GuEnumType GuEnumType, GuType_enum;
struct GuEnumType {
GuType_repr repr_base;
GuSList(GuEnumConstant) constants;
};
#define GU_ENUM_C(t_, x) { \
.name = #x, \
.value = x, \
.enum_value = (const t_[1]){ x } \
}
#define GU_TYPE_INIT_enum(k_, t_, ...) { \
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
.constants = GU_SLIST(GuEnumConstant, __VA_ARGS__) \
}
GuEnumConstant*
gu_enum_value(GuEnumType* etype, const void* enump);
bool gu_type_has_kind(const GuType* type, const GuKind* kind);
typedef const struct GuTypeTableEntry GuTypeTableEntry;
struct GuTypeTableEntry {
GuKind* kind;
void* val;
};
typedef const struct GuTypeTable GuTypeTable;
struct GuTypeTable {
GuSList(const GuTypeTable*) parents;
GuSList(GuTypeTableEntry) entries;
};
#define GU_TYPETABLE(parents_, ...) { \
.parents = parents_, \
.entries = GU_SLIST(GuTypeTableEntry, \
__VA_ARGS__) \
}
typedef struct GuTypeMap GuTypeMap;
GuTypeMap*
gu_new_type_map(GuTypeTable* table, GuPool* pool);
void*
gu_type_map_get(GuTypeMap* tmap, GuType* type);
size_t
gu_type_size(GuType* type);
GuTypeRepr*
gu_type_repr(GuType* type);
const void*
gu_type_check_cast(GuType* t, GuKind* k);
const void*
gu_type_dyn_cast(GuType* t, GuKind* k);
#define gu_type_try_cast(type_, k_) \
((GU_KIND_TYPE(k_)*)gu_type_dyn_cast(type_, gu_kind(k_)))
#ifndef NDEBUG
#define gu_type_cast(type_, k_) \
((GU_KIND_TYPE(k_)*)gu_type_check_cast(type_, gu_kind(k_)))
#else
#define gu_type_cast(type_, k_) \
((GU_KIND_TYPE(k_)*)(type_))
#endif
void* gu_type_malloc(GuType* type, GuPool* pool);
#if 0
void* gu_type_ptr_get(GuType* type, const void* pp);
void gu_type_ptr_set(GuType* type, void* pp, void* p);
#endif
#endif // GU_TYPE_H_

135
src/runtime/c/gu/ucs.c Normal file
View File

@@ -0,0 +1,135 @@
#include <gu/ucs.h>
#include <gu/assert.h>
#include <guconfig.h>
GU_DEFINE_TYPE(GuUCSExn, abstract, _);
#ifdef GU_CHAR_ASCII
bool
gu_char_is_valid(char c)
{
if (c < 0) {
return false;
} else if (c < 64) {
return UINT64_C(0xffffffef00003f81) & (UINT64_C(1) << c);
}
#if CHAR_MAX > 127 // Let's avoid spurious warnings
else if (c > 127) {
return false;
}
#endif
return UINT64_C(0x7ffffffefffffffe) & (UINT64_C(1) << (c - 64));
}
char
gu_ucs_char(GuUCS uc, GuExn* err)
{
if (0 <= uc && uc <= 127) {
char c = (char) uc;
if (gu_char_is_valid(c)) {
return c;
}
}
gu_raise(err, GuUCSExn);
return 0;
}
#else // defined(GU_CHAR_ASCII)
static const char gu_ucs_ascii[128] =
"\0\0\0\0\0\0\0\a\b\t\n\v\f\r\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
" !\"#\0%&'()*+,-./0123456789:;<=>?"
"\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"\0abcdefghijklmnopqrstuvwxyz{|}~\0";
const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX] = {
['\0'] = 0x00, ['\a'] = 0x07, ['\b'] = 0x08, ['\t'] = 0x09,
['\n'] = 0x0a, ['\v'] = 0x0b, ['\f'] = 0x0c, ['\r'] = 0x0d,
[' '] = 0x20, ['!'] = 0x21, ['"'] = 0x22, ['#'] = 0x23, ['%'] = 0x25,
['&'] = 0x26, ['\''] = 0x27, ['('] = 0x28, [')'] = 0x29, ['*'] = 0x2a,
['+'] = 0x2b, [','] = 0x2c, ['-'] = 0x2d, ['.'] = 0x2e, ['/'] = 0x2f,
['0'] = 0x30, ['1'] = 0x31, ['2'] = 0x32, ['3'] = 0x33, ['4'] = 0x34,
['5'] = 0x35, ['6'] = 0x36, ['7'] = 0x37, ['8'] = 0x38, ['9'] = 0x39,
[':'] = 0x3a, [';'] = 0x3b, ['<'] = 0x3c, ['='] = 0x3d, ['>'] = 0x3e,
['?'] = 0x3f, ['A'] = 0x41, ['B'] = 0x42, ['C'] = 0x43, ['D'] = 0x44,
['E'] = 0x45, ['F'] = 0x46, ['G'] = 0x47, ['H'] = 0x48, ['I'] = 0x49,
['J'] = 0x4a, ['K'] = 0x4b, ['L'] = 0x4c, ['M'] = 0x4d, ['N'] = 0x4e,
['O'] = 0x4f, ['P'] = 0x50, ['Q'] = 0x51, ['R'] = 0x52, ['S'] = 0x53,
['T'] = 0x54, ['U'] = 0x55, ['V'] = 0x56, ['W'] = 0x57, ['X'] = 0x58,
['Y'] = 0x59, ['Z'] = 0x5a, ['['] = 0x5b, ['\\'] = 0x5c, [']'] = 0x5d,
['^'] = 0x5e, ['_'] = 0x5f, ['a'] = 0x61, ['b'] = 0x62, ['c'] = 0x63,
['d'] = 0x64, ['e'] = 0x65, ['f'] = 0x66, ['g'] = 0x67, ['h'] = 0x68,
['i'] = 0x69, ['j'] = 0x6a, ['k'] = 0x6b, ['l'] = 0x6c, ['m'] = 0x6d,
['n'] = 0x6e, ['o'] = 0x6f, ['p'] = 0x70, ['q'] = 0x71, ['r'] = 0x72,
['s'] = 0x73, ['t'] = 0x74, ['u'] = 0x75, ['v'] = 0x76, ['w'] = 0x77,
['x'] = 0x78, ['y'] = 0x79, ['z'] = 0x7a, ['{'] = 0x7b, ['|'] = 0x7c,
['}'] = 0x7d, ['~'] = 0x7e
};
bool
gu_char_is_valid(char c)
{
if (c > 0) {
return (gu_ucs_ascii_reverse_[(int) c] > 0);
}
return (c == '\0');
}
char
gu_ucs_char(GuUCS uc, GuExn* err)
{
if (uc == 0) {
return '\0';
} else if (0 < uc && uc <= 127) {
char c = gu_ucs_ascii[uc];
if (c != '\0') {
return (unsigned char) c;
}
}
gu_raise(err, GuUCSExn);
return 0;
}
#endif
size_t
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err)
{
size_t n = 0;
while (n < len) {
char c = cbuf[n];
if (!gu_char_is_valid(c)) {
gu_raise(err, GuUCSExn);
return n;
}
ubuf[n] = gu_char_ucs(c);
n++;
}
return n;
}
size_t
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err)
{
size_t n = 0;
while (n < len) {
char c = gu_ucs_char(ubuf[n], err);
if (!gu_ok(err)) {
break;
}
cbuf[n] = c;
n++;
}
return n;
}
extern inline bool
gu_ucs_valid(GuUCS ucs);
extern inline GuUCS
gu_char_ucs(char c);

53
src/runtime/c/gu/ucs.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef GU_UCS_H_
#define GU_UCS_H_
#include <gu/defs.h>
#include <gu/exn.h>
#include <gu/assert.h>
#if defined(__STDC_ISO_10646__) && WCHAR_MAX >= 0x10FFFF
#include <wchar.h>
#define GU_UCS_WCHAR
typedef wchar_t GuUCS;
#else
typedef int32_t GuUCS;
#endif
#define GU_UCS_MAX ((GuUCS)(0x10FFFF))
bool
gu_char_is_valid(char c);
inline bool
gu_ucs_valid(GuUCS ucs)
{
return ucs >= 0 && ucs <= GU_UCS_MAX;
}
inline GuUCS
gu_char_ucs(char c)
{
gu_require(gu_char_is_valid(c));
#ifdef GU_CHAR_ASCII
GuUCS u = (GuUCS) c;
#else
extern const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX];
GuUCS u = gu_ucs_ascii_reverse_[(unsigned char) c];
#endif
gu_ensure(u < 0x80);
return u;
}
char
gu_ucs_char(GuUCS uc, GuExn* err);
size_t
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err);
size_t
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err);
extern GU_DECLARE_TYPE(GuUCSExn, abstract);
#endif // GU_ISO10646_H_

220
src/runtime/c/gu/utf8.c Normal file
View File

@@ -0,0 +1,220 @@
#include <gu/assert.h>
#include <gu/utf8.h>
#include <guconfig.h>
GuUCS
gu_utf8_decode(const uint8_t** src_inout)
{
const uint8_t* src = *src_inout;
uint8_t c = src[0];
if (c < 0x80) {
*src_inout = src + 1;
return (GuUCS) c;
}
size_t len = (c < 0xe0 ? 1 :
c < 0xf0 ? 2 :
3);
uint32_t mask = 0x07071f7f;
uint32_t u = c & (mask >> (len * 8));
for (size_t i = 1; i <= len; i++) {
c = src[i];
u = u << 6 | (c & 0x3f);
}
*src_inout = &src[len + 1];
return (GuUCS) u;
}
GuUCS
gu_in_utf8_(GuIn* in, GuExn* err)
{
uint8_t c = gu_in_u8(in, err);
if (!gu_ok(err)) {
return 0;
}
int len = (c < 0x80 ? 0 :
c < 0xc2 ? -1 :
c < 0xe0 ? 1 :
c < 0xf0 ? 2 :
c < 0xf5 ? 3 :
-1);
if (len < 0) {
goto fail;
} else if (len == 0) {
return c;
}
static const uint8_t mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
uint32_t u = c & mask[len];
uint8_t buf[3];
// If reading the extra bytes causes EOF, it is an encoding
// error, not a legitimate end of character stream.
GuExn* tmp_err = gu_exn(err, GuEOF, NULL);
gu_in_bytes(in, buf, len, tmp_err);
if (tmp_err->caught) {
goto fail;
}
if (!gu_ok(err)) {
return 0;
}
for (int i = 0; i < len; i++) {
c = buf[i];
if ((c & 0xc0) != 0x80) {
goto fail;
}
u = u << 6 | (c & 0x3f);
}
GuUCS ucs = (GuUCS) u;
if (!gu_ucs_valid(ucs)) {
goto fail;
}
return ucs;
fail:
gu_raise(err, GuUCSExn);
return 0;
}
size_t
gu_advance_utf8(GuUCS ucs, uint8_t* buf)
{
gu_require(gu_ucs_valid(ucs));
if (ucs < 0x80) {
buf[0] = (uint8_t) ucs;
return 1;
} else if (ucs < 0x800) {
buf[0] = 0xc0 | (ucs >> 6);
buf[1] = 0x80 | (ucs & 0x3f);
return 2;
} else if (ucs < 0x10000) {
buf[0] = 0xe0 | (ucs >> 12);
buf[1] = 0x80 | ((ucs >> 6) & 0x3f);
buf[2] = 0x80 | (ucs & 0x3f);
return 3;
} else {
buf[0] = 0xf0 | (ucs >> 18);
buf[1] = 0x80 | ((ucs >> 12) & 0x3f);
buf[2] = 0x80 | ((ucs >> 6) & 0x3f);
buf[3] = 0x80 | (ucs & 0x3f);
return 4;
}
}
char
gu_in_utf8_char_(GuIn* in, GuExn* err)
{
return gu_ucs_char(gu_in_utf8(in, err), err);
}
void
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err)
{
uint8_t buf[4];
size_t sz = gu_advance_utf8(ucs, buf);
switch (sz) {
case 2:
gu_out_bytes(out, buf, 2, err);
break;
case 3:
gu_out_bytes(out, buf, 3, err);
break;
case 4:
gu_out_bytes(out, buf, 4, err);
break;
default:
gu_impossible();
}
}
extern inline void
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err);
static size_t
gu_utf32_out_utf8_buffered_(const GuUCS* src, size_t len, GuOut* out,
GuExn* err)
{
size_t src_i = 0;
while (src_i < len) {
size_t dst_sz;
uint8_t* dst = gu_out_begin_span(out, len - src_i, &dst_sz, err);
if (!gu_ok(err)) {
return src_i;
}
if (!dst) {
gu_out_utf8(src[src_i], out, err);
if (!gu_ok(err)) {
return src_i;
}
src_i++;
break;
}
size_t dst_i = 0;
while (true) {
size_t safe = (dst_sz - dst_i) / 4;
size_t end = GU_MIN(len, src_i + safe);
if (end == src_i) {
break;
}
do {
GuUCS ucs = src[src_i++];
dst_i += gu_advance_utf8(ucs, &dst[dst_i]);
} while (src_i < end);
}
gu_out_end_span(out, dst_i);
}
return src_i;
}
size_t
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err)
{
if (gu_out_is_buffered(out)) {
return gu_utf32_out_utf8_buffered_(src, len, out, err);
}
for (size_t i = 0; i < len; i++) {
gu_out_utf8(src[i], out, err);
if (!gu_ok(err)) {
return i;
}
}
return len;
}
#ifndef GU_CHAR_ASCII
void gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err)
{
size_t len = strlen(str);
size_t sz = 0;
uint8_t* buf = gu_out_begin_span(out, len, &sz, err);
if (!gu_ok(err)) {
return;
}
if (buf != NULL && sz < len) {
gu_out_end_span(out, 0);
buf = NULL;
}
GuPool* tmp_pool = buf ? NULL : gu_local_pool();
buf = buf ? buf : gu_new_n(uint8_t, len, tmp_pool);
for (size_t i = 0; i < len; i++) {
GuUCS ucs = gu_char_ucs(str[i]);
buf[i] = (uint8_t) ucs;
}
if (tmp_pool) {
gu_out_bytes(out, buf, len, err);
gu_pool_free(tmp_pool);
} else {
gu_out_end_span(out, len);
}
}
#endif
extern inline void
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err);
extern inline GuUCS
gu_in_utf8(GuIn* in, GuExn* err);
extern inline char
gu_in_utf8_char(GuIn* in, GuExn* err);

67
src/runtime/c/gu/utf8.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef GU_UTF8_H_
#define GU_UTF8_H_
#include <gu/in.h>
#include <gu/out.h>
#include <gu/ucs.h>
inline GuUCS
gu_in_utf8(GuIn* in, GuExn* err)
{
int i = gu_in_peek_u8(in);
if (i >= 0 && i < 0x80) {
gu_in_consume(in, 1);
return (GuUCS) i;
}
extern GuUCS gu_in_utf8_(GuIn* in, GuExn* err);
return gu_in_utf8_(in, err);
}
inline char
gu_in_utf8_char(GuIn* in, GuExn* err)
{
#ifdef GU_CHAR_ASCII
int i = gu_in_peek_u8(in);
if (i >= 0 && i < 0x80) {
gu_in_consume(in, 1);
return (char) i;
}
#endif
extern char gu_in_utf8_char_(GuIn* in, GuExn* err);
return gu_in_utf8_char_(in, err);
}
void
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err);
inline void
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err)
{
gu_require(gu_ucs_valid(ucs));
if (GU_LIKELY(ucs < 0x80)) {
gu_out_u8(out, ucs, err);
} else {
gu_out_utf8_long_(ucs, out, err);
}
}
size_t
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err);
GuUCS
gu_utf8_decode(const uint8_t** utf8);
inline void
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err)
{
#ifdef GU_CHAR_ASCII
gu_out_bytes(out, (const uint8_t*) str, strlen(str), err);
#else
extern void
gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err);
gu_str_out_utf8_(str, out, err);
#endif
}
#endif // GU_UTF8_H_

100
src/runtime/c/gu/variant.c Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
#include "variant.h"
#include "bits.h"
enum {
GU_VARIANT_ALIGNMENT = sizeof(uintptr_t)
};
void*
gu_alloc_variant(uint8_t tag, size_t size,
size_t align, GuVariant* variant_out, GuPool* pool)
{
align = gu_max(align, GU_VARIANT_ALIGNMENT);
if (((size_t)tag) > GU_VARIANT_ALIGNMENT - 2) {
uint8_t* alloc = gu_malloc_aligned(pool, align + size, align);
alloc[align - 1] = tag;
void* p = &alloc[align];
variant_out->p = (uintptr_t)p;
return p;
}
void* p = gu_malloc_aligned(pool, size, align);
variant_out->p = ((uintptr_t)p) | (tag + 1);
return p;
}
GuVariant
gu_make_variant(uint8_t tag, size_t size, size_t align, const void* init,
GuPool* pool)
{
GuVariant v;
void* data = gu_alloc_variant(tag, size, align, &v, pool);
memcpy(data, init, size);
return v;
}
int
gu_variant_tag(GuVariant variant)
{
if (gu_variant_is_null(variant)) {
return GU_VARIANT_NULL;
}
int u = variant.p % GU_VARIANT_ALIGNMENT;
if (u == 0) {
uint8_t* mem = (uint8_t*)variant.p;
return mem[-1];
}
return u - 1;
}
void*
gu_variant_data(GuVariant variant)
{
if (gu_variant_is_null(variant)) {
return NULL;
}
return (void*)gu_align_backward(variant.p, GU_VARIANT_ALIGNMENT);
}
GuVariantInfo gu_variant_open(GuVariant variant)
{
GuVariantInfo info = {
.tag = gu_variant_tag(variant),
.data = gu_variant_data(variant)
};
return info;
}
int
gu_variant_intval(GuVariant variant)
{
int u = variant.p % GU_VARIANT_ALIGNMENT;
if (u == 0) {
int* mem = (int*)variant.p;
return *mem;
}
return (variant.p / GU_VARIANT_ALIGNMENT);
}
const GuVariant gu_null_variant = { (GuWord) NULL };
GU_DEFINE_KIND(GuVariant, repr);
GU_DEFINE_KIND(GuVariantAsPtr, repr);

167
src/runtime/c/gu/variant.h Normal file
View File

@@ -0,0 +1,167 @@
/*
* Copyright 2010 University of Helsinki.
*
* This file is part of libgu.
*
* Libgu is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Libgu is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file
*
* Lightweight tagged data.
*/
#ifndef GU_VARIANT_H_
#define GU_VARIANT_H_
#include <gu/defs.h>
#include <gu/mem.h>
#include <gu/type.h>
/** @name Variants
* @{
*/
typedef struct GuVariant GuVariant;
void* gu_alloc_variant(uint8_t tag,
size_t size, size_t align,
GuVariant* variant_out, GuPool* pool);
GuVariant gu_make_variant(uint8_t tag,
size_t size, size_t align,
const void* init, GuPool* pool);
#define gu_new_variant(tag, type, variant_out, pool) \
((type*)gu_alloc_variant(tag, sizeof(type), \
gu_alignof(type), variant_out, pool))
/**<
* @hideinitializer */
#define gu_new_variant_i(POOL, TAG, T, ...) \
gu_make_variant(TAG, sizeof(T), gu_alignof(T), \
&(T){ __VA_ARGS__ }, POOL)
#define gu_new_flex_variant(tag, type, flex_mem, n_elems, variant_out, pool) \
((type*)gu_alloc_variant(tag, \
GU_FLEX_SIZE(type, flex_mem, n_elems), \
gu_flex_alignof(type), \
variant_out, pool))
/**<
* @hideinitializer */
enum {
GU_VARIANT_NULL = -1
};
int gu_variant_tag(GuVariant variant);
void* gu_variant_data(GuVariant variant);
typedef struct GuVariantInfo GuVariantInfo;
struct GuVariantInfo {
int tag;
void* data;
};
GuVariantInfo gu_variant_open(GuVariant variant);
/** @privatesection */
struct GuVariant {
uintptr_t p;
/**< @private */
};
/** @} */
static inline void*
gu_variant_to_ptr(GuVariant variant)
{
return (void*)variant.p;
}
static inline GuVariant
gu_variant_from_ptr(const void* p)
{
GuVariant v = { (uintptr_t)p };
return v;
}
extern const GuVariant gu_null_variant;
static inline bool
gu_variant_is_null(GuVariant v) {
return ((void*)v.p == NULL);
}
// variant
typedef const struct GuConstructor GuConstructor;
struct GuConstructor {
int c_tag;
const char* c_name;
const GuType* type;
};
#define GU_CONSTRUCTOR_V(ctag, c_type) { \
.c_tag = ctag, \
.c_name = #ctag, \
.type = c_type \
}
#define GU_CONSTRUCTOR(ctag, t_) \
GU_CONSTRUCTOR_V(ctag, gu_type(t_))
#define GU_CONSTRUCTOR_P(ctag, t_) \
GU_CONSTRUCTOR_V(ctag, gu_ptr_type(t_))
#define GU_CONSTRUCTOR_S(ctag, t_, ...) \
GU_CONSTRUCTOR_V(ctag, GU_TYPE_LIT(struct, t_, __VA_ARGS__))
#define GU_CONSTRUCTOR_S1(ctag, t_, mem1_, type1_) \
GU_CONSTRUCTOR_S(ctag, t_, \
GU_MEMBER(t_, mem1_, type1_))
#define GU_CONSTRUCTOR_S2(ctag, t_, mem1_, type1_, mem2_, type2_) \
GU_CONSTRUCTOR_S(ctag, t_, \
GU_MEMBER(t_, mem1_, type1_), \
GU_MEMBER(t_, mem2_, type2_))
typedef GuSList(GuConstructor) GuConstructors;
typedef const struct GuVariantType GuVariantType, GuType_GuVariant;
struct GuVariantType {
GuType_repr repr_base;
GuConstructors ctors;
};
#define GU_TYPE_INIT_GuVariant(k_, t_, ...) { \
.repr_base = GU_TYPE_INIT_repr(k_, GuVariant, _), \
.ctors = GU_SLIST(GuConstructor, __VA_ARGS__) \
}
extern GU_DECLARE_KIND(GuVariant);
#endif // GU_VARIANT_H_

174
src/runtime/c/gu/write.c Normal file
View File

@@ -0,0 +1,174 @@
#include <gu/write.h>
size_t
gu_utf32_write(const GuUCS* src, size_t len, GuWriter* wtr, GuExn* err)
{
return gu_utf32_out_utf8(src, len, &wtr->out_, err);
}
void
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err)
{
GuPool* tmp_pool = gu_local_pool();
char* str = gu_vasprintf(fmt, args, tmp_pool);
gu_puts(str, wtr, err);
gu_pool_free(tmp_pool);
}
void
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
gu_vprintf(fmt, args, wtr, err);
va_end(args);
}
GuWriter*
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool)
{
GuOutStream* stream = gu_out_proxy_stream(utf8_out, pool);
GuWriter* wtr = gu_new(GuWriter, pool);
wtr->out_ = gu_init_out(stream);
return wtr;
}
#if 0
#ifdef GU_UCS_WCHAR
#include <stdlib.h>
#include <wchar.h>
static const mbstate_t gu_init_mbstate; // implicitly initialized to zero
#endif
typedef struct GuLocaleWriter GuLocaleWriter;
struct GuLocaleWriter {
GuOutWriter owtr;
#ifdef GU_UCS_WCHAR
mbstate_t ps;
size_t mb_cur_max;
#endif
};
size_t
gu_locale_writer_write(GuWriter* wtr, const uint8_t* utf8_src, size_t sz,
GuExn* err)
{
GuLocaleWriter* lwtr = (GuLocaleWriter*) wtr;
size_t done = 0;
static const size_t bufsize = 256;
#ifdef GU_UCS_WCHAR
size_t margin = lwtr->mb_cur_max;
#else
size_t margin = 1;
#endif
GuOut* out = lwtr->owtr.out;
if (gu_out_is_buffered(out)) {
while (done < sz) {
size_t dst_sz;
uint8_t* dst = gu_out_begin_span(out, &dst_sz);
if (!dst) {
break;
}
if (dst_sz <= margin) {
gu_out_end_span(out, 0);
break;
}
size_t end = dst_sz - margin;
const uint8_t*
size_t n = done;
while (n < sz && dst_i <= end) {
#ifdef GU_UCS_WCHAR
GuUCS ucs = gu_
wchar_t wc = src[n];
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
#else
*p = (uint8_t) gu_ucs_char(buf[n], err);
size_t nb = 1;
if (!gu_ok(err)) {
gu_exn_clear(err);
nb = (size_t) -1;
}
#endif
if (nb == (size_t) -1) {
*p++ = (uint8_t) '?';
} else {
p += nb;
}
}
for (
}
}
uint8_t cbuf[256];
while (done < size && gu_ok(err)) {
uint8_t* p = cbuf;
uint8_t* edge = &cbuf[bufsize - margin];
size_t n;
for (n = done; p <= edge && n < size; n++) {
#ifdef GU_UCS_WCHAR
wchar_t wc = buf[n];
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
#else
*p = (uint8_t) gu_ucs_char(buf[n], err);
size_t nb = 1;
if (!gu_ok(err)) {
gu_exn_clear(err);
nb = (size_t) -1;
}
#endif
if (nb == (size_t) -1) {
*p++ = (uint8_t) '?';
} else {
p += nb;
}
}
gu_out_bytes(lwtr->owtr.out, cbuf, p - cbuf, err);
if (gu_ok(err)) {
done = n;
}
}
return done;
}
GuWriter*
gu_locale_writer(GuOut* out, GuPool* pool)
{
GuLocaleWriter* lwtr = gu_new_s(
pool, GuLocaleWriter,
.wtr.out.output = gu_locale_writer_output,
.wtr.out.flush = gu_locale_writer_flush,
.out = out);
#ifdef GU_UCS_WCHAR
lwtr->ps = gu_init_mbstate;
lwtr->mb_cur_max = MB_CUR_MAX;
#endif
return (GuWriter*) lwtr;
}
#endif
extern inline void
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err);
extern inline void
gu_writer_flush(GuWriter* wtr, GuExn* err);
extern inline void
gu_putc(char c, GuWriter* wtr, GuExn* err);
extern inline void
gu_puts(const char* str, GuWriter* wtr, GuExn* err);
extern inline size_t
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err);

64
src/runtime/c/gu/write.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef GU_WRITE_H_
#define GU_WRITE_H_
#include <gu/exn.h>
#include <gu/ucs.h>
#include <gu/out.h>
#include <gu/utf8.h>
typedef struct GuWriter GuWriter;
struct GuWriter {
GuOut out_;
};
size_t
gu_utf32_write(const GuUCS* buf, size_t size, GuWriter* wtr, GuExn* err);
inline void
gu_writer_flush(GuWriter* wtr, GuExn* err)
{
gu_out_flush(&wtr->out_, err);
}
inline void
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err)
{
gu_out_utf8(ucs, &wtr->out_, err);
}
inline void
gu_putc(char c, GuWriter* wtr, GuExn* err)
{
GuUCS ucs = gu_char_ucs(c);
gu_out_u8(&wtr->out_, (uint8_t) ucs, err);
}
inline void
gu_puts(const char* str, GuWriter* wtr, GuExn* err)
{
gu_str_out_utf8(str, &wtr->out_, err);
}
inline size_t
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err)
{
return gu_out_bytes(&wtr->out_, src, sz, err);
}
void
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err);
void
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...);
//GuWriter
//gu_init_utf8_writer(GuOut* utf8_out);
GuWriter*
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool);
GuWriter*
gu_make_locale_writer(GuOut* locale_out, GuPool* pool);
#endif // GU_WRITE_H_

339
src/runtime/c/gu/yaml.c Normal file
View File

@@ -0,0 +1,339 @@
#include <gu/yaml.h>
#include <gu/seq.h>
#include <gu/assert.h>
#include <gu/read.h>
#include <gu/ucs.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
const GuYamlAnchor gu_yaml_null_anchor = 0;
typedef const struct GuYamlState GuYamlState;
struct GuYamlState {
const char* prefix;
const char* suffix;
GuYamlState* next;
};
static const struct {
GuYamlState document, first_key, key, value, first_elem, elem;
} gu_yaml_states = {
.document = {
.prefix = "---\n",
.suffix = "\n...\n",
.next = &gu_yaml_states.document,
},
.key = {
.prefix = "? ",
.next = &gu_yaml_states.value,
},
.value = {
.prefix = ": ",
.suffix = ",",
.next = &gu_yaml_states.key,
},
.elem = {
.suffix = ",",
.next = &gu_yaml_states.elem,
},
};
typedef const struct GuYamlFrameClass GuYamlFrameClass;
struct GuYamlFrameClass {
const char* open;
GuYamlState* first;
const char* close;
};
static const struct {
GuYamlFrameClass document, mapping, sequence;
} gu_yaml_frame_classes = {
.mapping = {
.open = "{",
.first = &gu_yaml_states.key,
.close = "}",
},
.sequence = {
.open = "[",
.first = &gu_yaml_states.elem,
.close = "]",
},
};
typedef struct GuYamlFrame GuYamlFrame;
struct GuYamlFrame {
GuYamlFrameClass* klass;
GuYamlState* next;
};
typedef GuBuf GuYamlStack;
struct GuYaml {
GuWriter* wtr;
GuExn* err;
GuPool* pool;
GuYamlState* state;
bool in_node;
bool have_anchor;
bool have_tag;
int next_anchor;
bool indent;
int indent_level;
bool indented;
GuYamlStack* stack;
};
GuYaml*
gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool)
{
GuYaml* yaml = gu_new(GuYaml, pool);
yaml->wtr = wtr;
yaml->pool = pool;
yaml->err = err;
yaml->state = &gu_yaml_states.document;
yaml->in_node = false;
yaml->have_anchor = false;
yaml->have_tag = false;
yaml->next_anchor = 1;
yaml->stack = gu_new_buf(GuYamlFrame, pool);
yaml->indent = true;
yaml->indent_level = 0;
yaml->indented = false;
return yaml;
}
static void
gu_yaml_printf(GuYaml* yaml, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
gu_vprintf(fmt, args, yaml->wtr, yaml->err);
va_end(args);
}
static void
gu_yaml_putc(GuYaml* yaml, char c)
{
gu_putc(c, yaml->wtr, yaml->err);
}
static void
gu_yaml_puts(GuYaml* yaml, const char* str)
{
gu_puts(str, yaml->wtr, yaml->err);
}
static void
gu_yaml_begin_line(GuYaml* yaml)
{
if (yaml->indent && !yaml->indented) {
for (int i = 0; i < yaml->indent_level; i++) {
gu_yaml_putc(yaml, ' ');
}
yaml->indented = true;
}
}
static void
gu_yaml_end_line(GuYaml* yaml)
{
if (yaml->indent) {
gu_yaml_putc(yaml, '\n');
}
yaml->indented = false;
}
static void
gu_yaml_begin_node(GuYaml* yaml)
{
gu_yaml_begin_line(yaml);
if (!yaml->in_node) {
if (yaml->state->prefix != NULL) {
gu_yaml_puts(yaml, yaml->state->prefix);
}
yaml->in_node = true;
}
}
static void
gu_yaml_end_node(GuYaml* yaml)
{
gu_assert(yaml->in_node);
if (yaml->state->suffix != NULL) {
gu_yaml_puts(yaml, yaml->state->suffix);
}
gu_yaml_end_line(yaml);
yaml->in_node = false;
yaml->have_anchor = false;
yaml->have_tag = false;
if (yaml->state != NULL) {
yaml->state = yaml->state->next;
}
}
static void
gu_yaml_begin(GuYaml* yaml, GuYamlFrameClass* klass)
{
gu_yaml_begin_node(yaml);
gu_yaml_puts(yaml, klass->open);
gu_buf_push(yaml->stack, GuYamlFrame,
((GuYamlFrame) { .klass = klass, .next = yaml->state}));
yaml->state = klass->first;
yaml->in_node = yaml->have_anchor = yaml->have_tag = false;
gu_yaml_end_line(yaml);
yaml->indent_level++;
}
void
gu_yaml_begin_mapping(GuYaml* yaml)
{
gu_yaml_begin(yaml, &gu_yaml_frame_classes.mapping);
}
void
gu_yaml_begin_sequence(GuYaml* yaml)
{
gu_yaml_begin(yaml, &gu_yaml_frame_classes.sequence);
}
void
gu_yaml_end(GuYaml* yaml)
{
gu_assert(!yaml->in_node);
yaml->indent_level--;
gu_yaml_begin_line(yaml);
GuYamlFrame f = gu_buf_pop(yaml->stack, GuYamlFrame);
gu_yaml_puts(yaml, f.klass->close);
yaml->state = f.next;
yaml->in_node = true;
gu_yaml_end_node(yaml);
}
void
gu_yaml_scalar(GuYaml* yaml, GuString s)
{
gu_yaml_begin_node(yaml);
gu_yaml_putc(yaml, '"');
GuPool* tmp_pool = gu_local_pool();
GuReader* rdr = gu_string_reader(s, tmp_pool);
GuExn* err = gu_exn(yaml->err, GuEOF, NULL);
static const char esc[0x20] = {
[0x00] = '0',
[0x07] = 'a', 'b', 't', 'n', 'v', 'f', 'r',
[0x1b] = 'e'
};
while (true) {
GuUCS u = gu_read_ucs(rdr, err);
if (!gu_ok(err)) {
break;
}
if (GU_LIKELY(u >= 0x20 && u < 0x7f)) {
if (GU_UNLIKELY(u == 0x22 || u == 0x5c)) {
gu_yaml_putc(yaml, '\\');
}
gu_ucs_write(u, yaml->wtr, yaml->err);
} else if (GU_UNLIKELY(u < 0x20 && esc[u])) {
gu_yaml_printf(yaml, "\\%c", esc[u]);
} else if (GU_UNLIKELY(u <= 0x9f)) {
gu_yaml_printf(yaml, "\\x%02x", (unsigned) u);
} else if (GU_UNLIKELY((u >= 0xd800 && u <= 0xdfff) ||
(u >= 0xfffe && u <= 0xffff))) {
gu_yaml_printf(yaml, "\\u%04x", (unsigned) u);
} else {
gu_ucs_write(u, yaml->wtr, yaml->err);
}
}
gu_pool_free(tmp_pool);
gu_yaml_putc(yaml, '"');
gu_yaml_end_node(yaml);
}
static void
gu_yaml_tag(GuYaml* yaml, const char* format, ...)
{
gu_yaml_begin_node(yaml);
gu_assert(!yaml->have_tag);
gu_yaml_putc(yaml, '!');
va_list args;
va_start(args, format);
gu_vprintf(format, args, yaml->wtr, yaml->err);
va_end(args);
gu_yaml_putc(yaml, ' ');
yaml->have_tag = true;
}
void
gu_yaml_tag_primary(GuYaml* yaml, const char* tag)
{
// TODO: check tag validity
gu_yaml_tag(yaml, "%s", tag);
}
void
gu_yaml_tag_secondary(GuYaml* yaml, const char* tag)
{
// TODO: check tag validity
gu_yaml_tag(yaml, "!%s", tag);
}
void
gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag)
{
// TODO: check tag validity
gu_yaml_tag(yaml, "%s!%s", handle, tag);
}
void
gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri)
{
// XXX: uri escaping?
gu_yaml_tag(yaml, "<%s>", uri);
}
void
gu_yaml_tag_non_specific(GuYaml* yaml)
{
gu_yaml_tag(yaml, "");
}
GuYamlAnchor
gu_yaml_anchor(GuYaml* yaml)
{
gu_yaml_begin_node(yaml);
gu_assert(!yaml->have_anchor);
yaml->have_anchor = true;
int anchor = yaml->next_anchor++;
gu_yaml_printf(yaml, "&%d ", anchor);
return anchor;
}
void
gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor)
{
gu_yaml_begin_node(yaml);
gu_assert(!yaml->have_anchor && !yaml->have_tag);
gu_yaml_printf(yaml, "*%d ", anchor);
gu_yaml_end_node(yaml);
return;
}
void gu_yaml_comment(GuYaml* yaml, GuString s)
{
gu_yaml_begin_line(yaml);
gu_yaml_puts(yaml, "# ");
// TODO: verify no newlines in comment
gu_string_write(s, yaml->wtr, yaml->err);
gu_yaml_puts(yaml, "\n");
yaml->indented = false;
}

38
src/runtime/c/gu/yaml.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef GU_YAML_H_
#define GU_YAML_H_
#include <gu/mem.h>
#include <gu/write.h>
#include <gu/string.h>
typedef struct GuYaml GuYaml;
typedef int GuYamlAnchor;
extern const GuYamlAnchor gu_yaml_null_anchor;
GuYaml* gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool);
GuYamlAnchor gu_yaml_anchor(GuYaml* yaml);
void gu_yaml_tag_primary(GuYaml* yaml, const char* tag);
void gu_yaml_tag_secondary(GuYaml* yaml, const char* tag);
void gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag);
void gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri);
void gu_yaml_tag_non_specific(GuYaml* yaml);
void gu_yaml_comment(GuYaml* yaml, GuString comment);
void gu_yaml_scalar(GuYaml* yaml, GuString scalar);
void gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor);
void gu_yaml_begin_document(GuYaml* yaml);
void gu_yaml_begin_sequence(GuYaml* yaml);
void gu_yaml_begin_mapping(GuYaml* yaml);
void gu_yaml_end(GuYaml* yaml);
#endif // GU_YAML_H_