rcx

library of miscellaneous bits of C code
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

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:
MMakefile | 30+++++++++++++++---------------
Dinc/cext/all.h | 10----------
Dinc/cext/alloc.h | 28----------------------------
Dinc/cext/bench.h | 25-------------------------
Dinc/cext/cext.h | 14--------------
Dinc/cext/def.h | 164-------------------------------------------------------------------------------
Dinc/cext/deque.h | 124-------------------------------------------------------------------------------
Dinc/cext/internal/util.h | 12------------
Dinc/cext/log.h | 31-------------------------------
Dinc/cext/str.h | 28----------------------------
Dinc/cext/unicode.h | 5-----
Dinc/cext/utf8.h | 21---------------------
Dinc/cext/vector.h | 118-------------------------------------------------------------------------------
Ainc/rcx/all.h | 10++++++++++
Ainc/rcx/alloc.h | 30++++++++++++++++++++++++++++++
Ainc/rcx/bench.h | 25+++++++++++++++++++++++++
Ainc/rcx/def.h | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainc/rcx/deque.h | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainc/rcx/internal/util.h | 12++++++++++++
Ainc/rcx/log.h | 31+++++++++++++++++++++++++++++++
Rinc/cext/opt.h -> inc/rcx/opt.h | 0
Ainc/rcx/rcx.h | 14++++++++++++++
Ainc/rcx/str.h | 28++++++++++++++++++++++++++++
Ainc/rcx/unicode.h | 5+++++
Ainc/rcx/utf8.h | 21+++++++++++++++++++++
Ainc/rcx/vector.h | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/alloc.c | 47++++++++++++++++++++++++++---------------------
Msrc/bench.c | 20++++++++++----------
Msrc/log.c | 8++++----
Msrc/str.c | 38+++++++++++++++++++-------------------
Msrc/unicode.c | 4++--
Msrc/utf8.c | 12++++++------
Mtool/ucattab.c | 28++++++++++++++--------------
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]);