/[LeafOK_CVS]/lbbs/src/trie_dict.c
ViewVC logotype

Contents of /lbbs/src/trie_dict.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.20 - (show annotations)
Wed Nov 19 15:50:13 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.19: +104 -42 lines
Content type: text/x-csrc
Use POSIX shared object instead of SysV shared segment in trie_dict

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * trie_dict
4 * - trie-tree based dict feature
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "common.h"
14 #include "log.h"
15 #include "trie_dict.h"
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25
26 struct trie_node_pool_t
27 {
28 size_t shm_size;
29 int node_count_limit;
30 int node_count;
31 TRIE_NODE *p_node_free_list;
32 };
33 typedef struct trie_node_pool_t TRIE_NODE_POOL;
34
35 static char trie_node_shm_name[FILE_PATH_LEN];
36 static TRIE_NODE_POOL *p_trie_node_pool;
37
38 int trie_dict_init(const char *filename, int node_count_limit)
39 {
40 char filepath[FILE_PATH_LEN];
41 int fd;
42 size_t size;
43 void *p_shm;
44 TRIE_NODE *p_trie_nodes;
45 int i;
46
47 if (filename == NULL)
48 {
49 log_error("NULL pointer error\n");
50 return -1;
51 }
52
53 if (p_trie_node_pool != NULL)
54 {
55 log_error("trie_dict_pool already initialized\n");
56 return -1;
57 }
58
59 if (node_count_limit <= 0 || node_count_limit > TRIE_NODE_PER_POOL)
60 {
61 log_error("trie_dict_init(%d) error: invalid node_count_limit\n", node_count_limit);
62 return -1;
63 }
64
65 // Allocate shared memory
66 size = sizeof(TRIE_NODE_POOL) + sizeof(TRIE_NODE) * (size_t)node_count_limit;
67
68 strncpy(filepath, filename, sizeof(filepath) - 1);
69 filepath[sizeof(filepath) - 1] = '\0';
70 snprintf(trie_node_shm_name, sizeof(trie_node_shm_name), "/TRIE_DICT_SHM_%s", basename(filepath));
71
72 if (shm_unlink(trie_node_shm_name) == -1 && errno != ENOENT)
73 {
74 log_error("shm_unlink(%s) error (%d)\n", trie_node_shm_name, errno);
75 return -2;
76 }
77
78 if ((fd = shm_open(trie_node_shm_name, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1)
79 {
80 log_error("shm_open(%s) error (%d)\n", trie_node_shm_name, errno);
81 return -2;
82 }
83 if (ftruncate(fd, (off_t)size) == -1)
84 {
85 log_error("ftruncate(size=%d) error (%d)\n", size, errno);
86 close(fd);
87 return -2;
88 }
89
90 p_shm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0L);
91 if (p_shm == MAP_FAILED)
92 {
93 log_error("mmap() error (%d)\n", errno);
94 close(fd);
95 return -2;
96 }
97
98 if (close(fd) < 0)
99 {
100 log_error("close(fd) error (%d)\n", errno);
101 return -1;
102 }
103
104 p_trie_node_pool = p_shm;
105 p_trie_node_pool->shm_size = size;
106 p_trie_node_pool->node_count_limit = node_count_limit;
107 p_trie_node_pool->node_count = 0;
108
109 p_trie_nodes = (TRIE_NODE *)((char *)p_shm + sizeof(TRIE_NODE_POOL));
110 p_trie_node_pool->p_node_free_list = &(p_trie_nodes[0]);
111 for (i = 0; i < node_count_limit - 1; i++)
112 {
113 p_trie_nodes[i].p_nodes[0] = &(p_trie_nodes[i + 1]);
114 }
115 p_trie_nodes[node_count_limit - 1].p_nodes[0] = NULL;
116
117 return 0;
118 }
119
120 int trie_dict_cleanup(void)
121 {
122 if (p_trie_node_pool == NULL)
123 {
124 return -1;
125 }
126
127 detach_trie_dict_shm();
128
129 if (shm_unlink(trie_node_shm_name) == -1 && errno != ENOENT)
130 {
131 log_error("shm_unlink(%s) error (%d)\n", trie_node_shm_name, errno);
132 return -2;
133 }
134
135 trie_node_shm_name[0] = '\0';
136
137 return 0;
138 }
139
140 int get_trie_dict_shm_readonly(void)
141 {
142 int fd;
143 struct stat sb;
144 size_t size;
145 void *p_shm;
146
147 if ((fd = shm_open(trie_node_shm_name, O_RDONLY, 0600)) == -1)
148 {
149 log_error("shm_open(%s) error (%d)\n", trie_node_shm_name, errno);
150 return -2;
151 }
152
153 if (fstat(fd, &sb) < 0)
154 {
155 log_error("fstat(fd) error (%d)\n", errno);
156 close(fd);
157 return -2;
158 }
159
160 size = (size_t)sb.st_size;
161
162 p_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0L);
163 if (p_shm == MAP_FAILED)
164 {
165 log_error("mmap() error (%d)\n", errno);
166 close(fd);
167 return -2;
168 }
169
170 if (close(fd) < 0)
171 {
172 log_error("close(fd) error (%d)\n", errno);
173 return -1;
174 }
175
176 if (p_trie_node_pool->shm_size != size)
177 {
178 log_error("Shared memory size mismatch (%ld != %ld)\n", p_trie_node_pool->shm_size, size);
179 munmap(p_shm, size);
180 return -3;
181 }
182
183 p_trie_node_pool = p_shm;
184
185 return 0;
186 }
187
188 int set_trie_dict_shm_readonly(void)
189 {
190 if (p_trie_node_pool != NULL && munmap(p_trie_node_pool, p_trie_node_pool->shm_size) < 0)
191 {
192 log_error("munmap() error (%d)\n", errno);
193 return -2;
194 }
195
196 if (get_trie_dict_shm_readonly() < 0)
197 {
198 log_error("get_trie_dict_shm_readonly() error\n");
199 return -3;
200 }
201
202 return 0;
203 }
204
205 int detach_trie_dict_shm(void)
206 {
207 if (p_trie_node_pool != NULL && munmap(p_trie_node_pool, p_trie_node_pool->shm_size) < 0)
208 {
209 log_error("munmap() error (%d)\n", errno);
210 return -1;
211 }
212
213 p_trie_node_pool = NULL;
214
215 return 0;
216 }
217
218 TRIE_NODE *trie_dict_create(void)
219 {
220 TRIE_NODE *p_dict = NULL;
221
222 if (p_trie_node_pool != NULL && p_trie_node_pool->p_node_free_list != NULL)
223 {
224 p_dict = p_trie_node_pool->p_node_free_list;
225 p_trie_node_pool->p_node_free_list = p_dict->p_nodes[0];
226
227 memset(p_dict, 0, sizeof(*p_dict));
228
229 p_trie_node_pool->node_count++;
230 }
231 else if (p_trie_node_pool != NULL)
232 {
233 log_error("trie_dict_create() error: node depleted %d >= %d\n", p_trie_node_pool->node_count, p_trie_node_pool->node_count_limit);
234 }
235
236 return p_dict;
237 }
238
239 void trie_dict_destroy(TRIE_NODE *p_dict)
240 {
241 if (p_trie_node_pool == NULL || p_dict == NULL)
242 {
243 return;
244 }
245
246 for (int i = 0; i < TRIE_CHILDREN; i++)
247 {
248 if (p_dict->p_nodes[i] != NULL)
249 {
250 trie_dict_destroy(p_dict->p_nodes[i]);
251 }
252 }
253
254 memset(p_dict, 0, sizeof(*p_dict));
255
256 p_dict->p_nodes[0] = p_trie_node_pool->p_node_free_list;
257 p_trie_node_pool->p_node_free_list = p_dict;
258
259 p_trie_node_pool->node_count--;
260 }
261
262 int trie_dict_set(TRIE_NODE *p_dict, const char *key, int64_t value)
263 {
264 int offset;
265
266 if (p_dict == NULL)
267 {
268 return -1;
269 }
270
271 while (key != NULL && *key != '\0')
272 {
273 offset = (256 + *key) % 256;
274 if (offset < 0 || offset >= TRIE_CHILDREN) // incorrect key character
275 {
276 return -1;
277 }
278
279 if (*(key + 1) == '\0')
280 {
281 if (p_dict->flags[offset] == 0 || p_dict->values[offset] != value)
282 {
283 p_dict->values[offset] = value;
284 p_dict->flags[offset] = 1;
285 return 1; // Set to new value
286 }
287 return 0; // Unchanged
288 }
289 else
290 {
291 if (p_dict->p_nodes[offset] == NULL)
292 {
293 if ((p_dict->p_nodes[offset] = trie_dict_create()) == NULL)
294 {
295 return -2; // OOM
296 }
297 }
298 p_dict = p_dict->p_nodes[offset];
299 key++;
300 }
301 }
302
303 return -1; // NULL key
304 }
305
306 int trie_dict_get(TRIE_NODE *p_dict, const char *key, int64_t *p_value)
307 {
308 int offset;
309
310 if (p_dict == NULL)
311 {
312 return -1;
313 }
314
315 while (key != NULL && *key != '\0')
316 {
317 offset = (256 + *key) % 256;
318 if (offset < 0 || offset >= TRIE_CHILDREN) // incorrect key character
319 {
320 return -1;
321 }
322
323 if (*(key + 1) == '\0')
324 {
325 if (p_dict->flags[offset] != 0)
326 {
327 *p_value = p_dict->values[offset];
328 return 1; // Found
329 }
330 else
331 {
332 return 0; // Not exist
333 }
334 }
335 else if (p_dict->p_nodes[offset] == NULL)
336 {
337 return 0; // Not exist
338 }
339 else
340 {
341 p_dict = p_dict->p_nodes[offset];
342 key++;
343 }
344 }
345
346 return -1; // NULL key
347 }
348
349 int trie_dict_del(TRIE_NODE *p_dict, const char *key)
350 {
351 int offset;
352
353 if (p_dict == NULL)
354 {
355 return -1;
356 }
357
358 while (key != NULL && *key != '\0')
359 {
360 offset = (256 + *key) % 256;
361 if (offset < 0 || offset >= TRIE_CHILDREN) // incorrect key character
362 {
363 return -1;
364 }
365
366 if (*(key + 1) == '\0')
367 {
368 if (p_dict->flags[offset] != 0)
369 {
370 p_dict->flags[offset] = 0;
371 p_dict->values[offset] = 0;
372 return 1; // Done
373 }
374 else
375 {
376 return 0; // Not exist
377 }
378 }
379 else if (p_dict->p_nodes[offset] == NULL)
380 {
381 return 0; // Not exist
382 }
383 else
384 {
385 p_dict = p_dict->p_nodes[offset];
386 key++;
387 }
388 }
389
390 return -1; // NULL key
391 }
392
393 static void _trie_dict_traverse(TRIE_NODE *p_dict, trie_dict_traverse_cb cb, char *key, int depth)
394 {
395 if (p_dict == NULL || depth >= TRIE_MAX_KEY_LEN)
396 {
397 return;
398 }
399
400 for (int i = 0; i < TRIE_CHILDREN; i++)
401 {
402 if (p_dict->flags[i] != 0)
403 {
404 key[depth] = (char)i;
405 key[depth + 1] = '\0';
406 (*cb)(key, p_dict->values[i]);
407 }
408
409 if (p_dict->p_nodes[i] != NULL && depth + 1 < TRIE_MAX_KEY_LEN)
410 {
411 key[depth] = (char)i;
412 _trie_dict_traverse(p_dict->p_nodes[i], cb, key, depth + 1);
413 }
414 }
415 }
416
417 void trie_dict_traverse(TRIE_NODE *p_dict, trie_dict_traverse_cb cb)
418 {
419 char key[TRIE_MAX_KEY_LEN + 1];
420
421 if (p_dict == NULL)
422 {
423 return;
424 }
425
426 _trie_dict_traverse(p_dict, cb, key, 0);
427 }
428
429 int trie_dict_used_nodes(void)
430 {
431 return (p_trie_node_pool == NULL ? -1 : p_trie_node_pool->node_count);
432 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1