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