├── .gitignore ├── lib ├── kdefault │ └── kdefault.c ├── kbool │ └── kbool.h ├── kbg │ ├── kbg.h │ └── kbg.c ├── kout │ ├── kout.c │ └── kout.h ├── kerr │ ├── kerr.c │ └── kerr.h └── kstr │ ├── kstr.h │ └── kstr.c ├── Makefile ├── README.md └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | ./temp 2 | -------------------------------------------------------------------------------- /lib/kdefault/kdefault.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/kbool/kbool.h: -------------------------------------------------------------------------------- 1 | 2 | /// kbool.h - represent true and false values 3 | #ifndef KBOOL_H 4 | #define KBOOL_H 5 | 6 | typedef enum { 7 | KFALSE = 0, 8 | KTRUE = 1, 9 | } kbool; 10 | 11 | #endif -------------------------------------------------------------------------------- /lib/kbg/kbg.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /// kbg.h - utilities for debugging values quickly 4 | #ifndef KBG_H 5 | #define KBG_H 6 | 7 | /// print a byte as a hexidecimal number 8 | void kbg_hex(unsigned char c); 9 | 10 | /// print a byte as a binary number 11 | void kbg_bin(unsigned char c); 12 | 13 | /// print a raw byte 14 | void kbg_char(char c); 15 | 16 | #endif -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SAFETY_FLAGS = -Wall -Wextra -Wpedantic -Werror -g -O0 3 | INCLUDE = -I./lib/kdefault -I./lib/kout -I./lib/kbg -I./lib/kbool -I./lib/kstr -I./lib/kerr 4 | ENTRYPOINT = main.c 5 | BINOUT = ./temp/main 6 | 7 | build-min: 8 | gcc $(INCLUDE) main.c -o ./temp/main; ./temp/main; 9 | 10 | 11 | build: 12 | gcc $(SAFETY_FLAGS) $(INCLUDE) $(ENTRYPOINT) -o $(BINOUT); ./temp/main; 13 | 14 | run: 15 | ./temp/main; -------------------------------------------------------------------------------- /lib/kbg/kbg.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | void kbg_hex(unsigned char c) { 4 | printf("HEX: 0x%02X\n", c); 5 | } 6 | 7 | void kbg_bin(unsigned char c) { 8 | printf("BIN: "); 9 | for (int i = 7; i >= 0; i--) { 10 | printf("%d", (c >> i) & 1); 11 | } 12 | printf("\n"); 13 | } 14 | 15 | void kbg_char(char c) { 16 | if (c == 0x00) { 17 | printf("CHAR: NULL TERMINATOR\n"); 18 | return; 19 | } 20 | printf("CHAR: %c\n", c); 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koto 2 | A standard library for modern c developers. 3 | 4 | ## Inspiration 5 | I started with Javascript, then went to Python, Go, Rust. Finally, I hit C. But where is my standard library? I want a server, where is it? I want a string with a stored length? A memory allocator? 6 | 7 | Introducting *koto*.. 8 | 9 | ## kstr 10 | A utf-8, cursor-based, string type. 11 | 12 | ## kbg 13 | Print out values on the fly. 14 | 15 | ## kbool 16 | A std-provided boolean type with the `KTRUE` and `KFALSE` variants. -------------------------------------------------------------------------------- /lib/kout/kout.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "kbool.h" 5 | #include "kout.h" 6 | 7 | 8 | kout kout_ok(void *data) { 9 | kout out; 10 | out.data = data; 11 | out.err = NULL; 12 | return out; 13 | } 14 | 15 | kbool kout_is_err(kout out) { 16 | if (out.err) { 17 | return KTRUE; 18 | } 19 | return KFALSE; 20 | } 21 | 22 | kout kout_fail(void *err) { 23 | kout out; 24 | out.data = NULL; 25 | out.err = err; 26 | return out; 27 | } 28 | 29 | void *kout_get_err(kout out) { 30 | if (kout_is_err(out) == KFALSE) { 31 | KERR_PANIC("KOUT: attempted to unwrap an error when no error exists"); 32 | } 33 | return out.err; 34 | } 35 | 36 | void *kout_get_data(kout out) { 37 | if (kout_is_err(out)) { 38 | KERR_PANIC("KOUT: attempted to unwrap data when no data exists"); 39 | } 40 | return out.data; 41 | } 42 | 43 | #define KOUT_OK(void_data) kout_ok((void *)void_data); 44 | #define KOUT_FAIL(void_err) kout_fail((void *)void_err); 45 | 46 | -------------------------------------------------------------------------------- /lib/kout/kout.h: -------------------------------------------------------------------------------- 1 | 2 | /// kbg.h - utilities for debugging values quickly 3 | #ifndef KOUT_H 4 | #define KOUT_H 5 | 6 | #include 7 | 8 | #include "kbool.h" 9 | 10 | /// represents the outcome of an operation and may contain an error or data 11 | typedef struct { 12 | void *err; 13 | void *data; 14 | } kout; 15 | 16 | /// generate a stack-allocted kout with a pointer to void *data 17 | kout kout_ok(void *data); 18 | 19 | /// check if an instance of kout contains an error 20 | kbool kout_is_err(kout out); 21 | 22 | /// generate a stack-allocated kout with a pointer to void *err 23 | kout kout_fail(void *err); 24 | 25 | /// extract the err from an instance of kout 26 | void *kout_get_err(kout out); 27 | 28 | /// extract the data from an instance of kout 29 | void *kout_get_data(kout out); 30 | 31 | /// auto-convert the data passed into kout_ok() into a void* 32 | #define KOUT_OK(void_data) kout_ok((void *)void_data); 33 | 34 | /// auto-convert the data passed into kout_fail() into a void* 35 | #define KOUT_FAIL(void_err) kout_fail((void *)void_err); 36 | 37 | #endif -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "kstr.h" 6 | #include "kbg.h" 7 | #include "kbool.h" 8 | #include "kerr.h" 9 | #include "kout.h" 10 | 11 | 12 | void test_kerr(void) { 13 | 14 | // testing basic comparison 15 | kerr err = kerr_onstack("some err", __FILE__, __LINE__); 16 | if (strcmp(err.message, "some err\0") != 0) { 17 | KERR_PANIC_TEST_FAILURE(); 18 | } 19 | 20 | // testing macro generation 21 | kerr err_again = KERR_ONSTACK("another err"); 22 | if (strcmp(err_again.message, "another err\0") != 0) { 23 | KERR_PANIC_TEST_FAILURE(); 24 | } 25 | 26 | // testing kout in error state 27 | kout out = KOUT_FAIL(KERR_ONHEAP("a test error")); 28 | if (kout_is_err(out) == KFALSE) { 29 | KERR_PANIC_TEST_FAILURE(); 30 | } 31 | kerr *out_err = kout_get_err(out); 32 | if (strcmp(out_err->message, "a test error\0") != 0) { 33 | KERR_PANIC_TEST_FAILURE(); 34 | } 35 | kerr_free(out_err); 36 | 37 | // testing kout in data state 38 | kout another_out = KOUT_OK(KERR_ONHEAP("fake err")); 39 | kerr *fake_err = kout_get_data(another_out); 40 | if (strcmp(fake_err->message, "fake err\0") != 0) { 41 | KERR_PANIC_TEST_FAILURE(); 42 | } 43 | kerr_free(fake_err); 44 | 45 | } 46 | 47 | 48 | 49 | 50 | 51 | int main() { 52 | test_kerr(); 53 | // test_kstr(); 54 | return EXIT_SUCCESS; 55 | } 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /lib/kerr/kerr.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "kbool.h" 5 | 6 | typedef struct { 7 | size_t line; 8 | char *file; 9 | char *message; 10 | } kerr; 11 | 12 | kerr kerr_onstack(char *message, char* file, size_t line) { 13 | kerr err; 14 | err.message = message; 15 | err.file = file; 16 | err.line = line; 17 | return err; 18 | } 19 | 20 | kerr *kerr_onheap(char *message, char* file, size_t line) { 21 | kerr *err = malloc(sizeof(kerr)); 22 | err->file = strdup(file); 23 | err->line = line; 24 | err->message = strdup(message); 25 | return err; 26 | } 27 | 28 | void kerr_free(kerr *err) { 29 | if (err) { 30 | if (err->message) { 31 | free(err->message); 32 | } 33 | if (err->file) { 34 | free(err->file); 35 | } 36 | free(err); 37 | } 38 | } 39 | 40 | kbool kerr_is_empty(kerr err) { 41 | return err.message && strlen(err.message) > 0 ? KTRUE : KFALSE; 42 | } 43 | 44 | void kerr_dbg(kerr err) { 45 | printf("🚨 (%s %ld) %s\n", err.file, err.line, err.message); 46 | } 47 | 48 | #define KERR_ONSTACK(message) kerr_onstack(message, __FILE__, __LINE__) 49 | #define KERR_ONHEAP(message) kerr_onheap(message, __FILE__, __LINE__) 50 | 51 | #define KERR_PANIC(message) \ 52 | do { \ 53 | kerr kerr_panic_queen = KERR_ONSTACK(message); \ 54 | kerr_dbg(kerr_panic_queen); \ 55 | printf("💣 PANICKING! SHUTTING DOWN..\n"); \ 56 | exit(EXIT_FAILURE); \ 57 | } while(0) 58 | 59 | #define KERR_PANIC_TEST_FAILURE() KERR_PANIC("TEST FAILURE") 60 | 61 | -------------------------------------------------------------------------------- /lib/kerr/kerr.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /// kerr.h - an error type with file and line number tracing built in 4 | #ifndef KERR_H 5 | #define KERR_H 6 | 7 | #include 8 | 9 | #include "kbool.h" 10 | #include "kstr.h" 11 | 12 | /// kerr enables us to quickly track down where errors in our application have occurred 13 | /// can be heap or stack allocated 14 | /// quality of life macros: KERR_ONSTACK, KERR_ONHEAP, KERR_PANIC, ect 15 | typedef struct { 16 | size_t line; 17 | char *file; 18 | char *message; 19 | } kerr; 20 | 21 | /// allocated a new instance of kerr on the stack 22 | kerr kerr_onstack(char *message, char* file, size_t line); 23 | 24 | /// allocated a new instance of kerr on the heap 25 | kerr *kerr_onheap(char *message, char* file, size_t line); 26 | 27 | /// free a heap allocated kerr 28 | void kerr_free(kerr *err); 29 | 30 | /// returns KTRUE if the error is empty 31 | kbool kerr_is_empty(kerr err); 32 | 33 | /// returns a kerr represented as a kstr 34 | kstr kerr_str(kerr err); 35 | 36 | /// print out a kerr in the terminal 37 | void kerr_dbg(kerr err); 38 | 39 | /// a macro to auto-insert the __FILE__ and __LINE__ with kerr_onstack() 40 | #define KERR_ONSTACK(message) kerr_onstack(message, __FILE__, __LINE__) 41 | 42 | /// a macro to auto-insert the __FILE__ and __LINE__ with kerr_onheap() 43 | #define KERR_ONHEAP(message) kerr_onheap(message, __FILE__, __LINE__) 44 | 45 | /// a macro for generating a kerr, printing it, and then exiting the program 46 | #define KERR_PANIC(message) \ 47 | do { \ 48 | kerr kerr_panic_queen = KERR_ONSTACK(message); \ 49 | kerr_dbg(kerr_panic_queen); \ 50 | printf("💣 PANICKING! SHUTTING DOWN..\n"); \ 51 | exit(EXIT_FAILURE); \ 52 | } while(0) 53 | 54 | /// a macro to quickly shutdown on test failure 55 | #define KERR_PANIC_TEST_FAILURE() KERR_PANIC("TEST FAILURE") 56 | 57 | #endif -------------------------------------------------------------------------------- /lib/kstr/kstr.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ifndef KSTR_H 5 | #define KSTR_H 6 | 7 | #include 8 | #include "kbool.h" 9 | 10 | /// count the number of utf-8 characters in a series of bytes 11 | size_t kstr_utf8_char_length(unsigned char byte); 12 | 13 | /// a utf-8 string type with cursor-based traversing 14 | struct kstr { 15 | size_t byte_count; 16 | size_t char_count; 17 | size_t position; 18 | char *string; 19 | }; 20 | 21 | /// quality of life typedef 22 | typedef struct kstr kstr; 23 | 24 | /// get the cursor position from the provided kstr 25 | size_t kstr_pos(kstr *s); 26 | 27 | /// returns KTRUE if the kstr position is equal to 0 28 | kbool kstr_at_start(kstr *s); 29 | 30 | /// return KTRUE if the kstr position is at the end of our bytes 31 | kbool kstr_at_end(kstr *s); 32 | 33 | /// moves our kstr position to the end of our bytes 34 | void kstr_goto_end(kstr *s); 35 | 36 | /// moves our kstr position to the start of our bytes 37 | void kstr_goto_start(kstr *s); 38 | 39 | /// returns KTRUE if the kstr position is out of bounds 40 | kbool kstr_out_of_bounds(kstr *s); 41 | 42 | /// get the last utf-8 character from the kstr 43 | unsigned char kstr_last_char(kstr *s); 44 | 45 | /// moves our kstr position to a specific position 46 | void kstr_goto_pos(kstr *s, size_t pos); 47 | 48 | /// count the number of utf-8 characters in a kstr 49 | size_t kstr_char_count(kstr *s); 50 | 51 | /// move the kstr position to the start of the next utf-8 char 52 | void kstr_next(kstr *s); 53 | 54 | /// create a heap-allocated kstr 55 | kstr *kstr_onheap(char *str); 56 | 57 | /// create a stack-allocated kstr 58 | kstr kstr_onstack(char *str); 59 | 60 | /// move the kstr position forward by a certain number of utf-8 chars 61 | void kstr_next_by(kstr *s, size_t by); 62 | 63 | /// move the kstr position backwards by one utf-8 char 64 | void kstr_prev(kstr *s); 65 | 66 | /// move the kstr position backwards by a certain number of utf-8 chars 67 | void kstr_prev_by(kstr *s, size_t by); 68 | 69 | /// extract bytes from a certain character range 70 | kstr *kstr_bytes_from_rng(kstr *s, size_t start, size_t end); 71 | 72 | /// extract bytes from the start the the kstr until the current position 73 | kstr *kstr_bytes_from_start(kstr *s); 74 | 75 | /// extract bytes from the current position to the end of the kstr 76 | kstr *kstr_bytes_from_end(kstr *s); 77 | 78 | /// print the kstr to the terminal 79 | void kstr_dbg(kstr *s); 80 | 81 | /// free a heap-allocated kstr 82 | void kstr_free(kstr *s); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /lib/kstr/kstr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kbool.h" 4 | #include "kstr.h" 5 | 6 | size_t kstr_utf8_char_length(unsigned char byte) { 7 | if ((byte & 0x80) == 0x00) { 8 | return 1; 9 | } else if ((byte & 0xE0) == 0xC0) { 10 | return 2; 11 | } else if ((byte & 0xF0) == 0xE0) { 12 | return 3; 13 | } else if ((byte & 0xF8) == 0xF0) { 14 | return 4; 15 | } else { 16 | return 0; 17 | } 18 | } 19 | 20 | size_t kstr_pos(kstr *s) { 21 | return s->position; 22 | } 23 | 24 | kbool kstr_at_start(kstr *s) { 25 | if (s->position == 0) { 26 | return KTRUE; 27 | } 28 | return KFALSE; 29 | } 30 | 31 | kbool kstr_at_end(kstr *s) { 32 | if (s->position >= s->byte_count) { 33 | return KTRUE; 34 | } 35 | return KFALSE; 36 | } 37 | 38 | void kstr_goto_end(kstr *s) { 39 | while (kstr_at_end(s) == KFALSE) { 40 | kstr_next(s); 41 | } 42 | } 43 | 44 | 45 | void kstr_goto_start(kstr *s) { 46 | s->position = 0; 47 | } 48 | 49 | kbool kstr_out_of_bounds(kstr *s) { 50 | if (s->position > s->byte_count-1) { 51 | return KTRUE; 52 | } 53 | return KFALSE; 54 | } 55 | 56 | unsigned char kstr_last_char(kstr *s) { 57 | if (s->byte_count == 0) { 58 | return s->string[0]; 59 | } 60 | return s->string[s->byte_count-1]; 61 | } 62 | 63 | void kstr_goto_pos(kstr *s, size_t pos) { 64 | if (pos > s->byte_count) { 65 | kstr_goto_end(s); 66 | return; 67 | } 68 | s->position = pos; 69 | } 70 | 71 | size_t kstr_char_count(kstr *s) { 72 | size_t start = kstr_pos(s); 73 | kstr_goto_start(s); 74 | size_t count = 0; 75 | while (kstr_at_end(s) == KFALSE) { 76 | kstr_next(s); 77 | count += 1; 78 | } 79 | kstr_goto_pos(s, start); 80 | return count; 81 | } 82 | 83 | 84 | void kstr_next(kstr *s) { 85 | size_t char_len = kstr_utf8_char_length(kstr_char(s)); 86 | s->position = s->position + char_len; 87 | if (kstr_out_of_bounds(s) == KTRUE) { 88 | kstr_goto_end(s); 89 | } 90 | } 91 | 92 | kstr *kstr_onheap(char *str) { 93 | size_t str_len = strlen(str); 94 | kstr *s = malloc(sizeof(kstr)); 95 | if (!s) { 96 | return NULL; 97 | } 98 | s->byte_count = str_len+1; 99 | s->string = strdup(str); 100 | s->position = 0; 101 | if (kstr_last_char(s) != 0x00) { 102 | printf("KSTR: attempted to create a heap-allocated kstr, but the input is not null-terminated\n"); 103 | return NULL; 104 | } 105 | s->char_count = kstr_char_count(s); 106 | return s; 107 | } 108 | 109 | kstr kstr_onstack(char *str) { 110 | size_t str_len = strlen(str); 111 | kstr s; 112 | s.byte_count = str_len+1; 113 | s.string = strdup(str); 114 | s.position = 0; 115 | if (kstr_last_char(&s) != 0x00) { 116 | printf("KSTR: attempted to create a stack-allocated kstr, but the input is not null-terminated\n"); 117 | printf("returning an empty kstr\n"); 118 | kstr out; 119 | out.byte_count = 1; 120 | out.char_count = 1; 121 | out.position = 0; 122 | out.string = '\0'; 123 | return out; 124 | } 125 | s.char_count = kstr_char_count(&s); 126 | return s; 127 | } 128 | 129 | 130 | void kstr_next_by(kstr *s, size_t by) { 131 | size_t count = 0; 132 | while (count != by) { 133 | kstr_next(s); 134 | count += 1; 135 | } 136 | } 137 | 138 | void kstr_prev(kstr *s) { 139 | while ((s->string[s->position] & 0xC0) == 0x80) { 140 | if (s->position == 0) break; 141 | s->position--; 142 | } 143 | 144 | if (s->position == 0) { 145 | return; 146 | } 147 | s->position = s->position - 1; 148 | while (kstr_utf8_char_length(kstr_char(s)) == 0) { 149 | s->position = s->position - 1; 150 | } 151 | } 152 | 153 | void kstr_prev_by(kstr *s, size_t by) { 154 | size_t count = 0; 155 | while (count != by) { 156 | kstr_prev(s); 157 | count += 1; 158 | } 159 | } 160 | 161 | char *kstr_bytes_from_rng(kstr *s, size_t start, size_t end) { 162 | if (end > s->byte_count-1) { 163 | end = s->byte_count-1; 164 | } 165 | if (start > end) { 166 | start = end; 167 | } 168 | size_t len = end - start+1; 169 | char *buffer = malloc(len+1); 170 | if (!buffer) { 171 | return NULL; 172 | } 173 | memcpy(buffer, &s->string[start], len); 174 | buffer[len] = '\0'; 175 | return buffer; 176 | } 177 | 178 | 179 | char *kstr_bytes_from_start(kstr *s) { 180 | if (s->position == 0) { 181 | char *buffer = malloc(1); 182 | buffer[0] = '\0'; 183 | kstr *out = kstr_new(buffer); 184 | return out; 185 | } 186 | size_t len = s->position + 1; 187 | char *buffer = malloc(len+1); 188 | memcpy(buffer, &s->string[0], len); 189 | buffer[len] = '\0'; 190 | return buffer; 191 | } 192 | 193 | kstr *kstr_bytes_from_end(kstr *s) { 194 | if (kstr_at_end(s)) { 195 | char *buffer = malloc(1); 196 | buffer[0] = '\0'; 197 | kstr *out = kstr_new(buffer); 198 | return out; 199 | } 200 | size_t byte_count = s->byte_count; 201 | char *buffer = malloc(byte_count+1); 202 | memcpy(buffer, &s->string[s->position], byte_count); 203 | buffer[byte_count] = '\0'; 204 | return buffer; 205 | } 206 | 207 | void kstr_dbg(kstr *s) { 208 | printf("DEBUGGING: kstr\n"); 209 | printf("BYTE COUNT: %ld\n", s->byte_count); 210 | printf("CHAR COUNT: %ld\n", s->char_count); 211 | printf("POSITION: %ld\n", s->position); 212 | printf("STRING: %s\n", s->string); 213 | printf("CHAR: %c\n", kstr_char(s)); 214 | } 215 | 216 | void kstr_free(kstr *s) { 217 | if (!s) return; 218 | if (s->string) free(s->string); 219 | free(s); 220 | } 221 | 222 | 223 | --------------------------------------------------------------------------------