FFmpeg
Loading...
Searching...
No Matches
hashtable.c
Go to the documentation of this file.
1/*
2 * Generic hashtable
3 * Copyright (C) 2025 Emma Worley <emma@emma.gg>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <stdint.h>
23#include <string.h>
24
26#include "libavutil/crc.h"
27#include "libavutil/error.h"
28#include "libavutil/macros.h"
29#include "libavutil/mem.h"
30#include "hashtable.h"
31
32#define ALIGN _Alignof(size_t)
33
35 size_t key_size;
36 size_t val_size;
37 size_t entry_size;
39 size_t nb_entries;
40 const AVCRC *crc;
41 uint8_t *table;
42 uint8_t swapbuf[];
43};
44
45/*
46 * Hash table entries are comprised of a probe sequence length (PSL), key, and
47 * value. When the PSL of an entry is zero, it means it is not occupied by a
48 * key/value pair. When the PSL is non-zero, it represents the "distance" of
49 * the entry from its "home" location plus one, where the "home" location is
50 * hash(key) % max_entries.
51 */
52
53#define ENTRY_PSL_VAL(entry) (*(size_t*)(entry))
54#define ENTRY_KEY_PTR(entry) ((entry) + sizeof(size_t))
55#define ENTRY_VAL_PTR(entry) (ENTRY_KEY_PTR(entry) + ctx->key_size)
56
57#define KEYS_EQUAL(k1, k2) (!memcmp((k1), (k2), ctx->key_size))
58
60 size_t val_size, size_t max_entries)
61{
62 const size_t keyval_size = key_size + val_size;
63
64 if (keyval_size < key_size || // did (unsigned,defined) wraparound happen?
65 keyval_size > FFMIN(SIZE_MAX - sizeof(size_t) - (ALIGN - 1),
66 (SIZE_MAX - sizeof(FFHashtableContext)) / 2))
67 return AVERROR(ERANGE);
68
69 FFHashtableContext *res = av_mallocz(sizeof(*res) + 2 * keyval_size);
70 if (!res)
71 return AVERROR(ENOMEM);
72 res->key_size = key_size;
73 res->val_size = val_size;
74 res->entry_size = FFALIGN(sizeof(size_t) + keyval_size, ALIGN);
75 res->max_entries = max_entries;
76 res->nb_entries = 0;
78 if (!res->crc) {
80 return AVERROR_BUG;
81 }
82 res->table = av_calloc(res->max_entries, res->entry_size);
83 if (!res->table) {
85 return AVERROR(ENOMEM);
86 }
87
88 *ctx = res;
89 return 0;
90}
91
92static size_t hash_key(const struct FFHashtableContext *ctx, const void *key)
93{
94 return av_crc(ctx->crc, 0, key, ctx->key_size) % ctx->max_entries;
95}
96
97int ff_hashtable_get(const struct FFHashtableContext *ctx, const void *key, void *val)
98{
99 if (!ctx->nb_entries)
100 return 0;
101
102 size_t hash = hash_key(ctx, key);
103
104 for (size_t psl = 1; psl <= ctx->max_entries; psl++) {
105 size_t wrapped_index = (hash + psl) % ctx->max_entries;
106 uint8_t *entry = ctx->table + wrapped_index * ctx->entry_size;
107 if (ENTRY_PSL_VAL(entry) < psl)
108 // When PSL stops increasing it means there are no further entries
109 // with the same key hash.
110 return 0;
112 memcpy(val, ENTRY_VAL_PTR(entry), ctx->val_size);
113 return 1;
114 }
115 }
116 return 0;
117}
118
119int ff_hashtable_set(struct FFHashtableContext *ctx, const void *key, const void *val)
120{
121 int swapping = 0;
122 size_t psl = 1;
123 size_t hash = hash_key(ctx, key);
124 size_t wrapped_index = hash % ctx->max_entries;
125 uint8_t *set = ctx->swapbuf;
126 uint8_t *tmp = ctx->swapbuf + ctx->key_size + ctx->val_size;
127
128 memcpy(set, key, ctx->key_size);
129 memcpy(set + ctx->key_size, val, ctx->val_size);
130
131 for (size_t i = 0; i < ctx->max_entries; i++) {
132 if (++wrapped_index == ctx->max_entries)
133 wrapped_index = 0;
134 uint8_t *entry = ctx->table + wrapped_index * ctx->entry_size;
135 if (!ENTRY_PSL_VAL(entry) || (!swapping && KEYS_EQUAL(ENTRY_KEY_PTR(entry), set))) {
136 if (!ENTRY_PSL_VAL(entry))
137 ctx->nb_entries++;
138 ENTRY_PSL_VAL(entry) = psl;
139 memcpy(ENTRY_KEY_PTR(entry), set, ctx->key_size + ctx->val_size);
140 return 1;
141 }
142 if (ENTRY_PSL_VAL(entry) < psl) {
143 // When PSL stops increasing it means there are no further entries
144 // with the same key hash. We can only hope to find an unoccupied
145 // entry.
146 if (ctx->nb_entries == ctx->max_entries)
147 // The table is full so inserts are impossible.
148 return 0;
149 // Robin Hood hash tables "steal from the rich" by minimizing the
150 // PSL of the inserted entry.
151 swapping = 1;
152 // set needs to swap with entry
153 memcpy(tmp, ENTRY_KEY_PTR(entry), ctx->key_size + ctx->val_size);
154 memcpy(ENTRY_KEY_PTR(entry), set, ctx->key_size + ctx->val_size);
155 FFSWAP(uint8_t*, set, tmp);
156 FFSWAP(size_t, psl, ENTRY_PSL_VAL(entry));
157 }
158 psl++;
159 }
160 return 0;
161}
162
164{
165 if (!ctx->nb_entries)
166 return 0;
167
168 uint8_t *next_entry;
169 size_t hash = hash_key(ctx, key);
170 size_t wrapped_index = hash % ctx->max_entries;
171
172 for (size_t psl = 1; psl <= ctx->max_entries; psl++) {
173 if (++wrapped_index == ctx->max_entries)
174 wrapped_index = 0;
175 uint8_t *entry = ctx->table + wrapped_index * ctx->entry_size;
176 if (ENTRY_PSL_VAL(entry) < psl)
177 // When PSL stops increasing it means there are no further entries
178 // with the same key hash.
179 return 0;
181 ENTRY_PSL_VAL(entry) = 0;
182 // Shift each following entry that will benefit from a reduced PSL.
183 for (psl++; psl <= ctx->max_entries; psl++) {
184 if (++wrapped_index == ctx->max_entries)
185 wrapped_index = 0;
186 next_entry = ctx->table + wrapped_index * ctx->entry_size;
187 if (ENTRY_PSL_VAL(next_entry) <= 1) {
188 ctx->nb_entries--;
189 return 1;
190 }
191 memcpy(entry, next_entry, ctx->entry_size);
193 ENTRY_PSL_VAL(next_entry) = 0;
194 entry = next_entry;
195 }
196 }
197 }
198 return 0;
199}
200
202{
203 memset(ctx->table, 0, ctx->entry_size * ctx->max_entries);
204}
205
207{
208 if (*ctx) {
209 av_freep(&(*ctx)->table);
210 av_freep(ctx);
211 }
212}
static double val(void *priv, double ch)
Definition aeval.c:77
#define entry
static uint8_t hash[HASH_SIZE]
static AVFormatContext * ctx
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
Public header for CRC hash function implementation.
error code definitions
const char * key
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
Definition crc.c:389
uint32_t AVCRC
Definition crc.h:46
uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)
Calculate the CRC of a block.
Definition crc.c:421
@ AV_CRC_32_IEEE
Definition crc.h:52
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition error.h:52
#define AVERROR(e)
Definition error.h:45
#define ENTRY_KEY_PTR(entry)
Definition hashtable.c:54
av_cold int ff_hashtable_alloc(FFHashtableContext **ctx, size_t key_size, size_t val_size, size_t max_entries)
Create a fixed-sized Robin Hood hash table.
Definition hashtable.c:59
#define ENTRY_PSL_VAL(entry)
Definition hashtable.c:53
#define ENTRY_VAL_PTR(entry)
Definition hashtable.c:55
#define KEYS_EQUAL(k1, k2)
Definition hashtable.c:57
int ff_hashtable_set(struct FFHashtableContext *ctx, const void *key, const void *val)
Store a value in a hash table given a key.
Definition hashtable.c:119
static size_t hash_key(const struct FFHashtableContext *ctx, const void *key)
Definition hashtable.c:92
int ff_hashtable_delete(struct FFHashtableContext *ctx, const void *key)
Delete a value from a hash table given a key.
Definition hashtable.c:163
void ff_hashtable_clear(struct FFHashtableContext *ctx)
Delete all values from a hash table.
Definition hashtable.c:201
int ff_hashtable_get(const struct FFHashtableContext *ctx, const void *key, void *val)
Look up a value from a hash table given a key.
Definition hashtable.c:97
#define ALIGN
Definition hashtable.c:32
av_cold void ff_hashtable_freep(FFHashtableContext **ctx)
Free a hash table.
Definition hashtable.c:206
Macro definitions for various function/variable attributes.
#define av_cold
Definition attributes.h:117
Utility Preprocessor macros.
#define FFSWAP(type, a, b)
Definition macros.h:52
#define FFMIN(a, b)
Definition macros.h:49
#define FFALIGN(x, a)
Definition macros.h:78
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
uint8_t * table
Definition hashtable.c:41
const AVCRC * crc
Definition hashtable.c:40
uint8_t swapbuf[]
Definition hashtable.c:42
#define av_mallocz(s)
#define av_freep(p)
static uint8_t tmp[40]
Definition aes_ctr.c:52
static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v)
Definition swresample.c:55