commit 18f833e9a6f8aae2b678ff9a749e9ec0c75eca3f
parent baa5991dfcb95a6e3f9a8b865f620c9231363d53
Author: Robert Russell <robertrussell.72001@gmail.com>
Date: Fri, 2 Sep 2022 12:51:46 -0700
Change library name from cext to rcx
rcx is short for Robert's C Extensions, or something. Mainly I just
wanted all the prefixes to be "r_" instead of "cext_", because the
latter is too long for functions that I use frequently.
Diffstat:
33 files changed, 678 insertions(+), 671 deletions(-)
diff --git a/Makefile b/Makefile
@@ -6,24 +6,24 @@ SRC =\
src/alloc.c\
src/bench.c\
src/log.c\
- src/opt.c\
src/str.c\
src/unicode.c\
src/utf8.c
+ # src/opt.c needs work
-libcext.a: $(SRC:.c=.o)
+librcx.a: $(SRC:.c=.o)
$(AR) -rcs $@ $(SRC:.c=.o)
.c.o:
$(CC) -c -o $@ $(CFLAGS) $<
-src/alloc.o: src/alloc.c inc/cext/alloc.h inc/cext/cext.h inc/cext/def.h inc/cext/log.h inc/cext/internal/util.h config.mk
-src/bench.o: src/bench.c inc/cext/bench.h inc/cext/cext.h inc/cext/def.h inc/cext/log.h config.mk
-src/log.o: src/log.c inc/cext/cext.h inc/cext/def.h inc/cext/log.h config.mk
-src/opt.o: src/opt.c inc/cext/cext.h inc/cext/def.h inc/cext/opt.h config.mk
-src/str.o: src/str.c inc/cext/alloc.h inc/cext/cext.h inc/cext/def.h inc/cext/log.h inc/cext/str.h config.mk
-src/unicode.o: src/unicode.c inc/cext/cext.h inc/cext/def.h gen/ucattab.inc config.mk
-src/utf8.o: src/utf8.c inc/cext/cext.h inc/cext/def.h inc/cext/utf8.h config.mk
+src/alloc.o: src/alloc.c inc/rcx/alloc.h inc/rcx/def.h inc/rcx/log.h inc/rcx/rcx.h inc/rcx/internal/util.h config.mk
+src/bench.o: src/bench.c inc/rcx/bench.h inc/rcx/def.h inc/rcx/log.h inc/rcx/rcx.h config.mk
+src/log.o: src/log.c inc/rcx/def.h inc/rcx/log.h inc/rcx/rcx.h config.mk
+src/opt.o: src/opt.c inc/rcx/def.h inc/rcx/opt.h inc/rcx/rcx.h config.mk
+src/str.o: src/str.c inc/rcx/alloc.h inc/rcx/def.h inc/rcx/log.h inc/rcx/rcx.h inc/rcx/str.h config.mk
+src/unicode.o: src/unicode.c inc/rcx/def.h inc/rcx/rcx.h gen/ucattab.inc config.mk
+src/utf8.o: src/utf8.c inc/rcx/def.h inc/rcx/rcx.h inc/rcx/utf8.h config.mk
gen/ucattab.inc: gen tool/ucattab gen/UnicodeData.txt
tool/ucattab gen/UnicodeData.txt > $@
@@ -37,17 +37,17 @@ gen:
tool/ucattab: tool/ucattab.c src/alloc.o src/log.o src/str.o
$(CC) -o $@ $(CFLAGS) $^
-install: libcext.a
+install: librcx.a
mkdir -p $(DESTDIR)$(INCPREFIX)
- cp -rf inc/cext $(DESTDIR)$(INCPREFIX)/
+ cp -rf inc/rcx $(DESTDIR)$(INCPREFIX)/
mkdir -p $(DESTDIR)$(LIBPREFIX)
- cp -f libcext.a $(DESTDIR)$(LIBPREFIX)/
+ cp -f librcx.a $(DESTDIR)$(LIBPREFIX)/
uninstall:
- rm -rf $(DESTDIR)$(INCPREFIX)/cext
- rm -f $(DESTDIR)$(LIBPREFIX)/libcext.a
+ rm -rf $(DESTDIR)$(INCPREFIX)/rcx
+ rm -f $(DESTDIR)$(LIBPREFIX)/librcx.a
clean:
- rm -rf libcext.a $(SRC:.c=.o) gen tool/ucattab
+ rm -rf librcx.a $(SRC:.c=.o) gen tool/ucattab
.PHONY: install uninstall clean
diff --git a/inc/cext/all.h b/inc/cext/all.h
@@ -1,10 +0,0 @@
-/* Everything except bench.h */
-#include "cext/alloc.h"
-#include "cext/cext.h"
-#include "cext/deque.h"
-#include "cext/log.h"
-#include "cext/opt.h"
-#include "cext/str.h"
-#include "cext/unicode.h"
-#include "cext/utf8.h"
-#include "cext/vector.h"
diff --git a/inc/cext/alloc.h b/inc/cext/alloc.h
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "cext/def.h"
-
-/* A consistently-named set of memory allocators: {,e}{,re}alloc{,n}{,z}
- * e- => allocation failures are fatal
- * re- => realloc-style allocator
- * -n => array allocator (with overflow check)
- * -z => new memory initialized to 0.
- * All these allocators are interoperable with the stdlib allocators. */
-void *alloc(usize size); /* aka malloc */
-void *allocz(usize size);
-void *allocn(usize len, usize size);
-void *allocnz(usize len, usize size); /* aka calloc */
-void *realloc(void *p, usize size);
-void *reallocz(void *p, usize osize, usize nsize);
-void *reallocn(void *p, usize len, usize size);
-void *reallocnz(void *p, usize olen, usize nlen, usize size);
-void *ealloc(usize size);
-void *eallocz(usize size);
-void *eallocn(usize len, usize size);
-void *eallocnz(usize len, usize size);
-void *erealloc(void *p, usize size);
-void *ereallocz(void *p, usize osize, usize nsize);
-void *ereallocn(void *p, usize len, usize size);
-void *ereallocnz(void *p, usize olen, usize nlen, usize size);
-
-void free(void *p);
diff --git a/inc/cext/bench.h b/inc/cext/bench.h
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "cext/def.h"
-
-/*
-Usage:
- void my_benchmark(u64 N) {
- <initialization (not timed)>
- cext_bench_start();
- for (u64 i = 0; i < N; i++) {
- <code to benchmark>
- }
- cext_bench_stop();
- <cleanup (not timed)>
- }
- int main(void) {
- cext_bench("my benchmark", my_benchmark, 3000);
- }
-Note that <code to benchmark> can contain calls to cext_bench_stop and
-cext_bench_start to pause and restart timing.
-*/
-
-void cext_bench(char *name, void (*fn)(u64 N), u32 goalms);
-void cext_bench_start(void);
-void cext_bench_stop(void);
diff --git a/inc/cext/cext.h b/inc/cext/cext.h
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <inttypes.h>
-#include <stddef.h>
-
-/* Standard headers that should be part of the language proper */
-#include <stdarg.h>
-#include <stdbool.h>
-#if __STDC_VERSION__ >= 201100L
-#include <stdalign.h>
-#include <stdnoreturn.h>
-#endif
-
-#include "cext/def.h"
diff --git a/inc/cext/def.h b/inc/cext/def.h
@@ -1,164 +0,0 @@
-#pragma once
-
-#include <limits.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#define JOIN_AUX(a,b) a##b
-#define JOIN(a,b) JOIN_AUX(a,b)
-
-#define LEN(a) (sizeof (a) / sizeof (a)[0])
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-
-#ifdef __GNUC__
-#define UNUSED __attribute__((unused))
-#define likely(x) __builtin_expect((x), 1)
-#define unlikely(x) __builtin_expect((x), 0)
-#else
-#define UNUSED
-#define likely(x) (x)
-#define unlikely(x) (x)
-#endif
-
-#if CHAR_BIT != 8
-#error "Expected CHAR_BIT == 8"
-#endif
-
-#if !defined(__STRICT_ANSI__) && defined(__SIZEOF_INT128__)
-#define CEXT_HAVE_128 1
-#endif
-
-/* Here, roughly speaking, "word" means the largest unit that the machine can
- * operate on in one instruction (typically the same width as registers and
- * the bus). This detection is probably wrong on some weird machines, but this
- * is the best we can do in C. */
-#if UINTPTR_MAX == UINT32_MAX
-#define WORD_SIZE 4
-#elif UINTPTR_MAX == UINT64_MAX
-#define WORD_SIZE 8
-#else /* This won't happen except on weird archs */
-#error "Could not determine machine word size"
-#endif
-
-/* Correct the mistakes of whoever named these macros */
-#define SHORT_MIN SHRT_MIN
-#define SHORT_MAX SHRT_MAX
-#define USHORT_MAX USHRT_MAX
-
-typedef signed char schar;
-typedef unsigned char uchar;
-typedef unsigned short ushort;
-typedef unsigned int uint;
-typedef unsigned long ulong;
-typedef long long llong;
-typedef unsigned long long ullong;
-
-#define I8_MIN INT8_MIN
-#define I8_MAX INT8_MAX
-#define I8_C INT8_C
-typedef int8_t i8;
-
-#define I16_MIN INT16_MIN
-#define I16_MAX INT16_MAX
-#define I16_C INT16_C
-typedef int16_t i16;
-
-#define I32_MIN INT32_MIN
-#define I32_MAX INT32_MAX
-#define I32_C INT32_C
-typedef int32_t i32;
-
-#define I64_MIN INT64_MIN
-#define I64_MAX INT64_MAX
-#define I64_C INT64_C
-typedef int64_t i64;
-
-#ifdef CEXT_HAVE_128
-#define I128_MIN ((i128)-1 - I128_MAX)
-#define I128_MAX ((i128)(U128_MAX >> 1))
-typedef __int128 i128;
-#endif
-
-#define IMAX_MIN INTMAX_MIN
-#define IMAX_MAX INTMAX_MAX
-#define IMAX_C INTMAX_C
-typedef intmax_t imax;
-
-#define IPTR_MIN INTPTR_MIN
-#define IPTR_MAX INTPTR_MAX
-typedef intptr_t iptr;
-
-/* Avoid dependence on POSIX sys/types.h for ssize_t */
-#if SIZE_MAX == UINT32_MAX
-typedef int32_t isize;
-#elif SIZE_MAX == UINT64_MAX
-typedef int64_t isize;
-#else /* This won't happen except on weird archs */
-#error "Could not find suitable type for isize"
-#endif
-
-#define IWORD_MIN INTPTR_MIN
-#define IWORD_MAX INTPTR_MAX
-#if WORD_SIZE == 4
-#define IWORD_C INT32_C
-#else /* WORD_SIZE == 8 */
-#define IWORD_C INT64_C
-#endif
-typedef intptr_t iword;
-
-#define U8_MAX UINT8_MAX
-#define U8_C UINT8_C
-typedef uint8_t u8;
-
-#define U16_MAX UINT16_MAX
-#define U16_C UINT16_C
-typedef uint16_t u16;
-
-#define U32_MAX UINT32_MAX
-#define U32_C UINT32_C
-typedef uint32_t u32;
-
-#define U64_MAX UINT64_MAX
-#define U64_C UINT64_C
-typedef uint64_t u64;
-
-#ifdef CEXT_HAVE_128
-#define U128_MAX (((u128)U64_MAX << 64) | U64_MAX)
-typedef unsigned __int128 u128;
-#endif
-
-#define UMAX_MAX UINTMAX_MAX
-#define UMAX_C UINTMAX_C
-typedef uintmax_t umax;
-
-#define UPTR_MAX UINTPTR_MAX
-typedef uintptr_t uptr;
-
-#define USIZE_MAX SIZE_MAX
-typedef size_t usize;
-
-#define UWORD_MAX UINTPTR_MAX
-#if WORD_SIZE == 4
-#define UWORD_C UINT32_C
-#else /* WORD_SIZE == 8 */
-#define UWORD_C UINT64_C
-#endif
-typedef uintptr_t uword;
-
-#define RUNE_BAD RUNE_C(0xFFFD)
-#define RUNE_MAX RUNE_C(0x10FFFF)
-#define RUNE_C UINT32_C
-typedef uint32_t rune;
-
-#if __STDC_VERSION__ >= 201100L
-typedef max_align_t maxalign;
-#else
-/* Fallback which is probably correct */
-typedef struct {
- intmax_t i; /* biggest integer */
- long double d; /* biggest floating point */
- void *p; /* data pointer */
- void (*f)(void); /* function pointer */
-} maxalign;
-#endif
diff --git a/inc/cext/deque.h b/inc/cext/deque.h
@@ -1,124 +0,0 @@
-#pragma once
-
-#include <string.h>
-
-#include "cext/alloc.h"
-#include "cext/def.h"
-
-/* Defaults */
-#define DEQUE_STATIC
-#define DEQUE_METHOD(name, prefix) JOIN(JOIN(prefix,_),name)
-#define DEQUE_MIN_BITS 3 /* 1<<3 = 8 elements */
-#define DEQUE_REALLOCN ereallocn
-#define DEQUE_FREE free
-
-#define DEQUE_TYPEDEF(D, T)\
-typedef struct D { \
- T *arr; \
- usize cap; /* Always a power of 2 for fast mod */ \
- usize l; /* Left end of active region */ \
- usize r; /* 1 past right end of active region */ \
-} D;
-
-/* Invariants:
- * Empty iff l == cap && r == 0
- * Full iff l == r */
-
-#define DEQUE_DECLARE(D, T, ...)\
-DEQUE_STATIC UNUSED void DEQUE_METHOD(free,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED usize DEQUE_METHOD(len,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED usize DEQUE_METHOD(cap,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED int DEQUE_METHOD(reserve,##__VA_ARGS__)(D *d, usize n); \
-DEQUE_STATIC UNUSED int DEQUE_METHOD(pushl,##__VA_ARGS__)(D *d, T e); \
-DEQUE_STATIC UNUSED int DEQUE_METHOD(pushr,##__VA_ARGS__)(D *d, T e); \
-DEQUE_STATIC UNUSED T DEQUE_METHOD(popl,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED T DEQUE_METHOD(popr,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED T DEQUE_METHOD(peekl,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED T DEQUE_METHOD(peekr,##__VA_ARGS__)(D *d); \
-DEQUE_STATIC UNUSED T *DEQUE_METHOD(idx,##__VA_ARGS__)(D *d, usize i);
-
-#define DEQUE_DEFINE(D, T, ...)\
-void DEQUE_METHOD(free,##__VA_ARGS__)(D *d) { \
- DEQUE_FREE(d->arr); \
- *d = (D){0}; \
-} \
-usize DEQUE_METHOD(len,##__VA_ARGS__)(D *d) { \
- return d->l == d->r ? d->cap : (d->r - d->l) & (d->cap - 1); \
-} \
-usize DEQUE_METHOD(cap,##__VA_ARGS__)(D *d) { \
- return d->cap; \
-} \
-int DEQUE_METHOD(reserve,##__VA_ARGS__)(D *d, usize n) { \
- usize rem = d->cap - DEQUE_METHOD(len,##__VA_ARGS__)(d); \
- if (n <= rem) \
- return 0; \
- usize need = n - rem; \
- usize ncap = MAX(d->cap, 1<<DEQUE_MIN_BITS); \
- while (need > ncap - d->cap) { \
- ncap <<= 1; \
- if (ncap == 0) \
- return -1; /* Overflow */ \
- } \
- T *narr = DEQUE_REALLOCN(d->arr, ncap, sizeof *narr); \
- if (!narr) \
- return -1; \
- if (d->l == d->cap) { \
- d->l = ncap; /* Maintain invariant for empty deques */ \
- } else if (d->r <= d->l) { \
- /* Move as little as possible */ \
- if (d->r <= d->cap - d->l) { \
- memcpy(&narr[d->cap], &narr[0], d->r * sizeof *narr); \
- d->r += d->cap; \
- } else { \
- usize m = d->cap - d->l; \
- memcpy(&narr[ncap-m], &narr[d->l], m * sizeof *narr); \
- d->l = ncap - m; \
- } \
- } \
- d->arr = narr; \
- d->cap = ncap; \
- return 0; \
-} \
-int DEQUE_METHOD(pushl,##__VA_ARGS__)(D *d, T e) { \
- if (DEQUE_METHOD(reserve,##__VA_ARGS__)(d, 1) < 0) \
- return -1; \
- d->l = (d->l - 1) & (d->cap - 1); \
- d->arr[d->l] = e; \
- return 0; \
-} \
-int DEQUE_METHOD(pushr,##__VA_ARGS__)(D *d, T e) { \
- if (DEQUE_METHOD(reserve,##__VA_ARGS__)(d, 1) < 0) \
- return -1; \
- d->arr[d->r] = e; \
- d->r = (d->r + 1) & (d->cap - 1); \
- if (d->l == d->cap) \
- d->l = 0; \
- return 0; \
-} \
-T DEQUE_METHOD(popl,##__VA_ARGS__)(D *d) { \
- T e = d->arr[d->l]; \
- d->l = (d->l + 1) & (d->cap - 1); \
- if (d->l == d->r) { \
- d->l = d->cap; \
- d->r = 0; \
- } \
- return e; \
-} \
-T DEQUE_METHOD(popr,##__VA_ARGS__)(D *d) { \
- d->r = (d->r - 1) & (d->cap - 1); \
- T e = d->arr[d->r]; \
- if (d->l == d->r) { \
- d->l = d->cap; \
- d->r = 0; \
- } \
- return e; \
-} \
-T DEQUE_METHOD(peekl,##__VA_ARGS__)(D *d) { \
- return d->arr[d->l]; \
-} \
-T DEQUE_METHOD(peekr,##__VA_ARGS__)(D *d) { \
- return d->arr[(d->r - 1) & (d->cap - 1)]; \
-} \
-T *DEQUE_METHOD(idx,##__VA_ARGS__)(D *d, usize i) { \
- return &d->arr[(d->l + i) & (d->cap - 1)]; \
-}
diff --git a/inc/cext/internal/util.h b/inc/cext/internal/util.h
@@ -1,12 +0,0 @@
-/* If s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW, then s1*s2 <= USIZE_MAX
- * (but not conversely). This lets us avoid division in overflow checks. */
-#define MUL_NO_OVERFLOW ((usize) 1 << (sizeof(usize) * 4))
-
-/* This is exposed to user when included in vector.h, so try to hide it. */
-static inline bool
-cext_mul_will_overflow_(usize a, usize b) {
- return (a >= MUL_NO_OVERFLOW || b >= MUL_NO_OVERFLOW)
- && a > 0 && USIZE_MAX/a < b;
-}
-
-#undef MUL_NO_OVERFLOW
diff --git a/inc/cext/log.h b/inc/cext/log.h
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <stdbool.h>
-
-#define infof(...) cext_log(__FILE__, __LINE__, 0, "INFO", "\x1b[32m", 0, __VA_ARGS__)
-#define warnf(...) cext_log(__FILE__, __LINE__, 1, "WARN", "\x1b[33m", 0, __VA_ARGS__)
-#define errorf(...) cext_log(__FILE__, __LINE__, 2, "ERROR", "\x1b[31m", 0, __VA_ARGS__)
-#define fatalf(...) cext_log(__FILE__, __LINE__, 3, "FATAL", "\x1b[31m", 1, __VA_ARGS__)
-
-/* Set global settings for cext_log.
- * color: <0 force disables color, 0 detects if color should be used,
- * and >0 force enables color
- * log_time: enable/disable timestamps in log messages
- * log_loc: enable/disable source location in log messages
- * min_level: output log messages iff their level is at least this value
- * The color detection happens when cext_log_init is called, not on each
- * subsequent invocation of cext_log.
- *
- * cext_log_init can be called multiple times, but it is not thread-safe.
- * Typically, you should call cext_log_init just once and before starting
- * multiple threads.
- *
- * Calling cext_log_init is optional. By default, color is off, time and source
- * location are not logged, and the minimum log level is set to 0 (so
- * everything is logged). */
-void cext_log_init(int color, bool log_time, bool log_loc, int min_level);
-
-/* Log a message to stderr. See definition of infof, warnf, etc. for usage.
- *
- * cext_log is thread-safe. */
-void cext_log(char *file, int line, int level, char *name, char *color, int code, char *fmt, ...);
diff --git a/inc/cext/str.h b/inc/cext/str.h
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <stdarg.h>
-#include <stdbool.h>
-
-#include "cext/def.h"
-
-char *cext_str_dup(char *s);
-char *cext_str_edup(char *s);
-
-bool cext_str_starts_with(char *s, char *sub);
-bool cext_str_ends_with(char *s, char *sub);
-
-/* Return the number of the nonoverlapping occurences of sub in s. */
-usize cext_str_count(char *s, char *sub);
-
-/* Split s into substrings separated by sep (which must be nonempty) and return
- * the number of separated substrings found. If n > 0, then fields must point
- * to an array of at least n strings; in this case, place at most n substrings
- * in fields, with the last substring being the unsplit remainder. Otherwise,
- * set *fields to an allocated array containing every separated substring in s.
- * If there is an allocation failure, set *fields to NULL and return 0. In
- * either case, the elements of *fields are pointers into s, and s will be
- * modified in order to null-terminate the substrings. */
-usize cext_str_split(char ***fields, char *s, char *sep, usize n);
-
-int cext_vaprintf(char **s, const char *fmt, va_list args);
-int cext_aprintf(char **s, const char *fmt, ...);
diff --git a/inc/cext/unicode.h b/inc/cext/unicode.h
@@ -1,5 +0,0 @@
-#pragma once
-
-#include "cext/def.h"
-
-char *cext_unicode_category(rune r);
diff --git a/inc/cext/utf8.h b/inc/cext/utf8.h
@@ -1,21 +0,0 @@
-#pragma once
-
-#include "cext/def.h"
-
-#define CEXT_UTF8_SIZE 4
-
-/* Return the number of bytes needed to encode c, or 0 if c is an invalid
- * codepoint. If s is nonnull, then it must have length >=
- * cext_utf8_encode(0, c), which is guaranteed to be at most CEXT_UTF8_SIZE;
- * in this case, if c is a valid codepoint, then encode c into s. */
-usize cext_utf8_encode(char *s, rune c);
-
-/* Decode the first rune in s and return the number of consumed bytes. If this
- * succeeds and c is nonnull, then set *c to the decoded rune. Otherwise, no
- * valid rune is legally encoded as a prefix of s; in this case, set *c to
- * RUNE_BAD if c is nonnull, and return n such that
- * - n = 0 iff s is null or an incomplete prefix of a valid rune;
- * - n > 0 iff the first min(n+1,slen) bytes of s are not a prefix of any
- * valid rune (but if n < slen, then s[n] might be the first byte of a
- * valid rune). */
-usize cext_utf8_decode(rune *c, char *s, usize slen);
diff --git a/inc/cext/vector.h b/inc/cext/vector.h
@@ -1,118 +0,0 @@
-#pragma once
-
-#include <errno.h>
-#include <string.h>
-
-#include "cext/alloc.h"
-#include "cext/def.h"
-
-#include "cext/internal/util.h"
-
-typedef struct {
- usize len;
- usize cap;
- maxalign arr[];
-} cext_vechdr_;
-
-#define CEXT_VECHDR_(v) ((cext_vechdr_ *)(v) - 1)
-
-/* Defaults */
-#define VECTOR_STATIC
-#define VECTOR_METHOD(name, prefix) JOIN(JOIN(prefix,_),name)
-#define VECTOR_MIN_CAP 8
-#define VECTOR_REALLOC erealloc
-#define VECTOR_FREE free
-
-#define VECTOR_DECLARE(T, ...)\
-VECTOR_STATIC UNUSED void VECTOR_METHOD(free,##__VA_ARGS__)(T **v); \
-VECTOR_STATIC UNUSED usize VECTOR_METHOD(len,##__VA_ARGS__)(T **v); \
-VECTOR_STATIC UNUSED usize VECTOR_METHOD(cap,##__VA_ARGS__)(T **v); \
-VECTOR_STATIC UNUSED int VECTOR_METHOD(resize,##__VA_ARGS__)(T **v, usize cap); \
-VECTOR_STATIC UNUSED int VECTOR_METHOD(reserve,##__VA_ARGS__)(T **v, usize n); \
-VECTOR_STATIC UNUSED int VECTOR_METHOD(ins,##__VA_ARGS__)(T **v, usize i, T e); \
-VECTOR_STATIC UNUSED int VECTOR_METHOD(push,##__VA_ARGS__)(T **v, T e); \
-VECTOR_STATIC UNUSED T VECTOR_METHOD(del,##__VA_ARGS__)(T **v, usize i); \
-VECTOR_STATIC UNUSED T VECTOR_METHOD(pop,##__VA_ARGS__)(T **v);
-
-#define VECTOR_DEFINE(T, ...)\
-void VECTOR_METHOD(free,##__VA_ARGS__)(T **v) { \
- if (*v) \
- VECTOR_FREE(CEXT_VECHDR_(*v)); \
- *v = 0; \
-} \
-usize VECTOR_METHOD(len,##__VA_ARGS__)(T **v) { \
- return *v ? CEXT_VECHDR_(*v)->len : 0; \
-} \
-usize VECTOR_METHOD(cap,##__VA_ARGS__)(T **v) { \
- return *v ? CEXT_VECHDR_(*v)->cap : 0; \
-} \
-int VECTOR_METHOD(resize,##__VA_ARGS__)(T **v, usize cap) { \
- if (cap == 0) { \
- VECTOR_METHOD(free,##__VA_ARGS__)(v); \
- } else { \
- cap = MAX(cap, VECTOR_MIN_CAP); \
- cext_vechdr_ *h = *v ? CEXT_VECHDR_(*v) : 0; \
- usize len = h ? h->len : 0; \
- usize arrsize = cap * sizeof (*v)[0]; \
- if (cext_mul_will_overflow_(cap, sizeof (*v)[0]) \
- || sizeof *h > USIZE_MAX - arrsize) { \
- errno = ENOMEM; \
- return -1; \
- } \
- h = VECTOR_REALLOC(h, sizeof *h + arrsize); \
- if (!h) return -1; \
- h->len = MIN(len, cap); \
- h->cap = cap; \
- *v = (void *)(h + 1); \
- } \
- return 0; \
-} \
-int VECTOR_METHOD(reserve,##__VA_ARGS__)(T **v, usize n) { \
- cext_vechdr_ *h = *v ? CEXT_VECHDR_(*v) : 0; \
- usize rem = h ? h->cap - h->len : 0; \
- if (n > rem) { \
- usize need = n - rem; \
- usize cap = h ? h->cap + MAX(h->cap, need) : need; \
- if (h && cap < h->cap) { /* Overflow? */ \
- errno = ENOMEM; \
- return -1; \
- } \
- return VECTOR_METHOD(resize,##__VA_ARGS__)(v, cap); \
- } else { \
- return 0; \
- } \
-} \
-int VECTOR_METHOD(ins,##__VA_ARGS__)(T **v, usize i, T e) { \
- if (VECTOR_METHOD(reserve,##__VA_ARGS__)(v, 1)) \
- return -1; \
- memmove(&(*v)[i+1], &(*v)[i], \
- (CEXT_VECHDR_(*v)->len - i) * sizeof (*v)[0]); \
- (*v)[i] = e; \
- CEXT_VECHDR_(*v)->len++; \
- return 0; \
-} \
-int VECTOR_METHOD(push,##__VA_ARGS__)(T **v, T e) { \
- return VECTOR_METHOD(ins,##__VA_ARGS__)(v, \
- VECTOR_METHOD(len,##__VA_ARGS__)(v), e); \
-} \
-T VECTOR_METHOD(del,##__VA_ARGS__)(T **v, usize i) { \
- T e = (*v)[i]; \
- memmove(&(*v)[i], &(*v)[i+1], \
- (CEXT_VECHDR_(*v)->len - i - 1) * sizeof (*v)[0]); \
- CEXT_VECHDR_(*v)->len--; \
- return e; \
-} \
-T VECTOR_METHOD(pop,##__VA_ARGS__)(T **v) { \
- return VECTOR_METHOD(del,##__VA_ARGS__)(v, CEXT_VECHDR_(*v)->len - 1); \
-}
-
-/* TODO?
-insn/insnz
-deln
-clr => set length to 0 without resizing
-dup => duplicate/clone vector
-optionally take cmp function and define:
- sort => qsort wrapper
- bsearch => bsearch wrapper
- lsearch => linear search on unsorted array
-*/
diff --git a/inc/rcx/all.h b/inc/rcx/all.h
@@ -0,0 +1,10 @@
+/* Everything except bench.h */
+#include "rcx/alloc.h"
+#include "rcx/deque.h"
+#include "rcx/log.h"
+#include "rcx/opt.h"
+#include "rcx/rcx.h"
+#include "rcx/str.h"
+#include "rcx/unicode.h"
+#include "rcx/utf8.h"
+#include "rcx/vector.h"
diff --git a/inc/rcx/alloc.h b/inc/rcx/alloc.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "rcx/def.h"
+
+/* TODO: change reallocaters to int r_realloc(void **p, ...) */
+
+/* A consistently-named set of memory allocators: r_{,e}{,re}alloc{,n}{,z}
+ * e- => allocation failures are fatal
+ * re- => realloc-style allocator
+ * -n => array allocator (with overflow check)
+ * -z => new memory initialized to 0.
+ * All these allocators are interoperable with the stdlib allocators. */
+void *r_alloc(usize size); /* aka malloc */
+void *r_allocz(usize size);
+void *r_allocn(usize len, usize size);
+void *r_allocnz(usize len, usize size); /* aka calloc */
+void *r_realloc(void *p, usize size);
+void *r_reallocz(void *p, usize osize, usize nsize);
+void *r_reallocn(void *p, usize len, usize size);
+void *r_reallocnz(void *p, usize olen, usize nlen, usize size);
+void *r_ealloc(usize size);
+void *r_eallocz(usize size);
+void *r_eallocn(usize len, usize size);
+void *r_eallocnz(usize len, usize size);
+void *r_erealloc(void *p, usize size);
+void *r_ereallocz(void *p, usize osize, usize nsize);
+void *r_ereallocn(void *p, usize len, usize size);
+void *r_ereallocnz(void *p, usize olen, usize nlen, usize size);
+
+void free(void *p);
diff --git a/inc/rcx/bench.h b/inc/rcx/bench.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "rcx/def.h"
+
+/*
+Usage:
+ void my_benchmark(u64 N) {
+ <initialization (not timed)>
+ r_bench_start();
+ for (u64 i = 0; i < N; i++) {
+ <code to benchmark>
+ }
+ r_bench_stop();
+ <cleanup (not timed)>
+ }
+ int main(void) {
+ r_bench("my benchmark", my_benchmark, 3000);
+ }
+Note that <code to benchmark> can contain calls to r_bench_stop and
+r_bench_start to pause and restart timing.
+*/
+
+void r_bench(char *name, void (*fn)(u64 N), u32 goalms);
+void r_bench_start(void);
+void r_bench_stop(void);
diff --git a/inc/rcx/def.h b/inc/rcx/def.h
@@ -0,0 +1,164 @@
+#pragma once
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define JOIN_AUX(a,b) a##b
+#define JOIN(a,b) JOIN_AUX(a,b)
+
+#define LEN(a) (sizeof (a) / sizeof (a)[0])
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((unused))
+#define likely(x) __builtin_expect((x), 1)
+#define unlikely(x) __builtin_expect((x), 0)
+#else
+#define UNUSED
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
+#if CHAR_BIT != 8
+#error "Expected CHAR_BIT == 8"
+#endif
+
+#if !defined(__STRICT_ANSI__) && defined(__SIZEOF_INT128__)
+#define R_HAVE_128 1
+#endif
+
+/* Here, roughly speaking, "word" means the largest unit that the machine can
+ * operate on in one instruction (typically the same width as registers and
+ * the bus). This detection is probably wrong on some weird machines, but this
+ * is the best we can do in C. */
+#if UINTPTR_MAX == UINT32_MAX
+#define WORD_SIZE 4
+#elif UINTPTR_MAX == UINT64_MAX
+#define WORD_SIZE 8
+#else /* This won't happen except on weird archs */
+#error "Could not determine machine word size"
+#endif
+
+/* Correct the mistakes of whoever named these macros */
+#define SHORT_MIN SHRT_MIN
+#define SHORT_MAX SHRT_MAX
+#define USHORT_MAX USHRT_MAX
+
+typedef signed char schar;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef long long llong;
+typedef unsigned long long ullong;
+
+#define I8_MIN INT8_MIN
+#define I8_MAX INT8_MAX
+#define I8_C INT8_C
+typedef int8_t i8;
+
+#define I16_MIN INT16_MIN
+#define I16_MAX INT16_MAX
+#define I16_C INT16_C
+typedef int16_t i16;
+
+#define I32_MIN INT32_MIN
+#define I32_MAX INT32_MAX
+#define I32_C INT32_C
+typedef int32_t i32;
+
+#define I64_MIN INT64_MIN
+#define I64_MAX INT64_MAX
+#define I64_C INT64_C
+typedef int64_t i64;
+
+#ifdef R_HAVE_128
+#define I128_MIN ((i128)-1 - I128_MAX)
+#define I128_MAX ((i128)(U128_MAX >> 1))
+typedef __int128 i128;
+#endif
+
+#define IMAX_MIN INTMAX_MIN
+#define IMAX_MAX INTMAX_MAX
+#define IMAX_C INTMAX_C
+typedef intmax_t imax;
+
+#define IPTR_MIN INTPTR_MIN
+#define IPTR_MAX INTPTR_MAX
+typedef intptr_t iptr;
+
+/* Avoid dependence on POSIX sys/types.h for ssize_t */
+#if SIZE_MAX == UINT32_MAX
+typedef int32_t isize;
+#elif SIZE_MAX == UINT64_MAX
+typedef int64_t isize;
+#else /* This won't happen except on weird archs */
+#error "Could not find suitable type for isize"
+#endif
+
+#define IWORD_MIN INTPTR_MIN
+#define IWORD_MAX INTPTR_MAX
+#if WORD_SIZE == 4
+#define IWORD_C INT32_C
+#else /* WORD_SIZE == 8 */
+#define IWORD_C INT64_C
+#endif
+typedef intptr_t iword;
+
+#define U8_MAX UINT8_MAX
+#define U8_C UINT8_C
+typedef uint8_t u8;
+
+#define U16_MAX UINT16_MAX
+#define U16_C UINT16_C
+typedef uint16_t u16;
+
+#define U32_MAX UINT32_MAX
+#define U32_C UINT32_C
+typedef uint32_t u32;
+
+#define U64_MAX UINT64_MAX
+#define U64_C UINT64_C
+typedef uint64_t u64;
+
+#ifdef R_HAVE_128
+#define U128_MAX (((u128)U64_MAX << 64) | U64_MAX)
+typedef unsigned __int128 u128;
+#endif
+
+#define UMAX_MAX UINTMAX_MAX
+#define UMAX_C UINTMAX_C
+typedef uintmax_t umax;
+
+#define UPTR_MAX UINTPTR_MAX
+typedef uintptr_t uptr;
+
+#define USIZE_MAX SIZE_MAX
+typedef size_t usize;
+
+#define UWORD_MAX UINTPTR_MAX
+#if WORD_SIZE == 4
+#define UWORD_C UINT32_C
+#else /* WORD_SIZE == 8 */
+#define UWORD_C UINT64_C
+#endif
+typedef uintptr_t uword;
+
+#define RUNE_BAD RUNE_C(0xFFFD)
+#define RUNE_MAX RUNE_C(0x10FFFF)
+#define RUNE_C UINT32_C
+typedef uint32_t rune;
+
+#if __STDC_VERSION__ >= 201100L
+typedef max_align_t maxalign;
+#else
+/* Fallback which is probably correct */
+typedef struct {
+ intmax_t i; /* biggest integer */
+ long double d; /* biggest floating point */
+ void *p; /* data pointer */
+ void (*f)(void); /* function pointer */
+} maxalign;
+#endif
diff --git a/inc/rcx/deque.h b/inc/rcx/deque.h
@@ -0,0 +1,124 @@
+#pragma once
+
+#include <string.h>
+
+#include "rcx/alloc.h"
+#include "rcx/def.h"
+
+/* Defaults */
+#define R_DEQUE_STATIC
+#define R_DEQUE_METHOD(name, prefix) JOIN(JOIN(prefix,_),name)
+#define R_DEQUE_MIN_BITS 3 /* 1<<3 = 8 elements */
+#define R_DEQUE_REALLOCN r_ereallocn
+#define R_DEQUE_FREE free
+
+#define R_DEQUE_TYPEDEF(D, T)\
+typedef struct D { \
+ T *arr; \
+ usize cap; /* Always a power of 2 for fast mod */ \
+ usize l; /* Left end of active region */ \
+ usize r; /* 1 past right end of active region */ \
+} D;
+
+/* Invariants:
+ * Empty iff l == cap && r == 0
+ * Full iff l == r */
+
+#define R_DEQUE_DECLARE(D, T, ...)\
+R_DEQUE_STATIC UNUSED void R_DEQUE_METHOD(free,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED usize R_DEQUE_METHOD(len,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED usize R_DEQUE_METHOD(cap,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED int R_DEQUE_METHOD(reserve,##__VA_ARGS__)(D *d, usize n); \
+R_DEQUE_STATIC UNUSED int R_DEQUE_METHOD(pushl,##__VA_ARGS__)(D *d, T e); \
+R_DEQUE_STATIC UNUSED int R_DEQUE_METHOD(pushr,##__VA_ARGS__)(D *d, T e); \
+R_DEQUE_STATIC UNUSED T R_DEQUE_METHOD(popl,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED T R_DEQUE_METHOD(popr,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED T R_DEQUE_METHOD(peekl,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED T R_DEQUE_METHOD(peekr,##__VA_ARGS__)(D *d); \
+R_DEQUE_STATIC UNUSED T *R_DEQUE_METHOD(idx,##__VA_ARGS__)(D *d, usize i);
+
+#define R_DEQUE_DEFINE(D, T, ...)\
+void R_DEQUE_METHOD(free,##__VA_ARGS__)(D *d) { \
+ R_DEQUE_FREE(d->arr); \
+ *d = (D){0}; \
+} \
+usize R_DEQUE_METHOD(len,##__VA_ARGS__)(D *d) { \
+ return d->l == d->r ? d->cap : (d->r - d->l) & (d->cap - 1); \
+} \
+usize R_DEQUE_METHOD(cap,##__VA_ARGS__)(D *d) { \
+ return d->cap; \
+} \
+int R_DEQUE_METHOD(reserve,##__VA_ARGS__)(D *d, usize n) { \
+ usize rem = d->cap - R_DEQUE_METHOD(len,##__VA_ARGS__)(d); \
+ if (n <= rem) \
+ return 0; \
+ usize need = n - rem; \
+ usize ncap = MAX(d->cap, 1<<R_DEQUE_MIN_BITS); \
+ while (need > ncap - d->cap) { \
+ ncap <<= 1; \
+ if (ncap == 0) \
+ return -1; /* Overflow */ \
+ } \
+ T *narr = R_DEQUE_REALLOCN(d->arr, ncap, sizeof *narr); \
+ if (!narr) \
+ return -1; \
+ if (d->l == d->cap) { \
+ d->l = ncap; /* Maintain invariant for empty deques */ \
+ } else if (d->r <= d->l) { \
+ /* Move as little as possible */ \
+ if (d->r <= d->cap - d->l) { \
+ memcpy(&narr[d->cap], &narr[0], d->r * sizeof *narr); \
+ d->r += d->cap; \
+ } else { \
+ usize m = d->cap - d->l; \
+ memcpy(&narr[ncap-m], &narr[d->l], m * sizeof *narr); \
+ d->l = ncap - m; \
+ } \
+ } \
+ d->arr = narr; \
+ d->cap = ncap; \
+ return 0; \
+} \
+int R_DEQUE_METHOD(pushl,##__VA_ARGS__)(D *d, T e) { \
+ if (R_DEQUE_METHOD(reserve,##__VA_ARGS__)(d, 1) < 0) \
+ return -1; \
+ d->l = (d->l - 1) & (d->cap - 1); \
+ d->arr[d->l] = e; \
+ return 0; \
+} \
+int R_DEQUE_METHOD(pushr,##__VA_ARGS__)(D *d, T e) { \
+ if (R_DEQUE_METHOD(reserve,##__VA_ARGS__)(d, 1) < 0) \
+ return -1; \
+ d->arr[d->r] = e; \
+ d->r = (d->r + 1) & (d->cap - 1); \
+ if (d->l == d->cap) \
+ d->l = 0; \
+ return 0; \
+} \
+T R_DEQUE_METHOD(popl,##__VA_ARGS__)(D *d) { \
+ T e = d->arr[d->l]; \
+ d->l = (d->l + 1) & (d->cap - 1); \
+ if (d->l == d->r) { \
+ d->l = d->cap; \
+ d->r = 0; \
+ } \
+ return e; \
+} \
+T R_DEQUE_METHOD(popr,##__VA_ARGS__)(D *d) { \
+ d->r = (d->r - 1) & (d->cap - 1); \
+ T e = d->arr[d->r]; \
+ if (d->l == d->r) { \
+ d->l = d->cap; \
+ d->r = 0; \
+ } \
+ return e; \
+} \
+T R_DEQUE_METHOD(peekl,##__VA_ARGS__)(D *d) { \
+ return d->arr[d->l]; \
+} \
+T R_DEQUE_METHOD(peekr,##__VA_ARGS__)(D *d) { \
+ return d->arr[(d->r - 1) & (d->cap - 1)]; \
+} \
+T *R_DEQUE_METHOD(idx,##__VA_ARGS__)(D *d, usize i) { \
+ return &d->arr[(d->l + i) & (d->cap - 1)]; \
+}
diff --git a/inc/rcx/internal/util.h b/inc/rcx/internal/util.h
@@ -0,0 +1,12 @@
+/* If s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW, then s1*s2 <= USIZE_MAX
+ * (but not conversely). This lets us avoid division in overflow checks. */
+#define MUL_NO_OVERFLOW ((usize) 1 << (sizeof(usize) * 4))
+
+/* This is exposed to user when included in vector.h, so try to hide it. */
+static inline bool
+r_mul_will_overflow_(usize a, usize b) {
+ return (a >= MUL_NO_OVERFLOW || b >= MUL_NO_OVERFLOW)
+ && a > 0 && USIZE_MAX/a < b;
+}
+
+#undef MUL_NO_OVERFLOW
diff --git a/inc/rcx/log.h b/inc/rcx/log.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdbool.h>
+
+#define r_infof(...) r_log(__FILE__, __LINE__, 0, "INFO", "\x1b[32m", 0, __VA_ARGS__)
+#define r_warnf(...) r_log(__FILE__, __LINE__, 1, "WARN", "\x1b[33m", 0, __VA_ARGS__)
+#define r_errorf(...) r_log(__FILE__, __LINE__, 2, "ERROR", "\x1b[31m", 0, __VA_ARGS__)
+#define r_fatalf(...) r_log(__FILE__, __LINE__, 3, "FATAL", "\x1b[31m", 1, __VA_ARGS__)
+
+/* Set global settings for r_log.
+ * color: <0 force disables color, 0 detects if color should be used,
+ * and >0 force enables color
+ * log_time: enable/disable timestamps in log messages
+ * log_loc: enable/disable source location in log messages
+ * min_level: output log messages iff their level is at least this value
+ * The color detection happens when r_log_init is called, not on each
+ * subsequent invocation of r_log.
+ *
+ * r_log_init can be called multiple times, but it is not thread-safe.
+ * Typically, you should call r_log_init just once and before starting
+ * multiple threads.
+ *
+ * Calling r_log_init is optional. By default, color is off, time and source
+ * location are not logged, and the minimum log level is set to 0 (so
+ * everything is logged). */
+void r_log_init(int color, bool log_time, bool log_loc, int min_level);
+
+/* Log a message to stderr. See definition of r_infof, r_warnf, etc. for usage.
+ *
+ * r_log is thread-safe. */
+void r_log(char *file, int line, int level, char *name, char *color, int code, char *fmt, ...);
diff --git a/inc/cext/opt.h b/inc/rcx/opt.h
diff --git a/inc/rcx/rcx.h b/inc/rcx/rcx.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stddef.h>
+
+/* Standard headers that should be part of the language proper */
+#include <stdarg.h>
+#include <stdbool.h>
+#if __STDC_VERSION__ >= 201100L
+#include <stdalign.h>
+#include <stdnoreturn.h>
+#endif
+
+#include "rcx/def.h"
diff --git a/inc/rcx/str.h b/inc/rcx/str.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "rcx/def.h"
+
+char *r_str_dup(char *s);
+char *r_str_edup(char *s);
+
+bool r_str_starts_with(char *s, char *sub);
+bool r_str_ends_with(char *s, char *sub);
+
+/* Return the number of the nonoverlapping occurences of sub in s. */
+usize r_str_count(char *s, char *sub);
+
+/* Split s into substrings separated by sep (which must be nonempty) and return
+ * the number of separated substrings found. If n > 0, then fields must point
+ * to an array of at least n strings; in this case, place at most n substrings
+ * in fields, with the last substring being the unsplit remainder. Otherwise,
+ * set *fields to an allocated array containing every separated substring in s.
+ * If there is an allocation failure, set *fields to NULL and return 0. In
+ * either case, the elements of *fields are pointers into s, and s will be
+ * modified in order to null-terminate the substrings. */
+usize r_str_split(char ***fields, char *s, char *sep, usize n);
+
+int r_vaprintf(char **s, const char *fmt, va_list args);
+int r_aprintf(char **s, const char *fmt, ...);
diff --git a/inc/rcx/unicode.h b/inc/rcx/unicode.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "rcx/def.h"
+
+char *r_unicode_category(rune r);
diff --git a/inc/rcx/utf8.h b/inc/rcx/utf8.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "rcx/def.h"
+
+#define R_UTF8_SIZE 4
+
+/* Return the number of bytes needed to encode c, or 0 if c is an invalid
+ * codepoint. If s is nonnull, then it must have length >=
+ * r_utf8_encode(0, c), which is guaranteed to be at most R_UTF8_SIZE;
+ * in this case, if c is a valid codepoint, then encode c into s. */
+usize r_utf8_encode(char *s, rune c);
+
+/* Decode the first rune in s and return the number of consumed bytes. If this
+ * succeeds and c is nonnull, then set *c to the decoded rune. Otherwise, no
+ * valid rune is legally encoded as a prefix of s; in this case, set *c to
+ * RUNE_BAD if c is nonnull, and return n such that
+ * - n = 0 iff s is null or an incomplete prefix of a valid rune;
+ * - n > 0 iff the first min(n+1,slen) bytes of s are not a prefix of any
+ * valid rune (but if n < slen, then s[n] might be the first byte of a
+ * valid rune). */
+usize r_utf8_decode(rune *c, char *s, usize slen);
diff --git a/inc/rcx/vector.h b/inc/rcx/vector.h
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+
+#include "rcx/alloc.h"
+#include "rcx/def.h"
+
+#include "rcx/internal/util.h"
+
+typedef struct {
+ usize len;
+ usize cap;
+ maxalign arr[];
+} r_vechdr_;
+
+#define R_VECHDR_(v) ((r_vechdr_ *)(v) - 1)
+
+/* Defaults */
+#define R_VECTOR_STATIC
+#define R_VECTOR_METHOD(name, prefix) JOIN(JOIN(prefix,_),name)
+#define R_VECTOR_MIN_CAP 8
+#define R_VECTOR_REALLOC r_erealloc
+#define R_VECTOR_FREE free
+
+#define R_VECTOR_DECLARE(T, ...)\
+R_VECTOR_STATIC UNUSED void R_VECTOR_METHOD(free,##__VA_ARGS__)(T **v); \
+R_VECTOR_STATIC UNUSED usize R_VECTOR_METHOD(len,##__VA_ARGS__)(T **v); \
+R_VECTOR_STATIC UNUSED usize R_VECTOR_METHOD(cap,##__VA_ARGS__)(T **v); \
+R_VECTOR_STATIC UNUSED int R_VECTOR_METHOD(resize,##__VA_ARGS__)(T **v, usize cap); \
+R_VECTOR_STATIC UNUSED int R_VECTOR_METHOD(reserve,##__VA_ARGS__)(T **v, usize n); \
+R_VECTOR_STATIC UNUSED int R_VECTOR_METHOD(ins,##__VA_ARGS__)(T **v, usize i, T e); \
+R_VECTOR_STATIC UNUSED int R_VECTOR_METHOD(push,##__VA_ARGS__)(T **v, T e); \
+R_VECTOR_STATIC UNUSED T R_VECTOR_METHOD(del,##__VA_ARGS__)(T **v, usize i); \
+R_VECTOR_STATIC UNUSED T R_VECTOR_METHOD(pop,##__VA_ARGS__)(T **v);
+
+#define R_VECTOR_DEFINE(T, ...)\
+void R_VECTOR_METHOD(free,##__VA_ARGS__)(T **v) { \
+ if (*v) \
+ R_VECTOR_FREE(R_VECHDR_(*v)); \
+ *v = 0; \
+} \
+usize R_VECTOR_METHOD(len,##__VA_ARGS__)(T **v) { \
+ return *v ? R_VECHDR_(*v)->len : 0; \
+} \
+usize R_VECTOR_METHOD(cap,##__VA_ARGS__)(T **v) { \
+ return *v ? R_VECHDR_(*v)->cap : 0; \
+} \
+int R_VECTOR_METHOD(resize,##__VA_ARGS__)(T **v, usize cap) { \
+ if (cap == 0) { \
+ R_VECTOR_METHOD(free,##__VA_ARGS__)(v); \
+ } else { \
+ cap = MAX(cap, R_VECTOR_MIN_CAP); \
+ r_vechdr_ *h = *v ? R_VECHDR_(*v) : 0; \
+ usize len = h ? h->len : 0; \
+ usize arrsize = cap * sizeof (*v)[0]; \
+ if (r_mul_will_overflow_(cap, sizeof (*v)[0]) \
+ || sizeof *h > USIZE_MAX - arrsize) { \
+ errno = ENOMEM; \
+ return -1; \
+ } \
+ h = R_VECTOR_REALLOC(h, sizeof *h + arrsize); \
+ if (!h) return -1; \
+ h->len = MIN(len, cap); \
+ h->cap = cap; \
+ *v = (void *)(h + 1); \
+ } \
+ return 0; \
+} \
+int R_VECTOR_METHOD(reserve,##__VA_ARGS__)(T **v, usize n) { \
+ r_vechdr_ *h = *v ? R_VECHDR_(*v) : 0; \
+ usize rem = h ? h->cap - h->len : 0; \
+ if (n > rem) { \
+ usize need = n - rem; \
+ usize cap = h ? h->cap + MAX(h->cap, need) : need; \
+ if (h && cap < h->cap) { /* Overflow? */ \
+ errno = ENOMEM; \
+ return -1; \
+ } \
+ return R_VECTOR_METHOD(resize,##__VA_ARGS__)(v, cap); \
+ } else { \
+ return 0; \
+ } \
+} \
+int R_VECTOR_METHOD(ins,##__VA_ARGS__)(T **v, usize i, T e) { \
+ if (R_VECTOR_METHOD(reserve,##__VA_ARGS__)(v, 1)) \
+ return -1; \
+ memmove(&(*v)[i+1], &(*v)[i], \
+ (R_VECHDR_(*v)->len - i) * sizeof (*v)[0]); \
+ (*v)[i] = e; \
+ R_VECHDR_(*v)->len++; \
+ return 0; \
+} \
+int R_VECTOR_METHOD(push,##__VA_ARGS__)(T **v, T e) { \
+ return R_VECTOR_METHOD(ins,##__VA_ARGS__)(v, \
+ R_VECTOR_METHOD(len,##__VA_ARGS__)(v), e); \
+} \
+T R_VECTOR_METHOD(del,##__VA_ARGS__)(T **v, usize i) { \
+ T e = (*v)[i]; \
+ memmove(&(*v)[i], &(*v)[i+1], \
+ (R_VECHDR_(*v)->len - i - 1) * sizeof (*v)[0]); \
+ R_VECHDR_(*v)->len--; \
+ return e; \
+} \
+T R_VECTOR_METHOD(pop,##__VA_ARGS__)(T **v) { \
+ return R_VECTOR_METHOD(del,##__VA_ARGS__)(v, R_VECHDR_(*v)->len - 1); \
+}
+
+/* TODO?
+insn/insnz
+deln
+clr => set length to 0 without resizing
+dup => duplicate/clone vector
+optionally take cmp function and define:
+ sort => qsort wrapper
+ bsearch => bsearch wrapper
+ lsearch => linear search on unsorted array
+*/
diff --git a/src/alloc.c b/src/alloc.c
@@ -2,68 +2,73 @@
#include <stdlib.h>
#include <string.h>
-#include "cext/alloc.h"
-#include "cext/cext.h"
-#include "cext/log.h"
+#include "rcx/alloc.h"
+#include "rcx/log.h"
+#include "rcx/rcx.h"
-#include "cext/internal/util.h"
+#include "rcx/internal/util.h"
void *
-alloc(usize size) {
+r_alloc(usize size) {
return malloc(size);
}
void *
-allocz(usize size) {
+r_allocz(usize size) {
return calloc(1, size);
}
void *
-allocn(usize len, usize size) {
- if (cext_mul_will_overflow_(len, size)) {
+r_allocn(usize len, usize size) {
+ if (r_mul_will_overflow_(len, size)) {
errno = ENOMEM;
return 0;
}
- return alloc(len * size);
+ return r_alloc(len * size);
}
void *
-allocnz(usize len, usize size) {
+r_allocnz(usize len, usize size) {
return calloc(len, size);
}
void *
-reallocz(void *p, usize osize, usize nsize) {
- p = realloc(p, nsize);
+r_realloc(void *p, usize size) {
+ return realloc(p, size);
+}
+
+void *
+r_reallocz(void *p, usize osize, usize nsize) {
+ p = r_realloc(p, nsize);
if (p && nsize > osize)
memset((char *) p + osize, 0, nsize - osize);
return p;
}
void *
-reallocn(void *p, usize len, usize size) {
- if (cext_mul_will_overflow_(len, size)) {
+r_reallocn(void *p, usize len, usize size) {
+ if (r_mul_will_overflow_(len, size)) {
errno = ENOMEM;
return 0;
}
- return realloc(p, len * size);
+ return r_realloc(p, len * size);
}
void *
-reallocnz(void *p, usize olen, usize nlen, usize size) {
- if (cext_mul_will_overflow_(nlen, size)) {
+r_reallocnz(void *p, usize olen, usize nlen, usize size) {
+ if (r_mul_will_overflow_(nlen, size)) {
errno = ENOMEM;
return 0;
}
- return reallocz(p, olen * size, nlen * size);
+ return r_reallocz(p, olen * size, nlen * size);
}
#define EALLOC(name, ...)\
- void *e##name(__VA_ARGS__) {\
- void *q = name(EALLOC_AUX
+ void *r_e##name(__VA_ARGS__) {\
+ void *q = r_##name(EALLOC_AUX
#define EALLOC_AUX(...)\
__VA_ARGS__);\
- if (!q) fatalf("allocation failure");\
+ if (!q) r_fatalf("allocation failure");\
return q;\
}
diff --git a/src/bench.c b/src/bench.c
@@ -6,9 +6,9 @@
#include <time.h>
#include <unistd.h>
-#include "cext/bench.h"
-#include "cext/cext.h"
-#include "cext/log.h"
+#include "rcx/bench.h"
+#include "rcx/log.h"
+#include "rcx/rcx.h"
#ifndef _POSIX_THREAD_CPUTIME
#error "Need CLOCK_PROCESS_CPUTIME_ID"
@@ -20,7 +20,7 @@ static u64 run(void (*fn)(u64 N), u64 N);
static u64 requiredN(u64 prevN, u64 prevns, u64 goalns);
static void printnsop(u64 N, u64 ns);
-static bool started; /* Has cext_bench_start been called? */
+static bool started; /* Has r_bench_start been called? */
static bool active; /* Is the timer currently on? */
static struct timespec start;
static u64 accumns;
@@ -31,7 +31,7 @@ run(void (*fn)(u64 N), u64 N) {
accumns = 0;
fn(N);
if (active || !started)
- fatalf("bench misuse");
+ r_fatalf("bench misuse");
return accumns;
}
@@ -64,7 +64,7 @@ printnsop(u64 N, u64 ns) {
}
void
-cext_bench(char *name, void (*fn)(u64 N), u32 goalms) {
+r_bench(char *name, void (*fn)(u64 N), u32 goalms) {
run(fn, 1); /* Warmup */
u64 goalns = (u64)goalms * U64_C(1000000);
u64 N = 1;
@@ -78,22 +78,22 @@ cext_bench(char *name, void (*fn)(u64 N), u32 goalms) {
}
void
-cext_bench_start(void) {
+r_bench_start(void) {
if (active)
return;
active = true;
started = true;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start) < 0)
- fatalf("clock_gettime: %s", strerror(errno));
+ r_fatalf("clock_gettime: %s", strerror(errno));
}
void
-cext_bench_stop(void) {
+r_bench_stop(void) {
if (!active)
return;
struct timespec stop;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &stop) < 0)
- fatalf("clock_gettime: %s", strerror(errno));
+ r_fatalf("clock_gettime: %s", strerror(errno));
active = false;
accumns += (stop.tv_sec - start.tv_sec) * 1000000000ULL
+ (stop.tv_nsec - start.tv_nsec);
diff --git a/src/log.c b/src/log.c
@@ -5,8 +5,8 @@
#include <time.h>
#include <unistd.h>
-#include "cext/cext.h"
-#include "cext/log.h"
+#include "rcx/log.h"
+#include "rcx/rcx.h"
#define ISO8601_SIZE (sizeof "YYYY-MM-DDThh:mm:ssZ")
#define SGR(c) (use_color ? (c) : "")
@@ -20,7 +20,7 @@ static bool log_loc = false;
static int min_level = 0;
void
-cext_log_init(int color, bool log_time_, bool log_loc_, int min_level_) {
+r_log_init(int color, bool log_time_, bool log_loc_, int min_level_) {
if (color > 0) { /* force on */
use_color = true;
} else if (color < 0) { /* force off */
@@ -45,7 +45,7 @@ iso8601(char *buf, time_t t) {
}
void
-cext_log(
+r_log(
char *file, int line,
int level, char *name, char *color,
int code,
diff --git a/src/str.c b/src/str.c
@@ -3,42 +3,42 @@
#include <stdio.h>
#include <string.h>
-#include "cext/alloc.h"
-#include "cext/cext.h"
-#include "cext/log.h"
-#include "cext/str.h"
+#include "rcx/alloc.h"
+#include "rcx/log.h"
+#include "rcx/rcx.h"
+#include "rcx/str.h"
/* TODO: str to int converters */
char *
-cext_str_dup(char *s) {
- char *dup = alloc(strlen(s) + 1);
+r_str_dup(char *s) {
+ char *dup = r_alloc(strlen(s) + 1);
if (!dup) return 0;
strcpy(dup, s);
return dup;
}
char *
-cext_str_edup(char *s) {
- char *dup = cext_str_dup(s);
- if (!dup) fatalf("allocation failure");
+r_str_edup(char *s) {
+ char *dup = r_str_dup(s);
+ if (!dup) r_fatalf("allocation failure");
return dup;
}
bool
-cext_str_starts_with(char *s, char *sub) {
+r_str_starts_with(char *s, char *sub) {
return !strncmp(s, sub, strlen(sub));
}
bool
-cext_str_ends_with(char *s, char *sub) {
+r_str_ends_with(char *s, char *sub) {
usize slen = strlen(s);
usize sublen = strlen(sub);
return slen >= sublen && !strcmp(s + slen - sublen, sub);
}
usize
-cext_str_count(char *s, char *sub) {
+r_str_count(char *s, char *sub) {
usize n = 0;
usize sublen = strlen(sub);
while ((s = strstr(s, sub))) {
@@ -49,12 +49,12 @@ cext_str_count(char *s, char *sub) {
}
usize
-cext_str_split(char ***fields, char *s, char *sep, usize n) {
+r_str_split(char ***fields, char *s, char *sep, usize n) {
assert(strcmp(sep, ""));
if (n == 0) {
- n = cext_str_count(s, sep) + 1;
- if (!(*fields = allocn(n, sizeof **fields)))
+ n = r_str_count(s, sep) + 1;
+ if (!(*fields = r_allocn(n, sizeof **fields)))
return 0;
}
@@ -73,7 +73,7 @@ cext_str_split(char ***fields, char *s, char *sep, usize n) {
}
int
-cext_vaprintf(char **s, const char *fmt, va_list args) {
+r_vaprintf(char **s, const char *fmt, va_list args) {
va_list args2;
va_copy(args2, args);
int len = vsnprintf(0, 0, fmt, args2);
@@ -81,7 +81,7 @@ cext_vaprintf(char **s, const char *fmt, va_list args) {
if (len < 0)
return len;
- char *buf = alloc(len+1);
+ char *buf = r_alloc(len+1);
if (!buf)
return -1;
@@ -95,10 +95,10 @@ cext_vaprintf(char **s, const char *fmt, va_list args) {
}
int
-cext_aprintf(char **s, const char *fmt, ...) {
+r_aprintf(char **s, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
- int ret = cext_vaprintf(s, fmt, args);
+ int ret = r_vaprintf(s, fmt, args);
va_end(args);
return ret;
}
diff --git a/src/unicode.c b/src/unicode.c
@@ -1,4 +1,4 @@
-#include "cext/cext.h"
+#include "rcx/rcx.h"
#include "../gen/ucattab.inc"
static char ucats[] =
@@ -11,7 +11,7 @@ static char ucats[] =
"Cc\0Cf\0Cs\0Co\0Cn";
char *
-cext_unicode_category(rune r) {
+r_unicode_category(rune r) {
if (r <= 0xff) /* Latin 1 */
return &ucats[3 * ucatl1tab[r]];
diff --git a/src/utf8.c b/src/utf8.c
@@ -1,5 +1,5 @@
-#include "cext/cext.h"
-#include "cext/utf8.h"
+#include "rcx/rcx.h"
+#include "rcx/utf8.h"
#define SURROGATE_MIN 0xD800
#define SURROGATE_MAX 0xDFFF
@@ -31,7 +31,7 @@ utf8_len(rune c) {
}
usize
-cext_utf8_encode(char *s, rune c) {
+r_utf8_encode(char *s, rune c) {
usize len = utf8_len(c);
if (!s || len == 0)
return len;
@@ -46,7 +46,7 @@ cext_utf8_encode(char *s, rune c) {
}
usize
-cext_utf8_decode(rune *c, char *s, usize slen) {
+r_utf8_decode(rune *c, char *s, usize slen) {
if (c)
*c = RUNE_BAD;
@@ -55,11 +55,11 @@ cext_utf8_decode(rune *c, char *s, usize slen) {
/* Determine encoded sequence length based on first byte */
usize len = 1;
- for (; len <= CEXT_UTF8_SIZE; len++) {
+ for (; len <= R_UTF8_SIZE; len++) {
if (((uchar)s[0] & utf8mask[len-1]) == utf8byte[len-1])
break;
}
- if (len > CEXT_UTF8_SIZE) /* Invalid leading byte? */
+ if (len > R_UTF8_SIZE) /* Invalid leading byte? */
return 1;
/* Decode codepoint */
diff --git a/tool/ucattab.c b/tool/ucattab.c
@@ -4,10 +4,10 @@
#include <stdlib.h>
#include <string.h>
-#include "cext/alloc.h"
-#include "cext/cext.h"
-#include "cext/log.h"
-#include "cext/str.h"
+#include "rcx/alloc.h"
+#include "rcx/rcx.h"
+#include "rcx/log.h"
+#include "rcx/str.h"
#define NF 15 /* Number of fields in UnicodeData.txt */
@@ -25,28 +25,28 @@ cattoi(char *cat) {
if (!strcmp(cat, &ucats[i]))
return i / 3;
}
- fatalf("bad category '%s'", cat);
+ r_fatalf("bad category '%s'", cat);
return 0; /* Suppress warning */
}
u8 *
parse_cats(char *filename) {
FILE *f = fopen(filename, "rb");
- if (!f) fatalf("fopen: %s", strerror(errno));
+ if (!f) r_fatalf("fopen: %s", strerror(errno));
- u8 *cats = ealloc(RUNE_MAX + 1);
+ u8 *cats = r_ealloc(RUNE_MAX + 1);
char line[512];
bool inrange = false;
i32 prevcp = -1;
while (fgets(line, sizeof line, f)) {
char *nl = strchr(line, '\n');
- if (!nl) fatalf("line too long");
+ if (!nl) r_fatalf("line too long");
*nl = '\0';
char **fields = (char *[NF]){0};
- if (cext_str_split(&fields, line, ";", NF) != NF)
- fatalf("line has too few fields");
+ if (r_str_split(&fields, line, ";", NF) != NF)
+ r_fatalf("line has too few fields");
i32 cp = strtol(fields[0], 0, 16);
char *name = fields[1];
u8 cat = cattoi(!strcmp(fields[2], "") ? "Cn" : fields[2]);
@@ -54,15 +54,15 @@ parse_cats(char *filename) {
/* We expect UnicodeData.txt to be sorted, but I can't find any
* guarantee of that in UAX #44. */
assert(cp > prevcp);
- assert(!inrange || cext_str_ends_with(name, "Last>"));
+ assert(!inrange || r_str_ends_with(name, "Last>"));
for (i32 c = prevcp+1; c <= cp; c++)
cats[c] = inrange || c == cp ? cat : cattoi("Cn");
- inrange = cext_str_ends_with(name, "First>");
+ inrange = r_str_ends_with(name, "First>");
prevcp = cp;
}
- if (!feof(f)) fatalf("fgets: %s", strerror(errno));
+ if (!feof(f)) r_fatalf("fgets: %s", strerror(errno));
for (i32 c = prevcp+1; c <= RUNE_MAX; c++)
cats[c] = cattoi("Cn");
@@ -75,7 +75,7 @@ parse_cats(char *filename) {
int
main(int argc, char **argv) {
if (argc != 2)
- fatalf("usage: %s UNICODE_DATA_FILE", argv[0]);
+ r_fatalf("usage: %s UNICODE_DATA_FILE", argv[0]);
u8 *cats = parse_cats(argv[1]);