1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
* str_table.c
*
* Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
*/
#include "config.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sqfs/error.h"
#include "util/str_table.h"
#include "util/util.h"
/* R5 hash function (borrowed from reiserfs) */
static sqfs_u32 strhash(const char *s)
{
const signed char *str = (const signed char *)s;
sqfs_u32 a = 0;
while (*str != '\0') {
a += *str << 4;
a += *str >> 4;
a *= 11;
str++;
}
return a;
}
static bool key_equals_function(void *user, const void *a, const void *b)
{
(void)user;
return strcmp(a, b) == 0;
}
int str_table_init(str_table_t *table)
{
memset(table, 0, sizeof(*table));
if (array_init(&table->bucket_ptrs, sizeof(str_bucket_t *), 0))
goto fail_arr;
table->ht = hash_table_create(NULL, key_equals_function);
if (table->ht == NULL)
goto fail_ht;
return 0;
fail_ht:
array_cleanup(&table->bucket_ptrs);
fail_arr:
memset(table, 0, sizeof(*table));
return SQFS_ERROR_ALLOC;
}
int str_table_copy(str_table_t *dst, const str_table_t *src)
{
str_bucket_t *bucket, **array;
int ret;
ret = array_init_copy(&dst->bucket_ptrs, &src->bucket_ptrs);
if (ret != 0)
return ret;
dst->ht = hash_table_clone(src->ht);
if (dst->ht == NULL) {
array_cleanup(&dst->bucket_ptrs);
return SQFS_ERROR_ALLOC;
}
array = (str_bucket_t **)dst->bucket_ptrs.data;
hash_table_foreach(dst->ht, ent) {
bucket = alloc_flex(sizeof(*bucket), 1, strlen(ent->key) + 1);
if (bucket == NULL) {
str_table_cleanup(dst);
return SQFS_ERROR_ALLOC;
}
memcpy(bucket, ent->data,
sizeof(*bucket) + strlen(ent->key) + 1);
ent->data = bucket;
ent->key = bucket->string;
array[bucket->index] = bucket;
}
return 0;
}
void str_table_cleanup(str_table_t *table)
{
hash_table_foreach(table->ht, ent) {
free(ent->data);
ent->data = NULL;
ent->key = NULL;
}
hash_table_destroy(table->ht, NULL);
array_cleanup(&table->bucket_ptrs);
memset(table, 0, sizeof(*table));
}
int str_table_get_index(str_table_t *table, const char *str, size_t *idx)
{
struct hash_entry *ent;
str_bucket_t *new;
sqfs_u32 hash;
hash = strhash(str);
ent = hash_table_search_pre_hashed(table->ht, hash, str);
if (ent != NULL) {
*idx = ((str_bucket_t *)ent->data)->index;
return 0;
}
new = alloc_flex(sizeof(*new), 1, strlen(str) + 1);
if (new == NULL)
return SQFS_ERROR_ALLOC;
new->index = table->next_index;
strcpy(new->string, str);
ent = hash_table_insert_pre_hashed(table->ht, hash, str, new);
if (ent == NULL) {
free(new);
return SQFS_ERROR_ALLOC;
}
ent->key = new->string;
if (array_append(&table->bucket_ptrs, &new) != 0) {
free(new);
ent->key = NULL;
ent->data = NULL;
return SQFS_ERROR_ALLOC;
}
*idx = table->next_index++;
return 0;
}
static str_bucket_t *bucket_by_index(const str_table_t *table, size_t index)
{
if (index >= table->bucket_ptrs.used)
return NULL;
return ((str_bucket_t **)table->bucket_ptrs.data)[index];
}
const char *str_table_get_string(const str_table_t *table, size_t index)
{
str_bucket_t *bucket = bucket_by_index(table, index);
return bucket == NULL ? NULL : bucket->string;
}
void str_table_add_ref(str_table_t *table, size_t index)
{
str_bucket_t *bucket = bucket_by_index(table, index);
if (bucket != NULL && bucket->refcount < ~((size_t)0))
bucket->refcount += 1;
}
void str_table_del_ref(str_table_t *table, size_t index)
{
str_bucket_t *bucket = bucket_by_index(table, index);
if (bucket != NULL && bucket->refcount > 0)
bucket->refcount -= 1;
}
size_t str_table_get_ref_count(const str_table_t *table, size_t index)
{
str_bucket_t *bucket = bucket_by_index(table, index);
return bucket != NULL ? bucket->refcount : 0;
}
|