diff options
| author | kr.angelov <kr.angelov@gmail.com> | 2012-01-20 13:41:10 +0000 |
|---|---|---|
| committer | kr.angelov <kr.angelov@gmail.com> | 2012-01-20 13:41:10 +0000 |
| commit | 2eee382a62a909d5a3f2f5eda94f30fe68fd5335 (patch) | |
| tree | b0b0d513535895f244214aebf6358e172b8dce6d /src/runtime/c/gu | |
| parent | b9728357126f8b9a6311cca17d9f0dcc2a7bfb9b (diff) | |
initial import of the C runtime
Diffstat (limited to 'src/runtime/c/gu')
57 files changed, 7468 insertions, 0 deletions
diff --git a/src/runtime/c/gu/assert.c b/src/runtime/c/gu/assert.c new file mode 100644 index 000000000..e111912d3 --- /dev/null +++ b/src/runtime/c/gu/assert.c @@ -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(); +} diff --git a/src/runtime/c/gu/assert.h b/src/runtime/c/gu/assert.h new file mode 100644 index 000000000..9d7ecc15f --- /dev/null +++ b/src/runtime/c/gu/assert.h @@ -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_ */ diff --git a/src/runtime/c/gu/bits.c b/src/runtime/c/gu/bits.c new file mode 100644 index 000000000..9126b0448 --- /dev/null +++ b/src/runtime/c/gu/bits.c @@ -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; +} diff --git a/src/runtime/c/gu/bits.h b/src/runtime/c/gu/bits.h new file mode 100644 index 000000000..9ba1b0c8e --- /dev/null +++ b/src/runtime/c/gu/bits.h @@ -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_ diff --git a/src/runtime/c/gu/choice.c b/src/runtime/c/gu/choice.c new file mode 100644 index 000000000..b1e4a3a5e --- /dev/null +++ b/src/runtime/c/gu/choice.c @@ -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; +} diff --git a/src/runtime/c/gu/choice.h b/src/runtime/c/gu/choice.h new file mode 100644 index 000000000..aea76dde8 --- /dev/null +++ b/src/runtime/c/gu/choice.h @@ -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_ diff --git a/src/runtime/c/gu/defs.c b/src/runtime/c/gu/defs.c new file mode 100644 index 000000000..2c3772b7c --- /dev/null +++ b/src/runtime/c/gu/defs.c @@ -0,0 +1,4 @@ +#include <gu/defs.h> + +void* const gu_null = NULL; +GuStruct* const gu_null_struct = NULL; diff --git a/src/runtime/c/gu/defs.h b/src/runtime/c/gu/defs.h new file mode 100644 index 000000000..9fa62b39b --- /dev/null +++ b/src/runtime/c/gu/defs.h @@ -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_ diff --git a/src/runtime/c/gu/dump.c b/src/runtime/c/gu/dump.c new file mode 100644 index 000000000..0329f5932 --- /dev/null +++ b/src/runtime/c/gu/dump.c @@ -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) }, + ); diff --git a/src/runtime/c/gu/dump.h b/src/runtime/c/gu/dump.h new file mode 100644 index 000000000..7d6f10a77 --- /dev/null +++ b/src/runtime/c/gu/dump.h @@ -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_ diff --git a/src/runtime/c/gu/enum.c b/src/runtime/c/gu/enum.c new file mode 100644 index 000000000..fa8595a55 --- /dev/null +++ b/src/runtime/c/gu/enum.c @@ -0,0 +1,7 @@ +#include <gu/enum.h> + +void +gu_enum_next(GuEnum* en, void* to, GuPool* pool) +{ + en->next(en, to, pool); +} diff --git a/src/runtime/c/gu/enum.h b/src/runtime/c/gu/enum.h new file mode 100644 index 000000000..88cd5ba6a --- /dev/null +++ b/src/runtime/c/gu/enum.h @@ -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_ */ diff --git a/src/runtime/c/gu/exn.c b/src/runtime/c/gu/exn.c new file mode 100644 index 000000000..7bcfeb088 --- /dev/null +++ b/src/runtime/c/gu/exn.c @@ -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, _); diff --git a/src/runtime/c/gu/exn.h b/src/runtime/c/gu/exn.h new file mode 100644 index 000000000..66ca107e3 --- /dev/null +++ b/src/runtime/c/gu/exn.h @@ -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_ diff --git a/src/runtime/c/gu/file.c b/src/runtime/c/gu/file.c new file mode 100644 index 000000000..ed1956537 --- /dev/null +++ b/src/runtime/c/gu/file.c @@ -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); +} diff --git a/src/runtime/c/gu/file.h b/src/runtime/c/gu/file.h new file mode 100644 index 000000000..1bb3fb00a --- /dev/null +++ b/src/runtime/c/gu/file.h @@ -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_ diff --git a/src/runtime/c/gu/fun.c b/src/runtime/c/gu/fun.c new file mode 100644 index 000000000..ca777c490 --- /dev/null +++ b/src/runtime/c/gu/fun.c @@ -0,0 +1 @@ +#include <gu/fun.h> diff --git a/src/runtime/c/gu/fun.h b/src/runtime/c/gu/fun.h new file mode 100644 index 000000000..0004e9923 --- /dev/null +++ b/src/runtime/c/gu/fun.h @@ -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_ diff --git a/src/runtime/c/gu/hash.c b/src/runtime/c/gu/hash.c new file mode 100644 index 000000000..1666263e6 --- /dev/null +++ b/src/runtime/c/gu/hash.c @@ -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 + } +}; diff --git a/src/runtime/c/gu/hash.h b/src/runtime/c/gu/hash.h new file mode 100644 index 000000000..e16c2f454 --- /dev/null +++ b/src/runtime/c/gu/hash.h @@ -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_ diff --git a/src/runtime/c/gu/in.c b/src/runtime/c/gu/in.c new file mode 100644 index 000000000..4238475f8 --- /dev/null +++ b/src/runtime/c/gu/in.c @@ -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); diff --git a/src/runtime/c/gu/in.h b/src/runtime/c/gu/in.h new file mode 100644 index 000000000..c85a6581b --- /dev/null +++ b/src/runtime/c/gu/in.h @@ -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_ diff --git a/src/runtime/c/gu/intern.c b/src/runtime/c/gu/intern.c new file mode 100644 index 000000000..35eaa1c5f --- /dev/null +++ b/src/runtime/c/gu/intern.c @@ -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; +} diff --git a/src/runtime/c/gu/intern.h b/src/runtime/c/gu/intern.h new file mode 100644 index 000000000..bf9e9b321 --- /dev/null +++ b/src/runtime/c/gu/intern.h @@ -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_ */ diff --git a/src/runtime/c/gu/list.c b/src/runtime/c/gu/list.c new file mode 100644 index 000000000..d98a42e41 --- /dev/null +++ b/src/runtime/c/gu/list.c @@ -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)]; +} diff --git a/src/runtime/c/gu/list.h b/src/runtime/c/gu/list.h new file mode 100644 index 000000000..f2add157f --- /dev/null +++ b/src/runtime/c/gu/list.h @@ -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_ diff --git a/src/runtime/c/gu/log.c b/src/runtime/c/gu/log.c new file mode 100644 index 000000000..399646c50 --- /dev/null +++ b/src/runtime/c/gu/log.c @@ -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); +} + diff --git a/src/runtime/c/gu/log.h b/src/runtime/c/gu/log.h new file mode 100644 index 000000000..ec9ecdf75 --- /dev/null +++ b/src/runtime/c/gu/log.h @@ -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_ diff --git a/src/runtime/c/gu/map.c b/src/runtime/c/gu/map.c new file mode 100644 index 000000000..2ee77bb23 --- /dev/null +++ b/src/runtime/c/gu/map.c @@ -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); + + diff --git a/src/runtime/c/gu/map.h b/src/runtime/c/gu/map.h new file mode 100644 index 000000000..6523a8057 --- /dev/null +++ b/src/runtime/c/gu/map.h @@ -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_ diff --git a/src/runtime/c/gu/mem.c b/src/runtime/c/gu/mem.c new file mode 100644 index 000000000..649105a6a --- /dev/null +++ b/src/runtime/c/gu/mem.c @@ -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); + diff --git a/src/runtime/c/gu/mem.h b/src/runtime/c/gu/mem.h new file mode 100644 index 000000000..4369f4036 --- /dev/null +++ b/src/runtime/c/gu/mem.h @@ -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_ diff --git a/src/runtime/c/gu/out.c b/src/runtime/c/gu/out.c new file mode 100644 index 000000000..61df2c184 --- /dev/null +++ b/src/runtime/c/gu/out.c @@ -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); +} + + diff --git a/src/runtime/c/gu/out.h b/src/runtime/c/gu/out.h new file mode 100644 index 000000000..df2638c72 --- /dev/null +++ b/src/runtime/c/gu/out.h @@ -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_ diff --git a/src/runtime/c/gu/prime.c b/src/runtime/c/gu/prime.c new file mode 100644 index 000000000..6452f8777 --- /dev/null +++ b/src/runtime/c/gu/prime.c @@ -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; +} + diff --git a/src/runtime/c/gu/prime.h b/src/runtime/c/gu/prime.h new file mode 100644 index 000000000..2ae0617f8 --- /dev/null +++ b/src/runtime/c/gu/prime.h @@ -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_ diff --git a/src/runtime/c/gu/read.c b/src/runtime/c/gu/read.c new file mode 100644 index 000000000..245c59f2c --- /dev/null +++ b/src/runtime/c/gu/read.c @@ -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; +} diff --git a/src/runtime/c/gu/read.h b/src/runtime/c/gu/read.h new file mode 100644 index 000000000..f2496975d --- /dev/null +++ b/src/runtime/c/gu/read.h @@ -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_ diff --git a/src/runtime/c/gu/seq.c b/src/runtime/c/gu/seq.c new file mode 100644 index 000000000..660cf5af6 --- /dev/null +++ b/src/runtime/c/gu/seq.c @@ -0,0 +1,245 @@ +#include <gu/out.h> +#include <gu/seq.h> +#include <gu/fun.h> +#include <gu/assert.h> +#include <string.h> + + +struct GuBuf { + uint8_t* data; + size_t elem_size; + size_t avail_len; + GuFinalizer fin; +}; + +GuBuf* +gu_seq_buf(GuSeq seq) +{ + gu_require(gu_tagged_tag(seq.w_) == 0); + return gu_word_ptr(seq.w_); +} + +GuSeq +gu_buf_seq(GuBuf* buf) +{ + return (GuSeq) { .w_ = gu_ptr_word(buf) }; +} + +size_t +gu_buf_length(GuBuf* dyn) +{ + return (size_t)(((GuWord*)(void*)dyn)[-1] >> 1); +} + +size_t +gu_buf_avail(GuBuf* buf) +{ + return buf->avail_len; +} + + +static void +gu_buf_set_length(GuBuf* dyn, size_t new_len) +{ + ((GuWord*)(void*)dyn)[-1] = ((GuWord) new_len) << 1 | 0x1; +} + +static void +gu_buf_fini(GuFinalizer* fin) +{ + GuBuf* buf = gu_container(fin, GuBuf, fin); + gu_mem_buf_free(buf->data); +} + +GuBuf* +gu_make_buf(size_t elem_size, GuPool* pool) +{ + GuBuf* buf = gu_new_prefixed(unsigned, GuBuf, pool); + gu_buf_set_length(buf, 0); + buf->elem_size = elem_size; + buf->data = NULL; + buf->avail_len = 0; + buf->fin.fn = gu_buf_fini; + gu_pool_finally(pool, &buf->fin); + gu_buf_set_length(buf, 0); + return buf; +} + +static const GuWord gu_empty_seq[2] = {0, 0}; + +GuSeq +gu_make_seq(size_t elem_size, size_t length, GuPool* pool) +{ + size_t size = elem_size * length; + if (0 < length && length <= GU_TAG_MAX) { + void* buf = gu_malloc(pool, size); + return (GuSeq) { gu_tagged(buf, length) }; + } else if (size == 0) { + return (GuSeq) { gu_tagged((void*)&gu_empty_seq[1], 0) }; + } else { + void* buf = gu_malloc_prefixed(pool, + gu_alignof(GuWord), + sizeof(GuWord), + 0, size); + ((GuWord*) buf)[-1] = ((GuWord) length) << 1; + return (GuSeq) { gu_tagged(buf, 0) }; + } +} + +static void +gu_buf_require(GuBuf* buf, size_t req_len) +{ + if (req_len <= buf->avail_len) { + return; + } + size_t req_size = buf->elem_size * req_len; + size_t real_size; + buf->data = gu_mem_buf_realloc(buf->data, req_size, + &real_size); + buf->avail_len = real_size / buf->elem_size; +} + +void* +gu_buf_data(GuBuf* buf) +{ + return buf->data; +} + +void* +gu_buf_extend_n(GuBuf* buf, size_t n_elems) +{ + size_t len = gu_buf_length(buf); + size_t new_len = len + n_elems; + gu_buf_require(buf, new_len); + gu_buf_set_length(buf, new_len); + return &buf->data[buf->elem_size * len]; +} + +void* +gu_buf_extend(GuBuf* buf) +{ + return gu_buf_extend_n(buf, 1); +} + +void +gu_buf_push_n(GuBuf* buf, const void* data, size_t n_elems) +{ + + void* p = gu_buf_extend_n(buf, n_elems); + memcpy(p, data, buf->elem_size * n_elems); +} + +const void* +gu_buf_trim_n(GuBuf* buf, size_t n_elems) +{ + gu_require(n_elems <= gu_buf_length(buf)); + size_t new_len = gu_buf_length(buf) - n_elems; + gu_buf_set_length(buf, new_len); + return &buf->data[buf->elem_size * new_len]; +} + +const void* +gu_buf_trim(GuBuf* buf) +{ + return gu_buf_trim_n(buf, 1); +} + +void +gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out) +{ + const void* p = gu_buf_trim_n(buf, n_elems); + memcpy(data_out, p, buf->elem_size * n_elems); +} + +GuSeq +gu_buf_freeze(GuBuf* buf, GuPool* pool) +{ + size_t len = gu_buf_length(buf); + GuSeq seq = gu_make_seq(buf->elem_size, len, pool); + void* bufdata = gu_buf_data(buf); + void* seqdata = gu_seq_data(seq); + memcpy(seqdata, bufdata, buf->elem_size * len); + return seq; +} + +typedef struct GuBufOut GuBufOut; +struct GuBufOut +{ + GuOutStream stream; + GuBuf* buf; +}; + +static size_t +gu_buf_out_output(GuOutStream* stream, const uint8_t* src, size_t sz, + GuExn* err) +{ + (void) err; + GuBufOut* bout = gu_container(stream, GuBufOut, stream); + GuBuf* buf = bout->buf; + gu_assert(sz % buf->elem_size == 0); + size_t len = sz / buf->elem_size; + gu_buf_push_n(bout->buf, src, len); + return len; +} + +static uint8_t* +gu_buf_outbuf_begin(GuOutStream* stream, size_t req, size_t* sz_out, GuExn* err) +{ + (void) req; + (void) err; + GuBufOut* bout = gu_container(stream, GuBufOut, stream); + GuBuf* buf = bout->buf; + size_t esz = buf->elem_size; + size_t len = gu_buf_length(buf); + gu_buf_require(buf, len + (req + esz - 1) / esz); + size_t avail = buf->avail_len; + gu_assert(len < avail); + *sz_out = esz * (avail - len); + return &buf->data[len * esz]; +} + +static void +gu_buf_outbuf_end(GuOutStream* stream, size_t sz, GuExn* err) +{ + (void) err; + GuBufOut* bout = gu_container(stream, GuBufOut, stream); + GuBuf* buf = bout->buf; + size_t len = gu_buf_length(buf); + size_t elem_size = buf->elem_size; + gu_require(sz % elem_size == 0); + gu_require(sz < elem_size * (len - buf->avail_len)); + gu_buf_set_length(buf, len + (sz / elem_size)); +} + +GuOut* +gu_buf_out(GuBuf* buf, GuPool* pool) +{ + GuBufOut* bout = gu_new_i(pool, GuBufOut, + .stream.output = gu_buf_out_output, + .stream.begin_buf = gu_buf_outbuf_begin, + .stream.end_buf = gu_buf_outbuf_end, + .buf = buf); + return gu_new_out(&bout->stream, pool); +} + +const GuSeq +gu_null_seq = GU_NULL_SEQ; + + +#include <gu/type.h> + +GU_DEFINE_KIND(GuSeq, GuOpaque); +GU_DEFINE_KIND(GuBuf, abstract); + +GU_DEFINE_TYPE(GuChars, GuSeq, gu_type(char)); +GU_DEFINE_TYPE(GuBytes, GuSeq, gu_type(uint8_t)); + +char* +gu_chars_str(GuChars chars, GuPool* pool) +{ + size_t len = gu_seq_length(chars); + char* data = gu_seq_data(chars); + char* str = gu_new_str(len, pool); + memcpy(str, data, len); + return str; +} diff --git a/src/runtime/c/gu/seq.h b/src/runtime/c/gu/seq.h new file mode 100644 index 000000000..257d71e5f --- /dev/null +++ b/src/runtime/c/gu/seq.h @@ -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 + diff --git a/src/runtime/c/gu/str.c b/src/runtime/c/gu/str.c new file mode 100644 index 000000000..073781b23 --- /dev/null +++ b/src/runtime/c/gu/str.c @@ -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; +} diff --git a/src/runtime/c/gu/str.h b/src/runtime/c/gu/str.h new file mode 100644 index 000000000..d40f57b08 --- /dev/null +++ b/src/runtime/c/gu/str.h @@ -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_ diff --git a/src/runtime/c/gu/string.c b/src/runtime/c/gu/string.c new file mode 100644 index 000000000..b24eeeb06 --- /dev/null +++ b/src/runtime/c/gu/string.c @@ -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); diff --git a/src/runtime/c/gu/string.h b/src/runtime/c/gu/string.h new file mode 100644 index 000000000..385b162ed --- /dev/null +++ b/src/runtime/c/gu/string.h @@ -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 + diff --git a/src/runtime/c/gu/sysdeps.h b/src/runtime/c/gu/sysdeps.h new file mode 100644 index 000000000..114e1c40d --- /dev/null +++ b/src/runtime/c/gu/sysdeps.h @@ -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_ diff --git a/src/runtime/c/gu/type.c b/src/runtime/c/gu/type.c new file mode 100644 index 000000000..13fbfa42d --- /dev/null +++ b/src/runtime/c/gu/type.c @@ -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 diff --git a/src/runtime/c/gu/type.h b/src/runtime/c/gu/type.h new file mode 100644 index 000000000..777b2e1f9 --- /dev/null +++ b/src/runtime/c/gu/type.h @@ -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_ diff --git a/src/runtime/c/gu/ucs.c b/src/runtime/c/gu/ucs.c new file mode 100644 index 000000000..34649f36a --- /dev/null +++ b/src/runtime/c/gu/ucs.c @@ -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); diff --git a/src/runtime/c/gu/ucs.h b/src/runtime/c/gu/ucs.h new file mode 100644 index 000000000..f1662a602 --- /dev/null +++ b/src/runtime/c/gu/ucs.h @@ -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_ diff --git a/src/runtime/c/gu/utf8.c b/src/runtime/c/gu/utf8.c new file mode 100644 index 000000000..a416c2dac --- /dev/null +++ b/src/runtime/c/gu/utf8.c @@ -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); diff --git a/src/runtime/c/gu/utf8.h b/src/runtime/c/gu/utf8.h new file mode 100644 index 000000000..053d8664e --- /dev/null +++ b/src/runtime/c/gu/utf8.h @@ -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_ diff --git a/src/runtime/c/gu/variant.c b/src/runtime/c/gu/variant.c new file mode 100644 index 000000000..62fdb92c1 --- /dev/null +++ b/src/runtime/c/gu/variant.c @@ -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); diff --git a/src/runtime/c/gu/variant.h b/src/runtime/c/gu/variant.h new file mode 100644 index 000000000..a9bb10a4a --- /dev/null +++ b/src/runtime/c/gu/variant.h @@ -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_ diff --git a/src/runtime/c/gu/write.c b/src/runtime/c/gu/write.c new file mode 100644 index 000000000..69573fb0d --- /dev/null +++ b/src/runtime/c/gu/write.c @@ -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); + diff --git a/src/runtime/c/gu/write.h b/src/runtime/c/gu/write.h new file mode 100644 index 000000000..414a7bc2c --- /dev/null +++ b/src/runtime/c/gu/write.h @@ -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_ diff --git a/src/runtime/c/gu/yaml.c b/src/runtime/c/gu/yaml.c new file mode 100644 index 000000000..4d769a4aa --- /dev/null +++ b/src/runtime/c/gu/yaml.c @@ -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; +} + diff --git a/src/runtime/c/gu/yaml.h b/src/runtime/c/gu/yaml.h new file mode 100644 index 000000000..5a61d0786 --- /dev/null +++ b/src/runtime/c/gu/yaml.h @@ -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_ |
