├── .gitignore ├── day3_ex.txt ├── day1_ex.txt ├── day4_ex.txt ├── day2_ex.txt ├── Makefile ├── day1.c ├── day7.c ├── day12.c ├── day2.c ├── day3.c ├── day5.c ├── main.c ├── day4.c ├── day6.c ├── day8.c ├── day9.c ├── day11.c ├── aoc.h ├── day10.c └── stb_ds.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /main 3 | -------------------------------------------------------------------------------- /day3_ex.txt: -------------------------------------------------------------------------------- 1 | 987654321111111 2 | 811111111111119 3 | 234234234234278 4 | 818181911112111 5 | -------------------------------------------------------------------------------- /day1_ex.txt: -------------------------------------------------------------------------------- 1 | L68 2 | L30 3 | R48 4 | L5 5 | R60 6 | L55 7 | L1 8 | L99 9 | R14 10 | L82 11 | -------------------------------------------------------------------------------- /day4_ex.txt: -------------------------------------------------------------------------------- 1 | ..@@.@@@@. 2 | @@@.@.@.@@ 3 | @@@@@.@.@@ 4 | @.@@@@..@. 5 | @@.@@@@.@@ 6 | .@@@@@@@.@ 7 | .@.@.@.@@@ 8 | @.@@@.@@@@ 9 | .@@@@@@@@. 10 | @.@.@@@.@. 11 | -------------------------------------------------------------------------------- /day2_ex.txt: -------------------------------------------------------------------------------- 1 | 11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | main: main.c 2 | cc -O3 -o main main.c 3 | 4 | day10.o: day10.c aoc.h 5 | cc -O3 -fPIC -shared -o day10.o day10.c $$(pkg-config --cflags --libs z3) 6 | 7 | day%.o: day%.c aoc.h 8 | cc -O3 -fPIC -shared -o $@ $< 9 | 10 | clean: 11 | rm *.o 12 | rm main 13 | 14 | day10: aoc.h day10.c 15 | -------------------------------------------------------------------------------- /day1.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | int count(char *in, void (*rotate)(int*, int*, int)) { 4 | int count = 0; 5 | int current = 50; 6 | while(*in == 'R' || *in == 'L') { 7 | int dx = *in == 'R' ? 1 : -1; 8 | in++; 9 | int how_much = atoi(in); 10 | while(*in != '\n') in++; 11 | in++; 12 | rotate(&count, ¤t, dx*how_much); 13 | } 14 | return count; 15 | } 16 | 17 | void part1(int *count, int *current, int how_much) { 18 | *current += (how_much%100); 19 | if(*current < 0) *current = 100 - abs(*current); 20 | if(*current > 99) *current -= 100; 21 | if(*current == 0) { 22 | *count += 1; 23 | } 24 | } 25 | 26 | void part2(int *count, int *current, int how_much) { 27 | int d = how_much < 0 ? -1 : 1; 28 | int c = abs(how_much); 29 | while(c) { 30 | *current += d; 31 | if(*current == -1) *current = 99; 32 | else if(*current == 100) *current = 0; 33 | if(*current == 0) *count += 1; 34 | c--; 35 | } 36 | } 37 | 38 | void day1(str in) { 39 | printf("Part1: %d\n", count(in.data, part1)); 40 | printf("Part2: %d\n", count(in.data, part2)); 41 | } 42 | -------------------------------------------------------------------------------- /day7.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | void day7(str input) { 4 | bool *beams = NULL; 5 | long *ts; 6 | str line; 7 | str_splitat(input, "\n", &line, &input); 8 | // find start pos 9 | int len = line.len; 10 | beams = malloc(sizeof(bool)*len); 11 | memset(beams, 0, sizeof(bool)*len); 12 | ts = malloc(sizeof(long)*len); // count timelines 13 | memset(ts, 0, sizeof(long)*len); 14 | 15 | for(int i=0;i>1]; 19 | long h1 = n/pow; 20 | long h2 = n%pow; 21 | return h1 == h2; 22 | } 23 | 24 | 25 | bool is_invalid_p2(long num) { 26 | long nd = ndigits(num); 27 | long half = pow10[nd>>1]; 28 | 29 | for(long pow=10; pow <= half; pow *= 10) { 30 | if(nd%(ndigits(pow-1)) != 0) continue; // num digits must divide evenly 31 | long n = num; 32 | long init = n%pow; 33 | n /= pow; 34 | bool invalid = true; 35 | while(n) { 36 | long here = n%pow; 37 | if(here != init) { 38 | invalid = false; 39 | break; 40 | } 41 | n /= pow; 42 | } 43 | if(invalid) return true; 44 | } 45 | return false; 46 | } 47 | 48 | void day2(str input) { 49 | char *in = input.data; 50 | char *end = in + input.len; 51 | long invalid_sum_p1 = 0, invalid_sum_p2 = 0; 52 | while(in < end) { 53 | long low = atol(in); 54 | while(*in != '-') in++; 55 | in++; 56 | long high = atol(in); 57 | while(*in != ',' && in < end) in++; 58 | in++; 59 | for(long n = low; n<=high; n++) { 60 | if(is_invalid_p1(n)) invalid_sum_p1 += n; 61 | if(is_invalid_p2(n)) invalid_sum_p2 += n; 62 | } 63 | } 64 | printf("Part1: %ld\nPart2: %ld\n", invalid_sum_p1, invalid_sum_p2); 65 | } 66 | -------------------------------------------------------------------------------- /day3.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | int joltage(char *in, char *end) { 4 | int sum = 0; 5 | 6 | while(in < end) { 7 | int a = -1, b = -1; 8 | // naively go through from here to end of line 9 | char *end = strchr(in, '\n'); 10 | int max = end - in; 11 | int bank_max = -1; 12 | for(int b1=0; b1 bank_max) bank_max = joltage; 18 | } 19 | } 20 | sum += bank_max; 21 | in = end + 1; 22 | } 23 | return sum; 24 | } 25 | 26 | long num12(long *D) { 27 | long n = 0; 28 | for(int i=0;i<12;i++) { 29 | n = (n*10) + D[i]; 30 | } 31 | return n; 32 | } 33 | 34 | long joltage2(char *in, char *end) { 35 | long sum = 0; 36 | while(in < end) { 37 | char *end = strchr(in, '\n'); 38 | int len = end - in; 39 | long digits[12] = {0}; 40 | long p = 0; // add position 41 | int last_pos = -1; // last of digit we took 42 | while(p<12) { 43 | // find the biggest digit that still has 12-p-1 digits after it 44 | // if there are multiple, take the earliest one 45 | long largest = -1; 46 | int pos = -1; 47 | for(int i=last_pos+1; i largest) { 50 | largest = dig; 51 | pos = i; 52 | } 53 | } 54 | digits[p++] = largest; 55 | last_pos = pos; 56 | 57 | } 58 | long bank_joltage = num12(digits); 59 | sum += bank_joltage; 60 | in = end + 1; 61 | } 62 | return sum; 63 | } 64 | 65 | void day3(str input) { 66 | char *in = input.data; 67 | char *end = in+input.len; 68 | printf("Part1: %d\n", joltage(in, end)); 69 | printf("Part2: %ld\n", joltage2(in, end)); 70 | } 71 | -------------------------------------------------------------------------------- /day5.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | typedef struct range { 4 | long low; long high; 5 | } range; 6 | 7 | bool fresh(range *r, long ingredient) { 8 | size_t len = arrlen(r); 9 | for (size_t i = 0; i < len; i++) { 10 | if (r[i].low <= ingredient && r[i].high >= ingredient) 11 | return true; 12 | } 13 | return false; 14 | } 15 | 16 | int compare_range(const void *a, const void *b) { 17 | range *r1 = (range *)a; 18 | range *r2 = (range *)b; 19 | if (r1->low < r2->low) 20 | return -1; 21 | else if (r1->low > r2->low) 22 | return 1; 23 | else 24 | return 0; 25 | } 26 | 27 | void day5(str input) { 28 | range *ranges = NULL; 29 | long *ingredients = NULL; 30 | 31 | str line; 32 | // parse fresh ranges 33 | while (str_splitat(input, "\n", &line, &input)) { 34 | if (line.len == 0) 35 | break; // done parsing ranges 36 | str lo, hi; 37 | str_splitat(line, "-", &lo, &hi); 38 | range r = (range){.low = str_to_long(lo), .high = str_to_long(hi)}; 39 | arrput(ranges, r); 40 | } 41 | 42 | // parse ingredients 43 | while (str_splitat(input, "\n", &line, &input)) { 44 | arrput(ingredients, str_to_long(line)); 45 | } 46 | 47 | long fresh_count = 0; 48 | long len = arrlen(ingredients); 49 | for (size_t i = 0; i < len; i++) { 50 | if(fresh(ranges, ingredients[i])) fresh_count++; 51 | } 52 | printf("Part1: %ld\n", fresh_count); 53 | 54 | /* count unique items in ranges */ 55 | range *unique = NULL; 56 | long unique_count = 0; 57 | 58 | // sort ranges by low 59 | qsort(ranges, arrlen(ranges), sizeof(range), compare_range); 60 | range prev = ranges[0]; 61 | for (int r = 1; r < arrlen(ranges); r++) { 62 | range at = ranges[r]; 63 | if (at.low <= prev.high) { 64 | // if range is low is within prev range, extend prev if bigger 65 | if(at.high > prev.high) 66 | prev.high = at.high; 67 | } else { 68 | // if low starts after prev high, add prev to unique 69 | unique_count += (1 + (prev.high - prev.low)); 70 | prev = at; 71 | } 72 | } 73 | unique_count += (1 + (prev.high - prev.low)); 74 | 75 | printf("Part2: %ld\n", unique_count); 76 | } 77 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | clock_t started; 9 | 10 | void time_start() { started = clock(); } 11 | void time_end(int day) { 12 | clock_t ended = clock(); 13 | printf("=> Day%d took %ldms\n\n", day, (ended-started) * 1000 / CLOCKS_PER_SEC); 14 | } 15 | 16 | 17 | char *input(const char *file, size_t *len) { 18 | FILE *f = fopen(file, "r"); 19 | if(!f) { 20 | fprintf(stderr, "Can't open input file: %s\n", file); 21 | return NULL; 22 | } 23 | struct stat fs; 24 | int in = fileno(f); 25 | if(fstat(in, &fs) == -1) { 26 | fprintf(stderr, "Can't stat input file: %s\n", file); 27 | return NULL; 28 | } 29 | char *map = mmap(0,fs.st_size, PROT_READ, MAP_SHARED, in, 0); 30 | if(!map) { 31 | fprintf(stderr, "Can't mmap input file: %s\n", file); 32 | return NULL; 33 | } 34 | if(len) *len = fs.st_size; 35 | 36 | char *data = malloc(*len); 37 | memcpy(data, map, *len); 38 | munmap(map, *len); 39 | return data; 40 | } 41 | 42 | int run_day(int day, bool example) { 43 | // load input and solution module 44 | 45 | char input_file[16]; 46 | snprintf(input_file, 16, "day%d%s.txt", day, example ? "_ex" : ""); 47 | 48 | char module_file[16]; 49 | snprintf(module_file, 16, "day%d.o", day); 50 | 51 | char function_name[16]; 52 | snprintf(function_name, 16, "day%d", day); 53 | 54 | void *solution = dlopen(module_file, RTLD_NOW); 55 | if(!solution) { 56 | fprintf(stderr, "Can't open module: %s\n", module_file); 57 | return 1; 58 | } 59 | 60 | void (*dayfn)(str) = dlsym(solution, function_name); 61 | if(!dayfn) { 62 | fprintf(stderr, "Day solve function not found: %s\n", function_name); 63 | return 1; 64 | } 65 | 66 | size_t len; 67 | char *in = input(input_file, &len); 68 | if (!in) 69 | return 1; 70 | 71 | time_start(); 72 | dayfn((str){.len = len, .data = in}); 73 | time_end(day); 74 | return 0; 75 | } 76 | 77 | int main(int argc, char **argv) { 78 | if(argc < 2) { 79 | fprintf(stderr, "Specify day, eg. ./main 1 [ex]\n"); 80 | return 1; 81 | } 82 | 83 | bool example = argc > 2 && strcmp(argv[2],"ex")==0; 84 | int ret=0; 85 | if(strcmp(argv[1], "all")==0) { 86 | for(int i=1;i<=12;i++) { 87 | ret += run_day(i, example); 88 | } 89 | } else { 90 | ret = run_day(atoi(argv[1]), example); 91 | } 92 | return ret; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /day4.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | typedef struct Grid { 4 | int w, h; 5 | char *data; 6 | } Grid; 7 | 8 | char grid_at(Grid *g, int x, int y) { 9 | if(x < 0 || x >= g->w || y < 0 || y >= g->h) return 0; 10 | return g->data[y*(g->w+1)+x]; 11 | } 12 | 13 | void grid_set(Grid *g, int x, int y, char v) { 14 | if(x < 0 || x >= g->w || y < 0 || y >= g->h) return; 15 | g->data[y*(g->w+1)+x] = v; 16 | } 17 | 18 | 19 | void grid_move(int dir, int x, int y, int *tox, int *toy) { 20 | switch(dir) { 21 | case 0: *tox = x; *toy = y-1; break; // NORTH 22 | case 1: *tox = x+1; *toy = y-1; break; // NORTH-EAST 23 | case 2: *tox = x+1; *toy = y; break; // EAST 24 | case 3: *tox = x+1; *toy = y+1; break; // SOUTH-EAST 25 | case 4: *tox = x; *toy = y+1; break; // SOUTH 26 | case 5: *tox = x-1; *toy = y+1; break; // SOUTH-WEST 27 | case 6: *tox = x-1; *toy = y; break; // WEST 28 | case 7: *tox = x-1; *toy = y-1; break; // NORTH-WEST 29 | default: 30 | fprintf(stderr, "There's no such direction: %d\n", dir); 31 | exit(1); 32 | } 33 | } 34 | 35 | void accessible_rolls(Grid *g, void (*accessible)(int,int)) { 36 | for(int y=0;yh;y++) { 37 | for(int x=0;xw;x++) { 38 | if(grid_at(g, x, y) != '@') continue; 39 | int around=0; 40 | for(int d=0;d<8;d++) { 41 | int tox, toy; 42 | grid_move(d, x, y, &tox, &toy); 43 | if(grid_at(g, tox, toy)=='@') around++; 44 | } 45 | if(around<4) { accessible(x,y); } 46 | } 47 | } 48 | } 49 | 50 | int accessible; 51 | void count_accessible(int _x, int _y) { 52 | accessible += 1; 53 | } 54 | 55 | typedef struct Pos { int x; int y; } Pos; 56 | Pos *removes; 57 | void mark_remove(int x, int y) { 58 | Pos p = (Pos){.x = x, .y = y}; 59 | arrput(removes, p); 60 | } 61 | 62 | void day4(str input) { 63 | size_t len = input.len; 64 | char *in = input.data; 65 | char *nl = strchr(in, '\n'); 66 | Grid g = {.w = nl-in, .h = len/(nl-in+1), .data=in}; 67 | accessible = 0; 68 | accessible_rolls(&g, count_accessible); 69 | printf("Part1: %d\n", accessible); 70 | 71 | int total_removed=0; 72 | /* While removes has items, do another round */ 73 | do { 74 | arrsetlen(removes, 0); 75 | accessible_rolls(&g, mark_remove); 76 | total_removed += arrlen(removes); 77 | for(int i=0;i 3 | 4 | typedef struct problem { 5 | long *nums; 6 | char op; 7 | } problem; 8 | 9 | long calc(problem p) { 10 | long out = p.nums[0]; 11 | for(int i=1;i= '0' && line.data[0] <= '9'; 31 | str n; 32 | int i=0; 33 | while(line.len) { 34 | if(str_splitat(line, " ", &n, &line)) { 35 | n = str_trim(n); 36 | line = str_trim(line); 37 | } else { 38 | n = line; 39 | line = (str){0}; 40 | } 41 | char op = parse_nums ? 0 : n.data[0]; 42 | long num = parse_nums ? str_to_long(n) : 0; 43 | if(arrlen(ps) <= i) { 44 | problem p = (problem) {0}; 45 | arrput(p.nums, num); 46 | arrput(ps, p); 47 | } else { 48 | if(parse_nums) arrput(ps[i].nums, num); 49 | else ps[i].op = op; 50 | } 51 | i++; 52 | } 53 | } 54 | 55 | long part1 = 0; 56 | for(int i=0;i max_len ? line.len : max_len; 71 | } 72 | 73 | /* start from max_len-1 go down each column, when you encounter an op, a problem is done */ 74 | int c = max_len-1; 75 | size_t nlines = arrlen(lines); 76 | int i = 0; 77 | arrsetlen(ps, 1); 78 | do { 79 | long n=0; 80 | for(int l=0;l c ? lines[l].data[c] : 0; 82 | 83 | if(isdigit(ch)) { 84 | n = n*10 + (ch-48); 85 | } 86 | if(l == nlines-1) { arrput(ps[i].nums, n); } 87 | if(ch == '*' || ch == '+') { 88 | ps[i].op = ch; 89 | if(c) { 90 | i++; 91 | arrsetlen(ps, i+1); 92 | ps[i] = (problem){0}; 93 | } 94 | c--; // skip space 95 | } 96 | } 97 | 98 | c--; 99 | } while(c >= 0); 100 | 101 | long part2 = 0; 102 | for(int i=0;i 3 | #include 4 | 5 | 6 | typedef struct box { 7 | long x, y, z; 8 | int circuit; // id of circuit this is in (starting from 1, 0 means not in any) 9 | } box; 10 | 11 | typedef struct circuit { 12 | int *bs; // box indexes 13 | bool defunct; // if true, this was merged with another one 14 | } circuit; 15 | 16 | double distance(box a, box b) { 17 | long dx = (b.x - a.x); 18 | long dy = (b.y - a.y); 19 | long dz = (b.z - a.z); 20 | return sqrt(dx*dx + dy*dy + dz*dz); 21 | } 22 | 23 | typedef struct pair { 24 | double dist; 25 | // index to boxes 26 | uint16_t a; 27 | uint16_t b; 28 | } pair; 29 | 30 | int cmp(const void *a, const void *b) { return (*(int *)b) - (*(int *)a); } 31 | 32 | int cmp_pair(const void *a, const void *b) { 33 | pair *p1 = (pair *)a; 34 | pair *p2 = (pair *)b; 35 | if (p2->dist > p1->dist) 36 | return -1; 37 | else if (p1->dist > p2->dist) 38 | return 1; 39 | else 40 | return 0; 41 | } 42 | 43 | void day8(str input) { 44 | box *bs = NULL; // junction boxes 45 | circuit *cs = NULL; // circuits 46 | str line; 47 | while (str_splitat(input, "\n", &line, &input)) { 48 | str c; 49 | str_splitat(line, ",", &c, &line); 50 | box b = {0}; 51 | b.x = str_to_long(c); 52 | str_splitat(line, ",", &c, &line); 53 | b.y = str_to_long(c); 54 | b.z = str_to_long(line); 55 | arrput(bs, b); 56 | } 57 | 58 | /* build distance pairs for all boxes */ 59 | pair *ps = NULL; 60 | uint16_t len = arrlen(bs); 61 | arrsetcap(ps, len*len); // preallocate enough memory 62 | 63 | for (uint16_t i = 0; i < len; i++) { 64 | for (uint16_t j = i+1; j < len; j++) { 65 | pair p = (pair){.a = i, .b = j, .dist = distance(bs[i], bs[j])}; 66 | arrput(ps, p); 67 | } 68 | } 69 | qsort(ps, arrlen(ps), sizeof(pair), cmp_pair); 70 | 71 | 72 | long part1_rounds = 1000; 73 | long rounds=0; 74 | for (;;) { 75 | pair p = ps[rounds]; 76 | 77 | box *ca = &bs[p.a]; 78 | box *cb = &bs[p.b]; 79 | if (ca->circuit && cb->circuit && ca->circuit == cb->circuit) { 80 | // do nothing, already in same circuit 81 | } else if (ca->circuit && cb->circuit && ca->circuit != cb->circuit) { 82 | circuit *to = &cs[ca->circuit - 1]; 83 | circuit *from = &cs[cb->circuit - 1]; 84 | for (int i = 0; i < arrlen(from->bs); i++) { 85 | int bi = from->bs[i]; 86 | arrput(to->bs, bi); 87 | bs[bi].circuit = ca->circuit; 88 | } 89 | from->defunct = true; 90 | } else if (ca->circuit) { 91 | circuit *c = &cs[ca->circuit - 1]; 92 | cb->circuit = ca->circuit; 93 | arrput(c->bs, p.b); 94 | } else if (cb->circuit) { 95 | circuit *c = &cs[cb->circuit - 1]; 96 | ca->circuit = cb->circuit; 97 | arrput(c->bs, p.a); 98 | } else { 99 | // new circuit 100 | int id = arrlen(cs)+1; 101 | circuit c = {0}; 102 | ca->circuit = id; 103 | cb->circuit = id; 104 | arrput(c.bs, p.a); 105 | arrput(c.bs, p.b); 106 | arrput(cs, c); 107 | } 108 | 109 | rounds++; 110 | if (rounds == part1_rounds) { 111 | int *sizes = NULL; 112 | for (int i = 0; i < arrlen(cs); i++) { 113 | if (!cs[i].defunct) { 114 | arrput(sizes, arrlen(cs[i].bs)); 115 | } 116 | } 117 | qsort(sizes, arrlen(sizes), sizeof(int), cmp); 118 | printf("Part1: %d\n", sizes[0] * sizes[1] * sizes[2]); 119 | arrfree(sizes); 120 | } 121 | 122 | /* check if last connection */ 123 | if (arrlen(cs[ca->circuit-1].bs) == arrlen(bs)) { 124 | printf("Part2: %ld\n", ca->x * cb->x); 125 | break; 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /day9.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | typedef struct pos { 4 | long x, y; 5 | } pos; 6 | 7 | typedef enum tile { OTHER = '.', RED = '#', GREEN = 'X' } tile; 8 | 9 | typedef struct grid { 10 | long min_x, min_y, max_x, max_y; 11 | char *data; 12 | } grid; 13 | 14 | #define G_WIDTH(g) ((g)->max_x - (g)->min_x + 1) 15 | #define G_HEIGHT(g) ((g)->max_y - (g)->min_y + 1) 16 | 17 | #define min(a,b) ((a) < (b) ? (a) : (b)) 18 | #define max(a,b) ((a) > (b) ? (a) : (b)) 19 | 20 | long calc_area(pos p1, pos p2) { 21 | long dx = labs(p1.x - p2.x) + 1; 22 | long dy = labs(p1.y - p2.y) + 1; 23 | return dx*dy; 24 | } 25 | 26 | bool is_inside(pos c1, pos c2, pos p) { 27 | int min_x = min(c1.x, c2.x); 28 | int max_x = max(c1.x, c2.x); 29 | int min_y = min(c1.y, c2.y); 30 | int max_y = max(c1.y, c2.y); 31 | return (p.x > min_x && p.x < max_x && p.y > min_y && p.y < max_y); 32 | } 33 | 34 | void day9(str input) { 35 | pos *ps = NULL; 36 | str line; 37 | while (str_splitat(input, "\n", &line, &input)) { 38 | str xp, yp; 39 | str_splitat(line, ",", &xp, &yp); 40 | pos p = (pos){.x = str_to_long(xp), .y = str_to_long(yp)}; 41 | arrput(ps, p); 42 | } 43 | 44 | grid g = {.min_x = -1, .max_x = -1, .min_y = -1, .max_y = -1}; 45 | long area = 0; 46 | for (int i = 0; i < arrlen(ps); i++) { 47 | // calculate area of our whole drawing surface 48 | if (g.min_x == -1 || ps[i].x < g.min_x) 49 | g.min_x = ps[i].x; 50 | if (g.max_x == -1 || ps[i].x > g.max_x) 51 | g.max_x = ps[i].x; 52 | if (g.min_y == -1 || ps[i].y < g.min_y) 53 | g.min_y = ps[i].y; 54 | if (g.max_y == -1 || ps[i].y > g.max_y) 55 | g.max_y = ps[i].y; 56 | 57 | 58 | for (int j = i + 1; j < arrlen(ps); j++) { 59 | long a = calc_area(ps[i], ps[j]); 60 | if(a > area) area = a; 61 | } 62 | } 63 | printf("Part1: %ld\n", area); // 4725826296 64 | 65 | 66 | /* write svg visualization of part 2 as we go */ 67 | 68 | FILE *f = fopen("poly.svg", "w"); 69 | fprintf(f, 70 | "", 74 | g.min_x - 1000, g.min_y - 1000, G_WIDTH(&g) + 2000, 75 | G_HEIGHT(&g) + 2000); 76 | fprintf(f, ""); 83 | 84 | for (int i = 0; i < arrlen(ps); i++) { 85 | fprintf(f, "", 86 | ps[i].x - 250, ps[i].y - 250); 87 | } 88 | 89 | 90 | long max_area = -1; 91 | pos pos_a, pos_b; 92 | int half = arrlen(ps)/2; 93 | //for (int i = arrlen(ps)/2; i < arrlen(ps)/2+2; i++) { 94 | for (int i = 0; i < arrlen(ps); i++) { 95 | for(int j=i+1; j max_area) { 98 | // check that we are on either side of center and no other point is inside 99 | bool valid = ((i <= half && j <= half) || (i > half && j > half)); 100 | 101 | if(valid) { 102 | for (int k = 0; k < arrlen(ps); k++) { 103 | if (k != i && k != j && is_inside(ps[i], ps[j], ps[k])) { 104 | valid = false; 105 | break; 106 | } 107 | } 108 | } 109 | if (valid) { 110 | max_area = area; 111 | pos_a = ps[i]; 112 | pos_b = ps[j]; 113 | } 114 | } 115 | } 116 | } 117 | 118 | long min_x = min(pos_a.x, pos_b.x); 119 | long max_x = max(pos_a.x, pos_b.x); 120 | long min_y = min(pos_a.y, pos_b.y); 121 | long max_y = max(pos_a.y, pos_b.y); 122 | 123 | fprintf(f, 124 | "", 125 | min_x, min_y, max_x-min_x, max_y-min_y); 126 | 127 | printf("Part2: %ld\n", max_area); // 1637556834 128 | 129 | 130 | fprintf(f, ""); 131 | fclose(f); 132 | } 133 | -------------------------------------------------------------------------------- /day11.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | 3 | // max input and output (observed from real input data) 4 | #define MAX_OUT 21 5 | 6 | typedef struct device { 7 | str name; 8 | u16 out[MAX_OUT]; // outpus (0 = unused) 9 | u8 outs; // actual output count 10 | } device; 11 | 12 | // id is a-z * 3 (so 25^3, 15bit id is enough) 13 | // have array of devices statically 14 | device ds[2<<15]; 15 | 16 | // turn C string into id 17 | u16 ID(const char *x) { 18 | return (((x[0]-'a') << 10) + ((x[1]-'a') << 5) + (x[2]-'a')); 19 | } 20 | 21 | 22 | int has_out(device d, u16 out) { 23 | for (int i = 0; i < d.outs; i++) { 24 | if (d.out[i] == out) 25 | return 1; 26 | } 27 | return 0; 28 | } 29 | 30 | /* Parse devices, returns devices, sets you and out ids */ 31 | void parse_devices(str orig_input) { 32 | device out = {.name = str_from_cstr("out")}; 33 | ds[ID("out")] = out; 34 | 35 | // first pass, create all devices, so we can refer to them by id 36 | str input = orig_input; 37 | str line; 38 | while (str_splitat(input, "\n", &line, &input)) { 39 | str name, outputs; 40 | str_splitat(line, ":", &name, &outputs); 41 | device d = (device) {.name = name}; 42 | ds[ID(name.data)] = d; 43 | } 44 | 45 | // second pass, add references to outputs 46 | input = orig_input; 47 | while (str_splitat(input, "\n", &line, &input)) { 48 | str name, outputs; 49 | str_splitat(line, ":", &name, &outputs); 50 | u16 d_id = ID(name.data); 51 | str out; 52 | outputs = str_trim(outputs); 53 | int o=0; 54 | while (str_splitat(outputs, " ", &out, &outputs)) { 55 | u16 out_id = ID(out.data); 56 | ds[d_id].out[o++] = out_id; 57 | } 58 | if (outputs.len) { 59 | u16 out_id = ID(outputs.data); 60 | ds[d_id].out[o++] = out_id; 61 | } 62 | ds[d_id].outs = o; 63 | } 64 | } 65 | 66 | 67 | typedef struct nodecount { 68 | bool calculated; 69 | long count; 70 | } nodecount; 71 | 72 | nodecount node_count[2<<15]; 73 | 74 | void clear() { memset(node_count, 0, sizeof(node_count)); } 75 | 76 | long traverse(u16 at, u16 target) { 77 | if (node_count[at].calculated) { 78 | return node_count[at].count; 79 | } else { 80 | if (at == target) { 81 | node_count[at] = (nodecount){.calculated = 1, .count = 1}; 82 | return 1; 83 | } else { 84 | long count = 0; 85 | for (int i = 0; i < MAX_OUT; i++) { 86 | if (!ds[at].out[i]) 87 | continue; 88 | count += traverse(ds[at].out[i], target); 89 | } 90 | node_count[at] = (nodecount){.calculated = 1, .count = count}; 91 | return count; 92 | } 93 | } 94 | } 95 | 96 | 97 | void diagram(device *ds) { 98 | FILE *f = fopen("day11.dot", "w"); 99 | if (!f) { 100 | fprintf(stderr, "Can't open day11.dot\n"); 101 | exit(1); 102 | } 103 | fprintf(f, "digraph {\n"); 104 | for (int i = 1; i < arrlen(ds); i++) { 105 | str from = ds[i].name; 106 | for (int j = 0; j < MAX_OUT; j++) { 107 | u16 out = ds[i].out[j]; 108 | if (out) { 109 | fprintf(f, "%.*s -> %.*s\n", (int)from.len, from.data, 110 | (int)ds[out].name.len, ds[out].name.data); 111 | } 112 | } 113 | } 114 | fprintf(f, "}"); 115 | fclose(f); 116 | } 117 | 118 | void day11(str input) { 119 | u16 you, out, svr, dac, fft; 120 | parse_devices(input); 121 | 122 | clear(); 123 | long paths1 = traverse(ID("you"), ID("out")); 124 | printf("Part1: %ld\n", paths1); 125 | 126 | // paths from svr->dac * dac->fft * fft->out 127 | // + svr->fft * ffr->dac * dac->out 128 | 129 | clear(); 130 | long svr_dac = traverse(ID("svr"), ID("dac")); 131 | 132 | clear(); 133 | long dac_fft = traverse(ID("dac"), ID("fft")); 134 | 135 | clear(); 136 | long fft_out = traverse(ID("fft"), ID("out")); 137 | 138 | clear(); 139 | long svr_fft = traverse(ID("svr"), ID("fft")); 140 | 141 | clear(); 142 | long fft_dac = traverse(ID("fft"), ID("dac")); 143 | 144 | clear(); 145 | long dac_out = traverse(ID("dac"), ID("out")); 146 | 147 | printf("Part2: %ld\n", 148 | (svr_dac * dac_fft * fft_out) + (svr_fft * fft_dac * dac_out)); 149 | 150 | //diagram(ds); 151 | 152 | } 153 | -------------------------------------------------------------------------------- /aoc.h: -------------------------------------------------------------------------------- 1 | #ifndef aoc_h 2 | #define aoc_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef uint8_t u8; 12 | typedef uint16_t u16; 13 | typedef uint64_t u64; 14 | 15 | typedef struct str { 16 | size_t len; 17 | char *data; 18 | } str; 19 | 20 | #endif 21 | 22 | #ifndef aoc_h_impl 23 | 24 | #define STB_DS_IMPLEMENTATION 25 | #include "stb_ds.h" 26 | 27 | /*bool str_scan(str in, const char *fmt, ...) { 28 | va_list args; 29 | va_start (args, fmt); 30 | 31 | va_end(args); 32 | }*/ 33 | 34 | 35 | bool str_splitat(str in, const char *chars, str *split, str *rest) { 36 | if(in.len == 0) return false; 37 | size_t at = 0; 38 | int chs = strlen(chars); 39 | while(at < in.len) { 40 | for(int i=0;ilen = at; 44 | split->data = in.data; 45 | rest->len = in.len - at - 1; 46 | rest->data = in.data + at + 1; 47 | return true; 48 | } 49 | } 50 | at++; 51 | } 52 | return false; 53 | } 54 | 55 | 56 | int str_indexof(str in, char ch) { 57 | for(int i=0;ilen == 0) return false; 65 | if(str_splitat(*lines, "\n", line, lines)) 66 | return true; 67 | if(lines->len) { 68 | // last line 69 | line->len = lines->len; 70 | line->data = lines->data; 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | bool is_space(char ch) { 77 | return ch == ' ' || ch == '\t'; 78 | } 79 | 80 | str str_ltrim(str in) { 81 | while(in.len && is_space(in.data[0])) { 82 | in.len--; 83 | in.data = &in.data[1]; 84 | } 85 | return in; 86 | } 87 | 88 | str str_rtrim(str in) { 89 | while(in.len && is_space(in.data[in.len-1])) in.len--; 90 | return in; 91 | } 92 | 93 | str str_trim(str in) { 94 | return str_ltrim(str_rtrim(in)); 95 | } 96 | 97 | long str_to_long(str s) { 98 | long l=0; 99 | for(size_t i=0;i haystack.len) return false; 106 | return strncmp(haystack.data,needle.data,needle.len) == 0; 107 | } 108 | 109 | str str_drop(str haystack, size_t len) { 110 | if(len > haystack.len) return (str) {.len = 0, .data=NULL}; 111 | return (str) {.len = haystack.len - len, 112 | .data = &haystack.data[len]}; 113 | } 114 | 115 | str str_take(str big, size_t len) { 116 | if(len > big.len) return big; 117 | return (str) {.len = len, .data = big.data}; 118 | } 119 | 120 | str str_from_cstr(const char *in) { 121 | size_t len = strlen(in); 122 | char *data = malloc(len); 123 | if(!data) fprintf(stderr, "Malloc failed!"); 124 | memcpy(data, in, len); 125 | return (str){.data = data, .len = len}; 126 | } 127 | 128 | str str_dup(str in) { 129 | char *data = malloc(in.len); 130 | if(!data) fprintf(stderr, "Malloc failed!"); 131 | memcpy(data, in.data, in.len); 132 | return (str) {.len = in.len, .data = data}; 133 | } 134 | 135 | void str_reverse(str mut) { 136 | size_t l=0,r=mut.len-1; 137 | while(ldata); 147 | s->len = 0; 148 | s->data = NULL; 149 | } 150 | 151 | bool str_eq(str a, str b) { 152 | if(a.len != b.len) return false; 153 | return strncmp(a.data, b.data, a.len) == 0; 154 | } 155 | 156 | bool str_eq_cstr(str str, const char *cstring) { 157 | int len = strlen(cstring); 158 | if(len != str.len) return false; 159 | return strncmp(str.data, cstring, len) == 0; 160 | } 161 | 162 | char str_char_at(str str, size_t idx) { 163 | if(idx < 0 || idx >= str.len) return 0; 164 | return str.data[idx]; 165 | } 166 | 167 | char *str_to_cstr(str str) { 168 | char *cstr = malloc(str.len+1); 169 | if(!cstr) { 170 | printf("malloc failed"); 171 | exit(1); 172 | } 173 | memcpy(cstr, str.data, str.len); // FIXME: check 174 | cstr[str.len] = 0; 175 | return cstr; 176 | } 177 | 178 | /* Allocate new dynamic array of lines extracted */ 179 | str *str_lines(str in) { 180 | str *lines = {0}; 181 | str line; 182 | str rest = in; 183 | while(str_splitat(rest, "\n", &line, &rest)) { 184 | arrput(lines, line); 185 | } 186 | if(rest.len > 0) 187 | arrput(lines, rest); 188 | return lines; 189 | } 190 | #endif 191 | -------------------------------------------------------------------------------- /day10.c: -------------------------------------------------------------------------------- 1 | #include "aoc.h" 2 | #include 3 | 4 | #include "z3.h" 5 | #include "z3_api.h" 6 | #include "z3_optimization.h" 7 | 8 | typedef uint16_t u16; 9 | typedef uint32_t u32; 10 | typedef uint8_t u8; 11 | 12 | // indicators encoding: 13 | // u32 with 14 | // high 8 bits = number of indicators 15 | // low bits = target state 16 | // 17 | // [.####.#..#] (len 9) 18 | // ........ ........ ........ ........ 19 | // 00001010 00000000 00000010 01011110 20 | // ^^len 9^ ^^bits on ^^ reversed! 21 | // 22 | // button is a u16 with the bits that it toggles on 23 | // (3,4,6,7,9) 24 | // 25 | // ........ ........ 26 | // 00000010 11011000 27 | // 9 76 43 28 | // 29 | // a combination of currently on and button 30 | // is u32 31 | // (indicators << 16) + button 32 | 33 | 34 | // get indicators len 35 | #define LEN(ind) ((ind) >> 24) 36 | 37 | // check if machine indicator target is on or button toggles 38 | #define ON(x, n) ((x) & (1 << (n))) 39 | 40 | // toggle specific indicator bit 41 | #define TOGGLE(x, n) ((x) ^= (1 << (n))) 42 | 43 | // hacky, but array pointers are poor values as not assignable directly 44 | typedef struct joltages { 45 | u16 joltage[10]; 46 | u8 count; 47 | } joltages; 48 | 49 | typedef struct machine { 50 | u32 indicators; 51 | u16 buttons[16]; 52 | joltages joltage; 53 | } machine; 54 | 55 | void print_joltages(joltages *p) { 56 | printf("{"); 57 | for (int i = 0; i < p->count; i++) { 58 | if(i) printf(","); 59 | printf("%d", p->joltage[i]); 60 | } 61 | printf("}\n"); 62 | } 63 | 64 | machine parse_machine(str in) { 65 | machine m = {0}; 66 | in = str_drop(in, 1); 67 | char ch = str_char_at(in, 0); 68 | int len = str_indexof(in, ']'); 69 | m.indicators = len << 24; 70 | int i=0; 71 | do { 72 | if (ch == '#') { 73 | TOGGLE(m.indicators, i); 74 | } 75 | i++; 76 | in = str_drop(in, 1); 77 | ch = str_char_at(in, 0); 78 | } while(ch != ']'); 79 | in = str_ltrim(str_drop(in,1)); 80 | ch = str_char_at(in, 0); 81 | i = 0; 82 | do { 83 | str list, idx; 84 | str_splitat(str_drop(in,1), ")", &list, &in); 85 | u16 b = 0; 86 | 87 | while (str_splitat(list, ",", &idx, &list)) { 88 | long indicator = str_to_long(idx); 89 | TOGGLE(b, indicator); 90 | } 91 | if (list.len) { 92 | long indicator = str_to_long(list); 93 | TOGGLE(b, indicator); 94 | } 95 | m.buttons[i++] = b; 96 | in = str_drop(in, 1); 97 | ch = str_char_at(in, 0); 98 | } while (ch == '('); 99 | 100 | in = str_ltrim(str_drop(in, 1)); 101 | in.len--; // ignore ending '}' 102 | str joltage; 103 | i = 0; 104 | while (str_splitat(in, ",", &joltage, &in)) { 105 | m.joltage.joltage[i++] = str_to_long(joltage); 106 | } 107 | if (in.len) 108 | m.joltage.joltage[i++] = str_to_long(in); 109 | m.joltage.count = i; 110 | return m; 111 | } 112 | 113 | void print_machine(machine m) { 114 | printf("["); 115 | for (int i = 0; i < LEN(m.indicators); i++) { 116 | printf("%c", ON(m.indicators, i) ? '#' : '.'); 117 | } 118 | printf("]"); 119 | for (int i = 0; i < 16; i++) { 120 | if(!m.buttons[i]) break; 121 | printf(" ("); 122 | bool first = true; 123 | for (int b = 0; b < 16; b++) { 124 | if (ON(m.buttons[i], b)) { 125 | if (!first) 126 | printf(","); 127 | printf("%d", b); 128 | first = false; 129 | } 130 | } 131 | printf(")"); 132 | } 133 | 134 | printf(" {"); 135 | for (int i = 0; i < m.joltage.count; i++) { 136 | if(i) printf(","); 137 | printf("%d", m.joltage.joltage[i]); 138 | } 139 | printf("}"); 140 | } 141 | 142 | /* use A* to get to target nodes, each button is a possible neighbor, 143 | * the distance is how many indicator indexes are in correct state 144 | * 145 | * [..#.#] vs 146 | * [.#..#] 147 | * => 2 distance, idx 1 and 2 differ 148 | * 149 | * when all are the same, we have reached the target 150 | */ 151 | 152 | void print_indicators(int len, uint16_t on) { 153 | printf("["); 154 | for (int i = 0; i < len; i++) { 155 | printf("%c", ON(on, i) ? '#' : '.'); 156 | } 157 | printf("]\n"); 158 | } 159 | 160 | /* hashtable structure to keep node scores */ 161 | struct score { 162 | u32 key; 163 | long value; 164 | }; 165 | 166 | struct camefrom { 167 | u16 key; 168 | u16 value; 169 | }; 170 | Z3_context ctx; 171 | Z3_sort int_sort; 172 | 173 | struct openset { 174 | u32 node; 175 | long score; 176 | }; 177 | 178 | #define INITIAL_SCORE 999999999 179 | 180 | long get_score(struct score *scores, u32 node) { 181 | int idx = hmgeti(scores, node); 182 | if (idx == -1) { 183 | return INITIAL_SCORE; 184 | } else { 185 | return scores[idx].value; 186 | } 187 | } 188 | 189 | 190 | void enqueue(struct openset **openSet, struct openset item) { 191 | // add to array from end, while item.score > at.score 192 | int len = arrlen(*openSet); 193 | for (int i = 0; i < len; i++) { 194 | if((*openSet)[i].node == item.node) return; 195 | } 196 | if (len == 0 || item.score < (*openSet)[len-1].score) { 197 | arrput(*openSet, item); 198 | } else { 199 | for (int p = arrlen(*openSet)-1; p; p--) { 200 | if (item.score < (*openSet)[p-1].score) { 201 | arrins(*openSet, p, item); 202 | return; 203 | } 204 | } 205 | arrins(*openSet, 0, item); 206 | } 207 | } 208 | 209 | int least_presses(machine m, u16 target) { 210 | struct openset *openSet = {0}; 211 | struct score *gScore = {0}; 212 | struct score *fScore = {0}; 213 | struct camefrom *cameFrom = {0}; 214 | 215 | u16 node = 0; // start node is all indicators of, 216 | hmput(gScore, node, 0); 217 | hmput(fScore, node, 0); 218 | 219 | struct openset from = (struct openset){.node = 0, .score=0}; 220 | enqueue(&openSet, from); 221 | 222 | long result = -1; 223 | 224 | while (arrlen(openSet)) { 225 | //printf(" openset: %ld\n", arrlen(openSet)); 226 | node = arrpop(openSet).node; 227 | //print_indicators(LEN(m.indicators), node>>16); 228 | if (node == (m.indicators & 0xFFFF)) { 229 | int len = 0; 230 | u32 at = node; 231 | int idx = hmgeti(cameFrom, at); 232 | while(idx != -1) { 233 | at = cameFrom[idx].value; 234 | len++; 235 | idx = hmgeti(cameFrom, at); 236 | } 237 | result = len; 238 | goto done; 239 | } else { 240 | // go through all neighbors, every button is a possible neighbor 241 | for (int i = 0; i < 16; i++) { 242 | if (m.buttons[i]) { 243 | u16 b = m.buttons[i]; 244 | u16 neighbor = node; 245 | for (int i = 0; i < 16; i++) { 246 | if (ON(b, i)) { 247 | TOGGLE(neighbor, i); 248 | } 249 | } 250 | 251 | long tentative_gScore = get_score(gScore, node) + 1; 252 | long current_gScore = get_score(gScore, neighbor); 253 | 254 | if (tentative_gScore < current_gScore) { 255 | // better than previous path 256 | hmput(cameFrom, neighbor, node); 257 | hmput(gScore, neighbor, tentative_gScore); 258 | long fs = tentative_gScore; 259 | // distance is useless as we calculate steps only 260 | // + distance(m, neighbor); 261 | hmput(fScore, neighbor, fs); 262 | enqueue(&openSet, (struct openset){.node = neighbor, .score=fs}); 263 | } 264 | } 265 | } 266 | } 267 | } 268 | done: 269 | hmfree(gScore); 270 | hmfree(fScore); 271 | hmfree(cameFrom); 272 | arrfree(openSet); 273 | if(result == -1) { 274 | fprintf(stderr, "didn't find solution!"); 275 | exit(1); 276 | } 277 | return result; 278 | } 279 | 280 | 281 | 282 | void z3_init() { 283 | Z3_config cfg = Z3_mk_config(); 284 | ctx = Z3_mk_context(cfg); 285 | int_sort = Z3_mk_int_sort(ctx); 286 | } 287 | 288 | void z3_deinit() { 289 | Z3_del_context(ctx); 290 | } 291 | 292 | 293 | int desired_joltage(machine m, joltages desired) { 294 | 295 | Z3_optimize s = Z3_mk_optimize(ctx); 296 | Z3_optimize_inc_ref(ctx, s); 297 | 298 | Z3_ast total_clicks = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "total"), int_sort); 299 | 300 | Z3_ast btn_clicks[16]; 301 | Z3_ast jolt[10]; 302 | 303 | for(int i=0;i pair into the hashmap. If the key is already 260 | present in the hashmap, updates its value. 261 | 262 | hmputs 263 | shputs 264 | T hmputs(T*, T item) 265 | T shputs(T*, T item) 266 | Inserts a struct with T.key into the hashmap. If the struct is already 267 | present in the hashmap, updates it. 268 | 269 | hmdel 270 | shdel 271 | int hmdel(T*, TK key) 272 | int shdel(T*, char* key) 273 | If 'key' is in the hashmap, deletes its entry and returns 1. 274 | Otherwise returns 0. 275 | 276 | Function interface (actually macros) for strings only: 277 | 278 | sh_new_strdup 279 | void sh_new_strdup(T*); 280 | Overwrites the existing pointer with a newly allocated 281 | string hashmap which will automatically allocate and free 282 | each string key using realloc/free 283 | 284 | sh_new_arena 285 | void sh_new_arena(T*); 286 | Overwrites the existing pointer with a newly allocated 287 | string hashmap which will automatically allocate each string 288 | key to a string arena. Every string key ever used by this 289 | hash table remains in the arena until the arena is freed. 290 | Additionally, any key which is deleted and reinserted will 291 | be allocated multiple times in the string arena. 292 | 293 | NOTES 294 | 295 | * These data structures are realloc'd when they grow, and the macro 296 | "functions" write to the provided pointer. This means: (a) the pointer 297 | must be an lvalue, and (b) the pointer to the data structure is not 298 | stable, and you must maintain it the same as you would a realloc'd 299 | pointer. For example, if you pass a pointer to a dynamic array to a 300 | function which updates it, the function must return back the new 301 | pointer to the caller. This is the price of trying to do this in C. 302 | 303 | * The following are the only functions that are thread-safe on a single data 304 | structure, i.e. can be run in multiple threads simultaneously on the same 305 | data structure 306 | hmlen shlen 307 | hmlenu shlenu 308 | hmget_ts shget_ts 309 | hmgeti_ts shgeti_ts 310 | hmgets_ts shgets_ts 311 | 312 | * You iterate over the contents of a dynamic array and a hashmap in exactly 313 | the same way, using arrlen/hmlen/shlen: 314 | 315 | for (i=0; i < arrlen(foo); ++i) 316 | ... foo[i] ... 317 | 318 | * All operations except arrins/arrdel are O(1) amortized, but individual 319 | operations can be slow, so these data structures may not be suitable 320 | for real time use. Dynamic arrays double in capacity as needed, so 321 | elements are copied an average of once. Hash tables double/halve 322 | their size as needed, with appropriate hysteresis to maintain O(1) 323 | performance. 324 | 325 | NOTES - DYNAMIC ARRAY 326 | 327 | * If you know how long a dynamic array is going to be in advance, you can avoid 328 | extra memory allocations by using arrsetlen to allocate it to that length in 329 | advance and use foo[n] while filling it out, or arrsetcap to allocate the memory 330 | for that length and use arrput/arrpush as normal. 331 | 332 | * Unlike some other versions of the dynamic array, this version should 333 | be safe to use with strict-aliasing optimizations. 334 | 335 | NOTES - HASH MAP 336 | 337 | * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel 338 | and variants, the key must be an lvalue (so the macro can take the address of it). 339 | Extensions are used that eliminate this requirement if you're using C99 and later 340 | in GCC or clang, or if you're using C++ in GCC. But note that this can make your 341 | code less portable. 342 | 343 | * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. 344 | 345 | * The iteration order of your data in the hashmap is determined solely by the 346 | order of insertions and deletions. In particular, if you never delete, new 347 | keys are always added at the end of the array. This will be consistent 348 | across all platforms and versions of the library. However, you should not 349 | attempt to serialize the internal hash table, as the hash is not consistent 350 | between different platforms, and may change with future versions of the library. 351 | 352 | * Use sh_new_arena() for string hashmaps that you never delete from. Initialize 353 | with NULL if you're managing the memory for your strings, or your strings are 354 | never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup(). 355 | @TODO: make an arena variant that garbage collects the strings with a trivial 356 | copy collector into a new arena whenever the table shrinks / rebuilds. Since 357 | current arena recommendation is to only use arena if it never deletes, then 358 | this can just replace current arena implementation. 359 | 360 | * If adversarial input is a serious concern and you're on a 64-bit platform, 361 | enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass 362 | a strong random number to stbds_rand_seed. 363 | 364 | * The default value for the hash table is stored in foo[-1], so if you 365 | use code like 'hmget(T,k)->value = 5' you can accidentally overwrite 366 | the value stored by hmdefault if 'k' is not present. 367 | 368 | CREDITS 369 | 370 | Sean Barrett -- library, idea for dynamic array API/implementation 371 | Per Vognsen -- idea for hash table API/implementation 372 | Rafael Sachetto -- arrpop() 373 | github:HeroicKatora -- arraddn() reworking 374 | 375 | Bugfixes: 376 | Andy Durdin 377 | Shane Liesegang 378 | Vinh Truong 379 | Andreas Molzer 380 | github:hashitaku 381 | github:srdjanstipic 382 | Macoy Madson 383 | Andreas Vennstrom 384 | Tobias Mansfield-Williams 385 | */ 386 | 387 | #ifdef STBDS_UNIT_TESTS 388 | #define _CRT_SECURE_NO_WARNINGS 389 | #endif 390 | 391 | #ifndef INCLUDE_STB_DS_H 392 | #define INCLUDE_STB_DS_H 393 | 394 | #include 395 | #include 396 | 397 | #ifndef STBDS_NO_SHORT_NAMES 398 | #define arrlen stbds_arrlen 399 | #define arrlenu stbds_arrlenu 400 | #define arrput stbds_arrput 401 | #define arrpush stbds_arrput 402 | #define arrpop stbds_arrpop 403 | #define arrfree stbds_arrfree 404 | #define arraddn stbds_arraddn // deprecated, use one of the following instead: 405 | #define arraddnptr stbds_arraddnptr 406 | #define arraddnindex stbds_arraddnindex 407 | #define arrsetlen stbds_arrsetlen 408 | #define arrlast stbds_arrlast 409 | #define arrins stbds_arrins 410 | #define arrinsn stbds_arrinsn 411 | #define arrdel stbds_arrdel 412 | #define arrdeln stbds_arrdeln 413 | #define arrdelswap stbds_arrdelswap 414 | #define arrcap stbds_arrcap 415 | #define arrsetcap stbds_arrsetcap 416 | 417 | #define hmput stbds_hmput 418 | #define hmputs stbds_hmputs 419 | #define hmget stbds_hmget 420 | #define hmget_ts stbds_hmget_ts 421 | #define hmgets stbds_hmgets 422 | #define hmgetp stbds_hmgetp 423 | #define hmgetp_ts stbds_hmgetp_ts 424 | #define hmgetp_null stbds_hmgetp_null 425 | #define hmgeti stbds_hmgeti 426 | #define hmgeti_ts stbds_hmgeti_ts 427 | #define hmdel stbds_hmdel 428 | #define hmlen stbds_hmlen 429 | #define hmlenu stbds_hmlenu 430 | #define hmfree stbds_hmfree 431 | #define hmdefault stbds_hmdefault 432 | #define hmdefaults stbds_hmdefaults 433 | 434 | #define shput stbds_shput 435 | #define shputi stbds_shputi 436 | #define shputs stbds_shputs 437 | #define shget stbds_shget 438 | #define shgeti stbds_shgeti 439 | #define shgets stbds_shgets 440 | #define shgetp stbds_shgetp 441 | #define shgetp_null stbds_shgetp_null 442 | #define shdel stbds_shdel 443 | #define shlen stbds_shlen 444 | #define shlenu stbds_shlenu 445 | #define shfree stbds_shfree 446 | #define shdefault stbds_shdefault 447 | #define shdefaults stbds_shdefaults 448 | #define sh_new_arena stbds_sh_new_arena 449 | #define sh_new_strdup stbds_sh_new_strdup 450 | 451 | #define stralloc stbds_stralloc 452 | #define strreset stbds_strreset 453 | #endif 454 | 455 | #if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE) 456 | #error "You must define both STBDS_REALLOC and STBDS_FREE, or neither." 457 | #endif 458 | #if !defined(STBDS_REALLOC) && !defined(STBDS_FREE) 459 | #include 460 | #define STBDS_REALLOC(c,p,s) realloc(p,s) 461 | #define STBDS_FREE(c,p) free(p) 462 | #endif 463 | 464 | #ifdef _MSC_VER 465 | #define STBDS_NOTUSED(v) (void)(v) 466 | #else 467 | #define STBDS_NOTUSED(v) (void)sizeof(v) 468 | #endif 469 | 470 | #ifdef __cplusplus 471 | extern "C" { 472 | #endif 473 | 474 | // for security against attackers, seed the library with a random number, at least time() but stronger is better 475 | extern void stbds_rand_seed(size_t seed); 476 | 477 | // these are the hash functions used internally if you want to test them or use them for other purposes 478 | extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed); 479 | extern size_t stbds_hash_string(char *str, size_t seed); 480 | 481 | // this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'. 482 | typedef struct stbds_string_arena stbds_string_arena; 483 | extern char * stbds_stralloc(stbds_string_arena *a, char *str); 484 | extern void stbds_strreset(stbds_string_arena *a); 485 | 486 | // have to #define STBDS_UNIT_TESTS to call this 487 | extern void stbds_unit_tests(void); 488 | 489 | /////////////// 490 | // 491 | // Everything below here is implementation details 492 | // 493 | 494 | extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap); 495 | extern void stbds_arrfreef(void *a); 496 | extern void stbds_hmfree_func(void *p, size_t elemsize); 497 | extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); 498 | extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode); 499 | extern void * stbds_hmput_default(void *a, size_t elemsize); 500 | extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); 501 | extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode); 502 | extern void * stbds_shmode_func(size_t elemsize, int mode); 503 | 504 | #ifdef __cplusplus 505 | } 506 | #endif 507 | 508 | #if defined(__GNUC__) || defined(__clang__) 509 | #define STBDS_HAS_TYPEOF 510 | #ifdef __cplusplus 511 | //#define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang 512 | #endif 513 | #endif 514 | 515 | #if !defined(__cplusplus) 516 | #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 517 | #define STBDS_HAS_LITERAL_ARRAY 518 | #endif 519 | #endif 520 | 521 | // this macro takes the address of the argument, but on gcc/clang can accept rvalues 522 | #if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF) 523 | #if __clang__ 524 | #define STBDS_ADDRESSOF(typevar, value) ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value 525 | #else 526 | #define STBDS_ADDRESSOF(typevar, value) ((typeof(typevar)[1]){value}) // literal array decays to pointer to value 527 | #endif 528 | #else 529 | #define STBDS_ADDRESSOF(typevar, value) &(value) 530 | #endif 531 | 532 | #define STBDS_OFFSETOF(var,field) ((char *) &(var)->field - (char *) (var)) 533 | 534 | #define stbds_header(t) ((stbds_array_header *) (t) - 1) 535 | #define stbds_temp(t) stbds_header(t)->temp 536 | #define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table) 537 | 538 | #define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n)) 539 | #define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0) 540 | #define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0) 541 | #define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0) 542 | #define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0) 543 | #define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v)) 544 | #define stbds_arrpush stbds_arrput // synonym 545 | #define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) 546 | #define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead: 547 | #define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a)) 548 | #define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a)) 549 | #define stbds_arraddnoff stbds_arraddnindex 550 | #define stbds_arrlast(a) ((a)[stbds_header(a)->length-1]) 551 | #define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL) 552 | #define stbds_arrdel(a,i) stbds_arrdeln(a,i,1) 553 | #define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n)) 554 | #define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1) 555 | #define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i)))) 556 | #define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v)) 557 | 558 | #define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \ 559 | ? (stbds_arrgrow(a,n,0),0) : 0) 560 | 561 | #define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c))) 562 | 563 | #define stbds_hmput(t, k, v) \ 564 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \ 565 | (t)[stbds_temp((t)-1)].key = (k), \ 566 | (t)[stbds_temp((t)-1)].value = (v)) 567 | 568 | #define stbds_hmputs(t, s) \ 569 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \ 570 | (t)[stbds_temp((t)-1)] = (s)) 571 | 572 | #define stbds_hmgeti(t,k) \ 573 | ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \ 574 | stbds_temp((t)-1)) 575 | 576 | #define stbds_hmgeti_ts(t,k,temp) \ 577 | ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \ 578 | (temp)) 579 | 580 | #define stbds_hmgetp(t, k) \ 581 | ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)]) 582 | 583 | #define stbds_hmgetp_ts(t, k, temp) \ 584 | ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp]) 585 | 586 | #define stbds_hmdel(t,k) \ 587 | (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0) 588 | 589 | #define stbds_hmdefault(t, v) \ 590 | ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v)) 591 | 592 | #define stbds_hmdefaults(t, s) \ 593 | ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s)) 594 | 595 | #define stbds_hmfree(p) \ 596 | ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL) 597 | 598 | #define stbds_hmgets(t, k) (*stbds_hmgetp(t,k)) 599 | #define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) 600 | #define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value) 601 | #define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0) 602 | #define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0) 603 | #define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) 604 | 605 | #define stbds_shput(t, k, v) \ 606 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ 607 | (t)[stbds_temp((t)-1)].value = (v)) 608 | 609 | #define stbds_shputi(t, k, v) \ 610 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ 611 | (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1)) 612 | 613 | #define stbds_shputs(t, s) \ 614 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \ 615 | (t)[stbds_temp((t)-1)] = (s), \ 616 | (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally 617 | 618 | #define stbds_pshput(t, p) \ 619 | ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \ 620 | (t)[stbds_temp((t)-1)] = (p)) 621 | 622 | #define stbds_shgeti(t,k) \ 623 | ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ 624 | stbds_temp((t)-1)) 625 | 626 | #define stbds_pshgeti(t,k) \ 627 | ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \ 628 | stbds_temp((t)-1)) 629 | 630 | #define stbds_shgetp(t, k) \ 631 | ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)]) 632 | 633 | #define stbds_pshget(t, k) \ 634 | ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)]) 635 | 636 | #define stbds_shdel(t,k) \ 637 | (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0) 638 | #define stbds_pshdel(t,k) \ 639 | (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0) 640 | 641 | #define stbds_sh_new_arena(t) \ 642 | ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) 643 | #define stbds_sh_new_strdup(t) \ 644 | ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) 645 | 646 | #define stbds_shdefault(t, v) stbds_hmdefault(t,v) 647 | #define stbds_shdefaults(t, s) stbds_hmdefaults(t,s) 648 | 649 | #define stbds_shfree stbds_hmfree 650 | #define stbds_shlenu stbds_hmlenu 651 | 652 | #define stbds_shgets(t, k) (*stbds_shgetp(t,k)) 653 | #define stbds_shget(t, k) (stbds_shgetp(t,k)->value) 654 | #define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) 655 | #define stbds_shlen stbds_hmlen 656 | 657 | typedef struct 658 | { 659 | size_t length; 660 | size_t capacity; 661 | void * hash_table; 662 | ptrdiff_t temp; 663 | } stbds_array_header; 664 | 665 | typedef struct stbds_string_block 666 | { 667 | struct stbds_string_block *next; 668 | char storage[8]; 669 | } stbds_string_block; 670 | 671 | struct stbds_string_arena 672 | { 673 | stbds_string_block *storage; 674 | size_t remaining; 675 | unsigned char block; 676 | unsigned char mode; // this isn't used by the string arena itself 677 | }; 678 | 679 | #define STBDS_HM_BINARY 0 680 | #define STBDS_HM_STRING 1 681 | 682 | enum 683 | { 684 | STBDS_SH_NONE, 685 | STBDS_SH_DEFAULT, 686 | STBDS_SH_STRDUP, 687 | STBDS_SH_ARENA 688 | }; 689 | 690 | #ifdef __cplusplus 691 | // in C we use implicit assignment from these void*-returning functions to T*. 692 | // in C++ these templates make the same code work 693 | template static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) { 694 | return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap); 695 | } 696 | template static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { 697 | return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode); 698 | } 699 | template static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) { 700 | return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode); 701 | } 702 | template static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) { 703 | return (T*)stbds_hmput_default((void *)a, elemsize); 704 | } 705 | template static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { 706 | return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode); 707 | } 708 | template static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){ 709 | return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode); 710 | } 711 | template static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) { 712 | return (T*)stbds_shmode_func(elemsize, mode); 713 | } 714 | #else 715 | #define stbds_arrgrowf_wrapper stbds_arrgrowf 716 | #define stbds_hmget_key_wrapper stbds_hmget_key 717 | #define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts 718 | #define stbds_hmput_default_wrapper stbds_hmput_default 719 | #define stbds_hmput_key_wrapper stbds_hmput_key 720 | #define stbds_hmdel_key_wrapper stbds_hmdel_key 721 | #define stbds_shmode_func_wrapper(t,e,m) stbds_shmode_func(e,m) 722 | #endif 723 | 724 | #endif // INCLUDE_STB_DS_H 725 | 726 | 727 | ////////////////////////////////////////////////////////////////////////////// 728 | // 729 | // IMPLEMENTATION 730 | // 731 | 732 | #ifdef STB_DS_IMPLEMENTATION 733 | #include 734 | #include 735 | 736 | #ifndef STBDS_ASSERT 737 | #define STBDS_ASSERT_WAS_UNDEFINED 738 | #define STBDS_ASSERT(x) ((void) 0) 739 | #endif 740 | 741 | #ifdef STBDS_STATISTICS 742 | #define STBDS_STATS(x) x 743 | size_t stbds_array_grow; 744 | size_t stbds_hash_grow; 745 | size_t stbds_hash_shrink; 746 | size_t stbds_hash_rebuild; 747 | size_t stbds_hash_probes; 748 | size_t stbds_hash_alloc; 749 | size_t stbds_rehash_probes; 750 | size_t stbds_rehash_items; 751 | #else 752 | #define STBDS_STATS(x) 753 | #endif 754 | 755 | // 756 | // stbds_arr implementation 757 | // 758 | 759 | //int *prev_allocs[65536]; 760 | //int num_prev; 761 | 762 | void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) 763 | { 764 | stbds_array_header temp={0}; // force debugging 765 | void *b; 766 | size_t min_len = stbds_arrlen(a) + addlen; 767 | (void) sizeof(temp); 768 | 769 | // compute the minimum capacity needed 770 | if (min_len > min_cap) 771 | min_cap = min_len; 772 | 773 | if (min_cap <= stbds_arrcap(a)) 774 | return a; 775 | 776 | // increase needed capacity to guarantee O(1) amortized 777 | if (min_cap < 2 * stbds_arrcap(a)) 778 | min_cap = 2 * stbds_arrcap(a); 779 | else if (min_cap < 4) 780 | min_cap = 4; 781 | 782 | //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1); 783 | //if (num_prev == 2201) 784 | // num_prev = num_prev; 785 | b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header)); 786 | //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b; 787 | b = (char *) b + sizeof(stbds_array_header); 788 | if (a == NULL) { 789 | stbds_header(b)->length = 0; 790 | stbds_header(b)->hash_table = 0; 791 | stbds_header(b)->temp = 0; 792 | } else { 793 | STBDS_STATS(++stbds_array_grow); 794 | } 795 | stbds_header(b)->capacity = min_cap; 796 | 797 | return b; 798 | } 799 | 800 | void stbds_arrfreef(void *a) 801 | { 802 | STBDS_FREE(NULL, stbds_header(a)); 803 | } 804 | 805 | // 806 | // stbds_hm hash table implementation 807 | // 808 | 809 | #ifdef STBDS_INTERNAL_SMALL_BUCKET 810 | #define STBDS_BUCKET_LENGTH 4 811 | #else 812 | #define STBDS_BUCKET_LENGTH 8 813 | #endif 814 | 815 | #define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2) 816 | #define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH-1) 817 | #define STBDS_CACHE_LINE_SIZE 64 818 | 819 | #define STBDS_ALIGN_FWD(n,a) (((n) + (a) - 1) & ~((a)-1)) 820 | 821 | typedef struct 822 | { 823 | size_t hash [STBDS_BUCKET_LENGTH]; 824 | ptrdiff_t index[STBDS_BUCKET_LENGTH]; 825 | } stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line 826 | 827 | typedef struct 828 | { 829 | char * temp_key; // this MUST be the first field of the hash table 830 | size_t slot_count; 831 | size_t used_count; 832 | size_t used_count_threshold; 833 | size_t used_count_shrink_threshold; 834 | size_t tombstone_count; 835 | size_t tombstone_count_threshold; 836 | size_t seed; 837 | size_t slot_count_log2; 838 | stbds_string_arena string; 839 | stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct 840 | } stbds_hash_index; 841 | 842 | #define STBDS_INDEX_EMPTY -1 843 | #define STBDS_INDEX_DELETED -2 844 | #define STBDS_INDEX_IN_USE(x) ((x) >= 0) 845 | 846 | #define STBDS_HASH_EMPTY 0 847 | #define STBDS_HASH_DELETED 1 848 | 849 | static size_t stbds_hash_seed=0x31415926; 850 | 851 | void stbds_rand_seed(size_t seed) 852 | { 853 | stbds_hash_seed = seed; 854 | } 855 | 856 | #define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \ 857 | temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */ \ 858 | var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \ 859 | var ^= temp ^ v32 860 | 861 | #define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8) 862 | 863 | static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2) 864 | { 865 | size_t pos; 866 | STBDS_NOTUSED(slot_log2); 867 | pos = hash & (slot_count-1); 868 | #ifdef STBDS_INTERNAL_BUCKET_START 869 | pos &= ~STBDS_BUCKET_MASK; 870 | #endif 871 | return pos; 872 | } 873 | 874 | static size_t stbds_log2(size_t slot_count) 875 | { 876 | size_t n=0; 877 | while (slot_count > 1) { 878 | slot_count >>= 1; 879 | ++n; 880 | } 881 | return n; 882 | } 883 | 884 | static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot) 885 | { 886 | stbds_hash_index *t; 887 | t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1); 888 | t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE); 889 | t->slot_count = slot_count; 890 | t->slot_count_log2 = stbds_log2(slot_count); 891 | t->tombstone_count = 0; 892 | t->used_count = 0; 893 | 894 | #if 0 // A1 895 | t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow 896 | t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild 897 | t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink 898 | #elif 1 // A2 899 | //t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow 900 | //t->tombstone_count_threshold = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild 901 | //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink 902 | 903 | // compute without overflowing 904 | t->used_count_threshold = slot_count - (slot_count>>2); 905 | t->tombstone_count_threshold = (slot_count>>3) + (slot_count>>4); 906 | t->used_count_shrink_threshold = slot_count >> 2; 907 | 908 | #elif 0 // B1 909 | t->used_count_threshold = slot_count*13/16; // if 13/16th of table is occupied, grow 910 | t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild 911 | t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink 912 | #else // C1 913 | t->used_count_threshold = slot_count*14/16; // if 14/16th of table is occupied, grow 914 | t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild 915 | t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink 916 | #endif 917 | // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 918 | // Note that the larger tables have high variance as they were run fewer times 919 | // A1 A2 B1 C1 920 | // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table 921 | // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table 922 | // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts creating 200K table 923 | // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts creating 2M table 924 | // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts creating 20M table 925 | // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & deletes in 2K table 926 | // 72.78ms : 62.45ms : 71.95ms : 72.85ms : 500,000 inserts & deletes in 20K table 927 | // 89.47ms : 77.72ms : 96.49ms : 96.75ms : 500,000 inserts & deletes in 200K table 928 | // 97.58ms : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M table 929 | // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & deletes in 20M table 930 | // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table 931 | 932 | if (slot_count <= STBDS_BUCKET_LENGTH) 933 | t->used_count_shrink_threshold = 0; 934 | // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes 935 | STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count); 936 | STBDS_STATS(++stbds_hash_alloc); 937 | if (ot) { 938 | t->string = ot->string; 939 | // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing 940 | t->seed = ot->seed; 941 | } else { 942 | size_t a,b,temp; 943 | memset(&t->string, 0, sizeof(t->string)); 944 | t->seed = stbds_hash_seed; 945 | // LCG 946 | // in 32-bit, a = 2147001325 b = 715136305 947 | // in 64-bit, a = 2862933555777941757 b = 3037000493 948 | stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); 949 | stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d); 950 | stbds_hash_seed = stbds_hash_seed * a + b; 951 | } 952 | 953 | { 954 | size_t i,j; 955 | for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { 956 | stbds_hash_bucket *b = &t->storage[i]; 957 | for (j=0; j < STBDS_BUCKET_LENGTH; ++j) 958 | b->hash[j] = STBDS_HASH_EMPTY; 959 | for (j=0; j < STBDS_BUCKET_LENGTH; ++j) 960 | b->index[j] = STBDS_INDEX_EMPTY; 961 | } 962 | } 963 | 964 | // copy out the old data, if any 965 | if (ot) { 966 | size_t i,j; 967 | t->used_count = ot->used_count; 968 | for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { 969 | stbds_hash_bucket *ob = &ot->storage[i]; 970 | for (j=0; j < STBDS_BUCKET_LENGTH; ++j) { 971 | if (STBDS_INDEX_IN_USE(ob->index[j])) { 972 | size_t hash = ob->hash[j]; 973 | size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2); 974 | size_t step = STBDS_BUCKET_LENGTH; 975 | STBDS_STATS(++stbds_rehash_items); 976 | for (;;) { 977 | size_t limit,z; 978 | stbds_hash_bucket *bucket; 979 | bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; 980 | STBDS_STATS(++stbds_rehash_probes); 981 | 982 | for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) { 983 | if (bucket->hash[z] == 0) { 984 | bucket->hash[z] = hash; 985 | bucket->index[z] = ob->index[j]; 986 | goto done; 987 | } 988 | } 989 | 990 | limit = pos & STBDS_BUCKET_MASK; 991 | for (z = 0; z < limit; ++z) { 992 | if (bucket->hash[z] == 0) { 993 | bucket->hash[z] = hash; 994 | bucket->index[z] = ob->index[j]; 995 | goto done; 996 | } 997 | } 998 | 999 | pos += step; // quadratic probing 1000 | step += STBDS_BUCKET_LENGTH; 1001 | pos &= (t->slot_count-1); 1002 | } 1003 | } 1004 | done: 1005 | ; 1006 | } 1007 | } 1008 | } 1009 | 1010 | return t; 1011 | } 1012 | 1013 | #define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n)))) 1014 | #define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n)))) 1015 | 1016 | size_t stbds_hash_string(char *str, size_t seed) 1017 | { 1018 | size_t hash = seed; 1019 | while (*str) 1020 | hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++; 1021 | 1022 | // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits 1023 | hash ^= seed; 1024 | hash = (~hash) + (hash << 18); 1025 | hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31); 1026 | hash = hash * 21; 1027 | hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11); 1028 | hash += (hash << 6); 1029 | hash ^= STBDS_ROTATE_RIGHT(hash,22); 1030 | return hash+seed; 1031 | } 1032 | 1033 | #ifdef STBDS_SIPHASH_2_4 1034 | #define STBDS_SIPHASH_C_ROUNDS 2 1035 | #define STBDS_SIPHASH_D_ROUNDS 4 1036 | typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1]; 1037 | #endif 1038 | 1039 | #ifndef STBDS_SIPHASH_C_ROUNDS 1040 | #define STBDS_SIPHASH_C_ROUNDS 1 1041 | #endif 1042 | #ifndef STBDS_SIPHASH_D_ROUNDS 1043 | #define STBDS_SIPHASH_D_ROUNDS 1 1044 | #endif 1045 | 1046 | #ifdef _MSC_VER 1047 | #pragma warning(push) 1048 | #pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()== 1049 | #endif 1050 | 1051 | static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) 1052 | { 1053 | unsigned char *d = (unsigned char *) p; 1054 | size_t i,j; 1055 | size_t v0,v1,v2,v3, data; 1056 | 1057 | // hash that works on 32- or 64-bit registers without knowing which we have 1058 | // (computes different results on 32-bit and 64-bit platform) 1059 | // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit 1060 | v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed; 1061 | v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed; 1062 | v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed; 1063 | v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed; 1064 | 1065 | #ifdef STBDS_TEST_SIPHASH_2_4 1066 | // hardcoded with key material in the siphash test vectors 1067 | v0 ^= 0x0706050403020100ull ^ seed; 1068 | v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; 1069 | v2 ^= 0x0706050403020100ull ^ seed; 1070 | v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; 1071 | #endif 1072 | 1073 | #define STBDS_SIPROUND() \ 1074 | do { \ 1075 | v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \ 1076 | v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \ 1077 | v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \ 1078 | v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \ 1079 | } while (0) 1080 | 1081 | for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) { 1082 | data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); 1083 | data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 1084 | 1085 | v3 ^= data; 1086 | for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) 1087 | STBDS_SIPROUND(); 1088 | v0 ^= data; 1089 | } 1090 | data = len << (STBDS_SIZE_T_BITS-8); 1091 | switch (len - i) { 1092 | case 7: data |= ((size_t) d[6] << 24) << 24; // fall through 1093 | case 6: data |= ((size_t) d[5] << 20) << 20; // fall through 1094 | case 5: data |= ((size_t) d[4] << 16) << 16; // fall through 1095 | case 4: data |= (d[3] << 24); // fall through 1096 | case 3: data |= (d[2] << 16); // fall through 1097 | case 2: data |= (d[1] << 8); // fall through 1098 | case 1: data |= d[0]; // fall through 1099 | case 0: break; 1100 | } 1101 | v3 ^= data; 1102 | for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) 1103 | STBDS_SIPROUND(); 1104 | v0 ^= data; 1105 | v2 ^= 0xff; 1106 | for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) 1107 | STBDS_SIPROUND(); 1108 | 1109 | #ifdef STBDS_SIPHASH_2_4 1110 | return v0^v1^v2^v3; 1111 | #else 1112 | return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply 1113 | #endif 1114 | } 1115 | 1116 | size_t stbds_hash_bytes(void *p, size_t len, size_t seed) 1117 | { 1118 | #ifdef STBDS_SIPHASH_2_4 1119 | return stbds_siphash_bytes(p,len,seed); 1120 | #else 1121 | unsigned char *d = (unsigned char *) p; 1122 | 1123 | if (len == 4) { 1124 | unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); 1125 | #if 0 1126 | // HASH32-A Bob Jenkin's hash function w/o large constants 1127 | hash ^= seed; 1128 | hash -= (hash<<6); 1129 | hash ^= (hash>>17); 1130 | hash -= (hash<<9); 1131 | hash ^= seed; 1132 | hash ^= (hash<<4); 1133 | hash -= (hash<<3); 1134 | hash ^= (hash<<10); 1135 | hash ^= (hash>>15); 1136 | #elif 1 1137 | // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts. 1138 | // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm 1139 | // not really sure what's going on. 1140 | hash ^= seed; 1141 | hash = (hash ^ 61) ^ (hash >> 16); 1142 | hash = hash + (hash << 3); 1143 | hash = hash ^ (hash >> 4); 1144 | hash = hash * 0x27d4eb2d; 1145 | hash ^= seed; 1146 | hash = hash ^ (hash >> 15); 1147 | #else // HASH32-C - Murmur3 1148 | hash ^= seed; 1149 | hash *= 0xcc9e2d51; 1150 | hash = (hash << 17) | (hash >> 15); 1151 | hash *= 0x1b873593; 1152 | hash ^= seed; 1153 | hash = (hash << 19) | (hash >> 13); 1154 | hash = hash*5 + 0xe6546b64; 1155 | hash ^= hash >> 16; 1156 | hash *= 0x85ebca6b; 1157 | hash ^= seed; 1158 | hash ^= hash >> 13; 1159 | hash *= 0xc2b2ae35; 1160 | hash ^= hash >> 16; 1161 | #endif 1162 | // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 1163 | // Note that the larger tables have high variance as they were run fewer times 1164 | // HASH32-A // HASH32-BB // HASH32-C 1165 | // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table 1166 | // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table 1167 | // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table 1168 | // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table 1169 | // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table 1170 | // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table 1171 | // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table 1172 | // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table 1173 | // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table 1174 | // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table 1175 | // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table 1176 | // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing 1177 | 1178 | return (((size_t) hash << 16 << 16) | hash) ^ seed; 1179 | } else if (len == 8 && sizeof(size_t) == 8) { 1180 | size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); 1181 | hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 1182 | hash ^= seed; 1183 | hash = (~hash) + (hash << 21); 1184 | hash ^= STBDS_ROTATE_RIGHT(hash,24); 1185 | hash *= 265; 1186 | hash ^= STBDS_ROTATE_RIGHT(hash,14); 1187 | hash ^= seed; 1188 | hash *= 21; 1189 | hash ^= STBDS_ROTATE_RIGHT(hash,28); 1190 | hash += (hash << 31); 1191 | hash = (~hash) + (hash << 18); 1192 | return hash; 1193 | } else { 1194 | return stbds_siphash_bytes(p,len,seed); 1195 | } 1196 | #endif 1197 | } 1198 | #ifdef _MSC_VER 1199 | #pragma warning(pop) 1200 | #endif 1201 | 1202 | 1203 | static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i) 1204 | { 1205 | if (mode >= STBDS_HM_STRING) 1206 | return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset)); 1207 | else 1208 | return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize); 1209 | } 1210 | 1211 | #define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize)) 1212 | #define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize)) 1213 | 1214 | #define stbds_hash_table(a) ((stbds_hash_index *) stbds_header(a)->hash_table) 1215 | 1216 | void stbds_hmfree_func(void *a, size_t elemsize) 1217 | { 1218 | if (a == NULL) return; 1219 | if (stbds_hash_table(a) != NULL) { 1220 | if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { 1221 | size_t i; 1222 | // skip 0th element, which is default 1223 | for (i=1; i < stbds_header(a)->length; ++i) 1224 | STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); 1225 | } 1226 | stbds_strreset(&stbds_hash_table(a)->string); 1227 | } 1228 | STBDS_FREE(NULL, stbds_header(a)->hash_table); 1229 | STBDS_FREE(NULL, stbds_header(a)); 1230 | } 1231 | 1232 | static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) 1233 | { 1234 | void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); 1235 | stbds_hash_index *table = stbds_hash_table(raw_a); 1236 | size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); 1237 | size_t step = STBDS_BUCKET_LENGTH; 1238 | size_t limit,i; 1239 | size_t pos; 1240 | stbds_hash_bucket *bucket; 1241 | 1242 | if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots 1243 | 1244 | pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); 1245 | 1246 | for (;;) { 1247 | STBDS_STATS(++stbds_hash_probes); 1248 | bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; 1249 | 1250 | // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache 1251 | for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { 1252 | if (bucket->hash[i] == hash) { 1253 | if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { 1254 | return (pos & ~STBDS_BUCKET_MASK)+i; 1255 | } 1256 | } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { 1257 | return -1; 1258 | } 1259 | } 1260 | 1261 | // search from beginning of bucket to pos 1262 | limit = pos & STBDS_BUCKET_MASK; 1263 | for (i = 0; i < limit; ++i) { 1264 | if (bucket->hash[i] == hash) { 1265 | if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { 1266 | return (pos & ~STBDS_BUCKET_MASK)+i; 1267 | } 1268 | } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { 1269 | return -1; 1270 | } 1271 | } 1272 | 1273 | // quadratic probing 1274 | pos += step; 1275 | step += STBDS_BUCKET_LENGTH; 1276 | pos &= (table->slot_count-1); 1277 | } 1278 | /* NOTREACHED */ 1279 | } 1280 | 1281 | void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) 1282 | { 1283 | size_t keyoffset = 0; 1284 | if (a == NULL) { 1285 | // make it non-empty so we can return a temp 1286 | a = stbds_arrgrowf(0, elemsize, 0, 1); 1287 | stbds_header(a)->length += 1; 1288 | memset(a, 0, elemsize); 1289 | *temp = STBDS_INDEX_EMPTY; 1290 | // adjust a to point after the default element 1291 | return STBDS_ARR_TO_HASH(a,elemsize); 1292 | } else { 1293 | stbds_hash_index *table; 1294 | void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); 1295 | // adjust a to point to the default element 1296 | table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; 1297 | if (table == 0) { 1298 | *temp = -1; 1299 | } else { 1300 | ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); 1301 | if (slot < 0) { 1302 | *temp = STBDS_INDEX_EMPTY; 1303 | } else { 1304 | stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; 1305 | *temp = b->index[slot & STBDS_BUCKET_MASK]; 1306 | } 1307 | } 1308 | return a; 1309 | } 1310 | } 1311 | 1312 | void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) 1313 | { 1314 | ptrdiff_t temp; 1315 | void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode); 1316 | stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp; 1317 | return p; 1318 | } 1319 | 1320 | void * stbds_hmput_default(void *a, size_t elemsize) 1321 | { 1322 | // three cases: 1323 | // a is NULL <- allocate 1324 | // a has a hash table but no entries, because of shmode <- grow 1325 | // a has entries <- do nothing 1326 | if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) { 1327 | a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1); 1328 | stbds_header(a)->length += 1; 1329 | memset(a, 0, elemsize); 1330 | a=STBDS_ARR_TO_HASH(a,elemsize); 1331 | } 1332 | return a; 1333 | } 1334 | 1335 | static char *stbds_strdup(char *str); 1336 | 1337 | void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) 1338 | { 1339 | size_t keyoffset=0; 1340 | void *raw_a; 1341 | stbds_hash_index *table; 1342 | 1343 | if (a == NULL) { 1344 | a = stbds_arrgrowf(0, elemsize, 0, 1); 1345 | memset(a, 0, elemsize); 1346 | stbds_header(a)->length += 1; 1347 | // adjust a to point AFTER the default element 1348 | a = STBDS_ARR_TO_HASH(a,elemsize); 1349 | } 1350 | 1351 | // adjust a to point to the default element 1352 | raw_a = a; 1353 | a = STBDS_HASH_TO_ARR(a,elemsize); 1354 | 1355 | table = (stbds_hash_index *) stbds_header(a)->hash_table; 1356 | 1357 | if (table == NULL || table->used_count >= table->used_count_threshold) { 1358 | stbds_hash_index *nt; 1359 | size_t slot_count; 1360 | 1361 | slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2; 1362 | nt = stbds_make_hash_index(slot_count, table); 1363 | if (table) 1364 | STBDS_FREE(NULL, table); 1365 | else 1366 | nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0; 1367 | stbds_header(a)->hash_table = table = nt; 1368 | STBDS_STATS(++stbds_hash_grow); 1369 | } 1370 | 1371 | // we iterate hash table explicitly because we want to track if we saw a tombstone 1372 | { 1373 | size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); 1374 | size_t step = STBDS_BUCKET_LENGTH; 1375 | size_t pos; 1376 | ptrdiff_t tombstone = -1; 1377 | stbds_hash_bucket *bucket; 1378 | 1379 | // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly 1380 | if (hash < 2) hash += 2; 1381 | 1382 | pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); 1383 | 1384 | for (;;) { 1385 | size_t limit, i; 1386 | STBDS_STATS(++stbds_hash_probes); 1387 | bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; 1388 | 1389 | // start searching from pos to end of bucket 1390 | for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { 1391 | if (bucket->hash[i] == hash) { 1392 | if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { 1393 | stbds_temp(a) = bucket->index[i]; 1394 | if (mode >= STBDS_HM_STRING) 1395 | stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset); 1396 | return STBDS_ARR_TO_HASH(a,elemsize); 1397 | } 1398 | } else if (bucket->hash[i] == 0) { 1399 | pos = (pos & ~STBDS_BUCKET_MASK) + i; 1400 | goto found_empty_slot; 1401 | } else if (tombstone < 0) { 1402 | if (bucket->index[i] == STBDS_INDEX_DELETED) 1403 | tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); 1404 | } 1405 | } 1406 | 1407 | // search from beginning of bucket to pos 1408 | limit = pos & STBDS_BUCKET_MASK; 1409 | for (i = 0; i < limit; ++i) { 1410 | if (bucket->hash[i] == hash) { 1411 | if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { 1412 | stbds_temp(a) = bucket->index[i]; 1413 | return STBDS_ARR_TO_HASH(a,elemsize); 1414 | } 1415 | } else if (bucket->hash[i] == 0) { 1416 | pos = (pos & ~STBDS_BUCKET_MASK) + i; 1417 | goto found_empty_slot; 1418 | } else if (tombstone < 0) { 1419 | if (bucket->index[i] == STBDS_INDEX_DELETED) 1420 | tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); 1421 | } 1422 | } 1423 | 1424 | // quadratic probing 1425 | pos += step; 1426 | step += STBDS_BUCKET_LENGTH; 1427 | pos &= (table->slot_count-1); 1428 | } 1429 | found_empty_slot: 1430 | if (tombstone >= 0) { 1431 | pos = tombstone; 1432 | --table->tombstone_count; 1433 | } 1434 | ++table->used_count; 1435 | 1436 | { 1437 | ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a); 1438 | // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type 1439 | if ((size_t) i+1 > stbds_arrcap(a)) 1440 | *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0); 1441 | raw_a = STBDS_ARR_TO_HASH(a,elemsize); 1442 | 1443 | STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a)); 1444 | stbds_header(a)->length = i+1; 1445 | bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; 1446 | bucket->hash[pos & STBDS_BUCKET_MASK] = hash; 1447 | bucket->index[pos & STBDS_BUCKET_MASK] = i-1; 1448 | stbds_temp(a) = i-1; 1449 | 1450 | switch (table->string.mode) { 1451 | case STBDS_SH_STRDUP: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break; 1452 | case STBDS_SH_ARENA: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break; 1453 | case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break; 1454 | default: memcpy((char *) a + elemsize*i, key, keysize); break; 1455 | } 1456 | } 1457 | return STBDS_ARR_TO_HASH(a,elemsize); 1458 | } 1459 | } 1460 | 1461 | void * stbds_shmode_func(size_t elemsize, int mode) 1462 | { 1463 | void *a = stbds_arrgrowf(0, elemsize, 0, 1); 1464 | stbds_hash_index *h; 1465 | memset(a, 0, elemsize); 1466 | stbds_header(a)->length = 1; 1467 | stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL); 1468 | h->string.mode = (unsigned char) mode; 1469 | return STBDS_ARR_TO_HASH(a,elemsize); 1470 | } 1471 | 1472 | void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) 1473 | { 1474 | if (a == NULL) { 1475 | return 0; 1476 | } else { 1477 | stbds_hash_index *table; 1478 | void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); 1479 | table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; 1480 | stbds_temp(raw_a) = 0; 1481 | if (table == 0) { 1482 | return a; 1483 | } else { 1484 | ptrdiff_t slot; 1485 | slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); 1486 | if (slot < 0) 1487 | return a; 1488 | else { 1489 | stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; 1490 | int i = slot & STBDS_BUCKET_MASK; 1491 | ptrdiff_t old_index = b->index[i]; 1492 | ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last' 1493 | STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count); 1494 | --table->used_count; 1495 | ++table->tombstone_count; 1496 | stbds_temp(raw_a) = 1; 1497 | STBDS_ASSERT(table->used_count >= 0); 1498 | //STBDS_ASSERT(table->tombstone_count < table->slot_count/4); 1499 | b->hash[i] = STBDS_HASH_DELETED; 1500 | b->index[i] = STBDS_INDEX_DELETED; 1501 | 1502 | if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP) 1503 | STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index)); 1504 | 1505 | // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip 1506 | if (old_index != final_index) { 1507 | // swap delete 1508 | memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize); 1509 | 1510 | // now find the slot for the last element 1511 | if (mode == STBDS_HM_STRING) 1512 | slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode); 1513 | else 1514 | slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode); 1515 | STBDS_ASSERT(slot >= 0); 1516 | b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; 1517 | i = slot & STBDS_BUCKET_MASK; 1518 | STBDS_ASSERT(b->index[i] == final_index); 1519 | b->index[i] = old_index; 1520 | } 1521 | stbds_header(raw_a)->length -= 1; 1522 | 1523 | if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) { 1524 | stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table); 1525 | STBDS_FREE(NULL, table); 1526 | STBDS_STATS(++stbds_hash_shrink); 1527 | } else if (table->tombstone_count > table->tombstone_count_threshold) { 1528 | stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count , table); 1529 | STBDS_FREE(NULL, table); 1530 | STBDS_STATS(++stbds_hash_rebuild); 1531 | } 1532 | 1533 | return a; 1534 | } 1535 | } 1536 | } 1537 | /* NOTREACHED */ 1538 | } 1539 | 1540 | static char *stbds_strdup(char *str) 1541 | { 1542 | // to keep replaceable allocator simple, we don't want to use strdup. 1543 | // rolling our own also avoids problem of strdup vs _strdup 1544 | size_t len = strlen(str)+1; 1545 | char *p = (char*) STBDS_REALLOC(NULL, 0, len); 1546 | memmove(p, str, len); 1547 | return p; 1548 | } 1549 | 1550 | #ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN 1551 | #define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u 1552 | #endif 1553 | #ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX 1554 | #define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20) 1555 | #endif 1556 | 1557 | char *stbds_stralloc(stbds_string_arena *a, char *str) 1558 | { 1559 | char *p; 1560 | size_t len = strlen(str)+1; 1561 | if (len > a->remaining) { 1562 | // compute the next blocksize 1563 | size_t blocksize = a->block; 1564 | 1565 | // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that 1566 | // there are log(SIZE) allocations to free when we destroy the table 1567 | blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1); 1568 | 1569 | // if size is under 1M, advance to next blocktype 1570 | if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX)) 1571 | ++a->block; 1572 | 1573 | if (len > blocksize) { 1574 | // if string is larger than blocksize, then just allocate the full size. 1575 | // note that we still advance string_block so block size will continue 1576 | // increasing, so e.g. if somebody only calls this with 1000-long strings, 1577 | // eventually the arena will start doubling and handling those as well 1578 | stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len); 1579 | memmove(sb->storage, str, len); 1580 | if (a->storage) { 1581 | // insert it after the first element, so that we don't waste the space there 1582 | sb->next = a->storage->next; 1583 | a->storage->next = sb; 1584 | } else { 1585 | sb->next = 0; 1586 | a->storage = sb; 1587 | a->remaining = 0; // this is redundant, but good for clarity 1588 | } 1589 | return sb->storage; 1590 | } else { 1591 | stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize); 1592 | sb->next = a->storage; 1593 | a->storage = sb; 1594 | a->remaining = blocksize; 1595 | } 1596 | } 1597 | 1598 | STBDS_ASSERT(len <= a->remaining); 1599 | p = a->storage->storage + a->remaining - len; 1600 | a->remaining -= len; 1601 | memmove(p, str, len); 1602 | return p; 1603 | } 1604 | 1605 | void stbds_strreset(stbds_string_arena *a) 1606 | { 1607 | stbds_string_block *x,*y; 1608 | x = a->storage; 1609 | while (x) { 1610 | y = x->next; 1611 | STBDS_FREE(NULL, x); 1612 | x = y; 1613 | } 1614 | memset(a, 0, sizeof(*a)); 1615 | } 1616 | 1617 | #endif 1618 | 1619 | ////////////////////////////////////////////////////////////////////////////// 1620 | // 1621 | // UNIT TESTS 1622 | // 1623 | 1624 | #ifdef STBDS_UNIT_TESTS 1625 | #include 1626 | #ifdef STBDS_ASSERT_WAS_UNDEFINED 1627 | #undef STBDS_ASSERT 1628 | #endif 1629 | #ifndef STBDS_ASSERT 1630 | #define STBDS_ASSERT assert 1631 | #include 1632 | #endif 1633 | 1634 | typedef struct { int key,b,c,d; } stbds_struct; 1635 | typedef struct { int key[2],b,c,d; } stbds_struct2; 1636 | 1637 | static char buffer[256]; 1638 | char *strkey(int n) 1639 | { 1640 | #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) 1641 | sprintf_s(buffer, sizeof(buffer), "test_%d", n); 1642 | #else 1643 | sprintf(buffer, "test_%d", n); 1644 | #endif 1645 | return buffer; 1646 | } 1647 | 1648 | void stbds_unit_tests(void) 1649 | { 1650 | #if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus) 1651 | // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing! 1652 | STBDS_ASSERT(0); 1653 | #else 1654 | const int testsize = 100000; 1655 | const int testsize2 = testsize/20; 1656 | int *arr=NULL; 1657 | struct { int key; int value; } *intmap = NULL; 1658 | struct { char *key; int value; } *strmap = NULL, s; 1659 | struct { stbds_struct key; int value; } *map = NULL; 1660 | stbds_struct *map2 = NULL; 1661 | stbds_struct2 *map3 = NULL; 1662 | stbds_string_arena sa = { 0 }; 1663 | int key3[2] = { 1,2 }; 1664 | ptrdiff_t temp; 1665 | 1666 | int i,j; 1667 | 1668 | STBDS_ASSERT(arrlen(arr)==0); 1669 | for (i=0; i < 20000; i += 50) { 1670 | for (j=0; j < i; ++j) 1671 | arrpush(arr,j); 1672 | arrfree(arr); 1673 | } 1674 | 1675 | for (i=0; i < 4; ++i) { 1676 | arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); 1677 | arrdel(arr,i); 1678 | arrfree(arr); 1679 | arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); 1680 | arrdelswap(arr,i); 1681 | arrfree(arr); 1682 | } 1683 | 1684 | for (i=0; i < 5; ++i) { 1685 | arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); 1686 | stbds_arrins(arr,i,5); 1687 | STBDS_ASSERT(arr[i] == 5); 1688 | if (i < 4) 1689 | STBDS_ASSERT(arr[4] == 4); 1690 | arrfree(arr); 1691 | } 1692 | 1693 | i = 1; 1694 | STBDS_ASSERT(hmgeti(intmap,i) == -1); 1695 | hmdefault(intmap, -2); 1696 | STBDS_ASSERT(hmgeti(intmap, i) == -1); 1697 | STBDS_ASSERT(hmget (intmap, i) == -2); 1698 | for (i=0; i < testsize; i+=2) 1699 | hmput(intmap, i, i*5); 1700 | for (i=0; i < testsize; i+=1) { 1701 | if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); 1702 | else STBDS_ASSERT(hmget(intmap, i) == i*5); 1703 | if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); 1704 | else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); 1705 | } 1706 | for (i=0; i < testsize; i+=2) 1707 | hmput(intmap, i, i*3); 1708 | for (i=0; i < testsize; i+=1) 1709 | if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); 1710 | else STBDS_ASSERT(hmget(intmap, i) == i*3); 1711 | for (i=2; i < testsize; i+=4) 1712 | hmdel(intmap, i); // delete half the entries 1713 | for (i=0; i < testsize; i+=1) 1714 | if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 ); 1715 | else STBDS_ASSERT(hmget(intmap, i) == i*3); 1716 | for (i=0; i < testsize; i+=1) 1717 | hmdel(intmap, i); // delete the rest of the entries 1718 | for (i=0; i < testsize; i+=1) 1719 | STBDS_ASSERT(hmget(intmap, i) == -2 ); 1720 | hmfree(intmap); 1721 | for (i=0; i < testsize; i+=2) 1722 | hmput(intmap, i, i*3); 1723 | hmfree(intmap); 1724 | 1725 | #if defined(__clang__) || defined(__GNUC__) 1726 | #ifndef __cplusplus 1727 | intmap = NULL; 1728 | hmput(intmap, 15, 7); 1729 | hmput(intmap, 11, 3); 1730 | hmput(intmap, 9, 5); 1731 | STBDS_ASSERT(hmget(intmap, 9) == 5); 1732 | STBDS_ASSERT(hmget(intmap, 11) == 3); 1733 | STBDS_ASSERT(hmget(intmap, 15) == 7); 1734 | #endif 1735 | #endif 1736 | 1737 | for (i=0; i < testsize; ++i) 1738 | stralloc(&sa, strkey(i)); 1739 | strreset(&sa); 1740 | 1741 | { 1742 | s.key = "a", s.value = 1; 1743 | shputs(strmap, s); 1744 | STBDS_ASSERT(*strmap[0].key == 'a'); 1745 | STBDS_ASSERT(strmap[0].key == s.key); 1746 | STBDS_ASSERT(strmap[0].value == s.value); 1747 | shfree(strmap); 1748 | } 1749 | 1750 | { 1751 | s.key = "a", s.value = 1; 1752 | sh_new_strdup(strmap); 1753 | shputs(strmap, s); 1754 | STBDS_ASSERT(*strmap[0].key == 'a'); 1755 | STBDS_ASSERT(strmap[0].key != s.key); 1756 | STBDS_ASSERT(strmap[0].value == s.value); 1757 | shfree(strmap); 1758 | } 1759 | 1760 | { 1761 | s.key = "a", s.value = 1; 1762 | sh_new_arena(strmap); 1763 | shputs(strmap, s); 1764 | STBDS_ASSERT(*strmap[0].key == 'a'); 1765 | STBDS_ASSERT(strmap[0].key != s.key); 1766 | STBDS_ASSERT(strmap[0].value == s.value); 1767 | shfree(strmap); 1768 | } 1769 | 1770 | for (j=0; j < 2; ++j) { 1771 | STBDS_ASSERT(shgeti(strmap,"foo") == -1); 1772 | if (j == 0) 1773 | sh_new_strdup(strmap); 1774 | else 1775 | sh_new_arena(strmap); 1776 | STBDS_ASSERT(shgeti(strmap,"foo") == -1); 1777 | shdefault(strmap, -2); 1778 | STBDS_ASSERT(shgeti(strmap,"foo") == -1); 1779 | for (i=0; i < testsize; i+=2) 1780 | shput(strmap, strkey(i), i*3); 1781 | for (i=0; i < testsize; i+=1) 1782 | if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); 1783 | else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); 1784 | for (i=2; i < testsize; i+=4) 1785 | shdel(strmap, strkey(i)); // delete half the entries 1786 | for (i=0; i < testsize; i+=1) 1787 | if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); 1788 | else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); 1789 | for (i=0; i < testsize; i+=1) 1790 | shdel(strmap, strkey(i)); // delete the rest of the entries 1791 | for (i=0; i < testsize; i+=1) 1792 | STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); 1793 | shfree(strmap); 1794 | } 1795 | 1796 | { 1797 | struct { char *key; char value; } *hash = NULL; 1798 | char name[4] = "jen"; 1799 | shput(hash, "bob" , 'h'); 1800 | shput(hash, "sally" , 'e'); 1801 | shput(hash, "fred" , 'l'); 1802 | shput(hash, "jen" , 'x'); 1803 | shput(hash, "doug" , 'o'); 1804 | 1805 | shput(hash, name , 'l'); 1806 | shfree(hash); 1807 | } 1808 | 1809 | for (i=0; i < testsize; i += 2) { 1810 | stbds_struct s = { i,i*2,i*3,i*4 }; 1811 | hmput(map, s, i*5); 1812 | } 1813 | 1814 | for (i=0; i < testsize; i += 1) { 1815 | stbds_struct s = { i,i*2,i*3 ,i*4 }; 1816 | stbds_struct t = { i,i*2,i*3+1,i*4 }; 1817 | if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); 1818 | else STBDS_ASSERT(hmget(map, s) == i*5); 1819 | if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); 1820 | else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); 1821 | //STBDS_ASSERT(hmget(map, t.key) == 0); 1822 | } 1823 | 1824 | for (i=0; i < testsize; i += 2) { 1825 | stbds_struct s = { i,i*2,i*3,i*4 }; 1826 | hmputs(map2, s); 1827 | } 1828 | hmfree(map); 1829 | 1830 | for (i=0; i < testsize; i += 1) { 1831 | stbds_struct s = { i,i*2,i*3,i*4 }; 1832 | stbds_struct t = { i,i*2,i*3+1,i*4 }; 1833 | if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); 1834 | else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); 1835 | //STBDS_ASSERT(hmgetp(map2, t.key) == 0); 1836 | } 1837 | hmfree(map2); 1838 | 1839 | for (i=0; i < testsize; i += 2) { 1840 | stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; 1841 | hmputs(map3, s); 1842 | } 1843 | for (i=0; i < testsize; i += 1) { 1844 | stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; 1845 | stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; 1846 | if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); 1847 | else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); 1848 | //STBDS_ASSERT(hmgetp(map3, t.key) == 0); 1849 | } 1850 | #endif 1851 | } 1852 | #endif 1853 | 1854 | 1855 | /* 1856 | ------------------------------------------------------------------------------ 1857 | This software is available under 2 licenses -- choose whichever you prefer. 1858 | ------------------------------------------------------------------------------ 1859 | ALTERNATIVE A - MIT License 1860 | Copyright (c) 2019 Sean Barrett 1861 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1862 | this software and associated documentation files (the "Software"), to deal in 1863 | the Software without restriction, including without limitation the rights to 1864 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1865 | of the Software, and to permit persons to whom the Software is furnished to do 1866 | so, subject to the following conditions: 1867 | The above copyright notice and this permission notice shall be included in all 1868 | copies or substantial portions of the Software. 1869 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1870 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1871 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1872 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1873 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1874 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1875 | SOFTWARE. 1876 | ------------------------------------------------------------------------------ 1877 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1878 | This is free and unencumbered software released into the public domain. 1879 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1880 | software, either in source code form or as a compiled binary, for any purpose, 1881 | commercial or non-commercial, and by any means. 1882 | In jurisdictions that recognize copyright laws, the author or authors of this 1883 | software dedicate any and all copyright interest in the software to the public 1884 | domain. We make this dedication for the benefit of the public at large and to 1885 | the detriment of our heirs and successors. We intend this dedication to be an 1886 | overt act of relinquishment in perpetuity of all present and future rights to 1887 | this software under copyright law. 1888 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1889 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1890 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1891 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1892 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1893 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1894 | ------------------------------------------------------------------------------ 1895 | */ 1896 | --------------------------------------------------------------------------------