mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-05-03 16:22:52 -06:00
initial import of the C runtime
This commit is contained in:
53
src/runtime/c/gu/assert.c
Normal file
53
src/runtime/c/gu/assert.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <gu/assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
const char*
|
||||
gu_assert_mode_descs[] = {
|
||||
[GU_ASSERT_PRECOND] = "precondition failed",
|
||||
[GU_ASSERT_POSTCOND] = "postcondition failed",
|
||||
[GU_ASSERT_ASSERTION] = "assertion failed",
|
||||
[GU_ASSERT_NEVER] = "control should not reach here",
|
||||
};
|
||||
|
||||
void
|
||||
gu_abort_v_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, va_list args)
|
||||
{
|
||||
const char* desc = gu_assert_mode_descs[mode];
|
||||
(void) fprintf(stderr, "%s (%s:%d): %s\n", func, file, line, desc);
|
||||
if (msg_fmt != NULL) {
|
||||
(void) fputc('\t', stderr);
|
||||
(void) vfprintf(stderr, msg_fmt, args);
|
||||
(void) fputc('\n', stderr);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
gu_abort_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, msg_fmt);
|
||||
gu_abort_v_(mode, file, func, line, msg_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_fatal(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fputs("Fatal error", stderr);
|
||||
if (fmt) {
|
||||
fputs(": ", stderr);
|
||||
(void) vfprintf(stderr, fmt, args);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
61
src/runtime/c/gu/assert.h
Normal file
61
src/runtime/c/gu/assert.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef GU_ASSERT_H_
|
||||
#define GU_ASSERT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
typedef enum {
|
||||
GU_ASSERT_PRECOND,
|
||||
GU_ASSERT_ASSERTION,
|
||||
GU_ASSERT_POSTCOND,
|
||||
GU_ASSERT_NEVER
|
||||
} GuAssertMode;
|
||||
|
||||
void
|
||||
gu_abort_v_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, va_list args);
|
||||
|
||||
void
|
||||
gu_abort_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, ...);
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define gu_assertion_(mode_, expr_, ...) \
|
||||
GU_BEGIN \
|
||||
if (!(expr_)) { \
|
||||
gu_abort_(mode_, __FILE__, __func__, __LINE__, __VA_ARGS__); \
|
||||
} \
|
||||
GU_END
|
||||
#else
|
||||
// this should prevent unused variable warnings when a variable is only used
|
||||
// in an assertion
|
||||
#define gu_assertion_(mode_, expr_, ...) \
|
||||
GU_BEGIN \
|
||||
(void) (sizeof (expr_)); \
|
||||
GU_END
|
||||
#endif
|
||||
|
||||
|
||||
#define gu_require(expr) \
|
||||
gu_assertion_(GU_ASSERT_PRECOND, expr, "%s", #expr)
|
||||
|
||||
#define gu_assert_msg(expr, ...) \
|
||||
gu_assertion_(GU_ASSERT_ASSERTION, expr, __VA_ARGS__)
|
||||
|
||||
#define gu_assert(expr) \
|
||||
gu_assert_msg(expr, "%s", #expr)
|
||||
|
||||
#define gu_ensure(expr) \
|
||||
gu_assertion_(GU_ASSERT_POSTCOND, expr, "%s", #expr)
|
||||
|
||||
#define gu_impossible_msg(...) \
|
||||
gu_assertion_(GU_ASSERT_ASSERTION, false, __VA_ARGS__)
|
||||
|
||||
#define gu_impossible() \
|
||||
gu_impossible_msg(NULL)
|
||||
|
||||
void
|
||||
gu_fatal(const char* fmt, ...);
|
||||
|
||||
#endif /* GU_ASSERT_H_ */
|
||||
53
src/runtime/c/gu/bits.c
Normal file
53
src/runtime/c/gu/bits.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <gu/bits.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
unsigned gu_ceil2e(unsigned u)
|
||||
{
|
||||
u--;
|
||||
u |= u >> 1;
|
||||
u |= u >> 2;
|
||||
u |= u >> 4;
|
||||
u |= u >> 8;
|
||||
#if UINT_MAX > UINT16_MAX
|
||||
u |= u >> 16;
|
||||
#endif
|
||||
#if UINT_MAX > UINT32_MAX
|
||||
u |= u >> 32;
|
||||
#endif
|
||||
u++;
|
||||
return u;
|
||||
}
|
||||
|
||||
GU_DEFINE_TYPE(GuIntDecodeExn, abstract, _);
|
||||
|
||||
double
|
||||
gu_decode_double(uint64_t u)
|
||||
{
|
||||
bool sign = u >> 63;
|
||||
unsigned rawexp = u >> 52 & 0x7ff;
|
||||
uint64_t mantissa = u & 0xfffffffffffff;
|
||||
double ret;
|
||||
|
||||
if (rawexp == 0x7ff) {
|
||||
if (mantissa == 0) {
|
||||
ret = INFINITY;
|
||||
} else {
|
||||
// At least glibc supports specifying the
|
||||
// mantissa like this.
|
||||
int len = snprintf(NULL, 0, "0x%" PRIx64, mantissa);
|
||||
char buf[len + 1];
|
||||
snprintf(buf, len + 1, "0x%" PRIx64, mantissa);
|
||||
ret = nan(buf);
|
||||
}
|
||||
} else {
|
||||
uint64_t m = rawexp ? 1ULL << 52 | mantissa : mantissa << 1;
|
||||
ret = ldexp((double) m, rawexp - 1075);
|
||||
}
|
||||
return sign ? copysign(ret, -1.0) : ret;
|
||||
}
|
||||
151
src/runtime/c/gu/bits.h
Normal file
151
src/runtime/c/gu/bits.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef GU_BITS_H_
|
||||
#define GU_BITS_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
#define GU_WORD_BITS (sizeof(GuWord) * CHAR_BIT)
|
||||
|
||||
|
||||
/*
|
||||
* Based on the Bit Twiddling Hacks collection by Sean Eron Anderson
|
||||
* <http://graphics.stanford.edu/~seander/bithacks.html>
|
||||
*/
|
||||
|
||||
unsigned gu_ceil2e(unsigned i);
|
||||
|
||||
static inline int
|
||||
gu_sign(int i) {
|
||||
return (i > 0) - (i < 0);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
gu_ceildiv(size_t size, size_t div)
|
||||
{
|
||||
return (size + div - 1) / div;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_aligned(uintptr_t addr, size_t alignment)
|
||||
{
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
return (addr & (alignment - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
gu_align_forward(uintptr_t addr, size_t alignment) {
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
uintptr_t mask = alignment - 1;
|
||||
return (addr + mask) & ~mask;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
gu_align_backward(uintptr_t addr, size_t alignment) {
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
return addr & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_bits_test(const GuWord* bitmap, int idx) {
|
||||
return !!(bitmap[idx / GU_WORD_BITS] & 1 << (idx % GU_WORD_BITS));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_bits_set(GuWord* bitmap, int idx) {
|
||||
bitmap[idx / GU_WORD_BITS] |= ((GuWord) 1) << (idx % GU_WORD_BITS);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_bits_clear(GuWord* bitmap, int idx) {
|
||||
bitmap[idx / GU_WORD_BITS] &= ~(((GuWord) 1) << (idx % GU_WORD_BITS));
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
gu_bits_size(size_t n_bits) {
|
||||
return gu_ceildiv(n_bits, GU_WORD_BITS) * sizeof(GuWord);
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_word_ptr(GuWord w)
|
||||
{
|
||||
return (void*) w;
|
||||
}
|
||||
|
||||
static inline GuWord
|
||||
gu_ptr_word(void* p)
|
||||
{
|
||||
return (GuWord) p;
|
||||
}
|
||||
|
||||
#define GuOpaque() struct { GuWord w_; }
|
||||
|
||||
typedef GuWord GuTagged;
|
||||
|
||||
#define GU_TAG_MAX (sizeof(GuWord) - 1)
|
||||
|
||||
static inline size_t
|
||||
gu_tagged_tag(GuTagged t) {
|
||||
return (int) (t & (sizeof(GuWord) - 1));
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_tagged_ptr(GuTagged w) {
|
||||
return (void*) gu_align_backward(w, sizeof(GuWord));
|
||||
}
|
||||
|
||||
static inline GuTagged
|
||||
gu_tagged(void* ptr, size_t tag) {
|
||||
gu_require(tag < sizeof(GuWord));
|
||||
uintptr_t u = (uintptr_t) ptr;
|
||||
gu_require(gu_align_backward(u, sizeof(GuWord)) == u);
|
||||
return (GuWord) { u | tag };
|
||||
}
|
||||
|
||||
#include <gu/exn.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
extern GU_DECLARE_TYPE(GuIntDecodeExn, abstract);
|
||||
|
||||
#define GU_DECODE_2C_(u_, t_, umax_, posmax_, tmin_, err_) \
|
||||
(((u_) <= (posmax_)) \
|
||||
? (t_) (u_) \
|
||||
: (tmin_) + ((t_) ((umax_) - (u_))) < 0 \
|
||||
? -1 - ((t_) ((umax_) - (u_))) \
|
||||
: (gu_raise(err_, GuIntDecodeExn), -1))
|
||||
|
||||
|
||||
static inline int8_t
|
||||
gu_decode_2c8(uint8_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int8_t, UINT8_C(0xff),
|
||||
INT8_C(0x7f), INT8_MIN, err);
|
||||
}
|
||||
|
||||
static inline int16_t
|
||||
gu_decode_2c16(uint16_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int16_t, UINT16_C(0xffff),
|
||||
INT16_C(0x7fff), INT16_MIN, err);
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
gu_decode_2c32(uint32_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int32_t, UINT32_C(0xffffffff),
|
||||
INT32_C(0x7fffffff), INT32_MIN, err);
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
gu_decode_2c64(uint64_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int64_t, UINT64_C(0xffffffffffffffff),
|
||||
INT64_C(0x7fffffffffffffff), INT64_MIN, err);
|
||||
}
|
||||
|
||||
double
|
||||
gu_decode_double(uint64_t u);
|
||||
|
||||
|
||||
|
||||
#endif // GU_BITS_H_
|
||||
73
src/runtime/c/gu/choice.c
Normal file
73
src/runtime/c/gu/choice.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <gu/choice.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
struct GuChoice {
|
||||
GuBuf* path;
|
||||
size_t path_idx;
|
||||
};
|
||||
|
||||
GuChoice*
|
||||
gu_new_choice(GuPool* pool)
|
||||
{
|
||||
GuChoice* ch = gu_new(GuChoice, pool);
|
||||
ch->path = gu_new_buf(uint8_t, pool);
|
||||
ch->path_idx = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
GuChoiceMark
|
||||
gu_choice_mark(GuChoice* ch)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
gu_debug("%p@%d: mark", ch, ch->path_idx);
|
||||
return (GuChoiceMark){ch->path_idx};
|
||||
}
|
||||
|
||||
void
|
||||
gu_choice_reset(GuChoice* ch, GuChoiceMark mark)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
gu_debug("%p@%d: reset %d", ch, ch->path_idx, mark.path_idx);
|
||||
gu_require(mark.path_idx <= ch->path_idx );
|
||||
ch->path_idx = mark.path_idx;
|
||||
}
|
||||
|
||||
int
|
||||
gu_choice_next(GuChoice* ch, int n_choices)
|
||||
{
|
||||
gu_assert(n_choices >= 0);
|
||||
gu_require(n_choices <= UINT8_MAX);
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
if (n_choices == 0) {
|
||||
return -1;
|
||||
}
|
||||
int i = 0;
|
||||
if (gu_buf_length(ch->path) > ch->path_idx) {
|
||||
i = (int) gu_buf_get(ch->path, uint8_t, ch->path_idx);
|
||||
gu_assert(i <= n_choices);
|
||||
} else {
|
||||
gu_buf_push(ch->path, uint8_t, n_choices);
|
||||
i = n_choices;
|
||||
}
|
||||
int ret = (i == 0) ? -1 : n_choices - i;
|
||||
gu_debug("%p@%d: %d", ch, ch->path_idx, ret);
|
||||
ch->path_idx++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_choice_advance(GuChoice* ch)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
|
||||
while (gu_buf_length(ch->path) > ch->path_idx) {
|
||||
uint8_t last = gu_buf_pop(ch->path, uint8_t);
|
||||
if (last > 1) {
|
||||
gu_buf_push(ch->path, uint8_t, last-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
37
src/runtime/c/gu/choice.h
Normal file
37
src/runtime/c/gu/choice.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef GU_CHOICE_H_
|
||||
#define GU_CHOICE_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
typedef struct GuChoice GuChoice;
|
||||
|
||||
typedef struct GuChoiceMark GuChoiceMark;
|
||||
|
||||
GuChoice*
|
||||
gu_new_choice(GuPool* pool);
|
||||
|
||||
int
|
||||
gu_choice_next(GuChoice* ch, int n_choices);
|
||||
|
||||
GuChoiceMark
|
||||
gu_choice_mark(GuChoice* ch);
|
||||
|
||||
void
|
||||
gu_choice_reset(GuChoice* ch, GuChoiceMark mark);
|
||||
|
||||
bool
|
||||
gu_choice_advance(GuChoice* ch);
|
||||
|
||||
|
||||
// private
|
||||
|
||||
struct GuChoiceMark {
|
||||
size_t path_idx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // GU_CHOICE_H_
|
||||
4
src/runtime/c/gu/defs.c
Normal file
4
src/runtime/c/gu/defs.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <gu/defs.h>
|
||||
|
||||
void* const gu_null = NULL;
|
||||
GuStruct* const gu_null_struct = NULL;
|
||||
217
src/runtime/c/gu/defs.h
Normal file
217
src/runtime/c/gu/defs.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Miscellaneous macros.
|
||||
*/
|
||||
|
||||
#ifndef GU_DEFS_H_
|
||||
#define GU_DEFS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <gu/sysdeps.h>
|
||||
|
||||
#define gu_container(mem_p, container_type, member) \
|
||||
((container_type*)(((uint8_t*) (mem_p)) - offsetof(container_type, member)))
|
||||
/**< Find the address of a containing structure.
|
||||
*
|
||||
* If @c s has type @c t*, where @c t is a struct or union type with a
|
||||
* member @m, then <tt>GU_CONTAINER_P(&s->m, t, m) == s</tt>.
|
||||
*
|
||||
* @param mem_p Pointer to the member of a structure.
|
||||
* @param container_type The type of the containing structure.
|
||||
* @param member The name of the member of @a container_type
|
||||
* @return The address of the containing structure.
|
||||
*
|
||||
* @hideinitializer */
|
||||
|
||||
|
||||
#define gu_member_p(struct_p_, offset_) \
|
||||
((void*)&((uint8_t*)(struct_p_))[offset_])
|
||||
|
||||
#define gu_member(t_, struct_p_, offset_) \
|
||||
(*(t_*)gu_member_p(struct_p_, offset_))
|
||||
|
||||
#ifdef GU_ALIGNOF
|
||||
# define gu_alignof GU_ALIGNOF
|
||||
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
#else
|
||||
# define gu_alignof(t_) \
|
||||
((size_t)(offsetof(struct { char c_; t_ e_; }, e_)))
|
||||
# ifdef GU_CAN_HAVE_FAM_IN_MEMBER
|
||||
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define GU_PLIT(type, expr) \
|
||||
((type[1]){ expr })
|
||||
|
||||
#define GU_LVALUE(type, expr) \
|
||||
(*((type[1]){ expr }))
|
||||
|
||||
#define GU_COMMA ,
|
||||
|
||||
#define GU_ARRAY_LEN(t,a) (sizeof((const t[])a) / sizeof(t))
|
||||
|
||||
#define GU_ID(...) __VA_ARGS__
|
||||
|
||||
// This trick is by Laurent Deniau <laurent.deniau@cern.ch>
|
||||
#define GU_N_ARGS(...) \
|
||||
GU_N_ARGS_(__VA_ARGS__, \
|
||||
31,30,29,28,27,26,25,24, \
|
||||
23,22,21,20,19,18,17,16, \
|
||||
15,14,13,12,11,10,9,8, \
|
||||
7,6,5,4,3,2,1,0)
|
||||
#define GU_N_ARGS_(...) GU_N_ARGS__(__VA_ARGS__)
|
||||
#define GU_N_ARGS__(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, \
|
||||
q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,N,...) \
|
||||
N
|
||||
|
||||
#define GU_ARG1(a1, ...) a1
|
||||
#define GU_ARG2(a1, a2, ...) a2
|
||||
|
||||
#define GU_BEGIN do {
|
||||
#define GU_END } while (false)
|
||||
|
||||
#define GU_NOP GU_BEGIN (void) 0; GU_END
|
||||
|
||||
/**< @hideinitializer */
|
||||
|
||||
//
|
||||
// Assert
|
||||
//
|
||||
|
||||
#define GU_MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_))
|
||||
#define GU_MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_))
|
||||
|
||||
static inline int
|
||||
gu_max(int a, int b) {
|
||||
return GU_MAX(a, b);
|
||||
}
|
||||
|
||||
static inline int
|
||||
gu_min(int a, int b) {
|
||||
return GU_MIN(a, b);
|
||||
}
|
||||
|
||||
#ifdef GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
#define gu_flex_alignof gu_alignof
|
||||
#else
|
||||
#define gu_flex_alignof(t) 0
|
||||
#endif
|
||||
|
||||
static inline size_t
|
||||
gu_flex_size(size_t ssize, size_t offset, int n_elems, size_t e_size)
|
||||
{
|
||||
return GU_MAX(ssize, offset + n_elems * e_size);
|
||||
}
|
||||
|
||||
#define GU_FLEX_SIZE(type, flex_member, n_elems) \
|
||||
gu_flex_size(sizeof(type), offsetof(type, flex_member), \
|
||||
n_elems, sizeof(((type*)NULL)->flex_member[0]))
|
||||
|
||||
|
||||
// The following are directly from gmacros.h in GLib
|
||||
|
||||
#define GU_PASTE_ARGS(id1_,id2_) \
|
||||
id1_ ## id2_
|
||||
|
||||
#define GU_PASTE(id1_, id2_) \
|
||||
GU_PASTE_ARGS(id1_, id2_)
|
||||
|
||||
#define GU_STATIC_ASSERT(expr_) \
|
||||
typedef struct { \
|
||||
char static_assert[(expr_) ? 1 : -1]; \
|
||||
} GU_PASTE(GuStaticAssert_, __LINE__)
|
||||
|
||||
|
||||
#define GU_ENSURE_TYPE(T, EXPR) \
|
||||
((void)(sizeof(*(T*)NULL=(EXPR))),(EXPR))
|
||||
|
||||
#define GU_END_DECLS \
|
||||
extern void gu_dummy_(void)
|
||||
|
||||
extern void* const gu_null;
|
||||
|
||||
// Dummy struct used for generic struct pointers
|
||||
typedef struct GuStruct GuStruct;
|
||||
|
||||
extern GuStruct* const gu_null_struct;
|
||||
|
||||
typedef uintptr_t GuWord;
|
||||
|
||||
#define GU_WORD_MAX UINTPTR_MAX
|
||||
|
||||
// TODO: use max_align_t once C1X is supported
|
||||
typedef union {
|
||||
char c;
|
||||
short s;
|
||||
int i;
|
||||
long l;
|
||||
long long ll;
|
||||
intmax_t im;
|
||||
float f;
|
||||
double d;
|
||||
long double ld;
|
||||
void* p;
|
||||
void (*fp)();
|
||||
} GuMaxAlign;
|
||||
|
||||
#define gu_alloca(N) \
|
||||
(((union { GuMaxAlign align_; uint8_t buf_[N]; }){{0}}).buf_)
|
||||
|
||||
|
||||
// For Doxygen
|
||||
#define GU_PRIVATE /** @private */
|
||||
|
||||
#ifdef GU_GNUC
|
||||
# define GU_LIKELY(EXPR) __builtin_expect(EXPR, 1)
|
||||
# define GU_UNLIKELY(EXPR) __builtin_expect(EXPR, 0)
|
||||
# define GU_IS_CONSTANT(EXPR) __builtin_constant_p(EXPR)
|
||||
#else
|
||||
# define GU_LIKELY(EXPR) (EXPR)
|
||||
# define GU_UNLIKELY(EXPR) (EXPR)
|
||||
# ifdef GU_OPTIMIZE_SIZE
|
||||
# define GU_IS_CONSTANT(EXPR) false
|
||||
# else
|
||||
# define GU_IS_CONSTANT(EXPR) true
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Splint annotations
|
||||
#define GU_ONLY GU_SPLINT(only)
|
||||
#define GU_NULL GU_SPLINT(null)
|
||||
#define GU_NOTNULL GU_SPLINT(notnull)
|
||||
#define GU_RETURNED GU_SPLINT(returned)
|
||||
#define GU_ABSTRACT GU_SPLINT(abstract)
|
||||
#define GU_IMMUTABLE GU_SPLINT(immutable)
|
||||
#define GU_NOTREACHED GU_SPLINT(notreached)
|
||||
#define GU_UNUSED GU_SPLINT(unused) GU_GNUC_ATTR(unused)
|
||||
#define GU_OUT GU_SPLINT(out)
|
||||
#define GU_IN GU_SPLINT(in)
|
||||
#define GU_NORETURN GU_SPLINT(noreturn) GU_GNUC_ATTR(noreturn)
|
||||
#define GU_MODIFIES(x) GU_SPLINT(modifies x)
|
||||
|
||||
#endif // GU_DEFS_H_
|
||||
411
src/runtime/c/gu/dump.c
Normal file
411
src/runtime/c/gu/dump.c
Normal file
@@ -0,0 +1,411 @@
|
||||
#include <gu/dump.h>
|
||||
#include <gu/list.h>
|
||||
#include <gu/variant.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/str.h>
|
||||
#include <gu/file.h>
|
||||
|
||||
GuDump*
|
||||
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool)
|
||||
{
|
||||
GuDump* ctx = gu_new(GuDump, pool);
|
||||
ctx->pool = pool;
|
||||
if (dumpers == NULL) {
|
||||
dumpers = &gu_dump_table;
|
||||
}
|
||||
ctx->dumpers = gu_new_type_map(dumpers, pool);
|
||||
ctx->yaml = gu_new_yaml(wtr, err, pool);
|
||||
ctx->data = gu_new_addr_map(void, void*, &gu_null, pool);
|
||||
ctx->print_address = false;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
gu_dump(GuType* type, const void* value, GuDump* ctx)
|
||||
{
|
||||
GuDumpFn* dumper = gu_type_map_get(ctx->dumpers, type);
|
||||
if (ctx->print_address) {
|
||||
GuPool* pool = gu_new_pool();
|
||||
GuString s = gu_format_string(pool, "%p", value);
|
||||
gu_yaml_comment(ctx->yaml, s);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
(*dumper)(dumper, type, value, ctx);
|
||||
}
|
||||
|
||||
void
|
||||
gu_dump_stderr(GuType* type, const void* value, GuExn* err)
|
||||
{
|
||||
GuPool* pool = gu_new_pool();
|
||||
GuOut* out = gu_file_out(stderr, pool);
|
||||
#if 0
|
||||
GuWriter* wtr = gu_locale_writer(out, pool);
|
||||
#else
|
||||
GuWriter* wtr = gu_new_utf8_writer(out, pool);
|
||||
#endif
|
||||
GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
|
||||
gu_dump(type, value, ctx);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_scalar(GuDump* ctx, const char* fmt, ...)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
GuString s = gu_format_string_v(fmt, args, tmp_pool);
|
||||
va_end(args);
|
||||
gu_yaml_scalar(ctx->yaml, s);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_str_scalar(GuDump* ctx, const char* str)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuString s = gu_str_string(str, tmp_pool);
|
||||
gu_yaml_scalar(ctx->yaml, s);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_null(GuDump* ctx)
|
||||
{
|
||||
gu_yaml_tag_secondary(ctx->yaml, "null");
|
||||
gu_yaml_scalar(ctx->yaml, gu_empty_string);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_int(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const int* ip = p;
|
||||
gu_dump_scalar(ctx, "%d", *ip);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_uint16(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const uint16_t* ip = p;
|
||||
gu_dump_scalar(ctx, "%" PRIu16, *ip);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_size(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) (dumper && type);
|
||||
const size_t* zp = p;
|
||||
gu_dump_scalar(ctx, "%zu", *zp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_double(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const double* dp = p;
|
||||
gu_dump_scalar(ctx, "%lf", *dp);
|
||||
}
|
||||
|
||||
static const char gu_dump_length_key[] = "gu_dump_length_key";
|
||||
|
||||
static void
|
||||
gu_dump_length(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuLength* ip = p;
|
||||
gu_dump_scalar(ctx, "%d", *ip);
|
||||
GuLength* lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
|
||||
if (lenp != NULL) {
|
||||
*lenp = *ip;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_str(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuStr* sp = p;
|
||||
gu_dump_str_scalar(ctx, *sp);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_string(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuString* sp = p;
|
||||
gu_yaml_scalar(ctx->yaml, *sp);
|
||||
}
|
||||
|
||||
|
||||
// For _non-shared_ pointers.
|
||||
static void
|
||||
gu_dump_pointer(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
void* const* pp = p;
|
||||
if (*pp == NULL) {
|
||||
gu_dump_null(ctx);
|
||||
} else {
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GuMapItor itor;
|
||||
GuMapType* mtype;
|
||||
GuDump* ctx;
|
||||
} GuDumpMapFn;
|
||||
|
||||
static void
|
||||
gu_dump_map_itor(GuMapItor* self, const void* key, void* value, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuDumpMapFn* clo = (GuDumpMapFn*) self;
|
||||
gu_dump(clo->mtype->key_type, key, clo->ctx);
|
||||
gu_dump(clo->mtype->value_type, value, clo->ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_map(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuMapType* mtype = (GuMapType*) type;
|
||||
GuMap* map = (GuMap*) p;
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
GuDumpMapFn clo = { { gu_dump_map_itor }, mtype, ctx };
|
||||
gu_map_iter(map, &clo.itor, NULL);
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_struct(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuStructRepr* srepr = (GuStructRepr*) type;
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
const uint8_t* data = p;
|
||||
GuLength* old_lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
|
||||
GuLength len = (GuLength)-1;
|
||||
gu_map_put(ctx->data, gu_dump_length_key, void*, &len);
|
||||
|
||||
for (int i = 0; i < srepr->members.len; i++) {
|
||||
const GuMember* member = &srepr->members.elems[i];
|
||||
gu_dump_str_scalar(ctx, member->name);
|
||||
const uint8_t* memp = &data[member->offset];
|
||||
if (member->is_flex) {
|
||||
// Flexible array member
|
||||
gu_assert(len != (GuLength)-1);
|
||||
size_t mem_s = gu_type_size(member->type);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (GuLength i = 0; i < len; i++) {
|
||||
gu_dump(member->type, &memp[i * mem_s], ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
} else {
|
||||
gu_dump(member->type, memp, ctx);
|
||||
}
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
if (old_lenp) {
|
||||
gu_map_set(ctx->data, gu_dump_length_key, void*, old_lenp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_alias(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuTypeAlias* alias = gu_type_cast(type, alias);
|
||||
|
||||
gu_dump(alias->type, p, ctx);
|
||||
}
|
||||
|
||||
static const char gu_dump_reference_key[] = "reference";
|
||||
|
||||
static bool
|
||||
gu_dump_anchor(GuDump* ctx, const void* p)
|
||||
{
|
||||
GuMap* map = gu_map_get(ctx->data, gu_dump_reference_key, void*);
|
||||
if (map == NULL) {
|
||||
map = gu_new_addr_map(void, GuYamlAnchor,
|
||||
&gu_yaml_null_anchor, ctx->pool);
|
||||
gu_map_put(ctx->data, gu_dump_reference_key, void*, map);
|
||||
}
|
||||
GuYamlAnchor a = gu_map_get(map, p, GuYamlAnchor);
|
||||
if (a == gu_yaml_null_anchor) {
|
||||
a = gu_yaml_anchor(ctx->yaml);
|
||||
gu_map_put(map, p, GuYamlAnchor, a);
|
||||
return true;
|
||||
} else {
|
||||
gu_yaml_alias(ctx->yaml, a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_referenced(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuTypeAlias* alias = gu_type_cast(type, alias);
|
||||
bool created = gu_dump_anchor(ctx, p);
|
||||
if (created) {
|
||||
gu_dump(alias->type, p, ctx);
|
||||
} else {
|
||||
// gu_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_reference(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
void* const* pp = p;
|
||||
bool created = gu_dump_anchor(ctx, *pp);
|
||||
if (created) {
|
||||
// gu_assert(false);
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_shared(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
void* const* pp = p;
|
||||
if (*pp == NULL) {
|
||||
gu_dump_null(ctx);
|
||||
} else {
|
||||
bool created = gu_dump_anchor(ctx, *pp);
|
||||
if (created) {
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_list(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuListType* ltype = (GuListType*) type;
|
||||
const uint8_t* up = p;
|
||||
int len = * (const int*) p;
|
||||
size_t elem_size = gu_type_size(ltype->elem_type);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (int i = 0; i < len; i++) {
|
||||
ptrdiff_t offset = ltype->elems_offset + i * elem_size;
|
||||
gu_dump(ltype->elem_type, &up[offset], ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_variant(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuVariantType* vtype = gu_type_cast(type, GuVariant);
|
||||
const GuVariant* vp = p;
|
||||
int tag = gu_variant_tag(*vp);
|
||||
for (int i = 0; i < vtype->ctors.len; i++) {
|
||||
GuConstructor* ctor = &vtype->ctors.elems[i];
|
||||
if (ctor->c_tag == tag) {
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
gu_dump_str_scalar(ctx, ctor->c_name);
|
||||
void* data = gu_variant_data(*vp);
|
||||
gu_dump(ctor->type, data, ctx);
|
||||
gu_yaml_end(ctx->yaml);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gu_assert(false);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_enum(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuEnumType* etype = gu_type_cast(type, enum);
|
||||
GuEnumConstant* cp = gu_enum_value(etype, p);
|
||||
gu_assert(cp != NULL);
|
||||
gu_dump_str_scalar(ctx, cp->name);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_seq(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuSeqType* dtype = gu_type_cast(type, GuSeq);
|
||||
size_t elem_size = gu_type_size(dtype->elem_type);
|
||||
const GuSeq* seqp = p;
|
||||
GuSeq seq = *seqp;
|
||||
if (gu_seq_is_null(seq)) {
|
||||
gu_dump_null(ctx);
|
||||
return;
|
||||
}
|
||||
size_t len = gu_seq_length(seq);
|
||||
const uint8_t* data = gu_seq_data(seq);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const void* elemp = &data[i * elem_size];
|
||||
gu_dump(dtype->elem_type, elemp, ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
|
||||
GuTypeTable
|
||||
gu_dump_table = GU_TYPETABLE(
|
||||
GU_SLIST_0,
|
||||
{ gu_kind(int), gu_fn(gu_dump_int) },
|
||||
{ gu_kind(uint16_t), gu_fn(gu_dump_uint16) },
|
||||
{ gu_kind(size_t), gu_fn(gu_dump_size) },
|
||||
{ gu_kind(GuStr), gu_fn(gu_dump_str) },
|
||||
{ gu_kind(GuString), gu_fn(gu_dump_string) },
|
||||
{ gu_kind(struct), gu_fn(gu_dump_struct) },
|
||||
{ gu_kind(pointer), gu_fn(gu_dump_pointer) },
|
||||
{ gu_kind(GuMap), gu_fn(gu_dump_map) },
|
||||
{ gu_kind(alias), gu_fn(gu_dump_alias) },
|
||||
{ gu_kind(reference), gu_fn(gu_dump_reference) },
|
||||
{ gu_kind(referenced), gu_fn(gu_dump_referenced) },
|
||||
{ gu_kind(shared), gu_fn(gu_dump_shared) },
|
||||
{ gu_kind(GuList), gu_fn(gu_dump_list) },
|
||||
{ gu_kind(GuSeq), gu_fn(gu_dump_seq) },
|
||||
{ gu_kind(GuLength), gu_fn(gu_dump_length) },
|
||||
{ gu_kind(GuVariant), gu_fn(gu_dump_variant) },
|
||||
{ gu_kind(double), gu_fn(gu_dump_double) },
|
||||
{ gu_kind(enum), gu_fn(gu_dump_enum) },
|
||||
);
|
||||
34
src/runtime/c/gu/dump.h
Normal file
34
src/runtime/c/gu/dump.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef GU_DUMP_H_
|
||||
#define GU_DUMP_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/yaml.h>
|
||||
#include <gu/type.h>
|
||||
#include <gu/map.h>
|
||||
|
||||
typedef struct GuDump GuDump;
|
||||
|
||||
struct GuDump {
|
||||
GuPool* pool;
|
||||
GuYaml* yaml;
|
||||
GuMap* data;
|
||||
GuTypeMap* dumpers;
|
||||
bool print_address;
|
||||
};
|
||||
|
||||
typedef void (*GuDumpFn)(GuFn* self, GuType* type, const void* value, GuDump* ctx);
|
||||
|
||||
GuDump*
|
||||
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool);
|
||||
|
||||
void
|
||||
gu_dump(GuType* type, const void* value, GuDump* ctx);
|
||||
|
||||
void
|
||||
gu_dump_stderr(GuType* type, const void* value, GuExn* err);
|
||||
|
||||
extern GuTypeTable
|
||||
gu_dump_table;
|
||||
|
||||
|
||||
#endif // GU_DUMP_H_
|
||||
7
src/runtime/c/gu/enum.c
Normal file
7
src/runtime/c/gu/enum.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <gu/enum.h>
|
||||
|
||||
void
|
||||
gu_enum_next(GuEnum* en, void* to, GuPool* pool)
|
||||
{
|
||||
en->next(en, to, pool);
|
||||
}
|
||||
35
src/runtime/c/gu/enum.h
Normal file
35
src/runtime/c/gu/enum.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef GU_ENUM_H_
|
||||
#define GU_ENUM_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
typedef struct GuEnum GuEnum;
|
||||
|
||||
struct GuEnum {
|
||||
void (*next)(GuEnum* self, void* to, GuPool* pool);
|
||||
};
|
||||
|
||||
void
|
||||
gu_enum_next(GuEnum* en, void* to, GuPool* pool);
|
||||
|
||||
#ifdef GU_GNUC
|
||||
|
||||
#define gu_next(ENUM, T, POOL) \
|
||||
({ \
|
||||
T gu_next_tmp_; \
|
||||
gu_enum_next((ENUM), &gu_next_tmp_, (POOL)); \
|
||||
gu_next_tmp_; \
|
||||
})
|
||||
#else
|
||||
static inline void*
|
||||
gu_enum_next_(GuEnum* en, void* to, GuPool* pool)
|
||||
{
|
||||
gu_enum_next(en, to, pool);
|
||||
return to;
|
||||
}
|
||||
#define gu_next(ENUM, T, POOL) \
|
||||
(*(T*)gu_enum_next_((ENUM), &(T){0}, (POOL)))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* GU_ENUM_H_ */
|
||||
72
src/runtime/c/gu/exn.c
Normal file
72
src/runtime/c/gu/exn.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
GuExn*
|
||||
gu_new_exn(GuExn* parent, GuKind* catch, GuPool* pool)
|
||||
{
|
||||
return gu_new_s(pool, GuExn,
|
||||
.state = GU_EXN_OK,
|
||||
.parent = parent,
|
||||
.catch = catch,
|
||||
.caught = NULL,
|
||||
.data.pool = pool,
|
||||
.data.data = NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gu_exn_block(GuExn* err)
|
||||
{
|
||||
if (err && err->state == GU_EXN_RAISED) {
|
||||
err->state = GU_EXN_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gu_exn_unblock(GuExn* err)
|
||||
{
|
||||
if (err && err->state == GU_EXN_BLOCKED) {
|
||||
err->state = GU_EXN_RAISED;
|
||||
}
|
||||
}
|
||||
|
||||
GuExnData*
|
||||
gu_exn_raise_debug_(GuExn* base, GuType* type,
|
||||
const char* filename, const char* func, int lineno)
|
||||
{
|
||||
gu_require(type);
|
||||
|
||||
// TODO: log the error, once there's a system for dumping
|
||||
// error objects.
|
||||
|
||||
GuExn* err = base;
|
||||
|
||||
while (err && !(err->catch && gu_type_has_kind(type, err->catch))) {
|
||||
err->state = GU_EXN_RAISED;
|
||||
err = err->parent;
|
||||
}
|
||||
if (!err) {
|
||||
gu_abort_(GU_ASSERT_ASSERTION, filename, func, lineno,
|
||||
"Unexpected error raised");
|
||||
}
|
||||
GuExnState old_state = err->state;
|
||||
err->state = GU_EXN_RAISED;
|
||||
if (old_state == GU_EXN_OK) {
|
||||
err->caught = type;
|
||||
if (err->data.pool) {
|
||||
return &err->data;
|
||||
}
|
||||
}
|
||||
// Exceptian had already been raised, possibly blocked, or no
|
||||
// exception value is required.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GuExnData*
|
||||
gu_exn_raise_(GuExn* base, GuType* type)
|
||||
{
|
||||
return gu_exn_raise_debug_(base, type, NULL, NULL, -1);
|
||||
}
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(GuErrno, signed, _);
|
||||
195
src/runtime/c/gu/exn.h
Normal file
195
src/runtime/c/gu/exn.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef GU_EXN_H_
|
||||
#define GU_EXN_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* @defgroup GuExn Exceptions
|
||||
* Defined in <gu/exn.h>.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// An exception frame.
|
||||
typedef struct GuExn GuExn;
|
||||
|
||||
/// @private
|
||||
typedef enum {
|
||||
GU_EXN_RAISED,
|
||||
GU_EXN_OK,
|
||||
GU_EXN_BLOCKED
|
||||
} GuExnState;
|
||||
|
||||
typedef struct GuExnData GuExnData;
|
||||
|
||||
/// A structure for storing exception values.
|
||||
struct GuExnData
|
||||
/**
|
||||
* When an exception is raised, if there is an associated value, it
|
||||
* must be allocated from a pool that still exists when control
|
||||
* returns to the handler of that exception. This structure is used to
|
||||
* communicate the exception from the raiser to the handler: the
|
||||
* handler sets #pool when setting up the exception frame, and the
|
||||
* raiser uses that pool to allocate the value and stores that in
|
||||
* #data. When control returns to the handler, it reads the value from
|
||||
* there.
|
||||
*/
|
||||
{
|
||||
|
||||
/// The pool that the exception value should be allocated from.
|
||||
GuPool* const pool;
|
||||
|
||||
/// The exception value.
|
||||
const void* data;
|
||||
};
|
||||
|
||||
struct GuExn {
|
||||
/// @privatesection
|
||||
GuExnState state;
|
||||
GuExn* parent;
|
||||
GuKind* catch;
|
||||
GuType* caught;
|
||||
GuExnData data;
|
||||
};
|
||||
|
||||
|
||||
/// @name Creating exception frames
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate a new local exception frame.
|
||||
#define gu_exn(parent_, catch_, pool_) &(GuExn){ \
|
||||
.state = GU_EXN_OK, \
|
||||
.parent = parent_, \
|
||||
.catch = gu_kind(catch_), \
|
||||
.caught = NULL, \
|
||||
.data.pool = pool_, \
|
||||
.data.data = NULL \
|
||||
}
|
||||
|
||||
|
||||
/// Allocate a new exception frame.
|
||||
GuExn*
|
||||
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);
|
||||
|
||||
|
||||
|
||||
static inline bool
|
||||
gu_exn_is_raised(GuExn* err) {
|
||||
return err && (err->state == GU_EXN_RAISED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_exn_clear(GuExn* err) {
|
||||
err->caught = NULL;
|
||||
err->state = GU_EXN_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuType*
|
||||
gu_exn_caught(GuExn* err);
|
||||
|
||||
const void*
|
||||
gu_exn_caught_data(GuExn* err);
|
||||
|
||||
/// Temporarily block a raised exception.
|
||||
void
|
||||
gu_exn_block(GuExn* err);
|
||||
|
||||
/// Show again a blocked exception.
|
||||
void
|
||||
gu_exn_unblock(GuExn* err);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_(GuExn* err, GuType* type);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_debug_(GuExn* err, GuType* type,
|
||||
const char* filename, const char* func, int lineno);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_(err_, type_)
|
||||
#else
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_debug_(err_, type_, \
|
||||
__FILE__, __func__, __LINE__)
|
||||
#endif
|
||||
|
||||
/// Raise an exception.
|
||||
#define gu_raise(exn, T) \
|
||||
gu_exn_raise(exn, gu_type(T))
|
||||
/**<
|
||||
* @param exn The current exception frame.
|
||||
*
|
||||
* @param T The C type of the exception to raise.
|
||||
*
|
||||
* @return A #GuExnData object that can be used to store the exception value, or
|
||||
* \c NULL if no value is required.
|
||||
*
|
||||
* @note The associated #GuType object for type \p T must be visible.
|
||||
*/
|
||||
|
||||
#define gu_raise_new(error_, t_, pool_, expr_) \
|
||||
GU_BEGIN \
|
||||
GuExnData* gu_raise_err_ = gu_raise(error_, t_); \
|
||||
if (gu_raise_err_) { \
|
||||
GuPool* pool_ = gu_raise_err_->pool; \
|
||||
gu_raise_err_->data = expr_; \
|
||||
} \
|
||||
GU_END
|
||||
|
||||
#define gu_raise_i(error_, t_, ...) \
|
||||
gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))
|
||||
|
||||
|
||||
/// Check the status of the current exception frame
|
||||
static inline bool
|
||||
gu_ok(GuExn* exn) {
|
||||
return !GU_UNLIKELY(gu_exn_is_raised(exn));
|
||||
}
|
||||
/**<
|
||||
* @return \c false if an exception has been raised in the frame \p exn
|
||||
* and it has not been blocked, \c true otherwise.
|
||||
*/
|
||||
|
||||
|
||||
/// Return from current function if an exception has been raised.
|
||||
#define gu_return_on_exn(exn_, retval_) \
|
||||
GU_BEGIN \
|
||||
if (gu_exn_is_raised(exn_)) return retval_; \
|
||||
GU_END
|
||||
/**<
|
||||
* @showinitializer
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
typedef int GuErrno;
|
||||
|
||||
extern GU_DECLARE_TYPE(GuErrno, signed);
|
||||
|
||||
|
||||
|
||||
#define gu_raise_errno(error_) \
|
||||
gu_raise_i(error_, GuErrno, errno)
|
||||
|
||||
#if 0
|
||||
|
||||
typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);
|
||||
|
||||
extern GuTypeTable gu_exn_default_printer;
|
||||
|
||||
void
|
||||
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // GU_EXN_H_
|
||||
73
src/runtime/c/gu/file.c
Normal file
73
src/runtime/c/gu/file.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <gu/file.h>
|
||||
|
||||
typedef struct GuFileOutStream GuFileOutStream;
|
||||
|
||||
struct GuFileOutStream {
|
||||
GuOutStream stream;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_file_output(GuOutStream* stream, const uint8_t* buf, size_t len, GuExn* err)
|
||||
{
|
||||
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
|
||||
errno = 0;
|
||||
size_t wrote = fwrite(buf, 1, len, fos->file);
|
||||
if (wrote < len) {
|
||||
if (ferror(fos->file)) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_file_flush(GuOutStream* stream, GuExn* err)
|
||||
{
|
||||
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
|
||||
errno = 0;
|
||||
if (fflush(fos->file) != 0) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_file_out(FILE* file, GuPool* pool)
|
||||
{
|
||||
GuFileOutStream* fos = gu_new_i(pool, GuFileOutStream,
|
||||
.stream.output = gu_file_output,
|
||||
.stream.flush = gu_file_flush,
|
||||
.file = file);
|
||||
return gu_new_out(&fos->stream, pool);
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuFileInStream GuFileInStream;
|
||||
|
||||
struct GuFileInStream {
|
||||
GuInStream stream;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_file_input(GuInStream* stream, uint8_t* buf, size_t sz, GuExn* err)
|
||||
{
|
||||
GuFileInStream* fis = gu_container(stream, GuFileInStream, stream);
|
||||
errno = 0;
|
||||
size_t got = fread(buf, 1, sz, fis->file);
|
||||
if (got == 0) {
|
||||
if (ferror(fis->file)) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_file_in(FILE* file, GuPool* pool)
|
||||
{
|
||||
GuFileInStream* fis = gu_new_s(pool, GuFileInStream,
|
||||
.stream.input = gu_file_input,
|
||||
.file = file);
|
||||
return gu_new_in(&fis->stream, pool);
|
||||
}
|
||||
14
src/runtime/c/gu/file.h
Normal file
14
src/runtime/c/gu/file.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef GU_FILE_H_
|
||||
#define GU_FILE_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/out.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GuOut*
|
||||
gu_file_out(FILE* file, GuPool* pool);
|
||||
|
||||
GuIn*
|
||||
gu_file_in(FILE* file, GuPool* pool);
|
||||
|
||||
#endif // GU_FILE_H_
|
||||
1
src/runtime/c/gu/fun.c
Normal file
1
src/runtime/c/gu/fun.c
Normal file
@@ -0,0 +1 @@
|
||||
#include <gu/fun.h>
|
||||
65
src/runtime/c/gu/fun.h
Normal file
65
src/runtime/c/gu/fun.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef GU_FUN_H_
|
||||
#define GU_FUN_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
typedef void (*GuFn)();
|
||||
typedef void (*GuFn0)(GuFn* clo);
|
||||
typedef void (*GuFn1)(GuFn* clo, void* arg1);
|
||||
typedef void (*GuFn2)(GuFn* clo, void* arg1, void* arg2);
|
||||
|
||||
#define gu_fn(fn_) (&(GuFn){ fn_ })
|
||||
|
||||
static inline void
|
||||
gu_apply0(GuFn* fn) {
|
||||
(*fn)(fn);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_apply1(GuFn* fn, void* arg1) {
|
||||
(*fn)(fn, arg1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_apply2(GuFn* fn, void* arg1, void* arg2) {
|
||||
(*fn)(fn, arg1, arg2);
|
||||
}
|
||||
|
||||
#define gu_apply(fn_, ...) \
|
||||
((fn_)->fn((fn_), __VA_ARGS__))
|
||||
|
||||
typedef struct GuClo0 GuClo0;
|
||||
|
||||
struct GuClo0 {
|
||||
GuFn fn;
|
||||
};
|
||||
|
||||
typedef struct GuClo1 GuClo1;
|
||||
|
||||
struct GuClo1 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
};
|
||||
|
||||
typedef struct GuClo2 GuClo2;
|
||||
struct GuClo2 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
void *env2;
|
||||
};
|
||||
|
||||
typedef struct GuClo3 GuClo3;
|
||||
struct GuClo3 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
void *env2;
|
||||
void *env3;
|
||||
};
|
||||
|
||||
typedef const struct GuEquality GuEquality;
|
||||
|
||||
struct GuEquality {
|
||||
bool (*is_equal)(GuEquality* self, const void* a, const void* b);
|
||||
};
|
||||
|
||||
#endif // GU_FUN_H_
|
||||
77
src/runtime/c/gu/hash.c
Normal file
77
src/runtime/c/gu/hash.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <gu/hash.h>
|
||||
|
||||
GuHash
|
||||
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len)
|
||||
{
|
||||
for (size_t n = 0; n < len; n++) {
|
||||
h = gu_hash_byte(h, buf[n]);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_int_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const int* ip1 = p1;
|
||||
const int* ip2 = p2;
|
||||
return *ip1 == *ip2;
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_int_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) *(const int*) p;
|
||||
}
|
||||
|
||||
GuHasher gu_int_hasher[1] = {
|
||||
{
|
||||
{ gu_int_eq_fn },
|
||||
gu_int_hash_fn
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
gu_addr_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
return (p1 == p2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_addr_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) (uintptr_t) p;
|
||||
}
|
||||
|
||||
GuHasher gu_addr_hasher[1] = {
|
||||
{
|
||||
{ gu_addr_eq_fn },
|
||||
gu_addr_hash_fn
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
gu_word_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuWord* wp1 = p1;
|
||||
const GuWord* wp2 = p2;
|
||||
return (*wp1 == *wp2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_word_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) (uintptr_t) p;
|
||||
}
|
||||
|
||||
GuHasher gu_word_hasher[1] = {
|
||||
{
|
||||
{ gu_word_eq_fn },
|
||||
gu_word_hash_fn
|
||||
}
|
||||
};
|
||||
40
src/runtime/c/gu/hash.h
Normal file
40
src/runtime/c/gu/hash.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef GU_HASH_H_
|
||||
#define GU_HASH_H_
|
||||
|
||||
#include <gu/fun.h>
|
||||
|
||||
typedef GuWord GuHash;
|
||||
|
||||
static inline GuHash
|
||||
gu_hash_ptr(void* ptr)
|
||||
{
|
||||
return (GuHash) ptr;
|
||||
}
|
||||
|
||||
|
||||
static inline GuHash
|
||||
gu_hash_byte(GuHash h, uint8_t u)
|
||||
{
|
||||
// Paul Larson's simple byte hash
|
||||
return h * 101 + u;
|
||||
}
|
||||
|
||||
|
||||
GuHash
|
||||
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len);
|
||||
|
||||
typedef const struct GuHasher GuHasher;
|
||||
|
||||
struct GuHasher {
|
||||
GuEquality eq;
|
||||
GuHash (*hash)(GuHasher* self, const void* p);
|
||||
};
|
||||
|
||||
|
||||
extern GuHasher gu_int_hasher[1];
|
||||
|
||||
extern GuHasher gu_addr_hasher[1];
|
||||
|
||||
extern GuHasher gu_word_hasher[1];
|
||||
|
||||
#endif // GU_HASH_H_
|
||||
421
src/runtime/c/gu/in.c
Normal file
421
src/runtime/c/gu/in.c
Normal file
@@ -0,0 +1,421 @@
|
||||
#include <gu/in.h>
|
||||
#include <gu/bits.h>
|
||||
#include <math.h>
|
||||
|
||||
GU_DEFINE_TYPE(GuEOF, abstract, _);
|
||||
|
||||
|
||||
static bool
|
||||
gu_in_is_buffering(GuIn* in)
|
||||
{
|
||||
return (in->buf_end != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_in_end_buffering(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (!gu_in_is_buffering(in)) {
|
||||
return;
|
||||
}
|
||||
if (in->stream->end_buffer) {
|
||||
size_t len = ((ptrdiff_t) in->buf_size) + in->buf_curr;
|
||||
in->stream->end_buffer(in->stream, len, err);
|
||||
}
|
||||
in->buf_curr = 0;
|
||||
in->buf_size = 0;
|
||||
in->buf_end = NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_in_begin_buffering(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (gu_in_is_buffering(in)) {
|
||||
if (in->buf_curr < 0) {
|
||||
return true;
|
||||
} else {
|
||||
gu_in_end_buffering(in, err);
|
||||
if (!gu_ok(err)) return false;
|
||||
}
|
||||
}
|
||||
if (!in->stream->begin_buffer) {
|
||||
return false;
|
||||
}
|
||||
size_t sz = 0;
|
||||
const uint8_t* new_buf =
|
||||
in->stream->begin_buffer(in->stream, &sz, err);
|
||||
if (new_buf) {
|
||||
in->buf_end = &new_buf[sz];
|
||||
in->buf_curr = -(ptrdiff_t) sz;
|
||||
in->buf_size = sz;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_in_input(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
if (sz == 0) {
|
||||
return 0;
|
||||
}
|
||||
gu_in_end_buffering(in, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
GuInStream* stream = in->stream;
|
||||
if (stream->input) {
|
||||
return stream->input(stream, dst, sz, err);
|
||||
}
|
||||
gu_raise(err, GuEOF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_in_some(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
gu_require(sz <= PTRDIFF_MAX);
|
||||
if (!gu_in_begin_buffering(in, err)) {
|
||||
if (!gu_ok(err)) return 0;
|
||||
return gu_in_input(in, dst, sz, err);
|
||||
}
|
||||
size_t real_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
||||
memcpy(dst, &in->buf_end[in->buf_curr], real_sz);
|
||||
in->buf_curr += real_sz;
|
||||
return real_sz;
|
||||
}
|
||||
|
||||
void
|
||||
gu_in_bytes_(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
size_t avail_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
||||
memcpy(dst, &in->buf_end[in->buf_curr], avail_sz);
|
||||
in->buf_curr += avail_sz;
|
||||
if (avail_sz < sz) {
|
||||
gu_in_input(in, &dst[avail_sz], sz - avail_sz, err);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err)
|
||||
{
|
||||
if (!gu_in_begin_buffering(in, err)) {
|
||||
return NULL;
|
||||
}
|
||||
*sz_out = (size_t) -in->buf_curr;
|
||||
return &in->buf_end[in->buf_curr];
|
||||
}
|
||||
|
||||
void
|
||||
gu_in_end_span(GuIn* in, size_t consumed)
|
||||
{
|
||||
gu_require(consumed <= (size_t) -in->buf_curr);
|
||||
in->buf_curr += (ptrdiff_t) consumed;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
gu_in_u8_(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (gu_in_begin_buffering(in, err) && in->buf_curr < 0) {
|
||||
return in->buf_end[in->buf_curr++];
|
||||
}
|
||||
uint8_t u = 0;
|
||||
size_t r = gu_in_input(in, &u, 1, err);
|
||||
if (r < 1) {
|
||||
gu_raise(err, GuEOF);
|
||||
return 0;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
gu_in_be(GuIn* in, GuExn* err, int n)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
gu_in_bytes(in, buf, n, err);
|
||||
uint64_t u = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u = u << 8 | buf[i];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
gu_in_le(GuIn* in, GuExn* err, int n)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
gu_in_bytes(in, buf, n, err);
|
||||
uint64_t u = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u = u << 8 | buf[i];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
int8_t
|
||||
gu_in_s8(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c8(gu_in_u8(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint16_t
|
||||
gu_in_u16le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 2);
|
||||
}
|
||||
|
||||
int16_t
|
||||
gu_in_s16le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c16(gu_in_u16le(in, err), err);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
gu_in_u16be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 2);
|
||||
}
|
||||
|
||||
int16_t
|
||||
gu_in_s16be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c16(gu_in_u16be(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
gu_in_u32le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 4);
|
||||
}
|
||||
|
||||
int32_t
|
||||
gu_in_s32le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c32(gu_in_u32le(in, err), err);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
gu_in_u32be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 4);
|
||||
}
|
||||
|
||||
int32_t
|
||||
gu_in_s32be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c32(gu_in_u32be(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
gu_in_u64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 8);
|
||||
}
|
||||
|
||||
int64_t
|
||||
gu_in_s64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c64(gu_in_u64le(in, err), err);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
gu_in_u64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 8);
|
||||
}
|
||||
|
||||
int64_t
|
||||
gu_in_s64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c64(gu_in_u64be(in, err), err);
|
||||
}
|
||||
|
||||
double
|
||||
gu_in_f64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_double(gu_in_u64le(in, err));
|
||||
}
|
||||
|
||||
double
|
||||
gu_in_f64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_double(gu_in_u64le(in, err));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_in_fini(GuFinalizer* fin)
|
||||
{
|
||||
GuIn* in = gu_container(fin, GuIn, fini);
|
||||
GuPool* pool = gu_local_pool();
|
||||
GuExn* err = gu_exn(NULL, type, pool);
|
||||
gu_in_end_buffering(in, err);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
|
||||
GuIn
|
||||
gu_init_in(GuInStream* stream)
|
||||
{
|
||||
return (GuIn) {
|
||||
.buf_end = NULL,
|
||||
.buf_curr = 0,
|
||||
.buf_size = 0,
|
||||
.stream = stream,
|
||||
.fini.fn = gu_in_fini
|
||||
};
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_new_in(GuInStream* stream, GuPool* pool)
|
||||
{
|
||||
GuIn* in = gu_new(GuIn, pool);
|
||||
*in = gu_init_in(stream);
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuProxyInStream GuProxyInStream;
|
||||
|
||||
struct GuProxyInStream {
|
||||
GuInStream stream;
|
||||
GuIn* real_in;
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_proxy_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
return gu_in_begin_span(pis->real_in, sz_out, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_in_end_buffer(GuInStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
gu_in_end_span(pis->real_in, sz);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_proxy_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
return gu_in_some(pis->real_in, dst, sz, err);
|
||||
}
|
||||
|
||||
GuInStream*
|
||||
gu_in_proxy_stream(GuIn* in, GuPool* pool)
|
||||
{
|
||||
return &gu_new_s(
|
||||
pool, GuProxyInStream,
|
||||
.stream.begin_buffer = gu_proxy_in_begin_buffer,
|
||||
.stream.end_buffer = gu_proxy_in_end_buffer,
|
||||
.stream.input = gu_proxy_in_input,
|
||||
.real_in = in)->stream;
|
||||
}
|
||||
|
||||
enum {
|
||||
GU_BUFFERED_IN_BUF_SIZE = 4096
|
||||
};
|
||||
|
||||
typedef struct GuBufferedInStream GuBufferedInStream;
|
||||
|
||||
struct GuBufferedInStream {
|
||||
GuInStream stream;
|
||||
size_t alloc;
|
||||
size_t have;
|
||||
size_t curr;
|
||||
GuIn* in;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_buffered_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
if (bis->curr == bis->have) {
|
||||
bis->curr = 0;
|
||||
bis->have = gu_in_some(bis->in, bis->buf, bis->alloc, err);
|
||||
if (!gu_ok(err)) return NULL;
|
||||
}
|
||||
*sz_out = bis->have - bis->curr;
|
||||
return &bis->buf[bis->curr];
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buffered_in_end_buffer(GuInStream* self, size_t consumed, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
gu_require(consumed < bis->have - bis->curr);
|
||||
bis->curr += consumed;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_buffered_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
return gu_in_some(bis->in, dst, sz, err);
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_buffered_in(GuIn* in, size_t buf_sz, GuPool* pool)
|
||||
{
|
||||
GuBufferedInStream* bis = gu_new_flex(pool, GuBufferedInStream,
|
||||
buf, buf_sz);
|
||||
bis->stream = (GuInStream) {
|
||||
.begin_buffer = gu_buffered_in_begin_buffer,
|
||||
.end_buffer = gu_buffered_in_end_buffer,
|
||||
.input = gu_buffered_in_input
|
||||
};
|
||||
bis->have = bis->curr = 0;
|
||||
bis->alloc = buf_sz;
|
||||
return gu_new_in(&bis->stream, pool);
|
||||
}
|
||||
|
||||
typedef struct GuDataIn GuDataIn;
|
||||
|
||||
struct GuDataIn {
|
||||
GuInStream stream;
|
||||
const uint8_t* data;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_data_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuDataIn* di = gu_container(self, GuDataIn, stream);
|
||||
const uint8_t* buf = di->data;
|
||||
if (buf) {
|
||||
*sz_out = di->sz;
|
||||
di->data = NULL;
|
||||
di->sz = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_data_in(const uint8_t* data, size_t sz, GuPool* pool)
|
||||
{
|
||||
GuDataIn* di = gu_new_s(pool, GuDataIn,
|
||||
.stream.begin_buffer = gu_data_in_begin_buffer,
|
||||
.data = data,
|
||||
.sz = sz);
|
||||
return gu_new_in(&di->stream, pool);
|
||||
}
|
||||
|
||||
extern inline uint8_t
|
||||
gu_in_u8(GuIn* restrict in, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err);
|
||||
|
||||
extern inline int
|
||||
gu_in_peek_u8(GuIn* restrict in);
|
||||
|
||||
extern inline void
|
||||
gu_in_consume(GuIn* restrict in, size_t sz);
|
||||
146
src/runtime/c/gu/in.h
Normal file
146
src/runtime/c/gu/in.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef GU_IN_H_
|
||||
#define GU_IN_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
typedef struct GuInStream GuInStream;
|
||||
|
||||
struct GuInStream {
|
||||
const uint8_t* (*begin_buffer)(GuInStream* self, size_t* sz_out,
|
||||
GuExn* err);
|
||||
void (*end_buffer)(GuInStream* self, size_t consumed, GuExn* err);
|
||||
size_t (*input)(GuInStream* self, uint8_t* buf, size_t max_sz,
|
||||
GuExn* err);
|
||||
};
|
||||
|
||||
typedef struct GuIn GuIn;
|
||||
|
||||
struct GuIn {
|
||||
const uint8_t* restrict buf_end;
|
||||
ptrdiff_t buf_curr;
|
||||
size_t buf_size;
|
||||
GuInStream* stream;
|
||||
GuFinalizer fini;
|
||||
};
|
||||
|
||||
|
||||
GuIn
|
||||
gu_init_in(GuInStream* stream);
|
||||
|
||||
GuIn*
|
||||
gu_new_in(GuInStream* stream, GuPool* pool);
|
||||
|
||||
GuInStream*
|
||||
gu_in_proxy_stream(GuIn* in, GuPool* pool);
|
||||
|
||||
const uint8_t*
|
||||
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err);
|
||||
|
||||
void
|
||||
gu_in_end_span(GuIn* in, size_t consumed);
|
||||
|
||||
size_t
|
||||
gu_in_some(GuIn* in, uint8_t* buf, size_t max_len, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err)
|
||||
{
|
||||
gu_require(sz < PTRDIFF_MAX);
|
||||
ptrdiff_t curr = in->buf_curr;
|
||||
ptrdiff_t new_curr = curr + (ptrdiff_t) sz;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
extern void gu_in_bytes_(GuIn* in, uint8_t* buf, size_t sz,
|
||||
GuExn* err);
|
||||
gu_in_bytes_(in, buf, sz, err);
|
||||
return;
|
||||
}
|
||||
memcpy(buf, &in->buf_end[curr], sz);
|
||||
in->buf_curr = new_curr;
|
||||
}
|
||||
|
||||
inline int
|
||||
gu_in_peek_u8(GuIn* restrict in)
|
||||
{
|
||||
if (GU_UNLIKELY(in->buf_curr == 0)) {
|
||||
return -1;
|
||||
}
|
||||
return in->buf_end[in->buf_curr];
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_in_consume(GuIn* restrict in, size_t sz)
|
||||
{
|
||||
gu_require((ptrdiff_t) sz + in->buf_curr <= 0);
|
||||
in->buf_curr += sz;
|
||||
}
|
||||
|
||||
|
||||
inline uint8_t
|
||||
gu_in_u8(GuIn* restrict in, GuExn* err)
|
||||
{
|
||||
if (GU_UNLIKELY(in->buf_curr == 0)) {
|
||||
extern uint8_t gu_in_u8_(GuIn* restrict in, GuExn* err);
|
||||
return gu_in_u8_(in, err);
|
||||
}
|
||||
return in->buf_end[in->buf_curr++];
|
||||
}
|
||||
|
||||
int8_t
|
||||
gu_in_s8(GuIn* in, GuExn* err);
|
||||
|
||||
uint16_t
|
||||
gu_in_u16le(GuIn* in, GuExn* err);
|
||||
|
||||
uint16_t
|
||||
gu_in_u16be(GuIn* in, GuExn* err);
|
||||
|
||||
int16_t
|
||||
gu_in_s16le(GuIn* in, GuExn* err);
|
||||
|
||||
int16_t
|
||||
gu_in_s16be(GuIn* in, GuExn* err);
|
||||
|
||||
uint32_t
|
||||
gu_in_u32le(GuIn* in, GuExn* err);
|
||||
|
||||
uint32_t
|
||||
gu_in_u32be(GuIn* in, GuExn* err);
|
||||
|
||||
int32_t
|
||||
gu_in_s32le(GuIn* in, GuExn* err);
|
||||
|
||||
int32_t
|
||||
gu_in_s32be(GuIn* in, GuExn* err);
|
||||
|
||||
uint64_t
|
||||
gu_in_u64le(GuIn* in, GuExn* err);
|
||||
|
||||
uint64_t
|
||||
gu_in_u64be(GuIn* in, GuExn* err);
|
||||
|
||||
int64_t
|
||||
gu_in_s64le(GuIn* in, GuExn* err);
|
||||
|
||||
int64_t
|
||||
gu_in_s64be(GuIn* in, GuExn* err);
|
||||
|
||||
double
|
||||
gu_in_f64le(GuIn* in, GuExn* err);
|
||||
|
||||
double
|
||||
gu_in_f64be(GuIn* in, GuExn* err);
|
||||
|
||||
GuIn*
|
||||
gu_buffered_in(GuIn* in, size_t sz, GuPool* pool);
|
||||
|
||||
GuIn*
|
||||
gu_data_in(const uint8_t* buf, size_t size, GuPool* pool);
|
||||
|
||||
|
||||
extern GU_DECLARE_TYPE(GuEOF, abstract);
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
#endif // GU_IN_H_
|
||||
59
src/runtime/c/gu/intern.c
Normal file
59
src/runtime/c/gu/intern.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "intern.h"
|
||||
|
||||
struct GuIntern {
|
||||
GuPool* str_pool;
|
||||
GuMap* map;
|
||||
};
|
||||
|
||||
GuIntern*
|
||||
gu_new_intern(GuPool* str_pool, GuPool* pool)
|
||||
{
|
||||
GuIntern* intern = gu_new(GuIntern, pool);
|
||||
intern->str_pool = str_pool;
|
||||
intern->map = gu_new_set(const char*, gu_str_hasher, pool);
|
||||
return intern;
|
||||
}
|
||||
|
||||
const char*
|
||||
gu_intern_str(GuIntern* intern, const char* cstr)
|
||||
{
|
||||
const char* const* strp = gu_map_find_key(intern->map, &cstr);
|
||||
if (strp) {
|
||||
return *strp;
|
||||
}
|
||||
const char* str = gu_strdup(cstr, intern->str_pool);
|
||||
gu_map_insert(intern->map, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct GuSymTable {
|
||||
GuPool* sym_pool;
|
||||
GuMap* map;
|
||||
};
|
||||
|
||||
GuSymTable*
|
||||
gu_new_symtable(GuPool* sym_pool, GuPool* pool)
|
||||
{
|
||||
GuSymTable* tab = gu_new(GuSymTable, pool);
|
||||
tab->sym_pool = sym_pool;
|
||||
tab->map = gu_new_set(GuSymbol, gu_string_hasher, pool);
|
||||
return tab;
|
||||
}
|
||||
|
||||
GuSymbol
|
||||
gu_symtable_intern(GuSymTable* tab, GuString string)
|
||||
{
|
||||
if (gu_string_is_stable(string)) {
|
||||
return string;
|
||||
}
|
||||
const GuSymbol* symp = gu_map_find_key(tab->map, &string);
|
||||
if (symp) {
|
||||
return *symp;
|
||||
}
|
||||
GuSymbol sym = gu_string_copy(string, tab->sym_pool);
|
||||
gu_map_insert(tab->map, &sym);
|
||||
return sym;
|
||||
}
|
||||
24
src/runtime/c/gu/intern.h
Normal file
24
src/runtime/c/gu/intern.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef GU_INTERN_H_
|
||||
#define GU_INTERN_H_
|
||||
|
||||
#include <gu/map.h>
|
||||
#include <gu/str.h>
|
||||
#include <gu/string.h>
|
||||
|
||||
typedef struct GuIntern GuIntern;
|
||||
|
||||
GuIntern* gu_new_intern(GuPool* str_pool, GuPool* pool);
|
||||
const char* gu_intern_str(GuIntern* intern, const char* cstr);
|
||||
|
||||
|
||||
typedef struct GuSymTable GuSymTable;
|
||||
|
||||
typedef GuString GuSymbol;
|
||||
|
||||
GuSymTable*
|
||||
gu_new_symtable(GuPool* sym_pool, GuPool* pool);
|
||||
|
||||
GuSymbol
|
||||
gu_symtable_intern(GuSymTable* symtab, GuString string);
|
||||
|
||||
#endif /* GU_INTERN_H_ */
|
||||
59
src/runtime/c/gu/list.c
Normal file
59
src/runtime/c/gu/list.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gu/list.h>
|
||||
#include <gu/assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static const int gu_list_empty = 0;
|
||||
|
||||
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
|
||||
int n_elems, size_t alignment)
|
||||
{
|
||||
gu_assert(n_elems >= 0);
|
||||
if (n_elems == 0) {
|
||||
return (void*) &gu_list_empty;
|
||||
}
|
||||
// XXX: use gu_flex_size, use offset of elems
|
||||
void* p = gu_malloc_aligned(pool, base_size + elem_size * n_elems,
|
||||
alignment);
|
||||
*(int*) p = n_elems;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
GU_DEFINE_KIND(GuList, abstract);
|
||||
|
||||
// GU_DEFINE_TYPE(GuStrs, GuList, gu_type(GuStr));
|
||||
// GU_DEFINE_TYPE(GuStrsP, pointer, gu_type(GuStrs));
|
||||
|
||||
void*
|
||||
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool)
|
||||
{
|
||||
return gu_list_alloc(pool, ltype->size,
|
||||
gu_type_size(ltype->elem_type),
|
||||
n_elems, ltype->align);
|
||||
}
|
||||
|
||||
void*
|
||||
gu_list_type_index(GuListType* ltype, void* list, int i)
|
||||
{
|
||||
uint8_t* p = list;
|
||||
return &p[ltype->elems_offset + i * gu_type_size(ltype->elem_type)];
|
||||
}
|
||||
140
src/runtime/c/gu/list.h
Normal file
140
src/runtime/c/gu/list.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Lists.
|
||||
*/
|
||||
|
||||
#ifndef GU_LIST_H_
|
||||
#define GU_LIST_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
|
||||
#define GuList(t) \
|
||||
struct { \
|
||||
const int len; \
|
||||
t elems[]; \
|
||||
}
|
||||
|
||||
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
|
||||
int n_elems, size_t alignment);
|
||||
|
||||
#define gu_new_list(t, pool, n) \
|
||||
((t*) gu_list_alloc(pool, \
|
||||
sizeof(t), \
|
||||
sizeof(((t*)NULL)->elems[0]), \
|
||||
(n), \
|
||||
gu_flex_alignof(t)))
|
||||
|
||||
static inline int
|
||||
gu_list_length(const void* list)
|
||||
{
|
||||
return *(const int*) list;
|
||||
}
|
||||
|
||||
#define gu_list_elems(lst) \
|
||||
((lst)->elems)
|
||||
|
||||
#define gu_list_index(lst, i) \
|
||||
(gu_list_elems(lst)[i])
|
||||
|
||||
typedef GuList(void*) GuPointers;
|
||||
//typedef GuList(uint8_t) GuBytes;
|
||||
|
||||
typedef GuList(int) GuInts;
|
||||
|
||||
|
||||
#define GuListN(t_, len_) \
|
||||
struct { \
|
||||
int len; \
|
||||
t elems[len_]; \
|
||||
}
|
||||
|
||||
#define gu_list_(qual_, t_, ...) \
|
||||
((qual_ GuList(t_) *) \
|
||||
((qual_ GuListN(t_, (sizeof((t_[]){__VA_ARGS__}) / sizeof(t_)))[]){ \
|
||||
__VA_ARGS__ \
|
||||
}))
|
||||
|
||||
#define gu_list(t_, ...) \
|
||||
gu_list_(, t_, __VA_ARGS__)
|
||||
|
||||
#define gu_clist(t_, ...) \
|
||||
gu_list_(const, t_, __VA_ARGS__)
|
||||
|
||||
#define GuSList(t) \
|
||||
const struct { \
|
||||
int len; \
|
||||
t* elems; \
|
||||
}
|
||||
|
||||
#define GU_SLIST_0 { .len = 0, .elems = NULL }
|
||||
|
||||
#define GU_SLIST(t, ...) \
|
||||
{ \
|
||||
.len = GU_ARRAY_LEN(t,GU_ID({__VA_ARGS__})), \
|
||||
.elems = ((t[]){__VA_ARGS__}) \
|
||||
}
|
||||
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
//
|
||||
// list
|
||||
//
|
||||
|
||||
typedef const struct GuListType GuListType, GuType_GuList;
|
||||
|
||||
struct GuListType {
|
||||
GuType_abstract abstract_base;
|
||||
size_t size;
|
||||
size_t align;
|
||||
GuType* elem_type;
|
||||
ptrdiff_t elems_offset;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuList(k_, t_, elem_type_) { \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
|
||||
.size = sizeof(t_), \
|
||||
.align = gu_alignof(t_), \
|
||||
.elem_type = elem_type_, \
|
||||
.elems_offset = offsetof(t_, elems) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(GuList);
|
||||
|
||||
void*
|
||||
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool);
|
||||
|
||||
void*
|
||||
gu_list_type_index(GuListType* ltype, void* list, int i);
|
||||
|
||||
#include <gu/str.h>
|
||||
|
||||
|
||||
typedef GuList(GuStr) GuStrs;
|
||||
typedef GuStrs* GuStrsP;
|
||||
|
||||
extern GU_DECLARE_TYPE(GuStrs, GuList);
|
||||
extern GU_DECLARE_TYPE(GuStrsP, pointer);
|
||||
|
||||
|
||||
#endif // GU_LIST_H_
|
||||
79
src/runtime/c/gu/log.c
Normal file
79
src/runtime/c/gu/log.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static int gu_log_depth = 0;
|
||||
|
||||
static bool
|
||||
gu_log_match(const char* pat, size_t patlen, const char* str)
|
||||
{
|
||||
if (patlen > 0 && pat[patlen-1] == '*') {
|
||||
return strncmp(pat, str, patlen-1) == 0;
|
||||
} else if (strlen(str) == patlen) {
|
||||
return strncmp(pat, str, patlen) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_log_enabled(const char* func, const char* file)
|
||||
{
|
||||
const char* cfg = getenv("GU_LOG");
|
||||
if (cfg == NULL) {
|
||||
return false;
|
||||
}
|
||||
const char* p = cfg;
|
||||
while (true) {
|
||||
size_t len = strcspn(p, ",");
|
||||
if (gu_log_match(p, len, func)) {
|
||||
return true;
|
||||
}
|
||||
if (gu_log_match(p, len, file)) {
|
||||
return true;
|
||||
}
|
||||
if (p[len] == '\0') {
|
||||
break;
|
||||
}
|
||||
p = &p[len + 1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, va_list args)
|
||||
{
|
||||
(void) (kind && line);
|
||||
if (!gu_log_enabled(func, file)) {
|
||||
return;
|
||||
}
|
||||
if (kind == GU_LOG_KIND_EXIT) {
|
||||
gu_log_depth--;
|
||||
}
|
||||
if (fmt) {
|
||||
int indent = gu_min(32 + gu_log_depth, 48);
|
||||
fprintf(stderr, "%-*s: ", indent, func);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (kind == GU_LOG_KIND_ENTER) {
|
||||
gu_log_depth++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_log_full_v(kind, func, file, line, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
65
src/runtime/c/gu/log.h
Normal file
65
src/runtime/c/gu/log.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef GU_LOG_H_
|
||||
#define GU_LOG_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef enum GuLogKind {
|
||||
GU_LOG_KIND_ENTER,
|
||||
GU_LOG_KIND_EXIT,
|
||||
GU_LOG_KIND_DEBUG,
|
||||
GU_LOG_KIND_ERROR
|
||||
} GuLogKind;
|
||||
|
||||
void
|
||||
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, ...);
|
||||
|
||||
|
||||
void
|
||||
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, va_list args);
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#define gu_logv(kind_, fmt_, args_) \
|
||||
gu_log_full_v(kind_, __func__, __FILE__, __LINE__, fmt_, args_)
|
||||
|
||||
#define gu_log(kind_, ...) \
|
||||
gu_log_full(kind_, __func__, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
gu_logv(GuLogKind kind, const char* fmt, va_list args)
|
||||
{
|
||||
(void) kind;
|
||||
(void) fmt;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_log(GuLogKind kind, const char* fmt, ...)
|
||||
{
|
||||
(void) kind;
|
||||
(void) fmt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#define gu_enter(...) \
|
||||
gu_log(GU_LOG_KIND_ENTER, __VA_ARGS__)
|
||||
|
||||
#define gu_exit(...) \
|
||||
gu_log(GU_LOG_KIND_EXIT, __VA_ARGS__)
|
||||
|
||||
#define gu_debug(...) \
|
||||
gu_log(GU_LOG_KIND_DEBUG, __VA_ARGS__)
|
||||
|
||||
#define gu_debugv(kind_, fmt_, args_) \
|
||||
gu_logv(GU_LOG_KIND_DEBUG, fmt_, args_)
|
||||
|
||||
#endif // GU_LOG_H_
|
||||
353
src/runtime/c/gu/map.c
Normal file
353
src/runtime/c/gu/map.c
Normal file
@@ -0,0 +1,353 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/prime.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
typedef enum {
|
||||
GU_MAP_GENERIC,
|
||||
GU_MAP_ADDR,
|
||||
GU_MAP_WORD
|
||||
} GuMapKind;
|
||||
|
||||
typedef struct GuMapData GuMapData;
|
||||
|
||||
struct GuMapData {
|
||||
uint8_t* keys;
|
||||
uint8_t* values;
|
||||
size_t n_occupied;
|
||||
size_t n_entries;
|
||||
size_t zero_idx;
|
||||
};
|
||||
|
||||
struct GuMap {
|
||||
GuMapKind const kind;
|
||||
GuHasher* const hasher;
|
||||
size_t const key_size;
|
||||
size_t const value_size;
|
||||
const void* default_value;
|
||||
GuMapData data;
|
||||
|
||||
GuFinalizer fin;
|
||||
};
|
||||
|
||||
static void
|
||||
gu_map_finalize(GuFinalizer* fin)
|
||||
{
|
||||
GuMap* map = gu_container(fin, GuMap, fin);
|
||||
gu_mem_buf_free(map->data.keys);
|
||||
if (map->value_size) {
|
||||
gu_mem_buf_free(map->data.values);
|
||||
}
|
||||
}
|
||||
|
||||
static const GuWord gu_map_empty_key = 0;
|
||||
|
||||
static bool
|
||||
gu_map_buf_is_zero(const uint8_t* p, size_t sz) {
|
||||
while (sz >= sizeof(GuWord)) {
|
||||
sz -= sizeof(GuWord);
|
||||
if (memcmp(&p[sz], &gu_map_empty_key, sizeof(GuWord)) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (memcmp(p, &gu_map_empty_key, sz) == 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_map_entry_is_free(GuMap* map, GuMapData* data, size_t idx)
|
||||
{
|
||||
if (idx == data->zero_idx) {
|
||||
return false;
|
||||
} else if (map->kind == GU_MAP_ADDR) {
|
||||
const void* key = ((const void**)data->keys)[idx];
|
||||
return key == NULL;
|
||||
} else if (map->kind == GU_MAP_WORD) {
|
||||
GuWord key = ((GuWord*)data->keys)[idx];
|
||||
return key == 0;
|
||||
}
|
||||
gu_assert(map->kind == GU_MAP_GENERIC);
|
||||
const void* key = &data->keys[idx * map->key_size];
|
||||
return gu_map_buf_is_zero(key, map->key_size);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_map_lookup(GuMap* map, const void* key, size_t* idx_out)
|
||||
{
|
||||
size_t n = map->data.n_entries;
|
||||
switch (map->kind) {
|
||||
case GU_MAP_GENERIC: {
|
||||
GuHasher* hasher = map->hasher;
|
||||
GuEquality* eq = (GuEquality*) hasher;
|
||||
GuHash hash = hasher->hash(hasher, key);
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
size_t key_size = map->key_size;
|
||||
while (true) {
|
||||
void* entry_key = &map->data.keys[idx * key_size];
|
||||
if (gu_map_buf_is_zero(entry_key, key_size) &&
|
||||
map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (eq->is_equal(eq, key, entry_key)) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
case GU_MAP_ADDR: {
|
||||
GuHash hash = (GuHash) key;
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
while (true) {
|
||||
const void* entry_key =
|
||||
((const void**)map->data.keys)[idx];
|
||||
if (entry_key == NULL && map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (entry_key == key) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
case GU_MAP_WORD: {
|
||||
GuWord w = *(const GuWord*)key;
|
||||
GuHash hash = (GuHash) w;
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
while (true) {
|
||||
GuWord entry_key = ((GuWord*)map->data.keys)[idx];
|
||||
if (entry_key == 0 && map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (entry_key == w) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
gu_impossible();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_map_resize(GuMap* map)
|
||||
{
|
||||
GuMapData* data = &map->data;
|
||||
GuMapData old_data = *data;
|
||||
size_t req_entries =
|
||||
gu_twin_prime_sup(GU_MAX(11, map->data.n_occupied * 4 / 3 + 1));
|
||||
|
||||
size_t key_size = map->key_size;
|
||||
size_t key_alloc = 0;
|
||||
data->keys = gu_mem_buf_alloc(req_entries * key_size, &key_alloc);
|
||||
|
||||
size_t value_size = map->value_size;
|
||||
size_t value_alloc = 0;
|
||||
if (value_size) {
|
||||
data->values = gu_mem_buf_alloc(req_entries * value_size,
|
||||
&value_alloc);
|
||||
memset(data->values, 0, value_alloc);
|
||||
}
|
||||
|
||||
data->n_entries = gu_twin_prime_inf(value_size ?
|
||||
GU_MIN(key_alloc / key_size,
|
||||
value_alloc / value_size)
|
||||
: key_alloc / key_size);
|
||||
switch (map->kind) {
|
||||
case GU_MAP_GENERIC:
|
||||
case GU_MAP_WORD:
|
||||
memset(data->keys, 0, key_alloc);
|
||||
break;
|
||||
case GU_MAP_ADDR:
|
||||
for (size_t i = 0; i < data->n_entries; i++) {
|
||||
((const void**)data->keys)[i] = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
|
||||
gu_assert(data->n_entries > data->n_occupied);
|
||||
gu_debug("Resized to %d entries", data->n_entries);
|
||||
|
||||
data->n_occupied = 0;
|
||||
data->zero_idx = SIZE_MAX;
|
||||
|
||||
for (size_t i = 0; i < old_data.n_entries; i++) {
|
||||
if (gu_map_entry_is_free(map, &old_data, i)) {
|
||||
continue;
|
||||
}
|
||||
void* old_key = &old_data.keys[i * key_size];
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
old_key = *(void**)old_key;
|
||||
}
|
||||
void* old_value = &old_data.values[i * value_size];
|
||||
|
||||
memcpy(gu_map_insert(map, old_key),
|
||||
old_value, map->value_size);
|
||||
}
|
||||
|
||||
gu_mem_buf_free(old_data.keys);
|
||||
if (value_size) {
|
||||
gu_mem_buf_free(old_data.values);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
gu_map_maybe_resize(GuMap* map)
|
||||
{
|
||||
if (map->data.n_entries <=
|
||||
map->data.n_occupied + (map->data.n_occupied / 4)) {
|
||||
gu_map_resize(map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_map_find(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (found) {
|
||||
return &map->data.values[idx * map->value_size];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_map_find_default(GuMap* map, const void* key)
|
||||
{
|
||||
void* p = gu_map_find(map, key);
|
||||
return p ? p : map->default_value;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_map_find_key(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (found) {
|
||||
return &map->data.keys[idx * map->key_size];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
gu_map_insert(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (!found) {
|
||||
if (gu_map_maybe_resize(map)) {
|
||||
found = gu_map_lookup(map, key, &idx);
|
||||
gu_assert(!found);
|
||||
}
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
((const void**)map->data.keys)[idx] = key;
|
||||
} else {
|
||||
memcpy(&map->data.keys[idx * map->key_size],
|
||||
key, map->key_size);
|
||||
}
|
||||
if (map->default_value) {
|
||||
memcpy(&map->data.values[idx * map->value_size],
|
||||
map->default_value, map->value_size);
|
||||
}
|
||||
if (gu_map_entry_is_free(map, &map->data, idx)) {
|
||||
gu_assert(map->data.zero_idx == SIZE_MAX);
|
||||
map->data.zero_idx = idx;
|
||||
}
|
||||
map->data.n_occupied++;
|
||||
}
|
||||
return &map->data.values[idx * map->value_size];
|
||||
}
|
||||
|
||||
void
|
||||
gu_map_iter(GuMap* map, GuMapItor* itor, GuExn* err)
|
||||
{
|
||||
for (size_t i = 0; i < map->data.n_entries && gu_ok(err); i++) {
|
||||
if (gu_map_entry_is_free(map, &map->data, i)) {
|
||||
continue;
|
||||
}
|
||||
const void* key = &map->data.keys[i * map->key_size];
|
||||
void* value = &map->data.values[i * map->value_size];
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
key = *(const void* const*) key;
|
||||
}
|
||||
itor->fn(itor, key, value, err);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t gu_map_no_values[1] = { 0 };
|
||||
|
||||
GuMap*
|
||||
gu_make_map(size_t key_size, GuHasher* hasher,
|
||||
size_t value_size, const void* default_value,
|
||||
GuPool* pool)
|
||||
{
|
||||
GuMapKind kind =
|
||||
((!hasher || hasher == gu_addr_hasher)
|
||||
? GU_MAP_ADDR
|
||||
: (key_size == sizeof(GuWord) && hasher == gu_word_hasher)
|
||||
? GU_MAP_WORD
|
||||
: GU_MAP_GENERIC);
|
||||
if (kind == GU_MAP_ADDR) {
|
||||
key_size = sizeof(GuWord);
|
||||
}
|
||||
GuMapData data = {
|
||||
.n_occupied = 0,
|
||||
.n_entries = 0,
|
||||
.keys = NULL,
|
||||
.values = value_size ? NULL : (uint8_t*) gu_map_no_values,
|
||||
.zero_idx = SIZE_MAX
|
||||
};
|
||||
GuMap* map = gu_new_i(
|
||||
pool, GuMap,
|
||||
.default_value = default_value,
|
||||
.hasher = hasher,
|
||||
.data = data,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.fin.fn = gu_map_finalize,
|
||||
.kind = kind
|
||||
);
|
||||
gu_pool_finally(pool, &map->fin);
|
||||
gu_map_resize(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
GuMap*
|
||||
gu_map_type_make(GuMapType* mtype, GuPool* pool)
|
||||
{
|
||||
size_t key_size = 0;
|
||||
if (mtype->hasher && mtype->hasher != gu_addr_hasher) {
|
||||
key_size = gu_type_size(mtype->key_type);
|
||||
}
|
||||
size_t value_size = gu_type_size(mtype->value_type);
|
||||
return gu_make_map(key_size, mtype->hasher,
|
||||
value_size, mtype->default_value, pool);
|
||||
}
|
||||
|
||||
GU_DEFINE_KIND(GuMap, abstract);
|
||||
// GU_DEFINE_KIND(GuIntMap, GuMap);
|
||||
|
||||
|
||||
121
src/runtime/c/gu/map.h
Normal file
121
src/runtime/c/gu/map.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#ifndef GU_MAP_H_
|
||||
#define GU_MAP_H_
|
||||
|
||||
#include <gu/hash.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/exn.h>
|
||||
|
||||
typedef const struct GuMapItor GuMapItor;
|
||||
|
||||
struct GuMapItor {
|
||||
void (*fn)(GuMapItor* self, const void* key, void* value,
|
||||
GuExn *err);
|
||||
};
|
||||
|
||||
typedef struct GuMap GuMap;
|
||||
|
||||
GuMap*
|
||||
gu_make_map(size_t key_size, GuHasher* hasher,
|
||||
size_t value_size, const void* default_value,
|
||||
GuPool* pool);
|
||||
|
||||
#define gu_new_map(K, HASHER, V, DV, POOL) \
|
||||
(gu_make_map(sizeof(K), (HASHER), sizeof(V), (DV), (POOL)))
|
||||
|
||||
#define gu_new_set(K, HASHER, POOL) \
|
||||
(gu_make_map(sizeof(K), (HASHER), 0, NULL, (POOL)))
|
||||
|
||||
#define gu_new_addr_map(K, V, DV, POOL) \
|
||||
(gu_make_map(0, NULL, sizeof(V), (DV), (POOL)))
|
||||
|
||||
size_t
|
||||
gu_map_count(GuMap* map);
|
||||
|
||||
void*
|
||||
gu_map_find_full(GuMap* ht, void* key_inout);
|
||||
|
||||
const void*
|
||||
gu_map_find_default(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_get(MAP, KEYP, V) \
|
||||
(*(V*)gu_map_find_default((MAP), (KEYP)))
|
||||
|
||||
void*
|
||||
gu_map_find(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_set(MAP, KEYP, V, VAL) \
|
||||
GU_BEGIN \
|
||||
V* gu_map_set_p_ = gu_map_find((MAP), (KEYP)); \
|
||||
*gu_map_set_p_ = (VAL); \
|
||||
GU_END
|
||||
|
||||
const void*
|
||||
gu_map_find_key(GuMap* ht, const void* key);
|
||||
|
||||
static inline bool
|
||||
gu_map_has(GuMap* ht, const void* key)
|
||||
{
|
||||
return gu_map_find_key(ht, key) != NULL;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
gu_map_insert(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_put(MAP, KEYP, V, VAL) \
|
||||
GU_BEGIN \
|
||||
V* gu_map_put_p_ = gu_map_insert((MAP), (KEYP)); \
|
||||
*gu_map_put_p_ = (VAL); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_map_iter(GuMap* ht, GuMapItor* itor, GuExn* err);
|
||||
|
||||
|
||||
typedef GuMap GuIntMap;
|
||||
|
||||
#define gu_new_int_map(VAL_T, DEFAULT, POOL) \
|
||||
gu_new_map(int, gu_int_hasher, VAL_T, DEFAULT, POOL)
|
||||
|
||||
|
||||
#if defined(GU_TYPE_H_) && !defined(GU_MAP_H_TYPE_)
|
||||
#define GU_MAP_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuMap);
|
||||
|
||||
typedef const struct GuMapType GuMapType, GuType_GuMap;
|
||||
|
||||
struct GuMapType {
|
||||
GuType_abstract abstract_base;
|
||||
GuHasher* hasher;
|
||||
GuType* key_type;
|
||||
GuType* value_type;
|
||||
const void* default_value;
|
||||
};
|
||||
|
||||
GuMap*
|
||||
gu_map_type_make(GuMapType* mtype, GuPool* pool);
|
||||
|
||||
#define gu_map_type_new(MAP_T, POOL) \
|
||||
gu_map_type_make(gu_type_cast(gu_type(MAP_T), GuMap), (POOL))
|
||||
|
||||
#define GU_TYPE_INIT_GuMap(k_, t_, kt_, h_, vt_, dv_) \
|
||||
{ \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
|
||||
.hasher = h_, \
|
||||
.key_type = kt_, \
|
||||
.value_type = vt_, \
|
||||
.default_value = dv_ \
|
||||
}
|
||||
|
||||
#define gu_type__GuIntMap gu_type__GuMap
|
||||
|
||||
typedef GuType_GuMap GuType_GuIntMap;
|
||||
|
||||
#define GU_TYPE_INIT_GuIntMap(KIND, MAP_T, VAL_T, DEFAULT) \
|
||||
GU_TYPE_INIT_GuMap(KIND, MAP_T, gu_type(int), gu_int_hasher, \
|
||||
VAL_T, DEFAULT)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // GU_MAP_H_
|
||||
346
src/runtime/c/gu/mem.c
Normal file
346
src/runtime/c/gu/mem.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/fun.h>
|
||||
#include <gu/bits.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/log.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef USE_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#define VG(X) X
|
||||
#else
|
||||
#define VG(X) GU_NOP
|
||||
#endif
|
||||
|
||||
static const size_t
|
||||
// Maximum request size for a chunk. The actual maximum chunk size
|
||||
// may be somewhat larger.
|
||||
gu_mem_chunk_max_size = 1024 * sizeof(void*),
|
||||
|
||||
// number of bytes to allocate in the pool when it is created
|
||||
gu_mem_pool_initial_size = 24 * sizeof(void*),
|
||||
|
||||
// Pool allocations larger than this will get their own chunk if
|
||||
// there's no room in the current one. Allocations smaller than this may trigger
|
||||
// the creation of a new chunk, in which case the remaining space in
|
||||
// the current chunk is left unused (internal fragmentation).
|
||||
gu_mem_max_shared_alloc = 64 * sizeof(void*),
|
||||
|
||||
// Should not be smaller than the granularity for malloc
|
||||
gu_mem_unit_size = 2 * sizeof(void*),
|
||||
|
||||
/* Malloc tuning: the additional memory used by malloc next to the
|
||||
allocated object */
|
||||
gu_malloc_overhead = sizeof(size_t);
|
||||
|
||||
static void*
|
||||
gu_mem_realloc(void* p, size_t size)
|
||||
{
|
||||
void* buf = realloc(p, size);
|
||||
if (size != 0 && buf == NULL) {
|
||||
gu_fatal("Memory allocation failed");
|
||||
}
|
||||
gu_debug("%p %zu -> %p", p, size, buf); // strictly illegal
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void*
|
||||
gu_mem_alloc(size_t size)
|
||||
{
|
||||
void* buf = malloc(size);
|
||||
if (buf == NULL) {
|
||||
gu_fatal("Memory allocation failed");
|
||||
}
|
||||
gu_debug("%zu -> %p", size, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_mem_free(void* p)
|
||||
{
|
||||
gu_debug("%p", p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_mem_padovan(size_t min)
|
||||
{
|
||||
// This could in principle be done faster with Q-matrices for
|
||||
// Padovan numbers, but not really worth it for our commonly
|
||||
// small numbers.
|
||||
if (min <= 5) {
|
||||
return min;
|
||||
}
|
||||
size_t a = 7, b = 9, c = 12;
|
||||
while (min > a) {
|
||||
if (b < a) {
|
||||
// overflow
|
||||
return SIZE_MAX;
|
||||
}
|
||||
size_t tmp = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
c = tmp;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_mem_buf_realloc(void* old_buf, size_t min_size, size_t* real_size_out)
|
||||
{
|
||||
size_t min_blocks = ((min_size + gu_malloc_overhead - 1) /
|
||||
gu_mem_unit_size) + 1;
|
||||
size_t blocks = gu_mem_padovan(min_blocks);
|
||||
size_t size = blocks * gu_mem_unit_size - gu_malloc_overhead;
|
||||
void* buf = gu_mem_realloc(old_buf, size);
|
||||
*real_size_out = buf ? size : 0;
|
||||
return buf;
|
||||
}
|
||||
void*
|
||||
gu_mem_buf_alloc(size_t min_size, size_t* real_size_out)
|
||||
{
|
||||
return gu_mem_buf_realloc(NULL, min_size, real_size_out);
|
||||
}
|
||||
|
||||
void
|
||||
gu_mem_buf_free(void* buf)
|
||||
{
|
||||
gu_mem_free(buf);
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuMemChunk GuMemChunk;
|
||||
|
||||
struct GuMemChunk {
|
||||
GuMemChunk* next;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
typedef struct GuFinalizerNode GuFinalizerNode;
|
||||
|
||||
struct GuFinalizerNode {
|
||||
GuFinalizerNode* next;
|
||||
GuFinalizer* fin;
|
||||
};
|
||||
|
||||
enum GuPoolFlags {
|
||||
GU_POOL_LOCAL = 1 << 0
|
||||
};
|
||||
|
||||
struct GuPool {
|
||||
uint8_t* curr_buf; // actually GuMemChunk*
|
||||
GuMemChunk* chunks;
|
||||
GuFinalizerNode* finalizers;
|
||||
uint16_t flags;
|
||||
uint16_t left_edge;
|
||||
uint16_t right_edge;
|
||||
uint16_t curr_size;
|
||||
uint8_t init_buf[];
|
||||
};
|
||||
|
||||
static GuPool*
|
||||
gu_init_pool(uint8_t* buf, size_t sz)
|
||||
{
|
||||
gu_require(gu_aligned((uintptr_t) (void*) buf, gu_alignof(GuPool)));
|
||||
gu_require(sz >= sizeof(GuPool));
|
||||
GuPool* pool = (GuPool*) buf;
|
||||
pool->flags = 0;
|
||||
pool->curr_size = sz;
|
||||
pool->curr_buf = (uint8_t*) pool;
|
||||
pool->chunks = NULL;
|
||||
pool->finalizers = NULL;
|
||||
pool->left_edge = offsetof(GuPool, init_buf);
|
||||
pool->right_edge = sz;
|
||||
VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
|
||||
return pool;
|
||||
}
|
||||
|
||||
GuPool*
|
||||
gu_local_pool_(uint8_t* buf, size_t sz)
|
||||
{
|
||||
GuPool* pool = gu_init_pool(buf, sz);
|
||||
pool->flags |= GU_POOL_LOCAL;
|
||||
gu_debug("%p", pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
GuPool*
|
||||
gu_new_pool(void)
|
||||
{
|
||||
size_t sz = GU_FLEX_SIZE(GuPool, init_buf, gu_mem_pool_initial_size);
|
||||
uint8_t* buf = gu_mem_buf_alloc(sz, &sz);
|
||||
GuPool* pool = gu_init_pool(buf, sz);
|
||||
gu_debug("%p", pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_pool_expand(GuPool* pool, size_t req)
|
||||
{
|
||||
size_t real_req = GU_MAX(req, GU_MIN(((size_t)pool->curr_size) + 1,
|
||||
gu_mem_chunk_max_size));
|
||||
gu_assert(real_req >= sizeof(GuMemChunk));
|
||||
size_t size = 0;
|
||||
GuMemChunk* chunk = gu_mem_buf_alloc(real_req, &size);
|
||||
chunk->next = pool->chunks;
|
||||
pool->chunks = chunk;
|
||||
pool->curr_buf = (uint8_t*) chunk;
|
||||
pool->left_edge = offsetof(GuMemChunk, data);
|
||||
pool->right_edge = pool->curr_size = size;
|
||||
// size should always fit in uint16_t
|
||||
gu_assert((size_t) pool->right_edge == size);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_mem_advance(size_t old_pos, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
size_t p = gu_align_forward(old_pos, pre_align);
|
||||
p += pre_size;
|
||||
p = gu_align_forward(p, align);
|
||||
p += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void*
|
||||
gu_pool_malloc_aligned(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
gu_require(size <= gu_mem_max_shared_alloc);
|
||||
size_t pos = gu_mem_advance(pool->left_edge, pre_align, pre_size,
|
||||
align, size);
|
||||
if (pos > (size_t) pool->right_edge) {
|
||||
pos = gu_mem_advance(offsetof(GuMemChunk, data),
|
||||
pre_align, pre_size, align, size);
|
||||
gu_pool_expand(pool, pos);
|
||||
gu_assert(pos <= pool->right_edge);
|
||||
}
|
||||
pool->left_edge = pos;
|
||||
uint8_t* addr = &pool->curr_buf[pos - size];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size, size + pre_size ));
|
||||
return addr;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_pool_avail(GuPool* pool)
|
||||
{
|
||||
return (size_t) pool->right_edge - (size_t) pool->left_edge;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_pool_malloc_unaligned(GuPool* pool, size_t size)
|
||||
{
|
||||
if (size > gu_pool_avail(pool)) {
|
||||
gu_pool_expand(pool, offsetof(GuMemChunk, data) + size);
|
||||
gu_assert(size <= gu_pool_avail(pool));
|
||||
}
|
||||
pool->right_edge -= size;
|
||||
void* addr = &pool->curr_buf[pool->right_edge];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr, size));
|
||||
return addr;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
gu_enter("-> %p %zu %zu %zu %zu",
|
||||
pool, pre_align, pre_size, align, size);
|
||||
void* ret = NULL;
|
||||
if (pre_align == 0) {
|
||||
pre_align = gu_alignof(GuMaxAlign);
|
||||
}
|
||||
if (align == 0) {
|
||||
align = gu_alignof(GuMaxAlign);
|
||||
}
|
||||
size_t full_size = gu_mem_advance(offsetof(GuMemChunk, data),
|
||||
pre_align, pre_size, align, size);
|
||||
if (full_size > gu_mem_max_shared_alloc) {
|
||||
GuMemChunk* chunk = gu_mem_alloc(full_size);
|
||||
chunk->next = pool->chunks;
|
||||
pool->chunks = chunk;
|
||||
uint8_t* addr = &chunk->data[full_size - size
|
||||
- offsetof(GuMemChunk, data)];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size,
|
||||
pre_size + size));
|
||||
ret = addr;
|
||||
} else if (pre_align == 1 && align == 1) {
|
||||
uint8_t* buf = gu_pool_malloc_unaligned(pool, pre_size + size);
|
||||
ret = &buf[pre_size];
|
||||
} else {
|
||||
ret = gu_pool_malloc_aligned(pool, pre_align, pre_size,
|
||||
align, size);
|
||||
}
|
||||
gu_exit("<- %p", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_malloc_aligned(GuPool* pool, size_t size, size_t align)
|
||||
{
|
||||
if (align == 0) {
|
||||
align = GU_MIN(size, gu_alignof(GuMaxAlign));
|
||||
}
|
||||
void* ret = gu_malloc_prefixed(pool, 1, 0, align, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_pool_finally(GuPool* pool, GuFinalizer* finalizer)
|
||||
{
|
||||
GuFinalizerNode* node = gu_new(GuFinalizerNode, pool);
|
||||
node->next = pool->finalizers;
|
||||
node->fin = finalizer;
|
||||
pool->finalizers = node;
|
||||
}
|
||||
|
||||
void
|
||||
gu_pool_free(GuPool* pool)
|
||||
{
|
||||
gu_debug("%p", pool);
|
||||
GuFinalizerNode* node = pool->finalizers;
|
||||
while (node) {
|
||||
GuFinalizerNode* next = node->next;
|
||||
node->fin->fn(node->fin);
|
||||
node = next;
|
||||
}
|
||||
GuMemChunk* chunk = pool->chunks;
|
||||
while (chunk) {
|
||||
GuMemChunk* next = chunk->next;
|
||||
gu_mem_buf_free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
VG(VALGRIND_DESTROY_MEMPOOL(pool));
|
||||
if (!pool->flags & GU_POOL_LOCAL) {
|
||||
gu_mem_buf_free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern inline void* gu_malloc(GuPool* pool, size_t size);
|
||||
|
||||
extern inline void*
|
||||
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
|
||||
const void* init);
|
||||
|
||||
276
src/runtime/c/gu/mem.h
Normal file
276
src/runtime/c/gu/mem.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Memory allocation tools.
|
||||
*/
|
||||
|
||||
#ifndef GU_MEM_H_
|
||||
#define GU_MEM_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/fun.h>
|
||||
|
||||
/** @defgroup GuPool Memory pools */
|
||||
//@{
|
||||
|
||||
|
||||
/// A memory pool.
|
||||
typedef struct GuPool GuPool;
|
||||
|
||||
/// @name Creating a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Create a new memory pool.
|
||||
GU_ONLY GuPool*
|
||||
gu_new_pool(void);
|
||||
|
||||
/**<
|
||||
* @return A new memory pool.
|
||||
*/
|
||||
|
||||
|
||||
//@private
|
||||
GuPool*
|
||||
gu_local_pool_(uint8_t* init_buf, size_t sz);
|
||||
|
||||
//@private
|
||||
#define GU_LOCAL_POOL_INIT_SIZE (16 * sizeof(GuWord))
|
||||
|
||||
|
||||
/// Create a stack-allocated memory pool.
|
||||
#define gu_local_pool() \
|
||||
gu_local_pool_(gu_alloca(GU_LOCAL_POOL_INIT_SIZE), \
|
||||
GU_LOCAL_POOL_INIT_SIZE)
|
||||
/**<
|
||||
* @return A memory pool whose first chunk is allocated directly from
|
||||
* the stack. This makes its creation faster, and more suitable for
|
||||
* functions that usually allocate only a little memory from the pool
|
||||
* until it is freed.
|
||||
*
|
||||
* @note The pool created with #gu_local_pool \e must be freed with
|
||||
* #gu_pool_free before the end of the block where #gu_local_pool was
|
||||
* called.
|
||||
*
|
||||
* @note Because #gu_local_pool uses relatively much stack space, it
|
||||
* should not be used in the bodies of recursive functions.
|
||||
*/
|
||||
|
||||
|
||||
//@}
|
||||
/// @name Destroying a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Free a memory pool and all objects allocated from it.
|
||||
void
|
||||
gu_pool_free(GU_ONLY GuPool* pool);
|
||||
/**<
|
||||
* When the pool is freed, all finalizers registered by
|
||||
* #gu_pool_finally on \p pool are invoked in reverse order of
|
||||
* registration.
|
||||
*
|
||||
* @note After the pool is freed, all objects allocated from it become
|
||||
* invalid and may no longer be used. */
|
||||
|
||||
//@}
|
||||
/// @name Allocating from a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate memory with a specified alignment.
|
||||
void*
|
||||
gu_malloc_aligned(GuPool* pool, size_t size, size_t alignment);
|
||||
|
||||
void*
|
||||
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size);
|
||||
|
||||
/// Allocate memory from a pool.
|
||||
inline void*
|
||||
gu_malloc(GuPool* pool, size_t size) {
|
||||
return gu_malloc_aligned(pool, size, 0);
|
||||
}
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//@private
|
||||
static inline void*
|
||||
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
|
||||
const void* init)
|
||||
{
|
||||
void* p = gu_malloc_aligned(pool, size, alignment);
|
||||
memcpy(p, init, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
//@private
|
||||
static inline void*
|
||||
gu_malloc_init(GuPool* pool, size_t size, const void* init)
|
||||
{
|
||||
return gu_malloc_init_aligned(pool, size, 0, init);
|
||||
}
|
||||
|
||||
|
||||
/** Allocate memory to store an array of objects of a given type. */
|
||||
|
||||
#define gu_new_n(type, n, pool) \
|
||||
((type*)gu_malloc_aligned((pool), \
|
||||
sizeof(type) * (n), \
|
||||
gu_alignof(type)))
|
||||
/**<
|
||||
* @param type The C type of the objects to allocate.
|
||||
*
|
||||
* @param n The number of objects to allocate.
|
||||
*
|
||||
* @param pool The memory pool to allocate from.
|
||||
*
|
||||
* @return A pointer to a heap-allocated array of \p n uninitialized
|
||||
* objects of type \p type.
|
||||
*/
|
||||
|
||||
|
||||
/** Allocate memory to store an object of a given type. */
|
||||
|
||||
#define gu_new(type, pool) \
|
||||
gu_new_n(type, 1, pool)
|
||||
/**<
|
||||
* @param type The C type of the object to allocate.
|
||||
*
|
||||
* @param pool The memory pool to allocate from.
|
||||
*
|
||||
* @return A pointer to a heap-allocated uninitialized object of type
|
||||
* \p type.
|
||||
*/
|
||||
|
||||
|
||||
#define gu_new_prefixed(pre_type, type, pool) \
|
||||
((type*)(gu_malloc_prefixed((pool), \
|
||||
gu_alignof(pre_type), sizeof(pre_type), \
|
||||
gu_alignof(type), sizeof(type))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
#define gu_new_i(pool, type, ...) \
|
||||
({ \
|
||||
type *gu_new_p_ = gu_new(type, pool); \
|
||||
memcpy((void*) gu_new_p_, &(type){ __VA_ARGS__ }, \
|
||||
sizeof(type)); \
|
||||
gu_new_p_; \
|
||||
})
|
||||
#else // GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
#define gu_new_i(pool, type, ...) \
|
||||
((type*)gu_malloc_init_aligned((pool), sizeof(type), \
|
||||
gu_alignof(type), \
|
||||
&(type){ __VA_ARGS__ }))
|
||||
#endif // GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
|
||||
/** @def gu_new_i(pool, type, ...)
|
||||
*
|
||||
* Allocate and initialize an object.
|
||||
*
|
||||
* @param pool The pool to allocate from.
|
||||
*
|
||||
* @param type The C type of the object to allocate.
|
||||
*
|
||||
* @param ... An initializer list for the object to allocate.
|
||||
*/
|
||||
|
||||
#define gu_new_s gu_new_i
|
||||
|
||||
// Alas, there's no portable way to get the alignment of flex structs.
|
||||
#define gu_new_flex(pool_, type_, flex_member_, n_elems_) \
|
||||
((type_ *)gu_malloc_aligned( \
|
||||
(pool_), \
|
||||
GU_FLEX_SIZE(type_, flex_member_, n_elems_), \
|
||||
gu_flex_alignof(type_)))
|
||||
|
||||
|
||||
//@}
|
||||
/// @name Finalizers
|
||||
//@{
|
||||
|
||||
|
||||
typedef struct GuFinalizer GuFinalizer;
|
||||
|
||||
struct GuFinalizer {
|
||||
void (*fn)(GuFinalizer* self);
|
||||
///< @param self A pointer to this finalizer.
|
||||
};
|
||||
|
||||
/// Register a finalizer.
|
||||
void gu_pool_finally(GuPool* pool, GuFinalizer* fini);
|
||||
|
||||
/**< Register \p fini to be called when \p pool is destroyed. The
|
||||
* finalizers are called in reverse order of registration.
|
||||
*/
|
||||
|
||||
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** @defgroup GuMemBuf Memory buffers
|
||||
*
|
||||
* Resizable blocks of heap-allocated memory. These operations differ
|
||||
* from standard \c malloc, \c realloc and \c free -functions in that
|
||||
* memory buffers are not allocated by exact size. Instead, a minimum
|
||||
* size is requested, and the returned buffer may be larger. This
|
||||
* gives the memory allocator more flexibility when the client code
|
||||
* can make use of larger buffers than requested.
|
||||
* */
|
||||
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate a new memory buffer.
|
||||
GU_ONLY void*
|
||||
gu_mem_buf_alloc(size_t min_size, size_t* real_size);
|
||||
/**<
|
||||
* @param min_size The minimum acceptable size for a returned memory block.
|
||||
*
|
||||
* @param[out] real_size The actual size of the returned memory
|
||||
* block. This is never less than \p min_size.
|
||||
*
|
||||
* @return A pointer to the memory buffer.
|
||||
*/
|
||||
|
||||
|
||||
/// Allocate a new memory buffer to replace an old one.
|
||||
GU_ONLY void*
|
||||
gu_mem_buf_realloc(
|
||||
GU_NULL GU_ONLY GU_RETURNED
|
||||
void* buf,
|
||||
size_t min_size,
|
||||
size_t* real_size_out);
|
||||
|
||||
|
||||
/// Free a memory buffer.
|
||||
void
|
||||
gu_mem_buf_free(GU_ONLY void* buf);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
#endif // GU_MEM_H_
|
||||
302
src/runtime/c/gu/out.c
Normal file
302
src/runtime/c/gu/out.c
Normal file
@@ -0,0 +1,302 @@
|
||||
#include <gu/seq.h>
|
||||
#include <gu/out.h>
|
||||
|
||||
|
||||
|
||||
GuOut
|
||||
gu_init_out(GuOutStream* stream)
|
||||
{
|
||||
gu_require(stream != NULL);
|
||||
GuOut out = {
|
||||
.buf_end = NULL,
|
||||
.buf_curr = 0,
|
||||
.stream = stream,
|
||||
.fini.fn = NULL
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_out_is_buffering(GuOut* out)
|
||||
{
|
||||
return !!out->buf_end;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_out_end_buf(GuOut* out, GuExn* err)
|
||||
{
|
||||
if (!gu_out_is_buffering(out)) {
|
||||
return;
|
||||
}
|
||||
GuOutStream* stream = out->stream;
|
||||
size_t curr_len = ((ptrdiff_t)out->buf_size) + out->buf_curr;
|
||||
stream->end_buf(stream, curr_len, err);
|
||||
out->buf_end = NULL;
|
||||
out->buf_size = out->buf_curr = 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_out_begin_buf(GuOut* out, size_t req, GuExn* err)
|
||||
{
|
||||
GuOutStream* stream = out->stream;
|
||||
if (gu_out_is_buffering(out)) {
|
||||
if (out->buf_curr < 0) {
|
||||
return true;
|
||||
} else {
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream->begin_buf) {
|
||||
size_t sz = 0;
|
||||
uint8_t* buf = stream->begin_buf(stream, req, &sz, err);
|
||||
gu_assert(sz <= PTRDIFF_MAX);
|
||||
if (buf) {
|
||||
out->buf_end = &buf[sz];
|
||||
out->buf_curr = -(ptrdiff_t) sz;
|
||||
out->buf_size = sz;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gu_out_fini(GuFinalizer* self)
|
||||
{
|
||||
GuOut* out = gu_container(self, GuOut, fini);
|
||||
if (gu_out_is_buffering(out)) {
|
||||
GuPool* pool = gu_local_pool();
|
||||
GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
|
||||
gu_out_end_buf(out, err);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_new_out(GuOutStream* stream, GuPool* pool)
|
||||
{
|
||||
GuOut* out = gu_new(GuOut, pool);
|
||||
*out = gu_init_out(stream);
|
||||
out->fini.fn = gu_out_fini;
|
||||
gu_pool_finally(pool, &out->fini);
|
||||
return out;
|
||||
}
|
||||
|
||||
extern inline bool
|
||||
gu_out_try_buf_(GuOut* out, const uint8_t* src, size_t len);
|
||||
|
||||
|
||||
extern inline size_t
|
||||
gu_out_bytes(GuOut* out, const uint8_t* buf, size_t len, GuExn* err);
|
||||
|
||||
static size_t
|
||||
gu_out_output(GuOut* out, const uint8_t* src, size_t len, GuExn* err)
|
||||
{
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
return out->stream->output(out->stream, src, len, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
gu_out_flush(GuOut* out, GuExn* err)
|
||||
{
|
||||
GuOutStream* stream = out->stream;
|
||||
if (out->buf_end) {
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (stream->flush) {
|
||||
stream->flush(stream, err);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
if (!out->buf_end && !gu_out_begin_buf(out, req, err)) {
|
||||
return NULL;
|
||||
}
|
||||
*sz_out = -out->buf_curr;
|
||||
return &out->buf_end[out->buf_curr];
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_end_span(GuOut* out, size_t sz)
|
||||
{
|
||||
ptrdiff_t new_curr = (ptrdiff_t) sz + out->buf_curr;
|
||||
gu_require(new_curr <= 0);
|
||||
out->buf_curr = new_curr;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src, size_t len,
|
||||
GuExn* err)
|
||||
{
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
} else if (gu_out_try_buf_(out, src, len)) {
|
||||
return len;
|
||||
}
|
||||
if (gu_out_begin_buf(out, len, err)) {
|
||||
if (gu_out_try_buf_(out, src, len)) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return gu_out_output(out, src, len, err);
|
||||
}
|
||||
|
||||
|
||||
void gu_out_u8_(GuOut* restrict out, uint8_t u, GuExn* err)
|
||||
{
|
||||
if (gu_out_begin_buf(out, 1, err)) {
|
||||
if (gu_out_try_u8_(out, u)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
gu_out_output(out, &u, 1, err);
|
||||
}
|
||||
|
||||
|
||||
extern inline void
|
||||
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err);
|
||||
|
||||
extern inline bool
|
||||
gu_out_is_buffered(GuOut* out);
|
||||
|
||||
extern inline bool
|
||||
gu_out_try_u8_(GuOut* restrict out, uint8_t u);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct GuProxyOutStream GuProxyOutStream;
|
||||
|
||||
struct GuProxyOutStream {
|
||||
GuOutStream stream;
|
||||
GuOut* real_out;
|
||||
};
|
||||
|
||||
|
||||
static uint8_t*
|
||||
gu_proxy_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
return gu_out_begin_span(pos->real_out, req, sz_out, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
gu_out_end_span(pos->real_out, sz);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_proxy_out_output(GuOutStream* self, const uint8_t* src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
return gu_out_bytes(pos->real_out, src, sz, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_out_flush(GuOutStream* self, GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
gu_out_flush(pos->real_out, err);
|
||||
}
|
||||
|
||||
|
||||
GuOutStream*
|
||||
gu_out_proxy_stream(GuOut* out, GuPool* pool)
|
||||
{
|
||||
return &gu_new_s(pool, GuProxyOutStream,
|
||||
.stream.begin_buf = gu_proxy_out_buf_begin,
|
||||
.stream.end_buf = gu_proxy_out_buf_end,
|
||||
.stream.output = gu_proxy_out_output,
|
||||
.stream.flush = gu_proxy_out_flush,
|
||||
.real_out = out)->stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct GuBufferedOutStream GuBufferedOutStream;
|
||||
|
||||
struct GuBufferedOutStream {
|
||||
GuProxyOutStream pstream;
|
||||
size_t sz;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
static uint8_t*
|
||||
gu_buffered_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err)
|
||||
{
|
||||
(void) (req && err);
|
||||
GuBufferedOutStream* b =
|
||||
gu_container(self, GuBufferedOutStream, pstream.stream);
|
||||
*sz_out = b->sz;
|
||||
return b->buf;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buffered_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuBufferedOutStream* b =
|
||||
gu_container(self, GuBufferedOutStream, pstream.stream);
|
||||
gu_require(sz <= b->sz);
|
||||
gu_out_bytes(b->pstream.real_out, b->buf, sz, err);
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_new_buffered_out(GuOut* out, size_t sz, GuPool* pool)
|
||||
{
|
||||
GuBufferedOutStream* b =
|
||||
gu_new_flex(pool, GuBufferedOutStream, buf, sz);
|
||||
b->pstream.stream = (GuOutStream) {
|
||||
.begin_buf = gu_buffered_out_buf_begin,
|
||||
.end_buf = gu_buffered_out_buf_end,
|
||||
.output = gu_proxy_out_output,
|
||||
.flush = gu_proxy_out_flush
|
||||
};
|
||||
b->pstream.real_out = out;
|
||||
b->sz = sz;
|
||||
return gu_new_out(&b->pstream.stream, pool);
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_out_buffered(GuOut* out, GuPool* pool)
|
||||
{
|
||||
if (gu_out_is_buffered(out)) {
|
||||
return out;
|
||||
}
|
||||
return gu_new_buffered_out(out, 4096, pool);
|
||||
}
|
||||
|
||||
|
||||
166
src/runtime/c/gu/out.h
Normal file
166
src/runtime/c/gu/out.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#ifndef GU_OUT_H_
|
||||
#define GU_OUT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/exn.h>
|
||||
|
||||
typedef struct GuOut GuOut;
|
||||
|
||||
typedef struct GuOutStream GuOutStream;
|
||||
|
||||
struct GuOutStream {
|
||||
uint8_t* (*begin_buf)(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err);
|
||||
void (*end_buf)(GuOutStream* self, size_t span, GuExn* err);
|
||||
size_t (*output)(GuOutStream* self, const uint8_t* buf, size_t size,
|
||||
GuExn* err);
|
||||
void (*flush)(GuOutStream* self, GuExn* err);
|
||||
};
|
||||
|
||||
|
||||
struct GuOut {
|
||||
uint8_t* restrict buf_end;
|
||||
ptrdiff_t buf_curr;
|
||||
size_t buf_size;
|
||||
GuOutStream* stream;
|
||||
GuFinalizer fini;
|
||||
};
|
||||
|
||||
|
||||
GuOut
|
||||
gu_init_out(GuOutStream* stream);
|
||||
|
||||
GuOut*
|
||||
gu_new_out(GuOutStream* stream, GuPool* pool);
|
||||
|
||||
inline bool
|
||||
gu_out_is_buffered(GuOut* out)
|
||||
{
|
||||
return !!out->stream->begin_buf;
|
||||
}
|
||||
|
||||
GuOutStream*
|
||||
gu_out_proxy_stream(GuOut* out, GuPool* pool);
|
||||
|
||||
GuOut*
|
||||
gu_new_buffered_out(GuOut* out, size_t buf_sz, GuPool* pool);
|
||||
|
||||
GuOut*
|
||||
gu_out_buffered(GuOut* out, GuPool* pool);
|
||||
|
||||
uint8_t*
|
||||
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err);
|
||||
|
||||
uint8_t*
|
||||
gu_out_force_span(GuOut* out, size_t min, size_t max, size_t* sz_out,
|
||||
GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_end_span(GuOut* out, size_t sz);
|
||||
|
||||
size_t
|
||||
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src,
|
||||
size_t len, GuExn* err);
|
||||
|
||||
inline bool
|
||||
gu_out_try_buf_(GuOut* restrict out, const uint8_t* restrict src, size_t len)
|
||||
{
|
||||
gu_require(len <= PTRDIFF_MAX);
|
||||
ptrdiff_t curr = out->buf_curr;
|
||||
ptrdiff_t new_curr = curr + (ptrdiff_t) len;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(&out->buf_end[curr], src, len);
|
||||
out->buf_curr = new_curr;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
gu_out_bytes(GuOut* restrict out, const uint8_t* restrict src, size_t len,
|
||||
GuExn* err)
|
||||
{
|
||||
if (GU_LIKELY(gu_out_try_buf_(out, src, len))) {
|
||||
return len;
|
||||
}
|
||||
return gu_out_bytes_(out, src, len, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_flush(GuOut* out, GuExn* err);
|
||||
|
||||
inline bool
|
||||
gu_out_try_u8_(GuOut* restrict out, uint8_t u)
|
||||
{
|
||||
ptrdiff_t curr = out->buf_curr;
|
||||
ptrdiff_t new_curr = curr + 1;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
return false;
|
||||
}
|
||||
out->buf_end[curr] = u;
|
||||
out->buf_curr = new_curr;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err)
|
||||
{
|
||||
if (GU_UNLIKELY(!gu_out_try_u8_(out, u))) {
|
||||
extern void gu_out_u8_(GuOut* restrict out, uint8_t u,
|
||||
GuExn* err);
|
||||
gu_out_u8_(out, u, err);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err)
|
||||
{
|
||||
gu_out_u8(out, (uint8_t) i, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
gu_out_u16le(GuOut* out, uint16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u16be(GuOut* out, uint16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s16le(GuOut* out, int16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s16be(GuOut* out, int16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u32le(GuOut* out, uint32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u32be(GuOut* out, uint32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s32le(GuOut* out, int32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s32be(GuOut* out, int32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u64le(GuOut* out, uint64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u64be(GuOut* out, uint64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s64le(GuOut* out, int64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s64be(GuOut* out, int64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_f64le(GuOut* out, double d, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_f64be(GuOut* out, double d, GuExn* err);
|
||||
|
||||
#endif // GU_OUT_H_
|
||||
154
src/runtime/c/gu/prime.c
Normal file
154
src/runtime/c/gu/prime.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
static const uint32_t gu_prime_wheel_mask = 0UL
|
||||
| 1 << 1
|
||||
| 1 << 7
|
||||
| 1 << 11
|
||||
| 1 << 13
|
||||
| 1 << 17
|
||||
| 1 << 19
|
||||
| 1 << 23
|
||||
| 1 << 29;
|
||||
|
||||
static bool
|
||||
gu_prime_wheel(int i)
|
||||
{
|
||||
gu_assert(i >= 0 && i < 30);
|
||||
return !!(gu_prime_wheel_mask & (1 << i));
|
||||
}
|
||||
|
||||
static const uint32_t gu_small_prime_mask = 0UL
|
||||
| 1 << 2
|
||||
| 1 << 3
|
||||
| 1 << 5
|
||||
| 1 << 7
|
||||
| 1 << 11
|
||||
| 1 << 13
|
||||
| 1 << 17
|
||||
| 1 << 19
|
||||
| 1 << 23
|
||||
| 1 << 29
|
||||
| 1U << 31;
|
||||
|
||||
static bool
|
||||
gu_is_wheel_prime(int u)
|
||||
{
|
||||
gu_assert(u > 30 && u % 2 != 0 && u % 3 != 0 && u % 5 != 0);
|
||||
int d = 0;
|
||||
int i = 7;
|
||||
goto start;
|
||||
while (d * d <= u) {
|
||||
for (i = 1; i <= 29; i+=2) {
|
||||
start:
|
||||
if (gu_prime_wheel(i) && u % (d + i) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
d += 30;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
gu_prime_inf(int i)
|
||||
{
|
||||
if (i < 2) {
|
||||
return 0;
|
||||
} else if (i < 32) {
|
||||
while (!(gu_small_prime_mask & (1 << i))) {
|
||||
i--;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int d = (i - 1) | 1;
|
||||
int r = d % 30;
|
||||
|
||||
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
|
||||
d -= 2;
|
||||
r -= 2;
|
||||
if (r < 0) {
|
||||
r += 30;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
gu_prime_sup(int i)
|
||||
{
|
||||
if (i <= 2) {
|
||||
return 2;
|
||||
} else if (i < 32) {
|
||||
while (!(gu_small_prime_mask & (1 << i))) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int d = i | 1;
|
||||
int r = d % 30;
|
||||
|
||||
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
|
||||
d += 2;
|
||||
r += 2;
|
||||
if (r > 30) {
|
||||
r -= 30;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_is_prime(int i)
|
||||
{
|
||||
if (i < 2) {
|
||||
return false;
|
||||
} else if (i < 30) {
|
||||
return !!(gu_small_prime_mask & (1 << i));
|
||||
} else if (!gu_prime_wheel(i % 30)) {
|
||||
return false;
|
||||
} else {
|
||||
return gu_is_wheel_prime(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gu_is_twin_prime(int i)
|
||||
{
|
||||
return gu_is_prime(i) && gu_is_prime(i - 2);
|
||||
}
|
||||
|
||||
int
|
||||
gu_twin_prime_inf(int i)
|
||||
{
|
||||
while (true) {
|
||||
i = gu_prime_inf(i);
|
||||
if (i == 0) {
|
||||
return 0;
|
||||
} else if (gu_is_prime(i - 2)) {
|
||||
return i;
|
||||
}
|
||||
i = i - 4;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
gu_twin_prime_sup(int i)
|
||||
{
|
||||
if (i <= 5) {
|
||||
return 5;
|
||||
}
|
||||
i = i - 2;
|
||||
while (true) {
|
||||
i = gu_prime_sup(i);
|
||||
if (gu_is_prime(i + 2)) {
|
||||
return i + 2;
|
||||
}
|
||||
i = i + 4;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
16
src/runtime/c/gu/prime.h
Normal file
16
src/runtime/c/gu/prime.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef GU_PRIME_H_
|
||||
#define GU_PRIME_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
bool gu_is_prime(int i);
|
||||
|
||||
bool gu_is_twin_prime(int i);
|
||||
|
||||
int gu_prime_inf(int i);
|
||||
int gu_twin_prime_inf(int i);
|
||||
|
||||
int gu_prime_sup(int i);
|
||||
int gu_twin_prime_sup(int i);
|
||||
|
||||
#endif // GU_PRIME_H_
|
||||
15
src/runtime/c/gu/read.c
Normal file
15
src/runtime/c/gu/read.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <gu/read.h>
|
||||
|
||||
extern inline GuUCS
|
||||
gu_read_ucs(GuReader* rdr, GuExn* err);
|
||||
|
||||
extern inline char
|
||||
gu_getc(GuReader* rdr, GuExn* err);
|
||||
|
||||
GuReader*
|
||||
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool)
|
||||
{
|
||||
GuReader* rdr = gu_new(GuReader, pool);
|
||||
rdr->in_ = gu_init_in(gu_in_proxy_stream(utf8_in, pool));
|
||||
return rdr;
|
||||
}
|
||||
31
src/runtime/c/gu/read.h
Normal file
31
src/runtime/c/gu/read.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef GU_READ_H_
|
||||
#define GU_READ_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
typedef struct GuReader GuReader;
|
||||
|
||||
struct GuReader {
|
||||
GuIn in_;
|
||||
};
|
||||
|
||||
inline GuUCS
|
||||
gu_read_ucs(GuReader* rdr, GuExn* err)
|
||||
{
|
||||
return gu_in_utf8(&rdr->in_, err);
|
||||
}
|
||||
|
||||
inline char
|
||||
gu_getc(GuReader* rdr, GuExn* err)
|
||||
{
|
||||
return gu_in_utf8_char(&rdr->in_, err);
|
||||
}
|
||||
|
||||
GuReader*
|
||||
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool);
|
||||
/**< @todo Implement. */
|
||||
|
||||
|
||||
#endif // GU_READ_H_
|
||||
245
src/runtime/c/gu/seq.c
Normal file
245
src/runtime/c/gu/seq.c
Normal file
@@ -0,0 +1,245 @@
|
||||
#include <gu/out.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/fun.h>
|
||||
#include <gu/assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct GuBuf {
|
||||
uint8_t* data;
|
||||
size_t elem_size;
|
||||
size_t avail_len;
|
||||
GuFinalizer fin;
|
||||
};
|
||||
|
||||
GuBuf*
|
||||
gu_seq_buf(GuSeq seq)
|
||||
{
|
||||
gu_require(gu_tagged_tag(seq.w_) == 0);
|
||||
return gu_word_ptr(seq.w_);
|
||||
}
|
||||
|
||||
GuSeq
|
||||
gu_buf_seq(GuBuf* buf)
|
||||
{
|
||||
return (GuSeq) { .w_ = gu_ptr_word(buf) };
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_buf_length(GuBuf* dyn)
|
||||
{
|
||||
return (size_t)(((GuWord*)(void*)dyn)[-1] >> 1);
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_buf_avail(GuBuf* buf)
|
||||
{
|
||||
return buf->avail_len;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_buf_set_length(GuBuf* dyn, size_t new_len)
|
||||
{
|
||||
((GuWord*)(void*)dyn)[-1] = ((GuWord) new_len) << 1 | 0x1;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_fini(GuFinalizer* fin)
|
||||
{
|
||||
GuBuf* buf = gu_container(fin, GuBuf, fin);
|
||||
gu_mem_buf_free(buf->data);
|
||||
}
|
||||
|
||||
GuBuf*
|
||||
gu_make_buf(size_t elem_size, GuPool* pool)
|
||||
{
|
||||
GuBuf* buf = gu_new_prefixed(unsigned, GuBuf, pool);
|
||||
gu_buf_set_length(buf, 0);
|
||||
buf->elem_size = elem_size;
|
||||
buf->data = NULL;
|
||||
buf->avail_len = 0;
|
||||
buf->fin.fn = gu_buf_fini;
|
||||
gu_pool_finally(pool, &buf->fin);
|
||||
gu_buf_set_length(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const GuWord gu_empty_seq[2] = {0, 0};
|
||||
|
||||
GuSeq
|
||||
gu_make_seq(size_t elem_size, size_t length, GuPool* pool)
|
||||
{
|
||||
size_t size = elem_size * length;
|
||||
if (0 < length && length <= GU_TAG_MAX) {
|
||||
void* buf = gu_malloc(pool, size);
|
||||
return (GuSeq) { gu_tagged(buf, length) };
|
||||
} else if (size == 0) {
|
||||
return (GuSeq) { gu_tagged((void*)&gu_empty_seq[1], 0) };
|
||||
} else {
|
||||
void* buf = gu_malloc_prefixed(pool,
|
||||
gu_alignof(GuWord),
|
||||
sizeof(GuWord),
|
||||
0, size);
|
||||
((GuWord*) buf)[-1] = ((GuWord) length) << 1;
|
||||
return (GuSeq) { gu_tagged(buf, 0) };
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_require(GuBuf* buf, size_t req_len)
|
||||
{
|
||||
if (req_len <= buf->avail_len) {
|
||||
return;
|
||||
}
|
||||
size_t req_size = buf->elem_size * req_len;
|
||||
size_t real_size;
|
||||
buf->data = gu_mem_buf_realloc(buf->data, req_size,
|
||||
&real_size);
|
||||
buf->avail_len = real_size / buf->elem_size;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_data(GuBuf* buf)
|
||||
{
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_extend_n(GuBuf* buf, size_t n_elems)
|
||||
{
|
||||
size_t len = gu_buf_length(buf);
|
||||
size_t new_len = len + n_elems;
|
||||
gu_buf_require(buf, new_len);
|
||||
gu_buf_set_length(buf, new_len);
|
||||
return &buf->data[buf->elem_size * len];
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_extend(GuBuf* buf)
|
||||
{
|
||||
return gu_buf_extend_n(buf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gu_buf_push_n(GuBuf* buf, const void* data, size_t n_elems)
|
||||
{
|
||||
|
||||
void* p = gu_buf_extend_n(buf, n_elems);
|
||||
memcpy(p, data, buf->elem_size * n_elems);
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_buf_trim_n(GuBuf* buf, size_t n_elems)
|
||||
{
|
||||
gu_require(n_elems <= gu_buf_length(buf));
|
||||
size_t new_len = gu_buf_length(buf) - n_elems;
|
||||
gu_buf_set_length(buf, new_len);
|
||||
return &buf->data[buf->elem_size * new_len];
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_buf_trim(GuBuf* buf)
|
||||
{
|
||||
return gu_buf_trim_n(buf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out)
|
||||
{
|
||||
const void* p = gu_buf_trim_n(buf, n_elems);
|
||||
memcpy(data_out, p, buf->elem_size * n_elems);
|
||||
}
|
||||
|
||||
GuSeq
|
||||
gu_buf_freeze(GuBuf* buf, GuPool* pool)
|
||||
{
|
||||
size_t len = gu_buf_length(buf);
|
||||
GuSeq seq = gu_make_seq(buf->elem_size, len, pool);
|
||||
void* bufdata = gu_buf_data(buf);
|
||||
void* seqdata = gu_seq_data(seq);
|
||||
memcpy(seqdata, bufdata, buf->elem_size * len);
|
||||
return seq;
|
||||
}
|
||||
|
||||
typedef struct GuBufOut GuBufOut;
|
||||
struct GuBufOut
|
||||
{
|
||||
GuOutStream stream;
|
||||
GuBuf* buf;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_buf_out_output(GuOutStream* stream, const uint8_t* src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
gu_assert(sz % buf->elem_size == 0);
|
||||
size_t len = sz / buf->elem_size;
|
||||
gu_buf_push_n(bout->buf, src, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
gu_buf_outbuf_begin(GuOutStream* stream, size_t req, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
(void) req;
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
size_t esz = buf->elem_size;
|
||||
size_t len = gu_buf_length(buf);
|
||||
gu_buf_require(buf, len + (req + esz - 1) / esz);
|
||||
size_t avail = buf->avail_len;
|
||||
gu_assert(len < avail);
|
||||
*sz_out = esz * (avail - len);
|
||||
return &buf->data[len * esz];
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_outbuf_end(GuOutStream* stream, size_t sz, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
size_t len = gu_buf_length(buf);
|
||||
size_t elem_size = buf->elem_size;
|
||||
gu_require(sz % elem_size == 0);
|
||||
gu_require(sz < elem_size * (len - buf->avail_len));
|
||||
gu_buf_set_length(buf, len + (sz / elem_size));
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_buf_out(GuBuf* buf, GuPool* pool)
|
||||
{
|
||||
GuBufOut* bout = gu_new_i(pool, GuBufOut,
|
||||
.stream.output = gu_buf_out_output,
|
||||
.stream.begin_buf = gu_buf_outbuf_begin,
|
||||
.stream.end_buf = gu_buf_outbuf_end,
|
||||
.buf = buf);
|
||||
return gu_new_out(&bout->stream, pool);
|
||||
}
|
||||
|
||||
const GuSeq
|
||||
gu_null_seq = GU_NULL_SEQ;
|
||||
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
GU_DEFINE_KIND(GuSeq, GuOpaque);
|
||||
GU_DEFINE_KIND(GuBuf, abstract);
|
||||
|
||||
GU_DEFINE_TYPE(GuChars, GuSeq, gu_type(char));
|
||||
GU_DEFINE_TYPE(GuBytes, GuSeq, gu_type(uint8_t));
|
||||
|
||||
char*
|
||||
gu_chars_str(GuChars chars, GuPool* pool)
|
||||
{
|
||||
size_t len = gu_seq_length(chars);
|
||||
char* data = gu_seq_data(chars);
|
||||
char* str = gu_new_str(len, pool);
|
||||
memcpy(str, data, len);
|
||||
return str;
|
||||
}
|
||||
198
src/runtime/c/gu/seq.h
Normal file
198
src/runtime/c/gu/seq.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#ifndef GU_SEQ_H_
|
||||
#define GU_SEQ_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/bits.h>
|
||||
|
||||
|
||||
typedef struct GuBuf GuBuf;
|
||||
|
||||
typedef GuOpaque() GuSeq;
|
||||
|
||||
GuSeq
|
||||
gu_make_seq(size_t elem_size, size_t len, GuPool* pool);
|
||||
|
||||
#define gu_new_seq(T, N, POOL) \
|
||||
gu_make_seq(sizeof(T), (N), (POOL))
|
||||
|
||||
static inline size_t
|
||||
gu_seq_length(GuSeq seq)
|
||||
{
|
||||
GuWord w = seq.w_;
|
||||
size_t tag = gu_tagged_tag(w);
|
||||
if (tag == 0) {
|
||||
GuWord* p = gu_tagged_ptr(w);
|
||||
return (size_t) (p[-1] >> 1);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_seq_data(GuSeq seq)
|
||||
{
|
||||
GuWord w = seq.w_;
|
||||
int tag = gu_tagged_tag(w);
|
||||
void* ptr = gu_tagged_ptr(w);
|
||||
if (tag == 0) {
|
||||
GuWord* p = ptr;
|
||||
if (p[-1] & 0x1) {
|
||||
return *(uint8_t**) ptr;
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_seq_is_null(GuSeq seq)
|
||||
{
|
||||
return (gu_tagged_ptr(seq.w_)) == NULL;
|
||||
}
|
||||
|
||||
|
||||
#define gu_seq_index(SEQ, T, I) \
|
||||
(&((T*)gu_seq_data(SEQ))[I])
|
||||
|
||||
#define gu_seq_get(SEQ, T, I) \
|
||||
(*gu_seq_index(SEQ, T, I))
|
||||
|
||||
#define gu_seq_set(SEQ, T, I, V) \
|
||||
GU_BEGIN \
|
||||
(*gu_seq_index(SEQ, T, I) = (V)); \
|
||||
GU_END
|
||||
|
||||
|
||||
|
||||
|
||||
GuBuf*
|
||||
gu_seq_buf(GuSeq seq);
|
||||
|
||||
GuSeq
|
||||
gu_buf_seq(GuBuf* buf);
|
||||
|
||||
GuBuf*
|
||||
gu_make_buf(size_t elem_size, GuPool* pool);
|
||||
|
||||
#define gu_new_buf(T, POOL) \
|
||||
gu_make_buf(sizeof(T), (POOL))
|
||||
|
||||
size_t
|
||||
gu_buf_length(GuBuf* buf);
|
||||
|
||||
size_t
|
||||
gu_buf_avail(GuBuf* buf);
|
||||
|
||||
void*
|
||||
gu_buf_data(GuBuf* buf);
|
||||
|
||||
#define gu_buf_index(BUF, T, I) \
|
||||
(&((T*)gu_buf_data(BUF))[I])
|
||||
|
||||
#define gu_buf_get(BUF, T, I) \
|
||||
(*gu_buf_index(BUF, T, I))
|
||||
|
||||
#define gu_buf_set(BUF, T, I) \
|
||||
GU_BEGIN \
|
||||
(*gu_buf_index(BUF, T, I) = (V)); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_buf_push_n(GuBuf* buf, const void* elems, size_t n_elems);
|
||||
|
||||
void*
|
||||
gu_buf_extend_n(GuBuf* buf, size_t n_elems);
|
||||
|
||||
void*
|
||||
gu_buf_extend(GuBuf* buf);
|
||||
|
||||
#define gu_buf_push(BUF, T, VAL) \
|
||||
GU_BEGIN \
|
||||
((*(T*)gu_buf_extend(BUF)) = (VAL)); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out);
|
||||
|
||||
const void*
|
||||
gu_buf_trim_n(GuBuf* buf, size_t n_elems);
|
||||
|
||||
const void*
|
||||
gu_buf_trim(GuBuf* buf);
|
||||
|
||||
#define gu_buf_pop(BUF, T) \
|
||||
(*(T*)gu_buf_trim(BUF))
|
||||
|
||||
void
|
||||
gu_seq_resize_tail(GuSeq seq, ptrdiff_t change);
|
||||
|
||||
#if 0
|
||||
void
|
||||
gu_buf_resize_head(GuBuf* buf, ptrdiff_t change);
|
||||
|
||||
void
|
||||
gu_buf_unshift(GuBuf* buf, const void* data, size_t size);
|
||||
|
||||
void
|
||||
gu_buf_shift(GuBuf* buf, size_t size, void* data_out);
|
||||
#endif
|
||||
|
||||
GuSeq
|
||||
gu_buf_freeze(GuBuf* buf, GuPool* pool);
|
||||
|
||||
extern const GuSeq gu_null_seq;
|
||||
|
||||
#define GU_NULL_SEQ { .w_ = (GuWord)(void*)NULL }
|
||||
|
||||
typedef GuSeq GuChars;
|
||||
typedef GuSeq GuBytes;
|
||||
typedef GuBuf GuCharBuf;
|
||||
typedef GuBuf GuByteBuf;
|
||||
|
||||
char*
|
||||
gu_chars_str(GuChars chars, GuPool* pool);
|
||||
|
||||
#endif // GU_SEQ_H_
|
||||
|
||||
#if defined(GU_OUT_H_) && !defined(GU_SEQ_H_OUT_)
|
||||
#define GU_SEQ_H_OUT_
|
||||
|
||||
GuOut*
|
||||
gu_buf_out(GuBuf* buf, GuPool* pool);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_TYPE_H_) && !defined(GU_SEQ_H_TYPE_)
|
||||
#define GU_SEQ_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuSeq);
|
||||
extern GU_DECLARE_KIND(GuBuf);
|
||||
|
||||
struct GuSeqType {
|
||||
GuType_GuOpaque opaque_base;
|
||||
GuType* elem_type;
|
||||
};
|
||||
|
||||
typedef const struct GuSeqType GuSeqType, GuType_GuSeq;
|
||||
|
||||
#define GU_TYPE_INIT_GuSeq(k_, t_, elem_type_) { \
|
||||
.opaque_base = GU_TYPE_INIT_GuOpaque(k_, t_, _), \
|
||||
.elem_type = elem_type_, \
|
||||
}
|
||||
|
||||
typedef struct GuBufType GuBufType, GuType_GuBuf;
|
||||
|
||||
struct GuBufType {
|
||||
GuType_abstract abstract_base;
|
||||
GuType* elem_type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuBuf(KIND, BUF_T, ELEM_T) { \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(KIND, BUF_T, _), \
|
||||
.elem_type = ELEM_T \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_TYPE(GuChars, GuSeq);
|
||||
extern GU_DECLARE_TYPE(GuBytes, GuSeq);
|
||||
|
||||
#endif
|
||||
|
||||
85
src/runtime/c/gu/str.c
Normal file
85
src/runtime/c/gu/str.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <gu/assert.h>
|
||||
#include <gu/str.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char gu_empty_str[] = "";
|
||||
const char* const gu_null_str = NULL;
|
||||
|
||||
char*
|
||||
gu_new_str(size_t size, GuPool* pool)
|
||||
{
|
||||
char* str = gu_new_n(char, size + 1, pool);
|
||||
memset(str, '\0', size + 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
char*
|
||||
gu_strdup(const char* cstr, GuPool* pool)
|
||||
{
|
||||
int len = strlen(cstr);
|
||||
char* str = gu_new_str(len, pool);
|
||||
memcpy(str, cstr, len);
|
||||
return str;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_str_eq(GuStr s1, GuStr s2)
|
||||
{
|
||||
return (strcmp(s1, s2)) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_str_is_equal(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuStr* sp1 = p1;
|
||||
const GuStr* sp2 = p2;
|
||||
return gu_str_eq(*sp1, *sp2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_str_hasher_hash(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
GuHash h = 0;
|
||||
const GuStr* sp = p;
|
||||
for (const char* s = *sp; *s != '\0'; s++) {
|
||||
h = 101 * h + (unsigned char) *s;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
GuHasher gu_str_hasher[1] = {
|
||||
{
|
||||
.eq = { .is_equal = gu_str_is_equal },
|
||||
.hash = gu_str_hasher_hash
|
||||
}
|
||||
};
|
||||
|
||||
GU_DEFINE_TYPE(GuStr, repr, _);
|
||||
|
||||
char*
|
||||
gu_vasprintf(const char* fmt, va_list args, GuPool* pool)
|
||||
{
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
int len = vsnprintf(NULL, 0, fmt, args2);
|
||||
gu_assert_msg(len >= 0, "Invalid format string: \"%s\"", fmt);
|
||||
va_end(args2);
|
||||
char* str = gu_new_str(len, pool);
|
||||
vsnprintf(str, len + 1, fmt, args);
|
||||
return str;
|
||||
}
|
||||
|
||||
char*
|
||||
gu_asprintf(GuPool* pool, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char* str = gu_vasprintf(fmt, args, pool);
|
||||
va_end(args);
|
||||
return str;
|
||||
}
|
||||
29
src/runtime/c/gu/str.h
Normal file
29
src/runtime/c/gu/str.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef GU_STR_H_
|
||||
#define GU_STR_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/hash.h>
|
||||
|
||||
extern const char gu_empty_str[];
|
||||
extern const char* const gu_null_str;
|
||||
|
||||
typedef const char* GuStr;
|
||||
|
||||
char* gu_new_str(size_t size, GuPool* pool);
|
||||
|
||||
char* gu_strdup(const char* str, GuPool* pool);
|
||||
|
||||
bool
|
||||
gu_str_eq(GuStr s1, GuStr s2);
|
||||
|
||||
extern GuHasher gu_str_hasher[1];
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
extern GU_DECLARE_TYPE(GuStr, repr);
|
||||
|
||||
char* gu_vasprintf(const char* fmt, va_list args, GuPool* pool);
|
||||
|
||||
char* gu_asprintf(GuPool* pool, const char* fmt, ...);
|
||||
|
||||
#endif // GU_STR_H_
|
||||
270
src/runtime/c/gu/string.c
Normal file
270
src/runtime/c/gu/string.c
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <gu/type.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/string.h>
|
||||
#include <gu/utf8.h>
|
||||
#include <gu/assert.h>
|
||||
#include "config.h"
|
||||
|
||||
const GuString gu_empty_string = { 1 };
|
||||
|
||||
struct GuStringBuf {
|
||||
GuByteBuf* bbuf;
|
||||
GuWriter* wtr;
|
||||
};
|
||||
|
||||
GuStringBuf*
|
||||
gu_string_buf(GuPool* pool)
|
||||
{
|
||||
GuBuf* buf = gu_new_buf(uint8_t, pool);
|
||||
GuOut* out = gu_buf_out(buf, pool);
|
||||
GuWriter* wtr = gu_new_utf8_writer(out, pool);
|
||||
return gu_new_s(pool, GuStringBuf,
|
||||
.bbuf = buf,
|
||||
.wtr = wtr);
|
||||
}
|
||||
|
||||
GuWriter*
|
||||
gu_string_buf_writer(GuStringBuf* sb)
|
||||
{
|
||||
return sb->wtr;
|
||||
}
|
||||
|
||||
static GuString
|
||||
gu_utf8_string(const uint8_t* buf, size_t sz, GuPool* pool)
|
||||
{
|
||||
if (sz < GU_MIN(sizeof(GuWord), 128)) {
|
||||
GuWord w = 0;
|
||||
for (size_t n = 0; n < sz; n++) {
|
||||
w = w << 8 | buf[n];
|
||||
}
|
||||
w = w << 8 | (sz << 1) | 1;
|
||||
return (GuString) { w };
|
||||
}
|
||||
uint8_t* p = NULL;
|
||||
if (sz < 256) {
|
||||
p = gu_malloc_aligned(pool, 1 + sz, 2);
|
||||
p[0] = (uint8_t) sz;
|
||||
} else {
|
||||
uint8_t* p =
|
||||
gu_malloc_prefixed(pool, gu_alignof(size_t),
|
||||
sizeof(size_t), 1, 1 + sizeof(sz));
|
||||
((size_t*) p)[-1] = sz;
|
||||
p[0] = 0;
|
||||
}
|
||||
memcpy(&p[1], buf, sz);
|
||||
return (GuString) { (GuWord) (void*) p };
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuString
|
||||
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool)
|
||||
{
|
||||
gu_writer_flush(sb->wtr, NULL);
|
||||
uint8_t* data = gu_buf_data(sb->bbuf);
|
||||
size_t len = gu_buf_length(sb->bbuf);
|
||||
return gu_utf8_string(data, len, pool);
|
||||
}
|
||||
|
||||
GuReader*
|
||||
gu_string_reader(GuString s, GuPool* pool)
|
||||
{
|
||||
GuWord w = s.w_;
|
||||
uint8_t* buf = NULL;
|
||||
size_t len = 0;
|
||||
if (w & 1) {
|
||||
len = (w & 0xff) >> 1;
|
||||
buf = gu_new_n(uint8_t, len, pool);
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
w >>= 8;
|
||||
buf[i] = w & 0xff;
|
||||
}
|
||||
} else {
|
||||
uint8_t* p = (void*) w;
|
||||
len = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
|
||||
buf = &p[1];
|
||||
}
|
||||
GuIn* in = gu_data_in(buf, len, pool);
|
||||
GuReader* rdr = gu_new_utf8_reader(in, pool);
|
||||
return rdr;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_string_is_long(GuString s)
|
||||
{
|
||||
return !(s.w_ & 1);
|
||||
}
|
||||
|
||||
bool
|
||||
gu_string_is_stable(GuString s)
|
||||
{
|
||||
return !gu_string_is_long(s);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_string_long_length(GuString s)
|
||||
{
|
||||
gu_assert(gu_string_is_long(s));
|
||||
uint8_t* p = (void*) s.w_;
|
||||
uint8_t len = p[0];
|
||||
if (len > 0) {
|
||||
return len;
|
||||
}
|
||||
return ((size_t*) p)[-1];
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_string_length(GuString s)
|
||||
{
|
||||
if (gu_string_is_long(s)) {
|
||||
return gu_string_long_length(s);
|
||||
}
|
||||
return (s.w_ & 0xff) >> 1;
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
gu_string_long_data(GuString s)
|
||||
{
|
||||
gu_require(gu_string_is_long(s));
|
||||
uint8_t* p = (void*) s.w_;
|
||||
return &p[1];
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_string_copy(GuString string, GuPool* pool)
|
||||
{
|
||||
if (gu_string_is_long(string)) {
|
||||
uint8_t* data = gu_string_long_data(string);
|
||||
size_t len = gu_string_long_length(string);
|
||||
return gu_utf8_string(data, len, pool);
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_string_write(GuString s, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuWord w = s.w_;
|
||||
uint8_t buf[sizeof(GuWord)];
|
||||
uint8_t* src;
|
||||
size_t sz;
|
||||
if (w & 1) {
|
||||
sz = (w & 0xff) >> 1;
|
||||
gu_assert(sz <= sizeof(GuWord));
|
||||
size_t i = sz;
|
||||
while (i > 0) {
|
||||
w >>= 8;
|
||||
buf[--i] = w & 0xff;
|
||||
}
|
||||
src = buf;
|
||||
} else {
|
||||
uint8_t* p = (void*) w;
|
||||
sz = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
|
||||
src = &p[1];
|
||||
}
|
||||
gu_utf8_write(src, sz, wtr, err);
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_format_string_v(const char* fmt, va_list args, GuPool* pool)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuStringBuf* sb = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sb);
|
||||
gu_vprintf(fmt, args, wtr, NULL);
|
||||
gu_writer_flush(wtr, NULL);
|
||||
GuString s = gu_string_buf_freeze(sb, pool);
|
||||
gu_pool_free(tmp_pool);
|
||||
return s;
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_format_string(GuPool* pool, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
GuString s = gu_format_string_v(fmt, args, pool);
|
||||
va_end(args);
|
||||
return s;
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_str_string(const char* str, GuPool* pool)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
return gu_utf8_string((const uint8_t*) str, strlen(str), pool);
|
||||
#else
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuStringBuf* sb = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sb);
|
||||
gu_puts(str, wtr, NULL);
|
||||
gu_writer_flush(wtr, NULL);
|
||||
GuString s = gu_string_buf_freeze(sb, pool);
|
||||
gu_pool_free(tmp_pool);
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
GuWord
|
||||
gu_string_hash(GuString s)
|
||||
{
|
||||
if (s.w_ & 1) {
|
||||
return s.w_;
|
||||
}
|
||||
size_t len = gu_string_length(s);
|
||||
uint8_t* data = gu_string_long_data(s);
|
||||
return gu_hash_bytes(0, data, len);
|
||||
}
|
||||
|
||||
bool
|
||||
gu_string_eq(GuString s1, GuString s2)
|
||||
{
|
||||
if (s1.w_ == s2.w_) {
|
||||
return true;
|
||||
} else if (gu_string_is_long(s1) && gu_string_is_long(s2)) {
|
||||
size_t len1 = gu_string_long_length(s1);
|
||||
size_t len2 = gu_string_long_length(s2);
|
||||
if (len1 != len2) {
|
||||
return false;
|
||||
}
|
||||
uint8_t* data1 = gu_string_long_data(s1);
|
||||
uint8_t* data2 = gu_string_long_data(s2);
|
||||
return (memcmp(data1, data2, len1) == 0);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static GuHash
|
||||
gu_string_hasher_hash(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
const GuString* sp = p;
|
||||
return gu_string_hash(*sp);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_string_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuString* sp1 = p1;
|
||||
const GuString* sp2 = p2;
|
||||
return gu_string_eq(*sp1, *sp2);
|
||||
}
|
||||
|
||||
GuHasher gu_string_hasher[1] = {
|
||||
{
|
||||
.eq = { gu_string_eq_fn },
|
||||
.hash = gu_string_hasher_hash
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(GuString, GuOpaque, _);
|
||||
GU_DEFINE_TYPE(GuStrings, GuSeq, gu_type(GuString));
|
||||
GU_DEFINE_KIND(GuStringMap, GuMap);
|
||||
125
src/runtime/c/gu/string.h
Normal file
125
src/runtime/c/gu/string.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2011 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GU_STRING_H_
|
||||
#define GU_STRING_H_
|
||||
|
||||
#include <gu/bits.h>
|
||||
#include <gu/read.h>
|
||||
#include <gu/write.h>
|
||||
|
||||
typedef GuOpaque() GuString;
|
||||
|
||||
extern const GuString gu_empty_string;
|
||||
|
||||
GuString
|
||||
gu_string_copy(GuString string, GuPool* pool);
|
||||
|
||||
void
|
||||
gu_string_write(GuString string, GuWriter* wtr, GuExn* err);
|
||||
|
||||
GuReader*
|
||||
gu_string_reader(GuString string, GuPool* pool);
|
||||
|
||||
bool
|
||||
gu_string_is_stable(GuString string);
|
||||
|
||||
GuString
|
||||
gu_ucs_string(const GuUCS* ubuf, size_t len, GuPool* pool);
|
||||
|
||||
typedef struct GuStringBuf GuStringBuf;
|
||||
|
||||
GuStringBuf*
|
||||
gu_string_buf(GuPool* pool);
|
||||
|
||||
GuWriter*
|
||||
gu_string_buf_writer(GuStringBuf* sb);
|
||||
|
||||
GuString
|
||||
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool);
|
||||
|
||||
GuString
|
||||
gu_format_string_v(const char* fmt, va_list args, GuPool* pool);
|
||||
|
||||
GuString
|
||||
gu_format_string(GuPool* pool, const char* fmt, ...);
|
||||
|
||||
GuString
|
||||
gu_str_string(const char* str, GuPool* pool);
|
||||
|
||||
#endif // GU_STRING_H_
|
||||
|
||||
#if defined(GU_HASH_H_) && !defined(GU_STRING_H_HASH_)
|
||||
#define GU_STRING_H_HASH_
|
||||
|
||||
uintptr_t
|
||||
gu_string_hash(GuString s);
|
||||
|
||||
extern GuHasher gu_string_hasher[1];
|
||||
|
||||
bool
|
||||
gu_string_eq(GuString s1, GuString s2);
|
||||
#endif
|
||||
|
||||
#ifdef GU_TYPE_H_
|
||||
# ifndef GU_STRING_H_TYPE_
|
||||
# define GU_STRING_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_TYPE(GuString, GuOpaque);
|
||||
# endif
|
||||
|
||||
# if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_TYPE_)
|
||||
# define GU_STRING_H_SEQ_TYPE_
|
||||
extern GU_DECLARE_TYPE(GuStrings, GuSeq);
|
||||
# endif
|
||||
|
||||
# if defined(GU_MAP_H_TYPE_) && !defined(GU_STRING_H_MAP_TYPE_)
|
||||
# define GU_STRING_H_MAP_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuStringMap);
|
||||
typedef GuType_GuMap GuType_GuStringMap;
|
||||
|
||||
#define GU_TYPE_INIT_GuStringMap(KIND, MAP_T, VAL_T, DEFAULT) \
|
||||
GU_TYPE_INIT_GuMap(KIND, MAP_T, \
|
||||
gu_type(GuString), gu_string_hasher, \
|
||||
VAL_T, DEFAULT)
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_)
|
||||
#define GU_STRING_H_SEQ_
|
||||
|
||||
typedef GuSeq GuStrings;
|
||||
// typedef GuBuf GuStringBuf;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_MAP_H_) && !defined(GU_STRING_H_MAP_)
|
||||
#define GU_STRING_H_MAP_
|
||||
|
||||
typedef GuMap GuStringMap;
|
||||
|
||||
#define gu_new_string_map(VAL_T, DEFAULT, POOL) \
|
||||
gu_new_map(GuString, gu_string_hasher, (VAL_T), (DEFAULT), (POOL))
|
||||
|
||||
#endif
|
||||
|
||||
30
src/runtime/c/gu/sysdeps.h
Normal file
30
src/runtime/c/gu/sysdeps.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef GU_SYSDEPS_H_
|
||||
#define GU_SYSDEPS_H_
|
||||
|
||||
#include <guconfig.h>
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
# define GU_GNUC
|
||||
#endif
|
||||
|
||||
#ifdef GU_GNUC
|
||||
# define GU_ALIGNOF __alignof
|
||||
# define GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
# define GU_GNUC_ATTR(x) __attribute__(( x ))
|
||||
# if defined(__OPTIMIZE_SIZE__)
|
||||
# define GU_OPTIMIZE_SIZE
|
||||
# elif defined(__OPTIMIZE__)
|
||||
# define GU_OPTIMIZE_SPEED
|
||||
# endif
|
||||
#else
|
||||
# define GU_GNUC_ATTR(x)
|
||||
#endif
|
||||
|
||||
#ifdef S_SPLINT_S
|
||||
# define GU_SPLINT(x) %{ x %}
|
||||
#else
|
||||
# define GU_SPLINT(x)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GU_SYSDEPS_H_
|
||||
229
src/runtime/c/gu/type.c
Normal file
229
src/runtime/c/gu/type.c
Normal file
@@ -0,0 +1,229 @@
|
||||
|
||||
#include <gu/type.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/map.h>
|
||||
|
||||
GuKind GU_TYPE_IDENT(type)[1] = {{ .super = NULL }};
|
||||
|
||||
GU_DEFINE_KIND(alias, type);
|
||||
GU_DEFINE_KIND(typedef, alias);
|
||||
GU_DEFINE_KIND(referenced, alias);
|
||||
|
||||
GU_DEFINE_KIND(repr, type);
|
||||
GU_DEFINE_KIND(GuOpaque, repr);
|
||||
|
||||
GU_DEFINE_KIND(abstract, type);
|
||||
|
||||
GU_DEFINE_KIND(struct, repr);
|
||||
|
||||
GU_DEFINE_KIND(pointer, repr);
|
||||
GU_DEFINE_KIND(reference, pointer);
|
||||
GU_DEFINE_KIND(shared, pointer);
|
||||
|
||||
GU_DEFINE_KIND(primitive, repr);
|
||||
|
||||
// sizeof(void) is illegal, so do this manually
|
||||
GuPrimType GU_TYPE_IDENT(void)[1] = {{
|
||||
.repr_base = {
|
||||
.type_base = {
|
||||
.kind_base = {
|
||||
.super = gu_kind(primitive),
|
||||
},
|
||||
},
|
||||
.size = 0,
|
||||
.align = 1,
|
||||
},
|
||||
.name = "void",
|
||||
}};
|
||||
|
||||
GU_DEFINE_KIND(integer, primitive);
|
||||
GU_DEFINE_TYPE(char, integer, _);
|
||||
|
||||
GU_DEFINE_KIND(signed, integer);
|
||||
GU_DEFINE_TYPE(int, signed, _);
|
||||
GU_DEFINE_TYPE(int8_t, signed, _);
|
||||
GU_DEFINE_TYPE(int16_t, signed, _);
|
||||
GU_DEFINE_TYPE(int32_t, signed, _);
|
||||
GU_DEFINE_TYPE(int64_t, signed, _);
|
||||
GU_DEFINE_TYPE(intptr_t, signed, _);
|
||||
GU_DEFINE_TYPE(intmax_t, signed, _);
|
||||
|
||||
GU_DEFINE_KIND(unsigned, integer);
|
||||
GU_DEFINE_TYPE(uint8_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint16_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint32_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint64_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uintmax_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(size_t, unsigned, _);
|
||||
|
||||
GU_DEFINE_TYPE(GuLength, unsigned, _);
|
||||
|
||||
GU_DEFINE_KIND(GuFloating, primitive);
|
||||
GU_DEFINE_TYPE(float, GuFloating, _);
|
||||
GU_DEFINE_TYPE(double, GuFloating, _);
|
||||
GU_DEFINE_TYPE(GuLongDouble, GuFloating, _);
|
||||
|
||||
|
||||
GU_DEFINE_KIND(enum, repr);
|
||||
|
||||
bool gu_type_has_kind(GuType* type, GuKind* kind)
|
||||
{
|
||||
GuKind* k = (GuKind*)type;
|
||||
while (k != NULL) {
|
||||
if (k == kind) {
|
||||
return true;
|
||||
}
|
||||
k = k->super;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
struct GuTypeMap {
|
||||
GuMap* map;
|
||||
};
|
||||
|
||||
static void
|
||||
gu_type_map_init(GuTypeMap* tmap, GuTypeTable* table)
|
||||
{
|
||||
for (int i = 0; i < table->parents.len; i++) {
|
||||
gu_type_map_init(tmap, table->parents.elems[i]);
|
||||
}
|
||||
for (int i = 0; i < table->entries.len; i++) {
|
||||
GuTypeTableEntry* e = &table->entries.elems[i];
|
||||
gu_map_put(tmap->map, e->kind, void*, e->val);
|
||||
}
|
||||
}
|
||||
|
||||
GuTypeMap*
|
||||
gu_new_type_map(GuTypeTable* table, GuPool* pool)
|
||||
{
|
||||
GuTypeMap* tmap =
|
||||
gu_new_i(pool, GuTypeMap,
|
||||
.map = gu_new_map(GuKind, NULL, void*, &gu_null, pool));
|
||||
gu_type_map_init(tmap, table);
|
||||
return tmap;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_struct_has_flex(GuStructRepr* srepr)
|
||||
{
|
||||
for (int i = 0; i < srepr->members.len; i++) {
|
||||
if (srepr->members.elems[i].is_flex) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_type_map_get(GuTypeMap* tmap, GuType* type)
|
||||
{
|
||||
GuKind* kind = (GuKind*)type;
|
||||
while (kind != NULL) {
|
||||
void* val = gu_map_get(tmap->map, kind, void*);
|
||||
if (val != NULL) {
|
||||
return val;
|
||||
}
|
||||
kind = kind->super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_type_dyn_cast(GuType* type, GuKind* kind)
|
||||
{
|
||||
if (gu_type_has_kind(type, kind)) {
|
||||
return type;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const void*
|
||||
gu_type_check_cast(GuType* type, GuKind* kind)
|
||||
{
|
||||
gu_assert(gu_type_has_kind(type, kind));
|
||||
return type;
|
||||
}
|
||||
|
||||
GuTypeRepr*
|
||||
gu_type_repr(GuType* type)
|
||||
{
|
||||
GuTypeAlias* alias;
|
||||
while ((alias = gu_type_try_cast(type, alias))) {
|
||||
type = alias->type;
|
||||
}
|
||||
return gu_type_try_cast(type, repr);
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_type_size(GuType* type)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_repr(type);
|
||||
return repr ? repr->size : 0;
|
||||
}
|
||||
|
||||
GuEnumConstant*
|
||||
gu_enum_value(GuEnumType* etype, const void* enump)
|
||||
{
|
||||
size_t esize = etype->repr_base.size;
|
||||
#define CHECK_ENUM_TYPE(t_) do { \
|
||||
if (esize == sizeof(t_)) { \
|
||||
t_ c = *(const t_*)enump; \
|
||||
for (int i = 0; i < etype->constants.len; i++) { \
|
||||
GuEnumConstant* cp = &etype->constants.elems[i]; \
|
||||
t_ d = *(const t_*)cp->enum_value; \
|
||||
if (c == d) { \
|
||||
return cp; \
|
||||
} \
|
||||
} \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
CHECK_ENUM_TYPE(int);
|
||||
CHECK_ENUM_TYPE(char);
|
||||
CHECK_ENUM_TYPE(short);
|
||||
CHECK_ENUM_TYPE(long);
|
||||
CHECK_ENUM_TYPE(long long);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_type_malloc(GuType* type, GuPool* pool)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_repr(type);
|
||||
gu_assert(repr);
|
||||
return gu_malloc_aligned(pool, repr->size, repr->align);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
typedef const struct GuPtrConvFns GuPtrConvFns;
|
||||
|
||||
struct GuPtrConvFns {
|
||||
void* (*get)(const void* pp);
|
||||
void (*set)(void** pp, void* p);
|
||||
};
|
||||
|
||||
#define GU_TYPE_PTR_DEFINE_GETSET(name_, t_) \
|
||||
static void* gu_type_##name_##_ptr_get(const void* pp) { \
|
||||
return *(t_* const*) pp; \
|
||||
} \
|
||||
\
|
||||
static void gu_type_##name_##_ptr_set(void* pp, void* p) { \
|
||||
*(t_**) pp = p; \
|
||||
} \
|
||||
static GuPtrConvFns gu_ptr_conv_##name_ = { \
|
||||
.get = gu_type_##name_##_ptr_get, \
|
||||
.set = gu_type_##name_##_ptr_set \
|
||||
}
|
||||
|
||||
GU_TYPE_PTR_DEFINE_GETSET(void, void);
|
||||
GU_TYPE_PTR_DEFINE_GETSET(struct, GuStruct);
|
||||
GU_TYPE_PTR_DEFINE_GETSET(int, int);
|
||||
|
||||
|
||||
#endif
|
||||
454
src/runtime/c/gu/type.h
Normal file
454
src/runtime/c/gu/type.h
Normal file
@@ -0,0 +1,454 @@
|
||||
|
||||
#ifndef GU_TYPE_H_
|
||||
#define GU_TYPE_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
//
|
||||
// kind
|
||||
//
|
||||
|
||||
typedef const struct GuKind GuKind;
|
||||
|
||||
struct GuKind {
|
||||
GuKind* super;
|
||||
};
|
||||
|
||||
// Use GU_PASTE here so k_ can be preprocessor-expanded
|
||||
#define GU_TYPE_IDENT(k_) GU_PASTE(gu_type__,k_)
|
||||
|
||||
#define gu_kind(k_) ((GuKind*)GU_TYPE_IDENT(k_))
|
||||
|
||||
#define GU_DECLARE_KIND(k_) \
|
||||
GuKind GU_TYPE_IDENT(k_)[1]
|
||||
|
||||
extern GU_DECLARE_KIND(kind);
|
||||
|
||||
#define GU_DEFINE_KIND(k_, super_k_) \
|
||||
GuKind GU_TYPE_IDENT(k_)[1] = {{ .super = gu_kind(super_k_) }}
|
||||
|
||||
//
|
||||
// type
|
||||
//
|
||||
|
||||
typedef const struct GuType GuType;
|
||||
|
||||
struct GuType {
|
||||
GuKind kind_base;
|
||||
};
|
||||
|
||||
typedef GuType GuType_type;
|
||||
|
||||
extern GU_DECLARE_KIND(type);
|
||||
|
||||
#define GU_TYPE_INIT_type(k_, t_, _) { .kind_base = { .super = gu_kind(k_) } }
|
||||
|
||||
#define gu_type(t_) ((GuType*)gu_kind(t_))
|
||||
|
||||
|
||||
#define GU_KIND_TYPE(k_) GU_PASTE(GuType_,k_)
|
||||
|
||||
// This cannot be used indirectly, since we don't want to pp-expand k_.
|
||||
// We must inline the body into other macros.
|
||||
#define GU_TYPE_INIT(k_, ...) \
|
||||
GU_TYPE_INIT_##k_(k_, __VA_ARGS__)
|
||||
|
||||
//#define GU_TYPE_LIT(k_, ...)
|
||||
// ((GuType*)(GuType_##k_[]){GU_TYPE_INIT(k_, __VA_ARGS__)})
|
||||
#define GU_TYPE_LIT(k_, ...) \
|
||||
((GuType*)&(GU_KIND_TYPE(k_)) GU_TYPE_INIT_##k_(k_, __VA_ARGS__))
|
||||
|
||||
#define GU_DECLARE_TYPE(t_, k_) \
|
||||
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1]
|
||||
|
||||
//#define GU_DEFINE_TYPE(t_, k_, ...)
|
||||
// GuType_##k_ GU_TYPE_IDENT(t_) = GU_TYPE_INIT(k_, t_, __VA_ARGS__)
|
||||
#define GU_DEFINE_TYPE(t_, k_, ...) \
|
||||
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1] = \
|
||||
{ GU_TYPE_INIT_##k_(k_, t_, __VA_ARGS__) }
|
||||
|
||||
#define GU_DEFINE_TYPE_ALIAS(t1_, t2_) \
|
||||
static GuType* const GU_TYPE_IDENT(t1_) = gu_type(t2_)
|
||||
|
||||
|
||||
//
|
||||
// abstract
|
||||
//
|
||||
|
||||
typedef GuType GuType_abstract;
|
||||
|
||||
#define GU_TYPE_INIT_abstract(k_, t_, _) \
|
||||
GU_TYPE_INIT_type(k_, t_, _)
|
||||
|
||||
extern GU_DECLARE_KIND(abstract);
|
||||
|
||||
|
||||
//
|
||||
// repr
|
||||
//
|
||||
|
||||
typedef struct GuTypeRepr GuTypeRepr, GuType_repr;
|
||||
|
||||
struct GuTypeRepr {
|
||||
GuType type_base;
|
||||
uint16_t size;
|
||||
uint16_t align;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_repr(k_, t_, _) { \
|
||||
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
|
||||
.size = sizeof(t_), \
|
||||
.align = gu_alignof(t_) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(repr);
|
||||
|
||||
|
||||
|
||||
//
|
||||
// GuOpaque
|
||||
//
|
||||
|
||||
typedef GuType_repr GuType_GuOpaque;
|
||||
|
||||
#define GU_TYPE_INIT_GuOpaque GU_TYPE_INIT_repr
|
||||
|
||||
extern GU_DECLARE_KIND(GuOpaque);
|
||||
|
||||
//
|
||||
// pointer
|
||||
//
|
||||
|
||||
typedef const struct GuPointerType GuPointerType, GuType_pointer;
|
||||
|
||||
struct GuPointerType {
|
||||
GuType_repr repr_base;
|
||||
GuType* pointed_type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_pointer(k_, t_, pointed_) \
|
||||
{ \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.pointed_type = pointed_ \
|
||||
}
|
||||
|
||||
|
||||
extern GU_DECLARE_KIND(pointer);
|
||||
|
||||
#define gu_ptr_type(t_) \
|
||||
GU_TYPE_LIT(pointer, t_*, gu_type(t_))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// alias
|
||||
//
|
||||
|
||||
|
||||
typedef const struct GuTypeAlias GuTypeAlias, GuType_alias;
|
||||
|
||||
struct GuTypeAlias {
|
||||
GuType type_base;
|
||||
GuType* type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_alias(k_, t_, type_) { \
|
||||
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
|
||||
.type = type_ \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(alias);
|
||||
|
||||
//
|
||||
// typedef
|
||||
//
|
||||
|
||||
typedef const struct GuTypeDef GuTypeDef, GuType_typedef;
|
||||
|
||||
struct GuTypeDef {
|
||||
GuType_alias alias_base;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_typedef(k_, t_, type_) { \
|
||||
.alias_base = GU_TYPE_INIT_alias(k_, t_, type_), \
|
||||
.name = #t_, \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(typedef);
|
||||
|
||||
#define GU_DEFINE_TYPEDEF_X(t_, dk_, k_, ...) \
|
||||
GU_DEFINE_TYPE(t_, dk_, GU_TYPE_LIT(k_, t_, __VA_ARGS__))
|
||||
|
||||
#define GU_DEFINE_TYPEDEF(t_, ...) \
|
||||
GU_DEFINE_TYPEDEF_X(t_, typedef, __VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// referenced
|
||||
//
|
||||
|
||||
extern GU_DECLARE_KIND(referenced);
|
||||
|
||||
typedef GuType_alias GuType_referenced;
|
||||
|
||||
#define GU_TYPE_INIT_referenced GU_TYPE_INIT_alias
|
||||
|
||||
|
||||
|
||||
#include <gu/list.h>
|
||||
|
||||
//
|
||||
// struct
|
||||
//
|
||||
|
||||
typedef const struct GuStructRepr GuStructRepr, GuType_struct;
|
||||
|
||||
typedef const struct GuMember GuMember;
|
||||
|
||||
struct GuMember {
|
||||
ptrdiff_t offset;
|
||||
const char* name;
|
||||
GuType* type;
|
||||
bool is_flex;
|
||||
};
|
||||
|
||||
struct GuStructRepr {
|
||||
GuType_repr repr_base;
|
||||
const char* name;
|
||||
GuSList(GuMember) members;
|
||||
};
|
||||
|
||||
extern GU_DECLARE_KIND(struct);
|
||||
|
||||
#define GU_MEMBER_AUX_(struct_, member_, type_, is_flex_) \
|
||||
{ \
|
||||
.offset = offsetof(struct_, member_), \
|
||||
.name = #member_, \
|
||||
.type = type_, \
|
||||
.is_flex = is_flex_, \
|
||||
}
|
||||
|
||||
#define GU_MEMBER_V(struct_, member_, type_) \
|
||||
GU_MEMBER_AUX_(struct_, member_, type_, false)
|
||||
|
||||
#define GU_MEMBER(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_type(t_))
|
||||
|
||||
#define GU_MEMBER_P(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_ptr_type(t_))
|
||||
|
||||
#define GU_MEMBER_S(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_shared_ptr_type(t_))
|
||||
|
||||
#define GU_FLEX_MEMBER_V(struct_, member_, type_) \
|
||||
GU_MEMBER_AUX_(struct_, member_, type_, true)
|
||||
|
||||
#define GU_FLEX_MEMBER(s_, m_, t_) \
|
||||
GU_FLEX_MEMBER_V(s_, m_, gu_type(t_))
|
||||
|
||||
#define GU_FLEX_MEMBER_P(s_, m_, t_) \
|
||||
GU_FLEX_MEMBER_V(s_, m_, gu_ptr_type(t_))
|
||||
|
||||
|
||||
#define GU_TYPE_INIT_struct(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.name = #t_, \
|
||||
.members = GU_SLIST(GuMember, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
bool
|
||||
gu_struct_has_flex(GuStructRepr* srepr);
|
||||
|
||||
|
||||
//
|
||||
// reference
|
||||
//
|
||||
|
||||
typedef GuType_pointer GuType_reference;
|
||||
|
||||
#define GU_TYPE_INIT_reference GU_TYPE_INIT_pointer
|
||||
|
||||
extern GU_DECLARE_KIND(reference);
|
||||
|
||||
|
||||
//
|
||||
// shared
|
||||
//
|
||||
|
||||
typedef GuType_pointer GuType_shared;
|
||||
|
||||
#define GU_TYPE_INIT_shared GU_TYPE_INIT_pointer
|
||||
|
||||
extern GU_DECLARE_KIND(shared);
|
||||
|
||||
#define gu_shared_ptr_type(t_) \
|
||||
GU_TYPE_LIT(shared, t_*, gu_type(t_))
|
||||
|
||||
//
|
||||
// primitives
|
||||
//
|
||||
|
||||
typedef const struct GuPrimType GuPrimType, GuType_primitive;
|
||||
|
||||
struct GuPrimType {
|
||||
GuType_repr repr_base;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_primitive(k_, t_, _) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.name = #t_ \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(primitive);
|
||||
extern GU_DECLARE_TYPE(void, primitive);
|
||||
|
||||
#define GU_TYPE_INIT_integer GU_TYPE_INIT_primitive
|
||||
typedef GuType_primitive GuType_integer;
|
||||
extern GU_DECLARE_KIND(integer);
|
||||
extern GU_DECLARE_TYPE(char, integer);
|
||||
|
||||
#define GU_TYPE_INIT_signed GU_TYPE_INIT_integer
|
||||
typedef GuType_integer GuType_signed;
|
||||
extern GU_DECLARE_KIND(signed);
|
||||
extern GU_DECLARE_TYPE(int, signed);
|
||||
extern GU_DECLARE_TYPE(int8_t, signed);
|
||||
extern GU_DECLARE_TYPE(int16_t, signed);
|
||||
extern GU_DECLARE_TYPE(int32_t, signed);
|
||||
extern GU_DECLARE_TYPE(int64_t, signed);
|
||||
extern GU_DECLARE_TYPE(intptr_t, signed);
|
||||
extern GU_DECLARE_TYPE(intmax_t, signed);
|
||||
|
||||
#define GU_TYPE_INIT_unsigned GU_TYPE_INIT_integer
|
||||
typedef GuType_integer GuType_unsigned;
|
||||
extern GU_DECLARE_KIND(unsigned);
|
||||
extern GU_DECLARE_TYPE(uint8_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint16_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint32_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint64_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uintmax_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(size_t, unsigned);
|
||||
|
||||
typedef size_t GuLength;
|
||||
extern GU_DECLARE_TYPE(GuLength, unsigned); // TODO: get rid
|
||||
|
||||
|
||||
#define GU_TYPE_INIT_GuFloating GU_TYPE_INIT_primitive
|
||||
typedef GuType_primitive GuType_GuFloating;
|
||||
extern GU_DECLARE_KIND(GuFloating);
|
||||
extern GU_DECLARE_TYPE(float, GuFloating);
|
||||
extern GU_DECLARE_TYPE(double, GuFloating);
|
||||
typedef long double GuLongDouble;
|
||||
extern GU_DECLARE_TYPE(GuLongDouble, GuFloating);
|
||||
|
||||
|
||||
|
||||
//
|
||||
// enum
|
||||
//
|
||||
|
||||
extern GU_DECLARE_KIND(enum);
|
||||
|
||||
typedef const struct GuEnumConstant GuEnumConstant;
|
||||
|
||||
struct GuEnumConstant {
|
||||
const char* name;
|
||||
int64_t value;
|
||||
const void* enum_value;
|
||||
};
|
||||
|
||||
typedef const struct GuEnumType GuEnumType, GuType_enum;
|
||||
|
||||
struct GuEnumType {
|
||||
GuType_repr repr_base;
|
||||
GuSList(GuEnumConstant) constants;
|
||||
};
|
||||
|
||||
#define GU_ENUM_C(t_, x) { \
|
||||
.name = #x, \
|
||||
.value = x, \
|
||||
.enum_value = (const t_[1]){ x } \
|
||||
}
|
||||
|
||||
#define GU_TYPE_INIT_enum(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.constants = GU_SLIST(GuEnumConstant, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
GuEnumConstant*
|
||||
gu_enum_value(GuEnumType* etype, const void* enump);
|
||||
|
||||
|
||||
|
||||
|
||||
bool gu_type_has_kind(const GuType* type, const GuKind* kind);
|
||||
|
||||
|
||||
|
||||
|
||||
typedef const struct GuTypeTableEntry GuTypeTableEntry;
|
||||
|
||||
struct GuTypeTableEntry {
|
||||
GuKind* kind;
|
||||
void* val;
|
||||
};
|
||||
|
||||
typedef const struct GuTypeTable GuTypeTable;
|
||||
|
||||
struct GuTypeTable {
|
||||
GuSList(const GuTypeTable*) parents;
|
||||
GuSList(GuTypeTableEntry) entries;
|
||||
};
|
||||
|
||||
#define GU_TYPETABLE(parents_, ...) { \
|
||||
.parents = parents_, \
|
||||
.entries = GU_SLIST(GuTypeTableEntry, \
|
||||
__VA_ARGS__) \
|
||||
}
|
||||
|
||||
typedef struct GuTypeMap GuTypeMap;
|
||||
|
||||
GuTypeMap*
|
||||
gu_new_type_map(GuTypeTable* table, GuPool* pool);
|
||||
|
||||
void*
|
||||
gu_type_map_get(GuTypeMap* tmap, GuType* type);
|
||||
|
||||
size_t
|
||||
gu_type_size(GuType* type);
|
||||
|
||||
GuTypeRepr*
|
||||
gu_type_repr(GuType* type);
|
||||
|
||||
const void*
|
||||
gu_type_check_cast(GuType* t, GuKind* k);
|
||||
|
||||
const void*
|
||||
gu_type_dyn_cast(GuType* t, GuKind* k);
|
||||
|
||||
#define gu_type_try_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)gu_type_dyn_cast(type_, gu_kind(k_)))
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define gu_type_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)gu_type_check_cast(type_, gu_kind(k_)))
|
||||
#else
|
||||
#define gu_type_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)(type_))
|
||||
#endif
|
||||
|
||||
void* gu_type_malloc(GuType* type, GuPool* pool);
|
||||
|
||||
#if 0
|
||||
void* gu_type_ptr_get(GuType* type, const void* pp);
|
||||
void gu_type_ptr_set(GuType* type, void* pp, void* p);
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GU_TYPE_H_
|
||||
135
src/runtime/c/gu/ucs.c
Normal file
135
src/runtime/c/gu/ucs.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/assert.h>
|
||||
#include <guconfig.h>
|
||||
|
||||
GU_DEFINE_TYPE(GuUCSExn, abstract, _);
|
||||
|
||||
|
||||
#ifdef GU_CHAR_ASCII
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c)
|
||||
{
|
||||
if (c < 0) {
|
||||
return false;
|
||||
} else if (c < 64) {
|
||||
return UINT64_C(0xffffffef00003f81) & (UINT64_C(1) << c);
|
||||
}
|
||||
#if CHAR_MAX > 127 // Let's avoid spurious warnings
|
||||
else if (c > 127) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return UINT64_C(0x7ffffffefffffffe) & (UINT64_C(1) << (c - 64));
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err)
|
||||
{
|
||||
if (0 <= uc && uc <= 127) {
|
||||
char c = (char) uc;
|
||||
if (gu_char_is_valid(c)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(GU_CHAR_ASCII)
|
||||
|
||||
static const char gu_ucs_ascii[128] =
|
||||
"\0\0\0\0\0\0\0\a\b\t\n\v\f\r\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
" !\"#\0%&'()*+,-./0123456789:;<=>?"
|
||||
"\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
|
||||
"\0abcdefghijklmnopqrstuvwxyz{|}~\0";
|
||||
|
||||
const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX] = {
|
||||
['\0'] = 0x00, ['\a'] = 0x07, ['\b'] = 0x08, ['\t'] = 0x09,
|
||||
['\n'] = 0x0a, ['\v'] = 0x0b, ['\f'] = 0x0c, ['\r'] = 0x0d,
|
||||
[' '] = 0x20, ['!'] = 0x21, ['"'] = 0x22, ['#'] = 0x23, ['%'] = 0x25,
|
||||
['&'] = 0x26, ['\''] = 0x27, ['('] = 0x28, [')'] = 0x29, ['*'] = 0x2a,
|
||||
['+'] = 0x2b, [','] = 0x2c, ['-'] = 0x2d, ['.'] = 0x2e, ['/'] = 0x2f,
|
||||
['0'] = 0x30, ['1'] = 0x31, ['2'] = 0x32, ['3'] = 0x33, ['4'] = 0x34,
|
||||
['5'] = 0x35, ['6'] = 0x36, ['7'] = 0x37, ['8'] = 0x38, ['9'] = 0x39,
|
||||
[':'] = 0x3a, [';'] = 0x3b, ['<'] = 0x3c, ['='] = 0x3d, ['>'] = 0x3e,
|
||||
['?'] = 0x3f, ['A'] = 0x41, ['B'] = 0x42, ['C'] = 0x43, ['D'] = 0x44,
|
||||
['E'] = 0x45, ['F'] = 0x46, ['G'] = 0x47, ['H'] = 0x48, ['I'] = 0x49,
|
||||
['J'] = 0x4a, ['K'] = 0x4b, ['L'] = 0x4c, ['M'] = 0x4d, ['N'] = 0x4e,
|
||||
['O'] = 0x4f, ['P'] = 0x50, ['Q'] = 0x51, ['R'] = 0x52, ['S'] = 0x53,
|
||||
['T'] = 0x54, ['U'] = 0x55, ['V'] = 0x56, ['W'] = 0x57, ['X'] = 0x58,
|
||||
['Y'] = 0x59, ['Z'] = 0x5a, ['['] = 0x5b, ['\\'] = 0x5c, [']'] = 0x5d,
|
||||
['^'] = 0x5e, ['_'] = 0x5f, ['a'] = 0x61, ['b'] = 0x62, ['c'] = 0x63,
|
||||
['d'] = 0x64, ['e'] = 0x65, ['f'] = 0x66, ['g'] = 0x67, ['h'] = 0x68,
|
||||
['i'] = 0x69, ['j'] = 0x6a, ['k'] = 0x6b, ['l'] = 0x6c, ['m'] = 0x6d,
|
||||
['n'] = 0x6e, ['o'] = 0x6f, ['p'] = 0x70, ['q'] = 0x71, ['r'] = 0x72,
|
||||
['s'] = 0x73, ['t'] = 0x74, ['u'] = 0x75, ['v'] = 0x76, ['w'] = 0x77,
|
||||
['x'] = 0x78, ['y'] = 0x79, ['z'] = 0x7a, ['{'] = 0x7b, ['|'] = 0x7c,
|
||||
['}'] = 0x7d, ['~'] = 0x7e
|
||||
};
|
||||
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c)
|
||||
{
|
||||
if (c > 0) {
|
||||
return (gu_ucs_ascii_reverse_[(int) c] > 0);
|
||||
}
|
||||
return (c == '\0');
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err)
|
||||
{
|
||||
if (uc == 0) {
|
||||
return '\0';
|
||||
} else if (0 < uc && uc <= 127) {
|
||||
char c = gu_ucs_ascii[uc];
|
||||
if (c != '\0') {
|
||||
return (unsigned char) c;
|
||||
}
|
||||
}
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t
|
||||
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (n < len) {
|
||||
char c = cbuf[n];
|
||||
if (!gu_char_is_valid(c)) {
|
||||
gu_raise(err, GuUCSExn);
|
||||
return n;
|
||||
}
|
||||
ubuf[n] = gu_char_ucs(c);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (n < len) {
|
||||
char c = gu_ucs_char(ubuf[n], err);
|
||||
if (!gu_ok(err)) {
|
||||
break;
|
||||
}
|
||||
cbuf[n] = c;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
extern inline bool
|
||||
gu_ucs_valid(GuUCS ucs);
|
||||
|
||||
extern inline GuUCS
|
||||
gu_char_ucs(char c);
|
||||
53
src/runtime/c/gu/ucs.h
Normal file
53
src/runtime/c/gu/ucs.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef GU_UCS_H_
|
||||
#define GU_UCS_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
#if defined(__STDC_ISO_10646__) && WCHAR_MAX >= 0x10FFFF
|
||||
#include <wchar.h>
|
||||
#define GU_UCS_WCHAR
|
||||
typedef wchar_t GuUCS;
|
||||
#else
|
||||
typedef int32_t GuUCS;
|
||||
#endif
|
||||
|
||||
#define GU_UCS_MAX ((GuUCS)(0x10FFFF))
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c);
|
||||
|
||||
inline bool
|
||||
gu_ucs_valid(GuUCS ucs)
|
||||
{
|
||||
return ucs >= 0 && ucs <= GU_UCS_MAX;
|
||||
}
|
||||
|
||||
inline GuUCS
|
||||
gu_char_ucs(char c)
|
||||
{
|
||||
gu_require(gu_char_is_valid(c));
|
||||
#ifdef GU_CHAR_ASCII
|
||||
GuUCS u = (GuUCS) c;
|
||||
#else
|
||||
extern const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX];
|
||||
GuUCS u = gu_ucs_ascii_reverse_[(unsigned char) c];
|
||||
#endif
|
||||
gu_ensure(u < 0x80);
|
||||
return u;
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err);
|
||||
|
||||
size_t
|
||||
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err);
|
||||
|
||||
size_t
|
||||
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err);
|
||||
|
||||
extern GU_DECLARE_TYPE(GuUCSExn, abstract);
|
||||
|
||||
#endif // GU_ISO10646_H_
|
||||
220
src/runtime/c/gu/utf8.c
Normal file
220
src/runtime/c/gu/utf8.c
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <gu/assert.h>
|
||||
#include <gu/utf8.h>
|
||||
#include <guconfig.h>
|
||||
|
||||
GuUCS
|
||||
gu_utf8_decode(const uint8_t** src_inout)
|
||||
{
|
||||
const uint8_t* src = *src_inout;
|
||||
uint8_t c = src[0];
|
||||
if (c < 0x80) {
|
||||
*src_inout = src + 1;
|
||||
return (GuUCS) c;
|
||||
}
|
||||
size_t len = (c < 0xe0 ? 1 :
|
||||
c < 0xf0 ? 2 :
|
||||
3);
|
||||
uint32_t mask = 0x07071f7f;
|
||||
uint32_t u = c & (mask >> (len * 8));
|
||||
for (size_t i = 1; i <= len; i++) {
|
||||
c = src[i];
|
||||
u = u << 6 | (c & 0x3f);
|
||||
}
|
||||
*src_inout = &src[len + 1];
|
||||
return (GuUCS) u;
|
||||
}
|
||||
|
||||
GuUCS
|
||||
gu_in_utf8_(GuIn* in, GuExn* err)
|
||||
{
|
||||
uint8_t c = gu_in_u8(in, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
int len = (c < 0x80 ? 0 :
|
||||
c < 0xc2 ? -1 :
|
||||
c < 0xe0 ? 1 :
|
||||
c < 0xf0 ? 2 :
|
||||
c < 0xf5 ? 3 :
|
||||
-1);
|
||||
if (len < 0) {
|
||||
goto fail;
|
||||
} else if (len == 0) {
|
||||
return c;
|
||||
}
|
||||
static const uint8_t mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
|
||||
uint32_t u = c & mask[len];
|
||||
uint8_t buf[3];
|
||||
// If reading the extra bytes causes EOF, it is an encoding
|
||||
// error, not a legitimate end of character stream.
|
||||
GuExn* tmp_err = gu_exn(err, GuEOF, NULL);
|
||||
gu_in_bytes(in, buf, len, tmp_err);
|
||||
if (tmp_err->caught) {
|
||||
goto fail;
|
||||
}
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = buf[i];
|
||||
if ((c & 0xc0) != 0x80) {
|
||||
goto fail;
|
||||
}
|
||||
u = u << 6 | (c & 0x3f);
|
||||
}
|
||||
GuUCS ucs = (GuUCS) u;
|
||||
if (!gu_ucs_valid(ucs)) {
|
||||
goto fail;
|
||||
}
|
||||
return ucs;
|
||||
|
||||
fail:
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
gu_advance_utf8(GuUCS ucs, uint8_t* buf)
|
||||
{
|
||||
gu_require(gu_ucs_valid(ucs));
|
||||
if (ucs < 0x80) {
|
||||
buf[0] = (uint8_t) ucs;
|
||||
return 1;
|
||||
} else if (ucs < 0x800) {
|
||||
buf[0] = 0xc0 | (ucs >> 6);
|
||||
buf[1] = 0x80 | (ucs & 0x3f);
|
||||
return 2;
|
||||
} else if (ucs < 0x10000) {
|
||||
buf[0] = 0xe0 | (ucs >> 12);
|
||||
buf[1] = 0x80 | ((ucs >> 6) & 0x3f);
|
||||
buf[2] = 0x80 | (ucs & 0x3f);
|
||||
return 3;
|
||||
} else {
|
||||
buf[0] = 0xf0 | (ucs >> 18);
|
||||
buf[1] = 0x80 | ((ucs >> 12) & 0x3f);
|
||||
buf[2] = 0x80 | ((ucs >> 6) & 0x3f);
|
||||
buf[3] = 0x80 | (ucs & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
char
|
||||
gu_in_utf8_char_(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_ucs_char(gu_in_utf8(in, err), err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
size_t sz = gu_advance_utf8(ucs, buf);
|
||||
switch (sz) {
|
||||
case 2:
|
||||
gu_out_bytes(out, buf, 2, err);
|
||||
break;
|
||||
case 3:
|
||||
gu_out_bytes(out, buf, 3, err);
|
||||
break;
|
||||
case 4:
|
||||
gu_out_bytes(out, buf, 4, err);
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
extern inline void
|
||||
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err);
|
||||
|
||||
static size_t
|
||||
gu_utf32_out_utf8_buffered_(const GuUCS* src, size_t len, GuOut* out,
|
||||
GuExn* err)
|
||||
{
|
||||
size_t src_i = 0;
|
||||
while (src_i < len) {
|
||||
size_t dst_sz;
|
||||
uint8_t* dst = gu_out_begin_span(out, len - src_i, &dst_sz, err);
|
||||
if (!gu_ok(err)) {
|
||||
return src_i;
|
||||
}
|
||||
if (!dst) {
|
||||
gu_out_utf8(src[src_i], out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return src_i;
|
||||
}
|
||||
src_i++;
|
||||
break;
|
||||
}
|
||||
size_t dst_i = 0;
|
||||
while (true) {
|
||||
size_t safe = (dst_sz - dst_i) / 4;
|
||||
size_t end = GU_MIN(len, src_i + safe);
|
||||
if (end == src_i) {
|
||||
break;
|
||||
}
|
||||
do {
|
||||
GuUCS ucs = src[src_i++];
|
||||
dst_i += gu_advance_utf8(ucs, &dst[dst_i]);
|
||||
} while (src_i < end);
|
||||
}
|
||||
gu_out_end_span(out, dst_i);
|
||||
}
|
||||
return src_i;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err)
|
||||
{
|
||||
if (gu_out_is_buffered(out)) {
|
||||
return gu_utf32_out_utf8_buffered_(src, len, out, err);
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
gu_out_utf8(src[i], out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
#ifndef GU_CHAR_ASCII
|
||||
|
||||
void gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
size_t sz = 0;
|
||||
uint8_t* buf = gu_out_begin_span(out, len, &sz, err);
|
||||
if (!gu_ok(err)) {
|
||||
return;
|
||||
}
|
||||
if (buf != NULL && sz < len) {
|
||||
gu_out_end_span(out, 0);
|
||||
buf = NULL;
|
||||
}
|
||||
GuPool* tmp_pool = buf ? NULL : gu_local_pool();
|
||||
buf = buf ? buf : gu_new_n(uint8_t, len, tmp_pool);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
GuUCS ucs = gu_char_ucs(str[i]);
|
||||
buf[i] = (uint8_t) ucs;
|
||||
}
|
||||
if (tmp_pool) {
|
||||
gu_out_bytes(out, buf, len, err);
|
||||
gu_pool_free(tmp_pool);
|
||||
} else {
|
||||
gu_out_end_span(out, len);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern inline void
|
||||
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err);
|
||||
|
||||
extern inline GuUCS
|
||||
gu_in_utf8(GuIn* in, GuExn* err);
|
||||
|
||||
extern inline char
|
||||
gu_in_utf8_char(GuIn* in, GuExn* err);
|
||||
67
src/runtime/c/gu/utf8.h
Normal file
67
src/runtime/c/gu/utf8.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef GU_UTF8_H_
|
||||
#define GU_UTF8_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/ucs.h>
|
||||
|
||||
inline GuUCS
|
||||
gu_in_utf8(GuIn* in, GuExn* err)
|
||||
{
|
||||
int i = gu_in_peek_u8(in);
|
||||
if (i >= 0 && i < 0x80) {
|
||||
gu_in_consume(in, 1);
|
||||
return (GuUCS) i;
|
||||
}
|
||||
extern GuUCS gu_in_utf8_(GuIn* in, GuExn* err);
|
||||
return gu_in_utf8_(in, err);
|
||||
}
|
||||
|
||||
|
||||
inline char
|
||||
gu_in_utf8_char(GuIn* in, GuExn* err)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
int i = gu_in_peek_u8(in);
|
||||
if (i >= 0 && i < 0x80) {
|
||||
gu_in_consume(in, 1);
|
||||
return (char) i;
|
||||
}
|
||||
#endif
|
||||
extern char gu_in_utf8_char_(GuIn* in, GuExn* err);
|
||||
return gu_in_utf8_char_(in, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err)
|
||||
{
|
||||
gu_require(gu_ucs_valid(ucs));
|
||||
if (GU_LIKELY(ucs < 0x80)) {
|
||||
gu_out_u8(out, ucs, err);
|
||||
} else {
|
||||
gu_out_utf8_long_(ucs, out, err);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err);
|
||||
|
||||
GuUCS
|
||||
gu_utf8_decode(const uint8_t** utf8);
|
||||
|
||||
inline void
|
||||
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
gu_out_bytes(out, (const uint8_t*) str, strlen(str), err);
|
||||
#else
|
||||
extern void
|
||||
gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err);
|
||||
gu_str_out_utf8_(str, out, err);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // GU_UTF8_H_
|
||||
100
src/runtime/c/gu/variant.c
Normal file
100
src/runtime/c/gu/variant.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "variant.h"
|
||||
#include "bits.h"
|
||||
|
||||
enum {
|
||||
GU_VARIANT_ALIGNMENT = sizeof(uintptr_t)
|
||||
};
|
||||
|
||||
void*
|
||||
gu_alloc_variant(uint8_t tag, size_t size,
|
||||
size_t align, GuVariant* variant_out, GuPool* pool)
|
||||
{
|
||||
align = gu_max(align, GU_VARIANT_ALIGNMENT);
|
||||
if (((size_t)tag) > GU_VARIANT_ALIGNMENT - 2) {
|
||||
uint8_t* alloc = gu_malloc_aligned(pool, align + size, align);
|
||||
alloc[align - 1] = tag;
|
||||
void* p = &alloc[align];
|
||||
variant_out->p = (uintptr_t)p;
|
||||
return p;
|
||||
}
|
||||
void* p = gu_malloc_aligned(pool, size, align);
|
||||
variant_out->p = ((uintptr_t)p) | (tag + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
GuVariant
|
||||
gu_make_variant(uint8_t tag, size_t size, size_t align, const void* init,
|
||||
GuPool* pool)
|
||||
{
|
||||
GuVariant v;
|
||||
void* data = gu_alloc_variant(tag, size, align, &v, pool);
|
||||
memcpy(data, init, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
gu_variant_tag(GuVariant variant)
|
||||
{
|
||||
if (gu_variant_is_null(variant)) {
|
||||
return GU_VARIANT_NULL;
|
||||
}
|
||||
int u = variant.p % GU_VARIANT_ALIGNMENT;
|
||||
if (u == 0) {
|
||||
uint8_t* mem = (uint8_t*)variant.p;
|
||||
return mem[-1];
|
||||
}
|
||||
return u - 1;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_variant_data(GuVariant variant)
|
||||
{
|
||||
if (gu_variant_is_null(variant)) {
|
||||
return NULL;
|
||||
}
|
||||
return (void*)gu_align_backward(variant.p, GU_VARIANT_ALIGNMENT);
|
||||
}
|
||||
|
||||
GuVariantInfo gu_variant_open(GuVariant variant)
|
||||
{
|
||||
GuVariantInfo info = {
|
||||
.tag = gu_variant_tag(variant),
|
||||
.data = gu_variant_data(variant)
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
int
|
||||
gu_variant_intval(GuVariant variant)
|
||||
{
|
||||
int u = variant.p % GU_VARIANT_ALIGNMENT;
|
||||
if (u == 0) {
|
||||
int* mem = (int*)variant.p;
|
||||
return *mem;
|
||||
}
|
||||
return (variant.p / GU_VARIANT_ALIGNMENT);
|
||||
}
|
||||
|
||||
const GuVariant gu_null_variant = { (GuWord) NULL };
|
||||
|
||||
GU_DEFINE_KIND(GuVariant, repr);
|
||||
GU_DEFINE_KIND(GuVariantAsPtr, repr);
|
||||
167
src/runtime/c/gu/variant.h
Normal file
167
src/runtime/c/gu/variant.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Lightweight tagged data.
|
||||
*/
|
||||
|
||||
#ifndef GU_VARIANT_H_
|
||||
#define GU_VARIANT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
/** @name Variants
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct GuVariant GuVariant;
|
||||
|
||||
|
||||
void* gu_alloc_variant(uint8_t tag,
|
||||
size_t size, size_t align,
|
||||
GuVariant* variant_out, GuPool* pool);
|
||||
|
||||
GuVariant gu_make_variant(uint8_t tag,
|
||||
size_t size, size_t align,
|
||||
const void* init, GuPool* pool);
|
||||
|
||||
#define gu_new_variant(tag, type, variant_out, pool) \
|
||||
((type*)gu_alloc_variant(tag, sizeof(type), \
|
||||
gu_alignof(type), variant_out, pool))
|
||||
|
||||
/**<
|
||||
* @hideinitializer */
|
||||
|
||||
#define gu_new_variant_i(POOL, TAG, T, ...) \
|
||||
gu_make_variant(TAG, sizeof(T), gu_alignof(T), \
|
||||
&(T){ __VA_ARGS__ }, POOL)
|
||||
|
||||
|
||||
|
||||
#define gu_new_flex_variant(tag, type, flex_mem, n_elems, variant_out, pool) \
|
||||
((type*)gu_alloc_variant(tag, \
|
||||
GU_FLEX_SIZE(type, flex_mem, n_elems), \
|
||||
gu_flex_alignof(type), \
|
||||
variant_out, pool))
|
||||
/**<
|
||||
* @hideinitializer */
|
||||
|
||||
enum {
|
||||
GU_VARIANT_NULL = -1
|
||||
};
|
||||
|
||||
int gu_variant_tag(GuVariant variant);
|
||||
|
||||
void* gu_variant_data(GuVariant variant);
|
||||
|
||||
|
||||
typedef struct GuVariantInfo GuVariantInfo;
|
||||
|
||||
struct GuVariantInfo {
|
||||
int tag;
|
||||
void* data;
|
||||
};
|
||||
|
||||
GuVariantInfo gu_variant_open(GuVariant variant);
|
||||
|
||||
/** @privatesection */
|
||||
struct GuVariant {
|
||||
uintptr_t p;
|
||||
/**< @private */
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
static inline void*
|
||||
gu_variant_to_ptr(GuVariant variant)
|
||||
{
|
||||
return (void*)variant.p;
|
||||
}
|
||||
|
||||
static inline GuVariant
|
||||
gu_variant_from_ptr(const void* p)
|
||||
{
|
||||
GuVariant v = { (uintptr_t)p };
|
||||
return v;
|
||||
}
|
||||
|
||||
extern const GuVariant gu_null_variant;
|
||||
|
||||
static inline bool
|
||||
gu_variant_is_null(GuVariant v) {
|
||||
return ((void*)v.p == NULL);
|
||||
}
|
||||
|
||||
|
||||
// variant
|
||||
|
||||
typedef const struct GuConstructor GuConstructor;
|
||||
|
||||
struct GuConstructor {
|
||||
int c_tag;
|
||||
const char* c_name;
|
||||
const GuType* type;
|
||||
};
|
||||
|
||||
#define GU_CONSTRUCTOR_V(ctag, c_type) { \
|
||||
.c_tag = ctag, \
|
||||
.c_name = #ctag, \
|
||||
.type = c_type \
|
||||
}
|
||||
|
||||
#define GU_CONSTRUCTOR(ctag, t_) \
|
||||
GU_CONSTRUCTOR_V(ctag, gu_type(t_))
|
||||
|
||||
#define GU_CONSTRUCTOR_P(ctag, t_) \
|
||||
GU_CONSTRUCTOR_V(ctag, gu_ptr_type(t_))
|
||||
|
||||
#define GU_CONSTRUCTOR_S(ctag, t_, ...) \
|
||||
GU_CONSTRUCTOR_V(ctag, GU_TYPE_LIT(struct, t_, __VA_ARGS__))
|
||||
|
||||
#define GU_CONSTRUCTOR_S1(ctag, t_, mem1_, type1_) \
|
||||
GU_CONSTRUCTOR_S(ctag, t_, \
|
||||
GU_MEMBER(t_, mem1_, type1_))
|
||||
|
||||
#define GU_CONSTRUCTOR_S2(ctag, t_, mem1_, type1_, mem2_, type2_) \
|
||||
GU_CONSTRUCTOR_S(ctag, t_, \
|
||||
GU_MEMBER(t_, mem1_, type1_), \
|
||||
GU_MEMBER(t_, mem2_, type2_))
|
||||
|
||||
|
||||
|
||||
typedef GuSList(GuConstructor) GuConstructors;
|
||||
|
||||
typedef const struct GuVariantType GuVariantType, GuType_GuVariant;
|
||||
|
||||
struct GuVariantType {
|
||||
GuType_repr repr_base;
|
||||
GuConstructors ctors;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuVariant(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, GuVariant, _), \
|
||||
.ctors = GU_SLIST(GuConstructor, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(GuVariant);
|
||||
|
||||
#endif // GU_VARIANT_H_
|
||||
174
src/runtime/c/gu/write.c
Normal file
174
src/runtime/c/gu/write.c
Normal file
@@ -0,0 +1,174 @@
|
||||
#include <gu/write.h>
|
||||
|
||||
|
||||
size_t
|
||||
gu_utf32_write(const GuUCS* src, size_t len, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
return gu_utf32_out_utf8(src, len, &wtr->out_, err);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
char* str = gu_vasprintf(fmt, args, tmp_pool);
|
||||
gu_puts(str, wtr, err);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
void
|
||||
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_vprintf(fmt, args, wtr, err);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
GuWriter*
|
||||
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool)
|
||||
{
|
||||
GuOutStream* stream = gu_out_proxy_stream(utf8_out, pool);
|
||||
GuWriter* wtr = gu_new(GuWriter, pool);
|
||||
wtr->out_ = gu_init_out(stream);
|
||||
return wtr;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#ifdef GU_UCS_WCHAR
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
static const mbstate_t gu_init_mbstate; // implicitly initialized to zero
|
||||
#endif
|
||||
|
||||
typedef struct GuLocaleWriter GuLocaleWriter;
|
||||
|
||||
struct GuLocaleWriter {
|
||||
GuOutWriter owtr;
|
||||
#ifdef GU_UCS_WCHAR
|
||||
mbstate_t ps;
|
||||
size_t mb_cur_max;
|
||||
#endif
|
||||
};
|
||||
|
||||
size_t
|
||||
gu_locale_writer_write(GuWriter* wtr, const uint8_t* utf8_src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
GuLocaleWriter* lwtr = (GuLocaleWriter*) wtr;
|
||||
size_t done = 0;
|
||||
static const size_t bufsize = 256;
|
||||
#ifdef GU_UCS_WCHAR
|
||||
size_t margin = lwtr->mb_cur_max;
|
||||
#else
|
||||
size_t margin = 1;
|
||||
#endif
|
||||
GuOut* out = lwtr->owtr.out;
|
||||
if (gu_out_is_buffered(out)) {
|
||||
while (done < sz) {
|
||||
size_t dst_sz;
|
||||
uint8_t* dst = gu_out_begin_span(out, &dst_sz);
|
||||
if (!dst) {
|
||||
break;
|
||||
}
|
||||
if (dst_sz <= margin) {
|
||||
gu_out_end_span(out, 0);
|
||||
break;
|
||||
}
|
||||
size_t end = dst_sz - margin;
|
||||
const uint8_t*
|
||||
size_t n = done;
|
||||
while (n < sz && dst_i <= end) {
|
||||
#ifdef GU_UCS_WCHAR
|
||||
GuUCS ucs = gu_
|
||||
wchar_t wc = src[n];
|
||||
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
|
||||
#else
|
||||
*p = (uint8_t) gu_ucs_char(buf[n], err);
|
||||
size_t nb = 1;
|
||||
if (!gu_ok(err)) {
|
||||
gu_exn_clear(err);
|
||||
nb = (size_t) -1;
|
||||
}
|
||||
#endif
|
||||
if (nb == (size_t) -1) {
|
||||
*p++ = (uint8_t) '?';
|
||||
} else {
|
||||
p += nb;
|
||||
}
|
||||
|
||||
}
|
||||
for (
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
uint8_t cbuf[256];
|
||||
while (done < size && gu_ok(err)) {
|
||||
uint8_t* p = cbuf;
|
||||
uint8_t* edge = &cbuf[bufsize - margin];
|
||||
size_t n;
|
||||
for (n = done; p <= edge && n < size; n++) {
|
||||
#ifdef GU_UCS_WCHAR
|
||||
wchar_t wc = buf[n];
|
||||
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
|
||||
#else
|
||||
*p = (uint8_t) gu_ucs_char(buf[n], err);
|
||||
size_t nb = 1;
|
||||
if (!gu_ok(err)) {
|
||||
gu_exn_clear(err);
|
||||
nb = (size_t) -1;
|
||||
}
|
||||
#endif
|
||||
if (nb == (size_t) -1) {
|
||||
*p++ = (uint8_t) '?';
|
||||
} else {
|
||||
p += nb;
|
||||
}
|
||||
}
|
||||
gu_out_bytes(lwtr->owtr.out, cbuf, p - cbuf, err);
|
||||
if (gu_ok(err)) {
|
||||
done = n;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
GuWriter*
|
||||
gu_locale_writer(GuOut* out, GuPool* pool)
|
||||
{
|
||||
GuLocaleWriter* lwtr = gu_new_s(
|
||||
pool, GuLocaleWriter,
|
||||
.wtr.out.output = gu_locale_writer_output,
|
||||
.wtr.out.flush = gu_locale_writer_flush,
|
||||
.out = out);
|
||||
#ifdef GU_UCS_WCHAR
|
||||
lwtr->ps = gu_init_mbstate;
|
||||
lwtr->mb_cur_max = MB_CUR_MAX;
|
||||
#endif
|
||||
return (GuWriter*) lwtr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern inline void
|
||||
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_writer_flush(GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_putc(char c, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_puts(const char* str, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline size_t
|
||||
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err);
|
||||
|
||||
64
src/runtime/c/gu/write.h
Normal file
64
src/runtime/c/gu/write.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef GU_WRITE_H_
|
||||
#define GU_WRITE_H_
|
||||
|
||||
#include <gu/exn.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
typedef struct GuWriter GuWriter;
|
||||
|
||||
struct GuWriter {
|
||||
GuOut out_;
|
||||
};
|
||||
|
||||
size_t
|
||||
gu_utf32_write(const GuUCS* buf, size_t size, GuWriter* wtr, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_writer_flush(GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_out_flush(&wtr->out_, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_out_utf8(ucs, &wtr->out_, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_putc(char c, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuUCS ucs = gu_char_ucs(c);
|
||||
gu_out_u8(&wtr->out_, (uint8_t) ucs, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_puts(const char* str, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_str_out_utf8(str, &wtr->out_, err);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
return gu_out_bytes(&wtr->out_, src, sz, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err);
|
||||
|
||||
void
|
||||
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...);
|
||||
|
||||
//GuWriter
|
||||
//gu_init_utf8_writer(GuOut* utf8_out);
|
||||
|
||||
GuWriter*
|
||||
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool);
|
||||
|
||||
GuWriter*
|
||||
gu_make_locale_writer(GuOut* locale_out, GuPool* pool);
|
||||
|
||||
#endif // GU_WRITE_H_
|
||||
339
src/runtime/c/gu/yaml.c
Normal file
339
src/runtime/c/gu/yaml.c
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <gu/yaml.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/read.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
const GuYamlAnchor gu_yaml_null_anchor = 0;
|
||||
|
||||
typedef const struct GuYamlState GuYamlState;
|
||||
|
||||
struct GuYamlState {
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
GuYamlState* next;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
GuYamlState document, first_key, key, value, first_elem, elem;
|
||||
} gu_yaml_states = {
|
||||
.document = {
|
||||
.prefix = "---\n",
|
||||
.suffix = "\n...\n",
|
||||
.next = &gu_yaml_states.document,
|
||||
},
|
||||
.key = {
|
||||
.prefix = "? ",
|
||||
.next = &gu_yaml_states.value,
|
||||
},
|
||||
.value = {
|
||||
.prefix = ": ",
|
||||
.suffix = ",",
|
||||
.next = &gu_yaml_states.key,
|
||||
},
|
||||
.elem = {
|
||||
.suffix = ",",
|
||||
.next = &gu_yaml_states.elem,
|
||||
},
|
||||
};
|
||||
|
||||
typedef const struct GuYamlFrameClass GuYamlFrameClass;
|
||||
|
||||
struct GuYamlFrameClass {
|
||||
const char* open;
|
||||
GuYamlState* first;
|
||||
const char* close;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
GuYamlFrameClass document, mapping, sequence;
|
||||
} gu_yaml_frame_classes = {
|
||||
.mapping = {
|
||||
.open = "{",
|
||||
.first = &gu_yaml_states.key,
|
||||
.close = "}",
|
||||
},
|
||||
.sequence = {
|
||||
.open = "[",
|
||||
.first = &gu_yaml_states.elem,
|
||||
.close = "]",
|
||||
},
|
||||
};
|
||||
|
||||
typedef struct GuYamlFrame GuYamlFrame;
|
||||
|
||||
struct GuYamlFrame {
|
||||
GuYamlFrameClass* klass;
|
||||
GuYamlState* next;
|
||||
};
|
||||
|
||||
typedef GuBuf GuYamlStack;
|
||||
|
||||
struct GuYaml {
|
||||
GuWriter* wtr;
|
||||
GuExn* err;
|
||||
GuPool* pool;
|
||||
GuYamlState* state;
|
||||
bool in_node;
|
||||
bool have_anchor;
|
||||
bool have_tag;
|
||||
int next_anchor;
|
||||
bool indent;
|
||||
int indent_level;
|
||||
bool indented;
|
||||
GuYamlStack* stack;
|
||||
};
|
||||
|
||||
|
||||
GuYaml*
|
||||
gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool)
|
||||
{
|
||||
GuYaml* yaml = gu_new(GuYaml, pool);
|
||||
yaml->wtr = wtr;
|
||||
yaml->pool = pool;
|
||||
yaml->err = err;
|
||||
yaml->state = &gu_yaml_states.document;
|
||||
yaml->in_node = false;
|
||||
yaml->have_anchor = false;
|
||||
yaml->have_tag = false;
|
||||
yaml->next_anchor = 1;
|
||||
yaml->stack = gu_new_buf(GuYamlFrame, pool);
|
||||
yaml->indent = true;
|
||||
yaml->indent_level = 0;
|
||||
yaml->indented = false;
|
||||
return yaml;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_printf(GuYaml* yaml, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_vprintf(fmt, args, yaml->wtr, yaml->err);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_putc(GuYaml* yaml, char c)
|
||||
{
|
||||
gu_putc(c, yaml->wtr, yaml->err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_puts(GuYaml* yaml, const char* str)
|
||||
{
|
||||
gu_puts(str, yaml->wtr, yaml->err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_begin_line(GuYaml* yaml)
|
||||
{
|
||||
if (yaml->indent && !yaml->indented) {
|
||||
for (int i = 0; i < yaml->indent_level; i++) {
|
||||
gu_yaml_putc(yaml, ' ');
|
||||
}
|
||||
yaml->indented = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_end_line(GuYaml* yaml)
|
||||
{
|
||||
if (yaml->indent) {
|
||||
gu_yaml_putc(yaml, '\n');
|
||||
}
|
||||
yaml->indented = false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_yaml_begin_node(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin_line(yaml);
|
||||
if (!yaml->in_node) {
|
||||
if (yaml->state->prefix != NULL) {
|
||||
gu_yaml_puts(yaml, yaml->state->prefix);
|
||||
}
|
||||
yaml->in_node = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_end_node(GuYaml* yaml)
|
||||
{
|
||||
gu_assert(yaml->in_node);
|
||||
if (yaml->state->suffix != NULL) {
|
||||
gu_yaml_puts(yaml, yaml->state->suffix);
|
||||
}
|
||||
gu_yaml_end_line(yaml);
|
||||
yaml->in_node = false;
|
||||
yaml->have_anchor = false;
|
||||
yaml->have_tag = false;
|
||||
if (yaml->state != NULL) {
|
||||
yaml->state = yaml->state->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_begin(GuYaml* yaml, GuYamlFrameClass* klass)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_yaml_puts(yaml, klass->open);
|
||||
gu_buf_push(yaml->stack, GuYamlFrame,
|
||||
((GuYamlFrame) { .klass = klass, .next = yaml->state}));
|
||||
yaml->state = klass->first;
|
||||
yaml->in_node = yaml->have_anchor = yaml->have_tag = false;
|
||||
gu_yaml_end_line(yaml);
|
||||
yaml->indent_level++;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_begin_mapping(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin(yaml, &gu_yaml_frame_classes.mapping);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_begin_sequence(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin(yaml, &gu_yaml_frame_classes.sequence);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_end(GuYaml* yaml)
|
||||
{
|
||||
gu_assert(!yaml->in_node);
|
||||
yaml->indent_level--;
|
||||
gu_yaml_begin_line(yaml);
|
||||
GuYamlFrame f = gu_buf_pop(yaml->stack, GuYamlFrame);
|
||||
gu_yaml_puts(yaml, f.klass->close);
|
||||
yaml->state = f.next;
|
||||
yaml->in_node = true;
|
||||
gu_yaml_end_node(yaml);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_yaml_scalar(GuYaml* yaml, GuString s)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_yaml_putc(yaml, '"');
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuReader* rdr = gu_string_reader(s, tmp_pool);
|
||||
GuExn* err = gu_exn(yaml->err, GuEOF, NULL);
|
||||
|
||||
static const char esc[0x20] = {
|
||||
[0x00] = '0',
|
||||
[0x07] = 'a', 'b', 't', 'n', 'v', 'f', 'r',
|
||||
[0x1b] = 'e'
|
||||
};
|
||||
|
||||
while (true) {
|
||||
GuUCS u = gu_read_ucs(rdr, err);
|
||||
if (!gu_ok(err)) {
|
||||
break;
|
||||
}
|
||||
if (GU_LIKELY(u >= 0x20 && u < 0x7f)) {
|
||||
if (GU_UNLIKELY(u == 0x22 || u == 0x5c)) {
|
||||
gu_yaml_putc(yaml, '\\');
|
||||
}
|
||||
gu_ucs_write(u, yaml->wtr, yaml->err);
|
||||
} else if (GU_UNLIKELY(u < 0x20 && esc[u])) {
|
||||
gu_yaml_printf(yaml, "\\%c", esc[u]);
|
||||
} else if (GU_UNLIKELY(u <= 0x9f)) {
|
||||
gu_yaml_printf(yaml, "\\x%02x", (unsigned) u);
|
||||
} else if (GU_UNLIKELY((u >= 0xd800 && u <= 0xdfff) ||
|
||||
(u >= 0xfffe && u <= 0xffff))) {
|
||||
gu_yaml_printf(yaml, "\\u%04x", (unsigned) u);
|
||||
} else {
|
||||
gu_ucs_write(u, yaml->wtr, yaml->err);
|
||||
}
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_yaml_putc(yaml, '"');
|
||||
gu_yaml_end_node(yaml);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_tag(GuYaml* yaml, const char* format, ...)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_tag);
|
||||
gu_yaml_putc(yaml, '!');
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
gu_vprintf(format, args, yaml->wtr, yaml->err);
|
||||
va_end(args);
|
||||
gu_yaml_putc(yaml, ' ');
|
||||
yaml->have_tag = true;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_primary(GuYaml* yaml, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "%s", tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_secondary(GuYaml* yaml, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "!%s", tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "%s!%s", handle, tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri)
|
||||
{
|
||||
// XXX: uri escaping?
|
||||
gu_yaml_tag(yaml, "<%s>", uri);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_non_specific(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_tag(yaml, "");
|
||||
}
|
||||
|
||||
GuYamlAnchor
|
||||
gu_yaml_anchor(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_anchor);
|
||||
yaml->have_anchor = true;
|
||||
int anchor = yaml->next_anchor++;
|
||||
gu_yaml_printf(yaml, "&%d ", anchor);
|
||||
return anchor;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_anchor && !yaml->have_tag);
|
||||
gu_yaml_printf(yaml, "*%d ", anchor);
|
||||
gu_yaml_end_node(yaml);
|
||||
return;
|
||||
}
|
||||
|
||||
void gu_yaml_comment(GuYaml* yaml, GuString s)
|
||||
{
|
||||
gu_yaml_begin_line(yaml);
|
||||
gu_yaml_puts(yaml, "# ");
|
||||
// TODO: verify no newlines in comment
|
||||
gu_string_write(s, yaml->wtr, yaml->err);
|
||||
gu_yaml_puts(yaml, "\n");
|
||||
yaml->indented = false;
|
||||
}
|
||||
|
||||
38
src/runtime/c/gu/yaml.h
Normal file
38
src/runtime/c/gu/yaml.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef GU_YAML_H_
|
||||
#define GU_YAML_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/write.h>
|
||||
#include <gu/string.h>
|
||||
|
||||
typedef struct GuYaml GuYaml;
|
||||
|
||||
typedef int GuYamlAnchor;
|
||||
|
||||
extern const GuYamlAnchor gu_yaml_null_anchor;
|
||||
|
||||
GuYaml* gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool);
|
||||
|
||||
GuYamlAnchor gu_yaml_anchor(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_tag_primary(GuYaml* yaml, const char* tag);
|
||||
void gu_yaml_tag_secondary(GuYaml* yaml, const char* tag);
|
||||
void gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag);
|
||||
void gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri);
|
||||
void gu_yaml_tag_non_specific(GuYaml* yaml);
|
||||
void gu_yaml_comment(GuYaml* yaml, GuString comment);
|
||||
|
||||
|
||||
void gu_yaml_scalar(GuYaml* yaml, GuString scalar);
|
||||
|
||||
void gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor);
|
||||
|
||||
void gu_yaml_begin_document(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_begin_sequence(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_begin_mapping(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_end(GuYaml* yaml);
|
||||
|
||||
#endif // GU_YAML_H_
|
||||
Reference in New Issue
Block a user