173 lines
3.9 KiB
C
Executable file
173 lines
3.9 KiB
C
Executable file
/*
|
|
* The fLisp parser and interpreter
|
|
* Copyright (C) 2025 Fcalva
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include "types.h"
|
|
#include "hash.h"
|
|
#include "config.h"
|
|
|
|
int heap_hashmap(HashMap *map, i32 size){
|
|
if(size < 32)
|
|
return 1;
|
|
map->curr_len = size;
|
|
map->buffer = malloc(sizeof(MapItem)*size);
|
|
map->bit_free = malloc(sizeof(u32)*size/32);
|
|
|
|
if(!map->buffer || !map->bit_free)
|
|
return 1;
|
|
|
|
memset(map->bit_free, 0, sizeof(u32)*size/32);
|
|
memset(map->buffer, 0, sizeof(MapItem)*size);
|
|
for(int i = 0; i < size; i++)
|
|
map->buffer[i].id = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void free_hashmap(HashMap *map){
|
|
free(map->buffer);
|
|
free(map->bit_free);
|
|
}
|
|
|
|
// ~djb2
|
|
i32 hash(i32 max, char *str){
|
|
if(!str)
|
|
return HASH_NULL;
|
|
u32 hsh = 5281;
|
|
char ch = str[0];
|
|
for(int i = 1; i < 32 && ch; i++){
|
|
hsh = (hsh << 5) ^ ch;
|
|
ch = str[i];
|
|
}
|
|
hsh %= max;
|
|
#if DEBUG == 2
|
|
assert(hsh < HASH_NULL);
|
|
#endif
|
|
return hsh;
|
|
}
|
|
|
|
i32 get_bit(u32 *bitmap, i32 pos){
|
|
return (bitmap[pos/32] >> (pos%32)) & 1;
|
|
}
|
|
|
|
void set_bit(u32 *bitmap, i32 pos){
|
|
bitmap[pos/32] |= (1<<(pos%32));
|
|
}
|
|
|
|
MapItem *hashmap_insert(HashMap *map, char *str){
|
|
#if DEBUG > 0
|
|
float load_factor = (float)(map->item_n+1)/(float)(map->curr_len);
|
|
//printf("%f\n", load_factor);
|
|
#if DEBUG == 1
|
|
assert(load_factor < 0.8);
|
|
#else
|
|
assert(load_factor < 0.7);
|
|
#endif
|
|
#endif
|
|
|
|
if(map->item_n+1 >= map->curr_len)
|
|
return NULL;
|
|
i32 hsh = hash(map->curr_len, str);
|
|
MapItem *match = hashmap_get(map, str);
|
|
if(match){
|
|
return match;
|
|
}
|
|
|
|
if(!map->bit_free[hsh/32]){
|
|
map->buffer[hsh].hash = hsh;
|
|
map->buffer[hsh].id = map->curr_id++;
|
|
strncpy(map->buffer[hsh].str, str, 32);
|
|
set_bit(map->bit_free, hsh);
|
|
map->item_n++;
|
|
return &map->buffer[hsh];
|
|
}
|
|
|
|
i32 pos = hsh;
|
|
i32 taken;
|
|
do {
|
|
taken = get_bit(map->bit_free, pos);
|
|
if(!taken)
|
|
break;
|
|
pos++;
|
|
} while(pos < map->curr_len && pos-hsh < RELOCATE_TRY_MAX);
|
|
|
|
#if DEBUG == 2
|
|
assert(!taken);
|
|
#endif
|
|
|
|
if(!taken){
|
|
map->buffer[pos].hash = hsh;
|
|
map->buffer[pos].id = map->curr_id++;
|
|
strncpy(map->buffer[pos].str, str, 32);
|
|
set_bit(map->bit_free, hsh);
|
|
map->item_n++;
|
|
return &map->buffer[pos];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MapItem *hashmap_get(HashMap *map, char *str){
|
|
i32 fhash = hash(map->curr_len, str);
|
|
i32 pos = fhash;
|
|
i32 match;
|
|
do {
|
|
i32 c_hash = map->buffer[pos].hash;
|
|
match = c_hash == fhash;
|
|
if(match)
|
|
match = !strncmp(str,map->buffer[pos].str,32);
|
|
pos++;
|
|
} while(!match && pos < map->curr_len && pos-fhash < RELOCATE_TRY_MAX);
|
|
pos--;
|
|
|
|
|
|
if(match)
|
|
return &map->buffer[pos];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void hashmap_remove(HashMap *map, char *str){
|
|
MapItem *ret = hashmap_get(map, str);
|
|
if(!ret)
|
|
return;
|
|
// Erase the existance of the entry
|
|
// (Literally 1984)
|
|
memset(ret, 0, sizeof(MapItem));
|
|
}
|
|
|
|
int cmp_mapitem_id(const void *_a, const void *_b){
|
|
const MapItem *a = _a;
|
|
const MapItem *b = _b;
|
|
return b->id - a->id;
|
|
}
|
|
|
|
void hashmap_remove_id(HashMap *map, i32 id){
|
|
i32 pos = 0;
|
|
int found = 0;
|
|
// TODO : Replace with bsearch again
|
|
for(int i = 0; i < map->curr_len && !found; i++){
|
|
found = map->buffer[pos++].id == id;
|
|
}
|
|
if(found)
|
|
memset(&map->buffer[--pos], 0, sizeof(MapItem));
|
|
}
|