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

Contents of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Wed May 21 09:18:17 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.4: +1 -1 lines
Content type: text/x-csrc
Add test for section_data_find_section_by_name

1 /***************************************************************************
2 section_list.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "section_list.h"
18 #include "log.h"
19 #include "trie_dict.h"
20 #include <stdio.h>
21 #include <string.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/shm.h>
27 #include <sys/ipc.h>
28
29 // ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT should be
30 // no less than BBS_article_block_limit_per_section * BBS_max_section,
31 // in order to allocate enough memory for blocks
32 #define ARTICLE_BLOCK_PER_SHM 400 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
33 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
34
35 struct article_block_shm_t
36 {
37 int shmid;
38 void *p_shm;
39 };
40 typedef struct article_block_shm_t ARTICLE_BLOCK_SHM;
41
42 static ARTICLE_BLOCK_SHM *p_article_block_shm_pool;
43 static int article_block_shm_count;
44 static ARTICLE_BLOCK *p_article_block_free_list;
45
46 static SECTION_DATA *p_section_data_pool;
47 static int section_data_count;
48 static TRIE_NODE *p_trie_dict_section_data;
49
50 int section_data_pool_init(const char *filename, int article_block_count)
51 {
52 int shmid;
53 int proj_id;
54 key_t key;
55 size_t size;
56 void *p_shm;
57 int i;
58 int article_block_count_in_shm;
59 ARTICLE_BLOCK *p_article_block_in_shm;
60 ARTICLE_BLOCK **pp_article_block_next;
61
62 if (p_article_block_shm_pool != NULL ||
63 p_article_block_free_list != NULL ||
64 p_section_data_pool != NULL ||
65 p_trie_dict_section_data != NULL)
66 {
67 log_error("section_data_pool already initialized\n");
68 return -1;
69 }
70
71 if (article_block_count > ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
72 {
73 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT);
74 return -2;
75 }
76
77 p_article_block_shm_pool = calloc((size_t)article_block_count / ARTICLE_BLOCK_PER_SHM + 1, sizeof(ARTICLE_BLOCK_SHM));
78 if (p_article_block_shm_pool == NULL)
79 {
80 log_error("calloc(%d ARTICLE_BLOCK_SHM) OOM\n", article_block_count / ARTICLE_BLOCK_PER_SHM + 1);
81 return -2;
82 }
83
84 p_section_data_pool = calloc(BBS_max_section, sizeof(SECTION_DATA));
85 if (p_section_data_pool == NULL)
86 {
87 log_error("calloc(%d SECTION_DATA) OOM\n", BBS_max_section);
88 return -2;
89 }
90 section_data_count = 0;
91
92 p_trie_dict_section_data = trie_dict_create();
93 if (p_trie_dict_section_data == NULL)
94 {
95 log_error("trie_dict_create() OOM\n", BBS_max_section);
96 return -2;
97 }
98
99 // Allocate shared memory
100 article_block_shm_count = 0;
101 pp_article_block_next = &p_article_block_free_list;
102
103 while (article_block_count > 0)
104 {
105 article_block_count_in_shm =
106 (article_block_count < ARTICLE_BLOCK_PER_SHM ? article_block_count : ARTICLE_BLOCK_PER_SHM);
107 article_block_count -= article_block_count_in_shm;
108
109 proj_id = getpid() + article_block_shm_count;
110 key = ftok(filename, proj_id);
111 if (key == -1)
112 {
113 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
114 section_data_pool_cleanup();
115 return -3;
116 }
117
118 size = sizeof(shmid) + sizeof(ARTICLE_BLOCK) * (size_t)article_block_count_in_shm;
119 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
120 if (shmid == -1)
121 {
122 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", article_block_shm_count, size, errno);
123 section_data_pool_cleanup();
124 return -3;
125 }
126 p_shm = shmat(shmid, NULL, 0);
127 if (p_shm == (void *)-1)
128 {
129 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
130 section_data_pool_cleanup();
131 return -3;
132 }
133
134 (p_article_block_shm_pool + article_block_shm_count)->shmid = shmid;
135 (p_article_block_shm_pool + article_block_shm_count)->p_shm = p_shm;
136 article_block_shm_count++;
137
138 p_article_block_in_shm = p_shm;
139 *pp_article_block_next = p_article_block_in_shm;
140
141 for (i = 0; i < article_block_count_in_shm; i++)
142 {
143 if (i < article_block_count_in_shm - 1)
144 {
145 (p_article_block_in_shm + i)->p_next_block = (p_article_block_in_shm + i + 1);
146 }
147 else
148 {
149 (p_article_block_in_shm + i)->p_next_block = NULL;
150 pp_article_block_next = &((p_article_block_in_shm + i)->p_next_block);
151 }
152 }
153 }
154
155 return 0;
156 }
157
158 void section_data_pool_cleanup(void)
159 {
160 int i;
161
162 if (p_trie_dict_section_data != NULL)
163 {
164 trie_dict_destroy(p_trie_dict_section_data);
165 p_trie_dict_section_data = NULL;
166 section_data_count = 0;
167 }
168
169 if (p_section_data_pool != NULL)
170 {
171 free(p_section_data_pool);
172 p_section_data_pool = NULL;
173 }
174
175 if (p_article_block_free_list != NULL)
176 {
177 p_article_block_free_list = NULL;
178 }
179
180 if (p_article_block_shm_pool != NULL)
181 {
182 for (i = 0; i < article_block_shm_count; i++)
183 {
184 if (shmdt((p_article_block_shm_pool + i)->p_shm) == -1)
185 {
186 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_shm_pool + i)->shmid, errno);
187 }
188
189 if (shmctl((p_article_block_shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
190 {
191 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_shm_pool + i)->shmid, errno);
192 }
193 }
194
195 p_article_block_shm_pool = NULL;
196 article_block_shm_count = 0;
197 }
198 }
199
200 inline static ARTICLE_BLOCK *pop_free_article_block(void)
201 {
202 ARTICLE_BLOCK *p_article_block = NULL;
203
204 if (p_article_block_free_list != NULL)
205 {
206 p_article_block = p_article_block_free_list;
207 p_article_block_free_list = p_article_block_free_list->p_next_block;
208 }
209
210 return p_article_block;
211 }
212
213 inline static void push_free_article_block(ARTICLE_BLOCK *p_article_block)
214 {
215 p_article_block->p_next_block = p_article_block_free_list;
216 p_article_block_free_list = p_article_block;
217 }
218
219 SECTION_DATA *section_data_create(const char *sname, const char *stitle, const char *master_name)
220 {
221 SECTION_DATA *p_section;
222 int index;
223
224 if (p_section_data_pool == NULL || p_trie_dict_section_data == NULL)
225 {
226 log_error("section_data not initialized\n");
227 return NULL;
228 }
229
230 if (section_data_count >= BBS_max_section)
231 {
232 log_error("section_data_count exceed limit %d\n", BBS_max_section);
233 return NULL;
234 }
235
236 index = section_data_count;
237 p_section = p_section_data_pool + index;
238
239 strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));
240 p_section->sname[sizeof(p_section->sname - 1)] = '\0';
241
242 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle - 1));
243 p_section->stitle[sizeof(p_section->stitle - 1)] = '\0';
244
245 strncpy(p_section->master_name, master_name, sizeof(p_section->master_name - 1));
246 p_section->master_name[sizeof(p_section->master_name - 1)] = '\0';
247
248 p_section->p_head_block = NULL;
249 p_section->p_tail_block = NULL;
250 p_section->block_count = 0;
251 p_section->article_count = 0;
252 p_section->delete_count = 0;
253
254 if (trie_dict_set(p_trie_dict_section_data, sname, index) != 1)
255 {
256 log_error("trie_dict_set(section_data, %s, %d) error\n", sname, index);
257 return NULL;
258 }
259
260 section_data_count++;
261
262 return p_section;
263 }
264
265 int section_data_free_block(SECTION_DATA *p_section)
266 {
267 ARTICLE_BLOCK *p_block;
268
269 if (p_section == NULL)
270 {
271 log_error("section_data_free_block() NULL pointer error\n");
272 return -1;
273 }
274
275 if (p_section_data_pool == NULL)
276 {
277 log_error("section_data not initialized\n");
278 return -1;
279 }
280
281 while (p_section->p_head_block != NULL)
282 {
283 p_block = p_section->p_head_block;
284 p_section->p_head_block = p_block->p_next_block;
285 push_free_article_block(p_block);
286 }
287
288 p_section->p_tail_block = NULL;
289 p_section->block_count = 0;
290 p_section->article_count = 0;
291 p_section->delete_count = 0;
292
293 return 0;
294 }
295
296 SECTION_DATA *section_data_find_section_by_name(const char *sname)
297 {
298 int64_t index;
299
300 if (p_section_data_pool == NULL || p_trie_dict_section_data == NULL)
301 {
302 log_error("section_data not initialized\n");
303 return NULL;
304 }
305
306 if (trie_dict_get(p_trie_dict_section_data, sname, &index) != 1)
307 {
308 log_error("trie_dict_get(section_data, %s) error\n", sname);
309 return NULL;
310 }
311
312 return (p_section_data_pool + index);
313 }
314
315 int section_data_append_article(SECTION_DATA *p_section, const ARTICLE *p_article)
316 {
317 ARTICLE_BLOCK *p_block;
318 int32_t last_aid = 0;
319 ARTICLE *p_topic_head;
320 ARTICLE *p_topic_tail;
321
322 if (p_section == NULL || p_article == NULL)
323 {
324 log_error("section_data_append_article() NULL pointer error\n");
325 return -1;
326 }
327
328 if (p_section_data_pool == NULL)
329 {
330 log_error("section_data not initialized\n");
331 return -1;
332 }
333
334 if (p_section->p_tail_block == NULL || p_section->p_tail_block->article_count >= BBS_article_limit_per_block)
335 {
336 if (p_section->block_count >= BBS_article_block_limit_per_section)
337 {
338 log_error("section block count %d reach limit\n", p_section->block_count);
339 return -2;
340 }
341
342 if ((p_block = pop_free_article_block()) == NULL)
343 {
344 log_error("pop_free_article_block() error\n");
345 return -2;
346 }
347
348 p_block->article_count = 0;
349 p_block->p_next_block = NULL;
350
351 if (p_section->p_tail_block == NULL)
352 {
353 p_section->p_head_block = p_block;
354 last_aid = 0;
355 }
356 else
357 {
358 p_section->p_tail_block->p_next_block = p_block;
359 last_aid = p_section->p_tail_block->articles[BBS_article_limit_per_block - 1].aid;
360 }
361 p_section->p_tail_block = p_block;
362 p_section->p_block[p_section->block_count] = p_block;
363 p_section->block_count++;
364 }
365 else
366 {
367 p_block = p_section->p_tail_block;
368 last_aid = p_block->articles[p_block->article_count - 1].aid;
369 }
370
371 // AID of articles should be strictly ascending
372 if (p_article->aid <= last_aid)
373 {
374 log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article->aid, last_aid);
375 return -3;
376 }
377
378 if (p_block->article_count == 0)
379 {
380 p_section->block_head_aid[p_section->block_count - 1] = p_article->aid;
381 }
382
383 if (p_article->tid != 0)
384 {
385 p_topic_head = section_data_find_article_by_aid(p_section, p_article->tid);
386 if (p_topic_head == NULL)
387 {
388 log_error("search head of topic (aid=%d) error\n", p_article->tid);
389 return -4;
390 }
391
392 p_topic_tail = section_data_find_article_by_aid(p_section, p_topic_head->prior_aid);
393 if (p_topic_tail == NULL)
394 {
395 log_error("search tail of topic (aid=%d) error\n", p_topic_head->prior_aid);
396 return -4;
397 }
398 }
399 else
400 {
401 p_topic_head = &(p_block->articles[p_block->article_count]);
402 p_topic_tail = p_topic_head;
403 }
404
405 // Copy article data
406 p_block->articles[p_block->article_count] = *p_article;
407
408 // Link appended article as tail node of topic bi-directional list;
409 p_block->articles[p_block->article_count].prior_aid = p_topic_tail->aid;
410 p_block->articles[p_block->article_count].next_aid = p_topic_head->aid;
411 p_topic_head->prior_aid = p_article->aid;
412 p_topic_tail->next_aid = p_article->aid;
413
414 p_block->article_count++;
415 p_section->article_count++;
416
417 return 0;
418 }
419
420 ARTICLE *section_data_find_article_by_aid(SECTION_DATA *p_section, int32_t aid)
421 {
422 ARTICLE *p_article;
423 ARTICLE_BLOCK *p_block;
424 int left;
425 int right;
426 int mid;
427
428 if (p_section == NULL)
429 {
430 log_error("section_data_find_article_by_aid() NULL pointer error\n");
431 return NULL;
432 }
433
434 if (p_section->block_count == 0) // empty section
435 {
436 return NULL;
437 }
438
439 left = 0;
440 right = p_section->block_count;
441
442 // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
443 while (left < right - 1)
444 {
445 // get block offset no less than mid value of left and right block offsets
446 mid = (left + right) / 2 + (right - left) % 2;
447
448 if (mid >= BBS_article_block_limit_per_section)
449 {
450 log_error("block_m(%d) is out of boundary\n", mid);
451 return NULL;
452 }
453
454 if (aid < p_section->block_head_aid[mid])
455 {
456 right = mid;
457 }
458 else
459 {
460 left = mid;
461 }
462 }
463
464 p_block = p_section->p_block[left];
465
466 left = 0;
467 right = p_block->article_count - 1;
468
469 // aid in the range [ aid of articles[left], aid of articles[right] ]
470 while (left < right)
471 {
472 mid = (left + right) / 2;
473
474 if (aid <= p_block->articles[mid].aid)
475 {
476 right = mid;
477 }
478 else
479 {
480 left = mid + 1;
481 }
482 }
483
484 p_article = &(p_block->articles[left]);
485
486 return p_article;
487 }
488
489 ARTICLE *section_data_find_article_by_index(SECTION_DATA *p_section, int index)
490 {
491 ARTICLE *p_article;
492 ARTICLE_BLOCK *p_block;
493
494 if (p_section == NULL)
495 {
496 log_error("section_data_find_article_by_index() NULL pointer error\n");
497 return NULL;
498 }
499
500 if (index < 0 || index >= p_section->article_count)
501 {
502 log_error("section_data_find_article_by_index(%d) is out of boundary [0, %d)\n", index, p_section->article_count);
503 return NULL;
504 }
505
506 p_block = p_section->p_block[index / BBS_article_limit_per_block];
507 p_article = &(p_block->articles[index % BBS_article_limit_per_block]);
508
509 return p_article;
510 }
511
512 int section_data_mark_del_article(SECTION_DATA *p_section, int32_t aid)
513 {
514 ARTICLE *p_article;
515
516 if (p_section == NULL)
517 {
518 log_error("section_data_mark_del_article() NULL pointer error\n");
519 return -2;
520 }
521
522 p_article = section_data_find_article_by_aid(p_section, aid);
523 if (p_article == NULL)
524 {
525 return -1; // Not found
526 }
527
528 if (p_article->visible == 0)
529 {
530 return 0; // Already deleted
531 }
532
533 p_article->visible = 0;
534 p_section->delete_count++;
535
536 return 1;
537 }

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