Initial commit
This commit is contained in:
commit
64c21ca43a
2
Makefile
Normal file
2
Makefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
all:
|
||||||
|
$(CC) -I./ -O2 -std=c11 -o nua main.c parse.c vm.c lexer.c -lm
|
||||||
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Impotent
|
||||||
|
|
||||||
|
This is an attempt to create a Lua virtual machine capable of true multithreading. Once the nctref compiler matures enough, I intend to plug it into Impotent as a JIT.
|
||||||
|
|
||||||
|
Impotent is still work-in-progress:
|
||||||
|
|
||||||
|
1. Integers are 32-bit only
|
||||||
|
2. No error handling, meaning any mistake will either crash the VM or make it silently fail
|
||||||
|
3. No standard library other than `print`
|
||||||
|
4. Tables cannot be resized
|
||||||
|
5. Most operators are missing
|
||||||
|
6. Integers and floats are always separate table keys (unlike real Lua where e.g. `5` and `5.0` are considered identical)
|
||||||
|
7. The GC isn't real and everything leaks
|
||||||
60
dump.h
Normal file
60
dump.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdarg.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
|
||||||
|
__attribute__((format(printf, 1, 2))) static inline char *malp(const char *fmt, ...) {
|
||||||
|
va_list v1, v2;
|
||||||
|
va_start(v1, fmt);
|
||||||
|
va_copy(v2, v1);
|
||||||
|
size_t len = vsnprintf(NULL, 0, fmt, v1);
|
||||||
|
va_end(v1);
|
||||||
|
va_start(v2, fmt);
|
||||||
|
char *str = malloc(len + 1);
|
||||||
|
vsnprintf(str, len + 1, fmt, v2);
|
||||||
|
str[len] = 0;
|
||||||
|
va_end(v2);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *LINST_NAMES[] = {
|
||||||
|
[L_GETGLOBAL] = "getglobal",
|
||||||
|
[L_SETGLOBAL] = "setglobal",
|
||||||
|
[L_SETINT16] = "setint16",
|
||||||
|
[L_SETINT32] = "setint32",
|
||||||
|
[L_SETFLOAT] = "setfloat",
|
||||||
|
[L_SETSTR] = "setstr",
|
||||||
|
[L_SETTABLE] = "settable",
|
||||||
|
[L_SETBOOL] = "setbool",
|
||||||
|
[L_SETNIL] = "setnil",
|
||||||
|
[L_SETFUNC] = "setfunc",
|
||||||
|
[L_ADD] = "add",
|
||||||
|
[L_SUB] = "sub",
|
||||||
|
[L_MUL] = "mul",
|
||||||
|
[L_DIV] = "div",
|
||||||
|
[L_IDIV] = "idiv",
|
||||||
|
[L_MOD] = "mod",
|
||||||
|
[L_RET] = "ret",
|
||||||
|
[L_JNOTCOND] = "jnotcond",
|
||||||
|
[L_MOVE] = "move",
|
||||||
|
[L_CALL] = "call",
|
||||||
|
[L_JUMP] = "jump",
|
||||||
|
[L_ADVANCETEST] = "advancetest",
|
||||||
|
[L_COND_EQ] = "cond_eq",
|
||||||
|
[L_COND_NEQ] = "cond_neq",
|
||||||
|
[L_SETFIELD] = "setfield",
|
||||||
|
[L_GETFIELD] = "getfield",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dump(LInst* i) {
|
||||||
|
while(1) {
|
||||||
|
printf("%s %02X %02X %02X\n", LINST_NAMES[i->opcode], i->a, i->b, i->c);
|
||||||
|
if(i->opcode == L_RET) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
150
lexer.c
Normal file
150
lexer.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#define i_implement
|
||||||
|
#include"lexer.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *txt;
|
||||||
|
TokenType type;
|
||||||
|
} KW;
|
||||||
|
|
||||||
|
static KW KWS[] = {
|
||||||
|
{"return", TOK_RETURN},
|
||||||
|
{"if", TOK_IF},
|
||||||
|
{"break", TOK_BREAK},
|
||||||
|
{"goto", TOK_GOTO},
|
||||||
|
{"end", TOK_END},
|
||||||
|
{"do", TOK_DO},
|
||||||
|
{"for", TOK_FOR},
|
||||||
|
{"repeat", TOK_REPEAT},
|
||||||
|
{"until", TOK_UNTIL},
|
||||||
|
{"local", TOK_LOCAL},
|
||||||
|
{"then", TOK_THEN},
|
||||||
|
{"else", TOK_ELSE},
|
||||||
|
{"elseif", TOK_ELSEIF},
|
||||||
|
{"false", TOK_FALSE},
|
||||||
|
{"true", TOK_TRUE},
|
||||||
|
{"nil", TOK_NIL},
|
||||||
|
{"function", TOK_FUNCTION},
|
||||||
|
{"while", TOK_WHILE},
|
||||||
|
};
|
||||||
|
|
||||||
|
static TokenType is_kw(const char *str, size_t len) {
|
||||||
|
for(int i = 0; i < sizeof(KWS) / sizeof(*KWS); i++) {
|
||||||
|
if(len == strlen(KWS[i].txt) && !strncmp(KWS[i].txt, str, len)) {
|
||||||
|
return KWS[i].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TOK_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_Token ltokenize(const char *buf, size_t len) {
|
||||||
|
vec_Token tokens = {};
|
||||||
|
|
||||||
|
size_t row = 1;
|
||||||
|
|
||||||
|
while(len) {
|
||||||
|
if(isspace(buf[0])) {
|
||||||
|
if(buf[0] == '\n') {
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
buf++, len--;
|
||||||
|
} else if(isalpha(buf[0])) {
|
||||||
|
size_t idlen = 0;
|
||||||
|
while(idlen < len && isalnum(buf[idlen])) {
|
||||||
|
idlen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType tt = is_kw(buf, idlen);
|
||||||
|
vec_Token_push(&tokens, (Token) {.text = tt == TOK_NAME ? strndup(buf, idlen) : NULL, .type = tt});
|
||||||
|
|
||||||
|
buf += idlen, len -= idlen;
|
||||||
|
} else if(buf[0] == '+') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_PLUS});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '=') {
|
||||||
|
if(len > 1 && buf[1] == '=') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_DOUBLE_EQUAL});
|
||||||
|
buf++, len--;
|
||||||
|
buf++, len--;
|
||||||
|
} else {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_EQUAL});
|
||||||
|
buf++, len--;
|
||||||
|
}
|
||||||
|
} else if(buf[0] == '(') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_PAREN_L});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == ')') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_PAREN_R});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '[') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_SQUAREN_L});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == ']') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_SQUAREN_R});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '.') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_DOT});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == ',') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_COMMA});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '%') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_PERCENT});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '{') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_SQUIGGLY_L});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(buf[0] == '}') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_SQUIGGLY_R});
|
||||||
|
buf++, len--;
|
||||||
|
} else if(len > 1 && buf[0] == '~' && buf[1] == '=') {
|
||||||
|
vec_Token_push(&tokens, (Token) {.type = TOK_NOT_EQUAL});
|
||||||
|
buf++, len--;
|
||||||
|
buf++, len--;
|
||||||
|
} else if(isdigit(buf[0]) || (len > 1 && buf[0] == '-' && isdigit(buf[1]))) {
|
||||||
|
size_t idlen = 0;
|
||||||
|
|
||||||
|
if(buf[0] == '-') {
|
||||||
|
idlen++;
|
||||||
|
}
|
||||||
|
while(idlen < len && isdigit(buf[idlen])) {
|
||||||
|
idlen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_Token_push(&tokens, (Token) {.text = strndup(buf, idlen), .type = TOK_NUMBER});
|
||||||
|
|
||||||
|
buf += idlen, len -= idlen;
|
||||||
|
} else if(buf[0] == '\'' || buf[0] == '\"') {
|
||||||
|
bool single = buf[0] == '\'';
|
||||||
|
|
||||||
|
buf++, len--;
|
||||||
|
|
||||||
|
size_t strlen = 1;
|
||||||
|
|
||||||
|
while(strlen < len) {
|
||||||
|
if(buf[strlen] == '\\') {
|
||||||
|
strlen += 2;
|
||||||
|
continue;
|
||||||
|
} else if(buf[strlen] == (single ? '\'' : '\"')) {
|
||||||
|
strlen++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
strlen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *str = strndup(buf, strlen - 1);
|
||||||
|
|
||||||
|
// TODO: unescaping
|
||||||
|
|
||||||
|
vec_Token_push(&tokens, (Token) {.text = str, .type = TOK_STRING});
|
||||||
|
|
||||||
|
buf += strlen, len -= strlen;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
86
lexer.h
Normal file
86
lexer.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
typedef enum TokenType {
|
||||||
|
TOK_NONE,
|
||||||
|
TOK_INVALID,
|
||||||
|
TOK_SEMICOLON,
|
||||||
|
TOK_EQUAL,
|
||||||
|
TOK_BREAK,
|
||||||
|
TOK_GOTO,
|
||||||
|
TOK_DO,
|
||||||
|
TOK_END,
|
||||||
|
TOK_WHILE,
|
||||||
|
TOK_REPEAT,
|
||||||
|
TOK_UNTIL,
|
||||||
|
TOK_IF,
|
||||||
|
TOK_THEN,
|
||||||
|
TOK_ELSEIF,
|
||||||
|
TOK_ELSE,
|
||||||
|
TOK_FOR,
|
||||||
|
TOK_COMMA,
|
||||||
|
TOK_IN,
|
||||||
|
TOK_FUNCTION,
|
||||||
|
TOK_LOCAL,
|
||||||
|
TOK_DOUBLE_COLON,
|
||||||
|
TOK_DOT,
|
||||||
|
TOK_COLON,
|
||||||
|
TOK_SQUAREN_L,
|
||||||
|
TOK_SQUAREN_R,
|
||||||
|
TOK_NIL,
|
||||||
|
TOK_TRUE,
|
||||||
|
TOK_FALSE,
|
||||||
|
TOK_ELLIPSIS,
|
||||||
|
TOK_PAREN_L,
|
||||||
|
TOK_PAREN_R,
|
||||||
|
TOK_SQUIGGLY_L,
|
||||||
|
TOK_SQUIGGLY_R,
|
||||||
|
TOK_PLUS,
|
||||||
|
TOK_MINUS,
|
||||||
|
TOK_MUL,
|
||||||
|
TOK_DIV,
|
||||||
|
TOK_IDIV,
|
||||||
|
TOK_CARET,
|
||||||
|
TOK_PERCENT,
|
||||||
|
TOK_AMPERSAND,
|
||||||
|
TOK_TILDE,
|
||||||
|
TOK_BAR,
|
||||||
|
TOK_DOUBLE_RIGHT,
|
||||||
|
TOK_DOUBLE_LEFT,
|
||||||
|
TOK_DOUBLE_DOT,
|
||||||
|
TOK_LEFT,
|
||||||
|
TOK_RIGHT,
|
||||||
|
TOK_LEFT_EQUAL,
|
||||||
|
TOK_RIGHT_EQUAL,
|
||||||
|
TOK_DOUBLE_EQUAL,
|
||||||
|
TOK_NOT_EQUAL,
|
||||||
|
TOK_AND,
|
||||||
|
TOK_OR,
|
||||||
|
TOK_NOT,
|
||||||
|
TOK_SHARP,
|
||||||
|
TOK_NAME,
|
||||||
|
TOK_RETURN,
|
||||||
|
TOK_NUMBER,
|
||||||
|
TOK_STRING,
|
||||||
|
TOK_EOF,
|
||||||
|
} TokenType;
|
||||||
|
|
||||||
|
typedef struct Token {
|
||||||
|
TokenType type;
|
||||||
|
int row;
|
||||||
|
char *text;
|
||||||
|
} Token;
|
||||||
|
|
||||||
|
#define i_header
|
||||||
|
#define T vec_Token, Token
|
||||||
|
#include"stc/vec.h"
|
||||||
|
#undef i_header
|
||||||
|
|
||||||
|
vec_Token ltokenize(const char *buf, size_t len);
|
||||||
44
main.c
Normal file
44
main.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include"value.h"
|
||||||
|
#include"table.h"
|
||||||
|
#include"parse.h"
|
||||||
|
#include"vm.h"
|
||||||
|
#include"lexer.h"
|
||||||
|
#include"str.h"
|
||||||
|
#include"dump.h"
|
||||||
|
|
||||||
|
static size_t native_print(LVM *lvm, void *ud, size_t argn, LValue *values) {
|
||||||
|
if(lvalue_tag(values[0]) == LTAG_STRING) {
|
||||||
|
LString *lstr = (void*) (values[0].u & ~LTAG_MASK);
|
||||||
|
printf("%.*s\n", (int) lstr->length, lstr->data);
|
||||||
|
} else if(lvalue_tag(values[0]) == LTAG_I32) {
|
||||||
|
printf("%i\n", lvalue_to_int32(values[0]));
|
||||||
|
} else if(values[0].u == LTAG_NIL) {
|
||||||
|
printf("nil\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
LTable *env = ltable_new(128);
|
||||||
|
|
||||||
|
LString *key = lstring_newz("print");
|
||||||
|
LFunc *func = lvm_func_from_native(native_print, NULL);
|
||||||
|
|
||||||
|
ltable_set(env, lvalue_from_string(key), lvalue_from_func(func));
|
||||||
|
|
||||||
|
const char *bufs = "for i = 1, 1000000 do print(i) if i % 3 == 0 then print(\"Fizz\") end if i % 5 == 0 then print(\"Buzz\") end end";
|
||||||
|
//const char *bufs = "local t = {a = 9} print(t.a)";
|
||||||
|
//const char *bufs = "z = 5 print(z)";
|
||||||
|
//const char *bufs = "local i = 0 while i ~= 1500000 do print(i) i = i + 1 end";
|
||||||
|
vec_Token toks = ltokenize(bufs, strlen(bufs));
|
||||||
|
|
||||||
|
LUnit *unit = lparse(toks.size, toks.data, env);
|
||||||
|
|
||||||
|
dump(unit->funcs[0].lua_instrs);
|
||||||
|
|
||||||
|
LValue regs[256];
|
||||||
|
lvm_reset_regs(regs);
|
||||||
|
|
||||||
|
LVM lvm = {};
|
||||||
|
lvm_run(&lvm, &unit->funcs[0], 0, regs);
|
||||||
|
}
|
||||||
15
parse.h
Normal file
15
parse.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include"lexer.h"
|
||||||
|
|
||||||
|
struct LUnit;
|
||||||
|
struct LTable;
|
||||||
|
|
||||||
|
struct LUnit *lparse(size_t sz, Token *tokens, struct LTable *environment);
|
||||||
11
stc/algorithm.h
Normal file
11
stc/algorithm.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef STC_ALGORITHM_H_INCLUDED
|
||||||
|
#define STC_ALGORITHM_H_INCLUDED
|
||||||
|
|
||||||
|
// IWYU pragma: begin_exports
|
||||||
|
#include "sys/crange.h"
|
||||||
|
#include "sys/filter.h"
|
||||||
|
#include "sys/utility.h"
|
||||||
|
#include "sys/sumtype.h"
|
||||||
|
// IWYU pragma: end_exports
|
||||||
|
|
||||||
|
#endif // STC_ALGORITHM_H_INCLUDED
|
||||||
254
stc/arc.h
Normal file
254
stc/arc.h
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/* arc: atomic reference counted shared_ptr (new implementation)
|
||||||
|
*
|
||||||
|
* The difference between arc and arc2 is that arc only takes up one pointer,
|
||||||
|
* whereas arc2 uses two. arc cannot be constructed from an already allocated pointer,
|
||||||
|
* which arc2 may. To use arc2, specify the `(c_arc2)` option after the key type, e.g.:
|
||||||
|
* #define T MyArc, MyType, (c_arc2 | c_no_atomic)
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#include <stc/cstr.h>
|
||||||
|
|
||||||
|
typedef struct { cstr name, last; } Person;
|
||||||
|
|
||||||
|
Person Person_make(const char* name, const char* last) {
|
||||||
|
return (Person){.name = cstr_from(name), .last = cstr_from(last)};
|
||||||
|
}
|
||||||
|
Person Person_clone(Person p) {
|
||||||
|
p.name = cstr_clone(p.name);
|
||||||
|
p.last = cstr_clone(p.last);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
void Person_drop(Person* p) {
|
||||||
|
printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last));
|
||||||
|
cstr_drop(&p->name);
|
||||||
|
cstr_drop(&p->last);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define T ArcPers, Person, (c_keyclass) // clone, drop, cmp, hash
|
||||||
|
#include <stc/arc.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
ArcPers p = ArcPers_from(Person_make("John", "Smiths"));
|
||||||
|
ArcPers q = ArcPers_clone(p); // share the pointer
|
||||||
|
|
||||||
|
printf("%s %s. uses: %ld\n", cstr_str(&q.get->name), cstr_str(&q.get->last), ArcPers_use_count(q));
|
||||||
|
c_drop(ArcPers, &p, &q);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_ARC_H_INCLUDED
|
||||||
|
#define STC_ARC_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined __GNUC__ || defined __clang__ || defined _MSC_VER || defined i_no_atomic
|
||||||
|
typedef long catomic_long;
|
||||||
|
#else // try with C11
|
||||||
|
typedef _Atomic(long) catomic_long;
|
||||||
|
#endif
|
||||||
|
#if defined _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#define c_atomic_inc(v) (void)_InterlockedIncrement(v)
|
||||||
|
#define c_atomic_dec_and_test(v) !_InterlockedDecrement(v)
|
||||||
|
#elif defined __GNUC__ || defined __clang__
|
||||||
|
#define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST)
|
||||||
|
#define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST)
|
||||||
|
#else // try with C11
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#define c_atomic_inc(v) (void)atomic_fetch_add(v, 1)
|
||||||
|
#define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1)
|
||||||
|
#endif
|
||||||
|
#endif // STC_ARC_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix arc_
|
||||||
|
#endif
|
||||||
|
#define _i_is_arc
|
||||||
|
#include "priv/template.h"
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
#if c_OPTION(c_no_atomic)
|
||||||
|
#define i_no_atomic
|
||||||
|
#endif
|
||||||
|
#if !defined i_no_atomic
|
||||||
|
#define _i_atomic_inc(v) c_atomic_inc(v)
|
||||||
|
#define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v)
|
||||||
|
#else
|
||||||
|
#define _i_atomic_inc(v) (void)(++*(v))
|
||||||
|
#define _i_atomic_dec_and_test(v) !(--*(v))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if c_OPTION(c_arc2)
|
||||||
|
#define i_arc2
|
||||||
|
#endif
|
||||||
|
#if !(defined i_arc2 || defined STC_USE_ARC2)
|
||||||
|
// ------------ Arc1 size of one pointer (union) -------------
|
||||||
|
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(declare_arc, Self, i_key);
|
||||||
|
#endif
|
||||||
|
struct _c_MEMB(_ctrl) {
|
||||||
|
_m_value value;
|
||||||
|
catomic_long counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
// c++: std::make_shared<_m_value>(val)
|
||||||
|
STC_INLINE Self _c_MEMB(_make)(_m_value val) {
|
||||||
|
Self arc = {.ctrl=_i_new_n(_c_MEMB(_ctrl), 1)};
|
||||||
|
arc.ctrl->value = val;
|
||||||
|
arc.ctrl->counter = 1;
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_toarc)(_m_value* arc_raw)
|
||||||
|
{ Self arc = {.ctrl=(_c_MEMB(_ctrl) *)arc_raw}; return arc; }
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
STC_INLINE void _c_MEMB(_drop)(const Self* self) {
|
||||||
|
if (self->ctrl && _i_atomic_dec_and_test(&self->ctrl->counter)) {
|
||||||
|
i_keydrop(self->get);
|
||||||
|
i_free(self->ctrl, c_sizeof *self->ctrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // ------------ Arc2 size of two pointers -------------
|
||||||
|
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(declare_arc2, Self, i_key);
|
||||||
|
#endif
|
||||||
|
struct _c_MEMB(_ctrl) {
|
||||||
|
catomic_long counter; // nb! counter <-> value order is swapped.
|
||||||
|
_m_value value;
|
||||||
|
};
|
||||||
|
#define ctrl ctrl2
|
||||||
|
|
||||||
|
// c++: std::make_shared<_m_value>(val)
|
||||||
|
STC_INLINE Self _c_MEMB(_make)(_m_value val) {
|
||||||
|
Self out = {.ctrl2=_i_new_n(_c_MEMB(_ctrl), 1)};
|
||||||
|
out.ctrl2->counter = 1;
|
||||||
|
out.get = &out.ctrl2->value;
|
||||||
|
*out.get = val;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* ptr) {
|
||||||
|
Self out = {.get=ptr};
|
||||||
|
if (ptr) {
|
||||||
|
enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)};
|
||||||
|
// Adds 2 dummy bytes to ensure that the second if-test in _drop() is safe.
|
||||||
|
catomic_long* _rc = (catomic_long*)i_malloc(OFFSET + 2);
|
||||||
|
out.ctrl2 = (_c_MEMB(_ctrl)*) _rc;
|
||||||
|
out.ctrl2->counter = 1;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
STC_INLINE void _c_MEMB(_drop)(const Self* self) {
|
||||||
|
if (self->ctrl2 && _i_atomic_dec_and_test(&self->ctrl2->counter)) {
|
||||||
|
enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)};
|
||||||
|
i_keydrop(self->get);
|
||||||
|
|
||||||
|
if ((char*)self->ctrl2 + OFFSET == (char*)self->get) {
|
||||||
|
i_free((void*)self->ctrl2, c_sizeof *self->ctrl2); // _make()
|
||||||
|
} else {
|
||||||
|
i_free((void*)self->ctrl2, OFFSET + 2); // _from_ptr()
|
||||||
|
i_free(self->get, c_sizeof *self->get);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// take ownership of pointer p
|
||||||
|
STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* ptr) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_from_ptr)(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ---------- end Arc2 with two pointers ------------
|
||||||
|
|
||||||
|
STC_INLINE long _c_MEMB(_use_count)(Self arc)
|
||||||
|
{ return arc.ctrl ? arc.ctrl->counter : 0; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ return c_literal(Self){0}; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from)(_m_raw raw)
|
||||||
|
{ return _c_MEMB(_make)(i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self)
|
||||||
|
{ return i_keytoraw(self->get); }
|
||||||
|
|
||||||
|
// move ownership to receiving arc
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self* self) {
|
||||||
|
Self arc = *self;
|
||||||
|
*self = (Self){0};
|
||||||
|
return arc; // now unowned
|
||||||
|
}
|
||||||
|
|
||||||
|
// take ownership of unowned arc
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned; // now owned
|
||||||
|
}
|
||||||
|
|
||||||
|
// make shared ownership with owned arc
|
||||||
|
STC_INLINE void _c_MEMB(_assign)(Self* self, const Self* owned) {
|
||||||
|
if (owned->ctrl)
|
||||||
|
_i_atomic_inc(&owned->ctrl->counter);
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = *owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone by sharing. Does not use i_keyclone, so OK to always define.
|
||||||
|
STC_INLINE Self _c_MEMB(_clone)(Self owned) {
|
||||||
|
if (owned.ctrl)
|
||||||
|
_i_atomic_inc(&owned.ctrl->counter);
|
||||||
|
return owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry)
|
||||||
|
{ return i_cmp(rx, ry); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry)
|
||||||
|
{ return i_eq(rx, ry); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_no_hash && defined _i_has_eq
|
||||||
|
STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx)
|
||||||
|
{ return i_hash(rx); }
|
||||||
|
#endif // i_no_hash
|
||||||
|
|
||||||
|
#undef ctrl
|
||||||
|
#undef i_no_atomic
|
||||||
|
#undef i_arc2
|
||||||
|
#undef _i_atomic_inc
|
||||||
|
#undef _i_atomic_dec_and_test
|
||||||
|
#undef _i_is_arc
|
||||||
|
#include "sys/finalize.h"
|
||||||
168
stc/box.h
Normal file
168
stc/box.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
|
||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* cbox: heap allocated boxed type
|
||||||
|
#include <stc/cstr.h>
|
||||||
|
|
||||||
|
typedef struct { cstr name, email; } Person;
|
||||||
|
|
||||||
|
Person Person_from(const char* name, const char* email) {
|
||||||
|
return (Person){.name = cstr_from(name), .email = cstr_from(email)};
|
||||||
|
}
|
||||||
|
Person Person_clone(Person p) {
|
||||||
|
p.name = cstr_clone(p.name);
|
||||||
|
p.email = cstr_clone(p.email);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
void Person_drop(Person* p) {
|
||||||
|
printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->email));
|
||||||
|
c_drop(cstr, &p->name, &p->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define T PBox, Person, (c_keyclass) // bind Person clone+drop fn's
|
||||||
|
#include <stc/box.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
PBox p = PBox_from(Person_from("John Smiths", "josmiths@gmail.com"));
|
||||||
|
PBox q = PBox_clone(p);
|
||||||
|
cstr_assign(&q.get->name, "Joe Smiths");
|
||||||
|
|
||||||
|
printf("%s %s.\n", cstr_str(&p.get->name), cstr_str(&p.get->email));
|
||||||
|
printf("%s %s.\n", cstr_str(&q.get->name), cstr_str(&q.get->email));
|
||||||
|
|
||||||
|
c_drop(PBox, &p, &q);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_BOX_H_INCLUDED
|
||||||
|
#define STC_BOX_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define cbox_null {0}
|
||||||
|
#endif // STC_BOX_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix box_
|
||||||
|
#endif
|
||||||
|
#define _i_is_box
|
||||||
|
#include "priv/template.h"
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(declare_box, Self, i_key);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// constructors (take ownership)
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ return c_literal(Self){0}; }
|
||||||
|
|
||||||
|
STC_INLINE long _c_MEMB(_use_count)(const Self* self)
|
||||||
|
{ return (long)(self->get != NULL); }
|
||||||
|
|
||||||
|
|
||||||
|
// c++: std::make_unique<i_key>(val)
|
||||||
|
STC_INLINE Self _c_MEMB(_make)(_m_value val) {
|
||||||
|
Self box = {_i_new_n(_m_value, 1)};
|
||||||
|
*box.get = val;
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* p)
|
||||||
|
{ return c_literal(Self){p}; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from)(_m_raw raw)
|
||||||
|
{ return _c_MEMB(_make)(i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self)
|
||||||
|
{ return i_keytoraw(self->get); }
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
STC_INLINE void _c_MEMB(_drop)(const Self* self) {
|
||||||
|
if (self->get) {
|
||||||
|
i_keydrop(self->get);
|
||||||
|
i_free(self->get, c_sizeof *self->get);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move ownership to receiving box
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self* self) {
|
||||||
|
Self box = *self;
|
||||||
|
self->get = NULL;
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release owned pointer, must be manually freed by receiver
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_release)(Self* self)
|
||||||
|
{ return _c_MEMB(_move)(self).get; }
|
||||||
|
|
||||||
|
// take ownership of pointer p
|
||||||
|
STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* p) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
self->get = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take ownership of unowned box
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transfer ownership from other; set other to NULL
|
||||||
|
STC_INLINE void _c_MEMB(_assign)(Self* self, Self* owned) {
|
||||||
|
if (owned->get == self->get)
|
||||||
|
return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = *owned;
|
||||||
|
owned->get = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_INLINE Self _c_MEMB(_clone)(Self other) {
|
||||||
|
if (other.get == NULL) return other;
|
||||||
|
Self out = {_i_new_n(_m_value, 1)};
|
||||||
|
*out.get = i_keyclone((*other.get));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry)
|
||||||
|
{ return i_cmp(rx, ry); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry)
|
||||||
|
{ return i_eq(rx, ry); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_no_hash && defined _i_has_eq
|
||||||
|
STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx)
|
||||||
|
{ return i_hash(rx); }
|
||||||
|
#endif // i_no_hash
|
||||||
|
#undef _i_is_box
|
||||||
|
#include "sys/finalize.h"
|
||||||
336
stc/cbits.h
Normal file
336
stc/cbits.h
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Similar to boost::dynamic_bitset / std::bitset
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "cbits.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
cbits bset = cbits_with_size(23, true);
|
||||||
|
cbits_reset(&bset, 9);
|
||||||
|
cbits_resize(&bset, 43, false);
|
||||||
|
|
||||||
|
printf("%4d: ", (int)cbits_size(&bset));
|
||||||
|
for (c_range(i, cbits_size(&bset)))
|
||||||
|
printf("%d", cbits_at(&bset, i));
|
||||||
|
puts("");
|
||||||
|
cbits_set(&bset, 28);
|
||||||
|
cbits_resize(&bset, 77, true);
|
||||||
|
cbits_resize(&bset, 93, false);
|
||||||
|
cbits_resize(&bset, 102, true);
|
||||||
|
cbits_set_value(&bset, 99, false);
|
||||||
|
|
||||||
|
printf("%4d: ", (int)cbits_size(&bset));
|
||||||
|
for (c_range(i, cbits_size(&bset)))
|
||||||
|
printf("%d", cbits_at(&bset, i));
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
cbits_drop(&bset);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#ifndef STC_CBITS_H_INCLUDED
|
||||||
|
#define STC_CBITS_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if INTPTR_MAX == INT64_MAX
|
||||||
|
#define _gnu_popc(x) __builtin_popcountll(x)
|
||||||
|
#define _msc_popc(x) (int)__popcnt64(x)
|
||||||
|
#else
|
||||||
|
#define _gnu_popc(x) __builtin_popcount(x)
|
||||||
|
#define _msc_popc(x) (int)__popcnt(x)
|
||||||
|
#endif
|
||||||
|
#define _cbits_WS c_sizeof(uintptr_t)
|
||||||
|
#define _cbits_WB (8*_cbits_WS)
|
||||||
|
#define _cbits_bit(i) ((uintptr_t)1 << ((i) & (_cbits_WB - 1)))
|
||||||
|
#define _cbits_words(n) (isize)(((n) + (_cbits_WB - 1))/_cbits_WB)
|
||||||
|
#define _cbits_bytes(n) (_cbits_words(n)*_cbits_WS)
|
||||||
|
|
||||||
|
#if defined _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
STC_INLINE int c_popcount(uintptr_t x) { return _msc_popc(x); }
|
||||||
|
#elif defined __GNUC__ || defined __clang__
|
||||||
|
STC_INLINE int c_popcount(uintptr_t x) { return _gnu_popc(x); }
|
||||||
|
#else
|
||||||
|
STC_INLINE int c_popcount(uintptr_t x) { /* http://en.wikipedia.org/wiki/Hamming_weight */
|
||||||
|
x -= (x >> 1) & (uintptr_t)0x5555555555555555;
|
||||||
|
x = (x & (uintptr_t)0x3333333333333333) + ((x >> 2) & (uintptr_t)0x3333333333333333);
|
||||||
|
x = (x + (x >> 4)) & (uintptr_t)0x0f0f0f0f0f0f0f0f;
|
||||||
|
return (int)((x*(uintptr_t)0x0101010101010101) >> (_cbits_WB - 8));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined __GNUC__ && !defined __clang__ && !defined __cplusplus
|
||||||
|
#pragma GCC diagnostic ignored "-Walloc-size-larger-than=" // gcc 11.4
|
||||||
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // gcc 11.4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cbits_print(...) c_MACRO_OVERLOAD(cbits_print, __VA_ARGS__)
|
||||||
|
#define cbits_print_1(self) cbits_print_4(self, stdout, 0, -1)
|
||||||
|
#define cbits_print_2(self, stream) cbits_print_4(self, stream, 0, -1)
|
||||||
|
#define cbits_print_4(self, stream, start, end) cbits_print_5(cbits, self, stream, start, end)
|
||||||
|
#define cbits_print_3(SetType, self, stream) cbits_print_5(SetType, self, stream, 0, -1)
|
||||||
|
#define cbits_print_5(SetType, self, stream, start, end) do { \
|
||||||
|
const SetType* _cb_set = self; \
|
||||||
|
isize _cb_start = start, _cb_end = end; \
|
||||||
|
if (_cb_end == -1) _cb_end = SetType##_size(_cb_set); \
|
||||||
|
for (c_range_3(_cb_i, _cb_start, _cb_end)) \
|
||||||
|
fputc(SetType##_test(_cb_set, _cb_i) ? '1' : '0', stream); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
STC_INLINE isize _cbits_count(const uintptr_t* set, const isize sz) {
|
||||||
|
const isize n = sz/_cbits_WB;
|
||||||
|
isize count = 0;
|
||||||
|
for (isize i = 0; i < n; ++i)
|
||||||
|
count += c_popcount(set[i]);
|
||||||
|
if (sz & (_cbits_WB - 1))
|
||||||
|
count += c_popcount(set[n] & (_cbits_bit(sz) - 1));
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE char* _cbits_to_str(const uintptr_t* set, const isize sz,
|
||||||
|
char* out, isize start, isize stop) {
|
||||||
|
if (stop > sz) stop = sz;
|
||||||
|
c_assert(start <= stop);
|
||||||
|
|
||||||
|
c_memset(out, '0', stop - start);
|
||||||
|
for (isize i = start; i < stop; ++i)
|
||||||
|
if ((set[i/_cbits_WB] & _cbits_bit(i)) != 0)
|
||||||
|
out[i - start] = '1';
|
||||||
|
out[stop - start] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _cbits_OPR(OPR, VAL) \
|
||||||
|
const isize n = sz/_cbits_WB; \
|
||||||
|
for (isize i = 0; i < n; ++i) \
|
||||||
|
if ((set[i] OPR other[i]) != VAL) \
|
||||||
|
return false; \
|
||||||
|
if ((sz & (_cbits_WB - 1)) == 0) \
|
||||||
|
return true; \
|
||||||
|
const uintptr_t i = (uintptr_t)n, m = _cbits_bit(sz) - 1; \
|
||||||
|
return ((set[i] OPR other[i]) & m) == (VAL & m)
|
||||||
|
|
||||||
|
STC_INLINE bool _cbits_subset_of(const uintptr_t* set, const uintptr_t* other, const isize sz)
|
||||||
|
{ _cbits_OPR(|, set[i]); }
|
||||||
|
|
||||||
|
STC_INLINE bool _cbits_disjoint(const uintptr_t* set, const uintptr_t* other, const isize sz)
|
||||||
|
{ _cbits_OPR(&, 0); }
|
||||||
|
|
||||||
|
#endif // STC_CBITS_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined T && !defined i_type
|
||||||
|
#define i_type T
|
||||||
|
#endif
|
||||||
|
#if defined i_type
|
||||||
|
#define Self c_GETARG(1, i_type)
|
||||||
|
#define _i_length c_GETARG(2, i_type)
|
||||||
|
#else
|
||||||
|
#define Self cbits
|
||||||
|
#endif
|
||||||
|
#ifndef i_allocator
|
||||||
|
#define i_allocator c
|
||||||
|
#endif
|
||||||
|
#define _i_MEMB(name) c_JOIN(Self, name)
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined _i_length // DYNAMIC SIZE BITARRAY
|
||||||
|
|
||||||
|
typedef struct { uintptr_t *buffer; isize _size; } Self;
|
||||||
|
#define _i_assert(x) c_assert(x)
|
||||||
|
|
||||||
|
STC_INLINE void cbits_drop(cbits* self) { i_free(self->buffer, _cbits_bytes(self->_size)); }
|
||||||
|
STC_INLINE isize cbits_size(const cbits* self) { return self->_size; }
|
||||||
|
|
||||||
|
STC_INLINE cbits* cbits_take(cbits* self, cbits other) {
|
||||||
|
if (self->buffer != other.buffer) {
|
||||||
|
cbits_drop(self);
|
||||||
|
*self = other;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cbits cbits_clone(cbits other) {
|
||||||
|
cbits set = other;
|
||||||
|
const isize bytes = _cbits_bytes(other._size);
|
||||||
|
set.buffer = (uintptr_t *)c_safe_memcpy(i_malloc(bytes), other.buffer, bytes);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cbits* cbits_copy(cbits* self, const cbits* other) {
|
||||||
|
if (self->buffer == other->buffer)
|
||||||
|
return self;
|
||||||
|
if (self->_size != other->_size)
|
||||||
|
return cbits_take(self, cbits_clone(*other));
|
||||||
|
c_memcpy(self->buffer, other->buffer, _cbits_bytes(other->_size));
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cbits_resize(cbits* self, const isize size, const bool value) {
|
||||||
|
const isize new_w = _cbits_words(size), osize = self->_size, old_w = _cbits_words(osize);
|
||||||
|
uintptr_t* b = (uintptr_t *)i_realloc(self->buffer, old_w*_cbits_WS, new_w*_cbits_WS);
|
||||||
|
if (b == NULL) return false;
|
||||||
|
self->buffer = b; self->_size = size;
|
||||||
|
if (size > osize) {
|
||||||
|
c_memset(self->buffer + old_w, -(int)value, (new_w - old_w)*_cbits_WS);
|
||||||
|
if (osize & (_cbits_WB - 1)) {
|
||||||
|
uintptr_t mask = _cbits_bit(osize) - 1;
|
||||||
|
if (value) self->buffer[old_w - 1] |= ~mask;
|
||||||
|
else self->buffer[old_w - 1] &= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void cbits_set_all(cbits *self, const bool value);
|
||||||
|
STC_INLINE void cbits_set_pattern(cbits *self, const uintptr_t pattern);
|
||||||
|
|
||||||
|
STC_INLINE cbits cbits_move(cbits* self) {
|
||||||
|
cbits tmp = *self;
|
||||||
|
self->buffer = NULL, self->_size = 0;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cbits cbits_with_size(const isize size, const bool value) {
|
||||||
|
cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size};
|
||||||
|
cbits_set_all(&set, value);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cbits cbits_with_pattern(const isize size, const uintptr_t pattern) {
|
||||||
|
cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size};
|
||||||
|
cbits_set_pattern(&set, pattern);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // _i_length: FIXED SIZE BITARRAY
|
||||||
|
|
||||||
|
#define _i_assert(x) (void)0
|
||||||
|
|
||||||
|
typedef struct { uintptr_t buffer[(_i_length - 1)/_cbits_WB + 1]; } Self;
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_drop)(Self* self) { (void)self; }
|
||||||
|
STC_INLINE isize _i_MEMB(_size)(const Self* self) { (void)self; return _i_length; }
|
||||||
|
STC_INLINE Self _i_MEMB(_move)(Self* self) { return *self; }
|
||||||
|
STC_INLINE Self* _i_MEMB(_take)(Self* self, Self other) { *self = other; return self; }
|
||||||
|
STC_INLINE Self _i_MEMB(_clone)(Self other) { return other; }
|
||||||
|
STC_INLINE void _i_MEMB(_copy)(Self* self, const Self* other) { *self = *other; }
|
||||||
|
STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value);
|
||||||
|
STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern);
|
||||||
|
|
||||||
|
STC_INLINE Self _i_MEMB(_with_size)(const isize size, const bool value) {
|
||||||
|
c_assert(size <= _i_length);
|
||||||
|
Self set; _i_MEMB(_set_all)(&set, value);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _i_MEMB(_with_pattern)(const isize size, const uintptr_t pattern) {
|
||||||
|
c_assert(size <= _i_length);
|
||||||
|
Self set; _i_MEMB(_set_pattern)(&set, pattern);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
#endif // _i_length
|
||||||
|
|
||||||
|
// COMMON:
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value)
|
||||||
|
{ c_memset(self->buffer, -(int)value, _cbits_bytes(_i_MEMB(_size)(self))); }
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern) {
|
||||||
|
isize n = _cbits_words(_i_MEMB(_size)(self));
|
||||||
|
while (n--) self->buffer[n] = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool _i_MEMB(_test)(const Self* self, const isize i)
|
||||||
|
{ return (self->buffer[i/_cbits_WB] & _cbits_bit(i)) != 0; }
|
||||||
|
|
||||||
|
STC_INLINE bool _i_MEMB(_at)(const Self* self, const isize i)
|
||||||
|
{ c_assert(c_uless(i, _i_MEMB(_size)(self))); return _i_MEMB(_test)(self, i); }
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_set)(Self *self, const isize i)
|
||||||
|
{ self->buffer[i/_cbits_WB] |= _cbits_bit(i); }
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_reset)(Self *self, const isize i)
|
||||||
|
{ self->buffer[i/_cbits_WB] &= ~_cbits_bit(i); }
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_set_value)(Self *self, const isize i, const bool b) {
|
||||||
|
self->buffer[i/_cbits_WB] ^= ((uintptr_t)-(int)b ^ self->buffer[i/_cbits_WB]) & _cbits_bit(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_flip)(Self *self, const isize i)
|
||||||
|
{ self->buffer[i/_cbits_WB] ^= _cbits_bit(i); }
|
||||||
|
|
||||||
|
STC_INLINE void _i_MEMB(_flip_all)(Self *self) {
|
||||||
|
isize n = _cbits_words(_i_MEMB(_size)(self));
|
||||||
|
while (n--) self->buffer[n] ^= ~(uintptr_t)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _i_MEMB(_from)(const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
Self set = _i_MEMB(_with_size)(n, false);
|
||||||
|
while (n--) if (str[n] == '1') _i_MEMB(_set)(&set, n);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intersection */
|
||||||
|
STC_INLINE void _i_MEMB(_intersect)(Self *self, const Self* other) {
|
||||||
|
_i_assert(self->_size == other->_size);
|
||||||
|
isize n = _cbits_words(_i_MEMB(_size)(self));
|
||||||
|
while (n--) self->buffer[n] &= other->buffer[n];
|
||||||
|
}
|
||||||
|
/* Union */
|
||||||
|
STC_INLINE void _i_MEMB(_union)(Self *self, const Self* other) {
|
||||||
|
_i_assert(self->_size == other->_size);
|
||||||
|
isize n = _cbits_words(_i_MEMB(_size)(self));
|
||||||
|
while (n--) self->buffer[n] |= other->buffer[n];
|
||||||
|
}
|
||||||
|
/* Exclusive disjunction */
|
||||||
|
STC_INLINE void _i_MEMB(_xor)(Self *self, const Self* other) {
|
||||||
|
_i_assert(self->_size == other->_size);
|
||||||
|
isize n = _cbits_words(_i_MEMB(_size)(self));
|
||||||
|
while (n--) self->buffer[n] ^= other->buffer[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _i_MEMB(_count)(const Self* self)
|
||||||
|
{ return _cbits_count(self->buffer, _i_MEMB(_size)(self)); }
|
||||||
|
|
||||||
|
STC_INLINE char* _i_MEMB(_to_str)(const Self* self, char* out, isize start, isize stop)
|
||||||
|
{ return _cbits_to_str(self->buffer, _i_MEMB(_size)(self), out, start, stop); }
|
||||||
|
|
||||||
|
STC_INLINE bool _i_MEMB(_subset_of)(const Self* self, const Self* other) {
|
||||||
|
_i_assert(self->_size == other->_size);
|
||||||
|
return _cbits_subset_of(self->buffer, other->buffer, _i_MEMB(_size)(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool _i_MEMB(_disjoint)(const Self* self, const Self* other) {
|
||||||
|
_i_assert(self->_size == other->_size);
|
||||||
|
return _cbits_disjoint(self->buffer, other->buffer, _i_MEMB(_size)(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "priv/linkage2.h"
|
||||||
|
#undef i_type
|
||||||
|
#undef _i_length
|
||||||
|
#undef _i_MEMB
|
||||||
|
#undef _i_assert
|
||||||
|
#undef Self
|
||||||
355
stc/common.h
Normal file
355
stc/common.h
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef STC_COMMON_H_INCLUDED
|
||||||
|
#define STC_COMMON_H_INCLUDED
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable: 4116 4996) // unnamed type definition in parentheses
|
||||||
|
#endif
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
typedef ptrdiff_t isize;
|
||||||
|
#ifndef STC_NO_INT_DEFS
|
||||||
|
typedef int8_t int8;
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef int16_t int16;
|
||||||
|
typedef uint16_t uint16;
|
||||||
|
typedef int32_t int32;
|
||||||
|
typedef uint32_t uint32;
|
||||||
|
typedef int64_t int64;
|
||||||
|
typedef uint64_t uint64;
|
||||||
|
#endif
|
||||||
|
#if !defined STC_HAS_TYPEOF && (_MSC_FULL_VER >= 193933428 || \
|
||||||
|
defined __GNUC__ || defined __clang__ || defined __TINYC__)
|
||||||
|
#define STC_HAS_TYPEOF 1
|
||||||
|
#endif
|
||||||
|
#if defined __GNUC__
|
||||||
|
#define c_GNUATTR(...) __attribute__((__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define c_GNUATTR(...)
|
||||||
|
#endif
|
||||||
|
#define STC_INLINE static inline c_GNUATTR(unused)
|
||||||
|
#define c_ZI PRIiPTR
|
||||||
|
#define c_ZU PRIuPTR
|
||||||
|
#define c_NPOS INTPTR_MAX
|
||||||
|
|
||||||
|
// Macro overloading feature support
|
||||||
|
#define c_MACRO_OVERLOAD(name, ...) \
|
||||||
|
c_JOIN(name ## _,c_NUMARGS(__VA_ARGS__))(__VA_ARGS__)
|
||||||
|
#define c_JOIN0(a, b) a ## b
|
||||||
|
#define c_JOIN(a, b) c_JOIN0(a, b)
|
||||||
|
#define c_NUMARGS(...) _c_APPLY_ARG_N((__VA_ARGS__, _c_RSEQ_N))
|
||||||
|
#define _c_APPLY_ARG_N(args) _c_ARG_N args
|
||||||
|
#define _c_RSEQ_N 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||||
|
#define _c_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,N,...) N
|
||||||
|
|
||||||
|
// Saturated overloading
|
||||||
|
// #define foo(...) foo_I(__VA_ARGS__, c_COMMA_N(foo_3), c_COMMA_N(foo_2), c_COMMA_N(foo_1),)(__VA_ARGS__)
|
||||||
|
// #define foo_I(a,b,c, n, ...) c_TUPLE_AT_1(n, foo_n,)
|
||||||
|
#define c_TUPLE_AT_1(x,y,...) y
|
||||||
|
#define c_COMMA_N(x) ,x
|
||||||
|
#define c_EXPAND(...) __VA_ARGS__
|
||||||
|
|
||||||
|
// Select arg, e.g. for #define i_type A,B then c_GETARG(2, i_type) is B
|
||||||
|
#define c_GETARG(N, ...) c_ARG_##N(__VA_ARGS__,)
|
||||||
|
#define c_ARG_1(a, ...) a
|
||||||
|
#define c_ARG_2(a, b, ...) b
|
||||||
|
#define c_ARG_3(a, b, c, ...) c
|
||||||
|
#define c_ARG_4(a, b, c, d, ...) d
|
||||||
|
|
||||||
|
#define _i_new_n(T, n) ((T*)i_malloc((n)*c_sizeof(T)))
|
||||||
|
#define _i_new_zeros(T, n) ((T*)i_calloc(n, c_sizeof(T)))
|
||||||
|
#define _i_realloc_n(ptr, old_n, n) i_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr))
|
||||||
|
#define _i_free_n(ptr, n) i_free(ptr, (n)*c_sizeof *(ptr))
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#define c_new(T, ...) ((T*)c_safe_memcpy(c_malloc(c_sizeof(T)), ((T[]){__VA_ARGS__}), c_sizeof(T)))
|
||||||
|
#define c_literal(T) (T)
|
||||||
|
#define c_make_array(T, ...) ((T[])__VA_ARGS__)
|
||||||
|
#define c_make_array2d(T, N, ...) ((T[][N])__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#include <new>
|
||||||
|
#define c_new(T, ...) new (c_malloc(c_sizeof(T))) T(__VA_ARGS__)
|
||||||
|
#define c_literal(T) T
|
||||||
|
template<typename T, int M, int N> struct _c_Array { T data[M][N]; };
|
||||||
|
#define c_make_array(T, ...) (_c_Array<T, 1, sizeof((T[])__VA_ARGS__)/sizeof(T)>{{__VA_ARGS__}}.data[0])
|
||||||
|
#define c_make_array2d(T, N, ...) (_c_Array<T, sizeof((T[][N])__VA_ARGS__)/sizeof(T[N]), N>{__VA_ARGS__}.data)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STC_ALLOCATOR
|
||||||
|
#define c_malloc c_JOIN(STC_ALLOCATOR, _malloc)
|
||||||
|
#define c_calloc c_JOIN(STC_ALLOCATOR, _calloc)
|
||||||
|
#define c_realloc c_JOIN(STC_ALLOCATOR, _realloc)
|
||||||
|
#define c_free c_JOIN(STC_ALLOCATOR, _free)
|
||||||
|
#else
|
||||||
|
#define c_malloc(sz) malloc(c_i2u_size(sz))
|
||||||
|
#define c_calloc(n, sz) calloc(c_i2u_size(n), c_i2u_size(sz))
|
||||||
|
#define c_realloc(ptr, old_sz, sz) realloc(ptr, c_i2u_size(1 ? (sz) : (old_sz)))
|
||||||
|
#define c_free(ptr, sz) ((void)(sz), free(ptr))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define c_new_n(T, n) ((T*)c_malloc((n)*c_sizeof(T)))
|
||||||
|
#define c_free_n(ptr, n) c_free(ptr, (n)*c_sizeof *(ptr))
|
||||||
|
#define c_realloc_n(ptr, old_n, n) c_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr))
|
||||||
|
#define c_delete_n(T, ptr, n) do { \
|
||||||
|
T* _tp = ptr; isize _n = n, _i = _n; \
|
||||||
|
while (_i--) T##_drop((_tp + _i)); \
|
||||||
|
c_free(_tp, _n*c_sizeof(T)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define c_static_assert(expr) (void)sizeof(int[(expr) ? 1 : -1])
|
||||||
|
#if defined STC_NDEBUG || defined NDEBUG
|
||||||
|
#define c_assert(expr) (void)sizeof(expr)
|
||||||
|
#else
|
||||||
|
#define c_assert(expr) assert(expr)
|
||||||
|
#endif
|
||||||
|
#define c_container_of(p, C, m) ((C*)((char*)(1 ? (p) : &((C*)0)->m) - offsetof(C, m)))
|
||||||
|
#define c_const_cast(Tp, p) ((Tp)(1 ? (p) : (Tp)0))
|
||||||
|
#define c_litstrlen(literal) (c_sizeof("" literal) - 1)
|
||||||
|
#define c_countof(a) (isize)(sizeof(a)/sizeof 0[a])
|
||||||
|
#define c_arraylen(a) c_countof(a) // [deprecated]?
|
||||||
|
|
||||||
|
// expect signed ints to/from these (use with gcc -Wconversion)
|
||||||
|
#define c_sizeof (isize)sizeof
|
||||||
|
#define c_strlen(s) (isize)strlen(s)
|
||||||
|
#define c_strncmp(a, b, ilen) strncmp(a, b, c_i2u_size(ilen))
|
||||||
|
#define c_memcpy(d, s, ilen) memcpy(d, s, c_i2u_size(ilen))
|
||||||
|
#define c_memmove(d, s, ilen) memmove(d, s, c_i2u_size(ilen))
|
||||||
|
#define c_memset(d, val, ilen) memset(d, val, c_i2u_size(ilen))
|
||||||
|
#define c_memcmp(a, b, ilen) memcmp(a, b, c_i2u_size(ilen))
|
||||||
|
// library internal, but may be useful in user code:
|
||||||
|
#define c_u2i_size(u) (isize)(1 ? (u) : (size_t)1) // warns if u is signed
|
||||||
|
#define c_i2u_size(i) (size_t)(1 ? (i) : -1) // warns if i is unsigned
|
||||||
|
#define c_uless(a, b) ((size_t)(a) < (size_t)(b))
|
||||||
|
#define c_safe_cast(T, From, x) ((T)(1 ? (x) : (From){0}))
|
||||||
|
|
||||||
|
// x, y are i_keyraw* type, which defaults to i_key*. vp is i_key* type.
|
||||||
|
#define c_memcmp_eq(x, y) (memcmp(x, y, sizeof *(x)) == 0)
|
||||||
|
#define c_default_eq(x, y) (*(x) == *(y))
|
||||||
|
#define c_default_less(x, y) (*(x) < *(y))
|
||||||
|
#define c_default_cmp(x, y) (c_default_less(y, x) - c_default_less(x, y))
|
||||||
|
#define c_default_hash(vp) c_hash_n(vp, sizeof *(vp))
|
||||||
|
#define c_default_clone(v) (v)
|
||||||
|
#define c_default_toraw(vp) (*(vp))
|
||||||
|
#define c_default_drop(vp) ((void) (vp))
|
||||||
|
|
||||||
|
// non-owning char pointer
|
||||||
|
typedef const char* cstr_raw;
|
||||||
|
#define cstr_raw_cmp(x, y) strcmp(*(x), *(y))
|
||||||
|
#define cstr_raw_eq(x, y) (cstr_raw_cmp(x, y) == 0)
|
||||||
|
#define cstr_raw_hash(vp) c_hash_str(*(vp))
|
||||||
|
#define cstr_raw_clone(v) (v)
|
||||||
|
#define cstr_raw_drop(vp) ((void)vp)
|
||||||
|
|
||||||
|
// Control block macros
|
||||||
|
|
||||||
|
// [deprecated]:
|
||||||
|
#define c_init(...) c_make(__VA_ARGS__)
|
||||||
|
#define c_forlist(...) for (c_items(_VA_ARGS__))
|
||||||
|
#define c_foritems(...) for (c_items(__VA_ARGS__))
|
||||||
|
#define c_foreach(...) for (c_each(__VA_ARGS__))
|
||||||
|
#define c_foreach_n(...) for (c_each_n(__VA_ARGS__))
|
||||||
|
#define c_foreach_kv(...) for (c_each_kv(__VA_ARGS__))
|
||||||
|
#define c_foreach_reverse(...) for (c_each_reverse(__VA_ARGS__))
|
||||||
|
#define c_forrange(...) for (c_range(__VA_ARGS__))
|
||||||
|
#define c_forrange32(...) for (c_range32(__VA_ARGS__))
|
||||||
|
|
||||||
|
// New:
|
||||||
|
#define c_each(...) c_MACRO_OVERLOAD(c_each, __VA_ARGS__)
|
||||||
|
#define c_each_3(it, C, cnt) \
|
||||||
|
C##_iter it = C##_begin(&cnt); it.ref; C##_next(&it)
|
||||||
|
#define c_each_4(it, C, start, end) \
|
||||||
|
_c_each(it, C, start, (end).ref, _)
|
||||||
|
|
||||||
|
#define c_each_n(...) c_MACRO_OVERLOAD(c_each_n, __VA_ARGS__)
|
||||||
|
#define c_each_n_3(it, C, cnt) c_each_n_4(it, C, cnt, INTPTR_MAX)
|
||||||
|
#define c_each_n_4(it, C, cnt, n) \
|
||||||
|
struct {C##_iter iter; C##_value* ref; isize size, index;} \
|
||||||
|
it = {.iter=C##_begin(&cnt), .size=n}; (it.ref = it.iter.ref) && it.index < it.size; C##_next(&it.iter), ++it.index
|
||||||
|
|
||||||
|
#define c_each_reverse(...) c_MACRO_OVERLOAD(c_each_reverse, __VA_ARGS__)
|
||||||
|
#define c_each_reverse_3(it, C, cnt) /* works for stack, vec, queue, deque */ \
|
||||||
|
C##_iter it = C##_rbegin(&cnt); it.ref; C##_rnext(&it)
|
||||||
|
#define c_each_reverse_4(it, C, start, end) \
|
||||||
|
_c_each(it, C, start, (end).ref, _r)
|
||||||
|
|
||||||
|
#define _c_each(it, C, start, endref, rev) /* private */ \
|
||||||
|
C##_iter it = (start), *_endref_##it = c_safe_cast(C##_iter*, C##_value*, endref) \
|
||||||
|
; it.ref != (C##_value*)_endref_##it; C##rev##next(&it)
|
||||||
|
|
||||||
|
#define c_each_kv(...) c_MACRO_OVERLOAD(c_each_kv, __VA_ARGS__)
|
||||||
|
#define c_each_kv_4(key, val, C, cnt) /* structured binding for maps */ \
|
||||||
|
_c_each_kv(key, val, C, C##_begin(&cnt), NULL)
|
||||||
|
#define c_each_kv_5(key, val, C, start, end) \
|
||||||
|
_c_each_kv(key, val, C, start, (end).ref)
|
||||||
|
|
||||||
|
#define _c_each_kv(key, val, C, start, endref) /* private */ \
|
||||||
|
const C##_key *key = (const C##_key*)&key; key; ) \
|
||||||
|
for (C##_mapped *val; key; key = NULL) \
|
||||||
|
for (C##_iter _it_##key = start, *_endref_##key = c_safe_cast(C##_iter*, C##_value*, endref); \
|
||||||
|
_it_##key.ref != (C##_value*)_endref_##key && (key = &_it_##key.ref->first, val = &_it_##key.ref->second); \
|
||||||
|
C##_next(&_it_##key)
|
||||||
|
|
||||||
|
#define c_items(it, T, ...) \
|
||||||
|
struct {T* ref; int size, index;} \
|
||||||
|
it = {.ref=c_make_array(T, __VA_ARGS__), .size=(int)(sizeof((T[])__VA_ARGS__)/sizeof(T))} \
|
||||||
|
; it.index < it.size ; ++it.ref, ++it.index
|
||||||
|
|
||||||
|
// c_range, c_range32: python-like int range iteration
|
||||||
|
#define c_range_t(...) c_MACRO_OVERLOAD(c_range_t, __VA_ARGS__)
|
||||||
|
#define c_range_t_3(T, i, stop) c_range_t_4(T, i, 0, stop)
|
||||||
|
#define c_range_t_4(T, i, start, stop) \
|
||||||
|
T i=start, _c_end_##i=stop; i < _c_end_##i; ++i
|
||||||
|
#define c_range_t_5(T, i, start, stop, step) \
|
||||||
|
T i=start, _c_inc_##i=step, _c_end_##i=(stop) - (_c_inc_##i > 0) \
|
||||||
|
; (_c_inc_##i > 0) == (i <= _c_end_##i) ; i += _c_inc_##i
|
||||||
|
|
||||||
|
#define c_range(...) c_MACRO_OVERLOAD(c_range, __VA_ARGS__)
|
||||||
|
#define c_range_1(stop) c_range_t_4(isize, _c_i1, 0, stop)
|
||||||
|
#define c_range_2(i, stop) c_range_t_4(isize, i, 0, stop)
|
||||||
|
#define c_range_3(i, start, stop) c_range_t_4(isize, i, start, stop)
|
||||||
|
#define c_range_4(i, start, stop, step) c_range_t_5(isize, i, start, stop, step)
|
||||||
|
|
||||||
|
#define c_range32(...) c_MACRO_OVERLOAD(c_range32, __VA_ARGS__)
|
||||||
|
#define c_range32_2(i, stop) c_range_t_4(int32_t, i, 0, stop)
|
||||||
|
#define c_range32_3(i, start, stop) c_range_t_4(int32_t, i, start, stop)
|
||||||
|
#define c_range32_4(i, start, stop, step) c_range_t_5(int32_t, i, start, stop, step)
|
||||||
|
|
||||||
|
// make container from a literal list
|
||||||
|
#define c_make(C, ...) \
|
||||||
|
C##_from_n(c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw))
|
||||||
|
|
||||||
|
// put multiple raw-type elements from a literal list into a container
|
||||||
|
#define c_put_items(C, cnt, ...) \
|
||||||
|
C##_put_n(cnt, c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw))
|
||||||
|
|
||||||
|
// drop multiple containers of same type
|
||||||
|
#define c_drop(C, ...) \
|
||||||
|
do { for (c_items(_c_i2, C*, {__VA_ARGS__})) C##_drop(*_c_i2.ref); } while(0)
|
||||||
|
|
||||||
|
// RAII scopes
|
||||||
|
#define c_defer(...) \
|
||||||
|
for (int _c_i3 = 0; _c_i3++ == 0; __VA_ARGS__)
|
||||||
|
|
||||||
|
#define c_with(...) c_MACRO_OVERLOAD(c_with, __VA_ARGS__)
|
||||||
|
#define c_with_2(init, deinit) \
|
||||||
|
for (int _c_i4 = 0; _c_i4 == 0; ) for (init; _c_i4++ == 0; deinit)
|
||||||
|
#define c_with_3(init, condition, deinit) \
|
||||||
|
for (int _c_i5 = 0; _c_i5 == 0; ) for (init; _c_i5++ == 0 && (condition); deinit)
|
||||||
|
|
||||||
|
// General functions
|
||||||
|
|
||||||
|
STC_INLINE void* c_safe_memcpy(void* dst, const void* src, isize size)
|
||||||
|
{ return dst ? memcpy(dst, src, (size_t)size) : NULL; }
|
||||||
|
|
||||||
|
#if INTPTR_MAX == INT64_MAX
|
||||||
|
#define FNV_BASIS 0xcbf29ce484222325
|
||||||
|
#define FNV_PRIME 0x00000100000001b3
|
||||||
|
#else
|
||||||
|
#define FNV_BASIS 0x811c9dc5
|
||||||
|
#define FNV_PRIME 0x01000193
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE size_t c_basehash_n(const void* key, isize len) {
|
||||||
|
const uint8_t* msg = (const uint8_t*)key;
|
||||||
|
size_t h = FNV_BASIS, block = 0;
|
||||||
|
|
||||||
|
while (len >= c_sizeof h) {
|
||||||
|
memcpy(&block, msg, sizeof h);
|
||||||
|
h ^= block;
|
||||||
|
h *= FNV_PRIME;
|
||||||
|
msg += c_sizeof h;
|
||||||
|
len -= c_sizeof h;
|
||||||
|
}
|
||||||
|
while (len--) {
|
||||||
|
h ^= *(msg++);
|
||||||
|
h *= FNV_PRIME;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE size_t c_hash_n(const void* key, isize len) {
|
||||||
|
uint64_t b8; uint32_t b4;
|
||||||
|
switch (len) {
|
||||||
|
case 8: memcpy(&b8, key, 8); return (size_t)(b8 * 0xc6a4a7935bd1e99d);
|
||||||
|
case 4: memcpy(&b4, key, 4); return b4 * FNV_BASIS;
|
||||||
|
default: return c_basehash_n(key, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE size_t c_hash_str(const char *str) {
|
||||||
|
const uint8_t* msg = (const uint8_t*)str;
|
||||||
|
size_t h = FNV_BASIS;
|
||||||
|
while (*msg) {
|
||||||
|
h ^= *(msg++);
|
||||||
|
h *= FNV_PRIME;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define c_hash_mix(...) /* non-commutative hash combine */ \
|
||||||
|
c_hash_mix_n(c_make_array(size_t, {__VA_ARGS__}), c_sizeof((size_t[]){__VA_ARGS__})/c_sizeof(size_t))
|
||||||
|
|
||||||
|
STC_INLINE size_t c_hash_mix_n(size_t h[], isize n) {
|
||||||
|
for (isize i = 1; i < n; ++i) h[0] += h[0] ^ h[i];
|
||||||
|
return h[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic typesafe swap
|
||||||
|
#define c_swap(xp, yp) do { \
|
||||||
|
(void)sizeof((xp) == (yp)); \
|
||||||
|
char _tv[sizeof *(xp)]; \
|
||||||
|
void *_xp = xp, *_yp = yp; \
|
||||||
|
memcpy(_tv, _xp, sizeof _tv); \
|
||||||
|
memcpy(_xp, _yp, sizeof _tv); \
|
||||||
|
memcpy(_yp, _tv, sizeof _tv); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// get next power of two
|
||||||
|
STC_INLINE isize c_next_pow2(isize n) {
|
||||||
|
n--;
|
||||||
|
n |= n >> 1, n |= n >> 2;
|
||||||
|
n |= n >> 4, n |= n >> 8;
|
||||||
|
n |= n >> 16;
|
||||||
|
#if INTPTR_MAX == INT64_MAX
|
||||||
|
n |= n >> 32;
|
||||||
|
#endif
|
||||||
|
return n + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE char* c_strnstrn(const char *str, isize slen, const char *needle, isize nlen) {
|
||||||
|
if (nlen == 0) return (char *)str;
|
||||||
|
if (nlen > slen) return NULL;
|
||||||
|
slen -= nlen;
|
||||||
|
do {
|
||||||
|
if (*str == *needle && !c_memcmp(str, needle, nlen))
|
||||||
|
return (char *)str;
|
||||||
|
++str;
|
||||||
|
} while (slen--);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif // STC_COMMON_H_INCLUDED
|
||||||
180
stc/coption.h
Normal file
180
stc/coption.h
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c
|
||||||
|
Fixed major bugs with optional arguments (both long and short).
|
||||||
|
Added arg->optstr output field, more consistent API.
|
||||||
|
|
||||||
|
coption_get() is similar to GNU's getopt_long(). Each call parses one option and
|
||||||
|
returns the option name. opt->arg points to the option argument if present.
|
||||||
|
The function returns -1 when all command-line arguments are parsed. In this case,
|
||||||
|
opt->ind is the index of the first non-option argument.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/coption.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
coption_long longopts[] = {
|
||||||
|
{"foo", coption_no_argument, 'f'},
|
||||||
|
{"bar", coption_required_argument, 'b'},
|
||||||
|
{"opt", coption_optional_argument, 'o'},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
const char* optstr = "xy:z::123";
|
||||||
|
printf("program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n");
|
||||||
|
int c;
|
||||||
|
coption opt = coption_init();
|
||||||
|
while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case '?': printf("error: unknown option: %s\n", opt.optstr); return 1;
|
||||||
|
case ':': printf("error: missing argument for %s (%c)\n", opt.optstr, opt.opt); return 2;
|
||||||
|
default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\nNon-option arguments:");
|
||||||
|
for (int i = opt.ind; i < argc; ++i)
|
||||||
|
printf(" %s", argv[i]);
|
||||||
|
putchar('\n');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifndef STC_COPTION_H_INCLUDED
|
||||||
|
#define STC_COPTION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
coption_no_argument,
|
||||||
|
coption_required_argument,
|
||||||
|
coption_optional_argument
|
||||||
|
} coption_type;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
coption_type type;
|
||||||
|
int val;
|
||||||
|
} coption_long;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ind; /* equivalent to optind */
|
||||||
|
int opt; /* equivalent to optopt */
|
||||||
|
const char *optstr; /* points to the option string */
|
||||||
|
const char *arg; /* equivalent to optarg */
|
||||||
|
int _i, _pos, _nargs;
|
||||||
|
char _optstr[4];
|
||||||
|
} coption;
|
||||||
|
|
||||||
|
static inline coption coption_init(void) {
|
||||||
|
coption opt = {1, 0, NULL, NULL, 1, 0, 0, {'-', '?', '\0'}};
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move argv[j] over n elements to the left */
|
||||||
|
static void coption_permute_(char *argv[], int j, int n) {
|
||||||
|
int k;
|
||||||
|
char *p = argv[j];
|
||||||
|
for (k = 0; k < n; ++k)
|
||||||
|
argv[j - k] = argv[j - k - 1];
|
||||||
|
argv[j - k] = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @param opt output; must be initialized to coption_init() on first call
|
||||||
|
* @return ASCII val for a short option; longopt.val for a long option;
|
||||||
|
* -1 if argv[] is fully processed; '?' for an unknown option or
|
||||||
|
* an ambiguous long option; ':' if an option argument is missing
|
||||||
|
*/
|
||||||
|
static int coption_get(coption *opt, int argc, char *argv[],
|
||||||
|
const char *shortopts, const coption_long *longopts) {
|
||||||
|
int optc = -1, i0, j, posixly_correct = (shortopts && shortopts[0] == '+');
|
||||||
|
if (!posixly_correct) {
|
||||||
|
while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0'))
|
||||||
|
++opt->_i, ++opt->_nargs;
|
||||||
|
}
|
||||||
|
opt->opt = 0, opt->optstr = NULL, opt->arg = NULL, i0 = opt->_i;
|
||||||
|
if (opt->_i >= argc || argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0') {
|
||||||
|
opt->ind = opt->_i - opt->_nargs;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (argv[opt->_i][0] == '-' && argv[opt->_i][1] == '-') { /* "--" or a long option */
|
||||||
|
if (argv[opt->_i][2] == '\0') { /* a bare "--" */
|
||||||
|
coption_permute_(argv, opt->_i, opt->_nargs);
|
||||||
|
++opt->_i, opt->ind = opt->_i - opt->_nargs;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
optc = '?', opt->_pos = -1;
|
||||||
|
if (longopts) { /* parse long options */
|
||||||
|
int k, n_exact = 0, n_partial = 0;
|
||||||
|
const coption_long *o = 0, *o_exact = 0, *o_partial = 0;
|
||||||
|
for (j = 2; argv[opt->_i][j] != '\0' && argv[opt->_i][j] != '='; ++j) {} /* find the end of the option name */
|
||||||
|
for (k = 0; longopts[k].name != 0; ++k)
|
||||||
|
if (strncmp(&argv[opt->_i][2], longopts[k].name, (size_t)(j - 2)) == 0) {
|
||||||
|
if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k];
|
||||||
|
else ++n_partial, o_partial = &longopts[k];
|
||||||
|
}
|
||||||
|
opt->optstr = argv[opt->_i];
|
||||||
|
if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?';
|
||||||
|
o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0;
|
||||||
|
if (o) {
|
||||||
|
opt->opt = optc = o->val;
|
||||||
|
if (o->type != coption_no_argument) {
|
||||||
|
if (argv[opt->_i][j] == '=')
|
||||||
|
opt->arg = &argv[opt->_i][j + 1];
|
||||||
|
else if (argv[opt->_i][j] == '\0' && opt->_i < argc - 1 && (o->type == coption_required_argument ||
|
||||||
|
argv[opt->_i + 1][0] != '-'))
|
||||||
|
opt->arg = argv[++opt->_i];
|
||||||
|
else if (o->type == coption_required_argument)
|
||||||
|
optc = ':'; /* missing option argument */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (shortopts) { /* a short option */
|
||||||
|
const char *p;
|
||||||
|
if (opt->_pos == 0) opt->_pos = 1;
|
||||||
|
optc = opt->opt = argv[opt->_i][opt->_pos++];
|
||||||
|
opt->_optstr[1] = optc, opt->optstr = opt->_optstr;
|
||||||
|
p = strchr(shortopts, optc);
|
||||||
|
if (p == 0) {
|
||||||
|
optc = '?'; /* unknown option */
|
||||||
|
} else if (p[1] == ':') {
|
||||||
|
if (argv[opt->_i][opt->_pos] != '\0')
|
||||||
|
opt->arg = &argv[opt->_i][opt->_pos];
|
||||||
|
else if (opt->_i < argc - 1 && (p[2] != ':' || argv[opt->_i + 1][0] != '-'))
|
||||||
|
opt->arg = argv[++opt->_i];
|
||||||
|
else if (p[2] != ':')
|
||||||
|
optc = ':';
|
||||||
|
opt->_pos = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->_pos < 0 || argv[opt->_i][opt->_pos] == 0) {
|
||||||
|
++opt->_i, opt->_pos = 0;
|
||||||
|
if (opt->_nargs > 0) /* permute */
|
||||||
|
for (j = i0; j < opt->_i; ++j)
|
||||||
|
coption_permute_(argv, j, opt->_nargs);
|
||||||
|
}
|
||||||
|
opt->ind = opt->_i - opt->_nargs;
|
||||||
|
return optc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_COPTION_H_INCLUDED
|
||||||
563
stc/coroutine.h
Normal file
563
stc/coroutine.h
Normal file
@ -0,0 +1,563 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef STC_COROUTINE_H_INCLUDED
|
||||||
|
#define STC_COROUTINE_H_INCLUDED
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/coroutine.h>
|
||||||
|
|
||||||
|
struct iterpair {
|
||||||
|
cco_base base; // required member
|
||||||
|
int max_x, max_y;
|
||||||
|
int x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
int iterpair(struct iterpair* I) {
|
||||||
|
cco_async (I) {
|
||||||
|
for (I->x = 0; I->x < I->max_x; I->x++)
|
||||||
|
for (I->y = 0; I->y < I->max_y; I->y++)
|
||||||
|
cco_yield; // suspend
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("done");
|
||||||
|
return 0; // CCO_DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct iterpair it = {.max_x=3, .max_y=3};
|
||||||
|
int n = 0;
|
||||||
|
while (iterpair(&it))
|
||||||
|
{
|
||||||
|
printf("%d %d\n", it.x, it.y);
|
||||||
|
// example of early stop:
|
||||||
|
if (++n == 7) cco_stop(&it); // signal to stop/finalize in next
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CCO_STATE_INIT = 0,
|
||||||
|
CCO_STATE_DONE = -1,
|
||||||
|
CCO_STATE_DROP = -2,
|
||||||
|
};
|
||||||
|
enum cco_status {
|
||||||
|
CCO_DONE = 0,
|
||||||
|
CCO_YIELD = 1<<12,
|
||||||
|
CCO_SUSPEND = 1<<13,
|
||||||
|
CCO_AWAIT = 1<<14,
|
||||||
|
};
|
||||||
|
#define CCO_CANCEL (1U<<30)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int launch_count;
|
||||||
|
int await_count;
|
||||||
|
} cco_group; // waitgroup
|
||||||
|
|
||||||
|
#define cco_state_struct(Prefix) \
|
||||||
|
struct Prefix##_state { \
|
||||||
|
int32_t pos:24; \
|
||||||
|
bool drop; \
|
||||||
|
struct Prefix##_fiber* fb; \
|
||||||
|
cco_group* wg; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cco_is_initial(co) ((co)->base.state.pos == CCO_STATE_INIT)
|
||||||
|
#define cco_is_done(co) ((co)->base.state.pos == CCO_STATE_DONE)
|
||||||
|
#define cco_is_active(co) ((co)->base.state.pos != CCO_STATE_DONE)
|
||||||
|
|
||||||
|
#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF
|
||||||
|
#define _cco_state(co) __typeof__((co)->base.state)
|
||||||
|
#define _cco_validate_task_struct(co) \
|
||||||
|
c_static_assert(/* error: co->base not first member in task struct */ \
|
||||||
|
sizeof((co)->base) == sizeof(cco_base) || \
|
||||||
|
offsetof(__typeof__(*(co)), base) == 0)
|
||||||
|
#else
|
||||||
|
#define _cco_state(co) cco_state
|
||||||
|
#define _cco_validate_task_struct(co) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cco_async(co) \
|
||||||
|
if (0) goto _resume; \
|
||||||
|
else for (_cco_state(co)* _state = (_cco_validate_task_struct(co), (_cco_state(co)*) &(co)->base.state) \
|
||||||
|
; _state->pos != CCO_STATE_DONE \
|
||||||
|
; _state->pos = CCO_STATE_DONE, \
|
||||||
|
(void)(sizeof((co)->base) > sizeof(cco_base) && _state->wg ? --_state->wg->launch_count : 0)) \
|
||||||
|
_resume: switch (_state->pos) case CCO_STATE_INIT: // thanks, @liigo!
|
||||||
|
|
||||||
|
#define cco_drop /* label */ \
|
||||||
|
_state->drop = true; /* FALLTHRU */ \
|
||||||
|
case CCO_STATE_DROP
|
||||||
|
#define cco_cleanup [fix: use cco_drop:]
|
||||||
|
#define cco_routine [fix: use cco_async]
|
||||||
|
|
||||||
|
#define cco_stop(co) \
|
||||||
|
do { \
|
||||||
|
cco_state* _s = (cco_state*)&(co)->base.state; \
|
||||||
|
if (!_s->drop) { _s->pos = CCO_STATE_DROP; _s->drop = true; } \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_reset_state(co) \
|
||||||
|
do { \
|
||||||
|
cco_state* _s = (cco_state*)&(co)->base.state; \
|
||||||
|
_s->pos = CCO_STATE_INIT, _s->drop = false; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_return \
|
||||||
|
do { \
|
||||||
|
_state->pos = (_state->drop ? CCO_STATE_DONE : CCO_STATE_DROP); \
|
||||||
|
_state->drop = true; \
|
||||||
|
goto _resume; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_exit() \
|
||||||
|
do { \
|
||||||
|
_state->pos = CCO_STATE_DONE; \
|
||||||
|
goto _resume; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_yield_v(status) \
|
||||||
|
do { \
|
||||||
|
_state->pos = __LINE__; return status; \
|
||||||
|
case __LINE__:; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_yield \
|
||||||
|
cco_yield_v(CCO_YIELD)
|
||||||
|
|
||||||
|
#define cco_suspend \
|
||||||
|
cco_yield_v(CCO_SUSPEND)
|
||||||
|
|
||||||
|
#define cco_await(until) \
|
||||||
|
do { \
|
||||||
|
_state->pos = __LINE__; /* FALLTHRU */ \
|
||||||
|
case __LINE__: if (!(until)) return CCO_AWAIT; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* cco_await_coroutine(): assumes coroutine returns a status value (int) */
|
||||||
|
#define cco_await_coroutine(...) c_MACRO_OVERLOAD(cco_await_coroutine, __VA_ARGS__)
|
||||||
|
#define cco_await_coroutine_1(corocall) cco_await_coroutine_2(corocall, CCO_DONE)
|
||||||
|
#define cco_await_coroutine_2(corocall, awaitbits) \
|
||||||
|
do { \
|
||||||
|
_state->pos = __LINE__; /* FALLTHRU */ \
|
||||||
|
case __LINE__: { \
|
||||||
|
int _res = corocall; \
|
||||||
|
if (_res & ~(awaitbits)) return _res; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* cco_run_coroutine(): assumes coroutine returns a status value (int) */
|
||||||
|
#define cco_run_coroutine(corocall) \
|
||||||
|
while ((1 ? (corocall) : -1) != CCO_DONE)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tasks and Fibers
|
||||||
|
*/
|
||||||
|
struct cco_error {
|
||||||
|
int32_t code, line;
|
||||||
|
const char* file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define cco_fiber_struct(Prefix, Env) \
|
||||||
|
typedef Env Prefix##_env; \
|
||||||
|
struct Prefix##_fiber { \
|
||||||
|
struct cco_task* task; \
|
||||||
|
Prefix##_env* env; \
|
||||||
|
struct cco_task* parent_task; \
|
||||||
|
struct cco_task_fiber* next; \
|
||||||
|
struct cco_task_state recover_state; \
|
||||||
|
struct cco_error err; \
|
||||||
|
int awaitbits, status; \
|
||||||
|
cco_base base; /* is a coroutine object itself */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Define a Task struct */
|
||||||
|
#define cco_task_struct(...) c_MACRO_OVERLOAD(cco_task_struct, __VA_ARGS__)
|
||||||
|
#define cco_task_struct_1(Task) \
|
||||||
|
cco_task_struct_2(Task, struct _cco_environment)
|
||||||
|
|
||||||
|
#define cco_task_struct_2(Task, Env) \
|
||||||
|
cco_fiber_struct(Task, Env); \
|
||||||
|
cco_state_struct(Task); \
|
||||||
|
_cco_task_struct(Task)
|
||||||
|
|
||||||
|
#define _cco_task_struct(Task) \
|
||||||
|
struct Task; \
|
||||||
|
typedef struct { \
|
||||||
|
int (*func)(struct Task*); \
|
||||||
|
int awaitbits; \
|
||||||
|
struct Task##_state state; \
|
||||||
|
struct cco_task* parent_task; \
|
||||||
|
} Task##_base; \
|
||||||
|
struct Task
|
||||||
|
|
||||||
|
/* Base cco_task type */
|
||||||
|
typedef cco_state_struct(cco_task) cco_state;
|
||||||
|
typedef struct { cco_state state; } cco_base;
|
||||||
|
cco_fiber_struct(cco_task, void);
|
||||||
|
_cco_task_struct(cco_task) { cco_task_base base; };
|
||||||
|
typedef struct cco_task_fiber cco_fiber;
|
||||||
|
typedef struct cco_task cco_task;
|
||||||
|
|
||||||
|
#define cco_err() (&_state->fb->err)
|
||||||
|
#define cco_status() (_state->fb->status + 0)
|
||||||
|
#define cco_fb(task) ((cco_fiber*)(task)->base.state.fb + 0)
|
||||||
|
#define cco_env(task) (task)->base.state.fb->env
|
||||||
|
#define cco_set_env(task, the_env) ((task)->base.state.fb->env = the_env)
|
||||||
|
|
||||||
|
#define cco_cast_task(...) \
|
||||||
|
((void)sizeof((__VA_ARGS__)->base.func(__VA_ARGS__)), (cco_task *)(__VA_ARGS__))
|
||||||
|
|
||||||
|
/* Return with error and unwind await stack; must be recovered in cco_drop section */
|
||||||
|
#define cco_throw(error_code) \
|
||||||
|
do { \
|
||||||
|
cco_fiber* _fb = (cco_fiber*)_state->fb; \
|
||||||
|
_fb->err.code = error_code; \
|
||||||
|
_fb->err.line = __LINE__; \
|
||||||
|
_fb->err.file = __FILE__; \
|
||||||
|
cco_return; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_cancel_fiber(a_fiber) \
|
||||||
|
do { \
|
||||||
|
cco_fiber* _fb1 = a_fiber; \
|
||||||
|
_fb1->err.code = CCO_CANCEL; \
|
||||||
|
_fb1->err.line = __LINE__; \
|
||||||
|
_fb1->err.file = __FILE__; \
|
||||||
|
cco_stop(_fb1->task); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Cancel job/task and unwind await stack; MAY be stopped (recovered) in cco_drop section */
|
||||||
|
/* Equals cco_throw(CCO_CANCEL) if a_task is in current fiber. */
|
||||||
|
#define cco_cancel(a_task) \
|
||||||
|
do { \
|
||||||
|
cco_fiber* _fb2 = cco_cast_task(a_task)->base.state.fb; \
|
||||||
|
cco_cancel_fiber(_fb2); \
|
||||||
|
if (_fb2 == (cco_fiber*)_state->fb) goto _resume; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_cancel_group(waitgroup) \
|
||||||
|
_cco_cancel_group((cco_fiber*)_state->fb, waitgroup)
|
||||||
|
|
||||||
|
#define cco_cancel_all() \
|
||||||
|
for (cco_fiber *_fbi = _state->fb->next; _fbi != (cco_fiber*)_state->fb; _fbi = _fbi->next) \
|
||||||
|
cco_cancel_fiber(_fbi) \
|
||||||
|
|
||||||
|
/* Recover the thrown error; to be used in cco_drop section upon handling cco_err()->code */
|
||||||
|
#define cco_recover \
|
||||||
|
do { \
|
||||||
|
cco_fiber* _fb = (cco_fiber*)_state->fb; \
|
||||||
|
c_assert(_fb->err.code); \
|
||||||
|
_fb->task->base.state = _fb->recover_state; \
|
||||||
|
_fb->err.code = 0; \
|
||||||
|
goto _resume; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Asymmetric coroutine await/call */
|
||||||
|
#define cco_await_task(...) c_MACRO_OVERLOAD(cco_await_task, __VA_ARGS__)
|
||||||
|
#define cco_await_task_1(a_task) cco_await_task_2(a_task, CCO_DONE)
|
||||||
|
#define cco_await_task_2(a_task, _awaitbits) do { \
|
||||||
|
{ cco_task* _await_task = cco_cast_task(a_task); \
|
||||||
|
(void)sizeof(cco_env(a_task) == _state->fb->env); \
|
||||||
|
cco_fiber* _fb = (cco_fiber*)_state->fb; \
|
||||||
|
_await_task->base.awaitbits = (_awaitbits); \
|
||||||
|
_await_task->base.parent_task = _fb->task; \
|
||||||
|
_fb->task = _await_task; \
|
||||||
|
_await_task->base.state.fb = _fb; \
|
||||||
|
} \
|
||||||
|
cco_suspend; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Symmetric coroutine flow of control transfer */
|
||||||
|
#define cco_yield_to(a_task) do { \
|
||||||
|
{ cco_task* _to_task = cco_cast_task(a_task); \
|
||||||
|
(void)sizeof(cco_env(a_task) == _state->fb->env); \
|
||||||
|
cco_fiber* _fb = (cco_fiber*)_state->fb; \
|
||||||
|
_to_task->base.awaitbits = _fb->task->base.awaitbits; \
|
||||||
|
_to_task->base.parent_task = NULL; \
|
||||||
|
_fb->task = _to_task; \
|
||||||
|
_to_task->base.state.fb = _fb; \
|
||||||
|
} \
|
||||||
|
cco_suspend; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_resume(a_task) \
|
||||||
|
_cco_resume_task(cco_cast_task(a_task))
|
||||||
|
|
||||||
|
static inline int _cco_resume_task(cco_task* task)
|
||||||
|
{ return task->base.func(task); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cco_run_fiber()/cco_run_task(): Run fibers/tasks in parallel
|
||||||
|
*/
|
||||||
|
#define cco_new_fiber(...) c_MACRO_OVERLOAD(cco_new_fiber, __VA_ARGS__)
|
||||||
|
#define cco_new_fiber_1(task) \
|
||||||
|
_cco_new_fiber(cco_cast_task(task), NULL, NULL)
|
||||||
|
#define cco_new_fiber_2(task, env) \
|
||||||
|
_cco_new_fiber(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), NULL)
|
||||||
|
|
||||||
|
#define cco_spawn(...) c_MACRO_OVERLOAD(cco_spawn, __VA_ARGS__)
|
||||||
|
#define cco_spawn_1(task) _cco_spawn(cco_cast_task(task), NULL, (cco_fiber*)_state->fb, NULL)
|
||||||
|
#define cco_spawn_2(task, env) \
|
||||||
|
_cco_spawn(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), (cco_fiber*)_state->fb)
|
||||||
|
#define cco_spawn_3(task, env, fiber) \
|
||||||
|
_cco_spawn(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), (cco_fiber*)((void)sizeof((fiber)->parent_task), fiber), NULL)
|
||||||
|
|
||||||
|
#define cco_reset_group(waitgroup) ((waitgroup)->launch_count = 0)
|
||||||
|
#define cco_launch(...) c_MACRO_OVERLOAD(cco_launch, __VA_ARGS__)
|
||||||
|
#define cco_launch_2(task, waitgroup) cco_launch_3(task, waitgroup, NULL)
|
||||||
|
#define cco_launch_3(task, waitgroup, env) do { \
|
||||||
|
cco_group* _wg = waitgroup; _wg->launch_count += 1; \
|
||||||
|
_cco_spawn(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), (cco_fiber*)_state->fb, _wg); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_await_all(waitgroup) \
|
||||||
|
cco_await((waitgroup)->launch_count == 0); \
|
||||||
|
|
||||||
|
#define cco_await_n(waitgroup, n) do { \
|
||||||
|
const int n_ = n; \
|
||||||
|
(waitgroup)->await_count = n_ < 0 ? -n_ : (waitgroup)->launch_count - n_; \
|
||||||
|
cco_await((waitgroup)->launch_count == (waitgroup)->await_count); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_await_any(waitgroup) \
|
||||||
|
cco_await_n(waitgroup, 1)
|
||||||
|
|
||||||
|
#define cco_await_cancel(waitgroup) do { \
|
||||||
|
/* Note: current fiber must not be in the waitgroup */ \
|
||||||
|
cco_cancel_group(waitgroup); \
|
||||||
|
cco_await_all(waitgroup); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define cco_run_fiber(...) c_MACRO_OVERLOAD(cco_run_fiber, __VA_ARGS__)
|
||||||
|
#define cco_run_fiber_1(fiber_ref) \
|
||||||
|
for (cco_fiber** _it_ref = (cco_fiber**)((void)sizeof((fiber_ref)[0]->env), fiber_ref) \
|
||||||
|
; (*_it_ref = cco_execute_next(*_it_ref)) != NULL; )
|
||||||
|
#define cco_run_fiber_2(it, fiber) \
|
||||||
|
for (cco_fiber* it = (cco_fiber*)((void)sizeof((fiber)->env), fiber) \
|
||||||
|
; (it = cco_execute_next(it)) != NULL; )
|
||||||
|
|
||||||
|
#define cco_run_task(...) c_MACRO_OVERLOAD(cco_run_task, __VA_ARGS__)
|
||||||
|
#define cco_run_task_1(task) cco_run_fiber_2(_it_fb, cco_new_fiber_1(task))
|
||||||
|
#define cco_run_task_2(task, env) cco_run_fiber_2(_it_fb, cco_new_fiber_2(task, env))
|
||||||
|
#define cco_run_task_3(it, task, env) cco_run_fiber_2(it, cco_new_fiber_2(task, env))
|
||||||
|
|
||||||
|
#define cco_joined() \
|
||||||
|
((cco_fiber*)_state->fb == _state->fb->next)
|
||||||
|
|
||||||
|
extern cco_fiber* _cco_new_fiber(cco_task* task, void* env, cco_group* wg);
|
||||||
|
extern cco_fiber* _cco_spawn(cco_task* task, void* env, cco_fiber* fb, cco_group* wg);
|
||||||
|
extern int cco_execute(cco_fiber* fb); // is a coroutine itself
|
||||||
|
extern cco_fiber* cco_execute_next(cco_fiber* fb); // resume and return the next fiber
|
||||||
|
extern void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup);
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement || defined STC_IMPLEMENT
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int cco_execute(cco_fiber* fb) {
|
||||||
|
cco_async (fb) {
|
||||||
|
while (1) {
|
||||||
|
fb->parent_task = fb->task->base.parent_task;
|
||||||
|
fb->awaitbits = fb->task->base.awaitbits;
|
||||||
|
fb->status = fb->task->base.func(fb->task); // resume
|
||||||
|
// Note: if fb->status == CCO_DONE, fb->task may already be destructed.
|
||||||
|
if (fb->err.code && (fb->status == CCO_DONE || !fb->task->base.state.drop)) {
|
||||||
|
fb->task = fb->parent_task;
|
||||||
|
if (fb->task == NULL)
|
||||||
|
break;
|
||||||
|
fb->recover_state = fb->task->base.state;
|
||||||
|
cco_stop(fb->task);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!((fb->status & ~fb->awaitbits) || (fb->task = fb->parent_task) != NULL))
|
||||||
|
break;
|
||||||
|
cco_suspend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint32_t)fb->err.code & ~CCO_CANCEL) { // Allow CCO_CANCEL not to trigger error.
|
||||||
|
fprintf(stderr, __FILE__ ": error: unhandled coroutine error '%d'\n"
|
||||||
|
"%s:%d: cco_throw(%d);\n",
|
||||||
|
fb->err.code, fb->err.file, fb->err.line, fb->err.code);
|
||||||
|
exit(fb->err.code);
|
||||||
|
}
|
||||||
|
return CCO_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cco_fiber* cco_execute_next(cco_fiber* fb) {
|
||||||
|
cco_fiber *_next = fb->next, *unlinked;
|
||||||
|
int ret = cco_execute(_next);
|
||||||
|
|
||||||
|
if (ret == CCO_DONE) {
|
||||||
|
unlinked = _next;
|
||||||
|
_next = (_next == fb ? NULL : _next->next);
|
||||||
|
fb->next = _next;
|
||||||
|
c_free_n(unlinked, 1);
|
||||||
|
}
|
||||||
|
return _next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup) {
|
||||||
|
for (cco_fiber *fbi = fb->next; fbi != fb; fbi = fbi->next) {
|
||||||
|
cco_task* top = fbi->task;
|
||||||
|
while (top->base.parent_task)
|
||||||
|
top = top->base.parent_task;
|
||||||
|
if (top->base.state.wg == waitgroup)
|
||||||
|
cco_cancel_fiber(fbi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cco_fiber* _cco_new_fiber(cco_task* _task, void* env, cco_group* wg) {
|
||||||
|
cco_fiber* new_fb = c_new(cco_fiber, {.task=_task, .env=env});
|
||||||
|
_task->base.state.fb = new_fb;
|
||||||
|
_task->base.state.wg = wg;
|
||||||
|
return (new_fb->next = new_fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
cco_fiber* _cco_spawn(cco_task* _task, void* env, cco_fiber* fb, cco_group* wg) {
|
||||||
|
cco_fiber* new_fb;
|
||||||
|
new_fb = fb->next = (fb->next == NULL ? fb : c_new(cco_fiber, {.next=fb->next}));
|
||||||
|
new_fb->task = _task;
|
||||||
|
new_fb->env = (env == NULL ? fb->env : env);
|
||||||
|
_task->base.state.fb = new_fb;
|
||||||
|
_task->base.state.wg = wg;
|
||||||
|
return new_fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef i_implement
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate containers with already defined iterator (prefer to use in coroutines only):
|
||||||
|
*/
|
||||||
|
#define cco_each(existing_it, C, cnt) \
|
||||||
|
existing_it = C##_begin(&cnt); (existing_it).ref; C##_next(&existing_it)
|
||||||
|
|
||||||
|
#define cco_each_reverse(existing_it, C, cnt) \
|
||||||
|
existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using c_filter with coroutine iterators:
|
||||||
|
*/
|
||||||
|
#define cco_flt_take(n) \
|
||||||
|
(c_flt_take(n), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1)
|
||||||
|
|
||||||
|
#define cco_flt_takewhile(pred) \
|
||||||
|
(c_flt_takewhile(pred), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semaphore
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct { ptrdiff_t acq_count; } cco_semaphore;
|
||||||
|
|
||||||
|
#define cco_make_semaphore(value) (c_literal(cco_semaphore){value})
|
||||||
|
#define cco_set_semaphore(sem, value) ((sem)->acq_count = value)
|
||||||
|
#define cco_acquire_semaphore(sem) (--(sem)->acq_count)
|
||||||
|
#define cco_release_semaphore(sem) (++(sem)->acq_count)
|
||||||
|
|
||||||
|
#define cco_await_semaphore(sem) \
|
||||||
|
do { \
|
||||||
|
cco_await((sem)->acq_count > 0); \
|
||||||
|
cco_acquire_semaphore(sem); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define _c_LINKC extern "C" __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define _c_LINKC __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#ifndef _WINDOWS_ // windows.h
|
||||||
|
typedef long long LARGE_INTEGER;
|
||||||
|
_c_LINKC int __stdcall QueryPerformanceCounter(LARGE_INTEGER*);
|
||||||
|
//_c_LINKC int __stdcall QueryPerformanceFrequency(LARGE_INTEGER*);
|
||||||
|
#endif
|
||||||
|
#define cco_timer_freq() 10000000LL /* 1/10th microseconds */
|
||||||
|
//static inline long long cco_timer_freq(void) {
|
||||||
|
// long long quad;
|
||||||
|
// QueryPerformanceFrequency((LARGE_INTEGER*)&quad);
|
||||||
|
// return quad;
|
||||||
|
//}
|
||||||
|
|
||||||
|
static inline long long cco_timer_ticks(void) {
|
||||||
|
long long quad;
|
||||||
|
QueryPerformanceCounter((LARGE_INTEGER*)&quad);
|
||||||
|
return quad;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <sys/time.h>
|
||||||
|
#define cco_timer_freq() 1000000LL
|
||||||
|
|
||||||
|
static inline long long cco_timer_ticks(void) { /* microseconds */
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec*cco_timer_freq() + tv.tv_usec;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct { double duration; long long start_time; } cco_timer;
|
||||||
|
|
||||||
|
static inline cco_timer cco_make_timer(double sec) {
|
||||||
|
cco_timer tm = {.duration=sec, .start_time=cco_timer_ticks()};
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cco_start_timer(cco_timer* tm, double sec) {
|
||||||
|
tm->duration = sec;
|
||||||
|
tm->start_time = cco_timer_ticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cco_restart_timer(cco_timer* tm) {
|
||||||
|
tm->start_time = cco_timer_ticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double cco_timer_elapsed(cco_timer* tm) {
|
||||||
|
return (double)(cco_timer_ticks() - tm->start_time)*(1.0/cco_timer_freq());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool cco_timer_expired(cco_timer* tm) {
|
||||||
|
return cco_timer_elapsed(tm) >= tm->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double cco_timer_remaining(cco_timer* tm) {
|
||||||
|
return tm->duration - cco_timer_elapsed(tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cco_await_timer(tm, sec) \
|
||||||
|
do { \
|
||||||
|
cco_start_timer(tm, sec); \
|
||||||
|
cco_await(cco_timer_expired(tm)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif // STC_COROUTINE_H_INCLUDED
|
||||||
168
stc/cregex.h
Normal file
168
stc/cregex.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
This is a Unix port of the Plan 9 regular expression library, by Rob Pike.
|
||||||
|
|
||||||
|
Copyright © 2021 Plan 9 Foundation
|
||||||
|
Copyright © 2022 Tyge Løvset, for additions made in 2022.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef STC_CREGEX_H_INCLUDED
|
||||||
|
#define STC_CREGEX_H_INCLUDED
|
||||||
|
/*
|
||||||
|
* cregex.h
|
||||||
|
*
|
||||||
|
* This is a extended version of regexp9, supporting UTF8 input, common
|
||||||
|
* shorthand character classes, ++.
|
||||||
|
*/
|
||||||
|
#include "common.h"
|
||||||
|
#include "types.h" // csview, cstr types
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CREG_DEFAULT = 0,
|
||||||
|
|
||||||
|
/* compile-flags */
|
||||||
|
CREG_DOTALL = 1<<0, /* dot matches newline too */
|
||||||
|
CREG_ICASE = 1<<1, /* ignore case */
|
||||||
|
|
||||||
|
/* match-flags */
|
||||||
|
CREG_FULLMATCH = 1<<2, /* like start-, end-of-line anchors were in pattern: "^ ... $" */
|
||||||
|
CREG_NEXT = 1<<3, /* use end of previous match[0] as start of input */
|
||||||
|
|
||||||
|
/* replace-flags */
|
||||||
|
CREG_STRIP = 1<<5, /* only keep the matched strings, strip rest */
|
||||||
|
|
||||||
|
/* limits */
|
||||||
|
CREG_MAX_CLASSES = 16,
|
||||||
|
CREG_MAX_CAPTURES = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CREG_OK = 0,
|
||||||
|
CREG_NOMATCH = -1,
|
||||||
|
CREG_MATCHERROR = -2,
|
||||||
|
CREG_OUTOFMEMORY = -3,
|
||||||
|
CREG_UNMATCHEDLEFTPARENTHESIS = -4,
|
||||||
|
CREG_UNMATCHEDRIGHTPARENTHESIS = -5,
|
||||||
|
CREG_TOOMANYSUBEXPRESSIONS = -6,
|
||||||
|
CREG_TOOMANYCHARACTERCLASSES = -7,
|
||||||
|
CREG_MALFORMEDCHARACTERCLASS = -8,
|
||||||
|
CREG_MISSINGOPERAND = -9,
|
||||||
|
CREG_UNKNOWNOPERATOR = -10,
|
||||||
|
CREG_OPERANDSTACKOVERFLOW = -11,
|
||||||
|
CREG_OPERATORSTACKOVERFLOW = -12,
|
||||||
|
CREG_OPERATORSTACKUNDERFLOW = -13,
|
||||||
|
} cregex_result;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct _Reprog* prog;
|
||||||
|
int error;
|
||||||
|
} cregex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const cregex* regex;
|
||||||
|
csview input;
|
||||||
|
csview match[CREG_MAX_CAPTURES];
|
||||||
|
} cregex_iter;
|
||||||
|
|
||||||
|
#define c_match(it, re, str) \
|
||||||
|
cregex_iter it = {.regex=re, .input={str}, .match={{0}}}; \
|
||||||
|
cregex_match(it.regex, it.input.buf, it.match, CREG_NEXT) == CREG_OK && it.match[0].size;
|
||||||
|
|
||||||
|
#define c_match_sv(it, re, strview) \
|
||||||
|
cregex_iter it = {.regex=re, .input=strview, .match={{0}}}; \
|
||||||
|
cregex_match_sv(it.regex, it.input, it.match, CREG_NEXT) == CREG_OK && it.match[0].size;
|
||||||
|
|
||||||
|
/* compile a regex from a pattern. return CREG_OK, or negative error code on failure. */
|
||||||
|
extern int cregex_compile_pro(cregex *re, const char* pattern, int cflags);
|
||||||
|
|
||||||
|
#define cregex_compile(...) \
|
||||||
|
c_ARG_4(__VA_ARGS__, cregex_compile_pro(__VA_ARGS__), cregex_compile_pro(__VA_ARGS__, CREG_DEFAULT), _too_few_args_)
|
||||||
|
|
||||||
|
/* construct and return a regex from a pattern. return CREG_OK, or negative error code on failure. */
|
||||||
|
STC_INLINE cregex cregex_make(const char* pattern, int cflags) {
|
||||||
|
cregex re = {0};
|
||||||
|
cregex_compile_pro(&re, pattern, cflags);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
STC_INLINE cregex cregex_from(const char* pattern)
|
||||||
|
{ return cregex_make(pattern, CREG_DEFAULT); }
|
||||||
|
|
||||||
|
/* destroy regex */
|
||||||
|
extern void cregex_drop(cregex* re);
|
||||||
|
|
||||||
|
/* number of capture groups in a regex pattern, excluding the full match capture (0) */
|
||||||
|
extern int cregex_captures(const cregex* re);
|
||||||
|
|
||||||
|
/* ----- Private ----- */
|
||||||
|
|
||||||
|
struct cregex_match_opt { csview* match; int flags; int _dummy; };
|
||||||
|
struct cregex_replace_opt { int count; bool(*xform)(int group, csview match, cstr* out); int flags; int _dummy; };
|
||||||
|
|
||||||
|
extern int cregex_match_opt(const cregex* re, const char* input, const char* input_end, struct cregex_match_opt opt);
|
||||||
|
extern int cregex_match_aio_opt(const char* pattern, const char* input, const char* input_end, struct cregex_match_opt opt);
|
||||||
|
extern cstr cregex_replace_opt(const cregex* re, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt);
|
||||||
|
extern cstr cregex_replace_aio_opt(const char* pattern, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt);
|
||||||
|
|
||||||
|
static inline int cregex_match_sv_opt(const cregex* re, csview sv, struct cregex_match_opt opt)
|
||||||
|
{ return cregex_match_opt(re, sv.buf, sv.buf+sv.size, opt); }
|
||||||
|
static inline int cregex_match_aio_sv_opt(const char* pattern, csview sv, struct cregex_match_opt opt)
|
||||||
|
{ return cregex_match_aio_opt(pattern, sv.buf, sv.buf+sv.size, opt); }
|
||||||
|
static inline cstr cregex_replace_sv_opt(const cregex* re, csview sv, const char* replace, struct cregex_replace_opt opt)
|
||||||
|
{ return cregex_replace_opt(re, sv.buf, sv.buf+sv.size, replace, opt); }
|
||||||
|
static inline cstr cregex_replace_aio_sv_opt(const char* pattern, csview sv, const char* replace, struct cregex_replace_opt opt)
|
||||||
|
{ return cregex_replace_aio_opt(pattern, sv.buf, sv.buf+sv.size, replace, opt); }
|
||||||
|
|
||||||
|
/* match: return CREG_OK, CREG_NOMATCH or CREG_MATCHERROR. */
|
||||||
|
#define _cregex_match(re, str, ...) cregex_match_opt(re, str, NULL, (struct cregex_match_opt){__VA_ARGS__})
|
||||||
|
#define _cregex_match_sv(re, sv, ...) cregex_match_sv_opt(re, sv, (struct cregex_match_opt){__VA_ARGS__})
|
||||||
|
/* all-in-one: compile RE pattern + match + free */
|
||||||
|
#define _cregex_match_aio(pattern, str, ...) cregex_match_aio_opt(pattern, str, NULL, (struct cregex_match_opt){__VA_ARGS__})
|
||||||
|
#define _cregex_match_aio_sv(pattern, sv, ...) cregex_match_aio_sv_opt(pattern, sv, (struct cregex_match_opt){__VA_ARGS__})
|
||||||
|
|
||||||
|
/* replace input with a string using regular expression */
|
||||||
|
#define _cregex_replace(re, str, replace, ...) cregex_replace_opt(re, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__})
|
||||||
|
#define _cregex_replace_sv(re, sv, replace, ...) cregex_replace_sv_opt(re, sv, replace, (struct cregex_replace_opt){__VA_ARGS__})
|
||||||
|
/* all-in-one: compile RE string pattern + match + replace + free */
|
||||||
|
#define _cregex_replace_aio(pattern, str, replace, ...) cregex_replace_aio_opt(pattern, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__})
|
||||||
|
#define _cregex_replace_aio_sv(pattern, sv, replace, ...) cregex_replace_aio_sv_opt(pattern, sv, replace, (struct cregex_replace_opt){__VA_ARGS__})
|
||||||
|
|
||||||
|
/* ----- API functions ---- */
|
||||||
|
|
||||||
|
#define cregex_match(...) _cregex_match(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_match_sv(...) _cregex_match_sv(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_match_aio(...) _cregex_match_aio(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_match_aio_sv(...) _cregex_match_aio_sv(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_is_match(re, str) (_cregex_match(re, str, 0) == CREG_OK)
|
||||||
|
|
||||||
|
#define cregex_replace(...) _cregex_replace(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_replace_sv(...) _cregex_replace_sv(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_replace_aio(...) _cregex_replace_aio(__VA_ARGS__, ._dummy=0)
|
||||||
|
#define cregex_replace_aio_sv(...) _cregex_replace_aio_sv(__VA_ARGS__, ._dummy=0)
|
||||||
|
|
||||||
|
#endif // STC_CREGEX_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined STC_IMPLEMENT || defined i_implement || defined i_import
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "priv/cregex_prv.c"
|
||||||
|
#if defined i_import
|
||||||
|
#include "priv/utf8_prv.c"
|
||||||
|
#include "priv/cstr_prv.c"
|
||||||
|
#endif
|
||||||
|
#include "priv/linkage2.h"
|
||||||
|
#endif
|
||||||
479
stc/cspan.h
Normal file
479
stc/cspan.h
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/cspan.h>
|
||||||
|
#include <stc/algorithm.h>
|
||||||
|
use_cspan(Span2f, float, 2);
|
||||||
|
use_cspan(Intspan, int);
|
||||||
|
|
||||||
|
int demo1() {
|
||||||
|
float raw[4*5];
|
||||||
|
Span2f ms = cspan_md(raw, 4, 5);
|
||||||
|
|
||||||
|
for (int i=0; i<ms.shape[0]; i++)
|
||||||
|
for (int j=0; j<ms.shape[1]; j++)
|
||||||
|
*cspan_at(&ms, i, j) = i*1000 + j;
|
||||||
|
|
||||||
|
printf("%f\n", *cspan_at(&ms, 3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
int demo2() {
|
||||||
|
int array[] = {10, 20, 30, 23, 22, 21};
|
||||||
|
Intspan span = cspan_from_array(array);
|
||||||
|
|
||||||
|
for (c_each(i, Intspan, span))
|
||||||
|
printf(" %d", *i.ref);
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
c_filter(Intspan, span
|
||||||
|
, c_flt_skipwhile(*value < 25)
|
||||||
|
&& (*value & 1) == 0 // even only
|
||||||
|
&& (printf(" %d", *value),
|
||||||
|
c_flt_take(2))
|
||||||
|
);
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define i_header
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
|
||||||
|
#ifndef STC_CSPAN_H_INCLUDED
|
||||||
|
#define STC_CSPAN_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#ifndef STC_CSPAN_INDEX_TYPE
|
||||||
|
#define STC_CSPAN_INDEX_TYPE int32_t
|
||||||
|
#endif
|
||||||
|
typedef STC_CSPAN_INDEX_TYPE _istride;
|
||||||
|
|
||||||
|
#define using_cspan use_cspan // [deprecated]
|
||||||
|
#define using_cspan2 use_cspan2 // [deprecated]
|
||||||
|
#define using_cspan3 use_cspan3 // [deprecated]
|
||||||
|
#define using_cspan2_with_eq use_cspan2_with_eq // [deprecated]
|
||||||
|
#define using_cspan3_with_eq use_cspan3_with_eq // [deprecated]
|
||||||
|
|
||||||
|
#define use_cspan(...) c_MACRO_OVERLOAD(use_cspan, __VA_ARGS__)
|
||||||
|
#define use_cspan_2(Self, T) \
|
||||||
|
use_cspan_3(Self, T, 1); \
|
||||||
|
STC_INLINE Self Self##_from_n(Self##_value* dataptr, isize n) \
|
||||||
|
{ return (Self)cspan_from_n(dataptr, n); } \
|
||||||
|
STC_INLINE const Self##_value* Self##_at(const Self* self, isize idx) \
|
||||||
|
{ return cspan_at(self, idx); } \
|
||||||
|
STC_INLINE Self##_value* Self##_at_mut(Self* self, isize idx) \
|
||||||
|
{ return cspan_at(self, idx); } \
|
||||||
|
struct stc_nostruct
|
||||||
|
|
||||||
|
#define use_cspan_with_eq(...) c_MACRO_OVERLOAD(use_cspan_with_eq, __VA_ARGS__)
|
||||||
|
#define use_cspan_with_eq_3(Self, T, i_eq) \
|
||||||
|
use_cspan_with_eq_4(Self, T, i_eq, 1); \
|
||||||
|
STC_INLINE Self Self##_from_n(Self##_value* dataptr, isize n) \
|
||||||
|
{ return (Self)cspan_from_n(dataptr, n); } \
|
||||||
|
struct stc_nostruct
|
||||||
|
|
||||||
|
#define use_cspan_3(Self, T, RANK) \
|
||||||
|
typedef T Self##_value; \
|
||||||
|
typedef T Self##_raw; \
|
||||||
|
typedef struct { \
|
||||||
|
Self##_value *data; \
|
||||||
|
_istride shape[RANK]; \
|
||||||
|
cspan_tuple##RANK stride; \
|
||||||
|
} Self; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
Self##_value *ref; \
|
||||||
|
const Self *_s; \
|
||||||
|
_istride pos[RANK]; \
|
||||||
|
} Self##_iter; \
|
||||||
|
\
|
||||||
|
STC_INLINE Self Self##_slice_(Self##_value* d, const _istride shape[], const _istride stri[], \
|
||||||
|
const isize args[][3], const int rank) { \
|
||||||
|
Self s; int outrank; \
|
||||||
|
s.data = d + _cspan_slice(s.shape, s.stride.d, &outrank, shape, stri, args, rank); \
|
||||||
|
c_assert(outrank == RANK); \
|
||||||
|
return s; \
|
||||||
|
} \
|
||||||
|
STC_INLINE Self##_iter Self##_begin(const Self* self) { \
|
||||||
|
return c_literal(Self##_iter){ \
|
||||||
|
.ref=RANK==1 && self->shape[0]==0 ? NULL : self->data, ._s=self}; \
|
||||||
|
} \
|
||||||
|
STC_INLINE Self##_iter Self##_end(const Self* self) { \
|
||||||
|
(void)self; \
|
||||||
|
return c_literal(Self##_iter){0}; \
|
||||||
|
} \
|
||||||
|
STC_INLINE void Self##_next(Self##_iter* it) { \
|
||||||
|
isize off = it->_s->stride.d[RANK - 1]; \
|
||||||
|
bool done = _cspan_next##RANK(it->pos, it->_s->shape, it->_s->stride.d, RANK, &off); \
|
||||||
|
if (done) it->ref = NULL; else it->ref += off; \
|
||||||
|
} \
|
||||||
|
STC_INLINE isize Self##_size(const Self* self) \
|
||||||
|
{ return cspan_size(self); } \
|
||||||
|
STC_INLINE Self Self##_transposed(Self sp) \
|
||||||
|
{ _cspan_transpose(sp.shape, sp.stride.d, cspan_rank(&sp)); return sp; } \
|
||||||
|
STC_INLINE Self Self##_swapped_axes(Self sp, int ax1, int ax2) \
|
||||||
|
{ _cspan_swap_axes(sp.shape, sp.stride.d, cspan_rank(&sp), ax1, ax2); return sp; } \
|
||||||
|
struct stc_nostruct
|
||||||
|
|
||||||
|
#define use_cspan_with_eq_4(Self, T, i_eq, RANK) \
|
||||||
|
use_cspan_3(Self, T, RANK); \
|
||||||
|
STC_INLINE bool Self##_eq(const Self* x, const Self* y) { \
|
||||||
|
if (memcmp(x->shape, y->shape, sizeof x->shape) != 0) \
|
||||||
|
return false; \
|
||||||
|
for (Self##_iter _i = Self##_begin(x), _j = Self##_begin(y); \
|
||||||
|
_i.ref != NULL; Self##_next(&_i), Self##_next(&_j)) \
|
||||||
|
{ if (!(i_eq(_i.ref, _j.ref))) return false; } \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
STC_INLINE bool Self##_equals(Self sp1, Self sp2) \
|
||||||
|
{ return Self##_eq(&sp1, &sp2); } \
|
||||||
|
struct stc_nostruct
|
||||||
|
|
||||||
|
#define use_cspan2(Self, T) use_cspan_2(Self, T); use_cspan_3(Self##2, T, 2)
|
||||||
|
#define use_cspan3(Self, T) use_cspan2(Self, T); use_cspan_3(Self##3, T, 3)
|
||||||
|
#define use_cspan2_with_eq(Self, T, eq) use_cspan_with_eq_3(Self, T, eq); \
|
||||||
|
use_cspan_with_eq_4(Self##2, T, eq, 2)
|
||||||
|
#define use_cspan3_with_eq(Self, T, eq) use_cspan2_with_eq(Self, T, eq); \
|
||||||
|
use_cspan_with_eq_4(Self##3, T, eq, 3)
|
||||||
|
#define use_cspan_tuple(N) typedef struct { _istride d[N]; } cspan_tuple##N
|
||||||
|
use_cspan_tuple(1); use_cspan_tuple(2);
|
||||||
|
use_cspan_tuple(3); use_cspan_tuple(4);
|
||||||
|
use_cspan_tuple(5); use_cspan_tuple(6);
|
||||||
|
use_cspan_tuple(7); use_cspan_tuple(8);
|
||||||
|
|
||||||
|
|
||||||
|
// Construct a cspan from a pointer+size
|
||||||
|
#define cspan_from_n(dataptr, n) \
|
||||||
|
{.data=dataptr, \
|
||||||
|
.shape={(_istride)(n)}, \
|
||||||
|
.stride=c_literal(cspan_tuple1){.d={1}}}
|
||||||
|
|
||||||
|
// Create a 1d-span in the local lexical scope. N must be a compile-time constant.
|
||||||
|
#define cspan_by_copy(dataptr, N) \
|
||||||
|
cspan_from_n(memcpy((char[(N)*sizeof *(dataptr)]){0}, dataptr, (N)*sizeof *(dataptr)), N)
|
||||||
|
|
||||||
|
// Create a zeroed out 1d-span in the local lexical scope. N must be a compile-time constant.
|
||||||
|
#define cspan_zeros(Span, N) \
|
||||||
|
((Span)cspan_from_n((Span##_value[N]){0}, N))
|
||||||
|
|
||||||
|
// Create a global scope 1d-span from constant initializer list, otherwise like c_make(Span, ...).
|
||||||
|
#define cspan_make(Span, ...) \
|
||||||
|
((Span)cspan_from_n(c_make_array(Span##_value, __VA_ARGS__), \
|
||||||
|
sizeof((Span##_value[])__VA_ARGS__)/sizeof(Span##_value)))
|
||||||
|
|
||||||
|
// Make 1d-span from a c-array.
|
||||||
|
#define cspan_from_array(array) \
|
||||||
|
cspan_from_n(array, c_arraylen(array))
|
||||||
|
|
||||||
|
// Make 1d-span from a vec or stack container.
|
||||||
|
#define cspan_from_vec(container) \
|
||||||
|
cspan_from_n((container)->data, (container)->size)
|
||||||
|
|
||||||
|
// Make a 1d-sub-span from a 1d-span
|
||||||
|
#define cspan_subspan(self, offset, count) \
|
||||||
|
{.data=cspan_at(self, offset), \
|
||||||
|
.shape={(_istride)(count)}, \
|
||||||
|
.stride=(self)->stride}
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
//
|
||||||
|
#define cspan_size(self) _cspan_size((self)->shape, cspan_rank(self))
|
||||||
|
#define cspan_rank(self) c_arraylen((self)->shape) // constexpr
|
||||||
|
#define cspan_at(self, ...) ((self)->data + cspan_index(self, __VA_ARGS__))
|
||||||
|
#define cspan_front(self) ((self)->data)
|
||||||
|
#define cspan_back(self) ((self)->data + cspan_size(self) - 1)
|
||||||
|
|
||||||
|
#define cspan_index(...) cspan_index_fn(__VA_ARGS__, c_COMMA_N(cspan_index_3d), c_COMMA_N(cspan_index_2d), \
|
||||||
|
c_COMMA_N(cspan_index_1d),)(__VA_ARGS__)
|
||||||
|
#define cspan_index_fn(self, i,j,k,n, ...) c_TUPLE_AT_1(n, cspan_index_nd,)
|
||||||
|
#define cspan_index_1d(self, i) (c_static_assert(cspan_rank(self) == 1), \
|
||||||
|
c_assert((i) < (self)->shape[0]), \
|
||||||
|
(i)*(self)->stride.d[0])
|
||||||
|
#define cspan_index_2d(self, i,j) (c_static_assert(cspan_rank(self) == 2), \
|
||||||
|
c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1]), \
|
||||||
|
(i)*(self)->stride.d[0] + (j)*(self)->stride.d[1])
|
||||||
|
#define cspan_index_3d(self, i,j,k) (c_static_assert(cspan_rank(self) == 3), \
|
||||||
|
c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1] && (k) < (self)->shape[2]), \
|
||||||
|
(i)*(self)->stride.d[0] + (j)*(self)->stride.d[1] + (k)*(self)->stride.d[2])
|
||||||
|
#define cspan_index_nd(self, ...) _cspan_index((self)->shape, (self)->stride.d, c_make_array(isize, {__VA_ARGS__}), \
|
||||||
|
(c_static_assert(cspan_rank(self) == c_NUMARGS(__VA_ARGS__)), cspan_rank(self)))
|
||||||
|
|
||||||
|
|
||||||
|
// Multi-dimensional span constructors
|
||||||
|
//
|
||||||
|
typedef enum {c_ROWMAJOR, c_COLMAJOR, c_STRIDED} cspan_layout;
|
||||||
|
|
||||||
|
#define cspan_is_colmajor(self) \
|
||||||
|
_cspan_is_layout(c_COLMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self))
|
||||||
|
#define cspan_is_rowmajor(self) \
|
||||||
|
_cspan_is_layout(c_ROWMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self))
|
||||||
|
#define cspan_get_layout(self) \
|
||||||
|
(cspan_is_rowmajor(self) ? c_ROWMAJOR : cspan_is_colmajor(self) ? c_COLMAJOR : c_STRIDED)
|
||||||
|
|
||||||
|
#define cspan_md(dataptr, ...) \
|
||||||
|
cspan_md_layout(c_ROWMAJOR, dataptr, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Span2 sp1 = cspan_md(data, 30, 50);
|
||||||
|
// Span2 sp2 = {data, cspan_shape(15, 25), cspan_strides(50*2, 2)}; // every second in each dim
|
||||||
|
#define cspan_shape(...) {__VA_ARGS__}
|
||||||
|
#define cspan_strides(...) {.d={__VA_ARGS__}}
|
||||||
|
|
||||||
|
#define cspan_md_layout(layout, dataptr, ...) \
|
||||||
|
{.data=dataptr, \
|
||||||
|
.shape={__VA_ARGS__}, \
|
||||||
|
.stride=*(c_JOIN(cspan_tuple,c_NUMARGS(__VA_ARGS__))*) \
|
||||||
|
_cspan_shape2stride(layout, c_make_array(_istride, {__VA_ARGS__}), c_NUMARGS(__VA_ARGS__))}
|
||||||
|
|
||||||
|
// Transpose matrix
|
||||||
|
#define cspan_transpose(self) \
|
||||||
|
_cspan_transpose((self)->shape, (self)->stride.d, cspan_rank(self))
|
||||||
|
|
||||||
|
// Swap two matrix axes
|
||||||
|
#define cspan_swap_axes(self, ax1, ax2) \
|
||||||
|
_cspan_swap_axes((self)->shape, (self)->stride.d, cspan_rank(self), ax1, ax2)
|
||||||
|
|
||||||
|
// Set all span elements to value.
|
||||||
|
#define cspan_set_all(Span, self, value) do { \
|
||||||
|
Span##_value _v = value; \
|
||||||
|
for (c_each_3(_it, Span, *(self))) *_it.ref = _v; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// General slicing function.
|
||||||
|
//
|
||||||
|
#define c_END (_istride)(((size_t)1 << (sizeof(_istride)*8 - 1)) - 1)
|
||||||
|
#define c_ALL 0,c_END
|
||||||
|
|
||||||
|
#define cspan_slice(self, Outspan, ...) \
|
||||||
|
Outspan##_slice_((self)->data, (self)->shape, (self)->stride.d, \
|
||||||
|
c_make_array2d(const isize, 3, {__VA_ARGS__}), \
|
||||||
|
(c_static_assert(cspan_rank(self) == sizeof((isize[][3]){__VA_ARGS__})/sizeof(isize[3])), cspan_rank(self)))
|
||||||
|
|
||||||
|
// submd#(): Reduces rank, fully typesafe + range checked by default
|
||||||
|
// int ms3[N1][N2][N3];
|
||||||
|
// int (*ms2)[N3] = ms3[1]; // traditional, lose range test/info. VLA.
|
||||||
|
// Span3 ms3 = cspan_md(data, N1,N2,N3); // Uses cspan_md instead.
|
||||||
|
// *cspan_at(&ms3, 1,1,1) = 42;
|
||||||
|
// Span2 ms2 = cspan_slice(&ms3, Span2, {1}, {c_ALL}, {c_ALL});
|
||||||
|
// Span2 ms2 = cspan_submd3(&ms3, 1); // Same as line above, optimized.
|
||||||
|
#define cspan_submd2(self, x) \
|
||||||
|
{.data=cspan_at(self, x, 0), \
|
||||||
|
.shape={(self)->shape[1]}, \
|
||||||
|
.stride=c_literal(cspan_tuple1){.d={(self)->stride.d[1]}}}
|
||||||
|
|
||||||
|
#define cspan_submd3(...) c_MACRO_OVERLOAD(cspan_submd3, __VA_ARGS__)
|
||||||
|
#define cspan_submd3_2(self, x) \
|
||||||
|
{.data=cspan_at(self, x, 0, 0), \
|
||||||
|
.shape={(self)->shape[1], (self)->shape[2]}, \
|
||||||
|
.stride=c_literal(cspan_tuple2){.d={(self)->stride.d[1], (self)->stride.d[2]}}}
|
||||||
|
#define cspan_submd3_3(self, x, y) \
|
||||||
|
{.data=cspan_at(self, x, y, 0), \
|
||||||
|
.shape={(self)->shape[2]}, \
|
||||||
|
.stride=c_literal(cspan_tuple1){.d={(self)->stride.d[2]}}}
|
||||||
|
|
||||||
|
#define cspan_submd4(...) c_MACRO_OVERLOAD(cspan_submd4, __VA_ARGS__)
|
||||||
|
#define cspan_submd4_2(self, x) \
|
||||||
|
{.data=cspan_at(self, x, 0, 0, 0), \
|
||||||
|
.shape={(self)->shape[1], (self)->shape[2], (self)->shape[3]}, \
|
||||||
|
.stride=c_literal(cspan_tuple3){.d={(self)->stride.d[1], (self)->stride.d[2], (self)->stride.d[3]}}}
|
||||||
|
#define cspan_submd4_3(self, x, y) \
|
||||||
|
{.data=cspan_at(self, x, y, 0, 0), \
|
||||||
|
.shape={(self)->shape[2], (self)->shape[3]}, \
|
||||||
|
.stride=c_literal(cspan_tuple2){.d={(self)->stride.d[2], (self)->stride.d[3]}}}
|
||||||
|
#define cspan_submd4_4(self, x, y, z) \
|
||||||
|
{.data=cspan_at(self, x, y, z, 0), \
|
||||||
|
.shape={(self)->shape[3]}, \
|
||||||
|
.stride=c_literal(cspan_tuple1){.d={(self)->stride.d[3]}}}
|
||||||
|
|
||||||
|
#define cspan_print(...) c_MACRO_OVERLOAD(cspan_print, __VA_ARGS__)
|
||||||
|
#define cspan_print_3(Span, fmt, span) \
|
||||||
|
cspan_print_4(Span, fmt, span, stdout)
|
||||||
|
#define cspan_print_4(Span, fmt, span, fp) \
|
||||||
|
cspan_print_5(Span, fmt, span, fp, "[]")
|
||||||
|
#define cspan_print_5(Span, fmt, span, fp, brackets) \
|
||||||
|
cspan_print_6(Span, fmt, span, fp, brackets, c_EXPAND)
|
||||||
|
#define cspan_print_complex(Span, prec, span, fp) \
|
||||||
|
cspan_print_6(Span, "%." #prec "f%+." #prec "fi", span, fp, "[]", cspan_CMPLX_FLD)
|
||||||
|
#define cspan_CMPLX_FLD(x) creal(x), cimag(x)
|
||||||
|
|
||||||
|
#define cspan_print_6(Span, fmt, span, fp, brackets, field) do { \
|
||||||
|
const Span _s = span; \
|
||||||
|
const char *_f = fmt, *_b = brackets; \
|
||||||
|
FILE* _fp = fp; \
|
||||||
|
int _w, _max = 0; \
|
||||||
|
char _res[2][20], _fld[64]; \
|
||||||
|
for (c_each_3(_it, Span, _s)) { \
|
||||||
|
_w = snprintf(NULL, 0ULL, _f, field(_it.ref[0])); \
|
||||||
|
if (_w > _max) _max = _w; \
|
||||||
|
} \
|
||||||
|
for (c_each_3(_it, Span, _s)) { \
|
||||||
|
_cspan_print_assist(_it.pos, _s.shape, cspan_rank(&_s), _b, _res); \
|
||||||
|
_w = _max + (_it.pos[cspan_rank(&_s) - 1] > 0); \
|
||||||
|
snprintf(_fld, sizeof _fld, _f, field(_it.ref[0])); \
|
||||||
|
fprintf(_fp, "%s%*s%s", _res[0], _w, _fld, _res[1]); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* ----- PRIVATE ----- */
|
||||||
|
|
||||||
|
STC_INLINE isize _cspan_size(const _istride shape[], int rank) {
|
||||||
|
isize size = shape[0];
|
||||||
|
while (--rank) size *= shape[rank];
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _cspan_swap_axes(_istride shape[], _istride stride[],
|
||||||
|
int rank, int ax1, int ax2) {
|
||||||
|
(void)rank;
|
||||||
|
c_assert(c_uless(ax1, rank) & c_uless(ax2, rank));
|
||||||
|
c_swap(shape + ax1, shape + ax2);
|
||||||
|
c_swap(stride + ax1, stride + ax2);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _cspan_transpose(_istride shape[], _istride stride[], int rank) {
|
||||||
|
for (int i = 0; i < --rank; ++i) {
|
||||||
|
c_swap(shape + i, shape + rank);
|
||||||
|
c_swap(stride + i, stride + rank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _cspan_index(const _istride shape[], const _istride stride[],
|
||||||
|
const isize args[], int rank) {
|
||||||
|
isize off = 0;
|
||||||
|
(void)shape;
|
||||||
|
while (rank-- != 0) {
|
||||||
|
c_assert(args[rank] < shape[rank]);
|
||||||
|
off += args[rank]*stride[rank];
|
||||||
|
}
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_API void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank,
|
||||||
|
const char* brackets, char result[2][20]);
|
||||||
|
|
||||||
|
STC_API bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[],
|
||||||
|
int rank, isize* off);
|
||||||
|
#define _cspan_next1(pos, shape, stride, rank, off) (++pos[0] == shape[0])
|
||||||
|
#define _cspan_next2(pos, shape, stride, rank, off) (++pos[1] == shape[1] && \
|
||||||
|
(pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0]))
|
||||||
|
#define _cspan_next3(pos, shape, stride, rank, off) (++pos[2] == shape[2] && \
|
||||||
|
(pos[2] = 0, *off += stride[1] - (isize)shape[2]*stride[2], ++pos[1] == shape[1]) && \
|
||||||
|
(pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0]))
|
||||||
|
#define _cspan_next4 _cspan_nextN
|
||||||
|
#define _cspan_next5 _cspan_nextN
|
||||||
|
#define _cspan_next6 _cspan_nextN
|
||||||
|
#define _cspan_next7 _cspan_nextN
|
||||||
|
#define _cspan_next8 _cspan_nextN
|
||||||
|
|
||||||
|
STC_API isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank,
|
||||||
|
const _istride shape[], const _istride stride[],
|
||||||
|
const isize args[][3], int rank);
|
||||||
|
STC_API _istride* _cspan_shape2stride(cspan_layout layout, _istride shape[], int rank);
|
||||||
|
STC_API bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank);
|
||||||
|
|
||||||
|
#endif // STC_CSPAN_H_INCLUDED
|
||||||
|
|
||||||
|
/* --------------------- IMPLEMENTATION --------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank) {
|
||||||
|
_istride tmpshape[16]; // 16 = "max" rank
|
||||||
|
size_t sz = (size_t)rank*sizeof(_istride);
|
||||||
|
memcpy(tmpshape, shape, sz);
|
||||||
|
return memcmp(strides, _cspan_shape2stride(layout, tmpshape, rank), sz) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank,
|
||||||
|
const char* brackets, char result[2][20]) {
|
||||||
|
int n = 0, j = 0, r = rank - 1;
|
||||||
|
memset(result, 0, 32);
|
||||||
|
|
||||||
|
// left braces:
|
||||||
|
while (n <= r && pos[r - n] == 0)
|
||||||
|
++n;
|
||||||
|
if (n) for (; j < rank; ++j)
|
||||||
|
result[0][j] = j < rank - n ? ' ' : brackets[0];
|
||||||
|
|
||||||
|
// right braces:
|
||||||
|
for (j = 0; r >= 0 && pos[r] + 1 == shape[r]; --r, ++j)
|
||||||
|
result[1][j] = brackets[1];
|
||||||
|
|
||||||
|
// comma and newlines:
|
||||||
|
n = (j > 0) + ((j > 1) & (j < rank));
|
||||||
|
if (brackets[2] && j < rank)
|
||||||
|
result[1][j++] = brackets[2]; // comma
|
||||||
|
while (n--)
|
||||||
|
result[1][j++] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[],
|
||||||
|
int rank, isize* off) {
|
||||||
|
++pos[--rank];
|
||||||
|
for (; rank && pos[rank] == shape[rank]; --rank) {
|
||||||
|
pos[rank] = 0; ++pos[rank - 1];
|
||||||
|
*off += stride[rank - 1] - (isize)shape[rank]*stride[rank];
|
||||||
|
}
|
||||||
|
return pos[rank] == shape[rank];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _istride* _cspan_shape2stride(cspan_layout layout, _istride shpstri[], int rank) {
|
||||||
|
int i, inc;
|
||||||
|
if (layout == c_COLMAJOR) i = 0, inc = 1;
|
||||||
|
else i = rank - 1, inc = -1;
|
||||||
|
_istride k = 1, s1 = shpstri[i], s2;
|
||||||
|
|
||||||
|
shpstri[i] = 1;
|
||||||
|
while (--rank) {
|
||||||
|
i += inc;
|
||||||
|
s2 = shpstri[i];
|
||||||
|
shpstri[i] = (k *= s1);
|
||||||
|
s1 = s2;
|
||||||
|
}
|
||||||
|
return shpstri;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank,
|
||||||
|
const _istride shape[], const _istride stride[],
|
||||||
|
const isize args[][3], int rank) {
|
||||||
|
isize end, off = 0;
|
||||||
|
int i = 0, oi = 0;
|
||||||
|
|
||||||
|
for (; i < rank; ++i) {
|
||||||
|
off += args[i][0]*stride[i];
|
||||||
|
switch (args[i][1]) {
|
||||||
|
case 0: c_assert(c_uless(args[i][0], shape[i])); continue;
|
||||||
|
case c_END: end = shape[i]; break;
|
||||||
|
default: end = args[i][1];
|
||||||
|
}
|
||||||
|
oshape[oi] = (_istride)(end - args[i][0]);
|
||||||
|
ostride[oi] = stride[i];
|
||||||
|
c_assert((oshape[oi] > 0) & !c_uless(shape[i], end));
|
||||||
|
if (args[i][2] > 0) {
|
||||||
|
ostride[oi] *= (_istride)args[i][2];
|
||||||
|
oshape[oi] = (oshape[oi] - 1)/(_istride)args[i][2] + 1;
|
||||||
|
}
|
||||||
|
++oi;
|
||||||
|
}
|
||||||
|
*orank = oi;
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
#endif // IMPLEMENT
|
||||||
|
#include "priv/linkage2.h"
|
||||||
51
stc/cstr.h
Normal file
51
stc/cstr.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* A string type with short string optimization in C99.
|
||||||
|
* Stores up to a 22 bytes long string inside a 24 bytes string representation (x64).
|
||||||
|
*/
|
||||||
|
#define i_header // external linkage by default. override with i_static.
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
|
||||||
|
#ifndef STC_CSTR_H_INCLUDED
|
||||||
|
#define STC_CSTR_H_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "priv/utf8_prv.h"
|
||||||
|
#include "priv/cstr_prv.h"
|
||||||
|
|
||||||
|
#endif // STC_CSTR_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined i_implement || \
|
||||||
|
defined STC_CSTR_CORE || \
|
||||||
|
defined STC_CSTR_IO || \
|
||||||
|
defined STC_CSTR_UTF8
|
||||||
|
#include "priv/cstr_prv.c"
|
||||||
|
#endif // i_implement
|
||||||
|
|
||||||
|
#if defined i_import || defined STC_CSTR_UTF8
|
||||||
|
#include "priv/utf8_prv.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "priv/linkage2.h"
|
||||||
246
stc/csview.h
Normal file
246
stc/csview.h
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// csview is a non-zero-terminated string view.
|
||||||
|
|
||||||
|
#ifndef STC_CSVIEW_H_INCLUDED
|
||||||
|
#define STC_CSVIEW_H_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "priv/utf8_prv.h"
|
||||||
|
|
||||||
|
#define csview_init() c_sv_1("")
|
||||||
|
#define csview_drop(p) c_default_drop(p)
|
||||||
|
#define csview_clone(sv) c_default_clone(sv)
|
||||||
|
|
||||||
|
csview_iter csview_advance(csview_iter it, isize u8pos);
|
||||||
|
csview csview_subview_pro(csview sv, isize pos, isize n);
|
||||||
|
csview csview_token(csview sv, const char* sep, isize* pos);
|
||||||
|
csview csview_u8_subview(csview sv, isize u8pos, isize u8len);
|
||||||
|
csview csview_u8_tail(csview sv, isize u8len);
|
||||||
|
csview_iter csview_u8_at(csview sv, isize u8pos);
|
||||||
|
|
||||||
|
STC_INLINE csview csview_from(const char* str)
|
||||||
|
{ return c_literal(csview){str, c_strlen(str)}; }
|
||||||
|
STC_INLINE csview csview_from_n(const char* str, isize n)
|
||||||
|
{ return c_literal(csview){str, n}; }
|
||||||
|
|
||||||
|
STC_INLINE void csview_clear(csview* self) { *self = csview_init(); }
|
||||||
|
STC_INLINE isize csview_size(csview sv) { return sv.size; }
|
||||||
|
STC_INLINE bool csview_is_empty(csview sv) { return sv.size == 0; }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_equals_sv(csview sv1, csview sv2)
|
||||||
|
{ return sv1.size == sv2.size && !c_memcmp(sv1.buf, sv2.buf, sv1.size); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_equals(csview sv, const char* str)
|
||||||
|
{ return csview_equals_sv(sv, c_sv_2(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE size_t csview_hash(const csview *self)
|
||||||
|
{ return c_basehash_n(self->buf, self->size); }
|
||||||
|
|
||||||
|
STC_INLINE isize csview_find_sv(csview sv, csview search) {
|
||||||
|
char* res = c_strnstrn(sv.buf, sv.size, search.buf, search.size);
|
||||||
|
return res ? (res - sv.buf) : c_NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize csview_find(csview sv, const char* str)
|
||||||
|
{ return csview_find_sv(sv, c_sv_2(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_contains(csview sv, const char* str)
|
||||||
|
{ return csview_find(sv, str) != c_NPOS; }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_starts_with(csview sv, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= sv.size && !c_memcmp(sv.buf, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool csview_ends_with(csview sv, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= sv.size && !c_memcmp(sv.buf + sv.size - n, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview csview_subview(csview sv, isize pos, isize len) {
|
||||||
|
c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0));
|
||||||
|
if (pos + len > sv.size) len = sv.size - pos;
|
||||||
|
sv.buf += pos, sv.size = len;
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview csview_slice(csview sv, isize p1, isize p2) {
|
||||||
|
c_assert(((size_t)p1 <= (size_t)p2) & ((size_t)p1 <= (size_t)sv.size));
|
||||||
|
if (p2 > sv.size) p2 = sv.size;
|
||||||
|
sv.buf += p1, sv.size = p2 - p1;
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview csview_trim_start(csview sv)
|
||||||
|
{ while (sv.size && *sv.buf <= ' ') ++sv.buf, --sv.size; return sv; }
|
||||||
|
|
||||||
|
STC_INLINE csview csview_trim_end(csview sv)
|
||||||
|
{ while (sv.size && sv.buf[sv.size - 1] <= ' ') --sv.size; return sv; }
|
||||||
|
|
||||||
|
STC_INLINE csview csview_trim(csview sv)
|
||||||
|
{ return csview_trim_end(csview_trim_start(sv)); }
|
||||||
|
|
||||||
|
STC_INLINE csview csview_tail(csview sv, isize len)
|
||||||
|
{ return csview_subview(sv, sv.size - len, len); }
|
||||||
|
|
||||||
|
/* utf8 iterator */
|
||||||
|
STC_INLINE csview_iter csview_begin(const csview* self) {
|
||||||
|
csview_iter it = {.u8 = {{self->buf, utf8_chr_size(self->buf)},
|
||||||
|
self->buf + self->size}};
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
STC_INLINE csview_iter csview_end(const csview* self) {
|
||||||
|
(void)self; csview_iter it = {0}; return it;
|
||||||
|
}
|
||||||
|
STC_INLINE void csview_next(csview_iter* it) {
|
||||||
|
it->ref += it->chr.size;
|
||||||
|
it->chr.size = utf8_chr_size(it->ref);
|
||||||
|
if (it->ref == it->u8.end) it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* utf8 */
|
||||||
|
STC_INLINE csview csview_u8_from(const char* str, isize u8pos, isize u8len)
|
||||||
|
{ return utf8_subview(str, u8pos, u8len); }
|
||||||
|
|
||||||
|
STC_INLINE isize csview_u8_size(csview sv)
|
||||||
|
{ return utf8_count_n(sv.buf, sv.size); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_u8_valid(csview sv) // requires linking with utf8 symbols
|
||||||
|
{ return utf8_valid_n(sv.buf, sv.size); }
|
||||||
|
|
||||||
|
#define c_fortoken(...) for (c_token(__VA_ARGS__)) // [deprecated]
|
||||||
|
|
||||||
|
#define c_token_sv(it, separator, sv) \
|
||||||
|
struct { csview input, token; const char* sep; isize pos; } \
|
||||||
|
it = {.input=sv, .sep=separator} ; \
|
||||||
|
it.pos <= it.input.size && (it.token = csview_token(it.input, it.sep, &it.pos)).buf ;
|
||||||
|
|
||||||
|
#define c_token(it, separator, str) \
|
||||||
|
c_token_sv(it, separator, csview_from(str))
|
||||||
|
|
||||||
|
/* ---- Container helper functions ---- */
|
||||||
|
|
||||||
|
STC_INLINE int csview_cmp(const csview* x, const csview* y) {
|
||||||
|
isize n = x->size < y->size ? x->size : y->size;
|
||||||
|
int c = c_memcmp(x->buf, y->buf, n);
|
||||||
|
return c ? c : c_default_cmp(&x->size, &y->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool csview_eq(const csview* x, const csview* y)
|
||||||
|
{ return x->size == y->size && !c_memcmp(x->buf, y->buf, x->size); }
|
||||||
|
|
||||||
|
/* ---- case insensitive ---- */
|
||||||
|
|
||||||
|
STC_INLINE bool csview_iequals_sv(csview sv1, csview sv2)
|
||||||
|
{ return sv1.size == sv2.size && !utf8_icompare(sv1, sv2); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_iequals(csview sv, const char* str)
|
||||||
|
{ return csview_iequals_sv(sv, c_sv(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_ieq(const csview* x, const csview* y)
|
||||||
|
{ return csview_iequals_sv(*x, *y); }
|
||||||
|
|
||||||
|
STC_INLINE int csview_icmp(const csview* x, const csview* y)
|
||||||
|
{ return utf8_icompare(*x, *y); }
|
||||||
|
|
||||||
|
STC_INLINE bool csview_istarts_with(csview sv, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= sv.size && !utf8_icompare(sv, c_sv(str, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool csview_iends_with(csview sv, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= sv.size && !utf8_icmp(sv.buf + sv.size - n, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_CSVIEW_H_INCLUDED
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined STC_IMPLEMENT || defined i_implement
|
||||||
|
#ifndef STC_CSVIEW_C_INCLUDED
|
||||||
|
#define STC_CSVIEW_C_INCLUDED
|
||||||
|
|
||||||
|
csview_iter csview_advance(csview_iter it, isize u8pos) {
|
||||||
|
int inc = 1;
|
||||||
|
if (u8pos < 0) u8pos = -u8pos, inc = -1;
|
||||||
|
while (u8pos && it.ref != it.u8.end)
|
||||||
|
u8pos -= (*(it.ref += inc) & 0xC0) != 0x80;
|
||||||
|
if (it.ref == it.u8.end) it.ref = NULL;
|
||||||
|
else it.chr.size = utf8_chr_size(it.ref);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
csview csview_subview_pro(csview sv, isize pos, isize len) {
|
||||||
|
if (pos < 0) {
|
||||||
|
pos += sv.size;
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
}
|
||||||
|
if (pos > sv.size) pos = sv.size;
|
||||||
|
if (pos + len > sv.size) len = sv.size - pos;
|
||||||
|
sv.buf += pos, sv.size = len;
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
csview csview_token(csview sv, const char* sep, isize* pos) {
|
||||||
|
isize sep_size = c_strlen(sep);
|
||||||
|
csview slice = {sv.buf + *pos, sv.size - *pos};
|
||||||
|
const char* res = c_strnstrn(slice.buf, slice.size, sep, sep_size);
|
||||||
|
csview tok = {slice.buf, res ? (res - slice.buf) : slice.size};
|
||||||
|
*pos += tok.size + sep_size;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
csview csview_u8_subview(csview sv, isize u8pos, isize u8len) {
|
||||||
|
const char* s, *end = &sv.buf[sv.size];
|
||||||
|
while ((u8pos > 0) & (sv.buf != end))
|
||||||
|
u8pos -= (*++sv.buf & 0xC0) != 0x80;
|
||||||
|
s = sv.buf;
|
||||||
|
while ((u8len > 0) & (s != end))
|
||||||
|
u8len -= (*++s & 0xC0) != 0x80;
|
||||||
|
sv.size = s - sv.buf; return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
csview csview_u8_tail(csview sv, isize u8len) {
|
||||||
|
const char* p = &sv.buf[sv.size];
|
||||||
|
while (u8len && p != sv.buf)
|
||||||
|
u8len -= (*--p & 0xC0) != 0x80;
|
||||||
|
sv.size -= p - sv.buf, sv.buf = p;
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
csview_iter csview_u8_at(csview sv, isize u8pos) {
|
||||||
|
const char *end = &sv.buf[sv.size];
|
||||||
|
while ((u8pos > 0) & (sv.buf != end))
|
||||||
|
u8pos -= (*++sv.buf & 0xC0) != 0x80;
|
||||||
|
sv.size = utf8_chr_size(sv.buf);
|
||||||
|
c_assert(sv.buf != end);
|
||||||
|
return c_literal(csview_iter){.u8 = {sv, end}};
|
||||||
|
}
|
||||||
|
#endif // STC_CSVIEW_C_INCLUDED
|
||||||
|
#endif // i_implement
|
||||||
|
|
||||||
|
#if defined i_import
|
||||||
|
#include "priv/utf8_prv.c"
|
||||||
|
#endif
|
||||||
205
stc/deque.h
Normal file
205
stc/deque.h
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// Deque - double ended queue. Implemented as a ring buffer, extension of queue.
|
||||||
|
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_DEQUE_H_INCLUDED
|
||||||
|
#define STC_DEQUE_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // STC_DEQUE_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix deque_
|
||||||
|
#endif
|
||||||
|
#define _pop _pop_front
|
||||||
|
#define _pull _pull_front
|
||||||
|
#include "priv/template.h"
|
||||||
|
#include "priv/queue_prv.h"
|
||||||
|
#undef _pop
|
||||||
|
#undef _pull
|
||||||
|
|
||||||
|
STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value);
|
||||||
|
STC_API _m_iter _c_MEMB(_insert_n)(Self* self, isize idx, const _m_value* arr, isize n);
|
||||||
|
STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n);
|
||||||
|
STC_API void _c_MEMB(_erase_n)(Self* self, isize idx, isize n);
|
||||||
|
|
||||||
|
STC_INLINE const _m_value*
|
||||||
|
_c_MEMB(_at)(const Self* self, isize idx) {
|
||||||
|
c_assert(c_uless(idx, _c_MEMB(_size)(self)));
|
||||||
|
return self->cbuf + _cbuf_topos(self, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value*
|
||||||
|
_c_MEMB(_at_mut)(Self* self, isize idx)
|
||||||
|
{ return (_m_value*)_c_MEMB(_at)(self, idx); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value*
|
||||||
|
_c_MEMB(_push_back)(Self* self, _m_value val)
|
||||||
|
{ return _c_MEMB(_push)(self, val); }
|
||||||
|
|
||||||
|
STC_INLINE void
|
||||||
|
_c_MEMB(_pop_back)(Self* self) {
|
||||||
|
c_assert(!_c_MEMB(_is_empty)(self));
|
||||||
|
self->end = (self->end - 1) & self->capmask;
|
||||||
|
i_keydrop((self->cbuf + self->end));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_pull_back)(Self* self) { // move back out of deque
|
||||||
|
c_assert(!_c_MEMB(_is_empty)(self));
|
||||||
|
self->end = (self->end - 1) & self->capmask;
|
||||||
|
return self->cbuf[self->end];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value val) {
|
||||||
|
isize idx = _cbuf_toidx(self, it.pos);
|
||||||
|
return _c_MEMB(_insert_n)(self, idx, &val, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_erase_at)(Self* self, _m_iter it) {
|
||||||
|
_c_MEMB(_erase_n)(self, _cbuf_toidx(self, it.pos), 1);
|
||||||
|
if (it.pos == self->end) it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) {
|
||||||
|
isize idx1 = _cbuf_toidx(self, it1.pos);
|
||||||
|
isize idx2 = _cbuf_toidx(self, it2.pos);
|
||||||
|
_c_MEMB(_erase_n)(self, idx1, idx2 - idx1);
|
||||||
|
if (it1.pos == self->end) it1.ref = NULL;
|
||||||
|
return it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_API _m_iter
|
||||||
|
_c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw* raw, isize n);
|
||||||
|
|
||||||
|
STC_INLINE _m_value*
|
||||||
|
_c_MEMB(_emplace_front)(Self* self, const _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push_front)(self, i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value*
|
||||||
|
_c_MEMB(_emplace_back)(Self* self, const _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_emplace_at)(Self* self, _m_iter it, const _m_raw raw)
|
||||||
|
{ return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter p1, _m_iter p2, _m_raw raw);
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_find)(const Self* self, _m_raw raw) {
|
||||||
|
return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw);
|
||||||
|
}
|
||||||
|
#endif // _i_has_eq
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
#include "priv/sort_prv.h"
|
||||||
|
#endif // _i_has_cmp
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push_front)(Self* self, _m_value value) {
|
||||||
|
isize start = (self->start - 1) & self->capmask;
|
||||||
|
if (start == self->end) { // full
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand
|
||||||
|
return NULL;
|
||||||
|
start = (self->start - 1) & self->capmask;
|
||||||
|
}
|
||||||
|
_m_value *v = self->cbuf + start;
|
||||||
|
self->start = start;
|
||||||
|
*v = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_erase_n)(Self* self, const isize idx, const isize n) {
|
||||||
|
const isize len = _c_MEMB(_size)(self);
|
||||||
|
c_assert(idx + n <= len);
|
||||||
|
for (isize i = idx + n - 1; i >= idx; --i)
|
||||||
|
i_keydrop(_c_MEMB(_at_mut)(self, i));
|
||||||
|
for (isize i = idx, j = i + n; j < len; ++i, ++j)
|
||||||
|
*_c_MEMB(_at_mut)(self, i) = *_c_MEMB(_at)(self, j);
|
||||||
|
self->end = (self->end - n) & self->capmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) {
|
||||||
|
const isize len = _c_MEMB(_size)(self);
|
||||||
|
_m_iter it = {._s=self};
|
||||||
|
if (len + n >= self->capmask)
|
||||||
|
if (!_c_MEMB(_reserve)(self, len + n)) // minimum 2x expand
|
||||||
|
return it;
|
||||||
|
it.pos = _cbuf_topos(self, idx);
|
||||||
|
it.ref = self->cbuf + it.pos;
|
||||||
|
self->end = (self->end + n) & self->capmask;
|
||||||
|
|
||||||
|
if (it.pos < self->end) // common case because of reserve policy
|
||||||
|
c_memmove(it.ref + n, it.ref, (len - idx)*c_sizeof *it.ref);
|
||||||
|
else for (isize i = len - 1, j = i + n; i >= idx; --i, --j)
|
||||||
|
*_c_MEMB(_at_mut)(self, j) = *_c_MEMB(_at)(self, i);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value* arr, const isize n) {
|
||||||
|
_m_iter it = _c_MEMB(_insert_uninit)(self, idx, n);
|
||||||
|
for (isize i = idx, j = 0; j < n; ++i, ++j)
|
||||||
|
*_c_MEMB(_at_mut)(self, i) = arr[j];
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw* raw, const isize n) {
|
||||||
|
_m_iter it = _c_MEMB(_insert_uninit)(self, idx, n);
|
||||||
|
for (isize i = idx, j = 0; j < n; ++i, ++j)
|
||||||
|
*_c_MEMB(_at_mut)(self, i) = i_keyfrom(raw[j]);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) {
|
||||||
|
(void)self;
|
||||||
|
for (; i1.ref != i2.ref; _c_MEMB(_next)(&i1)) {
|
||||||
|
const _m_raw r = i_keytoraw(i1.ref);
|
||||||
|
if (i_eq((&raw), (&r)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return i1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // IMPLEMENTATION
|
||||||
|
#include "sys/finalize.h"
|
||||||
43
stc/hashmap.h
Normal file
43
stc/hashmap.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvmap
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unordered map - implemented with the robin-hood hashing scheme.
|
||||||
|
/*
|
||||||
|
#define T IMap, int, int
|
||||||
|
#include <stc/hashmap.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
IMap map = c_make(IMap, {{12, 32}, {42, 54}});
|
||||||
|
IMap_insert(&map, 5, 15);
|
||||||
|
IMap_insert(&map, 8, 18);
|
||||||
|
|
||||||
|
for (c_each_kv(k, v, IMap, map))
|
||||||
|
printf(" %d -> %d\n", *k, *v);
|
||||||
|
|
||||||
|
IMap_drop(&map);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix hmap_
|
||||||
|
#include "hmap.h"
|
||||||
44
stc/hashset.h
Normal file
44
stc/hashset.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unordered set - implemented with the robin-hood hashing scheme.
|
||||||
|
/*
|
||||||
|
#define T ISet, int
|
||||||
|
#include <stc/hashset.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
ISet set = {0};
|
||||||
|
ISet_insert(&set, 5);
|
||||||
|
ISet_insert(&set, 8);
|
||||||
|
|
||||||
|
for (c_each(i, ISet, set))
|
||||||
|
printf(" %d\n", *i.ref);
|
||||||
|
|
||||||
|
ISet_drop(&set);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix hset_
|
||||||
|
#define _i_is_set
|
||||||
|
#include "hmap.h"
|
||||||
513
stc/hmap.h
Normal file
513
stc/hmap.h
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unordered set/map - implemented with the robin-hood hashing scheme.
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define T icmap, int, char
|
||||||
|
#include <stc/hashmap.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
icmap m = {0};
|
||||||
|
icmap_emplace(&m, 5, 'a');
|
||||||
|
icmap_emplace(&m, 8, 'b');
|
||||||
|
icmap_emplace(&m, 12, 'c');
|
||||||
|
|
||||||
|
icmap_value* v = icmap_get(&m, 10); // NULL
|
||||||
|
char val = *icmap_at(&m, 5); // 'a'
|
||||||
|
icmap_emplace_or_assign(&m, 5, 'd'); // update
|
||||||
|
icmap_erase(&m, 8);
|
||||||
|
|
||||||
|
for (c_each(i, icmap, m))
|
||||||
|
printf("map %d: %c\n", i.ref->first, i.ref->second);
|
||||||
|
|
||||||
|
icmap_drop(&m);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_HMAP_H_INCLUDED
|
||||||
|
#define STC_HMAP_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define _hashmask 0x3fU
|
||||||
|
#define _distmask 0x3ffU
|
||||||
|
struct hmap_meta { uint16_t hashx:6, dist:10; }; // dist: 0=empty, 1=PSL 0, 2=PSL 1, ...
|
||||||
|
#endif // STC_HMAP_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix hmap_
|
||||||
|
#endif
|
||||||
|
#ifndef _i_is_set
|
||||||
|
#define _i_is_map
|
||||||
|
#define _i_MAP_ONLY c_true
|
||||||
|
#define _i_SET_ONLY c_false
|
||||||
|
#define _i_keyref(vp) (&(vp)->first)
|
||||||
|
#else
|
||||||
|
#define _i_MAP_ONLY c_false
|
||||||
|
#define _i_SET_ONLY c_true
|
||||||
|
#define _i_keyref(vp) (vp)
|
||||||
|
#endif
|
||||||
|
#define _i_is_hash
|
||||||
|
#include "priv/template.h"
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_htable, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_i_MAP_ONLY( struct _m_value {
|
||||||
|
_m_key first;
|
||||||
|
_m_mapped second;
|
||||||
|
}; )
|
||||||
|
|
||||||
|
typedef i_keyraw _m_keyraw;
|
||||||
|
typedef i_valraw _m_rmapped;
|
||||||
|
typedef _i_SET_ONLY( i_keyraw )
|
||||||
|
_i_MAP_ONLY( struct { _m_keyraw first;
|
||||||
|
_m_rmapped second; } )
|
||||||
|
_m_raw;
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self map);
|
||||||
|
#endif
|
||||||
|
STC_API void _c_MEMB(_drop)(const Self* cself);
|
||||||
|
STC_API void _c_MEMB(_clear)(Self* self);
|
||||||
|
STC_API bool _c_MEMB(_reserve)(Self* self, isize capacity);
|
||||||
|
STC_API void _c_MEMB(_erase_entry)(Self* self, _m_value* val);
|
||||||
|
STC_API float _c_MEMB(_max_load_factor)(const Self* self);
|
||||||
|
STC_API isize _c_MEMB(_capacity)(const Self* map);
|
||||||
|
STC_API _m_result _c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr);
|
||||||
|
STC_API _m_result _c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr);
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* map) { return !map->size; }
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* map) { return (isize)map->size; }
|
||||||
|
STC_INLINE isize _c_MEMB(_bucket_count)(Self* map) { return map->bucket_count; }
|
||||||
|
STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey)
|
||||||
|
{ return self->size && _c_MEMB(_bucket_lookup_)(self, &rkey).ref; }
|
||||||
|
STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self)
|
||||||
|
{ _c_MEMB(_reserve)(self, (isize)self->size); }
|
||||||
|
|
||||||
|
#ifndef i_max_load_factor
|
||||||
|
#define i_max_load_factor 0.80f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE _m_result
|
||||||
|
_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) {
|
||||||
|
if (self->size >= (isize)((float)self->bucket_count * (i_max_load_factor)))
|
||||||
|
if (!_c_MEMB(_reserve)(self, (isize)(self->size*3/2 + 2)))
|
||||||
|
return c_literal(_m_result){0};
|
||||||
|
|
||||||
|
_m_result res = _c_MEMB(_bucket_insert_)(self, &rkey);
|
||||||
|
self->size += res.inserted;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped);
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey) {
|
||||||
|
_m_result res = _c_MEMB(_bucket_lookup_)(self, &rkey);
|
||||||
|
c_assert(res.ref);
|
||||||
|
return &res.ref->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey)
|
||||||
|
{ return (_m_mapped*)_c_MEMB(_at)(self, rkey); }
|
||||||
|
#endif // _i_is_map
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) {
|
||||||
|
if (self == other)
|
||||||
|
return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_clone)(*other);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) {
|
||||||
|
(void)self;
|
||||||
|
*_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val)));
|
||||||
|
_i_MAP_ONLY( _val.second = i_valclone(_val.second); )
|
||||||
|
return _val;
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_INLINE _m_result
|
||||||
|
_c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, rkey);
|
||||||
|
if (_res.inserted) {
|
||||||
|
*_i_keyref(_res.ref) = i_keyfrom(rkey);
|
||||||
|
_i_MAP_ONLY( _res.ref->second = i_valfrom(rmapped); )
|
||||||
|
}
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) {
|
||||||
|
return _i_SET_ONLY( i_keytoraw(val) )
|
||||||
|
_i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)), i_valtoraw((&val->second))} );
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* _val) {
|
||||||
|
(void)self;
|
||||||
|
i_keydrop(_i_keyref(_val));
|
||||||
|
_i_MAP_ONLY( i_valdrop((&_val->second)); )
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->bucket_count = self->size = 0;
|
||||||
|
self->meta = NULL; self->table = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_result
|
||||||
|
_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key)));
|
||||||
|
if (_res.inserted)
|
||||||
|
{ *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )}
|
||||||
|
else
|
||||||
|
{ i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )}
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val)));
|
||||||
|
if (_res.inserted)
|
||||||
|
*_res.ref = _val;
|
||||||
|
else
|
||||||
|
_c_MEMB(_value_drop)(self, &_val);
|
||||||
|
return _res.ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) {
|
||||||
|
#ifdef i_no_emplace
|
||||||
|
return _c_MEMB(_insert_or_assign)(self, rkey, rmapped);
|
||||||
|
#else
|
||||||
|
return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) {
|
||||||
|
while (n--)
|
||||||
|
#if defined _i_is_set && defined i_no_emplace
|
||||||
|
_c_MEMB(_insert)(self, *raw++);
|
||||||
|
#elif defined _i_is_set
|
||||||
|
_c_MEMB(_emplace)(self, *raw++);
|
||||||
|
#else
|
||||||
|
_c_MEMB(_put)(self, raw->first, raw->second), ++raw;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ Self cx = {0}; return cx; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_API _m_iter _c_MEMB(_begin)(const Self* self);
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_end)(const Self* self)
|
||||||
|
{ (void)self; return c_literal(_m_iter){0}; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_next)(_m_iter* it) {
|
||||||
|
while ((++it->ref, (++it->_mref)->dist == 0)) ;
|
||||||
|
if (it->ref == it->_end) it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) {
|
||||||
|
while (n-- && it.ref) _c_MEMB(_next)(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_find)(const Self* self, _m_keyraw rkey) {
|
||||||
|
_m_value* ref;
|
||||||
|
if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL)
|
||||||
|
return c_literal(_m_iter){ref,
|
||||||
|
&self->table[self->bucket_count],
|
||||||
|
&self->meta[ref - self->table]};
|
||||||
|
return _c_MEMB(_end)(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE const _m_value*
|
||||||
|
_c_MEMB(_get)(const Self* self, _m_keyraw rkey) {
|
||||||
|
return self->size ? _c_MEMB(_bucket_lookup_)(self, &rkey).ref : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value*
|
||||||
|
_c_MEMB(_get_mut)(Self* self, _m_keyraw rkey)
|
||||||
|
{ return (_m_value*)_c_MEMB(_get)(self, rkey); }
|
||||||
|
|
||||||
|
STC_INLINE int
|
||||||
|
_c_MEMB(_erase)(Self* self, _m_keyraw rkey) {
|
||||||
|
_m_value* ref;
|
||||||
|
if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL)
|
||||||
|
{ _c_MEMB(_erase_entry)(self, ref); return 1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_erase_at)(Self* self, _m_iter it) {
|
||||||
|
_c_MEMB(_erase_entry)(self, it.ref);
|
||||||
|
if (it._mref->dist == 0)
|
||||||
|
_c_MEMB(_next)(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool
|
||||||
|
_c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false;
|
||||||
|
for (_m_iter i = _c_MEMB(_begin)(self); i.ref; _c_MEMB(_next)(&i)) {
|
||||||
|
const _m_keyraw _raw = i_keytoraw(_i_keyref(i.ref));
|
||||||
|
if (!_c_MEMB(_contains)(other, _raw)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF _m_iter _c_MEMB(_begin)(const Self* self) {
|
||||||
|
_m_iter it = {self->table, self->table, self->meta};
|
||||||
|
if (it.ref == NULL) return it;
|
||||||
|
it._end += self->bucket_count;
|
||||||
|
while (it._mref->dist == 0)
|
||||||
|
++it.ref, ++it._mref;
|
||||||
|
if (it.ref == it._end) it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF float _c_MEMB(_max_load_factor)(const Self* self) {
|
||||||
|
(void)self; return (float)(i_max_load_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF isize _c_MEMB(_capacity)(const Self* map) {
|
||||||
|
return (isize)((float)map->bucket_count * (i_max_load_factor));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _c_MEMB(_wipe_)(Self* self) {
|
||||||
|
if (self->size == 0)
|
||||||
|
return;
|
||||||
|
_m_value* d = self->table, *_end = &d[self->bucket_count];
|
||||||
|
struct hmap_meta* m = self->meta;
|
||||||
|
for (; d != _end; ++d)
|
||||||
|
if ((m++)->dist)
|
||||||
|
_c_MEMB(_value_drop)(self, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void _c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
if (self->bucket_count > 0) {
|
||||||
|
_c_MEMB(_wipe_)(self);
|
||||||
|
_i_free_n(self->meta, self->bucket_count + 1);
|
||||||
|
_i_free_n(self->table, self->bucket_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void _c_MEMB(_clear)(Self* self) {
|
||||||
|
_c_MEMB(_wipe_)(self);
|
||||||
|
self->size = 0;
|
||||||
|
c_memset(self->meta, 0, c_sizeof(struct hmap_meta)*self->bucket_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key)));
|
||||||
|
_m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped;
|
||||||
|
if (_res.inserted)
|
||||||
|
_res.ref->first = _key;
|
||||||
|
else
|
||||||
|
{ i_keydrop((&_key)); i_valdrop(_mp); }
|
||||||
|
*_mp = _mapped;
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, rkey);
|
||||||
|
if (_res.inserted)
|
||||||
|
_res.ref->first = i_keyfrom(rkey);
|
||||||
|
else {
|
||||||
|
if (_res.ref == NULL) return _res;
|
||||||
|
i_valdrop((&_res.ref->second));
|
||||||
|
}
|
||||||
|
_res.ref->second = i_valfrom(rmapped);
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
#endif // _i_is_map
|
||||||
|
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr) {
|
||||||
|
const size_t _hash = i_hash(rkeyptr);
|
||||||
|
const size_t _idxmask = (size_t)self->bucket_count - 1;
|
||||||
|
_m_result _res = {.idx=_hash & _idxmask, .hashx=(uint8_t)((_hash >> 24) & _hashmask), .dist=1};
|
||||||
|
|
||||||
|
while (_res.dist <= self->meta[_res.idx].dist) {
|
||||||
|
if (self->meta[_res.idx].hashx == _res.hashx) {
|
||||||
|
const _m_keyraw _raw = i_keytoraw(_i_keyref(&self->table[_res.idx]));
|
||||||
|
if (i_eq((&_raw), rkeyptr)) {
|
||||||
|
_res.ref = &self->table[_res.idx];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_res.idx = (_res.idx + 1) & _idxmask;
|
||||||
|
++_res.dist;
|
||||||
|
}
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr) {
|
||||||
|
_m_result res = _c_MEMB(_bucket_lookup_)(self, rkeyptr);
|
||||||
|
if (res.ref) // bucket exists
|
||||||
|
return res;
|
||||||
|
res.ref = &self->table[res.idx];
|
||||||
|
res.inserted = true;
|
||||||
|
struct hmap_meta mnew = {.hashx=(uint16_t)(res.hashx & _hashmask),
|
||||||
|
.dist=(uint16_t)(res.dist & _distmask)};
|
||||||
|
struct hmap_meta mcur = self->meta[res.idx];
|
||||||
|
self->meta[res.idx] = mnew;
|
||||||
|
|
||||||
|
if (mcur.dist != 0) { // collision, reorder buckets
|
||||||
|
size_t mask = (size_t)self->bucket_count - 1;
|
||||||
|
_m_value dcur = *res.ref;
|
||||||
|
for (;;) {
|
||||||
|
res.idx = (res.idx + 1) & mask;
|
||||||
|
++mcur.dist;
|
||||||
|
if (self->meta[res.idx].dist == 0)
|
||||||
|
break;
|
||||||
|
if (self->meta[res.idx].dist < mcur.dist) {
|
||||||
|
c_swap(&mcur, &self->meta[res.idx]);
|
||||||
|
c_swap(&dcur, &self->table[res.idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->meta[res.idx] = mcur;
|
||||||
|
self->table[res.idx] = dcur;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_clone)(Self map) {
|
||||||
|
if (map.bucket_count == 0)
|
||||||
|
return c_literal(Self){0};
|
||||||
|
Self out = map, *self = &out; // _i_new_n may refer self via i_aux
|
||||||
|
const isize _mbytes = (map.bucket_count + 1)*c_sizeof *map.meta;
|
||||||
|
out.table = (_m_value *)i_malloc(map.bucket_count*c_sizeof *out.table);
|
||||||
|
out.meta = (struct hmap_meta *)i_malloc(_mbytes);
|
||||||
|
|
||||||
|
if (out.table && out.meta) {
|
||||||
|
c_memcpy(out.meta, map.meta, _mbytes);
|
||||||
|
for (isize i = 0; i < map.bucket_count; ++i)
|
||||||
|
if (map.meta[i].dist)
|
||||||
|
out.table[i] = _c_MEMB(_value_clone)(self, map.table[i]);
|
||||||
|
return out;
|
||||||
|
} else {
|
||||||
|
if (out.meta) i_free(out.meta, _mbytes);
|
||||||
|
if (out.table) _i_free_n(out.table, map.bucket_count);
|
||||||
|
return c_literal(Self){0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_reserve)(Self* _self, const isize _newcap) {
|
||||||
|
isize _newbucks = (isize)((float)_newcap / (i_max_load_factor)) + 4;
|
||||||
|
_newbucks = c_next_pow2(_newbucks);
|
||||||
|
|
||||||
|
if (_newcap < _self->size || _newbucks == _self->bucket_count)
|
||||||
|
return true;
|
||||||
|
Self map = *_self, *self = ↦ (void)self;
|
||||||
|
map.table = _i_new_n(_m_value, _newbucks);
|
||||||
|
map.meta = _i_new_zeros(struct hmap_meta, _newbucks + 1);
|
||||||
|
map.bucket_count = _newbucks;
|
||||||
|
|
||||||
|
bool ok = map.table && map.meta;
|
||||||
|
if (ok) { // Rehash:
|
||||||
|
map.meta[_newbucks].dist = _distmask; // end-mark for iter
|
||||||
|
const _m_value* d = _self->table;
|
||||||
|
const struct hmap_meta* m = _self->meta;
|
||||||
|
|
||||||
|
for (isize i = 0; i < _self->bucket_count; ++i, ++d) if (m[i].dist != 0) {
|
||||||
|
_m_keyraw r = i_keytoraw(_i_keyref(d));
|
||||||
|
*_c_MEMB(_bucket_insert_)(&map, &r).ref = *d; // move element
|
||||||
|
}
|
||||||
|
c_swap(_self, &map);
|
||||||
|
}
|
||||||
|
_i_free_n(map.meta, map.bucket_count + (int)(map.meta != NULL));
|
||||||
|
_i_free_n(map.table, map.bucket_count);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_erase_entry)(Self* self, _m_value* _val) {
|
||||||
|
_m_value* d = self->table;
|
||||||
|
struct hmap_meta *m = self->meta;
|
||||||
|
size_t i = (size_t)(_val - d), j = i;
|
||||||
|
size_t mask = (size_t)self->bucket_count - 1;
|
||||||
|
|
||||||
|
_c_MEMB(_value_drop)(self, _val);
|
||||||
|
for (;;) {
|
||||||
|
j = (j + 1) & mask;
|
||||||
|
if (m[j].dist < 2) // 0 => empty, 1 => PSL 0
|
||||||
|
break;
|
||||||
|
d[i] = d[j];
|
||||||
|
m[i] = m[j];
|
||||||
|
--m[i].dist;
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
m[i].dist = 0;
|
||||||
|
--self->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // i_implement
|
||||||
|
#undef i_max_load_factor
|
||||||
|
#undef _i_is_set
|
||||||
|
#undef _i_is_map
|
||||||
|
#undef _i_is_hash
|
||||||
|
#undef _i_keyref
|
||||||
|
#undef _i_MAP_ONLY
|
||||||
|
#undef _i_SET_ONLY
|
||||||
|
#include "sys/finalize.h"
|
||||||
43
stc/hset.h
Normal file
43
stc/hset.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unordered set - implemented with the robin-hood hashing scheme.
|
||||||
|
/*
|
||||||
|
#define T iset, int
|
||||||
|
#include <stc/hashset.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
iset set = {0};
|
||||||
|
iset_insert(&set, 5);
|
||||||
|
iset_insert(&set, 8);
|
||||||
|
|
||||||
|
for (c_each(i, iset, set))
|
||||||
|
printf("set %d\n", *i.ref);
|
||||||
|
iset_drop(&set);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix hset_
|
||||||
|
#define _i_is_set
|
||||||
|
#include "hmap.h"
|
||||||
425
stc/list.h
Normal file
425
stc/list.h
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Circular Singly-linked Lists.
|
||||||
|
This implements a std::forward_list-like class in C. Because it is circular,
|
||||||
|
it also support both push_back() and push_front(), unlike std::forward_list:
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/random.h>
|
||||||
|
|
||||||
|
#define T List, long, (c_use_cmp) // enable sorting, uses default *x < *y.
|
||||||
|
#include <stc/list.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
List list = {0};
|
||||||
|
|
||||||
|
for (int i = 0; i < 5000000; ++i) // five million
|
||||||
|
List_push_back(&list, crand64_uint() & (1<<24) - 1;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
for (c_each(i, List, list))
|
||||||
|
if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref);
|
||||||
|
|
||||||
|
// Sort them...
|
||||||
|
List_sort(&list); // sort.h quicksort
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
puts("sorted");
|
||||||
|
for (c_each(i, List, list))
|
||||||
|
if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref);
|
||||||
|
|
||||||
|
List_drop(&list);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_LIST_H_INCLUDED
|
||||||
|
#define STC_LIST_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define _c_list_complete_types(SELF, dummy) \
|
||||||
|
struct SELF##_node { \
|
||||||
|
SELF##_value value; /* must be first! */ \
|
||||||
|
struct SELF##_node *next; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _clist_tonode(vp) c_safe_cast(_m_node*, _m_value*, vp)
|
||||||
|
|
||||||
|
#define _c_list_insert_entry_after(ref, val) \
|
||||||
|
_m_node *entry = _i_new_n(_m_node, 1); entry->value = val; \
|
||||||
|
_c_list_insert_after_node(ref, entry)
|
||||||
|
|
||||||
|
#define _c_list_insert_after_node(ref, entry) \
|
||||||
|
if (ref) entry->next = ref->next, ref->next = entry; \
|
||||||
|
else entry->next = entry
|
||||||
|
// +: set self->last based on node
|
||||||
|
|
||||||
|
#endif // STC_LIST_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix list_
|
||||||
|
#endif
|
||||||
|
#include "priv/template.h"
|
||||||
|
|
||||||
|
#define _i_is_list
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_list, Self, i_key, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
_c_DEFTYPES(_c_list_complete_types, Self, dummy);
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
STC_API void _c_MEMB(_drop)(const Self* cself);
|
||||||
|
STC_API _m_value* _c_MEMB(_push_back)(Self* self, _m_value value);
|
||||||
|
STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value);
|
||||||
|
STC_API _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value);
|
||||||
|
STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it);
|
||||||
|
STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2);
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val);
|
||||||
|
STC_API isize _c_MEMB(_remove)(Self* self, _m_raw val);
|
||||||
|
#endif
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
STC_API bool _c_MEMB(_sort)(Self* self);
|
||||||
|
#endif
|
||||||
|
STC_API void _c_MEMB(_reverse)(Self* self);
|
||||||
|
STC_API _m_iter _c_MEMB(_splice)(Self* self, _m_iter it, Self* other);
|
||||||
|
STC_API Self _c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2);
|
||||||
|
STC_API _m_value* _c_MEMB(_push_back_node)(Self* self, _m_node* node);
|
||||||
|
STC_API _m_value* _c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node);
|
||||||
|
STC_API _m_node* _c_MEMB(_unlink_after_node)(Self* self, _m_node* ref);
|
||||||
|
STC_API void _c_MEMB(_erase_after_node)(Self* self, _m_node* ref);
|
||||||
|
STC_INLINE _m_node* _c_MEMB(_get_node)(_m_value* pval) { return _clist_tonode(pval); }
|
||||||
|
STC_INLINE _m_node* _c_MEMB(_unlink_front_node)(Self* self)
|
||||||
|
{ return _c_MEMB(_unlink_after_node)(self, self->last); }
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self cx);
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val)
|
||||||
|
{ (void)self; return i_keyclone(val); }
|
||||||
|
|
||||||
|
STC_INLINE void
|
||||||
|
_c_MEMB(_copy)(Self *self, const Self* other) {
|
||||||
|
if (self->last == other->last) return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_clone)(*other);
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push_back)(self, i_keyfrom(raw)); }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace_front)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push_front)(self, i_keyfrom(raw)); }
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push_back)(self, i_keyfrom(raw)); }
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n)
|
||||||
|
{ while (n--) _c_MEMB(_push_back)(self, i_keyfrom(*raw++)); }
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void) { return c_literal(Self){0}; }
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; }
|
||||||
|
#endif
|
||||||
|
STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) { (void)(self + n); return true; }
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->last == NULL; }
|
||||||
|
STC_INLINE void _c_MEMB(_clear)(Self* self) { _c_MEMB(_drop)(self); }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value)
|
||||||
|
{ return _c_MEMB(_push_back)(self, value); }
|
||||||
|
STC_INLINE void _c_MEMB(_pop_front)(Self* self)
|
||||||
|
{ c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_after_node)(self, self->last); }
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return &self->last->next->value; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return &self->last->next->value; }
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->last->value; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->last->value; }
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval) { return i_keytoraw(pval); }
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* pval) { (void)self; i_keydrop(pval); }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->last = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize
|
||||||
|
_c_MEMB(_count)(const Self* self) {
|
||||||
|
isize n = 1; const _m_node *node = self->last;
|
||||||
|
if (node == NULL) return 0;
|
||||||
|
while ((node = node->next) != self->last) ++n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_begin)(const Self* self) {
|
||||||
|
_m_value* head = self->last ? &self->last->next->value : NULL;
|
||||||
|
return c_literal(_m_iter){head, &self->last, self->last};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_end)(const Self* self)
|
||||||
|
{ (void)self; return c_literal(_m_iter){0}; }
|
||||||
|
|
||||||
|
STC_INLINE void
|
||||||
|
_c_MEMB(_next)(_m_iter* it) {
|
||||||
|
_m_node* node = it->prev = _clist_tonode(it->ref);
|
||||||
|
it->ref = (node == *it->_last ? NULL : &node->next->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_advance)(_m_iter it, size_t n) {
|
||||||
|
while (n-- && it.ref) _c_MEMB(_next)(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_splice_range)(Self* self, _m_iter it,
|
||||||
|
Self* other, _m_iter it1, _m_iter it2) {
|
||||||
|
Self tmp = _c_MEMB(_split_off)(other, it1, it2);
|
||||||
|
return _c_MEMB(_splice)(self, it, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_find)(const Self* self, _m_raw val) {
|
||||||
|
return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
_m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other);
|
||||||
|
for (; i.ref && j.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) {
|
||||||
|
const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref);
|
||||||
|
if (!(i_eq((&_rx), (&_ry)))) return false;
|
||||||
|
}
|
||||||
|
return !(i.ref || j.ref);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -------------------------- IMPLEMENTATION -------------------------
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_clone)(Self lst) {
|
||||||
|
Self out = lst, *self = &out; (void)self; // may be used by i_keyclone via i_aux
|
||||||
|
out.last = NULL;
|
||||||
|
for (c_each(it, Self, lst))
|
||||||
|
_c_MEMB(_push_back)(&out, i_keyclone((*it.ref)));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
while (self->last)
|
||||||
|
_c_MEMB(_erase_after_node)(self, self->last);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push_back)(Self* self, _m_value value) {
|
||||||
|
_c_list_insert_entry_after(self->last, value);
|
||||||
|
self->last = entry;
|
||||||
|
return &entry->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push_front)(Self* self, _m_value value) {
|
||||||
|
_c_list_insert_entry_after(self->last, value);
|
||||||
|
if (self->last == NULL)
|
||||||
|
self->last = entry;
|
||||||
|
return &entry->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push_back_node)(Self* self, _m_node* node) {
|
||||||
|
_c_list_insert_after_node(self->last, node);
|
||||||
|
self->last = node;
|
||||||
|
return &node->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node) {
|
||||||
|
_c_list_insert_after_node(ref, node);
|
||||||
|
if (self->last == NULL)
|
||||||
|
self->last = node;
|
||||||
|
return &node->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value) {
|
||||||
|
_m_node* node = it.ref ? it.prev : self->last;
|
||||||
|
_c_list_insert_entry_after(node, value);
|
||||||
|
if (self->last == NULL || it.ref == NULL) {
|
||||||
|
it.prev = self->last ? self->last : entry;
|
||||||
|
self->last = entry;
|
||||||
|
}
|
||||||
|
it.ref = &entry->value;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_erase_at)(Self* self, _m_iter it) {
|
||||||
|
_m_node *node = _clist_tonode(it.ref);
|
||||||
|
it.ref = (node == self->last) ? NULL : &node->next->value;
|
||||||
|
_c_MEMB(_erase_after_node)(self, it.prev);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) {
|
||||||
|
_m_node *end = it2.ref ? _clist_tonode(it2.ref) : self->last->next;
|
||||||
|
if (it1.ref != it2.ref) do {
|
||||||
|
_c_MEMB(_erase_after_node)(self, it1.prev);
|
||||||
|
if (self->last == NULL) break;
|
||||||
|
} while (it1.prev->next != end);
|
||||||
|
return it2;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_erase_after_node)(Self* self, _m_node* ref) {
|
||||||
|
_m_node* node = _c_MEMB(_unlink_after_node)(self, ref);
|
||||||
|
i_keydrop((&node->value));
|
||||||
|
_i_free_n(node, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_node*
|
||||||
|
_c_MEMB(_unlink_after_node)(Self* self, _m_node* ref) {
|
||||||
|
_m_node* node = ref->next, *next = node->next;
|
||||||
|
ref->next = next;
|
||||||
|
if (node == next)
|
||||||
|
self->last = NULL;
|
||||||
|
else if (node == self->last)
|
||||||
|
self->last = ref;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_reverse)(Self* self) {
|
||||||
|
Self rev = *self;
|
||||||
|
rev.last = NULL;
|
||||||
|
while (self->last) {
|
||||||
|
_m_node* node = _c_MEMB(_unlink_after_node)(self, self->last);
|
||||||
|
_c_MEMB(_insert_after_node)(&rev, rev.last, node);
|
||||||
|
}
|
||||||
|
*self = rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_splice)(Self* self, _m_iter it, Self* other) {
|
||||||
|
if (self->last == NULL)
|
||||||
|
self->last = other->last;
|
||||||
|
else if (other->last) {
|
||||||
|
_m_node *p = it.ref ? it.prev : self->last, *next = p->next;
|
||||||
|
it.prev = other->last;
|
||||||
|
p->next = it.prev->next;
|
||||||
|
it.prev->next = next;
|
||||||
|
if (it.ref == NULL) self->last = it.prev;
|
||||||
|
}
|
||||||
|
other->last = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2) {
|
||||||
|
Self lst = *self;
|
||||||
|
lst.last = NULL;
|
||||||
|
if (it1.ref == it2.ref)
|
||||||
|
return lst;
|
||||||
|
_m_node *p1 = it1.prev,
|
||||||
|
*p2 = it2.ref ? it2.prev : self->last;
|
||||||
|
p1->next = p2->next;
|
||||||
|
p2->next = _clist_tonode(it1.ref);
|
||||||
|
if (self->last == p2)
|
||||||
|
self->last = (p1 == p2) ? NULL : p1;
|
||||||
|
lst.last = p2;
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val) {
|
||||||
|
(void)self;
|
||||||
|
for (c_each(it, Self, it1, it2)) {
|
||||||
|
_m_raw r = i_keytoraw(it.ref);
|
||||||
|
if (i_eq((&r), (&val)))
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
it2.ref = NULL; return it2;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF isize
|
||||||
|
_c_MEMB(_remove)(Self* self, _m_raw val) {
|
||||||
|
isize n = 0;
|
||||||
|
_m_node *prev = self->last, *node;
|
||||||
|
if (prev) do {
|
||||||
|
node = prev->next;
|
||||||
|
_m_raw r = i_keytoraw((&node->value));
|
||||||
|
if (i_eq((&r), (&val))) {
|
||||||
|
_c_MEMB(_erase_after_node)(self, prev), ++n;
|
||||||
|
if (self->last == NULL) break;
|
||||||
|
} else
|
||||||
|
prev = node;
|
||||||
|
} while (node != self->last);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
#include "priv/sort_prv.h"
|
||||||
|
|
||||||
|
STC_DEF bool _c_MEMB(_sort)(Self* self) {
|
||||||
|
isize len = 0, cap = 0;
|
||||||
|
_m_value *arr = NULL, *p = NULL;
|
||||||
|
_m_node* keep;
|
||||||
|
for (c_each(i, Self, *self)) {
|
||||||
|
if (len == cap) {
|
||||||
|
isize cap_n = cap + cap/2 + 8;
|
||||||
|
if ((p = (_m_value *)_i_realloc_n(arr, cap, cap_n)) == NULL)
|
||||||
|
goto done;
|
||||||
|
arr = p, cap = cap_n;
|
||||||
|
}
|
||||||
|
arr[len++] = *i.ref;
|
||||||
|
}
|
||||||
|
keep = self->last;
|
||||||
|
self->last = (_m_node *)arr;
|
||||||
|
_c_MEMB(_sort_lowhigh)(self, 0, len - 1);
|
||||||
|
self->last = keep;
|
||||||
|
for (c_each(i, Self, *self))
|
||||||
|
*i.ref = *p++;
|
||||||
|
done: _i_free_n(arr, cap);
|
||||||
|
return p != NULL;
|
||||||
|
}
|
||||||
|
#endif // _i_has_cmp
|
||||||
|
#endif // i_implement
|
||||||
|
#undef _i_is_list
|
||||||
|
#include "sys/finalize.h"
|
||||||
181
stc/pqueue.h
Normal file
181
stc/pqueue.h
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_PQUEUE_H_INCLUDED
|
||||||
|
#define STC_PQUEUE_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // STC_PQUEUIE_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix pqueue_
|
||||||
|
#endif
|
||||||
|
#define _i_sorted
|
||||||
|
#include "priv/template.h"
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
STC_API void _c_MEMB(_make_heap)(Self* self);
|
||||||
|
STC_API void _c_MEMB(_erase_at)(Self* self, isize idx);
|
||||||
|
STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value);
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n)
|
||||||
|
{ while (n--) _c_MEMB(_push)(self, i_keyfrom(*raw++)); }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_reserve)(Self* self, const isize cap) {
|
||||||
|
if (cap != self->size && cap <= self->capacity) return true;
|
||||||
|
_m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, cap);
|
||||||
|
return d ? (self->data = d, self->capacity = cap, true) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self)
|
||||||
|
{ _c_MEMB(_reserve)(self, self->size); }
|
||||||
|
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ return c_literal(Self){0}; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_clear)(Self* self) {
|
||||||
|
isize i = self->size; self->size = 0;
|
||||||
|
while (i--) { i_keydrop((self->data + i)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
_i_free_n(self->data, self->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->size = self->capacity = 0;
|
||||||
|
self->data = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* q)
|
||||||
|
{ return q->size; }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* q)
|
||||||
|
{ return !q->size; }
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* q)
|
||||||
|
{ return q->capacity; }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self)
|
||||||
|
{ return &self->data[0]; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_pop)(Self* self)
|
||||||
|
{ c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_at)(self, 0); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_pull)(Self* self)
|
||||||
|
{ _m_value v = self->data[0]; _c_MEMB(_erase_at)(self, 0); return v; }
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self q);
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) {
|
||||||
|
if (self == other) return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_clone)(*other);
|
||||||
|
}
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val)
|
||||||
|
{ (void)self; return i_keyclone(val); }
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_INLINE void _c_MEMB(_emplace)(Self* self, _m_raw raw)
|
||||||
|
{ _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_sift_down_)(Self* self, const isize idx, const isize n) {
|
||||||
|
_m_value t, *arr = self->data - 1;
|
||||||
|
for (isize r = idx, c = idx*2; c <= n; c *= 2) {
|
||||||
|
c += i_less((&arr[c]), (&arr[c + (c < n)]));
|
||||||
|
if (!(i_less((&arr[r]), (&arr[c])))) return;
|
||||||
|
t = arr[r], arr[r] = arr[c], arr[r = c] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_make_heap)(Self* self) {
|
||||||
|
isize n = self->size;
|
||||||
|
for (isize k = n/2; k != 0; --k)
|
||||||
|
_c_MEMB(_sift_down_)(self, k, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF Self _c_MEMB(_clone)(Self q) {
|
||||||
|
Self out = q, *self = &out; (void)self;
|
||||||
|
out.capacity = out.size = 0; out.data = NULL;
|
||||||
|
_c_MEMB(_reserve)(&out, q.size);
|
||||||
|
out.size = q.size;
|
||||||
|
for (c_range(i, q.size))
|
||||||
|
out.data[i] = i_keyclone(q.data[i]);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_erase_at)(Self* self, const isize idx) {
|
||||||
|
i_keydrop((self->data + idx));
|
||||||
|
const isize n = --self->size;
|
||||||
|
self->data[idx] = self->data[n];
|
||||||
|
_c_MEMB(_sift_down_)(self, idx + 1, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push)(Self* self, _m_value value) {
|
||||||
|
if (self->size == self->capacity)
|
||||||
|
_c_MEMB(_reserve)(self, self->size*3/2 + 4);
|
||||||
|
_m_value *arr = self->data - 1; /* base 1 */
|
||||||
|
isize c = ++self->size;
|
||||||
|
for (; c > 1 && (i_less((&arr[c/2]), (&value))); c /= 2)
|
||||||
|
arr[c] = arr[c/2];
|
||||||
|
arr[c] = value;
|
||||||
|
return arr + c;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef _i_sorted
|
||||||
|
#include "sys/finalize.h"
|
||||||
1340
stc/priv/cregex_prv.c
Normal file
1340
stc/priv/cregex_prv.c
Normal file
File diff suppressed because it is too large
Load Diff
291
stc/priv/cstr_prv.c
Normal file
291
stc/priv/cstr_prv.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// ------------------- STC_CSTR_CORE --------------------
|
||||||
|
#if !defined STC_CSTR_CORE_C_INCLUDED && \
|
||||||
|
(defined i_implement || defined STC_CSTR_CORE)
|
||||||
|
#define STC_CSTR_CORE_C_INCLUDED
|
||||||
|
|
||||||
|
void cstr_drop(const cstr* self) {
|
||||||
|
if (cstr_is_long(self))
|
||||||
|
cstr_l_drop(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr* cstr_take(cstr* self, const cstr s) {
|
||||||
|
if (cstr_is_long(self) && self->lon.data != s.lon.data)
|
||||||
|
cstr_l_drop(self);
|
||||||
|
*self = s;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cstr_hash(const cstr *self) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
return c_hash_str(sv.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
isize cstr_find_sv(const cstr* self, csview search) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
char* res = c_strnstrn(sv.buf, sv.size, search.buf, search.size);
|
||||||
|
return res ? (res - sv.buf) : c_NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* _cstr_internal_move(cstr* self, const isize pos1, const isize pos2) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (pos1 != pos2) {
|
||||||
|
const isize newlen = (b.size + pos2 - pos1);
|
||||||
|
if (newlen > b.cap)
|
||||||
|
b.data = cstr_reserve(self, b.size*3/2 + pos2 - pos1);
|
||||||
|
c_memmove(&b.data[pos2], &b.data[pos1], b.size - pos1);
|
||||||
|
_cstr_set_size(self, newlen);
|
||||||
|
}
|
||||||
|
return b.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* _cstr_init(cstr* self, const isize len, const isize cap) {
|
||||||
|
if (cap > cstr_s_cap) {
|
||||||
|
self->lon.data = (char *)c_malloc(cap + 1);
|
||||||
|
cstr_l_set_size(self, len);
|
||||||
|
cstr_l_set_cap(self, cap);
|
||||||
|
return self->lon.data;
|
||||||
|
}
|
||||||
|
cstr_s_set_size(self, len);
|
||||||
|
return self->sml.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cstr_reserve(cstr* self, const isize cap) {
|
||||||
|
if (cstr_is_long(self)) {
|
||||||
|
if (cap > cstr_l_cap(self)) {
|
||||||
|
self->lon.data = (char *)c_realloc(self->lon.data, cstr_l_cap(self) + 1, cap + 1);
|
||||||
|
cstr_l_set_cap(self, cap);
|
||||||
|
}
|
||||||
|
return self->lon.data;
|
||||||
|
}
|
||||||
|
/* from short to long: */
|
||||||
|
if (cap > cstr_s_cap) {
|
||||||
|
char* data = (char *)c_malloc(cap + 1);
|
||||||
|
const isize len = cstr_s_size(self);
|
||||||
|
/* copy full short buffer to emulate realloc() */
|
||||||
|
c_memcpy(data, self->sml.data, c_sizeof self->sml);
|
||||||
|
self->lon.data = data;
|
||||||
|
self->lon.size = (size_t)len;
|
||||||
|
cstr_l_set_cap(self, cap);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return self->sml.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cstr_resize(cstr* self, const isize size, const char value) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (size > b.size) {
|
||||||
|
if (size > b.cap && (b.data = cstr_reserve(self, size)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
c_memset(b.data + b.size, value, size - b.size);
|
||||||
|
}
|
||||||
|
_cstr_set_size(self, size);
|
||||||
|
return b.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize cstr_find_at(const cstr* self, const isize pos, const char* search) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
if (pos > sv.size) return c_NPOS;
|
||||||
|
const char* res = strstr((char*)sv.buf + pos, search);
|
||||||
|
return res ? (res - sv.buf) : c_NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cstr_assign_n(cstr* self, const char* str, const isize len) {
|
||||||
|
char* d = cstr_reserve(self, len);
|
||||||
|
if (d) { _cstr_set_size(self, len); c_memmove(d, str, len); }
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cstr_append_n(cstr* self, const char* str, const isize len) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (b.size + len > b.cap) {
|
||||||
|
const size_t off = (size_t)(str - b.data);
|
||||||
|
b.data = cstr_reserve(self, b.size*3/2 + len);
|
||||||
|
if (b.data == NULL) return NULL;
|
||||||
|
if (off <= (size_t)b.size) str = b.data + off; /* handle self append */
|
||||||
|
}
|
||||||
|
c_memcpy(b.data + b.size, str, len);
|
||||||
|
_cstr_set_size(self, b.size + len);
|
||||||
|
return b.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr cstr_from_replace(csview in, csview search, csview repl, int32_t count) {
|
||||||
|
cstr out = cstr_init();
|
||||||
|
isize from = 0; char* res;
|
||||||
|
if (count == 0) count = INT32_MAX;
|
||||||
|
if (search.size)
|
||||||
|
while (count-- && (res = c_strnstrn(in.buf + from, in.size - from, search.buf, search.size))) {
|
||||||
|
const isize pos = (res - in.buf);
|
||||||
|
cstr_append_n(&out, in.buf + from, pos - from);
|
||||||
|
cstr_append_n(&out, repl.buf, repl.size);
|
||||||
|
from = pos + search.size;
|
||||||
|
}
|
||||||
|
cstr_append_n(&out, in.buf + from, in.size - from);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cstr_erase(cstr* self, const isize pos, isize len) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (len > b.size - pos) len = b.size - pos;
|
||||||
|
c_memmove(&b.data[pos], &b.data[pos + len], b.size - (pos + len));
|
||||||
|
_cstr_set_size(self, b.size - len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cstr_shrink_to_fit(cstr* self) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (b.size == b.cap)
|
||||||
|
return;
|
||||||
|
if (b.size > cstr_s_cap) {
|
||||||
|
self->lon.data = (char *)c_realloc(self->lon.data, cstr_l_cap(self) + 1, b.size + 1);
|
||||||
|
cstr_l_set_cap(self, b.size);
|
||||||
|
} else if (b.cap > cstr_s_cap) {
|
||||||
|
c_memcpy(self->sml.data, b.data, b.size + 1);
|
||||||
|
cstr_s_set_size(self, b.size);
|
||||||
|
c_free(b.data, b.cap + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STC_CSTR_CORE_C_INCLUDED
|
||||||
|
|
||||||
|
// ------------------- STC_CSTR_IO --------------------
|
||||||
|
#if !defined STC_CSTR_IO_C_INCLUDED && \
|
||||||
|
(defined i_import || defined STC_CSTR_IO)
|
||||||
|
#define STC_CSTR_IO_C_INCLUDED
|
||||||
|
|
||||||
|
char* cstr_append_uninit(cstr *self, isize len) {
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
if (b.size + len > b.cap && (b.data = cstr_reserve(self, b.size*3/2 + len)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
_cstr_set_size(self, b.size + len);
|
||||||
|
return b.data + b.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cstr_getdelim(cstr *self, const int delim, FILE *fp) {
|
||||||
|
int c = fgetc(fp);
|
||||||
|
if (c == EOF)
|
||||||
|
return false;
|
||||||
|
isize pos = 0;
|
||||||
|
cstr_buf b = cstr_getbuf(self);
|
||||||
|
for (;;) {
|
||||||
|
if (c == delim || c == EOF) {
|
||||||
|
_cstr_set_size(self, pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pos == b.cap) {
|
||||||
|
_cstr_set_size(self, pos);
|
||||||
|
char* data = cstr_reserve(self, (b.cap = b.cap*3/2 + 16));
|
||||||
|
b.data = data;
|
||||||
|
}
|
||||||
|
b.data[pos++] = (char) c;
|
||||||
|
c = fgetc(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isize cstr_vfmt(cstr* self, isize start, const char* fmt, va_list args) {
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args);
|
||||||
|
const int n = vsnprintf(NULL, 0ULL, fmt, args);
|
||||||
|
vsnprintf(cstr_reserve(self, start + n) + start, (size_t)n+1, fmt, args2);
|
||||||
|
va_end(args2);
|
||||||
|
_cstr_set_size(self, start + n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr cstr_from_fmt(const char* fmt, ...) {
|
||||||
|
cstr s = cstr_init();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
cstr_vfmt(&s, 0, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize cstr_append_fmt(cstr* self, const char* fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
const isize n = cstr_vfmt(self, cstr_size(self), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NB! self-data in args is UB */
|
||||||
|
isize cstr_printf(cstr* self, const char* fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
const isize n = cstr_vfmt(self, 0, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
#endif // STC_CSTR_IO_C_INCLUDED
|
||||||
|
|
||||||
|
// ------------------- STC_CSTR_UTF8 --------------------
|
||||||
|
#if !defined STC_CSTR_UTF8_C_INCLUDED && \
|
||||||
|
(defined i_import || defined STC_CSTR_UTF8 || defined STC_UTF8_PRV_C_INCLUDED)
|
||||||
|
#define STC_CSTR_UTF8_C_INCLUDED
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
void cstr_u8_erase(cstr* self, const isize u8pos, const isize u8len) {
|
||||||
|
csview b = cstr_sv(self);
|
||||||
|
csview span = utf8_subview(b.buf, u8pos, u8len);
|
||||||
|
c_memmove((void *)&span.buf[0], &span.buf[span.size], b.size - span.size - (span.buf - b.buf));
|
||||||
|
_cstr_set_size(self, b.size - span.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cstr_u8_valid(const cstr* self)
|
||||||
|
{ return utf8_valid(cstr_str(self)); }
|
||||||
|
|
||||||
|
static int toLower(int c)
|
||||||
|
{ return c >= 'A' && c <= 'Z' ? c + 32 : c; }
|
||||||
|
static int toUpper(int c)
|
||||||
|
{ return c >= 'a' && c <= 'z' ? c - 32 : c; }
|
||||||
|
static struct {
|
||||||
|
int (*conv_asc)(int);
|
||||||
|
uint32_t (*conv_utf)(uint32_t);
|
||||||
|
}
|
||||||
|
fn_tocase[] = {{toLower, utf8_casefold},
|
||||||
|
{toLower, utf8_tolower},
|
||||||
|
{toUpper, utf8_toupper}};
|
||||||
|
|
||||||
|
cstr cstr_tocase_sv(csview sv, int k) {
|
||||||
|
cstr out = {0};
|
||||||
|
char *buf = cstr_reserve(&out, sv.size*3/2);
|
||||||
|
isize sz = 0;
|
||||||
|
utf8_decode_t d = {.state=0};
|
||||||
|
const char* end = sv.buf + sv.size;
|
||||||
|
|
||||||
|
while (sv.buf < end) {
|
||||||
|
sv.buf += utf8_decode_codepoint(&d, sv.buf, end);
|
||||||
|
|
||||||
|
if (d.codep < 0x80)
|
||||||
|
buf[sz++] = (char)fn_tocase[k].conv_asc((int)d.codep);
|
||||||
|
else {
|
||||||
|
uint32_t cp = fn_tocase[k].conv_utf(d.codep);
|
||||||
|
sz += utf8_encode(buf + sz, cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_cstr_set_size(&out, sz);
|
||||||
|
cstr_shrink_to_fit(&out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif // i_import STC_CSTR_UTF8_C_INCLUDED
|
||||||
420
stc/priv/cstr_prv.h
Normal file
420
stc/priv/cstr_prv.h
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private, include "stc/cstr.h"
|
||||||
|
#ifndef STC_CSTR_PRV_H_INCLUDED
|
||||||
|
#define STC_CSTR_PRV_H_INCLUDED
|
||||||
|
|
||||||
|
#include <stdio.h> /* FILE*, vsnprintf */
|
||||||
|
#include <stdlib.h> /* malloc */
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
#include <stdarg.h> /* cstr_vfmt() */
|
||||||
|
/**************************** PRIVATE API **********************************/
|
||||||
|
|
||||||
|
#if defined __GNUC__ && !defined __clang__
|
||||||
|
// linkage.h already does diagnostic push
|
||||||
|
// Warns wrongfully on -O3 on cstr_assign(&str, "literal longer than 23 ...");
|
||||||
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum { cstr_s_cap = sizeof(cstr_buf) - 2 };
|
||||||
|
#define cstr_s_size(s) ((isize)(s)->sml.size)
|
||||||
|
#define cstr_s_set_size(s, len) ((s)->sml.data[(s)->sml.size = (uint8_t)(len)] = 0)
|
||||||
|
#define cstr_s_data(s) (s)->sml.data
|
||||||
|
|
||||||
|
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#define byte_rotl_(x, b) ((x) << (b)*8 | (x) >> (sizeof(x) - (b))*8)
|
||||||
|
#define cstr_l_cap(s) (isize)(~byte_rotl_((s)->lon.ncap, sizeof((s)->lon.ncap) - 1))
|
||||||
|
#define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~byte_rotl_((uintptr_t)(cap), 1))
|
||||||
|
#else
|
||||||
|
#define cstr_l_cap(s) (isize)(~(s)->lon.ncap)
|
||||||
|
#define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~(uintptr_t)(cap))
|
||||||
|
#endif
|
||||||
|
#define cstr_l_size(s) (isize)((s)->lon.size)
|
||||||
|
#define cstr_l_set_size(s, len) ((s)->lon.data[(s)->lon.size = (uintptr_t)(len)] = 0)
|
||||||
|
#define cstr_l_data(s) (s)->lon.data
|
||||||
|
#define cstr_l_drop(s) c_free((s)->lon.data, cstr_l_cap(s) + 1)
|
||||||
|
|
||||||
|
#define cstr_is_long(s) ((s)->sml.size >= 128)
|
||||||
|
extern char* _cstr_init(cstr* self, isize len, isize cap);
|
||||||
|
extern char* _cstr_internal_move(cstr* self, isize pos1, isize pos2);
|
||||||
|
|
||||||
|
/**************************** PUBLIC API **********************************/
|
||||||
|
|
||||||
|
#define cstr_init() (c_literal(cstr){0})
|
||||||
|
#define cstr_lit(literal) cstr_from_n(literal, c_litstrlen(literal))
|
||||||
|
|
||||||
|
extern cstr cstr_from_replace(csview sv, csview search, csview repl, int32_t count);
|
||||||
|
extern cstr cstr_from_fmt(const char* fmt, ...) c_GNUATTR(format(printf, 1, 2));
|
||||||
|
|
||||||
|
extern void cstr_drop(const cstr* self);
|
||||||
|
extern cstr* cstr_take(cstr* self, const cstr s);
|
||||||
|
extern char* cstr_reserve(cstr* self, isize cap);
|
||||||
|
extern void cstr_shrink_to_fit(cstr* self);
|
||||||
|
extern char* cstr_resize(cstr* self, isize size, char value);
|
||||||
|
extern isize cstr_find_at(const cstr* self, isize pos, const char* search);
|
||||||
|
extern isize cstr_find_sv(const cstr* self, csview search);
|
||||||
|
extern char* cstr_assign_n(cstr* self, const char* str, isize len);
|
||||||
|
extern char* cstr_append_n(cstr* self, const char* str, isize len);
|
||||||
|
extern isize cstr_append_fmt(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3));
|
||||||
|
extern char* cstr_append_uninit(cstr *self, isize len);
|
||||||
|
|
||||||
|
extern bool cstr_getdelim(cstr *self, int delim, FILE *fp);
|
||||||
|
extern void cstr_erase(cstr* self, isize pos, isize len);
|
||||||
|
extern isize cstr_printf(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3));
|
||||||
|
extern isize cstr_vfmt(cstr* self, isize start, const char* fmt, va_list args);
|
||||||
|
extern size_t cstr_hash(const cstr *self);
|
||||||
|
extern bool cstr_u8_valid(const cstr* self);
|
||||||
|
extern void cstr_u8_erase(cstr* self, isize u8pos, isize u8len);
|
||||||
|
|
||||||
|
STC_INLINE cstr_buf cstr_getbuf(cstr* s) {
|
||||||
|
return cstr_is_long(s) ? c_literal(cstr_buf){s->lon.data, cstr_l_size(s), cstr_l_cap(s)}
|
||||||
|
: c_literal(cstr_buf){s->sml.data, cstr_s_size(s), cstr_s_cap};
|
||||||
|
}
|
||||||
|
STC_INLINE zsview cstr_zv(const cstr* s) {
|
||||||
|
return cstr_is_long(s) ? c_literal(zsview){s->lon.data, cstr_l_size(s)}
|
||||||
|
: c_literal(zsview){s->sml.data, cstr_s_size(s)};
|
||||||
|
}
|
||||||
|
STC_INLINE csview cstr_sv(const cstr* s) {
|
||||||
|
return cstr_is_long(s) ? c_literal(csview){s->lon.data, cstr_l_size(s)}
|
||||||
|
: c_literal(csview){s->sml.data, cstr_s_size(s)};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_from_n(const char* str, const isize len) {
|
||||||
|
cstr s;
|
||||||
|
c_memcpy(_cstr_init(&s, len, len), str, len);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_from(const char* str)
|
||||||
|
{ return cstr_from_n(str, c_strlen(str)); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_from_sv(csview sv)
|
||||||
|
{ return cstr_from_n(sv.buf, sv.size); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_from_zv(zsview zv)
|
||||||
|
{ return cstr_from_n(zv.str, zv.size); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_with_size(const isize size, const char value) {
|
||||||
|
cstr s;
|
||||||
|
c_memset(_cstr_init(&s, size, size), value, size);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_with_capacity(const isize cap) {
|
||||||
|
cstr s;
|
||||||
|
_cstr_init(&s, 0, cap);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_move(cstr* self) {
|
||||||
|
cstr tmp = *self;
|
||||||
|
*self = cstr_init();
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_clone(cstr s) {
|
||||||
|
csview sv = cstr_sv(&s);
|
||||||
|
return cstr_from_n(sv.buf, sv.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SSO_CALL(s, call) (cstr_is_long(s) ? cstr_l_##call : cstr_s_##call)
|
||||||
|
|
||||||
|
STC_INLINE void _cstr_set_size(cstr* self, isize len)
|
||||||
|
{ SSO_CALL(self, set_size(self, len)); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_clear(cstr* self)
|
||||||
|
{ _cstr_set_size(self, 0); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_data(cstr* self)
|
||||||
|
{ return SSO_CALL(self, data(self)); }
|
||||||
|
|
||||||
|
STC_INLINE const char* cstr_str(const cstr* self)
|
||||||
|
{ return SSO_CALL(self, data(self)); }
|
||||||
|
|
||||||
|
STC_INLINE const char* cstr_toraw(const cstr* self)
|
||||||
|
{ return SSO_CALL(self, data(self)); }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_size(const cstr* self)
|
||||||
|
{ return SSO_CALL(self, size(self)); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_is_empty(const cstr* self)
|
||||||
|
{ return cstr_size(self) == 0; }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_capacity(const cstr* self)
|
||||||
|
{ return cstr_is_long(self) ? cstr_l_cap(self) : cstr_s_cap; }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_to_index(const cstr* self, cstr_iter it)
|
||||||
|
{ return it.ref - cstr_str(self); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_from_s(cstr s, isize pos, isize len)
|
||||||
|
{ return cstr_from_n(cstr_str(&s) + pos, len); }
|
||||||
|
|
||||||
|
STC_INLINE csview cstr_subview(const cstr* self, isize pos, isize len) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0));
|
||||||
|
if (pos + len > sv.size) len = sv.size - pos;
|
||||||
|
return c_literal(csview){sv.buf + pos, len};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE zsview cstr_tail(const cstr* self, isize len) {
|
||||||
|
c_assert(len >= 0);
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
if (len > sv.size) len = sv.size;
|
||||||
|
return c_literal(zsview){&sv.buf[sv.size - len], len};
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN utf8 functions =====
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_u8_from(const char* str, isize u8pos, isize u8len)
|
||||||
|
{ str = utf8_at(str, u8pos); return cstr_from_n(str, utf8_to_index(str, u8len)); }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_u8_size(const cstr* self)
|
||||||
|
{ return utf8_count(cstr_str(self)); }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_u8_to_index(const cstr* self, isize u8pos)
|
||||||
|
{ return utf8_to_index(cstr_str(self), u8pos); }
|
||||||
|
|
||||||
|
STC_INLINE zsview cstr_u8_tail(const cstr* self, isize u8len) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
const char* p = &sv.buf[sv.size];
|
||||||
|
while (u8len && p != sv.buf)
|
||||||
|
u8len -= (*--p & 0xC0) != 0x80;
|
||||||
|
return c_literal(zsview){p, sv.size - (p - sv.buf)};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview cstr_u8_subview(const cstr* self, isize u8pos, isize u8len)
|
||||||
|
{ return utf8_subview(cstr_str(self), u8pos, u8len); }
|
||||||
|
|
||||||
|
STC_INLINE cstr_iter cstr_u8_at(const cstr* self, isize u8pos) {
|
||||||
|
csview sv;
|
||||||
|
sv.buf = utf8_at(cstr_str(self), u8pos);
|
||||||
|
sv.size = utf8_chr_size(sv.buf);
|
||||||
|
c_assert(sv.size);
|
||||||
|
return c_literal(cstr_iter){.chr = sv};
|
||||||
|
}
|
||||||
|
|
||||||
|
// utf8 iterator
|
||||||
|
|
||||||
|
STC_INLINE cstr_iter cstr_begin(const cstr* self) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
cstr_iter it = {.chr = {sv.buf, utf8_chr_size(sv.buf)}};
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
STC_INLINE cstr_iter cstr_end(const cstr* self) {
|
||||||
|
(void)self; cstr_iter it = {0}; return it;
|
||||||
|
}
|
||||||
|
STC_INLINE void cstr_next(cstr_iter* it) {
|
||||||
|
it->ref += it->chr.size;
|
||||||
|
it->chr.size = utf8_chr_size(it->ref);
|
||||||
|
if (*it->ref == '\0') it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE cstr_iter cstr_advance(cstr_iter it, isize u8pos) {
|
||||||
|
it.ref = utf8_offset(it.ref, u8pos);
|
||||||
|
it.chr.size = utf8_chr_size(it.ref);
|
||||||
|
if (*it.ref == '\0') it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// utf8 case conversion: requires `#define i_import` before including cstr.h in one TU.
|
||||||
|
extern cstr cstr_tocase_sv(csview sv, int k);
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_casefold_sv(csview sv)
|
||||||
|
{ return cstr_tocase_sv(sv, 0); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_tolower_sv(csview sv)
|
||||||
|
{ return cstr_tocase_sv(sv, 1); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_toupper_sv(csview sv)
|
||||||
|
{ return cstr_tocase_sv(sv, 2); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_tolower(const char* str)
|
||||||
|
{ return cstr_tolower_sv(c_sv(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE cstr cstr_toupper(const char* str)
|
||||||
|
{ return cstr_toupper_sv(c_sv(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_lowercase(cstr* self)
|
||||||
|
{ cstr_take(self, cstr_tolower_sv(cstr_sv(self))); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_uppercase(cstr* self)
|
||||||
|
{ cstr_take(self, cstr_toupper_sv(cstr_sv(self))); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_istarts_with(const cstr* self, const char* sub) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
isize len = c_strlen(sub);
|
||||||
|
return len <= sv.size && !utf8_icompare((sv.size = len, sv), c_sv(sub, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_iends_with(const cstr* self, const char* sub) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
isize len = c_strlen(sub);
|
||||||
|
return len <= sv.size && !utf8_icmp(sv.buf + sv.size - len, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE int cstr_icmp(const cstr* s1, const cstr* s2)
|
||||||
|
{ return utf8_icmp(cstr_str(s1), cstr_str(s2)); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_ieq(const cstr* s1, const cstr* s2) {
|
||||||
|
csview x = cstr_sv(s1), y = cstr_sv(s2);
|
||||||
|
return x.size == y.size && !utf8_icompare(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_iequals(const cstr* self, const char* str)
|
||||||
|
{ return !utf8_icmp(cstr_str(self), str); }
|
||||||
|
|
||||||
|
// END utf8 =====
|
||||||
|
|
||||||
|
STC_INLINE int cstr_cmp(const cstr* s1, const cstr* s2)
|
||||||
|
{ return strcmp(cstr_str(s1), cstr_str(s2)); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_eq(const cstr* s1, const cstr* s2) {
|
||||||
|
csview x = cstr_sv(s1), y = cstr_sv(s2);
|
||||||
|
return x.size == y.size && !c_memcmp(x.buf, y.buf, x.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_equals(const cstr* self, const char* str)
|
||||||
|
{ return !strcmp(cstr_str(self), str); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_equals_sv(const cstr* self, csview sv)
|
||||||
|
{ return sv.size == cstr_size(self) && !c_memcmp(cstr_str(self), sv.buf, sv.size); }
|
||||||
|
|
||||||
|
STC_INLINE isize cstr_find(const cstr* self, const char* search) {
|
||||||
|
const char *str = cstr_str(self), *res = strstr((char*)str, search);
|
||||||
|
return res ? (res - str) : c_NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_contains(const cstr* self, const char* search)
|
||||||
|
{ return strstr((char*)cstr_str(self), search) != NULL; }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_contains_sv(const cstr* self, csview search)
|
||||||
|
{ return cstr_find_sv(self, search) != c_NPOS; }
|
||||||
|
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_starts_with_sv(const cstr* self, csview sub) {
|
||||||
|
if (sub.size > cstr_size(self)) return false;
|
||||||
|
return !c_memcmp(cstr_str(self), sub.buf, sub.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_starts_with(const cstr* self, const char* sub) {
|
||||||
|
const char* str = cstr_str(self);
|
||||||
|
while (*sub && *str == *sub) ++str, ++sub;
|
||||||
|
return !*sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_ends_with_sv(const cstr* self, csview sub) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
if (sub.size > sv.size) return false;
|
||||||
|
return !c_memcmp(sv.buf + sv.size - sub.size, sub.buf, sub.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_ends_with(const cstr* self, const char* sub)
|
||||||
|
{ return cstr_ends_with_sv(self, c_sv(sub, c_strlen(sub))); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_assign(cstr* self, const char* str)
|
||||||
|
{ return cstr_assign_n(self, str, c_strlen(str)); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_assign_sv(cstr* self, csview sv)
|
||||||
|
{ return cstr_assign_n(self, sv.buf, sv.size); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_copy(cstr* self, cstr s) {
|
||||||
|
csview sv = cstr_sv(&s);
|
||||||
|
return cstr_assign_n(self, sv.buf, sv.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_push(cstr* self, const char* chr)
|
||||||
|
{ return cstr_append_n(self, chr, utf8_chr_size(chr)); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_pop(cstr* self) {
|
||||||
|
csview sv = cstr_sv(self);
|
||||||
|
const char* s = sv.buf + sv.size;
|
||||||
|
while ((*--s & 0xC0) == 0x80) ;
|
||||||
|
_cstr_set_size(self, (s - sv.buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_append(cstr* self, const char* str)
|
||||||
|
{ return cstr_append_n(self, str, c_strlen(str)); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_append_sv(cstr* self, csview sv)
|
||||||
|
{ return cstr_append_n(self, sv.buf, sv.size); }
|
||||||
|
|
||||||
|
STC_INLINE char* cstr_append_s(cstr* self, cstr s)
|
||||||
|
{ return cstr_append_sv(self, cstr_sv(&s)); }
|
||||||
|
|
||||||
|
#define cstr_join(self, sep, vec) do { \
|
||||||
|
struct _vec_s { cstr* data; ptrdiff_t size; } \
|
||||||
|
*_vec = (struct _vec_s*)&(vec); \
|
||||||
|
(void)sizeof((vec).data == _vec->data && &(vec).size == &_vec->size); \
|
||||||
|
cstr_join_sn(self, sep, _vec->data, _vec->size); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define cstr_join_items(self, sep, ...) \
|
||||||
|
cstr_join_n(self, sep, c_make_array(const char*, __VA_ARGS__), c_sizeof((const char*[])__VA_ARGS__)/c_sizeof(char*))
|
||||||
|
|
||||||
|
STC_INLINE void cstr_join_n(cstr* self, const char* sep, const char* arr[], isize n) {
|
||||||
|
const char* _sep = cstr_is_empty(self) ? "" : sep;
|
||||||
|
while (n--) { cstr_append(self, _sep); cstr_append(self, *arr++); _sep = sep; }
|
||||||
|
}
|
||||||
|
STC_INLINE void cstr_join_sn(cstr* self, const char* sep, const cstr arr[], isize n) {
|
||||||
|
const char* _sep = cstr_is_empty(self) ? "" : sep;
|
||||||
|
while (n--) { cstr_append(self, _sep); cstr_append_s(self, *arr++); _sep = sep; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
STC_INLINE void cstr_replace_sv(cstr* self, csview search, csview repl, int32_t count)
|
||||||
|
{ cstr_take(self, cstr_from_replace(cstr_sv(self), search, repl, count)); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_replace_nfirst(cstr* self, const char* search, const char* repl, int32_t count)
|
||||||
|
{ cstr_replace_sv(self, c_sv(search, c_strlen(search)), c_sv(repl, c_strlen(repl)), count); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_replace(cstr* self, const char* search, const char* repl)
|
||||||
|
{ cstr_replace_nfirst(self, search, repl, INT32_MAX); }
|
||||||
|
|
||||||
|
|
||||||
|
STC_INLINE void cstr_replace_at_sv(cstr* self, isize pos, isize len, const csview repl) {
|
||||||
|
char* d = _cstr_internal_move(self, pos + len, pos + repl.size);
|
||||||
|
c_memcpy(d + pos, repl.buf, repl.size);
|
||||||
|
}
|
||||||
|
STC_INLINE void cstr_replace_at(cstr* self, isize pos, isize len, const char* repl)
|
||||||
|
{ cstr_replace_at_sv(self, pos, len, c_sv(repl, c_strlen(repl))); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_u8_replace(cstr* self, isize u8pos, isize u8len, const char* repl) {
|
||||||
|
const char* s = cstr_str(self); csview span = utf8_subview(s, u8pos, u8len);
|
||||||
|
cstr_replace_at(self, span.buf - s, span.size, repl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
STC_INLINE void cstr_insert_sv(cstr* self, isize pos, csview sv)
|
||||||
|
{ cstr_replace_at_sv(self, pos, 0, sv); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_insert(cstr* self, isize pos, const char* str)
|
||||||
|
{ cstr_replace_at_sv(self, pos, 0, c_sv(str, c_strlen(str))); }
|
||||||
|
|
||||||
|
STC_INLINE void cstr_u8_insert(cstr* self, isize u8pos, const char* str)
|
||||||
|
{ cstr_insert(self, utf8_to_index(cstr_str(self), u8pos), str); }
|
||||||
|
|
||||||
|
STC_INLINE bool cstr_getline(cstr *self, FILE *fp)
|
||||||
|
{ return cstr_getdelim(self, '\n', fp); }
|
||||||
|
|
||||||
|
#endif // STC_CSTR_PRV_H_INCLUDED
|
||||||
77
stc/priv/linkage.h
Normal file
77
stc/priv/linkage.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#undef STC_API
|
||||||
|
#undef STC_DEF
|
||||||
|
|
||||||
|
#if !defined i_static && !defined STC_STATIC && (defined i_header || defined STC_HEADER || \
|
||||||
|
defined i_implement || defined STC_IMPLEMENT)
|
||||||
|
#define STC_API extern
|
||||||
|
#define STC_DEF
|
||||||
|
#else
|
||||||
|
#define i_implement
|
||||||
|
#if defined __GNUC__ || defined __clang__ || defined __INTEL_LLVM_COMPILER
|
||||||
|
#define STC_API static __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define STC_API static inline
|
||||||
|
#endif
|
||||||
|
#define STC_DEF static
|
||||||
|
#endif
|
||||||
|
#if defined STC_IMPLEMENT || defined i_import
|
||||||
|
#define i_implement
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined i_aux && defined i_allocator
|
||||||
|
#define _i_aux_alloc
|
||||||
|
#endif
|
||||||
|
#ifndef i_allocator
|
||||||
|
#define i_allocator c
|
||||||
|
#endif
|
||||||
|
#ifndef i_free
|
||||||
|
#define i_malloc c_JOIN(i_allocator, _malloc)
|
||||||
|
#define i_calloc c_JOIN(i_allocator, _calloc)
|
||||||
|
#define i_realloc c_JOIN(i_allocator, _realloc)
|
||||||
|
#define i_free c_JOIN(i_allocator, _free)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined __clang__ && !defined __cplusplus
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic warning "-Wall"
|
||||||
|
#pragma clang diagnostic warning "-Wextra"
|
||||||
|
#pragma clang diagnostic warning "-Wpedantic"
|
||||||
|
#pragma clang diagnostic warning "-Wconversion"
|
||||||
|
#pragma clang diagnostic warning "-Wwrite-strings"
|
||||||
|
// ignored
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
#elif defined __GNUC__ && !defined __cplusplus
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic warning "-Wall"
|
||||||
|
#pragma GCC diagnostic warning "-Wextra"
|
||||||
|
#pragma GCC diagnostic warning "-Wpedantic"
|
||||||
|
#pragma GCC diagnostic warning "-Wconversion"
|
||||||
|
#pragma GCC diagnostic warning "-Wwrite-strings"
|
||||||
|
// ignored
|
||||||
|
#pragma GCC diagnostic ignored "-Wclobbered"
|
||||||
|
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough=3"
|
||||||
|
#pragma GCC diagnostic ignored "-Wstringop-overflow="
|
||||||
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
#endif
|
||||||
42
stc/priv/linkage2.h
Normal file
42
stc/priv/linkage2.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef i_aux
|
||||||
|
#undef _i_aux_alloc
|
||||||
|
|
||||||
|
#undef i_allocator
|
||||||
|
#undef i_malloc
|
||||||
|
#undef i_calloc
|
||||||
|
#undef i_realloc
|
||||||
|
#undef i_free
|
||||||
|
|
||||||
|
#undef i_static
|
||||||
|
#undef i_header
|
||||||
|
#undef i_implement
|
||||||
|
#undef i_import
|
||||||
|
|
||||||
|
#if defined __clang__ && !defined __cplusplus
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif defined __GNUC__ && !defined __cplusplus
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
285
stc/priv/queue_prv.h
Normal file
285
stc/priv/queue_prv.h
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// IWYU pragma: private
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_queue, Self, i_key, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
STC_API bool _c_MEMB(_reserve)(Self* self, const isize cap);
|
||||||
|
STC_API void _c_MEMB(_clear)(Self* self);
|
||||||
|
STC_API void _c_MEMB(_drop)(const Self* cself);
|
||||||
|
STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value); // push_back
|
||||||
|
STC_API void _c_MEMB(_shrink_to_fit)(Self *self);
|
||||||
|
STC_API _m_iter _c_MEMB(_advance)(_m_iter it, isize n);
|
||||||
|
|
||||||
|
#define _cbuf_toidx(self, pos) (((pos) - (self)->start) & (self)->capmask)
|
||||||
|
#define _cbuf_topos(self, idx) (((self)->start + (idx)) & (self)->capmask)
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n)
|
||||||
|
{ while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val)
|
||||||
|
{ (void)self; i_keydrop(val); }
|
||||||
|
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ Self out = {0}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) {
|
||||||
|
cap = c_next_pow2(cap + 1);
|
||||||
|
Self out = {_i_new_n(_m_value, cap), 0, 0, cap - 1};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size)
|
||||||
|
{ Self out = _c_MEMB(_with_capacity)(size); out.end = size; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) {
|
||||||
|
Self out = _c_MEMB(_with_capacity)(size);
|
||||||
|
while (out.end < size) out.cbuf[out.end++] = i_keyfrom(default_raw);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) {
|
||||||
|
Self out = _c_MEMB(_with_capacity)(n);
|
||||||
|
_c_MEMB(_put_n)(&out, raw, n); return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_API bool _c_MEMB(_eq)(const Self* self, const Self* other);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self q);
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val)
|
||||||
|
{ (void)self; return i_keyclone(val); }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_copy)(Self* self, const Self* other) {
|
||||||
|
if (self == other) return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_clone)(*other);
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* self)
|
||||||
|
{ return _cbuf_toidx(self, self->end); }
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* self)
|
||||||
|
{ return self->capmask; }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* self)
|
||||||
|
{ return self->start == self->end; }
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval)
|
||||||
|
{ return i_keytoraw(pval); }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self)
|
||||||
|
{ return self->cbuf + self->start; }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self)
|
||||||
|
{ return self->cbuf + self->start; }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self)
|
||||||
|
{ return self->cbuf + ((self->end - 1) & self->capmask); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self)
|
||||||
|
{ return (_m_value*)_c_MEMB(_back)(self); }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->capmask = self->start = self->end = 0;
|
||||||
|
self->cbuf = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned)
|
||||||
|
{ _c_MEMB(_drop)(self); *self = unowned; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_pop)(Self* self) { // pop_front
|
||||||
|
c_assert(!_c_MEMB(_is_empty)(self));
|
||||||
|
i_keydrop((self->cbuf + self->start));
|
||||||
|
self->start = (self->start + 1) & self->capmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_pull)(Self* self) { // move front out of queue
|
||||||
|
c_assert(!_c_MEMB(_is_empty)(self));
|
||||||
|
isize s = self->start;
|
||||||
|
self->start = (s + 1) & self->capmask;
|
||||||
|
return self->cbuf[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) {
|
||||||
|
return c_literal(_m_iter){
|
||||||
|
.ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + self->start,
|
||||||
|
.pos=self->start, ._s=self};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) {
|
||||||
|
isize pos = (self->end - 1) & self->capmask;
|
||||||
|
return c_literal(_m_iter){
|
||||||
|
.ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + pos,
|
||||||
|
.pos=pos, ._s=self};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_end)(const Self* self)
|
||||||
|
{ (void)self; return c_literal(_m_iter){0}; }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self)
|
||||||
|
{ (void)self; return c_literal(_m_iter){0}; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_next)(_m_iter* it) {
|
||||||
|
if (it->pos != it->_s->capmask) { ++it->ref; ++it->pos; }
|
||||||
|
else { it->ref -= it->pos; it->pos = 0; }
|
||||||
|
if (it->pos == it->_s->end) it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) {
|
||||||
|
if (it->pos == it->_s->start) it->ref = NULL;
|
||||||
|
else if (it->pos != 0) { --it->ref; --it->pos; }
|
||||||
|
else it->ref += (it->pos = it->_s->capmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it)
|
||||||
|
{ return _cbuf_toidx(self, it.pos); }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n)
|
||||||
|
{ self->end = (self->end + n) & self->capmask; }
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF _m_iter _c_MEMB(_advance)(_m_iter it, isize n) {
|
||||||
|
isize len = _c_MEMB(_size)(it._s);
|
||||||
|
isize pos = it.pos, idx = _cbuf_toidx(it._s, pos);
|
||||||
|
it.pos = (pos + n) & it._s->capmask;
|
||||||
|
it.ref += it.pos - pos;
|
||||||
|
if (!c_uless(idx + n, len)) it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_clear)(Self* self) {
|
||||||
|
for (c_each(i, Self, *self))
|
||||||
|
{ i_keydrop(i.ref); }
|
||||||
|
self->start = 0, self->end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
_i_free_n(self->cbuf, self->capmask + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_reserve)(Self* self, const isize cap) {
|
||||||
|
isize oldpow2 = self->capmask + (self->capmask & 1); // handle capmask = 0
|
||||||
|
isize newpow2 = c_next_pow2(cap + 1);
|
||||||
|
if (newpow2 <= oldpow2)
|
||||||
|
return self->cbuf != NULL;
|
||||||
|
_m_value* d = (_m_value *)_i_realloc_n(self->cbuf, oldpow2, newpow2);
|
||||||
|
if (d == NULL)
|
||||||
|
return false;
|
||||||
|
isize head = oldpow2 - self->start;
|
||||||
|
if (self->start <= self->end) // [..S########E....|................]
|
||||||
|
;
|
||||||
|
else if (head < self->end) { // [#######E.....S##|.............s!!]
|
||||||
|
c_memcpy(d + newpow2 - head, d + self->start, head*c_sizeof *d);
|
||||||
|
self->start = newpow2 - head;
|
||||||
|
} else { // [##E.....S#######|!!e.............]
|
||||||
|
c_memcpy(d + oldpow2, d, self->end*c_sizeof *d);
|
||||||
|
self->end += oldpow2;
|
||||||
|
}
|
||||||
|
self->capmask = newpow2 - 1;
|
||||||
|
self->cbuf = d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_push)(Self* self, _m_value value) { // push_back
|
||||||
|
isize end = (self->end + 1) & self->capmask;
|
||||||
|
if (end == self->start) { // full
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand
|
||||||
|
return NULL;
|
||||||
|
end = (self->end + 1) & self->capmask;
|
||||||
|
}
|
||||||
|
_m_value *v = self->cbuf + self->end;
|
||||||
|
self->end = end;
|
||||||
|
*v = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_shrink_to_fit)(Self *self) {
|
||||||
|
isize sz = _c_MEMB(_size)(self);
|
||||||
|
isize newpow2 = c_next_pow2(sz + 1);
|
||||||
|
if (newpow2 > self->capmask)
|
||||||
|
return;
|
||||||
|
if (self->start <= self->end) {
|
||||||
|
c_memmove(self->cbuf, self->cbuf + self->start, sz*c_sizeof *self->cbuf);
|
||||||
|
self->start = 0, self->end = sz;
|
||||||
|
} else {
|
||||||
|
isize n = self->capmask - self->start + 1;
|
||||||
|
c_memmove(self->cbuf + (newpow2 - n), self->cbuf + self->start, n*c_sizeof *self->cbuf);
|
||||||
|
self->start = newpow2 - n;
|
||||||
|
}
|
||||||
|
self->cbuf = (_m_value *)_i_realloc_n(self->cbuf, self->capmask + 1, newpow2);
|
||||||
|
self->capmask = newpow2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_clone)(Self q) {
|
||||||
|
Self out = q, *self = &out; (void)self; // may be used by _i_new_n/i_keyclone via i_aux.
|
||||||
|
out.start = 0; out.end = _c_MEMB(_size)(&q);
|
||||||
|
out.capmask = c_next_pow2(out.end + 1) - 1;
|
||||||
|
out.cbuf = _i_new_n(_m_value, out.capmask + 1);
|
||||||
|
isize i = 0;
|
||||||
|
if (out.cbuf)
|
||||||
|
for (c_each(it, Self, q))
|
||||||
|
out.cbuf[i++] = i_keyclone((*it.ref));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif // i_no_clone
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false;
|
||||||
|
for (_m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other);
|
||||||
|
i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j))
|
||||||
|
{
|
||||||
|
const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref);
|
||||||
|
if (!(i_eq((&_rx), (&_ry)))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // _i_has_eq
|
||||||
|
#endif // IMPLEMENTATION
|
||||||
136
stc/priv/sort_prv.h
Normal file
136
stc/priv/sort_prv.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// IWYU pragma: private
|
||||||
|
#ifdef _i_is_list
|
||||||
|
#define i_at(self, idx) (&((_m_value *)(self)->last)[idx])
|
||||||
|
#define i_at_mut i_at
|
||||||
|
#elif !defined i_at
|
||||||
|
#define i_at(self, idx) _c_MEMB(_at)(self, idx)
|
||||||
|
#define i_at_mut(self, idx) _c_MEMB(_at_mut)(self, idx)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_API void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi);
|
||||||
|
|
||||||
|
#ifdef _i_is_array
|
||||||
|
STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end);
|
||||||
|
STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end);
|
||||||
|
|
||||||
|
static inline void _c_MEMB(_sort)(Self* arr, isize n)
|
||||||
|
{ _c_MEMB(_sort_lowhigh)(arr, 0, n - 1); }
|
||||||
|
|
||||||
|
static inline isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_lower_bound)(const Self* arr, const _m_raw raw, isize n)
|
||||||
|
{ return _c_MEMB(_lower_bound_range)(arr, raw, 0, n); }
|
||||||
|
|
||||||
|
static inline isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_binary_search)(const Self* arr, const _m_raw raw, isize n)
|
||||||
|
{ return _c_MEMB(_binary_search_range)(arr, raw, 0, n); }
|
||||||
|
|
||||||
|
#elif !defined _i_is_list
|
||||||
|
STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end);
|
||||||
|
STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end);
|
||||||
|
|
||||||
|
static inline void _c_MEMB(_sort)(Self* self)
|
||||||
|
{ _c_MEMB(_sort_lowhigh)(self, 0, _c_MEMB(_size)(self) - 1); }
|
||||||
|
|
||||||
|
static inline isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_lower_bound)(const Self* self, const _m_raw raw)
|
||||||
|
{ return _c_MEMB(_lower_bound_range)(self, raw, 0, _c_MEMB(_size)(self)); }
|
||||||
|
|
||||||
|
static inline isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_binary_search)(const Self* self, const _m_raw raw)
|
||||||
|
{ return _c_MEMB(_binary_search_range)(self, raw, 0, _c_MEMB(_size)(self)); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
static void _c_MEMB(_insertsort_lowhigh)(Self* self, isize lo, isize hi) {
|
||||||
|
for (isize j = lo, i = lo + 1; i <= hi; j = i, ++i) {
|
||||||
|
_m_value x = *i_at(self, i);
|
||||||
|
_m_raw rx = i_keytoraw((&x));
|
||||||
|
while (j >= 0) {
|
||||||
|
_m_raw ry = i_keytoraw(i_at(self, j));
|
||||||
|
if (!(i_less((&rx), (&ry)))) break;
|
||||||
|
*i_at_mut(self, j + 1) = *i_at(self, j);
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
*i_at_mut(self, j + 1) = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi) {
|
||||||
|
isize i = lo, j;
|
||||||
|
while (lo < hi) {
|
||||||
|
_m_raw pivot = i_keytoraw(i_at(self, (isize)(lo + (hi - lo)*7LL/16))), rx;
|
||||||
|
j = hi;
|
||||||
|
do {
|
||||||
|
do { rx = i_keytoraw(i_at(self, i)); } while ((i_less((&rx), (&pivot))) && ++i);
|
||||||
|
do { rx = i_keytoraw(i_at(self, j)); } while ((i_less((&pivot), (&rx))) && --j);
|
||||||
|
if (i > j) break;
|
||||||
|
c_swap(i_at_mut(self, i), i_at_mut(self, j));
|
||||||
|
++i; --j;
|
||||||
|
} while (i <= j);
|
||||||
|
|
||||||
|
if (j - lo > hi - i) {
|
||||||
|
c_swap(&lo, &i);
|
||||||
|
c_swap(&hi, &j);
|
||||||
|
}
|
||||||
|
if (j - lo > 64) _c_MEMB(_sort_lowhigh)(self, lo, j);
|
||||||
|
else if (j > lo) _c_MEMB(_insertsort_lowhigh)(self, lo, j);
|
||||||
|
lo = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _i_is_list
|
||||||
|
STC_DEF isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end) {
|
||||||
|
isize count = end - start, step = count/2;
|
||||||
|
while (count > 0) {
|
||||||
|
const _m_raw rx = i_keytoraw(i_at(self, start + step));
|
||||||
|
if (i_less((&rx), (&raw))) {
|
||||||
|
start += step + 1;
|
||||||
|
count -= step + 1;
|
||||||
|
step = count*7/8;
|
||||||
|
} else {
|
||||||
|
count = step;
|
||||||
|
step = count/8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return start >= end ? c_NPOS : start;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF isize // c_NPOS = not found
|
||||||
|
_c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end) {
|
||||||
|
isize res = _c_MEMB(_lower_bound_range)(self, raw, start, end);
|
||||||
|
if (res != c_NPOS) {
|
||||||
|
const _m_raw rx = i_keytoraw(i_at(self, res));
|
||||||
|
if (i_less((&raw), (&rx))) res = c_NPOS;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif // !_i_is_list
|
||||||
|
#endif // IMPLEMENTATION
|
||||||
|
#undef i_at
|
||||||
|
#undef i_at_mut
|
||||||
297
stc/priv/template.h
Normal file
297
stc/priv/template.h
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private
|
||||||
|
#ifndef _i_template
|
||||||
|
#define _i_template
|
||||||
|
|
||||||
|
#ifndef STC_TEMPLATE_H_INCLUDED
|
||||||
|
#define STC_TEMPLATE_H_INCLUDED
|
||||||
|
|
||||||
|
#define _c_MEMB(name) c_JOIN(Self, name)
|
||||||
|
#define _c_DEFTYPES(macro, SELF, ...) macro(SELF, __VA_ARGS__)
|
||||||
|
#define _m_value _c_MEMB(_value)
|
||||||
|
#define _m_key _c_MEMB(_key)
|
||||||
|
#define _m_mapped _c_MEMB(_mapped)
|
||||||
|
#define _m_rmapped _c_MEMB(_rmapped)
|
||||||
|
#define _m_raw _c_MEMB(_raw)
|
||||||
|
#define _m_keyraw _c_MEMB(_keyraw)
|
||||||
|
#define _m_iter _c_MEMB(_iter)
|
||||||
|
#define _m_result _c_MEMB(_result)
|
||||||
|
#define _m_node _c_MEMB(_node)
|
||||||
|
|
||||||
|
#define c_OPTION(flag) ((i_opt) & (flag))
|
||||||
|
#define c_declared (1<<0)
|
||||||
|
#define c_no_atomic (1<<1)
|
||||||
|
#define c_arc2 (1<<2)
|
||||||
|
#define c_no_clone (1<<3)
|
||||||
|
#define c_no_hash (1<<4)
|
||||||
|
#define c_use_cmp (1<<5)
|
||||||
|
#define c_use_eq (1<<6)
|
||||||
|
#define c_cmpclass (1<<7)
|
||||||
|
#define c_keyclass (1<<8)
|
||||||
|
#define c_valclass (1<<9)
|
||||||
|
#define c_keypro (1<<10)
|
||||||
|
#define c_valpro (1<<11)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined i_rawclass // [deprecated]
|
||||||
|
#define i_cmpclass i_rawclass
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined T && !defined i_type
|
||||||
|
#define i_type T
|
||||||
|
#endif
|
||||||
|
#if defined i_type && c_NUMARGS(i_type) > 1
|
||||||
|
#define Self c_GETARG(1, i_type)
|
||||||
|
#define i_key c_GETARG(2, i_type)
|
||||||
|
#if c_NUMARGS(i_type) == 3
|
||||||
|
#if defined _i_is_map
|
||||||
|
#define i_val c_GETARG(3, i_type)
|
||||||
|
#else
|
||||||
|
#define i_opt c_GETARG(3, i_type)
|
||||||
|
#endif
|
||||||
|
#elif c_NUMARGS(i_type) == 4
|
||||||
|
#define i_val c_GETARG(3, i_type)
|
||||||
|
#define i_opt c_GETARG(4, i_type)
|
||||||
|
#endif
|
||||||
|
#elif !defined Self && defined i_type
|
||||||
|
#define Self i_type
|
||||||
|
#elif !defined Self
|
||||||
|
#define Self c_JOIN(_i_prefix, i_tag)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined i_aux && c_NUMARGS(i_aux) == 2
|
||||||
|
// shorthand for defining i_aux AND i_allocator as a one-liner combo.
|
||||||
|
#define _i_aux_alloc
|
||||||
|
#define _i_aux_def c_GETARG(1, i_aux) aux;
|
||||||
|
#undef i_allocator // override:
|
||||||
|
#define i_allocator c_GETARG(2, i_aux)
|
||||||
|
#elif defined i_aux
|
||||||
|
#define _i_aux_def i_aux aux;
|
||||||
|
#else
|
||||||
|
#define _i_aux_def
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if c_OPTION(c_declared)
|
||||||
|
#define i_declared
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_no_hash)
|
||||||
|
#define i_no_hash
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_use_cmp)
|
||||||
|
#define i_use_cmp
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_use_eq)
|
||||||
|
#define i_use_eq
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_no_clone) || defined _i_is_arc
|
||||||
|
#define i_no_clone
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_keyclass)
|
||||||
|
#define i_keyclass i_key
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_valclass)
|
||||||
|
#define i_valclass i_val
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_cmpclass)
|
||||||
|
#define i_cmpclass i_key
|
||||||
|
#define i_use_cmp
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_keypro)
|
||||||
|
#define i_keypro i_key
|
||||||
|
#endif
|
||||||
|
#if c_OPTION(c_valpro)
|
||||||
|
#define i_valpro i_val
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined i_keypro
|
||||||
|
#define i_keyclass i_keypro
|
||||||
|
#define i_cmpclass c_JOIN(i_keypro, _raw)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined i_cmpclass
|
||||||
|
#define i_keyraw i_cmpclass
|
||||||
|
#if !(defined i_key || defined i_keyclass)
|
||||||
|
#define i_key i_cmpclass
|
||||||
|
#endif
|
||||||
|
#elif defined i_keyclass && !defined i_keyraw
|
||||||
|
// Special: When only i_keyclass is defined, also define i_cmpclass to the same.
|
||||||
|
// Do not define i_keyraw here, otherwise _from() / _toraw() is expected to exist.
|
||||||
|
#define i_cmpclass i_key
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind to i_key "class members": _clone, _drop, _from and _toraw (when conditions are met).
|
||||||
|
#if defined i_keyclass
|
||||||
|
#ifndef i_key
|
||||||
|
#define i_key i_keyclass
|
||||||
|
#endif
|
||||||
|
#if !defined i_keyclone && !defined i_no_clone
|
||||||
|
#define i_keyclone c_JOIN(i_keyclass, _clone)
|
||||||
|
#endif
|
||||||
|
#ifndef i_keydrop
|
||||||
|
#define i_keydrop c_JOIN(i_keyclass, _drop)
|
||||||
|
#endif
|
||||||
|
#if !defined i_keyfrom && defined i_keyraw
|
||||||
|
#define i_keyfrom c_JOIN(i_keyclass, _from)
|
||||||
|
#endif
|
||||||
|
#if !defined i_keytoraw && defined i_keyraw
|
||||||
|
#define i_keytoraw c_JOIN(i_keyclass, _toraw)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define when container has support for sorting (cmp) and linear search (eq)
|
||||||
|
#if defined i_use_cmp || defined i_cmp || defined i_less
|
||||||
|
#define _i_has_cmp
|
||||||
|
#endif
|
||||||
|
#if defined i_use_cmp || defined i_cmp || defined i_use_eq || defined i_eq
|
||||||
|
#define _i_has_eq
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind to i_cmpclass "class members": _cmp, _eq and _hash (when conditions are met).
|
||||||
|
#if defined i_cmpclass
|
||||||
|
#if !(defined i_cmp || defined i_less) && (defined i_use_cmp || defined _i_sorted)
|
||||||
|
#define i_cmp c_JOIN(i_cmpclass, _cmp)
|
||||||
|
#endif
|
||||||
|
#if !defined i_eq && (defined i_use_eq || defined i_hash || defined _i_is_hash)
|
||||||
|
#define i_eq c_JOIN(i_cmpclass, _eq)
|
||||||
|
#endif
|
||||||
|
#if !(defined i_hash || defined i_no_hash)
|
||||||
|
#define i_hash c_JOIN(i_cmpclass, _hash)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_key
|
||||||
|
#error "No i_key defined"
|
||||||
|
#elif defined i_keyraw && !(c_OPTION(c_cmpclass) || defined i_keytoraw)
|
||||||
|
#error "If i_cmpclass / i_keyraw is defined, i_keytoraw must be defined too"
|
||||||
|
#elif !defined i_no_clone && (defined i_keyclone ^ defined i_keydrop)
|
||||||
|
#error "Both i_keyclone and i_keydrop must be defined, if any (unless i_no_clone defined)."
|
||||||
|
#elif defined i_from || defined i_drop
|
||||||
|
#error "i_from / i_drop not supported. Use i_keyfrom/i_keydrop"
|
||||||
|
#elif defined i_keyto || defined i_valto
|
||||||
|
#error i_keyto / i_valto not supported. Use i_keytoraw / i_valtoraw
|
||||||
|
#elif defined i_keyraw && defined i_use_cmp && !defined _i_has_cmp
|
||||||
|
#error "For smap / sset / pqueue, i_cmp or i_less must be defined when i_keyraw is defined."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fill in missing i_eq, i_less, i_cmp functions with defaults.
|
||||||
|
#if !defined i_eq && defined i_cmp
|
||||||
|
#define i_eq(x, y) (i_cmp(x, y)) == 0
|
||||||
|
#elif !defined i_eq
|
||||||
|
#define i_eq(x, y) *x == *y // works for integral types
|
||||||
|
#endif
|
||||||
|
#if !defined i_less && defined i_cmp
|
||||||
|
#define i_less(x, y) (i_cmp(x, y)) < 0
|
||||||
|
#elif !defined i_less
|
||||||
|
#define i_less(x, y) *x < *y // works for integral types
|
||||||
|
#endif
|
||||||
|
#if !defined i_cmp && defined i_less
|
||||||
|
#define i_cmp(x, y) (i_less(y, x)) - (i_less(x, y))
|
||||||
|
#endif
|
||||||
|
#if !(defined i_hash || defined i_no_hash)
|
||||||
|
#define i_hash c_default_hash
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define i_no_emplace
|
||||||
|
|
||||||
|
#ifndef i_tag
|
||||||
|
#define i_tag i_key
|
||||||
|
#endif
|
||||||
|
#if !defined i_keyfrom
|
||||||
|
#define i_keyfrom c_default_clone
|
||||||
|
#else
|
||||||
|
#undef i_no_emplace
|
||||||
|
#endif
|
||||||
|
#ifndef i_keyraw
|
||||||
|
#define i_keyraw i_key
|
||||||
|
#endif
|
||||||
|
#ifndef i_keytoraw
|
||||||
|
#define i_keytoraw c_default_toraw
|
||||||
|
#endif
|
||||||
|
#ifndef i_keyclone
|
||||||
|
#define i_keyclone c_default_clone
|
||||||
|
#endif
|
||||||
|
#ifndef i_keydrop
|
||||||
|
#define i_keydrop c_default_drop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _i_is_map // ---- process hashmap/sortedmap value i_val, ... ----
|
||||||
|
|
||||||
|
#if defined i_valpro
|
||||||
|
#define i_valclass i_valpro
|
||||||
|
#define i_valraw c_JOIN(i_valpro, _raw)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef i_valclass
|
||||||
|
#ifndef i_val
|
||||||
|
#define i_val i_valclass
|
||||||
|
#endif
|
||||||
|
#if !defined i_valclone && !defined i_no_clone
|
||||||
|
#define i_valclone c_JOIN(i_valclass, _clone)
|
||||||
|
#endif
|
||||||
|
#ifndef i_valdrop
|
||||||
|
#define i_valdrop c_JOIN(i_valclass, _drop)
|
||||||
|
#endif
|
||||||
|
#if !defined i_valfrom && defined i_valraw
|
||||||
|
#define i_valfrom c_JOIN(i_valclass, _from)
|
||||||
|
#endif
|
||||||
|
#if !defined i_valtoraw && defined i_valraw
|
||||||
|
#define i_valtoraw c_JOIN(i_valclass, _toraw)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef i_val
|
||||||
|
#error "i_val* must be defined for maps"
|
||||||
|
#elif defined i_valraw && !defined i_valtoraw
|
||||||
|
#error "If i_valraw is defined, i_valtoraw must be defined too"
|
||||||
|
#elif !defined i_no_clone && (defined i_valclone ^ defined i_valdrop)
|
||||||
|
#error "Both i_valclone and i_valdrop must be defined, if any"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined i_valfrom
|
||||||
|
#define i_valfrom c_default_clone
|
||||||
|
#else
|
||||||
|
#undef i_no_emplace
|
||||||
|
#endif
|
||||||
|
#ifndef i_valraw
|
||||||
|
#define i_valraw i_val
|
||||||
|
#endif
|
||||||
|
#ifndef i_valtoraw
|
||||||
|
#define i_valtoraw c_default_toraw
|
||||||
|
#endif
|
||||||
|
#ifndef i_valclone
|
||||||
|
#define i_valclone c_default_clone
|
||||||
|
#endif
|
||||||
|
#ifndef i_valdrop
|
||||||
|
#define i_valdrop c_default_drop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // !_i_is_map
|
||||||
|
|
||||||
|
#ifndef i_val
|
||||||
|
#define i_val i_key
|
||||||
|
#endif
|
||||||
|
#ifndef i_valraw
|
||||||
|
#define i_valraw i_keyraw
|
||||||
|
#endif
|
||||||
|
#endif // STC_TEMPLATE_H_INCLUDED
|
||||||
71
stc/priv/template2.h
Normal file
71
stc/priv/template2.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private
|
||||||
|
#undef T // alias for i_type
|
||||||
|
#undef i_type
|
||||||
|
#undef i_class
|
||||||
|
#undef i_tag
|
||||||
|
#undef i_opt
|
||||||
|
#undef i_capacity
|
||||||
|
|
||||||
|
#undef i_key
|
||||||
|
#undef i_keypro // Replaces next two
|
||||||
|
#undef i_key_str // [deprecated]
|
||||||
|
#undef i_key_arcbox // [deprecated]
|
||||||
|
#undef i_keyclass
|
||||||
|
#undef i_cmpclass // define i_keyraw, and bind i_cmp, i_eq, i_hash "class members"
|
||||||
|
#undef i_rawclass // [deprecated] for i_cmpclass
|
||||||
|
#undef i_keyclone
|
||||||
|
#undef i_keydrop
|
||||||
|
#undef i_keyraw
|
||||||
|
#undef i_keyfrom
|
||||||
|
#undef i_keytoraw
|
||||||
|
#undef i_cmp
|
||||||
|
#undef i_less
|
||||||
|
#undef i_eq
|
||||||
|
#undef i_hash
|
||||||
|
|
||||||
|
#undef i_val
|
||||||
|
#undef i_valpro // Replaces next two
|
||||||
|
#undef i_val_str // [deprecated]
|
||||||
|
#undef i_val_arcbox // [deprecated]
|
||||||
|
#undef i_valclass
|
||||||
|
#undef i_valclone
|
||||||
|
#undef i_valdrop
|
||||||
|
#undef i_valraw
|
||||||
|
#undef i_valfrom
|
||||||
|
#undef i_valtoraw
|
||||||
|
|
||||||
|
#undef i_use_cmp
|
||||||
|
#undef i_use_eq
|
||||||
|
#undef i_no_hash
|
||||||
|
#undef i_no_clone
|
||||||
|
#undef i_no_emplace
|
||||||
|
#undef i_declared
|
||||||
|
|
||||||
|
#undef _i_aux_def
|
||||||
|
#undef _i_has_cmp
|
||||||
|
#undef _i_has_eq
|
||||||
|
#undef _i_prefix
|
||||||
|
#undef _i_template
|
||||||
|
#undef Self
|
||||||
482
stc/priv/ucd_prv.c
Normal file
482
stc/priv/ucd_prv.c
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef STC_UCD_PRV_C_INCLUDED
|
||||||
|
#define STC_UCD_PRV_C_INCLUDED
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// The following requires linking with utf8 symbols.
|
||||||
|
// To call them, either define i_import before including
|
||||||
|
// one of cstr, csview, zsview, or link with src/libstc.o.
|
||||||
|
|
||||||
|
enum {
|
||||||
|
U8G_Cc, U8G_Lt, U8G_Nd, U8G_Nl,
|
||||||
|
U8G_Pc, U8G_Pd, U8G_Pf, U8G_Pi,
|
||||||
|
U8G_Sc, U8G_Zl, U8G_Zp, U8G_Zs,
|
||||||
|
U8G_Arabic, U8G_Bengali, U8G_Cyrillic,
|
||||||
|
U8G_Devanagari, U8G_Georgian, U8G_Greek,
|
||||||
|
U8G_Han, U8G_Hiragana, U8G_Katakana,
|
||||||
|
U8G_Latin, U8G_Thai,
|
||||||
|
U8G_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool utf8_isgroup(int group, uint32_t c);
|
||||||
|
|
||||||
|
static bool utf8_isalpha(uint32_t c) {
|
||||||
|
static int16_t groups[] = {U8G_Latin, U8G_Nl, U8G_Cyrillic, U8G_Han, U8G_Devanagari,
|
||||||
|
U8G_Arabic, U8G_Bengali, U8G_Hiragana, U8G_Katakana,
|
||||||
|
U8G_Thai, U8G_Greek, U8G_Georgian};
|
||||||
|
if (c < 128) return isalpha((int)c) != 0;
|
||||||
|
for (int j=0; j < (int)(sizeof groups/sizeof groups[0]); ++j)
|
||||||
|
if (utf8_isgroup(groups[j], c))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf8_iscased(uint32_t c) {
|
||||||
|
if (c < 128) return isalpha((int)c) != 0;
|
||||||
|
return utf8_islower(c) || utf8_isupper(c) ||
|
||||||
|
utf8_isgroup(U8G_Lt, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf8_isalnum(uint32_t c) {
|
||||||
|
if (c < 128) return isalnum((int)c) != 0;
|
||||||
|
return utf8_isalpha(c) || utf8_isgroup(U8G_Nd, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf8_isword(uint32_t c) {
|
||||||
|
if (c < 128) return (isalnum((int)c) != 0) | (c == '_');
|
||||||
|
return utf8_isalpha(c) || utf8_isgroup(U8G_Nd, c) ||
|
||||||
|
utf8_isgroup(U8G_Pc, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf8_isblank(uint32_t c) {
|
||||||
|
if (c < 128) return (c == ' ') | (c == '\t');
|
||||||
|
return utf8_isgroup(U8G_Zs, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool utf8_isspace(uint32_t c) {
|
||||||
|
if (c < 128) return isspace((int)c) != 0;
|
||||||
|
return ((c == 8232) | (c == 8233)) || utf8_isgroup(U8G_Zs, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The tables below are extracted from the RE2 library */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t lo;
|
||||||
|
uint16_t hi;
|
||||||
|
} URange16;
|
||||||
|
|
||||||
|
static const URange16 Cc_range16[] = { // Control
|
||||||
|
{ 0, 31 },
|
||||||
|
{ 127, 159 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Lt_range16[] = { // Title case
|
||||||
|
{ 453, 453 },
|
||||||
|
{ 456, 456 },
|
||||||
|
{ 459, 459 },
|
||||||
|
{ 498, 498 },
|
||||||
|
{ 8072, 8079 },
|
||||||
|
{ 8088, 8095 },
|
||||||
|
{ 8104, 8111 },
|
||||||
|
{ 8124, 8124 },
|
||||||
|
{ 8140, 8140 },
|
||||||
|
{ 8188, 8188 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Nd_range16[] = { // Decimal number
|
||||||
|
{ 48, 57 },
|
||||||
|
{ 1632, 1641 },
|
||||||
|
{ 1776, 1785 },
|
||||||
|
{ 1984, 1993 },
|
||||||
|
{ 2406, 2415 },
|
||||||
|
{ 2534, 2543 },
|
||||||
|
{ 2662, 2671 },
|
||||||
|
{ 2790, 2799 },
|
||||||
|
{ 2918, 2927 },
|
||||||
|
{ 3046, 3055 },
|
||||||
|
{ 3174, 3183 },
|
||||||
|
{ 3302, 3311 },
|
||||||
|
{ 3430, 3439 },
|
||||||
|
{ 3558, 3567 },
|
||||||
|
{ 3664, 3673 },
|
||||||
|
{ 3792, 3801 },
|
||||||
|
{ 3872, 3881 },
|
||||||
|
{ 4160, 4169 },
|
||||||
|
{ 4240, 4249 },
|
||||||
|
{ 6112, 6121 },
|
||||||
|
{ 6160, 6169 },
|
||||||
|
{ 6470, 6479 },
|
||||||
|
{ 6608, 6617 },
|
||||||
|
{ 6784, 6793 },
|
||||||
|
{ 6800, 6809 },
|
||||||
|
{ 6992, 7001 },
|
||||||
|
{ 7088, 7097 },
|
||||||
|
{ 7232, 7241 },
|
||||||
|
{ 7248, 7257 },
|
||||||
|
{ 42528, 42537 },
|
||||||
|
{ 43216, 43225 },
|
||||||
|
{ 43264, 43273 },
|
||||||
|
{ 43472, 43481 },
|
||||||
|
{ 43504, 43513 },
|
||||||
|
{ 43600, 43609 },
|
||||||
|
{ 44016, 44025 },
|
||||||
|
{ 65296, 65305 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Nl_range16[] = { // Number letter
|
||||||
|
{ 5870, 5872 },
|
||||||
|
{ 8544, 8578 },
|
||||||
|
{ 8581, 8584 },
|
||||||
|
{ 12295, 12295 },
|
||||||
|
{ 12321, 12329 },
|
||||||
|
{ 12344, 12346 },
|
||||||
|
{ 42726, 42735 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Pc_range16[] = { // Connector punctuation
|
||||||
|
{ 95, 95 },
|
||||||
|
{ 8255, 8256 },
|
||||||
|
{ 8276, 8276 },
|
||||||
|
{ 65075, 65076 },
|
||||||
|
{ 65101, 65103 },
|
||||||
|
{ 65343, 65343 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Pd_range16[] = { // Dash punctuation
|
||||||
|
{ 45, 45 },
|
||||||
|
{ 1418, 1418 },
|
||||||
|
{ 1470, 1470 },
|
||||||
|
{ 5120, 5120 },
|
||||||
|
{ 6150, 6150 },
|
||||||
|
{ 8208, 8213 },
|
||||||
|
{ 11799, 11799 },
|
||||||
|
{ 11802, 11802 },
|
||||||
|
{ 11834, 11835 },
|
||||||
|
{ 11840, 11840 },
|
||||||
|
{ 11869, 11869 },
|
||||||
|
{ 12316, 12316 },
|
||||||
|
{ 12336, 12336 },
|
||||||
|
{ 12448, 12448 },
|
||||||
|
{ 65073, 65074 },
|
||||||
|
{ 65112, 65112 },
|
||||||
|
{ 65123, 65123 },
|
||||||
|
{ 65293, 65293 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Pf_range16[] = { // Final punctuation
|
||||||
|
{ 187, 187 },
|
||||||
|
{ 8217, 8217 },
|
||||||
|
{ 8221, 8221 },
|
||||||
|
{ 8250, 8250 },
|
||||||
|
{ 11779, 11779 },
|
||||||
|
{ 11781, 11781 },
|
||||||
|
{ 11786, 11786 },
|
||||||
|
{ 11789, 11789 },
|
||||||
|
{ 11805, 11805 },
|
||||||
|
{ 11809, 11809 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Pi_range16[] = { // Initial punctuation
|
||||||
|
{ 171, 171 },
|
||||||
|
{ 8216, 8216 },
|
||||||
|
{ 8219, 8220 },
|
||||||
|
{ 8223, 8223 },
|
||||||
|
{ 8249, 8249 },
|
||||||
|
{ 11778, 11778 },
|
||||||
|
{ 11780, 11780 },
|
||||||
|
{ 11785, 11785 },
|
||||||
|
{ 11788, 11788 },
|
||||||
|
{ 11804, 11804 },
|
||||||
|
{ 11808, 11808 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Sc_range16[] = { // Currency symbol
|
||||||
|
{ 36, 36 },
|
||||||
|
{ 162, 165 },
|
||||||
|
{ 1423, 1423 },
|
||||||
|
{ 1547, 1547 },
|
||||||
|
{ 2046, 2047 },
|
||||||
|
{ 2546, 2547 },
|
||||||
|
{ 2555, 2555 },
|
||||||
|
{ 2801, 2801 },
|
||||||
|
{ 3065, 3065 },
|
||||||
|
{ 3647, 3647 },
|
||||||
|
{ 6107, 6107 },
|
||||||
|
{ 8352, 8384 },
|
||||||
|
{ 43064, 43064 },
|
||||||
|
{ 65020, 65020 },
|
||||||
|
{ 65129, 65129 },
|
||||||
|
{ 65284, 65284 },
|
||||||
|
{ 65504, 65505 },
|
||||||
|
{ 65509, 65510 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Zl_range16[] = { // Line separator
|
||||||
|
{ 8232, 8232 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Zp_range16[] = { // Paragraph separator
|
||||||
|
{ 8233, 8233 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Zs_range16[] = { // Space separator
|
||||||
|
{ 32, 32 },
|
||||||
|
{ 160, 160 },
|
||||||
|
{ 5760, 5760 },
|
||||||
|
{ 8192, 8202 },
|
||||||
|
{ 8239, 8239 },
|
||||||
|
{ 8287, 8287 },
|
||||||
|
{ 12288, 12288 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Arabic_range16[] = {
|
||||||
|
{ 1536, 1540 },
|
||||||
|
{ 1542, 1547 },
|
||||||
|
{ 1549, 1562 },
|
||||||
|
{ 1564, 1566 },
|
||||||
|
{ 1568, 1599 },
|
||||||
|
{ 1601, 1610 },
|
||||||
|
{ 1622, 1647 },
|
||||||
|
{ 1649, 1756 },
|
||||||
|
{ 1758, 1791 },
|
||||||
|
{ 1872, 1919 },
|
||||||
|
{ 2160, 2190 },
|
||||||
|
{ 2192, 2193 },
|
||||||
|
{ 2200, 2273 },
|
||||||
|
{ 2275, 2303 },
|
||||||
|
{ 64336, 64450 },
|
||||||
|
{ 64467, 64829 },
|
||||||
|
{ 64832, 64911 },
|
||||||
|
{ 64914, 64967 },
|
||||||
|
{ 64975, 64975 },
|
||||||
|
{ 65008, 65023 },
|
||||||
|
{ 65136, 65140 },
|
||||||
|
{ 65142, 65276 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Bengali_range16[] = {
|
||||||
|
{ 2432, 2435 },
|
||||||
|
{ 2437, 2444 },
|
||||||
|
{ 2447, 2448 },
|
||||||
|
{ 2451, 2472 },
|
||||||
|
{ 2474, 2480 },
|
||||||
|
{ 2482, 2482 },
|
||||||
|
{ 2486, 2489 },
|
||||||
|
{ 2492, 2500 },
|
||||||
|
{ 2503, 2504 },
|
||||||
|
{ 2507, 2510 },
|
||||||
|
{ 2519, 2519 },
|
||||||
|
{ 2524, 2525 },
|
||||||
|
{ 2527, 2531 },
|
||||||
|
{ 2534, 2558 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Cyrillic_range16[] = {
|
||||||
|
{ 1024, 1156 },
|
||||||
|
{ 1159, 1327 },
|
||||||
|
{ 7296, 7304 },
|
||||||
|
{ 7467, 7467 },
|
||||||
|
{ 7544, 7544 },
|
||||||
|
{ 11744, 11775 },
|
||||||
|
{ 42560, 42655 },
|
||||||
|
{ 65070, 65071 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Devanagari_range16[] = {
|
||||||
|
{ 2304, 2384 },
|
||||||
|
{ 2389, 2403 },
|
||||||
|
{ 2406, 2431 },
|
||||||
|
{ 43232, 43263 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Georgian_range16[] = {
|
||||||
|
{ 4256, 4293 },
|
||||||
|
{ 4295, 4295 },
|
||||||
|
{ 4301, 4301 },
|
||||||
|
{ 4304, 4346 },
|
||||||
|
{ 4348, 4351 },
|
||||||
|
{ 7312, 7354 },
|
||||||
|
{ 7357, 7359 },
|
||||||
|
{ 11520, 11557 },
|
||||||
|
{ 11559, 11559 },
|
||||||
|
{ 11565, 11565 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Greek_range16[] = {
|
||||||
|
{ 880, 883 },
|
||||||
|
{ 885, 887 },
|
||||||
|
{ 890, 893 },
|
||||||
|
{ 895, 895 },
|
||||||
|
{ 900, 900 },
|
||||||
|
{ 902, 902 },
|
||||||
|
{ 904, 906 },
|
||||||
|
{ 908, 908 },
|
||||||
|
{ 910, 929 },
|
||||||
|
{ 931, 993 },
|
||||||
|
{ 1008, 1023 },
|
||||||
|
{ 7462, 7466 },
|
||||||
|
{ 7517, 7521 },
|
||||||
|
{ 7526, 7530 },
|
||||||
|
{ 7615, 7615 },
|
||||||
|
{ 7936, 7957 },
|
||||||
|
{ 7960, 7965 },
|
||||||
|
{ 7968, 8005 },
|
||||||
|
{ 8008, 8013 },
|
||||||
|
{ 8016, 8023 },
|
||||||
|
{ 8025, 8025 },
|
||||||
|
{ 8027, 8027 },
|
||||||
|
{ 8029, 8029 },
|
||||||
|
{ 8031, 8061 },
|
||||||
|
{ 8064, 8116 },
|
||||||
|
{ 8118, 8132 },
|
||||||
|
{ 8134, 8147 },
|
||||||
|
{ 8150, 8155 },
|
||||||
|
{ 8157, 8175 },
|
||||||
|
{ 8178, 8180 },
|
||||||
|
{ 8182, 8190 },
|
||||||
|
{ 8486, 8486 },
|
||||||
|
{ 43877, 43877 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Han_range16[] = {
|
||||||
|
{ 11904, 11929 },
|
||||||
|
{ 11931, 12019 },
|
||||||
|
{ 12032, 12245 },
|
||||||
|
{ 12293, 12293 },
|
||||||
|
{ 12295, 12295 },
|
||||||
|
{ 12321, 12329 },
|
||||||
|
{ 12344, 12347 },
|
||||||
|
{ 13312, 19903 },
|
||||||
|
{ 19968, 40959 },
|
||||||
|
{ 63744, 64109 },
|
||||||
|
{ 64112, 64217 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Hiragana_range16[] = {
|
||||||
|
{ 12353, 12438 },
|
||||||
|
{ 12445, 12447 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Katakana_range16[] = {
|
||||||
|
{ 12449, 12538 },
|
||||||
|
{ 12541, 12543 },
|
||||||
|
{ 12784, 12799 },
|
||||||
|
{ 13008, 13054 },
|
||||||
|
{ 13056, 13143 },
|
||||||
|
{ 65382, 65391 },
|
||||||
|
{ 65393, 65437 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Latin_range16[] = {
|
||||||
|
{ 65, 90 },
|
||||||
|
{ 97, 122 },
|
||||||
|
{ 170, 170 },
|
||||||
|
{ 186, 186 },
|
||||||
|
{ 192, 214 },
|
||||||
|
{ 216, 246 },
|
||||||
|
{ 248, 696 },
|
||||||
|
{ 736, 740 },
|
||||||
|
{ 7424, 7461 },
|
||||||
|
{ 7468, 7516 },
|
||||||
|
{ 7522, 7525 },
|
||||||
|
{ 7531, 7543 },
|
||||||
|
{ 7545, 7614 },
|
||||||
|
{ 7680, 7935 },
|
||||||
|
{ 8305, 8305 },
|
||||||
|
{ 8319, 8319 },
|
||||||
|
{ 8336, 8348 },
|
||||||
|
{ 8490, 8491 },
|
||||||
|
{ 8498, 8498 },
|
||||||
|
{ 8526, 8526 },
|
||||||
|
{ 8544, 8584 },
|
||||||
|
{ 11360, 11391 },
|
||||||
|
{ 42786, 42887 },
|
||||||
|
{ 42891, 42954 },
|
||||||
|
{ 42960, 42961 },
|
||||||
|
{ 42963, 42963 },
|
||||||
|
{ 42965, 42969 },
|
||||||
|
{ 42994, 43007 },
|
||||||
|
{ 43824, 43866 },
|
||||||
|
{ 43868, 43876 },
|
||||||
|
{ 43878, 43881 },
|
||||||
|
{ 64256, 64262 },
|
||||||
|
{ 65313, 65338 },
|
||||||
|
{ 65345, 65370 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const URange16 Thai_range16[] = {
|
||||||
|
{ 3585, 3642 },
|
||||||
|
{ 3648, 3675 },
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define _e_arg(k, v) v
|
||||||
|
#else
|
||||||
|
#define _e_arg(k, v) [k] = v
|
||||||
|
#endif
|
||||||
|
#define UNI_ENTRY(Code) { Code##_range16, sizeof(Code##_range16)/sizeof(URange16) }
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const URange16 *r16;
|
||||||
|
int nr16;
|
||||||
|
} UGroup;
|
||||||
|
|
||||||
|
static const UGroup _utf8_unicode_groups[U8G_SIZE] = {
|
||||||
|
_e_arg(U8G_Cc, UNI_ENTRY(Cc)),
|
||||||
|
_e_arg(U8G_Lt, UNI_ENTRY(Lt)),
|
||||||
|
_e_arg(U8G_Nd, UNI_ENTRY(Nd)),
|
||||||
|
_e_arg(U8G_Nl, UNI_ENTRY(Nl)),
|
||||||
|
_e_arg(U8G_Pc, UNI_ENTRY(Pc)),
|
||||||
|
_e_arg(U8G_Pd, UNI_ENTRY(Pd)),
|
||||||
|
_e_arg(U8G_Pf, UNI_ENTRY(Pf)),
|
||||||
|
_e_arg(U8G_Pi, UNI_ENTRY(Pi)),
|
||||||
|
_e_arg(U8G_Sc, UNI_ENTRY(Sc)),
|
||||||
|
_e_arg(U8G_Zl, UNI_ENTRY(Zl)),
|
||||||
|
_e_arg(U8G_Zp, UNI_ENTRY(Zp)),
|
||||||
|
_e_arg(U8G_Zs, UNI_ENTRY(Zs)),
|
||||||
|
_e_arg(U8G_Arabic, UNI_ENTRY(Arabic)),
|
||||||
|
_e_arg(U8G_Bengali, UNI_ENTRY(Bengali)),
|
||||||
|
_e_arg(U8G_Cyrillic, UNI_ENTRY(Cyrillic)),
|
||||||
|
_e_arg(U8G_Devanagari, UNI_ENTRY(Devanagari)),
|
||||||
|
_e_arg(U8G_Georgian, UNI_ENTRY(Georgian)),
|
||||||
|
_e_arg(U8G_Greek, UNI_ENTRY(Greek)),
|
||||||
|
_e_arg(U8G_Han, UNI_ENTRY(Han)),
|
||||||
|
_e_arg(U8G_Hiragana, UNI_ENTRY(Hiragana)),
|
||||||
|
_e_arg(U8G_Katakana, UNI_ENTRY(Katakana)),
|
||||||
|
_e_arg(U8G_Latin, UNI_ENTRY(Latin)),
|
||||||
|
_e_arg(U8G_Thai, UNI_ENTRY(Thai)),
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool utf8_isgroup(int group, uint32_t c) {
|
||||||
|
for (int j=0; j<_utf8_unicode_groups[group].nr16; ++j) {
|
||||||
|
if (c < _utf8_unicode_groups[group].r16[j].lo)
|
||||||
|
return false;
|
||||||
|
if (c <= _utf8_unicode_groups[group].r16[j].hi)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_UCD_PRV_C_INCLUDED
|
||||||
177
stc/priv/utf8_prv.c
Normal file
177
stc/priv/utf8_prv.c
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef STC_UTF8_PRV_C_INCLUDED
|
||||||
|
#define STC_UTF8_PRV_C_INCLUDED
|
||||||
|
|
||||||
|
#include "utf8_tab.c"
|
||||||
|
|
||||||
|
const uint8_t utf8_dtab[] = {
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||||
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||||
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||||
|
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||||
|
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
|
||||||
|
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
|
||||||
|
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
|
||||||
|
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
|
||||||
|
12,36,12,12,12,12,12,12,12,12,12,12,
|
||||||
|
};
|
||||||
|
|
||||||
|
int utf8_encode(char *out, uint32_t c) {
|
||||||
|
if (c < 0x80U) {
|
||||||
|
out[0] = (char) c;
|
||||||
|
return 1;
|
||||||
|
} else if (c < 0x0800U) {
|
||||||
|
out[0] = (char) ((c>>6 & 0x1F) | 0xC0);
|
||||||
|
out[1] = (char) ((c & 0x3F) | 0x80);
|
||||||
|
return 2;
|
||||||
|
} else if (c < 0x010000U) {
|
||||||
|
if ((c < 0xD800U) | (c >= 0xE000U)) {
|
||||||
|
out[0] = (char) ((c>>12 & 0x0F) | 0xE0);
|
||||||
|
out[1] = (char) ((c>>6 & 0x3F) | 0x80);
|
||||||
|
out[2] = (char) ((c & 0x3F) | 0x80);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
} else if (c < 0x110000U) {
|
||||||
|
out[0] = (char) ((c>>18 & 0x07) | 0xF0);
|
||||||
|
out[1] = (char) ((c>>12 & 0x3F) | 0x80);
|
||||||
|
out[2] = (char) ((c>>6 & 0x3F) | 0x80);
|
||||||
|
out[3] = (char) ((c & 0x3F) | 0x80);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t utf8_peek_at(const char* s, isize offset) {
|
||||||
|
return utf8_peek(utf8_offset(s, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utf8_valid(const char* s) {
|
||||||
|
utf8_decode_t d = {.state=0};
|
||||||
|
while ((utf8_decode(&d, (uint8_t)*s) != utf8_REJECT) & (*s != '\0'))
|
||||||
|
++s;
|
||||||
|
return d.state == utf8_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utf8_valid_n(const char* s, isize nbytes) {
|
||||||
|
utf8_decode_t d = {.state=0};
|
||||||
|
for (; nbytes-- != 0; ++s)
|
||||||
|
if ((utf8_decode(&d, (uint8_t)*s) == utf8_REJECT) | (*s == '\0'))
|
||||||
|
break;
|
||||||
|
return d.state == utf8_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _binsearch(c, at, N, ret) do { \
|
||||||
|
int _n = N, _i = 0, _mid = _n/2; \
|
||||||
|
while (_n > 0) { \
|
||||||
|
if (at(_i + _mid) < c) { \
|
||||||
|
_i += _mid + 1; \
|
||||||
|
_n -= _mid + 1; \
|
||||||
|
_mid = _n*7/8; \
|
||||||
|
} else { \
|
||||||
|
_n = _mid; \
|
||||||
|
_mid = _n/8; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
ret = (_i >= N || at(_i) < c) ? N : _i; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
uint32_t utf8_casefold(uint32_t c) {
|
||||||
|
#define _at_fold(idx) casemappings[idx].c2
|
||||||
|
int i;
|
||||||
|
_binsearch(c, _at_fold, casefold_len, i);
|
||||||
|
if (i < casefold_len && casemappings[i].c1 <= c && c <= casemappings[i].c2) {
|
||||||
|
const struct CaseMapping entry = casemappings[i];
|
||||||
|
int d = entry.m2 - entry.c2;
|
||||||
|
if (d == 1) return c + ((entry.c2 & 1U) == (c & 1U));
|
||||||
|
return (uint32_t)((int)c + d);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t utf8_tolower(uint32_t c) {
|
||||||
|
#define _at_upper(idx) casemappings[upcase_ind[idx]].c2
|
||||||
|
int i, n = c_countof(upcase_ind);
|
||||||
|
_binsearch(c, _at_upper, n, i);
|
||||||
|
if (i < n) {
|
||||||
|
const struct CaseMapping entry = casemappings[upcase_ind[i]];
|
||||||
|
if (entry.c1 <= c && c <= entry.c2) {
|
||||||
|
int d = entry.m2 - entry.c2;
|
||||||
|
if (d == 1) return c + ((entry.c2 & 1U) == (c & 1U));
|
||||||
|
return (uint32_t)((int)c + d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t utf8_toupper(uint32_t c) {
|
||||||
|
#define _at_lower(idx) casemappings[lowcase_ind[idx]].m2
|
||||||
|
int i, n = c_countof(lowcase_ind);
|
||||||
|
_binsearch(c, _at_lower, n, i);
|
||||||
|
if (i < n) {
|
||||||
|
const struct CaseMapping entry = casemappings[lowcase_ind[i]];
|
||||||
|
int d = entry.m2 - entry.c2;
|
||||||
|
if (entry.c1 + (uint32_t)d <= c && c <= entry.m2) {
|
||||||
|
if (d == 1) return c - ((entry.m2 & 1U) == (c & 1U));
|
||||||
|
return (uint32_t)((int)c - d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int utf8_decode_codepoint(utf8_decode_t* d, const char* s, const char* end) { // s < end
|
||||||
|
const char* start = s;
|
||||||
|
do switch (utf8_decode(d, (uint8_t)*s++)) {
|
||||||
|
case utf8_ACCEPT: return (int)(s - start);
|
||||||
|
case utf8_REJECT: goto recover;
|
||||||
|
} while (s != end);
|
||||||
|
|
||||||
|
recover: // non-complete utf8 is also treated as utf8_REJECT
|
||||||
|
d->state = utf8_ACCEPT;
|
||||||
|
d->codep = 0xFFFD;
|
||||||
|
//return 1;
|
||||||
|
int n = (int)(s - start);
|
||||||
|
return n > 2 ? n - 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int utf8_icompare(const csview s1, const csview s2) {
|
||||||
|
utf8_decode_t d1 = {.state=0}, d2 = {.state=0};
|
||||||
|
const char *e1 = s1.buf + s1.size, *e2 = s2.buf + s2.size;
|
||||||
|
isize j1 = 0, j2 = 0;
|
||||||
|
while ((j1 < s1.size) & (j2 < s2.size)) {
|
||||||
|
if (s2.buf[j2] == '\0') return s1.buf[j1];
|
||||||
|
|
||||||
|
j1 += utf8_decode_codepoint(&d1, s1.buf + j1, e1);
|
||||||
|
j2 += utf8_decode_codepoint(&d2, s2.buf + j2, e2);
|
||||||
|
|
||||||
|
int32_t c = (int32_t)utf8_casefold(d1.codep) - (int32_t)utf8_casefold(d2.codep);
|
||||||
|
if (c != 0) return (int)c;
|
||||||
|
}
|
||||||
|
return (int)(s1.size - s2.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_UTF8_PRV_C_INCLUDED
|
||||||
127
stc/priv/utf8_prv.h
Normal file
127
stc/priv/utf8_prv.h
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private, include "stc/utf8.h"
|
||||||
|
#ifndef STC_UTF8_PRV_H_INCLUDED
|
||||||
|
#define STC_UTF8_PRV_H_INCLUDED
|
||||||
|
|
||||||
|
// The following functions assume valid utf8 strings:
|
||||||
|
|
||||||
|
/* number of bytes in the utf8 codepoint from s */
|
||||||
|
STC_INLINE int utf8_chr_size(const char *s) {
|
||||||
|
unsigned b = (uint8_t)*s;
|
||||||
|
if (b < 0x80) return 1;
|
||||||
|
/*if (b < 0xC2) return 0;*/
|
||||||
|
if (b < 0xE0) return 2;
|
||||||
|
if (b < 0xF0) return 3;
|
||||||
|
/*if (b < 0xF5)*/ return 4;
|
||||||
|
/*return 0;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* number of codepoints in the utf8 string s */
|
||||||
|
STC_INLINE isize utf8_count(const char *s) {
|
||||||
|
isize size = 0;
|
||||||
|
while (*s)
|
||||||
|
size += (*++s & 0xC0) != 0x80;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize utf8_count_n(const char *s, isize nbytes) {
|
||||||
|
isize size = 0;
|
||||||
|
while ((nbytes-- != 0) & (*s != 0)) {
|
||||||
|
size += (*++s & 0xC0) != 0x80;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE const char* utf8_at(const char *s, isize u8pos) {
|
||||||
|
while ((u8pos > 0) & (*s != 0))
|
||||||
|
u8pos -= (*++s & 0xC0) != 0x80;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE const char* utf8_offset(const char* s, isize u8pos) {
|
||||||
|
int inc = 1;
|
||||||
|
if (u8pos < 0) u8pos = -u8pos, inc = -1;
|
||||||
|
while (u8pos && *s)
|
||||||
|
u8pos -= (*(s += inc) & 0xC0) != 0x80;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize utf8_to_index(const char* s, isize u8pos)
|
||||||
|
{ return utf8_at(s, u8pos) - s; }
|
||||||
|
|
||||||
|
STC_INLINE csview utf8_subview(const char *s, isize u8pos, isize u8len) {
|
||||||
|
csview span;
|
||||||
|
span.buf = utf8_at(s, u8pos);
|
||||||
|
span.size = utf8_to_index(span.buf, u8len);
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// The following requires linking with utf8 symbols.
|
||||||
|
// To call them, either define i_import before including
|
||||||
|
// one of cstr, csview, zsview, or link with src/libstc.o.
|
||||||
|
|
||||||
|
/* decode next utf8 codepoint. https://bjoern.hoehrmann.de/utf-8/decoder/dfa */
|
||||||
|
typedef struct { uint32_t state, codep; } utf8_decode_t;
|
||||||
|
extern const uint8_t utf8_dtab[]; /* utf8code.c */
|
||||||
|
#define utf8_ACCEPT 0
|
||||||
|
#define utf8_REJECT 12
|
||||||
|
|
||||||
|
extern bool utf8_valid(const char* s);
|
||||||
|
extern bool utf8_valid_n(const char* s, isize nbytes);
|
||||||
|
extern int utf8_encode(char *out, uint32_t c);
|
||||||
|
extern int utf8_decode_codepoint(utf8_decode_t* d, const char* s, const char* end);
|
||||||
|
extern int utf8_icompare(const csview s1, const csview s2);
|
||||||
|
extern uint32_t utf8_peek_at(const char* s, isize u8offset);
|
||||||
|
extern uint32_t utf8_casefold(uint32_t c);
|
||||||
|
extern uint32_t utf8_tolower(uint32_t c);
|
||||||
|
extern uint32_t utf8_toupper(uint32_t c);
|
||||||
|
|
||||||
|
STC_INLINE bool utf8_isupper(uint32_t c)
|
||||||
|
{ return utf8_tolower(c) != c; }
|
||||||
|
|
||||||
|
STC_INLINE bool utf8_islower(uint32_t c)
|
||||||
|
{ return utf8_toupper(c) != c; }
|
||||||
|
|
||||||
|
STC_INLINE uint32_t utf8_decode(utf8_decode_t* d, const uint32_t byte) {
|
||||||
|
const uint32_t type = utf8_dtab[byte];
|
||||||
|
d->codep = d->state ? (byte & 0x3fu) | (d->codep << 6)
|
||||||
|
: (0xffU >> type) & byte;
|
||||||
|
return d->state = utf8_dtab[256 + d->state + type];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE uint32_t utf8_peek(const char* s) {
|
||||||
|
utf8_decode_t d = {.state=0};
|
||||||
|
do {
|
||||||
|
utf8_decode(&d, (uint8_t)*s++);
|
||||||
|
} while (d.state > utf8_REJECT);
|
||||||
|
return d.state == utf8_ACCEPT ? d.codep : 0xFFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* case-insensitive utf8 string comparison */
|
||||||
|
STC_INLINE int utf8_icmp(const char* s1, const char* s2) {
|
||||||
|
return utf8_icompare(c_sv(s1, INTPTR_MAX), c_sv(s2, INTPTR_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_UTF8_PRV_H_INCLUDED
|
||||||
250
stc/priv/utf8_tab.c
Normal file
250
stc/priv/utf8_tab.c
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
|
||||||
|
struct CaseMapping { uint16_t c1, c2, m2; };
|
||||||
|
|
||||||
|
static struct CaseMapping casemappings[] = {
|
||||||
|
{0x0041, 0x005A, 0x007A}, // A a (26) LATIN CAPITAL LETTER A
|
||||||
|
{0x00B5, 0x00B5, 0x03BC}, // µ μ ( 1) MICRO SIGN
|
||||||
|
{0x00C0, 0x00D6, 0x00F6}, // À à (23) LATIN CAPITAL LETTER A WITH GRAVE
|
||||||
|
{0x00D8, 0x00DE, 0x00FE}, // Ø ø ( 7) LATIN CAPITAL LETTER O WITH STROKE
|
||||||
|
{0x0100, 0x012E, 0x012F}, // Ā ā (24) LATIN CAPITAL LETTER A WITH MACRON
|
||||||
|
{0x0132, 0x0136, 0x0137}, // IJ ij ( 3) LATIN CAPITAL LIGATURE IJ
|
||||||
|
{0x0139, 0x0147, 0x0148}, // Ĺ ĺ ( 8) LATIN CAPITAL LETTER L WITH ACUTE
|
||||||
|
{0x014A, 0x0176, 0x0177}, // Ŋ ŋ (23) LATIN CAPITAL LETTER ENG
|
||||||
|
{0x0178, 0x0178, 0x00FF}, // Ÿ ÿ ( 1) LATIN CAPITAL LETTER Y WITH DIAERESIS
|
||||||
|
{0x0179, 0x017D, 0x017E}, // Ź ź ( 3) LATIN CAPITAL LETTER Z WITH ACUTE
|
||||||
|
{0x017F, 0x017F, 0x0073}, // ſ s ( 1) LATIN SMALL LETTER LONG S
|
||||||
|
{0x0181, 0x0181, 0x0253}, // Ɓ ɓ ( 1) LATIN CAPITAL LETTER B WITH HOOK
|
||||||
|
{0x0182, 0x0184, 0x0185}, // Ƃ ƃ ( 2) LATIN CAPITAL LETTER B WITH TOPBAR
|
||||||
|
{0x0186, 0x0186, 0x0254}, // Ɔ ɔ ( 1) LATIN CAPITAL LETTER OPEN O
|
||||||
|
{0x0187, 0x0187, 0x0188}, // Ƈ ƈ ( 1) LATIN CAPITAL LETTER C WITH HOOK
|
||||||
|
{0x0189, 0x018A, 0x0257}, // Ɖ ɖ ( 2) LATIN CAPITAL LETTER AFRICAN D
|
||||||
|
{0x018B, 0x018B, 0x018C}, // Ƌ ƌ ( 1) LATIN CAPITAL LETTER D WITH TOPBAR
|
||||||
|
{0x018E, 0x018E, 0x01DD}, // Ǝ ǝ ( 1) LATIN CAPITAL LETTER REVERSED E
|
||||||
|
{0x018F, 0x018F, 0x0259}, // Ə ə ( 1) LATIN CAPITAL LETTER SCHWA
|
||||||
|
{0x0190, 0x0190, 0x025B}, // Ɛ ɛ ( 1) LATIN CAPITAL LETTER OPEN E
|
||||||
|
{0x0191, 0x0191, 0x0192}, // Ƒ ƒ ( 1) LATIN CAPITAL LETTER F WITH HOOK
|
||||||
|
{0x0193, 0x0193, 0x0260}, // Ɠ ɠ ( 1) LATIN CAPITAL LETTER G WITH HOOK
|
||||||
|
{0x0194, 0x0194, 0x0263}, // Ɣ ɣ ( 1) LATIN CAPITAL LETTER GAMMA
|
||||||
|
{0x0196, 0x0196, 0x0269}, // Ɩ ɩ ( 1) LATIN CAPITAL LETTER IOTA
|
||||||
|
{0x0197, 0x0197, 0x0268}, // Ɨ ɨ ( 1) LATIN CAPITAL LETTER I WITH STROKE
|
||||||
|
{0x0198, 0x0198, 0x0199}, // Ƙ ƙ ( 1) LATIN CAPITAL LETTER K WITH HOOK
|
||||||
|
{0x019C, 0x019C, 0x026F}, // Ɯ ɯ ( 1) LATIN CAPITAL LETTER TURNED M
|
||||||
|
{0x019D, 0x019D, 0x0272}, // Ɲ ɲ ( 1) LATIN CAPITAL LETTER N WITH LEFT HOOK
|
||||||
|
{0x019F, 0x019F, 0x0275}, // Ɵ ɵ ( 1) LATIN CAPITAL LETTER O WITH MIDDLE TILDE
|
||||||
|
{0x01A0, 0x01A4, 0x01A5}, // Ơ ơ ( 3) LATIN CAPITAL LETTER O WITH HORN
|
||||||
|
{0x01A6, 0x01A6, 0x0280}, // Ʀ ʀ ( 1) LATIN LETTER YR
|
||||||
|
{0x01A7, 0x01A7, 0x01A8}, // Ƨ ƨ ( 1) LATIN CAPITAL LETTER TONE TWO
|
||||||
|
{0x01A9, 0x01A9, 0x0283}, // Ʃ ʃ ( 1) LATIN CAPITAL LETTER ESH
|
||||||
|
{0x01AC, 0x01AC, 0x01AD}, // Ƭ ƭ ( 1) LATIN CAPITAL LETTER T WITH HOOK
|
||||||
|
{0x01AE, 0x01AE, 0x0288}, // Ʈ ʈ ( 1) LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
|
||||||
|
{0x01AF, 0x01AF, 0x01B0}, // Ư ư ( 1) LATIN CAPITAL LETTER U WITH HORN
|
||||||
|
{0x01B1, 0x01B2, 0x028B}, // Ʊ ʊ ( 2) LATIN CAPITAL LETTER UPSILON
|
||||||
|
{0x01B3, 0x01B5, 0x01B6}, // Ƴ ƴ ( 2) LATIN CAPITAL LETTER Y WITH HOOK
|
||||||
|
{0x01B7, 0x01B7, 0x0292}, // Ʒ ʒ ( 1) LATIN CAPITAL LETTER EZH
|
||||||
|
{0x01B8, 0x01B8, 0x01B9}, // Ƹ ƹ ( 1) LATIN CAPITAL LETTER EZH REVERSED
|
||||||
|
{0x01BC, 0x01BC, 0x01BD}, // Ƽ ƽ ( 1) LATIN CAPITAL LETTER TONE FIVE
|
||||||
|
{0x01C4, 0x01C4, 0x01C6}, // DŽ dž ( 1) LATIN CAPITAL LETTER DZ WITH CARON
|
||||||
|
{0x01C5, 0x01C5, 0x01C6}, // Dž dž ( 1) LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
|
||||||
|
{0x01C7, 0x01C7, 0x01C9}, // LJ lj ( 1) LATIN CAPITAL LETTER LJ
|
||||||
|
{0x01C8, 0x01C8, 0x01C9}, // Lj lj ( 1) LATIN CAPITAL LETTER L WITH SMALL LETTER J
|
||||||
|
{0x01CA, 0x01CA, 0x01CC}, // NJ nj ( 1) LATIN CAPITAL LETTER NJ
|
||||||
|
{0x01CB, 0x01DB, 0x01DC}, // Nj nj ( 9) LATIN CAPITAL LETTER N WITH SMALL LETTER J
|
||||||
|
{0x01DE, 0x01EE, 0x01EF}, // Ǟ ǟ ( 9) LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
|
||||||
|
{0x01F1, 0x01F1, 0x01F3}, // DZ dz ( 1) LATIN CAPITAL LETTER DZ
|
||||||
|
{0x01F2, 0x01F4, 0x01F5}, // Dz dz ( 2) LATIN CAPITAL LETTER D WITH SMALL LETTER Z
|
||||||
|
{0x01F6, 0x01F6, 0x0195}, // Ƕ ƕ ( 1) LATIN CAPITAL LETTER HWAIR
|
||||||
|
{0x01F7, 0x01F7, 0x01BF}, // Ƿ ƿ ( 1) LATIN CAPITAL LETTER WYNN
|
||||||
|
{0x01F8, 0x021E, 0x021F}, // Ǹ ǹ (20) LATIN CAPITAL LETTER N WITH GRAVE
|
||||||
|
{0x0220, 0x0220, 0x019E}, // Ƞ ƞ ( 1) LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
|
||||||
|
{0x0222, 0x0232, 0x0233}, // Ȣ ȣ ( 9) LATIN CAPITAL LETTER OU
|
||||||
|
{0x023A, 0x023A, 0x2C65}, // Ⱥ ⱥ ( 1) LATIN CAPITAL LETTER A WITH STROKE
|
||||||
|
{0x023B, 0x023B, 0x023C}, // Ȼ ȼ ( 1) LATIN CAPITAL LETTER C WITH STROKE
|
||||||
|
{0x023D, 0x023D, 0x019A}, // Ƚ ƚ ( 1) LATIN CAPITAL LETTER L WITH BAR
|
||||||
|
{0x023E, 0x023E, 0x2C66}, // Ⱦ ⱦ ( 1) LATIN CAPITAL LETTER T WITH DIAGONAL STROKE
|
||||||
|
{0x0241, 0x0241, 0x0242}, // Ɂ ɂ ( 1) LATIN CAPITAL LETTER GLOTTAL STOP
|
||||||
|
{0x0243, 0x0243, 0x0180}, // Ƀ ƀ ( 1) LATIN CAPITAL LETTER B WITH STROKE
|
||||||
|
{0x0244, 0x0244, 0x0289}, // Ʉ ʉ ( 1) LATIN CAPITAL LETTER U BAR
|
||||||
|
{0x0245, 0x0245, 0x028C}, // Ʌ ʌ ( 1) LATIN CAPITAL LETTER TURNED V
|
||||||
|
{0x0246, 0x024E, 0x024F}, // Ɇ ɇ ( 5) LATIN CAPITAL LETTER E WITH STROKE
|
||||||
|
{0x0345, 0x0345, 0x03B9}, // ͅ ι ( 1) COMBINING GREEK YPOGEGRAMMENI
|
||||||
|
{0x0370, 0x0372, 0x0373}, // Ͱ ͱ ( 2) GREEK CAPITAL LETTER HETA
|
||||||
|
{0x0376, 0x0376, 0x0377}, // Ͷ ͷ ( 1) GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
|
||||||
|
{0x037F, 0x037F, 0x03F3}, // Ϳ ϳ ( 1) GREEK CAPITAL LETTER YOT
|
||||||
|
{0x0386, 0x0386, 0x03AC}, // Ά ά ( 1) GREEK CAPITAL LETTER ALPHA WITH TONOS
|
||||||
|
{0x0388, 0x038A, 0x03AF}, // Έ έ ( 3) GREEK CAPITAL LETTER EPSILON WITH TONOS
|
||||||
|
{0x038C, 0x038C, 0x03CC}, // Ό ό ( 1) GREEK CAPITAL LETTER OMICRON WITH TONOS
|
||||||
|
{0x038E, 0x038F, 0x03CE}, // Ύ ύ ( 2) GREEK CAPITAL LETTER UPSILON WITH TONOS
|
||||||
|
{0x0391, 0x03A1, 0x03C1}, // Α α (17) GREEK CAPITAL LETTER ALPHA
|
||||||
|
{0x03A3, 0x03AB, 0x03CB}, // Σ σ ( 9) GREEK CAPITAL LETTER SIGMA
|
||||||
|
{0x03C2, 0x03C2, 0x03C3}, // ς σ ( 1) GREEK SMALL LETTER FINAL SIGMA
|
||||||
|
{0x03CF, 0x03CF, 0x03D7}, // Ϗ ϗ ( 1) GREEK CAPITAL KAI SYMBOL
|
||||||
|
{0x03D0, 0x03D0, 0x03B2}, // ϐ β ( 1) GREEK BETA SYMBOL
|
||||||
|
{0x03D1, 0x03D1, 0x03B8}, // ϑ θ ( 1) GREEK THETA SYMBOL
|
||||||
|
{0x03D5, 0x03D5, 0x03C6}, // ϕ φ ( 1) GREEK PHI SYMBOL
|
||||||
|
{0x03D6, 0x03D6, 0x03C0}, // ϖ π ( 1) GREEK PI SYMBOL
|
||||||
|
{0x03D8, 0x03EE, 0x03EF}, // Ϙ ϙ (12) GREEK LETTER ARCHAIC KOPPA
|
||||||
|
{0x03F0, 0x03F0, 0x03BA}, // ϰ κ ( 1) GREEK KAPPA SYMBOL
|
||||||
|
{0x03F1, 0x03F1, 0x03C1}, // ϱ ρ ( 1) GREEK RHO SYMBOL
|
||||||
|
{0x03F4, 0x03F4, 0x03B8}, // ϴ θ ( 1) GREEK CAPITAL THETA SYMBOL
|
||||||
|
{0x03F5, 0x03F5, 0x03B5}, // ϵ ε ( 1) GREEK LUNATE EPSILON SYMBOL
|
||||||
|
{0x03F7, 0x03F7, 0x03F8}, // Ϸ ϸ ( 1) GREEK CAPITAL LETTER SHO
|
||||||
|
{0x03F9, 0x03F9, 0x03F2}, // Ϲ ϲ ( 1) GREEK CAPITAL LUNATE SIGMA SYMBOL
|
||||||
|
{0x03FA, 0x03FA, 0x03FB}, // Ϻ ϻ ( 1) GREEK CAPITAL LETTER SAN
|
||||||
|
{0x03FD, 0x03FF, 0x037D}, // Ͻ ͻ ( 3) GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL
|
||||||
|
{0x0400, 0x040F, 0x045F}, // Ѐ ѐ (16) CYRILLIC CAPITAL LETTER IE WITH GRAVE
|
||||||
|
{0x0410, 0x042F, 0x044F}, // А а (32) CYRILLIC CAPITAL LETTER A
|
||||||
|
{0x0460, 0x0480, 0x0481}, // Ѡ ѡ (17) CYRILLIC CAPITAL LETTER OMEGA
|
||||||
|
{0x048A, 0x04BE, 0x04BF}, // Ҋ ҋ (27) CYRILLIC CAPITAL LETTER SHORT I WITH TAIL
|
||||||
|
{0x04C0, 0x04C0, 0x04CF}, // Ӏ ӏ ( 1) CYRILLIC LETTER PALOCHKA
|
||||||
|
{0x04C1, 0x04CD, 0x04CE}, // Ӂ ӂ ( 7) CYRILLIC CAPITAL LETTER ZHE WITH BREVE
|
||||||
|
{0x04D0, 0x052E, 0x052F}, // Ӑ ӑ (48) CYRILLIC CAPITAL LETTER A WITH BREVE
|
||||||
|
{0x0531, 0x0556, 0x0586}, // Ա ա (38) ARMENIAN CAPITAL LETTER AYB
|
||||||
|
{0x10A0, 0x10C5, 0x2D25}, // Ⴀ ⴀ (38) GEORGIAN CAPITAL LETTER AN
|
||||||
|
{0x10C7, 0x10C7, 0x2D27}, // Ⴧ ⴧ ( 1) GEORGIAN CAPITAL LETTER YN
|
||||||
|
{0x10CD, 0x10CD, 0x2D2D}, // Ⴭ ⴭ ( 1) GEORGIAN CAPITAL LETTER AEN
|
||||||
|
{0x13F8, 0x13FD, 0x13F5}, // ᏸ Ᏸ ( 6) CHEROKEE SMALL LETTER YE
|
||||||
|
{0x1C80, 0x1C80, 0x0432}, // ᲀ в ( 1) CYRILLIC SMALL LETTER ROUNDED VE
|
||||||
|
{0x1C81, 0x1C81, 0x0434}, // ᲁ д ( 1) CYRILLIC SMALL LETTER LONG-LEGGED DE
|
||||||
|
{0x1C82, 0x1C82, 0x043E}, // ᲂ о ( 1) CYRILLIC SMALL LETTER NARROW O
|
||||||
|
{0x1C83, 0x1C84, 0x0442}, // ᲃ с ( 2) CYRILLIC SMALL LETTER WIDE ES
|
||||||
|
{0x1C85, 0x1C85, 0x0442}, // ᲅ т ( 1) CYRILLIC SMALL LETTER THREE-LEGGED TE
|
||||||
|
{0x1C86, 0x1C86, 0x044A}, // ᲆ ъ ( 1) CYRILLIC SMALL LETTER TALL HARD SIGN
|
||||||
|
{0x1C87, 0x1C87, 0x0463}, // ᲇ ѣ ( 1) CYRILLIC SMALL LETTER TALL YAT
|
||||||
|
{0x1C88, 0x1C88, 0xA64B}, // ᲈ ꙋ ( 1) CYRILLIC SMALL LETTER UNBLENDED UK
|
||||||
|
{0x1C90, 0x1CBA, 0x10FA}, // Ა ა (43) GEORGIAN MTAVRULI CAPITAL LETTER AN
|
||||||
|
{0x1CBD, 0x1CBF, 0x10FF}, // Ჽ ჽ ( 3) GEORGIAN MTAVRULI CAPITAL LETTER AEN
|
||||||
|
{0x1E00, 0x1E94, 0x1E95}, // Ḁ ḁ (75) LATIN CAPITAL LETTER A WITH RING BELOW
|
||||||
|
{0x1E9B, 0x1E9B, 0x1E61}, // ẛ ṡ ( 1) LATIN SMALL LETTER LONG S WITH DOT ABOVE
|
||||||
|
{0x1E9E, 0x1E9E, 0x00DF}, // ẞ ß ( 1) LATIN CAPITAL LETTER SHARP S
|
||||||
|
{0x1EA0, 0x1EFE, 0x1EFF}, // Ạ ạ (48) LATIN CAPITAL LETTER A WITH DOT BELOW
|
||||||
|
{0x1F08, 0x1F0F, 0x1F07}, // Ἀ ἀ ( 8) GREEK CAPITAL LETTER ALPHA WITH PSILI
|
||||||
|
{0x1F18, 0x1F1D, 0x1F15}, // Ἐ ἐ ( 6) GREEK CAPITAL LETTER EPSILON WITH PSILI
|
||||||
|
{0x1F28, 0x1F2F, 0x1F27}, // Ἠ ἠ ( 8) GREEK CAPITAL LETTER ETA WITH PSILI
|
||||||
|
{0x1F38, 0x1F3F, 0x1F37}, // Ἰ ἰ ( 8) GREEK CAPITAL LETTER IOTA WITH PSILI
|
||||||
|
{0x1F48, 0x1F4D, 0x1F45}, // Ὀ ὀ ( 6) GREEK CAPITAL LETTER OMICRON WITH PSILI
|
||||||
|
{0x1F59, 0x1F5F, 0x1F57}, // Ὑ ὑ ( 7) GREEK CAPITAL LETTER UPSILON WITH DASIA
|
||||||
|
{0x1F68, 0x1F6F, 0x1F67}, // Ὠ ὠ ( 8) GREEK CAPITAL LETTER OMEGA WITH PSILI
|
||||||
|
{0x1F88, 0x1F8F, 0x1F87}, // ᾈ ᾀ ( 8) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
|
||||||
|
{0x1F98, 0x1F9F, 0x1F97}, // ᾘ ᾐ ( 8) GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
|
||||||
|
{0x1FA8, 0x1FAF, 0x1FA7}, // ᾨ ᾠ ( 8) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
|
||||||
|
{0x1FB8, 0x1FB9, 0x1FB1}, // Ᾰ ᾰ ( 2) GREEK CAPITAL LETTER ALPHA WITH VRACHY
|
||||||
|
{0x1FBA, 0x1FBB, 0x1F71}, // Ὰ ὰ ( 2) GREEK CAPITAL LETTER ALPHA WITH VARIA
|
||||||
|
{0x1FBC, 0x1FBC, 0x1FB3}, // ᾼ ᾳ ( 1) GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
|
||||||
|
{0x1FBE, 0x1FBE, 0x03B9}, // ι ι ( 1) GREEK PROSGEGRAMMENI
|
||||||
|
{0x1FC8, 0x1FCB, 0x1F75}, // Ὲ ὲ ( 4) GREEK CAPITAL LETTER EPSILON WITH VARIA
|
||||||
|
{0x1FCC, 0x1FCC, 0x1FC3}, // ῌ ῃ ( 1) GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
|
||||||
|
{0x1FD8, 0x1FD9, 0x1FD1}, // Ῐ ῐ ( 2) GREEK CAPITAL LETTER IOTA WITH VRACHY
|
||||||
|
{0x1FDA, 0x1FDB, 0x1F77}, // Ὶ ὶ ( 2) GREEK CAPITAL LETTER IOTA WITH VARIA
|
||||||
|
{0x1FE8, 0x1FE9, 0x1FE1}, // Ῠ ῠ ( 2) GREEK CAPITAL LETTER UPSILON WITH VRACHY
|
||||||
|
{0x1FEA, 0x1FEB, 0x1F7B}, // Ὺ ὺ ( 2) GREEK CAPITAL LETTER UPSILON WITH VARIA
|
||||||
|
{0x1FEC, 0x1FEC, 0x1FE5}, // Ῥ ῥ ( 1) GREEK CAPITAL LETTER RHO WITH DASIA
|
||||||
|
{0x1FF8, 0x1FF9, 0x1F79}, // Ὸ ὸ ( 2) GREEK CAPITAL LETTER OMICRON WITH VARIA
|
||||||
|
{0x1FFA, 0x1FFB, 0x1F7D}, // Ὼ ὼ ( 2) GREEK CAPITAL LETTER OMEGA WITH VARIA
|
||||||
|
{0x1FFC, 0x1FFC, 0x1FF3}, // ῼ ῳ ( 1) GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
|
||||||
|
{0x2126, 0x2126, 0x03C9}, // Ω ω ( 1) OHM SIGN
|
||||||
|
{0x212A, 0x212A, 0x006B}, // K k ( 1) KELVIN SIGN
|
||||||
|
{0x212B, 0x212B, 0x00E5}, // Å å ( 1) ANGSTROM SIGN
|
||||||
|
{0x2132, 0x2132, 0x214E}, // Ⅎ ⅎ ( 1) TURNED CAPITAL F
|
||||||
|
{0x2160, 0x216F, 0x217F}, // Ⅰ ⅰ (16) ROMAN NUMERAL ONE
|
||||||
|
{0x2183, 0x2183, 0x2184}, // Ↄ ↄ ( 1) ROMAN NUMERAL REVERSED ONE HUNDRED
|
||||||
|
{0x24B6, 0x24CF, 0x24E9}, // Ⓐ ⓐ (26) CIRCLED LATIN CAPITAL LETTER A
|
||||||
|
{0x2C00, 0x2C2F, 0x2C5F}, // Ⰰ ⰰ (48) GLAGOLITIC CAPITAL LETTER AZU
|
||||||
|
{0x2C60, 0x2C60, 0x2C61}, // Ⱡ ⱡ ( 1) LATIN CAPITAL LETTER L WITH DOUBLE BAR
|
||||||
|
{0x2C62, 0x2C62, 0x026B}, // Ɫ ɫ ( 1) LATIN CAPITAL LETTER L WITH MIDDLE TILDE
|
||||||
|
{0x2C63, 0x2C63, 0x1D7D}, // Ᵽ ᵽ ( 1) LATIN CAPITAL LETTER P WITH STROKE
|
||||||
|
{0x2C64, 0x2C64, 0x027D}, // Ɽ ɽ ( 1) LATIN CAPITAL LETTER R WITH TAIL
|
||||||
|
{0x2C67, 0x2C6B, 0x2C6C}, // Ⱨ ⱨ ( 3) LATIN CAPITAL LETTER H WITH DESCENDER
|
||||||
|
{0x2C6D, 0x2C6D, 0x0251}, // Ɑ ɑ ( 1) LATIN CAPITAL LETTER ALPHA
|
||||||
|
{0x2C6E, 0x2C6E, 0x0271}, // Ɱ ɱ ( 1) LATIN CAPITAL LETTER M WITH HOOK
|
||||||
|
{0x2C6F, 0x2C6F, 0x0250}, // Ɐ ɐ ( 1) LATIN CAPITAL LETTER TURNED A
|
||||||
|
{0x2C70, 0x2C70, 0x0252}, // Ɒ ɒ ( 1) LATIN CAPITAL LETTER TURNED ALPHA
|
||||||
|
{0x2C72, 0x2C72, 0x2C73}, // Ⱳ ⱳ ( 1) LATIN CAPITAL LETTER W WITH HOOK
|
||||||
|
{0x2C75, 0x2C75, 0x2C76}, // Ⱶ ⱶ ( 1) LATIN CAPITAL LETTER HALF H
|
||||||
|
{0x2C7E, 0x2C7F, 0x0240}, // Ȿ ȿ ( 2) LATIN CAPITAL LETTER S WITH SWASH TAIL
|
||||||
|
{0x2C80, 0x2CE2, 0x2CE3}, // Ⲁ ⲁ (50) COPTIC CAPITAL LETTER ALFA
|
||||||
|
{0x2CEB, 0x2CED, 0x2CEE}, // Ⳬ ⳬ ( 2) COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI
|
||||||
|
{0x2CF2, 0x2CF2, 0x2CF3}, // Ⳳ ⳳ ( 1) COPTIC CAPITAL LETTER BOHAIRIC KHEI
|
||||||
|
{0xA640, 0xA66C, 0xA66D}, // Ꙁ ꙁ (23) CYRILLIC CAPITAL LETTER ZEMLYA
|
||||||
|
{0xA680, 0xA69A, 0xA69B}, // Ꚁ ꚁ (14) CYRILLIC CAPITAL LETTER DWE
|
||||||
|
{0xA722, 0xA72E, 0xA72F}, // Ꜣ ꜣ ( 7) LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF
|
||||||
|
{0xA732, 0xA76E, 0xA76F}, // Ꜳ ꜳ (31) LATIN CAPITAL LETTER AA
|
||||||
|
{0xA779, 0xA77B, 0xA77C}, // Ꝺ ꝺ ( 2) LATIN CAPITAL LETTER INSULAR D
|
||||||
|
{0xA77D, 0xA77D, 0x1D79}, // Ᵹ ᵹ ( 1) LATIN CAPITAL LETTER INSULAR G
|
||||||
|
{0xA77E, 0xA786, 0xA787}, // Ꝿ ꝿ ( 5) LATIN CAPITAL LETTER TURNED INSULAR G
|
||||||
|
{0xA78B, 0xA78B, 0xA78C}, // Ꞌ ꞌ ( 1) LATIN CAPITAL LETTER SALTILLO
|
||||||
|
{0xA78D, 0xA78D, 0x0265}, // Ɥ ɥ ( 1) LATIN CAPITAL LETTER TURNED H
|
||||||
|
{0xA790, 0xA792, 0xA793}, // Ꞑ ꞑ ( 2) LATIN CAPITAL LETTER N WITH DESCENDER
|
||||||
|
{0xA796, 0xA7A8, 0xA7A9}, // Ꞗ ꞗ (10) LATIN CAPITAL LETTER B WITH FLOURISH
|
||||||
|
{0xA7AA, 0xA7AA, 0x0266}, // Ɦ ɦ ( 1) LATIN CAPITAL LETTER H WITH HOOK
|
||||||
|
{0xA7AB, 0xA7AB, 0x025C}, // Ɜ ɜ ( 1) LATIN CAPITAL LETTER REVERSED OPEN E
|
||||||
|
{0xA7AC, 0xA7AC, 0x0261}, // Ɡ ɡ ( 1) LATIN CAPITAL LETTER SCRIPT G
|
||||||
|
{0xA7AD, 0xA7AD, 0x026C}, // Ɬ ɬ ( 1) LATIN CAPITAL LETTER L WITH BELT
|
||||||
|
{0xA7AE, 0xA7AE, 0x026A}, // Ɪ ɪ ( 1) LATIN CAPITAL LETTER SMALL CAPITAL I
|
||||||
|
{0xA7B0, 0xA7B0, 0x029E}, // Ʞ ʞ ( 1) LATIN CAPITAL LETTER TURNED K
|
||||||
|
{0xA7B1, 0xA7B1, 0x0287}, // Ʇ ʇ ( 1) LATIN CAPITAL LETTER TURNED T
|
||||||
|
{0xA7B2, 0xA7B2, 0x029D}, // Ʝ ʝ ( 1) LATIN CAPITAL LETTER J WITH CROSSED-TAIL
|
||||||
|
{0xA7B3, 0xA7B3, 0xAB53}, // Ꭓ ꭓ ( 1) LATIN CAPITAL LETTER CHI
|
||||||
|
{0xA7B4, 0xA7C2, 0xA7C3}, // Ꞵ ꞵ ( 8) LATIN CAPITAL LETTER BETA
|
||||||
|
{0xA7C4, 0xA7C4, 0xA794}, // Ꞔ ꞔ ( 1) LATIN CAPITAL LETTER C WITH PALATAL HOOK
|
||||||
|
{0xA7C5, 0xA7C5, 0x0282}, // Ʂ ʂ ( 1) LATIN CAPITAL LETTER S WITH HOOK
|
||||||
|
{0xA7C6, 0xA7C6, 0x1D8E}, // Ᶎ ᶎ ( 1) LATIN CAPITAL LETTER Z WITH PALATAL HOOK
|
||||||
|
{0xA7C7, 0xA7C9, 0xA7CA}, // Ꟈ ꟈ ( 2) LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
|
||||||
|
{0xA7D0, 0xA7D0, 0xA7D1}, // Ꟑ ꟑ ( 1) LATIN CAPITAL LETTER CLOSED INSULAR G
|
||||||
|
{0xA7D6, 0xA7D8, 0xA7D9}, // Ꟗ ꟗ ( 2) LATIN CAPITAL LETTER MIDDLE SCOTS S
|
||||||
|
{0xA7F5, 0xA7F5, 0xA7F6}, // Ꟶ ꟶ ( 1) LATIN CAPITAL LETTER REVERSED HALF H
|
||||||
|
{0xAB70, 0xABBF, 0x13EF}, // ꭰ Ꭰ (80) CHEROKEE SMALL LETTER A
|
||||||
|
{0xFF21, 0xFF3A, 0xFF5A}, // A a (26) FULLWIDTH LATIN CAPITAL LETTER A
|
||||||
|
{0x0130, 0x0130, 0x0069}, // İ i ( 1) LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||||
|
{0x01CD, 0x01DB, 0x01DC}, // Ǎ ǎ ( 8) LATIN CAPITAL LETTER A WITH CARON
|
||||||
|
{0x01F4, 0x01F4, 0x01F5}, // Ǵ ǵ ( 1) LATIN CAPITAL LETTER G WITH ACUTE
|
||||||
|
{0x13A0, 0x13EF, 0xABBF}, // Ꭰ ꭰ (80) CHEROKEE LETTER A
|
||||||
|
{0x13F0, 0x13F5, 0x13FD}, // Ᏸ ᏸ ( 6) CHEROKEE LETTER YE
|
||||||
|
{0x039C, 0x039C, 0x00B5}, // Μ µ ( 1)
|
||||||
|
{0x0049, 0x0049, 0x0131}, // I ı ( 1)
|
||||||
|
{0x0053, 0x0053, 0x017F}, // S ſ ( 1)
|
||||||
|
{0x03A3, 0x03A3, 0x03C2}, // Σ ς ( 1)
|
||||||
|
{0x0392, 0x0392, 0x03D0}, // Β ϐ ( 1)
|
||||||
|
{0x0398, 0x0398, 0x03D1}, // Θ ϑ ( 1)
|
||||||
|
{0x03A6, 0x03A6, 0x03D5}, // Φ ϕ ( 1)
|
||||||
|
{0x03A0, 0x03A0, 0x03D6}, // Π ϖ ( 1)
|
||||||
|
{0x039A, 0x039A, 0x03F0}, // Κ ϰ ( 1)
|
||||||
|
{0x03A1, 0x03A1, 0x03F1}, // Ρ ϱ ( 1)
|
||||||
|
{0x0395, 0x0395, 0x03F5}, // Ε ϵ ( 1)
|
||||||
|
{0x0412, 0x0412, 0x1C80}, // В ᲀ ( 1)
|
||||||
|
{0x0414, 0x0414, 0x1C81}, // Д ᲁ ( 1)
|
||||||
|
{0x041E, 0x041E, 0x1C82}, // О ᲂ ( 1)
|
||||||
|
{0x0421, 0x0422, 0x1C84}, // С ᲃ ( 2)
|
||||||
|
{0x0422, 0x0422, 0x1C85}, // Т ᲅ ( 1)
|
||||||
|
{0x042A, 0x042A, 0x1C86}, // Ъ ᲆ ( 1)
|
||||||
|
{0x0462, 0x0462, 0x1C87}, // Ѣ ᲇ ( 1)
|
||||||
|
{0xA64A, 0xA64A, 0x1C88}, // Ꙋ ᲈ ( 1)
|
||||||
|
{0x1E60, 0x1E60, 0x1E9B}, // Ṡ ẛ ( 1)
|
||||||
|
{0x0399, 0x0399, 0x1FBE}, // Ι ι ( 1)
|
||||||
|
}; // 218
|
||||||
|
|
||||||
|
enum { casefold_len = 192 };
|
||||||
|
|
||||||
|
static uint8_t upcase_ind[162] = {
|
||||||
|
0, 2, 3, 4, 192, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
|
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||||
|
41, 43, 45, 193, 47, 48, 194, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
|
||||||
|
63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 80, 83, 85, 86, 87, 88, 89, 90, 91,
|
||||||
|
92, 93, 94, 95, 96, 97, 98, 99, 195, 196, 109, 110, 111, 113, 114, 115, 116, 117, 118, 119,
|
||||||
|
120, 121, 125, 126, 129, 131, 132, 133, 134, 135, 136, 137, 139, 140, 141, 142, 144, 146, 147, 148,
|
||||||
|
149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
|
||||||
|
169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
|
||||||
|
189, 191,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t lowcase_ind[184] = {
|
||||||
|
0, 197, 113, 2, 3, 8, 4, 198, 5, 6, 7, 9, 199, 60, 12, 14, 16, 20, 50, 25,
|
||||||
|
57, 53, 29, 31, 33, 35, 37, 39, 40, 51, 41, 43, 45, 193, 17, 47, 48, 194, 52, 54,
|
||||||
|
56, 158, 59, 63, 154, 152, 155, 11, 13, 15, 18, 19, 174, 21, 175, 22, 170, 173, 24, 23,
|
||||||
|
177, 148, 176, 26, 153, 27, 28, 150, 30, 184, 32, 179, 34, 61, 36, 62, 38, 180, 178, 65,
|
||||||
|
66, 88, 68, 69, 72, 200, 73, 70, 71, 201, 202, 203, 204, 75, 80, 205, 206, 86, 67, 207,
|
||||||
|
85, 87, 90, 89, 91, 92, 94, 93, 95, 96, 109, 110, 196, 208, 209, 210, 211, 212, 213, 214,
|
||||||
|
215, 167, 149, 185, 111, 216, 114, 115, 116, 117, 118, 119, 120, 121, 126, 129, 132, 136, 134, 137,
|
||||||
|
122, 123, 124, 125, 127, 217, 130, 131, 133, 135, 138, 142, 144, 146, 147, 55, 58, 151, 156, 157,
|
||||||
|
159, 160, 161, 97, 98, 99, 162, 163, 164, 165, 166, 168, 169, 171, 183, 172, 182, 186, 187, 188,
|
||||||
|
189, 181, 195, 191,
|
||||||
|
};
|
||||||
39
stc/queue.h
Normal file
39
stc/queue.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Queue. Implemented as a ring buffer.
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_QUEUE_H_INCLUDED
|
||||||
|
#define STC_QUEUE_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // STC_QUEUE_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix queue_
|
||||||
|
#endif
|
||||||
|
#include "priv/template.h"
|
||||||
|
#include "priv/queue_prv.h"
|
||||||
|
#include "sys/finalize.h"
|
||||||
251
stc/random.h
Normal file
251
stc/random.h
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#define i_header // external linkage of normal_dist by default.
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
|
||||||
|
#ifndef STC_RANDOM_H_INCLUDED
|
||||||
|
#define STC_RANDOM_H_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
// ===== crand64 ===================================
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t data[4];
|
||||||
|
} crand64;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double mean, stddev;
|
||||||
|
double _next;
|
||||||
|
int _has_next;
|
||||||
|
} crand64_normal_dist;
|
||||||
|
|
||||||
|
STC_API double crand64_normal(crand64_normal_dist* d);
|
||||||
|
STC_API double crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d);
|
||||||
|
|
||||||
|
#if INTPTR_MAX == INT64_MAX
|
||||||
|
#define crandWS crand64
|
||||||
|
#else
|
||||||
|
#define crandWS crand32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define c_shuffle_seed(s) \
|
||||||
|
c_JOIN(crandWS, _seed)(s)
|
||||||
|
|
||||||
|
#define c_shuffle_array(array, n) do { \
|
||||||
|
typedef struct { char d[sizeof 0[array]]; } _etype; \
|
||||||
|
_etype* _arr = (_etype *)(array); \
|
||||||
|
for (isize _i = (n) - 1; _i > 0; --_i) { \
|
||||||
|
isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \
|
||||||
|
c_swap(_arr + _i, _arr + _j); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// Compiles with vec, stack, and deque container types:
|
||||||
|
#define c_shuffle(CntType, self) do { \
|
||||||
|
CntType* _self = self; \
|
||||||
|
for (isize _i = CntType##_size(_self) - 1; _i > 0; --_i) { \
|
||||||
|
isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \
|
||||||
|
c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
STC_INLINE void crand64_seed_r(crand64* rng, uint64_t seed) {
|
||||||
|
uint64_t* s = rng->data;
|
||||||
|
s[0] = seed*0x9e3779b97f4a7c15; s[0] ^= s[0] >> 30;
|
||||||
|
s[1] = s[0]*0xbf58476d1ce4e5b9; s[1] ^= s[1] >> 27;
|
||||||
|
s[2] = s[1]*0x94d049bb133111eb; s[2] ^= s[2] >> 31;
|
||||||
|
s[3] = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum period length 2^64 per stream. 2^63 streams (odd numbers only)
|
||||||
|
STC_INLINE uint64_t crand64_uint_r(crand64* rng, uint64_t stream) {
|
||||||
|
uint64_t* s = rng->data;
|
||||||
|
const uint64_t result = (s[0] ^ (s[3] += stream)) + s[1];
|
||||||
|
s[0] = s[1] ^ (s[1] >> 11);
|
||||||
|
s[1] = s[2] + (s[2] << 3);
|
||||||
|
s[2] = ((s[2] << 24) | (s[2] >> 40)) + result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE double crand64_real_r(crand64* rng, uint64_t stream)
|
||||||
|
{ return (double)(crand64_uint_r(rng, stream) >> 11) * 0x1.0p-53; }
|
||||||
|
|
||||||
|
STC_INLINE crand64* _stc64(void) {
|
||||||
|
static crand64 rng = {{0x9e3779bb07979af0,0x6f682616bae3641a,0xe220a8397b1dcdaf,0x1}};
|
||||||
|
return &rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void crand64_seed(uint64_t seed)
|
||||||
|
{ crand64_seed_r(_stc64(), seed); }
|
||||||
|
|
||||||
|
STC_INLINE crand64 crand64_from(uint64_t seed)
|
||||||
|
{ crand64 rng; crand64_seed_r(&rng, seed); return rng; }
|
||||||
|
|
||||||
|
STC_INLINE uint64_t crand64_uint(void)
|
||||||
|
{ return crand64_uint_r(_stc64(), 1); }
|
||||||
|
|
||||||
|
STC_INLINE double crand64_real(void)
|
||||||
|
{ return crand64_real_r(_stc64(), 1); }
|
||||||
|
|
||||||
|
// --- crand64_uniform ---
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int64_t low;
|
||||||
|
uint64_t range, threshold;
|
||||||
|
} crand64_uniform_dist;
|
||||||
|
|
||||||
|
STC_INLINE crand64_uniform_dist
|
||||||
|
crand64_make_uniform(int64_t low, int64_t high) {
|
||||||
|
crand64_uniform_dist d = {low, (uint64_t)(high - low + 1)};
|
||||||
|
d.threshold = (uint64_t)(0 - d.range) % d.range;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 128-bit multiplication
|
||||||
|
#if defined(__SIZEOF_INT128__)
|
||||||
|
#define c_umul128(a, b, lo, hi) \
|
||||||
|
do { __uint128_t _z = (__uint128_t)(a)*(b); \
|
||||||
|
*(lo) = (uint64_t)_z, *(hi) = (uint64_t)(_z >> 64U); } while(0)
|
||||||
|
#elif defined(_MSC_VER) && defined(_WIN64)
|
||||||
|
#include <intrin.h>
|
||||||
|
#define c_umul128(a, b, lo, hi) ((void)(*(lo) = _umul128(a, b, hi)))
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
#define c_umul128(a, b, lo, hi) \
|
||||||
|
asm("mulq %3" : "=a"(*(lo)), "=d"(*(hi)) : "a"(a), "rm"(b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE int64_t
|
||||||
|
crand64_uniform_r(crand64* rng, uint64_t stream, crand64_uniform_dist* d) {
|
||||||
|
uint64_t lo, hi;
|
||||||
|
#ifdef c_umul128
|
||||||
|
do { c_umul128(crand64_uint_r(rng, stream), d->range, &lo, &hi); } while (lo < d->threshold);
|
||||||
|
#else
|
||||||
|
do { lo = crand64_uint_r(rng, stream); hi = lo % d->range; } while (lo - hi > -d->range);
|
||||||
|
#endif
|
||||||
|
return d->low + (int64_t)hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE int64_t crand64_uniform(crand64_uniform_dist* d)
|
||||||
|
{ return crand64_uniform_r(_stc64(), 1, d); }
|
||||||
|
|
||||||
|
// ===== crand32 ===================================
|
||||||
|
|
||||||
|
typedef struct { uint32_t data[4]; } crand32;
|
||||||
|
|
||||||
|
STC_INLINE void crand32_seed_r(crand32* rng, uint32_t seed) {
|
||||||
|
uint32_t* s = rng->data;
|
||||||
|
s[0] = seed*0x9e3779b9; s[0] ^= s[0] >> 16;
|
||||||
|
s[1] = s[0]*0x21f0aaad; s[1] ^= s[1] >> 15;
|
||||||
|
s[2] = s[1]*0x735a2d97; s[2] ^= s[2] >> 15;
|
||||||
|
s[3] = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum period length 2^32 per stream. 2^31 streams (odd numbers only)
|
||||||
|
STC_INLINE uint32_t crand32_uint_r(crand32* rng, uint32_t stream) {
|
||||||
|
uint32_t* s = rng->data;
|
||||||
|
const uint32_t result = (s[0] ^ (s[3] += stream)) + s[1];
|
||||||
|
s[0] = s[1] ^ (s[1] >> 9);
|
||||||
|
s[1] = s[2] + (s[2] << 3);
|
||||||
|
s[2] = ((s[2] << 21) | (s[2] >> 11)) + result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE double crand32_real_r(crand32* rng, uint32_t stream)
|
||||||
|
{ return crand32_uint_r(rng, stream) * 0x1.0p-32; }
|
||||||
|
|
||||||
|
STC_INLINE crand32* _stc32(void) {
|
||||||
|
static crand32 rng = {{0x9e37e78e,0x6eab1ba1,0x64625032,0x1}};
|
||||||
|
return &rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void crand32_seed(uint32_t seed)
|
||||||
|
{ crand32_seed_r(_stc32(), seed); }
|
||||||
|
|
||||||
|
STC_INLINE crand32 crand32_from(uint32_t seed)
|
||||||
|
{ crand32 rng; crand32_seed_r(&rng, seed); return rng; }
|
||||||
|
|
||||||
|
STC_INLINE uint32_t crand32_uint(void)
|
||||||
|
{ return crand32_uint_r(_stc32(), 1); }
|
||||||
|
|
||||||
|
STC_INLINE double crand32_real(void)
|
||||||
|
{ return crand32_real_r(_stc32(), 1); }
|
||||||
|
|
||||||
|
// --- crand32_uniform ---
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t low;
|
||||||
|
uint32_t range, threshold;
|
||||||
|
} crand32_uniform_dist;
|
||||||
|
|
||||||
|
STC_INLINE crand32_uniform_dist
|
||||||
|
crand32_make_uniform(int32_t low, int32_t high) {
|
||||||
|
crand32_uniform_dist d = {low, (uint32_t)(high - low + 1)};
|
||||||
|
d.threshold = (uint32_t)(0 - d.range) % d.range;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE int32_t
|
||||||
|
crand32_uniform_r(crand32* rng, uint32_t stream, crand32_uniform_dist* d) {
|
||||||
|
uint64_t r;
|
||||||
|
do {
|
||||||
|
r = crand32_uint_r(rng, stream) * (uint64_t)d->range;
|
||||||
|
} while ((uint32_t)r < d->threshold);
|
||||||
|
return d->low + (int32_t)(r >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE int64_t crand32_uniform(crand32_uniform_dist* d)
|
||||||
|
{ return crand32_uniform_r(_stc32(), 1, d); }
|
||||||
|
|
||||||
|
#endif // STC_RANDOM_H_INCLUDED
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
#ifndef STC_RANDOM_C_INCLUDED
|
||||||
|
#define STC_RANDOM_C_INCLUDED
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
STC_DEF double
|
||||||
|
crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d) {
|
||||||
|
double v1, v2, sq, rt;
|
||||||
|
if (d->_has_next++ & 1)
|
||||||
|
return d->_next*d->stddev + d->mean;
|
||||||
|
do {
|
||||||
|
// range (-1.0, 1.0):
|
||||||
|
v1 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52;
|
||||||
|
v2 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52;
|
||||||
|
|
||||||
|
sq = v1*v1 + v2*v2;
|
||||||
|
} while (sq >= 1.0 || sq == 0.0);
|
||||||
|
rt = sqrt(-2.0 * log(sq) / sq);
|
||||||
|
d->_next = v2*rt;
|
||||||
|
return (v1*rt)*d->stddev + d->mean;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF double crand64_normal(crand64_normal_dist* d)
|
||||||
|
{ return crand64_normal_r(_stc64(), 1, d); }
|
||||||
|
|
||||||
|
#endif // STC_RANDOM_C_INCLUDED
|
||||||
|
#endif // i_implement
|
||||||
|
#include "priv/linkage2.h"
|
||||||
38
stc/rc.h
Normal file
38
stc/rc.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvmap
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unordered map - implemented with the robin-hood hashing scheme.
|
||||||
|
/*
|
||||||
|
#define T IRefc, int
|
||||||
|
#include <stc/rc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
IRefc rc = IRefc_make(42);
|
||||||
|
IRefc_drop(&rc);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define i_no_atomic
|
||||||
|
#define _i_prefix rc_
|
||||||
|
#include "arc.h"
|
||||||
606
stc/smap.h
Normal file
606
stc/smap.h
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sorted/Ordered set and map - implemented as an AA-tree.
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/cstr.h>
|
||||||
|
|
||||||
|
#define T SMap, cstr, double, (c_keypro) // Sorted map<cstr, double>
|
||||||
|
#include <stc/sortedmap.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
SMap m = {0};
|
||||||
|
SMap_emplace(&m, "Testing one", 1.234);
|
||||||
|
SMap_emplace(&m, "Testing two", 12.34);
|
||||||
|
SMap_emplace(&m, "Testing three", 123.4);
|
||||||
|
|
||||||
|
SMap_value *v = SMap_get(&m, "Testing five"); // NULL
|
||||||
|
double num = *SMap_at(&m, "Testing one");
|
||||||
|
SMap_emplace_or_assign(&m, "Testing three", 1000.0); // update
|
||||||
|
SMap_erase(&m, "Testing two");
|
||||||
|
|
||||||
|
for (c_each(i, SMap, m))
|
||||||
|
printf("map %s: %g\n", cstr_str(&i.ref->first), i.ref->second);
|
||||||
|
|
||||||
|
SMap_drop(&m);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_SMAP_H_INCLUDED
|
||||||
|
#define STC_SMAP_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // STC_SMAP_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix smap_
|
||||||
|
#endif
|
||||||
|
#ifndef _i_is_set
|
||||||
|
#define _i_is_map
|
||||||
|
#define _i_MAP_ONLY c_true
|
||||||
|
#define _i_SET_ONLY c_false
|
||||||
|
#define _i_keyref(vp) (&(vp)->first)
|
||||||
|
#else
|
||||||
|
#define _i_MAP_ONLY c_false
|
||||||
|
#define _i_SET_ONLY c_true
|
||||||
|
#define _i_keyref(vp) (vp)
|
||||||
|
#endif
|
||||||
|
#define _i_sorted
|
||||||
|
#include "priv/template.h"
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_aatree, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_i_MAP_ONLY( struct _m_value {
|
||||||
|
_m_key first;
|
||||||
|
_m_mapped second;
|
||||||
|
}; )
|
||||||
|
struct _m_node {
|
||||||
|
int32_t link[2];
|
||||||
|
int8_t level;
|
||||||
|
_m_value value;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i_keyraw _m_keyraw;
|
||||||
|
typedef i_valraw _m_rmapped;
|
||||||
|
typedef _i_SET_ONLY( _m_keyraw )
|
||||||
|
_i_MAP_ONLY( struct { _m_keyraw first; _m_rmapped second; } )
|
||||||
|
_m_raw;
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_API _m_result _c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped));
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self tree);
|
||||||
|
#endif // !i_no_clone
|
||||||
|
STC_API void _c_MEMB(_drop)(const Self* cself);
|
||||||
|
STC_API bool _c_MEMB(_reserve)(Self* self, isize cap);
|
||||||
|
STC_API _m_value* _c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out);
|
||||||
|
STC_API _m_iter _c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey);
|
||||||
|
STC_API _m_value* _c_MEMB(_front)(const Self* self);
|
||||||
|
STC_API _m_value* _c_MEMB(_back)(const Self* self);
|
||||||
|
STC_API int _c_MEMB(_erase)(Self* self, _m_keyraw rkey);
|
||||||
|
STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it);
|
||||||
|
STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2);
|
||||||
|
STC_API _m_iter _c_MEMB(_begin)(const Self* self);
|
||||||
|
STC_API void _c_MEMB(_next)(_m_iter* it);
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->size == 0; }
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; }
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; }
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; _c_MEMB(_find_it)(self, rkey, &it); return it; }
|
||||||
|
STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it) != NULL; }
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_get)(const Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_get_mut)(Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); }
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) {
|
||||||
|
return _i_SET_ONLY( i_keytoraw(val) )
|
||||||
|
_i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)),
|
||||||
|
i_valtoraw((&val->second))} );
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) {
|
||||||
|
(void)self;
|
||||||
|
i_keydrop(_i_keyref(val));
|
||||||
|
_i_MAP_ONLY( i_valdrop((&val->second)); )
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->capacity = self->size = self->root = self->disp = self->head = 0;
|
||||||
|
self->nodes = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_clear)(Self* self) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
(void)_c_MEMB(_move)(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) {
|
||||||
|
(void)self;
|
||||||
|
*_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val)));
|
||||||
|
_i_MAP_ONLY( _val.second = i_valclone(_val.second); )
|
||||||
|
return _val;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) {
|
||||||
|
if (self == other)
|
||||||
|
return;
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = _c_MEMB(_clone)(*other);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_shrink_to_fit)(Self *self) {
|
||||||
|
Self tmp = _c_MEMB(_clone)(*self);
|
||||||
|
_c_MEMB(_drop)(self); *self = tmp;
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
STC_API _m_result _c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey);
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped);
|
||||||
|
#ifndef i_no_emplace
|
||||||
|
STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; }
|
||||||
|
|
||||||
|
STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey)
|
||||||
|
{ _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; }
|
||||||
|
#endif // _i_is_map
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) {
|
||||||
|
_m_iter it; (void)self;
|
||||||
|
it.ref = NULL, it._top = 0, it._tn = 0;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) {
|
||||||
|
while (n-- && it.ref)
|
||||||
|
_c_MEMB(_next)(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE bool
|
||||||
|
_c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false;
|
||||||
|
_m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other);
|
||||||
|
for (; i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) {
|
||||||
|
const _m_keyraw _rx = i_keytoraw(_i_keyref(i.ref)), _ry = i_keytoraw(_i_keyref(j.ref));
|
||||||
|
if (!(i_eq((&_rx), (&_ry)))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE _m_result
|
||||||
|
_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key)));
|
||||||
|
if (_res.inserted)
|
||||||
|
{ *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )}
|
||||||
|
else
|
||||||
|
{ i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )}
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val)));
|
||||||
|
if (_res.inserted)
|
||||||
|
*_res.ref = _val;
|
||||||
|
else
|
||||||
|
_c_MEMB(_value_drop)(self, &_val);
|
||||||
|
return _res.ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) {
|
||||||
|
#ifdef i_no_emplace
|
||||||
|
return _c_MEMB(_insert_or_assign)(self, rkey, rmapped);
|
||||||
|
#else
|
||||||
|
return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) {
|
||||||
|
while (n--)
|
||||||
|
#if defined _i_is_set && defined i_no_emplace
|
||||||
|
_c_MEMB(_insert)(self, *raw++);
|
||||||
|
#elif defined _i_is_set
|
||||||
|
_c_MEMB(_emplace)(self, *raw++);
|
||||||
|
#else
|
||||||
|
_c_MEMB(_put)(self, raw->first, raw->second), ++raw;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ Self cx = {0}; return cx; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap)
|
||||||
|
{ Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_next)(_m_iter *it) {
|
||||||
|
int32_t tn = it->_tn;
|
||||||
|
if (it->_top || tn) {
|
||||||
|
while (tn) {
|
||||||
|
it->_st[it->_top++] = tn;
|
||||||
|
tn = it->_d[tn].link[0];
|
||||||
|
}
|
||||||
|
tn = it->_st[--it->_top];
|
||||||
|
it->_tn = it->_d[tn].link[1];
|
||||||
|
it->ref = &it->_d[tn].value;
|
||||||
|
} else
|
||||||
|
it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_begin)(const Self* self) {
|
||||||
|
_m_iter it;
|
||||||
|
it.ref = NULL;
|
||||||
|
it._d = self->nodes, it._top = 0;
|
||||||
|
it._tn = self->root;
|
||||||
|
if (it._tn)
|
||||||
|
_c_MEMB(_next)(&it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_reserve)(Self* self, const isize cap) {
|
||||||
|
if (cap <= self->capacity)
|
||||||
|
return false;
|
||||||
|
_m_node* nodes = (_m_node*)_i_realloc_n(self->nodes, self->capacity + 1, cap + 1);
|
||||||
|
if (nodes == NULL)
|
||||||
|
return false;
|
||||||
|
nodes[0] = c_literal(_m_node){0};
|
||||||
|
self->nodes = nodes;
|
||||||
|
self->capacity = (int32_t)cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_front)(const Self* self) {
|
||||||
|
_m_node *d = self->nodes;
|
||||||
|
int32_t tn = self->root;
|
||||||
|
while (d[tn].link[0])
|
||||||
|
tn = d[tn].link[0];
|
||||||
|
return &d[tn].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_back)(const Self* self) {
|
||||||
|
_m_node *d = self->nodes;
|
||||||
|
int32_t tn = self->root;
|
||||||
|
while (d[tn].link[1])
|
||||||
|
tn = d[tn].link[1];
|
||||||
|
return &d[tn].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t
|
||||||
|
_c_MEMB(_new_node_)(Self* self, int level) {
|
||||||
|
int32_t tn;
|
||||||
|
if (self->disp != 0) {
|
||||||
|
tn = self->disp;
|
||||||
|
self->disp = self->nodes[tn].link[1];
|
||||||
|
} else {
|
||||||
|
if (self->head == self->capacity)
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->head*3/2 + 4))
|
||||||
|
return 0;
|
||||||
|
tn = ++self->head; /* start with 1, 0 is nullnode. */
|
||||||
|
}
|
||||||
|
_m_node* dn = &self->nodes[tn];
|
||||||
|
dn->link[0] = dn->link[1] = 0; dn->level = (int8_t)level;
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _i_is_map
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key)));
|
||||||
|
_m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped;
|
||||||
|
if (_res.inserted)
|
||||||
|
_res.ref->first = _key;
|
||||||
|
else
|
||||||
|
{ i_keydrop((&_key)); i_valdrop(_mp); }
|
||||||
|
*_mp = _mapped;
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) {
|
||||||
|
_m_result _res = _c_MEMB(_insert_entry_)(self, rkey);
|
||||||
|
if (_res.inserted)
|
||||||
|
_res.ref->first = i_keyfrom(rkey);
|
||||||
|
else {
|
||||||
|
if (_res.ref == NULL) return _res;
|
||||||
|
i_valdrop((&_res.ref->second));
|
||||||
|
}
|
||||||
|
_res.ref->second = i_valfrom(rmapped);
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
#endif // !_i_is_map
|
||||||
|
|
||||||
|
STC_DEF _m_value*
|
||||||
|
_c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out) {
|
||||||
|
int32_t tn = self->root;
|
||||||
|
_m_node *d = out->_d = self->nodes;
|
||||||
|
out->_top = 0;
|
||||||
|
while (tn) {
|
||||||
|
int c; const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tn].value));
|
||||||
|
if ((c = i_cmp((&_raw), (&rkey))) < 0)
|
||||||
|
tn = d[tn].link[1];
|
||||||
|
else if (c > 0)
|
||||||
|
{ out->_st[out->_top++] = tn; tn = d[tn].link[0]; }
|
||||||
|
else
|
||||||
|
{ out->_tn = d[tn].link[1]; return (out->ref = &d[tn].value); }
|
||||||
|
}
|
||||||
|
return (out->ref = NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey) {
|
||||||
|
_m_iter it;
|
||||||
|
_c_MEMB(_find_it)(self, rkey, &it);
|
||||||
|
if (it.ref == NULL && it._top != 0) {
|
||||||
|
int32_t tn = it._st[--it._top];
|
||||||
|
it._tn = it._d[tn].link[1];
|
||||||
|
it.ref = &it._d[tn].value;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF int32_t
|
||||||
|
_c_MEMB(_skew_)(_m_node *d, int32_t tn) {
|
||||||
|
if (tn != 0 && d[d[tn].link[0]].level == d[tn].level) {
|
||||||
|
int32_t tmp = d[tn].link[0];
|
||||||
|
d[tn].link[0] = d[tmp].link[1];
|
||||||
|
d[tmp].link[1] = tn;
|
||||||
|
tn = tmp;
|
||||||
|
}
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF int32_t
|
||||||
|
_c_MEMB(_split_)(_m_node *d, int32_t tn) {
|
||||||
|
if (d[d[d[tn].link[1]].link[1]].level == d[tn].level) {
|
||||||
|
int32_t tmp = d[tn].link[1];
|
||||||
|
d[tn].link[1] = d[tmp].link[0];
|
||||||
|
d[tmp].link[0] = tn;
|
||||||
|
tn = tmp;
|
||||||
|
++d[tn].level;
|
||||||
|
}
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF int32_t
|
||||||
|
_c_MEMB(_insert_entry_i_)(Self* self, int32_t tn, const _m_keyraw* rkey, _m_result* _res) {
|
||||||
|
int32_t up[64], tx = tn;
|
||||||
|
_m_node* d = self->nodes;
|
||||||
|
int c, top = 0, dir = 0;
|
||||||
|
while (tx) {
|
||||||
|
up[top++] = tx;
|
||||||
|
const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tx].value));
|
||||||
|
if ((c = i_cmp((&_raw), rkey)) == 0)
|
||||||
|
{ _res->ref = &d[tx].value; return tn; }
|
||||||
|
dir = (c < 0);
|
||||||
|
tx = d[tx].link[dir];
|
||||||
|
}
|
||||||
|
if ((tx = _c_MEMB(_new_node_)(self, 1)) == 0)
|
||||||
|
return 0;
|
||||||
|
d = self->nodes;
|
||||||
|
_res->ref = &d[tx].value;
|
||||||
|
_res->inserted = true;
|
||||||
|
if (top == 0)
|
||||||
|
return tx;
|
||||||
|
d[up[top - 1]].link[dir] = tx;
|
||||||
|
while (top--) {
|
||||||
|
if (top != 0)
|
||||||
|
dir = (d[up[top - 1]].link[1] == up[top]);
|
||||||
|
up[top] = _c_MEMB(_skew_)(d, up[top]);
|
||||||
|
up[top] = _c_MEMB(_split_)(d, up[top]);
|
||||||
|
if (top)
|
||||||
|
d[up[top - 1]].link[dir] = up[top];
|
||||||
|
}
|
||||||
|
return up[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) {
|
||||||
|
_m_result res = {0};
|
||||||
|
int32_t tn = _c_MEMB(_insert_entry_i_)(self, self->root, &rkey, &res);
|
||||||
|
self->root = tn;
|
||||||
|
self->size += res.inserted;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF int32_t
|
||||||
|
_c_MEMB(_erase_r_)(Self *self, int32_t tn, const _m_keyraw* rkey, int *erased) {
|
||||||
|
_m_node *d = self->nodes;
|
||||||
|
if (tn == 0)
|
||||||
|
return 0;
|
||||||
|
_m_keyraw raw = i_keytoraw(_i_keyref(&d[tn].value));
|
||||||
|
int32_t tx; int c = i_cmp((&raw), rkey);
|
||||||
|
if (c != 0)
|
||||||
|
d[tn].link[c < 0] = _c_MEMB(_erase_r_)(self, d[tn].link[c < 0], rkey, erased);
|
||||||
|
else {
|
||||||
|
if ((*erased)++ == 0)
|
||||||
|
_c_MEMB(_value_drop)(self, &d[tn].value); // drop first time, not second.
|
||||||
|
if (d[tn].link[0] && d[tn].link[1]) {
|
||||||
|
tx = d[tn].link[0];
|
||||||
|
while (d[tx].link[1])
|
||||||
|
tx = d[tx].link[1];
|
||||||
|
d[tn].value = d[tx].value; /* move */
|
||||||
|
raw = i_keytoraw(_i_keyref(&d[tn].value));
|
||||||
|
d[tn].link[0] = _c_MEMB(_erase_r_)(self, d[tn].link[0], &raw, erased);
|
||||||
|
} else { /* unlink node */
|
||||||
|
tx = tn;
|
||||||
|
tn = d[tn].link[ d[tn].link[0] == 0 ];
|
||||||
|
/* move it to disposed nodes list */
|
||||||
|
d[tx].link[1] = self->disp;
|
||||||
|
self->disp = tx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx = d[tn].link[1];
|
||||||
|
if (d[d[tn].link[0]].level < d[tn].level - 1 || d[tx].level < d[tn].level - 1) {
|
||||||
|
if (d[tx].level > --d[tn].level)
|
||||||
|
d[tx].level = d[tn].level;
|
||||||
|
tn = _c_MEMB(_skew_)(d, tn);
|
||||||
|
tx = d[tn].link[1] = _c_MEMB(_skew_)(d, d[tn].link[1]);
|
||||||
|
d[tx].link[1] = _c_MEMB(_skew_)(d, d[tx].link[1]);
|
||||||
|
tn = _c_MEMB(_split_)(d, tn);
|
||||||
|
d[tn].link[1] = _c_MEMB(_split_)(d, d[tn].link[1]);
|
||||||
|
}
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF int
|
||||||
|
_c_MEMB(_erase)(Self* self, _m_keyraw rkey) {
|
||||||
|
int erased = 0;
|
||||||
|
int32_t root = _c_MEMB(_erase_r_)(self, self->root, &rkey, &erased);
|
||||||
|
if (erased == 0)
|
||||||
|
return 0;
|
||||||
|
self->root = root;
|
||||||
|
--self->size;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_erase_at)(Self* self, _m_iter it) {
|
||||||
|
_m_keyraw raw = i_keytoraw(_i_keyref(it.ref));
|
||||||
|
_c_MEMB(_next)(&it);
|
||||||
|
if (it.ref != NULL) {
|
||||||
|
_m_keyraw nxt = i_keytoraw(_i_keyref(it.ref));
|
||||||
|
_c_MEMB(_erase)(self, raw);
|
||||||
|
_c_MEMB(_find_it)(self, nxt, &it);
|
||||||
|
} else
|
||||||
|
_c_MEMB(_erase)(self, raw);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) {
|
||||||
|
if (it2.ref == NULL) {
|
||||||
|
while (it1.ref != NULL)
|
||||||
|
it1 = _c_MEMB(_erase_at)(self, it1);
|
||||||
|
return it1;
|
||||||
|
}
|
||||||
|
_m_key k1 = *_i_keyref(it1.ref), k2 = *_i_keyref(it2.ref);
|
||||||
|
_m_keyraw r1 = i_keytoraw((&k1));
|
||||||
|
for (;;) {
|
||||||
|
if (memcmp(&k1, &k2, sizeof k1) == 0)
|
||||||
|
return it1;
|
||||||
|
_c_MEMB(_next)(&it1);
|
||||||
|
k1 = *_i_keyref(it1.ref);
|
||||||
|
_c_MEMB(_erase)(self, r1);
|
||||||
|
r1 = i_keytoraw((&k1));
|
||||||
|
_c_MEMB(_find_it)(self, r1, &it1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF int32_t
|
||||||
|
_c_MEMB(_clone_r_)(Self* self, _m_node* src, int32_t sn) {
|
||||||
|
if (sn == 0)
|
||||||
|
return 0;
|
||||||
|
int32_t tx, tn = _c_MEMB(_new_node_)(self, src[sn].level);
|
||||||
|
self->nodes[tn].value = _c_MEMB(_value_clone)(self, src[sn].value);
|
||||||
|
tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[0]); self->nodes[tn].link[0] = tx;
|
||||||
|
tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[1]); self->nodes[tn].link[1] = tx;
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_clone)(Self tree) {
|
||||||
|
Self out = tree;
|
||||||
|
out.root = out.disp = out.head = out.size = out.capacity = 0;
|
||||||
|
out.nodes = NULL; _c_MEMB(_reserve)(&out, tree.size);
|
||||||
|
out.root = _c_MEMB(_clone_r_)(&out, tree.nodes, tree.root);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_DEF _m_result
|
||||||
|
_c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) {
|
||||||
|
_m_result res = _c_MEMB(_insert_entry_)(self, rkey);
|
||||||
|
if (res.inserted) {
|
||||||
|
*_i_keyref(res.ref) = i_keyfrom(rkey);
|
||||||
|
_i_MAP_ONLY(res.ref->second = i_valfrom(rmapped);)
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif // i_no_emplace
|
||||||
|
|
||||||
|
static void
|
||||||
|
_c_MEMB(_drop_r_)(Self* s, int32_t tn) {
|
||||||
|
if (tn != 0) {
|
||||||
|
_c_MEMB(_drop_r_)(s, s->nodes[tn].link[0]);
|
||||||
|
_c_MEMB(_drop_r_)(s, s->nodes[tn].link[1]);
|
||||||
|
_c_MEMB(_value_drop)(s, &s->nodes[tn].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
if (self->capacity != 0) {
|
||||||
|
_c_MEMB(_drop_r_)(self, self->root);
|
||||||
|
_i_free_n(self->nodes, self->capacity + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // i_implement
|
||||||
|
#undef _i_is_set
|
||||||
|
#undef _i_is_map
|
||||||
|
#undef _i_sorted
|
||||||
|
#undef _i_keyref
|
||||||
|
#undef _i_MAP_ONLY
|
||||||
|
#undef _i_SET_ONLY
|
||||||
|
#include "sys/finalize.h"
|
||||||
109
stc/sort.h
Normal file
109
stc/sort.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/* Generic Quicksort in C, performs as fast as c++ std::sort(), and more robust.
|
||||||
|
template params:
|
||||||
|
#define i_key keytype - [required] (or use i_type, see below)
|
||||||
|
#define i_less(xp, yp) - optional less function. default: *xp < *yp
|
||||||
|
#define i_cmp(xp, yp) - alternative 3-way comparison. c_default_cmp(xp, yp)
|
||||||
|
#define T name - optional, defines {name}_sort(), else {i_key}s_sort().
|
||||||
|
#define T name, key - alternative one-liner to define both i_type and i_key.
|
||||||
|
|
||||||
|
// ex1:
|
||||||
|
#include <stdio.h>
|
||||||
|
#define i_key int
|
||||||
|
#include <stc/sort.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int nums[] = {23, 321, 5434, 25, 245, 1, 654, 33, 543, 21};
|
||||||
|
|
||||||
|
ints_sort(nums, c_arraylen(nums));
|
||||||
|
|
||||||
|
for (int i = 0; i < c_arraylen(nums); i++)
|
||||||
|
printf(" %d", nums[i]);
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
isize idx = ints_binary_search(nums, 25, c_arraylen(nums));
|
||||||
|
if (idx != c_NPOS) printf("found: %d\n", nums[idx]);
|
||||||
|
|
||||||
|
idx = ints_lower_bound(nums, 200, c_arraylen(nums));
|
||||||
|
if (idx != c_NPOS) printf("found lower 200: %d\n", nums[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ex2: Test on a deque !!
|
||||||
|
#include <stdio.h>
|
||||||
|
#define T IDeq, int, (c_use_cmp) // enable comparison functions
|
||||||
|
#include <stc/deque.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
IDeq nums = c_make(IDeq, {5434, 25, 245, 1, 654, 33, 543, 21});
|
||||||
|
IDeq_push_front(&nums, 23);
|
||||||
|
IDeq_push_front(&nums, 321);
|
||||||
|
|
||||||
|
IDeq_sort(&nums);
|
||||||
|
|
||||||
|
for (c_each (i, IDeq, nums))
|
||||||
|
printf(" %d", *i.ref);
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
isize idx = IDeq_binary_search(&nums, 25);
|
||||||
|
if (idx != c_NPOS) printf("found: %d\n", *IDeq_at(&nums, idx));
|
||||||
|
|
||||||
|
idx = IDeq_lower_bound(&nums, 200);
|
||||||
|
if (idx != c_NPOS) printf("found lower 200: %d\n", *IDeq_at(&nums, idx));
|
||||||
|
|
||||||
|
IDeq_drop(&nums);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifndef _i_template
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define _i_is_array
|
||||||
|
#if defined T && !defined i_type
|
||||||
|
#define i_type T
|
||||||
|
#endif
|
||||||
|
#if defined i_type && !defined i_key
|
||||||
|
#define Self c_GETARG(1, i_type)
|
||||||
|
#define i_key c_GETARG(2, i_type)
|
||||||
|
#elif defined i_type
|
||||||
|
#define Self i_type
|
||||||
|
#else
|
||||||
|
#define Self c_JOIN(i_key, s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef i_key Self;
|
||||||
|
typedef Self c_JOIN(Self, _value), c_JOIN(Self, _raw);
|
||||||
|
#define i_at(arr, idx) (&(arr)[idx])
|
||||||
|
#define i_at_mut i_at
|
||||||
|
#include "priv/template.h" // IWYU pragma: keep
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "priv/sort_prv.h"
|
||||||
|
|
||||||
|
#ifdef _i_is_array
|
||||||
|
#undef _i_is_array
|
||||||
|
#include "priv/linkage2.h"
|
||||||
|
#include "priv/template2.h"
|
||||||
|
#endif
|
||||||
|
#undef i_at
|
||||||
|
#undef i_at_mut
|
||||||
46
stc/sortedmap.h
Normal file
46
stc/sortedmap.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sorted map - implemented as an AA-tree (balanced binary tree).
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define T Intmap, int, int
|
||||||
|
#include <stc/sortedmap.h> // sorted map of int
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Intmap map = {0};
|
||||||
|
Intmap_insert(&map, 5, 25);
|
||||||
|
Intmap_insert(&map, 8, 38);
|
||||||
|
Intmap_insert(&map, 3, 43);
|
||||||
|
Intmap_insert(&map, 5, 55);
|
||||||
|
|
||||||
|
for (c_each_kv(k, v, Intmap, map))
|
||||||
|
printf(" %d -> %d\n", *k, *v);
|
||||||
|
|
||||||
|
Intmap_drop(&map);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix smap_
|
||||||
|
#include "smap.h"
|
||||||
47
stc/sortedset.h
Normal file
47
stc/sortedset.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sorted set - implemented as an AA-tree (balanced binary tree).
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define T Intset, int
|
||||||
|
#include <stc/sortedset.h> // sorted set of int
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Intset set = {0};
|
||||||
|
Intset_insert(&set, 5);
|
||||||
|
Intset_insert(&set, 8);
|
||||||
|
Intset_insert(&set, 3);
|
||||||
|
Intset_insert(&set, 5);
|
||||||
|
|
||||||
|
for (c_each(k, Intset, set))
|
||||||
|
printf(" %d\n", *k.ref);
|
||||||
|
|
||||||
|
Intset_drop(&set);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix sset_
|
||||||
|
#define _i_is_set
|
||||||
|
#include "smap.h"
|
||||||
46
stc/sset.h
Normal file
46
stc/sset.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sorted set - implemented as an AA-tree (balanced binary tree).
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define T Intset, int
|
||||||
|
#include <stc/sortedset.h> // sorted set of int
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Intset s = {0};
|
||||||
|
Intset_insert(&s, 5);
|
||||||
|
Intset_insert(&s, 8);
|
||||||
|
Intset_insert(&s, 3);
|
||||||
|
Intset_insert(&s, 5);
|
||||||
|
|
||||||
|
for (c_each(k, Intset, s))
|
||||||
|
printf("set %d\n", *k.ref);
|
||||||
|
Intset_drop(&s);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _i_prefix sset_
|
||||||
|
#define _i_is_set
|
||||||
|
#include "smap.h"
|
||||||
282
stc/stack.h
Normal file
282
stc/stack.h
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
// Stack - a simplified vec type without linear search and insert/erase inside the stack.
|
||||||
|
|
||||||
|
#ifndef STC_STACK_H_INCLUDED
|
||||||
|
#define STC_STACK_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // STC_STACK_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix stack_
|
||||||
|
#endif
|
||||||
|
#include "priv/template.h"
|
||||||
|
#ifndef i_declared
|
||||||
|
#if c_NUMARGS(i_type) == 4
|
||||||
|
#define i_capacity i_val
|
||||||
|
#endif
|
||||||
|
#ifdef i_capacity
|
||||||
|
#define i_no_clone
|
||||||
|
_c_DEFTYPES(declare_stack_fixed, Self, i_key, i_capacity);
|
||||||
|
#else
|
||||||
|
_c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
|
||||||
|
#ifdef i_capacity
|
||||||
|
STC_INLINE void _c_MEMB(_init)(Self* news)
|
||||||
|
{ news->size = 0; }
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* self)
|
||||||
|
{ (void)self; return i_capacity; }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n)
|
||||||
|
{ (void)self; return n <= i_capacity; }
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->capacity = self->size = 0;
|
||||||
|
self->data = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* self)
|
||||||
|
{ return self->capacity; }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) {
|
||||||
|
if (n > self->capacity || (n && n == self->size)) {
|
||||||
|
_m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, n);
|
||||||
|
if (d == NULL)
|
||||||
|
return false;
|
||||||
|
self->data = d;
|
||||||
|
self->capacity = n;
|
||||||
|
}
|
||||||
|
return self->data != NULL;
|
||||||
|
}
|
||||||
|
#endif // i_capacity
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_clear)(Self* self) {
|
||||||
|
if (self->size == 0) return;
|
||||||
|
_m_value *p = self->data + self->size;
|
||||||
|
while (p-- != self->data) { i_keydrop(p); }
|
||||||
|
self->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
#ifndef i_capacity
|
||||||
|
_i_free_n(self->data, self->capacity);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* self)
|
||||||
|
{ return self->size; }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* self)
|
||||||
|
{ return !self->size; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val)
|
||||||
|
{ (void)self; i_keydrop(val); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_append_uninit)(Self *self, isize n) {
|
||||||
|
isize len = self->size;
|
||||||
|
if (len + n >= _c_MEMB(_capacity)(self))
|
||||||
|
if (!_c_MEMB(_reserve)(self, len*3/2 + n))
|
||||||
|
return NULL;
|
||||||
|
self->size += n;
|
||||||
|
return self->data + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self)
|
||||||
|
{ _c_MEMB(_reserve)(self, self->size); }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self)
|
||||||
|
{ return &self->data[0]; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self)
|
||||||
|
{ return &self->data[0]; }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self)
|
||||||
|
{ return &self->data[self->size - 1]; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self)
|
||||||
|
{ return &self->data[self->size - 1]; }
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self)
|
||||||
|
{ return _c_MEMB(_back)(self); }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_top_mut)(Self* self)
|
||||||
|
{ return _c_MEMB(_back_mut)(self); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value val) {
|
||||||
|
if (self->size == _c_MEMB(_capacity)(self))
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->size*3/2 + 4))
|
||||||
|
return NULL;
|
||||||
|
_m_value* vp = self->data + self->size++;
|
||||||
|
*vp = val; return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_pop)(Self* self)
|
||||||
|
{ c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_pull)(Self* self)
|
||||||
|
{ c_assert(self->size); return self->data[--self->size]; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n)
|
||||||
|
{ while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; }
|
||||||
|
|
||||||
|
#if !defined _i_aux_alloc && !defined i_capacity
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ Self out = {0}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(isize cap)
|
||||||
|
{ Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size)
|
||||||
|
{ Self out = {_i_new_n(_m_value, size), size, size}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) {
|
||||||
|
Self out = {_i_new_n(_m_value, size), size, size};
|
||||||
|
while (size) out.data[--size] = i_keyfrom(default_raw);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) {
|
||||||
|
Self out = _c_MEMB(_with_capacity)(n);
|
||||||
|
_c_MEMB(_put_n)(&out, raw, n); return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, isize idx)
|
||||||
|
{ c_assert(c_uless(idx, self->size)); return self->data + idx; }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, isize idx)
|
||||||
|
{ c_assert(c_uless(idx, self->size)); return self->data + idx; }
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_INLINE Self _c_MEMB(_clone)(Self stk) {
|
||||||
|
Self out = stk, *self = &out; (void)self; // i_keyclone may use self via i_aux
|
||||||
|
out.data = NULL; out.size = out.capacity = 0;
|
||||||
|
_c_MEMB(_reserve)(&out, stk.size);
|
||||||
|
out.size = stk.size;
|
||||||
|
for (c_range(i, stk.size))
|
||||||
|
out.data[i] = i_keyclone(stk.data[i]);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) {
|
||||||
|
if (self == other) return;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
_c_MEMB(_reserve)(self, other->size);
|
||||||
|
for (c_range(i, other->size))
|
||||||
|
self->data[self->size++] = i_keyclone((other->data[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val)
|
||||||
|
{ (void)self; return i_keyclone(val); }
|
||||||
|
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val)
|
||||||
|
{ return i_keytoraw(val); }
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
// iteration
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) {
|
||||||
|
_m_iter it = {(_m_value*)self->data, (_m_value*)self->data};
|
||||||
|
if (self->size) it.end += self->size;
|
||||||
|
else it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) {
|
||||||
|
_m_iter it = {(_m_value*)self->data, (_m_value*)self->data};
|
||||||
|
if (self->size) { it.ref += self->size - 1; it.end -= 1; }
|
||||||
|
else it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_end)(const Self* self)
|
||||||
|
{ (void)self; _m_iter it = {0}; return it; }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self)
|
||||||
|
{ (void)self; _m_iter it = {0}; return it; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_next)(_m_iter* it)
|
||||||
|
{ if (++it->ref == it->end) it->ref = NULL; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_rnext)(_m_iter* it)
|
||||||
|
{ if (--it->ref == it->end) it->ref = NULL; }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n)
|
||||||
|
{ if ((it.ref += n) >= it.end) it.ref = NULL ; return it; }
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it)
|
||||||
|
{ return (it.ref - self->data); }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n)
|
||||||
|
{ self->size += n; }
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
#include "priv/sort_prv.h"
|
||||||
|
#endif // _i_has_cmp
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) {
|
||||||
|
(void)self;
|
||||||
|
const _m_value* p2 = i2.ref ? i2.ref : i1.end;
|
||||||
|
for (; i1.ref != p2; ++i1.ref) {
|
||||||
|
const _m_raw r = i_keytoraw(i1.ref);
|
||||||
|
if (i_eq((&raw), (&r)))
|
||||||
|
return i1;
|
||||||
|
}
|
||||||
|
i2.ref = NULL;
|
||||||
|
return i2;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); }
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
if (self->size != other->size) return false;
|
||||||
|
for (isize i = 0; i < self->size; ++i) {
|
||||||
|
const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i));
|
||||||
|
if (!(i_eq((&_rx), (&_ry)))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#include "sys/finalize.h"
|
||||||
118
stc/sys/crange.h
Normal file
118
stc/sys/crange.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/algorithm.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
crange r1 = crange_make(80, 90);
|
||||||
|
for (c_each(i, crange, r1))
|
||||||
|
printf(" %d", *i.ref);
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
c_filter(crange, c_iota(100, INT_MAX, 10), true
|
||||||
|
&& c_flt_skip(25)
|
||||||
|
&& c_flt_take(3)
|
||||||
|
&& printf(" %d", *value)
|
||||||
|
);
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private, include "stc/algorithm.h"
|
||||||
|
#ifndef STC_CRANGE_H_INCLUDED
|
||||||
|
#define STC_CRANGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../priv/linkage.h"
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// crange: isize range -----
|
||||||
|
|
||||||
|
typedef isize crange_value;
|
||||||
|
typedef struct { crange_value start, end, step, value; } crange;
|
||||||
|
typedef struct { crange_value *ref, end, step; } crange_iter;
|
||||||
|
|
||||||
|
STC_INLINE crange crange_make_3(crange_value start, crange_value stop, crange_value step)
|
||||||
|
{ crange r = {start, stop - (step > 0), step}; return r; }
|
||||||
|
|
||||||
|
#define crange_make(...) c_MACRO_OVERLOAD(crange_make, __VA_ARGS__)
|
||||||
|
#define crange_make_1(stop) crange_make_3(0, stop, 1) // NB! arg is stop
|
||||||
|
#define crange_make_2(start, stop) crange_make_3(start, stop, 1)
|
||||||
|
|
||||||
|
STC_INLINE crange_iter crange_begin(crange* self) {
|
||||||
|
self->value = self->start;
|
||||||
|
crange_iter it = {&self->value, self->end, self->step};
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void crange_next(crange_iter* it) {
|
||||||
|
if ((it->step > 0) == ((*it->ref += it->step) > it->end))
|
||||||
|
it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE crange_iter crange_advance(crange_iter it, size_t n) {
|
||||||
|
if ((it.step > 0) == ((*it.ref += it.step*(isize)n) > it.end))
|
||||||
|
it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iota: c++-like std::iota, use in iterations on-the-fly -----
|
||||||
|
// Note: c_iota() does not compile with c++, crange does.
|
||||||
|
#define c_iota(...) c_MACRO_OVERLOAD(c_iota, __VA_ARGS__)
|
||||||
|
#define c_iota_1(start) c_iota_3(start, INTPTR_MAX, 1) // NB! arg is start.
|
||||||
|
#define c_iota_2(start, stop) c_iota_3(start, stop, 1)
|
||||||
|
#define c_iota_3(start, stop, step) ((crange[]){crange_make_3(start, stop, step)})[0]
|
||||||
|
|
||||||
|
|
||||||
|
// crange32 -----
|
||||||
|
|
||||||
|
typedef int32_t crange32_value;
|
||||||
|
typedef struct { crange32_value start, end, step, value; } crange32;
|
||||||
|
typedef struct { crange32_value *ref, end, step; } crange32_iter;
|
||||||
|
|
||||||
|
STC_INLINE crange32 crange32_make_3(crange32_value start, crange32_value stop, crange32_value step)
|
||||||
|
{ crange32 r = {start, stop - (step > 0), step}; return r; }
|
||||||
|
|
||||||
|
#define crange32_make(...) c_MACRO_OVERLOAD(crange32_make, __VA_ARGS__)
|
||||||
|
#define crange32_make_1(stop) crange32_make_3(0, stop, 1) // NB! arg is stop
|
||||||
|
#define crange32_make_2(start, stop) crange32_make_3(start, stop, 1)
|
||||||
|
|
||||||
|
STC_INLINE crange32_iter crange32_begin(crange32* self) {
|
||||||
|
self->value = self->start;
|
||||||
|
crange32_iter it = {&self->value, self->end, self->step};
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void crange32_next(crange32_iter* it) {
|
||||||
|
if ((it->step > 0) == ((*it->ref += it->step) > it->end))
|
||||||
|
it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE crange32_iter crange32_advance(crange32_iter it, uint32_t n) {
|
||||||
|
if ((it.step > 0) == ((*it.ref += it.step*(int32_t)n) > it.end))
|
||||||
|
it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "../priv/linkage2.h"
|
||||||
|
#endif // STC_CRANGE_H_INCLUDE
|
||||||
185
stc/sys/filter.h
Normal file
185
stc/sys/filter.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#define T Vec, int
|
||||||
|
#include <stc/cstack.h>
|
||||||
|
#include <stc/algorithm.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Vec vec = c_make(Vec, {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10, 11, 12, 5});
|
||||||
|
|
||||||
|
c_filter(Vec, vec, true
|
||||||
|
&& c_flt_skipwhile(*value < 3) // skip leading values < 3
|
||||||
|
&& (*value & 1) == 1 // then use odd values only
|
||||||
|
&& c_flt_map(*value * 2) // multiply by 2
|
||||||
|
&& c_flt_takewhile(*value < 20) // stop if mapped *value >= 20
|
||||||
|
&& printf(" %d", *value) // print value
|
||||||
|
);
|
||||||
|
// 6 10 14 2 6 18
|
||||||
|
puts("");
|
||||||
|
Vec_drop(&vec);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private, include "stc/algorithm.h"
|
||||||
|
#ifndef STC_FILTER_H_INCLUDED
|
||||||
|
#define STC_FILTER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// ------- c_filter --------
|
||||||
|
#define c_flt_take(n) _flt_take(&fltbase, n)
|
||||||
|
#define c_flt_skip(n) (c_flt_counter() > (n))
|
||||||
|
#define c_flt_takewhile(pred) _flt_takewhile(&fltbase, pred)
|
||||||
|
#define c_flt_skipwhile(pred) (fltbase.sb[fltbase.sb_top++] |= !(pred))
|
||||||
|
#define c_flt_counter() (++fltbase.sn[++fltbase.sn_top])
|
||||||
|
#define c_flt_getcount() (fltbase.sn[fltbase.sn_top])
|
||||||
|
#define c_flt_map(expr) (_mapped = (expr), value = &_mapped)
|
||||||
|
#define c_flt_src _it.ref
|
||||||
|
|
||||||
|
#define c_filter(C, cnt, pred) \
|
||||||
|
_c_filter(C, C##_begin(&cnt), _, pred)
|
||||||
|
|
||||||
|
#define c_filter_from(C, start, pred) \
|
||||||
|
_c_filter(C, start, _, pred)
|
||||||
|
|
||||||
|
#define c_filter_reverse(C, cnt, pred) \
|
||||||
|
_c_filter(C, C##_rbegin(&cnt), _r, pred)
|
||||||
|
|
||||||
|
#define c_filter_reverse_from(C, start, pred) \
|
||||||
|
_c_filter(C, start, _r, pred)
|
||||||
|
|
||||||
|
#define _c_filter(C, start, rev, pred) do { \
|
||||||
|
struct _flt_base fltbase = {0}; \
|
||||||
|
C##_iter _it = start; \
|
||||||
|
C##_value *value = _it.ref, _mapped = {0}; \
|
||||||
|
for ((void)_mapped ; !fltbase.done & (_it.ref != NULL) ; \
|
||||||
|
C##rev##next(&_it), value = _it.ref, fltbase.sn_top=0, fltbase.sb_top=0) \
|
||||||
|
(void)(pred); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// ------- c_filter_zip --------
|
||||||
|
#define c_filter_zip(...) c_MACRO_OVERLOAD(c_filter_zip, __VA_ARGS__)
|
||||||
|
#define c_filter_zip_4(C, cnt1, cnt2, pred) \
|
||||||
|
c_filter_zip_5(C, cnt1, C, cnt2, pred)
|
||||||
|
#define c_filter_zip_5(C1, cnt1, C2, cnt2, pred) \
|
||||||
|
_c_filter_zip(C1, C1##_begin(&cnt1), C2, C2##_begin(&cnt2), _, pred)
|
||||||
|
|
||||||
|
#define c_filter_reverse_zip(...) c_MACRO_OVERLOAD(c_filter_reverse_zip, __VA_ARGS__)
|
||||||
|
#define c_filter_reverse_zip_4(C, cnt1, cnt2, pred) \
|
||||||
|
c_filter_reverse_zip_5(C, cnt1, C, cnt2, pred)
|
||||||
|
#define c_filter_reverse_zip_5(C1, cnt1, C2, cnt2, pred) \
|
||||||
|
_c_filter_zip(C1, C1##_rbegin(&cnt1), C2, C2##_rbegin(&cnt2), _r, pred)
|
||||||
|
|
||||||
|
#define c_filter_pairwise(C, cnt, pred) \
|
||||||
|
_c_filter_zip(C, C##_begin(&cnt), C, C##_advance(_it1, 1), _, pred)
|
||||||
|
|
||||||
|
#define c_flt_map1(expr) (_mapped1 = (expr), value1 = &_mapped1)
|
||||||
|
#define c_flt_map2(expr) (_mapped2 = (expr), value2 = &_mapped2)
|
||||||
|
#define c_flt_src1 _it1.ref
|
||||||
|
#define c_flt_src2 _it2.ref
|
||||||
|
|
||||||
|
#define _c_filter_zip(C1, start1, C2, start2, rev, pred) do { \
|
||||||
|
struct _flt_base fltbase = {0}; \
|
||||||
|
C1##_iter _it1 = start1; \
|
||||||
|
C2##_iter _it2 = start2; \
|
||||||
|
C1##_value* value1 = _it1.ref, _mapped1; (void)_mapped1; \
|
||||||
|
C2##_value* value2 = _it2.ref, _mapped2; (void)_mapped2; \
|
||||||
|
for (; !fltbase.done & (_it1.ref != NULL) & (_it2.ref != NULL); \
|
||||||
|
C1##rev##next(&_it1), value1 = _it1.ref, C2##rev##next(&_it2), value2 = _it2.ref, \
|
||||||
|
fltbase.sn_top=0, fltbase.sb_top=0) \
|
||||||
|
(void)(pred); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// ------- c_ffilter --------
|
||||||
|
// c_ffilter allows to execute imperative statements for each element
|
||||||
|
// in a for-loop, e.g., calling nested generic statements instead
|
||||||
|
// of defining a function/expression for it:
|
||||||
|
/*
|
||||||
|
Vec vec = ..., vec2 = ...;
|
||||||
|
for (c_ffilter(i, Vec, vec, true
|
||||||
|
&& c_fflt_skipwhile(i, *i.ref < 3) // skip leading values < 3
|
||||||
|
&& (*i.ref & 1) == 1 // then use odd values only
|
||||||
|
&& c_fflt_map(i, *i.ref * 2) // multiply by 2
|
||||||
|
&& c_fflt_takewhile(i, *i.ref < 20) // stop if mapped *i.ref >= 20
|
||||||
|
)){
|
||||||
|
c_eraseremove_if(Vec, &vec2, *value == *i.ref);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define c_fflt_take(i, n) _flt_take(&i.base, n)
|
||||||
|
#define c_fflt_skip(i, n) (c_fflt_counter(i) > (n))
|
||||||
|
#define c_fflt_takewhile(i, pred) _flt_takewhile(&i.base, pred)
|
||||||
|
#define c_fflt_skipwhile(i, pred) (i.base.sb[i.base.sb_top++] |= !(pred))
|
||||||
|
#define c_fflt_counter(i) (++i.base.sn[++i.base.sn_top])
|
||||||
|
#define c_fflt_getcount(i) (i.base.sn[i.base.sn_top])
|
||||||
|
#define c_fflt_map(i, expr) (i.mapped = (expr), i.ref = &i.mapped)
|
||||||
|
#define c_fflt_src(i) i.iter.ref
|
||||||
|
|
||||||
|
#define c_forfilter(...) for (c_ffilter(__VA_ARGS__))
|
||||||
|
#define c_forfilter_from(...) for (c_ffilter_from(__VA_ARGS__))
|
||||||
|
#define c_forfilter_reverse(...) for (c_ffilter_reverse(__VA_ARGS__))
|
||||||
|
#define c_forfilter_reverse_from(...) for (c_ffilter_reverse_from(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define c_ffilter(i, C, cnt, pred) \
|
||||||
|
_c_ffilter(i, C, C##_begin(&cnt), _, pred)
|
||||||
|
|
||||||
|
#define c_ffilter_from(i, C, start, pred) \
|
||||||
|
_c_ffilter(i, C, start, _, pred)
|
||||||
|
|
||||||
|
#define c_ffilter_reverse(i, C, cnt,pred) \
|
||||||
|
_c_ffilter(i, C, C##_rbegin(&cnt), _r, pred)
|
||||||
|
|
||||||
|
#define c_ffilter_reverse_from(i, C, start, pred) \
|
||||||
|
_c_ffilter(i, C, start, _r, pred)
|
||||||
|
|
||||||
|
#define _c_ffilter(i, C, start, rev, pred) \
|
||||||
|
struct {C##_iter iter; C##_value *ref, mapped; struct _flt_base base;} \
|
||||||
|
i = {.iter=start, .ref=i.iter.ref} ; !i.base.done & (i.iter.ref != NULL) ; \
|
||||||
|
C##rev##next(&i.iter), i.ref = i.iter.ref, i.base.sn_top=0, i.base.sb_top=0) \
|
||||||
|
if (!(pred)) ; else if (1
|
||||||
|
|
||||||
|
// ------------------------ private -------------------------
|
||||||
|
#ifndef c_NFILTERS
|
||||||
|
#define c_NFILTERS 20
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _flt_base {
|
||||||
|
uint8_t sn_top, sb_top;
|
||||||
|
bool done, sb[c_NFILTERS];
|
||||||
|
uint32_t sn[c_NFILTERS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool _flt_take(struct _flt_base* base, uint32_t n) {
|
||||||
|
uint32_t k = ++base->sn[++base->sn_top];
|
||||||
|
base->done |= (k >= n);
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _flt_takewhile(struct _flt_base* base, bool pred) {
|
||||||
|
bool skip = (base->sb[base->sb_top++] |= !pred);
|
||||||
|
base->done |= skip;
|
||||||
|
return !skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_FILTER_H_INCLUDED
|
||||||
5
stc/sys/finalize.h
Normal file
5
stc/sys/finalize.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef i_extend
|
||||||
|
#include "../priv/linkage2.h"
|
||||||
|
#include "../priv/template2.h"
|
||||||
|
#endif
|
||||||
|
#undef i_extend
|
||||||
171
stc/sys/sumtype.h
Normal file
171
stc/sys/sumtype.h
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
// https://stackoverflow.com/questions/70935435/how-to-create-variants-in-rust
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stc/cstr.h>
|
||||||
|
#include <stc/algorithm.h>
|
||||||
|
|
||||||
|
c_sumtype (Action,
|
||||||
|
(ActionSpeak, cstr),
|
||||||
|
(ActionQuit, bool),
|
||||||
|
(ActionRunFunc, struct {
|
||||||
|
int32_t (*func)(int32_t, int32_t);
|
||||||
|
int32_t v1, v2;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
void Action_drop(Action* self) {
|
||||||
|
if (c_is(self, ActionSpeak, s))
|
||||||
|
cstr_drop(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void action(Action* action) {
|
||||||
|
c_when (action) {
|
||||||
|
c_is(ActionSpeak, s) {
|
||||||
|
printf("Asked to speak: %s\n", cstr_str(s));
|
||||||
|
}
|
||||||
|
c_is(ActionQuit) {
|
||||||
|
printf("Asked to quit!\n");
|
||||||
|
}
|
||||||
|
c_is(ActionRunFunc, r) {
|
||||||
|
int32_t res = r->func(r->v1, r->v2);
|
||||||
|
printf("v1: %d, v2: %d, res: %d\n", r->v1, r->v2, res);
|
||||||
|
}
|
||||||
|
c_otherwise assert(!"no match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t add(int32_t a, int32_t b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Action act1 = c_variant(ActionSpeak, cstr_from("Hello"));
|
||||||
|
Action act2 = c_variant(ActionQuit, 1);
|
||||||
|
Action act3 = c_variant(ActionRunFunc, {add, 5, 6});
|
||||||
|
|
||||||
|
action(&act1);
|
||||||
|
action(&act2);
|
||||||
|
action(&act3);
|
||||||
|
|
||||||
|
c_drop(Action, &act1, &act2, &act3);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifndef STC_SUMTYPE_H_INCLUDED
|
||||||
|
#define STC_SUMTYPE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#define _c_EMPTY()
|
||||||
|
#define _c_LOOP_INDIRECTION() c_LOOP
|
||||||
|
#define _c_LOOP_END_1 ,_c_LOOP1
|
||||||
|
#define _c_LOOP0(f,T,x,...) f c_EXPAND((T, c_EXPAND x)) _c_LOOP_INDIRECTION _c_EMPTY()()(f,T,__VA_ARGS__)
|
||||||
|
#define _c_LOOP1(...)
|
||||||
|
#define _c_CHECK(x,...) c_TUPLE_AT_1(__VA_ARGS__,x,)
|
||||||
|
#define _c_E0(...) __VA_ARGS__
|
||||||
|
#define _c_E1(...) _c_E0(_c_E0(_c_E0(_c_E0(_c_E0(_c_E0(__VA_ARGS__))))))
|
||||||
|
#define _c_E2(...) _c_E1(_c_E1(_c_E1(_c_E1(_c_E1(_c_E1(__VA_ARGS__))))))
|
||||||
|
#define c_EVAL(...) _c_E2(_c_E2(_c_E2(__VA_ARGS__))) // currently supports up to 130 variants
|
||||||
|
#define c_LOOP(f,T,x,...) _c_CHECK(_c_LOOP0, c_JOIN(_c_LOOP_END_, c_NUMARGS(c_EXPAND x)))(f,T,x,__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
|
#define _c_enum_1(x,...) (x=__LINE__*1000, __VA_ARGS__)
|
||||||
|
#define _c_vartuple_tag(T, Tag, ...) Tag,
|
||||||
|
#define _c_vartuple_type(T, Tag, ...) typedef __VA_ARGS__ Tag##_type; typedef T Tag##_sumtype;
|
||||||
|
#define _c_vartuple_var(T, Tag, ...) struct { enum enum_##T tag; Tag##_type get; } Tag;
|
||||||
|
|
||||||
|
#define c_sumtype(T, ...) \
|
||||||
|
typedef union T T; \
|
||||||
|
enum enum_##T { c_EVAL(c_LOOP(_c_vartuple_tag, T, _c_enum_1 __VA_ARGS__, (0),)) }; \
|
||||||
|
c_EVAL(c_LOOP(_c_vartuple_type, T, __VA_ARGS__, (0),)) \
|
||||||
|
union T { \
|
||||||
|
struct { enum enum_##T tag; } _any_; \
|
||||||
|
c_EVAL(c_LOOP(_c_vartuple_var, T, __VA_ARGS__, (0),)) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF
|
||||||
|
#define c_when(varptr) \
|
||||||
|
for (__typeof__(varptr) _vp1 = (varptr); _vp1; _vp1 = NULL) \
|
||||||
|
switch (_vp1->_any_.tag)
|
||||||
|
|
||||||
|
#define c_is_2(Tag, x) \
|
||||||
|
break; case Tag: \
|
||||||
|
for (__typeof__(_vp1->Tag.get)* x = &_vp1->Tag.get; x; x = NULL)
|
||||||
|
|
||||||
|
#define c_is_3(varptr, Tag, x) \
|
||||||
|
false) ; else for (__typeof__(varptr) _vp2 = (varptr); _vp2; _vp2 = NULL) \
|
||||||
|
if (c_is_variant(_vp2, Tag)) \
|
||||||
|
for (__typeof__(_vp2->Tag.get) *x = &_vp2->Tag.get; x; x = NULL
|
||||||
|
#else
|
||||||
|
typedef union { struct { int tag; } _any_; } _c_any_variant;
|
||||||
|
#define c_when(varptr) \
|
||||||
|
for (_c_any_variant* _vp1 = (_c_any_variant *)(varptr); \
|
||||||
|
_vp1; _vp1 = NULL, (void)sizeof((varptr)->_any_.tag)) \
|
||||||
|
switch (_vp1->_any_.tag)
|
||||||
|
|
||||||
|
#define c_is_2(Tag, x) \
|
||||||
|
break; case Tag: \
|
||||||
|
for (Tag##_type *x = &((Tag##_sumtype *)_vp1)->Tag.get; x; x = NULL)
|
||||||
|
|
||||||
|
#define c_is_3(varptr, Tag, x) \
|
||||||
|
false) ; else for (Tag##_sumtype* _vp2 = c_const_cast(Tag##_sumtype*, varptr); _vp2; _vp2 = NULL) \
|
||||||
|
if (c_is_variant(_vp2, Tag)) \
|
||||||
|
for (Tag##_type *x = &_vp2->Tag.get; x; x = NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handling multiple tags with different payloads:
|
||||||
|
#define c_is(...) c_MACRO_OVERLOAD(c_is, __VA_ARGS__)
|
||||||
|
#define c_is_1(Tag) \
|
||||||
|
break; case Tag:
|
||||||
|
|
||||||
|
#define c_or_is(Tag) \
|
||||||
|
; case Tag:
|
||||||
|
|
||||||
|
// Type checked multiple tags with same payload:
|
||||||
|
#define c_is_same(...) c_MACRO_OVERLOAD(c_is_same, __VA_ARGS__)
|
||||||
|
#define _c_chk(Tag1, Tag2) \
|
||||||
|
case 1 ? Tag1 : sizeof((Tag1##_type*)0 == (Tag2##_type*)0):
|
||||||
|
#define c_is_same_2(Tag1, Tag2) \
|
||||||
|
break; _c_chk(Tag1, Tag2) case Tag2:
|
||||||
|
#define c_is_same_3(Tag1, Tag2, Tag3) \
|
||||||
|
break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) case Tag3:
|
||||||
|
#define c_is_same_4(Tag1, Tag2, Tag3, Tag4) \
|
||||||
|
break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) _c_chk(Tag3, Tag4) case Tag4:
|
||||||
|
|
||||||
|
#define c_otherwise \
|
||||||
|
break; default:
|
||||||
|
|
||||||
|
#define c_variant(Tag, ...) \
|
||||||
|
(c_literal(Tag##_sumtype){.Tag={.tag=Tag, .get=__VA_ARGS__}})
|
||||||
|
|
||||||
|
#define c_is_variant(varptr, Tag) \
|
||||||
|
((varptr)->Tag.tag == Tag)
|
||||||
|
|
||||||
|
#define c_get_if(varptr, Tag) \
|
||||||
|
(c_is_variant(varptr, Tag) ? &(varptr)->Tag.get : NULL)
|
||||||
|
|
||||||
|
#define c_variant_index(varptr) \
|
||||||
|
((int)(varptr)->_any_.tag)
|
||||||
|
|
||||||
|
#endif // STC_SUMTYPE_H_INCLUDED
|
||||||
188
stc/sys/utility.h
Normal file
188
stc/sys/utility.h
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
// IWYU pragma: private, include "stc/algorithm.h"
|
||||||
|
#ifndef STC_UTILITY_H_INCLUDED
|
||||||
|
#define STC_UTILITY_H_INCLUDED
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_find_if, c_find_reverse_if
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
#define c_find_if(...) c_MACRO_OVERLOAD(c_find_if, __VA_ARGS__)
|
||||||
|
#define c_find_if_4(C, cnt, outit_ptr, pred) \
|
||||||
|
_c_find(C, C##_begin(&cnt), NULL, _, outit_ptr, pred)
|
||||||
|
|
||||||
|
#define c_find_if_5(C, start, finish, outit_ptr, pred) \
|
||||||
|
_c_find(C, start, (finish).ref, _, outit_ptr, pred)
|
||||||
|
|
||||||
|
#define c_find_reverse_if(...) c_MACRO_OVERLOAD(c_find_reverse_if, __VA_ARGS__)
|
||||||
|
#define c_find_reverse_if_4(C, cnt, outit_ptr, pred) \
|
||||||
|
_c_find(C, C##_rbegin(&cnt), NULL, _r, outit_ptr, pred)
|
||||||
|
|
||||||
|
#define c_find_reverse_if_5(C, rstart, rfinish, outit_ptr, pred) \
|
||||||
|
_c_find(C, rstart, (rfinish).ref, _r, outit_ptr, pred)
|
||||||
|
|
||||||
|
// private
|
||||||
|
#define _c_find(C, start, endref, rev, outit_ptr, pred) do { \
|
||||||
|
C##_iter* _out = outit_ptr; \
|
||||||
|
const C##_value *value, *_endref = endref; \
|
||||||
|
for (*_out = start; (value = _out->ref) != _endref; C##rev##next(_out)) \
|
||||||
|
if (pred) goto c_JOIN(findif_, __LINE__); \
|
||||||
|
_out->ref = NULL; c_JOIN(findif_, __LINE__):; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_reverse
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
#define c_reverse_array(array, n) do { \
|
||||||
|
typedef struct { char d[sizeof 0[array]]; } _etype; \
|
||||||
|
_etype* _arr = (_etype *)(array); \
|
||||||
|
for (isize _i = 0, _j = (n) - 1; _i < _j; ++_i, --_j) \
|
||||||
|
c_swap(_arr + _i, _arr + _j); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// Compiles with vec, stack, and deque, and cspan container types:
|
||||||
|
#define c_reverse(CntType, self) do { \
|
||||||
|
CntType* _self = self; \
|
||||||
|
for (isize _i = 0, _j = CntType##_size(_self) - 1; _i < _j; ++_i, --_j) \
|
||||||
|
c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_erase_if
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
// Use with: list, hashmap, hashset, sortedmap, sortedset:
|
||||||
|
#define c_erase_if(C, cnt_ptr, pred) do { \
|
||||||
|
C* _cnt = cnt_ptr; \
|
||||||
|
const C##_value* value; \
|
||||||
|
for (C##_iter _it = C##_begin(_cnt); (value = _it.ref); ) { \
|
||||||
|
if (pred) _it = C##_erase_at(_cnt, _it); \
|
||||||
|
else C##_next(&_it); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_eraseremove_if
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
// Use with: stack, vec, deque, queue:
|
||||||
|
#define c_eraseremove_if(C, cnt_ptr, pred) do { \
|
||||||
|
C* _cnt = cnt_ptr; \
|
||||||
|
isize _n = 0; \
|
||||||
|
const C##_value* value; \
|
||||||
|
C##_iter _i, _it = C##_begin(_cnt); \
|
||||||
|
while ((value = _it.ref) && !(pred)) \
|
||||||
|
C##_next(&_it); \
|
||||||
|
for (_i = _it; (value = _it.ref); C##_next(&_it)) { \
|
||||||
|
if (pred) C##_value_drop(_cnt, _it.ref), ++_n; \
|
||||||
|
else *_i.ref = *_it.ref, C##_next(&_i); \
|
||||||
|
} \
|
||||||
|
C##_adjust_end_(_cnt, -_n); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_copy_to, c_copy_if
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
#define c_copy_to(...) c_MACRO_OVERLOAD(c_copy_to, __VA_ARGS__)
|
||||||
|
#define c_copy_to_3(C, outcnt_ptr, cnt) \
|
||||||
|
_c_copy_if(C, outcnt_ptr, _, C, cnt, true)
|
||||||
|
|
||||||
|
#define c_copy_to_4(C_out, outcnt_ptr, C, cnt) \
|
||||||
|
_c_copy_if(C_out, outcnt_ptr, _, C, cnt, true)
|
||||||
|
|
||||||
|
#define c_copy_if(...) c_MACRO_OVERLOAD(c_copy_if, __VA_ARGS__)
|
||||||
|
#define c_copy_if_4(C, outcnt_ptr, cnt, pred) \
|
||||||
|
_c_copy_if(C, outcnt_ptr, _, C, cnt, pred)
|
||||||
|
|
||||||
|
#define c_copy_if_5(C_out, outcnt_ptr, C, cnt, pred) \
|
||||||
|
_c_copy_if(C_out, outcnt_ptr, _, C, cnt, pred)
|
||||||
|
|
||||||
|
// private
|
||||||
|
#define _c_copy_if(C_out, outcnt_ptr, rev, C, cnt, pred) do { \
|
||||||
|
C_out *_out = outcnt_ptr; \
|
||||||
|
C _cnt = cnt; \
|
||||||
|
const C##_value* value; \
|
||||||
|
for (C##_iter _it = C##rev##begin(&_cnt); (value = _it.ref); C##rev##next(&_it)) \
|
||||||
|
if (pred) C_out##_push(_out, C_out##_value_clone(_out, *_it.ref)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_all_of, c_any_of, c_none_of
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
#define c_all_of(C, cnt, outbool_ptr, pred) do { \
|
||||||
|
C##_iter _it; \
|
||||||
|
c_find_if_4(C, cnt, &_it, !(pred)); \
|
||||||
|
*(outbool_ptr) = _it.ref == NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define c_any_of(C, cnt, outbool_ptr, pred) do { \
|
||||||
|
C##_iter _it; \
|
||||||
|
c_find_if_4(C, cnt, &_it, pred); \
|
||||||
|
*(outbool_ptr) = _it.ref != NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define c_none_of(C, cnt, outbool_ptr, pred) do { \
|
||||||
|
C##_iter _it; \
|
||||||
|
c_find_if_4(C, cnt, &_it, pred); \
|
||||||
|
*(outbool_ptr) = _it.ref == NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
// c_min, c_max, c_min_n, c_max_n
|
||||||
|
// --------------------------------
|
||||||
|
#define _c_minmax_call(fn, T, ...) \
|
||||||
|
fn(c_make_array(T, {__VA_ARGS__}), c_sizeof((T[]){__VA_ARGS__})/c_sizeof(T))
|
||||||
|
|
||||||
|
#define c_min(...) _c_minmax_call(c_min_n, isize, __VA_ARGS__)
|
||||||
|
#define c_umin(...) _c_minmax_call(c_umin_n, size_t, __VA_ARGS__)
|
||||||
|
#define c_min32(...) _c_minmax_call(c_min32_n, int32_t, __VA_ARGS__)
|
||||||
|
#define c_fmin(...) _c_minmax_call(c_fmin_n, float, __VA_ARGS__)
|
||||||
|
#define c_dmin(...) _c_minmax_call(c_dmin_n, double, __VA_ARGS__)
|
||||||
|
#define c_max(...) _c_minmax_call(c_max_n, isize, __VA_ARGS__)
|
||||||
|
#define c_umax(...) _c_minmax_call(c_umax_n, size_t, __VA_ARGS__)
|
||||||
|
#define c_max32(...) _c_minmax_call(c_max32_n, int32_t, __VA_ARGS__)
|
||||||
|
#define c_fmax(...) _c_minmax_call(c_fmax_n, float, __VA_ARGS__)
|
||||||
|
#define c_dmax(...) _c_minmax_call(c_dmax_n, double, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define _c_minmax_def(fn, T, opr) \
|
||||||
|
static inline T fn(const T a[], isize n) { \
|
||||||
|
T x = a[0]; \
|
||||||
|
for (isize i = 1; i < n; ++i) if (a[i] opr x) x = a[i]; \
|
||||||
|
return x; \
|
||||||
|
}
|
||||||
|
_c_minmax_def(c_min32_n, int32_t, <)
|
||||||
|
_c_minmax_def(c_min_n, isize, <)
|
||||||
|
_c_minmax_def(c_umin_n, size_t, <)
|
||||||
|
_c_minmax_def(c_fmin_n, float, <)
|
||||||
|
_c_minmax_def(c_dmin_n, double, <)
|
||||||
|
_c_minmax_def(c_max32_n, int32_t, >)
|
||||||
|
_c_minmax_def(c_max_n, isize, >)
|
||||||
|
_c_minmax_def(c_umax_n, size_t, >)
|
||||||
|
_c_minmax_def(c_fmax_n, float, >)
|
||||||
|
_c_minmax_def(c_dmax_n, double, >)
|
||||||
|
|
||||||
|
#endif // STC_UTILITY_H_INCLUDED
|
||||||
223
stc/types.h
Normal file
223
stc/types.h
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STC_TYPES_H_INCLUDED
|
||||||
|
#define STC_TYPES_H_INCLUDED
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define declare_rc(C, KEY) declare_arc(C, KEY)
|
||||||
|
#define declare_list(C, KEY) _declare_list(C, KEY,)
|
||||||
|
#define declare_stack(C, KEY) _declare_stack(C, KEY,)
|
||||||
|
#define declare_vec(C, KEY) _declare_stack(C, KEY,)
|
||||||
|
#define declare_pqueue(C, KEY) _declare_stack(C, KEY,)
|
||||||
|
#define declare_queue(C, KEY) _declare_queue(C, KEY,)
|
||||||
|
#define declare_deque(C, KEY) _declare_queue(C, KEY,)
|
||||||
|
#define declare_hashmap(C, KEY, VAL) _declare_htable(C, KEY, VAL, c_true, c_false,)
|
||||||
|
#define declare_hashset(C, KEY) _declare_htable(C, KEY, KEY, c_false, c_true,)
|
||||||
|
#define declare_sortedmap(C, KEY, VAL) _declare_aatree(C, KEY, VAL, c_true, c_false,)
|
||||||
|
#define declare_sortedset(C, KEY) _declare_aatree(C, KEY, KEY, c_false, c_true,)
|
||||||
|
|
||||||
|
#define declare_list_aux(C, KEY, AUX) _declare_list(C, KEY, AUX aux;)
|
||||||
|
#define declare_stack_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;)
|
||||||
|
#define declare_vec_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;)
|
||||||
|
#define declare_pqueue_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;)
|
||||||
|
#define declare_queue_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;)
|
||||||
|
#define declare_deque_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;)
|
||||||
|
#define declare_hashmap_aux(C, KEY, VAL, AUX) _declare_htable(C, KEY, VAL, c_true, c_false, AUX aux;)
|
||||||
|
#define declare_hashset_aux(C, KEY, AUX) _declare_htable(C, KEY, KEY, c_false, c_true, AUX aux;)
|
||||||
|
#define declare_sortedmap_aux(C, KEY, VAL, AUX) _declare_aatree(C, KEY, VAL, c_true, c_false, AUX aux;)
|
||||||
|
#define declare_sortedset_aux(C, KEY, AUX) _declare_aatree(C, KEY, KEY, c_false, c_true, AUX aux;)
|
||||||
|
|
||||||
|
// csview : non-null terminated string view
|
||||||
|
typedef const char csview_value;
|
||||||
|
typedef struct csview {
|
||||||
|
csview_value* buf;
|
||||||
|
ptrdiff_t size;
|
||||||
|
} csview;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
csview_value* ref;
|
||||||
|
csview chr;
|
||||||
|
struct { csview chr; csview_value* end; } u8;
|
||||||
|
} csview_iter;
|
||||||
|
|
||||||
|
#define c_sv(...) c_MACRO_OVERLOAD(c_sv, __VA_ARGS__)
|
||||||
|
#define c_sv_1(literal) c_sv_2(literal, c_litstrlen(literal))
|
||||||
|
#define c_sv_2(str, n) (c_literal(csview){str, n})
|
||||||
|
#define c_svfmt "%.*s"
|
||||||
|
#define c_svarg(sv) (int)(sv).size, (sv).buf // printf(c_svfmt "\n", c_svarg(sv));
|
||||||
|
|
||||||
|
// zsview : zero-terminated string view
|
||||||
|
typedef csview_value zsview_value;
|
||||||
|
typedef struct zsview {
|
||||||
|
zsview_value* str;
|
||||||
|
ptrdiff_t size;
|
||||||
|
} zsview;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
zsview_value* ref;
|
||||||
|
csview chr;
|
||||||
|
} zsview_iter;
|
||||||
|
|
||||||
|
#define c_zv(literal) (c_literal(zsview){literal, c_litstrlen(literal)})
|
||||||
|
|
||||||
|
// cstr : zero-terminated owning string (short string optimized - sso)
|
||||||
|
typedef char cstr_value;
|
||||||
|
typedef struct { cstr_value* data; intptr_t size, cap; } cstr_buf;
|
||||||
|
typedef union cstr {
|
||||||
|
struct { cstr_buf *a, *b, *c; } _dummy;
|
||||||
|
struct { cstr_value* data; uintptr_t size; uintptr_t ncap; } lon;
|
||||||
|
struct { cstr_value data[ sizeof(cstr_buf) - 1 ]; uint8_t size; } sml;
|
||||||
|
} cstr;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
csview chr; // utf8 character/codepoint
|
||||||
|
const cstr_value* ref;
|
||||||
|
} cstr_iter;
|
||||||
|
|
||||||
|
#define c_true(...) __VA_ARGS__
|
||||||
|
#define c_false(...)
|
||||||
|
|
||||||
|
#define declare_arc(SELF, VAL) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
typedef struct SELF##_ctrl SELF##_ctrl; \
|
||||||
|
\
|
||||||
|
typedef union SELF { \
|
||||||
|
SELF##_value* get; \
|
||||||
|
SELF##_ctrl* ctrl; \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define declare_arc2(SELF, VAL) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
typedef struct SELF##_ctrl SELF##_ctrl; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_value* get; \
|
||||||
|
SELF##_ctrl* ctrl2; \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define declare_box(SELF, VAL) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_value* get; \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define _declare_queue(SELF, VAL, AUXDEF) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_value *cbuf; \
|
||||||
|
ptrdiff_t start, end, capmask; \
|
||||||
|
AUXDEF \
|
||||||
|
} SELF; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref; \
|
||||||
|
ptrdiff_t pos; \
|
||||||
|
const SELF* _s; \
|
||||||
|
} SELF##_iter
|
||||||
|
|
||||||
|
#define _declare_list(SELF, VAL, AUXDEF) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
typedef struct SELF##_node SELF##_node; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref; \
|
||||||
|
SELF##_node *const *_last, *prev; \
|
||||||
|
} SELF##_iter; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_node *last; \
|
||||||
|
AUXDEF \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define _declare_htable(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \
|
||||||
|
typedef KEY SELF##_key; \
|
||||||
|
typedef VAL SELF##_mapped; \
|
||||||
|
\
|
||||||
|
typedef SET_ONLY( SELF##_key ) \
|
||||||
|
MAP_ONLY( struct SELF##_value ) \
|
||||||
|
SELF##_value, SELF##_entry; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref; \
|
||||||
|
size_t idx; \
|
||||||
|
bool inserted; \
|
||||||
|
uint8_t hashx; \
|
||||||
|
uint16_t dist; \
|
||||||
|
} SELF##_result; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref, *_end; \
|
||||||
|
struct hmap_meta *_mref; \
|
||||||
|
} SELF##_iter; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_value* table; \
|
||||||
|
struct hmap_meta* meta; \
|
||||||
|
ptrdiff_t size, bucket_count; \
|
||||||
|
AUXDEF \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define _declare_aatree(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \
|
||||||
|
typedef KEY SELF##_key; \
|
||||||
|
typedef VAL SELF##_mapped; \
|
||||||
|
typedef struct SELF##_node SELF##_node; \
|
||||||
|
\
|
||||||
|
typedef SET_ONLY( SELF##_key ) \
|
||||||
|
MAP_ONLY( struct SELF##_value ) \
|
||||||
|
SELF##_value, SELF##_entry; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref; \
|
||||||
|
bool inserted; \
|
||||||
|
} SELF##_result; \
|
||||||
|
\
|
||||||
|
typedef struct { \
|
||||||
|
SELF##_value *ref; \
|
||||||
|
SELF##_node *_d; \
|
||||||
|
int _top; \
|
||||||
|
int32_t _tn, _st[36]; \
|
||||||
|
} SELF##_iter; \
|
||||||
|
\
|
||||||
|
typedef struct SELF { \
|
||||||
|
SELF##_node *nodes; \
|
||||||
|
int32_t root, disp, head, size, capacity; \
|
||||||
|
AUXDEF \
|
||||||
|
} SELF
|
||||||
|
|
||||||
|
#define declare_stack_fixed(SELF, VAL, CAP) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
typedef struct { SELF##_value *ref, *end; } SELF##_iter; \
|
||||||
|
typedef struct SELF { SELF##_value data[CAP]; ptrdiff_t size; } SELF
|
||||||
|
|
||||||
|
#define _declare_stack(SELF, VAL, AUXDEF) \
|
||||||
|
typedef VAL SELF##_value; \
|
||||||
|
typedef struct { SELF##_value *ref, *end; } SELF##_iter; \
|
||||||
|
typedef struct SELF { SELF##_value *data; ptrdiff_t size, capacity; AUXDEF } SELF
|
||||||
|
|
||||||
|
#endif // STC_TYPES_H_INCLUDED
|
||||||
37
stc/utf8.h
Normal file
37
stc/utf8.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
|
||||||
|
#ifndef STC_UTF8_H_INCLUDED
|
||||||
|
#define STC_UTF8_H_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h" // IWYU pragma: keep
|
||||||
|
#include "types.h"
|
||||||
|
#include "priv/utf8_prv.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#endif // STC_UTF8_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined i_implement
|
||||||
|
#include "priv/utf8_prv.c"
|
||||||
|
#endif
|
||||||
|
#include "priv/linkage2.h"
|
||||||
392
stc/vec.h
Normal file
392
stc/vec.h
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define i_implement
|
||||||
|
#include <stc/cstr.h>
|
||||||
|
#include <stc/types.h>
|
||||||
|
|
||||||
|
declare_vec(vec_i32, int);
|
||||||
|
|
||||||
|
typedef struct MyStruct {
|
||||||
|
vec_i32 int_vec;
|
||||||
|
cstr name;
|
||||||
|
} MyStruct;
|
||||||
|
|
||||||
|
#define i_key float
|
||||||
|
#include <stc/vec.h>
|
||||||
|
|
||||||
|
#define i_keypro cstr // cstr is a "pro"-type
|
||||||
|
#include <stc/vec.h>
|
||||||
|
|
||||||
|
#define T vec_i32, int32_t, (c_declared)
|
||||||
|
#include <stc/vec.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
vec_i32 vec = {0};
|
||||||
|
vec_i32_push(&vec, 123);
|
||||||
|
vec_i32_drop(&vec);
|
||||||
|
|
||||||
|
vec_float fvec = {0};
|
||||||
|
vec_float_push(&fvec, 123.3);
|
||||||
|
vec_float_drop(&fvec);
|
||||||
|
|
||||||
|
vec_cstr svec = {0};
|
||||||
|
vec_cstr_emplace(&svec, "Hello, friend");
|
||||||
|
vec_cstr_drop(&svec);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#include "priv/linkage.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef STC_VEC_H_INCLUDED
|
||||||
|
#define STC_VEC_H_INCLUDED
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define _it2_ptr(it1, it2) (it1.ref && !it2.ref ? it1.end : it2.ref)
|
||||||
|
#define _it_ptr(it) (it.ref ? it.ref : it.end)
|
||||||
|
#endif // STC_VEC_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef _i_prefix
|
||||||
|
#define _i_prefix vec_
|
||||||
|
#endif
|
||||||
|
#include "priv/template.h"
|
||||||
|
|
||||||
|
#ifndef i_declared
|
||||||
|
_c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def);
|
||||||
|
#endif
|
||||||
|
typedef i_keyraw _m_raw;
|
||||||
|
STC_API void _c_MEMB(_drop)(const Self* cself);
|
||||||
|
STC_API void _c_MEMB(_clear)(Self* self);
|
||||||
|
STC_API bool _c_MEMB(_reserve)(Self* self, isize cap);
|
||||||
|
STC_API bool _c_MEMB(_resize)(Self* self, isize size, _m_value null);
|
||||||
|
STC_API _m_iter _c_MEMB(_erase_n)(Self* self, isize idx, isize n);
|
||||||
|
STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n);
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw raw);
|
||||||
|
#endif // _i_has_eq
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val)
|
||||||
|
{ (void)self; i_keydrop(val); }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_move)(Self *self) {
|
||||||
|
Self m = *self;
|
||||||
|
self->capacity = self->size = 0;
|
||||||
|
self->data = NULL;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) {
|
||||||
|
_c_MEMB(_drop)(self);
|
||||||
|
*self = unowned;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value) {
|
||||||
|
if (self->size == self->capacity)
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->size*2 + 4))
|
||||||
|
return NULL;
|
||||||
|
_m_value *v = self->data + self->size++;
|
||||||
|
*v = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n)
|
||||||
|
{ while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; }
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_API _m_iter _c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw raw[], isize n);
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_push)(self, i_keyfrom(raw)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw)
|
||||||
|
{ return _c_MEMB(_emplace_n)(self, _it_ptr(it) - self->data, &raw, 1); }
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_API void _c_MEMB(_copy)(Self* self, const Self* other);
|
||||||
|
STC_API _m_iter _c_MEMB(_copy_to)(Self* self, isize idx, const _m_value arr[], isize n);
|
||||||
|
STC_API Self _c_MEMB(_clone)(Self vec);
|
||||||
|
STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val)
|
||||||
|
{ (void)self; return i_keyclone(val); }
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; }
|
||||||
|
STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; }
|
||||||
|
STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return !self->size; }
|
||||||
|
STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { return i_keytoraw(val); }
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return self->data; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return self->data; }
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->data[self->size - 1]; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->data[self->size - 1]; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_pop)(Self* self)
|
||||||
|
{ c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); }
|
||||||
|
STC_INLINE _m_value _c_MEMB(_pull)(Self* self)
|
||||||
|
{ c_assert(self->size); return self->data[--self->size]; }
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_push_back)(Self* self, _m_value value)
|
||||||
|
{ return _c_MEMB(_push)(self, value); }
|
||||||
|
STC_INLINE void _c_MEMB(_pop_back)(Self* self) { _c_MEMB(_pop)(self); }
|
||||||
|
|
||||||
|
#ifndef _i_aux_alloc
|
||||||
|
STC_INLINE Self _c_MEMB(_init)(void)
|
||||||
|
{ return c_literal(Self){0}; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_capacity)(isize cap)
|
||||||
|
{ Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size)
|
||||||
|
{ Self out = {_i_new_n(_m_value, size), size, size}; return out; }
|
||||||
|
|
||||||
|
STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) {
|
||||||
|
Self out = {_i_new_n(_m_value, size), size, size};
|
||||||
|
while (size) out.data[--size] = i_keyfrom(default_raw);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) {
|
||||||
|
Self out = _c_MEMB(_with_capacity)(n);
|
||||||
|
_c_MEMB(_put_n)(&out, raw, n); return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self)
|
||||||
|
{ _c_MEMB(_reserve)(self, _c_MEMB(_size)(self)); }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter
|
||||||
|
_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value arr[], const isize n) {
|
||||||
|
_m_iter it = _c_MEMB(_insert_uninit)(self, idx, n);
|
||||||
|
if (it.ref)
|
||||||
|
c_memcpy(it.ref, arr, n*c_sizeof *arr);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value value) {
|
||||||
|
return _c_MEMB(_insert_n)(self, _it_ptr(it) - self->data, &value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it) {
|
||||||
|
return _c_MEMB(_erase_n)(self, it.ref - self->data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter i1, _m_iter i2) {
|
||||||
|
return _c_MEMB(_erase_n)(self, i1.ref - self->data, _it2_ptr(i1, i2) - i1.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, const isize idx) {
|
||||||
|
c_assert(c_uless(idx, self->size)); return self->data + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, const isize idx) {
|
||||||
|
c_assert(c_uless(idx, self->size)); return self->data + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iteration
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) {
|
||||||
|
_m_iter it = {(_m_value*)self->data, (_m_value*)self->data};
|
||||||
|
if (self->size) it.end += self->size;
|
||||||
|
else it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) {
|
||||||
|
_m_iter it = {(_m_value*)self->data, (_m_value*)self->data};
|
||||||
|
if (self->size) { it.ref += self->size - 1; it.end -= 1; }
|
||||||
|
else it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_end)(const Self* self)
|
||||||
|
{ (void)self; _m_iter it = {0}; return it; }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self)
|
||||||
|
{ (void)self; _m_iter it = {0}; return it; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_next)(_m_iter* it)
|
||||||
|
{ if (++it->ref == it->end) it->ref = NULL; }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_rnext)(_m_iter* it)
|
||||||
|
{ if (--it->ref == it->end) it->ref = NULL; }
|
||||||
|
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) {
|
||||||
|
if ((it.ref += n) >= it.end) it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it)
|
||||||
|
{ return (it.ref - self->data); }
|
||||||
|
|
||||||
|
STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n)
|
||||||
|
{ self->size += n; }
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw) {
|
||||||
|
return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) {
|
||||||
|
if (self->size != other->size) return false;
|
||||||
|
for (isize i = 0; i < self->size; ++i) {
|
||||||
|
const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i));
|
||||||
|
if (!(i_eq((&_rx), (&_ry)))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // _i_has_eq
|
||||||
|
|
||||||
|
#if defined _i_has_cmp
|
||||||
|
#include "priv/sort_prv.h"
|
||||||
|
#endif // _i_has_cmp
|
||||||
|
|
||||||
|
/* -------------------------- IMPLEMENTATION ------------------------- */
|
||||||
|
#if defined i_implement
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_clear)(Self* self) {
|
||||||
|
if (self->size == 0) return;
|
||||||
|
_m_value *p = self->data + self->size;
|
||||||
|
while (p-- != self->data) { i_keydrop(p); }
|
||||||
|
self->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_drop)(const Self* cself) {
|
||||||
|
Self* self = (Self*)cself;
|
||||||
|
if (self->capacity == 0)
|
||||||
|
return;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
_i_free_n(self->data, self->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_reserve)(Self* self, const isize cap) {
|
||||||
|
if (cap > self->capacity || (cap && cap == self->size)) {
|
||||||
|
_m_value* d = (_m_value*)_i_realloc_n(self->data, self->capacity, cap);
|
||||||
|
if (d == NULL)
|
||||||
|
return false;
|
||||||
|
self->data = d;
|
||||||
|
self->capacity = cap;
|
||||||
|
}
|
||||||
|
return self->data != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF bool
|
||||||
|
_c_MEMB(_resize)(Self* self, const isize len, _m_value null) {
|
||||||
|
if (!_c_MEMB(_reserve)(self, len))
|
||||||
|
return false;
|
||||||
|
const isize n = self->size;
|
||||||
|
for (isize i = len; i < n; ++i)
|
||||||
|
{ i_keydrop((self->data + i)); }
|
||||||
|
for (isize i = n; i < len; ++i)
|
||||||
|
self->data[i] = null;
|
||||||
|
self->size = len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) {
|
||||||
|
if (self->size + n >= self->capacity)
|
||||||
|
if (!_c_MEMB(_reserve)(self, self->size*3/2 + n))
|
||||||
|
return _c_MEMB(_end)(self);
|
||||||
|
|
||||||
|
_m_value *pos = self->data + idx;
|
||||||
|
c_memmove(pos + n, pos, (self->size - idx)*c_sizeof *pos);
|
||||||
|
self->size += n;
|
||||||
|
return c_literal(_m_iter){pos, self->data + self->size};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_erase_n)(Self* self, const isize idx, const isize len) {
|
||||||
|
c_assert(idx + len <= self->size);
|
||||||
|
_m_value* d = self->data + idx, *p = d, *end = self->data + self->size;
|
||||||
|
for (isize i = 0; i < len; ++i, ++p)
|
||||||
|
{ i_keydrop(p); }
|
||||||
|
memmove(d, p, (size_t)(end - p)*sizeof *d);
|
||||||
|
self->size -= len;
|
||||||
|
return c_literal(_m_iter){p == end ? NULL : d, end - len};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined i_no_clone
|
||||||
|
STC_DEF void
|
||||||
|
_c_MEMB(_copy)(Self* self, const Self* other) {
|
||||||
|
if (self == other) return;
|
||||||
|
_c_MEMB(_clear)(self);
|
||||||
|
_c_MEMB(_reserve)(self, other->size);
|
||||||
|
self->size = other->size;
|
||||||
|
for (c_range(i, other->size))
|
||||||
|
self->data[i] = i_keyclone((other->data[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF Self
|
||||||
|
_c_MEMB(_clone)(Self vec) {
|
||||||
|
Self out = vec, *self = &out; (void)self;
|
||||||
|
out.data = NULL; out.size = out.capacity = 0;
|
||||||
|
_c_MEMB(_reserve)(&out, vec.size);
|
||||||
|
out.size = vec.size;
|
||||||
|
for (c_range(i, vec.size))
|
||||||
|
out.data[i] = i_keyclone(vec.data[i]);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_copy_to)(Self* self, const isize idx,
|
||||||
|
const _m_value arr[], const isize n) {
|
||||||
|
_m_iter it = _c_MEMB(_insert_uninit)(self, idx, n);
|
||||||
|
if (it.ref)
|
||||||
|
for (_m_value* p = it.ref, *q = p + n; p != q; ++arr)
|
||||||
|
*p++ = i_keyclone((*arr));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
#endif // !i_no_clone
|
||||||
|
|
||||||
|
#if !defined i_no_emplace
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw raw[], isize n) {
|
||||||
|
_m_iter it = _c_MEMB(_insert_uninit)(self, idx, n);
|
||||||
|
if (it.ref)
|
||||||
|
for (_m_value* p = it.ref; n--; ++raw, ++p)
|
||||||
|
*p = i_keyfrom((*raw));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
#endif // !i_no_emplace
|
||||||
|
|
||||||
|
#if defined _i_has_eq
|
||||||
|
STC_DEF _m_iter
|
||||||
|
_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) {
|
||||||
|
(void)self;
|
||||||
|
const _m_value* p2 = _it2_ptr(i1, i2);
|
||||||
|
for (; i1.ref != p2; ++i1.ref) {
|
||||||
|
const _m_raw r = i_keytoraw(i1.ref);
|
||||||
|
if (i_eq((&raw), (&r)))
|
||||||
|
return i1;
|
||||||
|
}
|
||||||
|
i2.ref = NULL;
|
||||||
|
return i2;
|
||||||
|
}
|
||||||
|
#endif // _i_has_eq
|
||||||
|
#endif // i_implement
|
||||||
|
#include "sys/finalize.h"
|
||||||
173
stc/zsview.h
Normal file
173
stc/zsview.h
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Tyge Løvset
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// zsview is a zero-terminated string view.
|
||||||
|
|
||||||
|
#ifndef STC_ZSVIEW_H_INCLUDED
|
||||||
|
#define STC_ZSVIEW_H_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "priv/utf8_prv.h"
|
||||||
|
|
||||||
|
#define zsview_init() c_zv("")
|
||||||
|
#define zsview_clone(zs) c_default_clone(zs)
|
||||||
|
#define zsview_drop(self) c_default_drop(self)
|
||||||
|
#define zsview_toraw(self) (self)->str
|
||||||
|
|
||||||
|
STC_INLINE zsview zsview_from(const char* str)
|
||||||
|
{ return c_literal(zsview){str, c_strlen(str)}; }
|
||||||
|
STC_INLINE void zsview_clear(zsview* self) { *self = c_zv(""); }
|
||||||
|
STC_INLINE csview zsview_sv(zsview zs) { return c_sv_2(zs.str, zs.size); }
|
||||||
|
|
||||||
|
STC_INLINE isize zsview_size(zsview zs) { return zs.size; }
|
||||||
|
STC_INLINE bool zsview_is_empty(zsview zs) { return zs.size == 0; }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_equals(zsview zs, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return zs.size == n && !c_memcmp(zs.str, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize zsview_find(zsview zs, const char* search) {
|
||||||
|
const char* res = strstr(zs.str, search);
|
||||||
|
return res ? (res - zs.str) : c_NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_contains(zsview zs, const char* str)
|
||||||
|
{ return zsview_find(zs, str) != c_NPOS; }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_starts_with(zsview zs, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= zs.size && !c_memcmp(zs.str, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_ends_with(zsview zs, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= zs.size && !c_memcmp(zs.str + zs.size - n, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE zsview zsview_from_pos(zsview zs, isize pos) {
|
||||||
|
if (pos > zs.size) pos = zs.size;
|
||||||
|
zs.str += pos; zs.size -= pos; return zs;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview zsview_subview(zsview zs, isize pos, isize len) {
|
||||||
|
c_assert(((size_t)pos <= (size_t)zs.size) & (len >= 0));
|
||||||
|
if (pos + len > zs.size) len = zs.size - pos;
|
||||||
|
return c_literal(csview){zs.str + pos, len};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE zsview zsview_tail(zsview zs, isize len) {
|
||||||
|
c_assert(len >= 0);
|
||||||
|
if (len > zs.size) len = zs.size;
|
||||||
|
zs.str += zs.size - len; zs.size = len;
|
||||||
|
return zs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* utf8 */
|
||||||
|
|
||||||
|
STC_INLINE zsview zsview_u8_from_pos(zsview zs, isize u8pos)
|
||||||
|
{ return zsview_from_pos(zs, utf8_to_index(zs.str, u8pos)); }
|
||||||
|
|
||||||
|
STC_INLINE zsview zsview_u8_tail(zsview zs, isize u8len) {
|
||||||
|
const char* p = &zs.str[zs.size];
|
||||||
|
while (u8len && p != zs.str)
|
||||||
|
u8len -= (*--p & 0xC0) != 0x80;
|
||||||
|
zs.size -= p - zs.str, zs.str = p;
|
||||||
|
return zs;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE csview zsview_u8_subview(zsview zs, isize u8pos, isize u8len)
|
||||||
|
{ return utf8_subview(zs.str, u8pos, u8len); }
|
||||||
|
|
||||||
|
STC_INLINE zsview_iter zsview_u8_at(zsview zs, isize u8pos) {
|
||||||
|
csview sv;
|
||||||
|
sv.buf = utf8_at(zs.str, u8pos);
|
||||||
|
sv.size = utf8_chr_size(sv.buf);
|
||||||
|
return c_literal(zsview_iter){.chr = sv};
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE isize zsview_u8_size(zsview zs)
|
||||||
|
{ return utf8_count(zs.str); }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_u8_valid(zsview zs) // requires linking with utf8 symbols
|
||||||
|
{ return utf8_valid_n(zs.str, zs.size); }
|
||||||
|
|
||||||
|
/* utf8 iterator */
|
||||||
|
|
||||||
|
STC_INLINE zsview_iter zsview_begin(const zsview* self) {
|
||||||
|
zsview_iter it = {.chr = {self->str, utf8_chr_size(self->str)}}; return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE zsview_iter zsview_end(const zsview* self) {
|
||||||
|
(void)self; zsview_iter it = {0}; return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE void zsview_next(zsview_iter* it) {
|
||||||
|
it->ref += it->chr.size;
|
||||||
|
it->chr.size = utf8_chr_size(it->ref);
|
||||||
|
if (*it->ref == '\0') it->ref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STC_INLINE zsview_iter zsview_advance(zsview_iter it, isize u8pos) {
|
||||||
|
it.ref = utf8_offset(it.ref, u8pos);
|
||||||
|
it.chr.size = utf8_chr_size(it.ref);
|
||||||
|
if (*it.ref == '\0') it.ref = NULL;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Container helper functions ---- */
|
||||||
|
|
||||||
|
STC_INLINE size_t zsview_hash(const zsview *self)
|
||||||
|
{ return c_hash_str(self->str); }
|
||||||
|
|
||||||
|
STC_INLINE int zsview_cmp(const zsview* x, const zsview* y)
|
||||||
|
{ return strcmp(x->str, y->str); }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_eq(const zsview* x, const zsview* y)
|
||||||
|
{ return x->size == y->size && !c_memcmp(x->str, y->str, x->size); }
|
||||||
|
|
||||||
|
STC_INLINE int zsview_icmp(const zsview* x, const zsview* y)
|
||||||
|
{ return utf8_icmp(x->str, y->str); }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_ieq(const zsview* x, const zsview* y)
|
||||||
|
{ return x->size == y->size && !utf8_icmp(x->str, y->str); }
|
||||||
|
|
||||||
|
/* ---- case insensitive ---- */
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_iequals(zsview zs, const char* str)
|
||||||
|
{ return c_strlen(str) == zs.size && !utf8_icmp(zs.str, str); }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_istarts_with(zsview zs, const char* str)
|
||||||
|
{ return c_strlen(str) <= zs.size && !utf8_icmp(zs.str, str); }
|
||||||
|
|
||||||
|
STC_INLINE bool zsview_iends_with(zsview zs, const char* str) {
|
||||||
|
isize n = c_strlen(str);
|
||||||
|
return n <= zs.size && !utf8_icmp(zs.str + zs.size - n, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STC_ZSVIEW_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined i_import
|
||||||
|
#include "priv/utf8_prv.c"
|
||||||
|
#endif
|
||||||
31
str.h
Normal file
31
str.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<stdatomic.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
typedef struct LString {
|
||||||
|
bool ref;
|
||||||
|
size_t length;
|
||||||
|
uint8_t data[];
|
||||||
|
} LString;
|
||||||
|
|
||||||
|
static inline LString *lstring_new(size_t len, uint8_t *data) {
|
||||||
|
LString *lstr = calloc(1, sizeof(*lstr) + len);
|
||||||
|
lstr->length = len;
|
||||||
|
memcpy(lstr->data, data, len);
|
||||||
|
return lstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LString *lstring_newz(const char *data) {
|
||||||
|
return lstring_new(strlen(data), (uint8_t*) data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define lstring_free free
|
||||||
107
table.h
Normal file
107
table.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<stdatomic.h>
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
#include"value.h"
|
||||||
|
|
||||||
|
typedef struct LTableEntry {
|
||||||
|
LValue key;
|
||||||
|
LValue val;
|
||||||
|
} LTableEntry;
|
||||||
|
|
||||||
|
typedef struct LTableBuckets {
|
||||||
|
size_t capacity;
|
||||||
|
LTableEntry data[];
|
||||||
|
} LTableBuckets;
|
||||||
|
|
||||||
|
typedef struct LTable {
|
||||||
|
bool ref;
|
||||||
|
LTableBuckets *buckets;
|
||||||
|
} LTable;
|
||||||
|
|
||||||
|
static inline LTable *ltable_new(size_t capacity) {
|
||||||
|
assert(capacity >= 8 && (capacity & (capacity - 1)) == 0);
|
||||||
|
|
||||||
|
LTable *tbl = calloc(1, sizeof(*tbl));
|
||||||
|
tbl->ref = false;
|
||||||
|
tbl->buckets = calloc(1, sizeof(LTableBuckets) + sizeof(LTableEntry) * capacity);
|
||||||
|
tbl->buckets->capacity = capacity;
|
||||||
|
|
||||||
|
for(int i = 0; i < tbl->buckets->capacity; i++) {
|
||||||
|
tbl->buckets->data[i] = (LTableEntry) {
|
||||||
|
.key = {.u = LTAG_NIL},
|
||||||
|
.val = {.u = LTAG_NIL},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return tbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ltablebuckets_set(LTableBuckets *self, LValue key, LValue val) {
|
||||||
|
size_t idx = lvalue_hash(key);
|
||||||
|
|
||||||
|
int probe_limit = __builtin_ctz(self->capacity);
|
||||||
|
probe_limit += probe_limit >> 1;
|
||||||
|
|
||||||
|
LTableEntry *current = self->data;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
idx &= self->capacity - 1;
|
||||||
|
|
||||||
|
LValue prevKey = {.u = LTAG_NIL};
|
||||||
|
atomic_compare_exchange_strong(¤t[idx].key.u, &prevKey.u, key.u);
|
||||||
|
|
||||||
|
if(prevKey.u == LTAG_NIL || prevKey.u == key.u) {
|
||||||
|
atomic_store(¤t[idx].val.u, val.u);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
probe_limit--;
|
||||||
|
if(probe_limit == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ltable_set(LTable *self, LValue key, LValue val) {
|
||||||
|
if(!ltablebuckets_set(self->buckets, key, val)) {
|
||||||
|
assert(0 && "No table resizing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue ltablebuckets_get(LTableBuckets *self, LValue key) {
|
||||||
|
size_t idx = lvalue_hash(key);
|
||||||
|
|
||||||
|
size_t tries = self->capacity;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
idx &= self->capacity - 1;
|
||||||
|
|
||||||
|
LValue foundKey;
|
||||||
|
foundKey.u = atomic_load(&self->data[idx].key.u);
|
||||||
|
|
||||||
|
if(lvalue_eq(foundKey, key)) {
|
||||||
|
return (LValue) {.u = atomic_load(&self->data[idx].val.u)};
|
||||||
|
} else if(--tries == 0) {
|
||||||
|
return (LValue) {.u = LTAG_NIL};
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue ltable_get(LTable *self, LValue key) {
|
||||||
|
return ltablebuckets_get(self->buckets, key);
|
||||||
|
}
|
||||||
126
value.h
Normal file
126
value.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<math.h>
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
#include"str.h"
|
||||||
|
|
||||||
|
#define LTAG_MASK 0xFFFF000000000000ULL
|
||||||
|
#define LTAG_NAN 0xFFF8000000000001ULL
|
||||||
|
#define LTAG_NIL 0xFFF8000000000002ULL
|
||||||
|
#define LTAG_FALSE 0xFFF8000000000003ULL
|
||||||
|
#define LTAG_TRUE 0xFFF8000000000004ULL
|
||||||
|
#define LTAG_I32 0xFFF9000000000000ULL
|
||||||
|
#define LTAG_TABLE 0xFFFA000000000000ULL
|
||||||
|
#define LTAG_USERDATA 0xFFFB000000000000ULL
|
||||||
|
#define LTAG_FUNCTION 0xFFFC000000000000ULL
|
||||||
|
#define LTAG_FLOAT 0xFFFD000000000000ULL
|
||||||
|
#define LTAG_STRING 0xFFFE000000000000ULL
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LT_NIL,
|
||||||
|
LT_STRING,
|
||||||
|
LT_NUMBER,
|
||||||
|
LT_INTEGER,
|
||||||
|
LT_TABLE,
|
||||||
|
LT_USERDATA,
|
||||||
|
LT_BOOLEAN,
|
||||||
|
LT_FUNCTION,
|
||||||
|
} LType;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
double f;
|
||||||
|
uint64_t u;
|
||||||
|
} LValue;
|
||||||
|
|
||||||
|
static inline uint64_t lhash64(uint64_t val) {
|
||||||
|
val ^= (val >> 33);
|
||||||
|
val *= 0xff51afd7ed558ccdUL;
|
||||||
|
val ^= (val >> 33);
|
||||||
|
val *= 0xc4ceb9fe1a85ec53UL;
|
||||||
|
val ^= (val >> 33);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue lvalue_from_double(double f) {
|
||||||
|
LValue v;
|
||||||
|
|
||||||
|
if(isnan(f)) {
|
||||||
|
// Normalize
|
||||||
|
v.u = LTAG_NAN;
|
||||||
|
} else {
|
||||||
|
v.f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (LValue) {.f = f};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue lvalue_from_int32(int32_t i) {
|
||||||
|
return (LValue) {.u = LTAG_I32 | (uint32_t) i};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue lvalue_from_bool(bool b) {
|
||||||
|
return (LValue) {.u = b ? LTAG_TRUE : LTAG_FALSE};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue lvalue_from_nil() {
|
||||||
|
return (LValue) {.u = LTAG_NIL};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LTable;
|
||||||
|
static inline LValue lvalue_from_table(struct LTable *tbl) {
|
||||||
|
return (LValue) {.u = LTAG_TABLE | (uintptr_t) tbl};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LString;
|
||||||
|
static inline LValue lvalue_from_string(struct LString *str) {
|
||||||
|
return (LValue) {.u = LTAG_STRING | (uintptr_t) str};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LFunc;
|
||||||
|
static inline LValue lvalue_from_func(struct LFunc *f) {
|
||||||
|
return (LValue) {.u = LTAG_FUNCTION | (uintptr_t) f};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t lvalue_to_int32(LValue v) {
|
||||||
|
return v.u & ~LTAG_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t lvalue_tag(LValue v) {
|
||||||
|
return v.u & LTAG_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LValue lvalue_raw(uint64_t tag, uint64_t rawval) {
|
||||||
|
assert(rawval == (rawval & ~LTAG_MASK));
|
||||||
|
return (LValue) {.u = tag | rawval};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t lvalue_hash(LValue lv) {
|
||||||
|
if(lvalue_tag(lv) == LTAG_STRING) {
|
||||||
|
LString *str = (LString*) (lv.u & ~LTAG_MASK);
|
||||||
|
|
||||||
|
uint64_t h = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < (str->length & ~7); i += 8) {
|
||||||
|
h = lhash64(h ^ *(uint64_t*) &str->data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t last = 0;
|
||||||
|
memcpy(&last, str->data + (str->length & ~7), str->length - (str->length & ~7));
|
||||||
|
|
||||||
|
h = lhash64(h ^ last);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhash64(lv.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lvalue_eq(LValue, LValue);
|
||||||
326
vm.c
Normal file
326
vm.c
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
#define i_implement
|
||||||
|
#include"vm.h"
|
||||||
|
#undef i_implement
|
||||||
|
|
||||||
|
#include"str.h"
|
||||||
|
|
||||||
|
#include<math.h>
|
||||||
|
|
||||||
|
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) {
|
||||||
|
if(func->is_native) {
|
||||||
|
return func->native_func(L, func->ud, arg_count, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *dispatch_table[] = {
|
||||||
|
[L_GETGLOBAL] = &&do_getglobal,
|
||||||
|
[L_SETGLOBAL] = &&do_setglobal,
|
||||||
|
[L_SETINT16] = &&do_setint16,
|
||||||
|
[L_SETINT32] = &&do_setint32,
|
||||||
|
[L_SETFLOAT] = &&do_setfloat,
|
||||||
|
[L_SETSTR] = &&do_setstr,
|
||||||
|
[L_SETTABLE] = &&do_settable,
|
||||||
|
[L_SETBOOL] = &&do_setbool,
|
||||||
|
[L_SETNIL] = &&do_setnil,
|
||||||
|
[L_SETFUNC] = &&do_setfunc,
|
||||||
|
[L_ADD] = &&do_add,
|
||||||
|
[L_SUB] = &&do_sub,
|
||||||
|
[L_MUL] = &&do_mul,
|
||||||
|
[L_DIV] = &&do_div,
|
||||||
|
[L_MOD] = &&do_mod,
|
||||||
|
[L_RET] = &&do_ret,
|
||||||
|
[L_JNOTCOND] = &&do_jnotcond,
|
||||||
|
[L_MOVE] = &&do_move,
|
||||||
|
[L_CALL] = &&do_call,
|
||||||
|
[L_JUMP] = &&do_jump,
|
||||||
|
[L_ADVANCETEST] = &&do_advancetest,
|
||||||
|
[L_COND_EQ] = &&do_cond_eq,
|
||||||
|
[L_COND_NEQ] = &&do_cond_neq,
|
||||||
|
[L_SETFIELD] = &&do_setfield,
|
||||||
|
[L_GETFIELD] = &&do_getfield,
|
||||||
|
};
|
||||||
|
|
||||||
|
LUnit *unit = func->unit;
|
||||||
|
|
||||||
|
LInst *inst = func->lua_instrs;
|
||||||
|
#define DISPATCH() goto *dispatch_table[(++inst)->opcode]
|
||||||
|
|
||||||
|
inst--;
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_getglobal:;
|
||||||
|
{
|
||||||
|
uint8_t *area = unit->abyss + inst->bc;
|
||||||
|
size_t len = *(uint16_t*) area;
|
||||||
|
area += 2;
|
||||||
|
|
||||||
|
LString *str = lstring_new(len, area);
|
||||||
|
regs[inst->a] = ltable_get(func->env, lvalue_from_string(str));
|
||||||
|
lstring_free(str);
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setglobal:;
|
||||||
|
{
|
||||||
|
uint8_t *area = unit->abyss + inst->bc;
|
||||||
|
size_t len = *(uint16_t*) area;
|
||||||
|
area += 2;
|
||||||
|
|
||||||
|
LString *str = lstring_new(len, area);
|
||||||
|
ltable_set(func->env, lvalue_from_string(str), regs[inst->a]);
|
||||||
|
lvm_gc_add(L, lvalue_from_string(str));
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setint16:;
|
||||||
|
regs[inst->a] = lvalue_from_int32((int16_t) inst->bc);
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setint32:;
|
||||||
|
regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]);
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setfloat:;
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setstr:;
|
||||||
|
{
|
||||||
|
uint8_t *area = unit->abyss + inst->bc;
|
||||||
|
size_t len = *(uint16_t*) area;
|
||||||
|
area += 2;
|
||||||
|
|
||||||
|
regs[inst->a] = lvalue_raw(LTAG_STRING, (uintptr_t) lstring_new(len, area));
|
||||||
|
lvm_gc_add(L, regs[inst->a]);
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_settable:;
|
||||||
|
{
|
||||||
|
LTable *tbl = ltable_new(inst->bc);
|
||||||
|
lvm_gc_add(L, lvalue_from_table(tbl));
|
||||||
|
regs[inst->a] = lvalue_from_table(tbl);
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setbool:;
|
||||||
|
regs[inst->a] = lvalue_from_bool(inst->b);
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setnil:;
|
||||||
|
regs[inst->a] = lvalue_from_nil();
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setfunc:;
|
||||||
|
regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]);
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_add:;
|
||||||
|
{
|
||||||
|
LValue x = regs[inst->b];
|
||||||
|
LValue y = regs[inst->c];
|
||||||
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||||
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f);
|
||||||
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y));
|
||||||
|
} else goto err;
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_sub:;
|
||||||
|
{
|
||||||
|
LValue x = regs[inst->b];
|
||||||
|
LValue y = regs[inst->c];
|
||||||
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||||
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f);
|
||||||
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y));
|
||||||
|
} else goto err;
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_mul:;
|
||||||
|
{
|
||||||
|
LValue x = regs[inst->b];
|
||||||
|
LValue y = regs[inst->c];
|
||||||
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||||
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f);
|
||||||
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y));
|
||||||
|
} else goto err;
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_div:;
|
||||||
|
{
|
||||||
|
LValue x = regs[inst->b];
|
||||||
|
LValue y = regs[inst->c];
|
||||||
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||||
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f);
|
||||||
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
int32_t yv = lvalue_to_int32(y);
|
||||||
|
if(yv == 0) {
|
||||||
|
regs[inst->a] = lvalue_from_nil();
|
||||||
|
} else {
|
||||||
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv);
|
||||||
|
}
|
||||||
|
} else goto err;
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_mod:;
|
||||||
|
{
|
||||||
|
LValue x = regs[inst->b];
|
||||||
|
LValue y = regs[inst->c];
|
||||||
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||||
|
regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
int32_t yv = lvalue_to_int32(y);
|
||||||
|
regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv));
|
||||||
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||||
|
int32_t yv = lvalue_to_int32(y);
|
||||||
|
if(yv == 0) {
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv);
|
||||||
|
}
|
||||||
|
} else goto err;
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_jump:;
|
||||||
|
inst += (int16_t) inst->bc;
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_jnotcond:;
|
||||||
|
{
|
||||||
|
LValue v = regs[inst->a];
|
||||||
|
if(v.u == LTAG_NIL || v.u == LTAG_FALSE) {
|
||||||
|
inst += (int16_t) inst->bc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_call:;
|
||||||
|
{
|
||||||
|
if(lvalue_tag(regs[inst->a]) != LTAG_FUNCTION) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *abyss_data = unit->abyss + inst->bc;
|
||||||
|
|
||||||
|
uint8_t ret_vreg = abyss_data[0];
|
||||||
|
uint8_t arg_count = abyss_data[1];
|
||||||
|
uint8_t *args = &abyss_data[2];
|
||||||
|
|
||||||
|
LValue regs2[256];
|
||||||
|
lvm_reset_regs(regs2);
|
||||||
|
for(int i = 0; i < arg_count; i++) {
|
||||||
|
regs2[i] = regs[args[i]];
|
||||||
|
}
|
||||||
|
size_t returned_count = lvm_run(L, (LFunc*) (regs[inst->a].u & ~LTAG_MASK), arg_count, regs2);
|
||||||
|
|
||||||
|
if(returned_count) {
|
||||||
|
// TODO: more than 1 return
|
||||||
|
regs[ret_vreg] = regs2[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_move:;
|
||||||
|
regs[inst->a] = regs[inst->b];
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_advancetest:;
|
||||||
|
{
|
||||||
|
int64_t a = lvalue_to_int32(regs[inst->a]);
|
||||||
|
int64_t b = lvalue_to_int32(regs[inst->b]);
|
||||||
|
int64_t c = lvalue_to_int32(regs[inst->c]);
|
||||||
|
if(!((c >= 0 && a > b) || (c < 0 && a < b))) {
|
||||||
|
inst++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_cond_eq:;
|
||||||
|
regs[inst->a] = lvalue_from_bool(lvalue_eq(regs[inst->b], regs[inst->c]));
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_cond_neq:;
|
||||||
|
regs[inst->a] = lvalue_from_bool(!lvalue_eq(regs[inst->b], regs[inst->c]));
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_setfield:;
|
||||||
|
{
|
||||||
|
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lvalue_tag(regs[inst->b]) == LTAG_NIL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTable *tbl = (void*) (regs[inst->a].u & ~LTAG_MASK);
|
||||||
|
|
||||||
|
ltable_set(tbl, regs[inst->b], regs[inst->c]);
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
do_getfield:;
|
||||||
|
{
|
||||||
|
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTable *tbl = (void*) (regs[inst->b].u & ~LTAG_MASK);
|
||||||
|
|
||||||
|
regs[inst->a] = ltable_get(tbl, regs[inst->c]);
|
||||||
|
}
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
|
err:;
|
||||||
|
do_ret:;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvm_gc_add(LVM *L, LValue lvalue) {
|
||||||
|
set_LValueU_insert(&L->gc_objects, lvalue.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
LFunc *lvm_func_from_native(LFuncCallback cb, void *ud) {
|
||||||
|
LFunc *f = calloc(1, sizeof(*f));
|
||||||
|
f->is_native = true;
|
||||||
|
f->ud = ud;
|
||||||
|
f->native_func = cb;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lvalue_eq(LValue a, LValue b) {
|
||||||
|
if(a.u == b.u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lvalue_tag(a) == LTAG_I32 && lvalue_tag(b) == LTAG_FLOAT) {
|
||||||
|
return (a.u & ~LTAG_MASK) == b.f;
|
||||||
|
} else if(lvalue_tag(a) == LTAG_FLOAT && lvalue_tag(b) == LTAG_I32) {
|
||||||
|
return (b.u & ~LTAG_MASK) == a.f;
|
||||||
|
} else if(lvalue_tag(a) == LTAG_STRING && lvalue_tag(b) == LTAG_STRING) {
|
||||||
|
LString *sa = (LString*) (a.u & ~LTAG_MASK);
|
||||||
|
LString *sb = (LString*) (b.u & ~LTAG_MASK);
|
||||||
|
|
||||||
|
if(sa->length != sb->length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !memcmp(sa->data, sb->data, sa->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
117
vm.h
Normal file
117
vm.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<ctype.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
#include"table.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
L_GETGLOBAL,
|
||||||
|
L_SETGLOBAL,
|
||||||
|
L_SETINT16,
|
||||||
|
L_SETFLOAT,
|
||||||
|
L_SETSTR,
|
||||||
|
L_SETTABLE,
|
||||||
|
L_SETBOOL,
|
||||||
|
L_SETNIL,
|
||||||
|
L_SETFUNC,
|
||||||
|
L_ADD,
|
||||||
|
L_SUB,
|
||||||
|
L_MUL,
|
||||||
|
L_DIV,
|
||||||
|
L_IDIV,
|
||||||
|
L_MOD,
|
||||||
|
L_POW,
|
||||||
|
L_BOR,
|
||||||
|
L_BAND,
|
||||||
|
L_BXOR,
|
||||||
|
L_COND_EQ,
|
||||||
|
L_COND_NEQ,
|
||||||
|
L_JUMP,
|
||||||
|
L_JNOTCOND,
|
||||||
|
L_RET,
|
||||||
|
L_MOVE,
|
||||||
|
L_CALL,
|
||||||
|
L_ADVANCETEST,
|
||||||
|
L_SETFIELD,
|
||||||
|
L_GETFIELD,
|
||||||
|
L_SETINT32,
|
||||||
|
} LOp;
|
||||||
|
|
||||||
|
typedef union __attribute__((packed)) {
|
||||||
|
struct {
|
||||||
|
uint8_t opcode;
|
||||||
|
union {
|
||||||
|
uint8_t argb[3];
|
||||||
|
struct __attribute__((packed)) {
|
||||||
|
uint8_t a;
|
||||||
|
union {
|
||||||
|
uint16_t bc;
|
||||||
|
struct __attribute__((packed)) {
|
||||||
|
uint8_t b;
|
||||||
|
uint8_t c;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
uint32_t hahalol;
|
||||||
|
} extension;
|
||||||
|
} LInst;
|
||||||
|
|
||||||
|
struct LUnit;
|
||||||
|
struct LVM;
|
||||||
|
|
||||||
|
typedef size_t(*LFuncCallback)(struct LVM*, void *ud, size_t argn, LValue *args);
|
||||||
|
typedef struct LFunc {
|
||||||
|
struct LUnit *unit;
|
||||||
|
bool is_native;
|
||||||
|
uint8_t upvalue_count;
|
||||||
|
LValue *upvalues;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
LFuncCallback native_func;
|
||||||
|
void *ud;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
LInst *lua_instrs;
|
||||||
|
LTable *env;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} LFunc;
|
||||||
|
|
||||||
|
typedef struct LUnit {
|
||||||
|
uint8_t *abyss;
|
||||||
|
|
||||||
|
size_t func_count;
|
||||||
|
LFunc *funcs;
|
||||||
|
} LUnit;
|
||||||
|
|
||||||
|
#define i_header
|
||||||
|
#define T set_LValueU, uint64_t
|
||||||
|
#include"stc/hashset.h"
|
||||||
|
#undef i_header
|
||||||
|
|
||||||
|
typedef struct LVM {
|
||||||
|
size_t unit_count;
|
||||||
|
LUnit *units;
|
||||||
|
|
||||||
|
set_LValueU gc_objects;
|
||||||
|
} LVM;
|
||||||
|
|
||||||
|
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs);
|
||||||
|
void lvm_gc_add(LVM *L, LValue lvalue);
|
||||||
|
|
||||||
|
LFunc *lvm_func_from_native(LFuncCallback, void *ud);
|
||||||
|
|
||||||
|
static inline void lvm_reset_regs(LValue *regs) {
|
||||||
|
for(int i = 0; i < 256; i++) {
|
||||||
|
regs[i] = lvalue_from_nil();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user