/* _,.---._ .-._ .--.-. ,--.--------. * _,..---._ ,-.' , - `. /==/ \ .-._/==/ //==/, - , -\ * /==/, - \ /==/_, , - \|==|, \/ /, |==\ -\\==\.-. - ,-./ * |==| _ _\==| .=. |==|- \| | \==\- \`--`\==\- \ * |==| .=. |==|_ : ;=: - |==| , | -| `--`-' \==\_ \ * |==|,| | -|==| , '=' |==| - _ | |==|- | * |==| '=' /\==\ - ,_ /|==| /\ , | |==|, | * |==|-, _`/ '.='. - .' /==/, | |- | /==/ -/ * `-.`.____.' `--`--'' `--`./ `--` `--`--` * _ __ ,---. .-._ .=-.-. _,.----. * .-`.' ,`..--.' \ /==/ \ .-._ /==/_ /.' .' - \ * /==/, - \==\-/\ \ |==|, \/ /, /==|, |/==/ , ,-' * |==| _ .=. /==/-|_\ | |==|- \| ||==| ||==|- | . * |==| , '=',\==\, - \ |==| , | -||==|- ||==|_ `-' \ * |==|- '..'/==/ - ,| |==| - _ ||==| ,||==| _ , | * |==|, | /==/- /\ - \|==| /\ , ||==|- |\==\. / * /==/ - | \==\ _.\=\.-'/==/, | |- |/==/. / `-.`.___.-' * `--`---' `--` `--`./ `--``--`-` * * @(#)Copyright (c) 2023, Nathan D. Fisher. * * This is free software. It comes with NO WARRANTY. * Permission to use, modify and distribute this source code * is granted subject to the following conditions. * 1/ that the above copyright notice and this notice * are preserved in all copies and that due credit be given * to the author. * 2/ that any changes to this code are clearly commented * as such so that the author does not get blamed for bugs * other than his own. */ #include "haggis_private.h" #include // PATH_MAX #include #include // calloc, free #include // strndup #define FNV64_OFFSET_BASIS 14695981039346656037u #define FNV64_PRIME 1099511628211u uint64_t hash_fnv1a_64(uint8_t *key, size_t len) { int i; uint64_t hash = FNV64_OFFSET_BASIS; for (i = 0; i < len; i++) { hash = hash ^ *key; hash = hash * FNV64_PRIME; key++; } return hash; } uint64_t hash_str_fnv1a_64(char * s) { uint64_t hash = FNV64_OFFSET_BASIS; while (*s != '\0') { hash = hash ^ *s; hash = hash * FNV64_PRIME; s++; } return hash; } haggis_bucket* haggis_bucket_init(ino_t inode, uint64_t hash, char * path) { haggis_bucket *bucket; bucket = calloc(1, sizeof(haggis_bucket)); if (bucket == NULL) return NULL; bucket->key.val = inode; bucket->hash = hash; bucket->path = path; bucket->next = NULL; return bucket; } void haggis_bucket_deinit(haggis_bucket *nod) { if (nod->path != NULL) free(nod->path); free(nod); } char* haggis_bucket_search_append(haggis_bucket *head, haggis_bucket *tail) { char* target; while (head->next != NULL) { if (head->key.val == tail->key.val) { target = strndup(head->path, PATH_MAX - 1); return target; } head = head->next; } head->next = tail; return NULL; } haggis_linkmap* haggis_linkmap_init() { haggis_linkmap *map; map = calloc(1, sizeof(haggis_linkmap)); if (map == NULL) return NULL; map->buckets = calloc(HAGGIS_BUCKETS_BASE, sizeof(size_t)); if (map->buckets == NULL) { free(map); return NULL; } map->capacity = HAGGIS_BUCKETS_BASE; return map; } void haggis_linkmap_deinit(haggis_linkmap *map) { int i; for (i = 0; i < map->capacity; i++) { if (map->buckets[i].next != NULL) { haggis_bucket_deinit(map->buckets[i].next); } } free(map->buckets); free(map); } int haggis_linkmap_expand(haggis_linkmap *map) { haggis_bucket *buckets_new; haggis_bucket *buckets_old; size_t i, hash, idx; buckets_new = calloc(map->capacity + HAGGIS_BUCKETS_BASE, sizeof(haggis_bucket)); if (buckets_new == NULL) return 2; for (i = 0; i < map->capacity; i++) { if (map->buckets[i].key.val != 0) { hash = hash_fnv1a_64(&map->buckets[i].key.bytes[0], sizeof(ino_t)); map ->capacity += HAGGIS_BUCKETS_BASE; idx = map->capacity % hash; buckets_new[idx] = map->buckets[i]; } } buckets_old = map->buckets; map->buckets = buckets_new; free(buckets_old); map->capacity += HAGGIS_BUCKETS_BASE; return 0; } char* haggis_linkmap_get_or_add(haggis_linkmap *map, ino_t inode, char * path) { union { ino_t val; u8 bytes[sizeof(ino_t)]; } key; char * target = ""; size_t idx, hash; haggis_bucket *b; pthread_mutex_lock(&map->mutex); if (map->len >= map->capacity) haggis_linkmap_expand(map); key.val = inode; hash = hash_fnv1a_64(key.bytes, sizeof(ino_t)); idx = map->capacity % hash; if (map->buckets[idx].key.val == inode) { target = strndup(target, PATH_MAX - 1); } else if (map->buckets[idx].key.val == 0) { map->buckets[idx].key.val = inode; map->buckets[idx].hash = hash; map->buckets[idx].path = path; map->len++; } else { b = haggis_bucket_init(key.val, hash, path); if (b == NULL) return NULL; target = haggis_bucket_search_append(&map->buckets[idx], b); } pthread_mutex_unlock(&map->mutex); return target; }