/[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.6 - (show annotations)
Wed May 21 12:43:04 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.5: +39 -18 lines
Content type: text/x-csrc
Add bi-directional list for section articles

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 p_section->p_article_head = NULL;
255 p_section->p_article_tail = NULL;
256
257 if (trie_dict_set(p_trie_dict_section_data, sname, index) != 1)
258 {
259 log_error("trie_dict_set(section_data, %s, %d) error\n", sname, index);
260 return NULL;
261 }
262
263 section_data_count++;
264
265 return p_section;
266 }
267
268 int section_data_free_block(SECTION_DATA *p_section)
269 {
270 ARTICLE_BLOCK *p_block;
271
272 if (p_section == NULL)
273 {
274 log_error("section_data_free_block() NULL pointer error\n");
275 return -1;
276 }
277
278 if (p_section_data_pool == NULL)
279 {
280 log_error("section_data not initialized\n");
281 return -1;
282 }
283
284 while (p_section->p_head_block != NULL)
285 {
286 p_block = p_section->p_head_block;
287 p_section->p_head_block = p_block->p_next_block;
288 push_free_article_block(p_block);
289 }
290
291 p_section->p_tail_block = NULL;
292 p_section->block_count = 0;
293 p_section->article_count = 0;
294 p_section->delete_count = 0;
295
296 p_section->p_article_head = NULL;
297 p_section->p_article_tail = NULL;
298
299 return 0;
300 }
301
302 SECTION_DATA *section_data_find_section_by_name(const char *sname)
303 {
304 int64_t index;
305
306 if (p_section_data_pool == NULL || p_trie_dict_section_data == NULL)
307 {
308 log_error("section_data not initialized\n");
309 return NULL;
310 }
311
312 if (trie_dict_get(p_trie_dict_section_data, sname, &index) != 1)
313 {
314 log_error("trie_dict_get(section_data, %s) error\n", sname);
315 return NULL;
316 }
317
318 return (p_section_data_pool + index);
319 }
320
321 int section_data_append_article(SECTION_DATA *p_section, const ARTICLE *p_article_src)
322 {
323 ARTICLE_BLOCK *p_block;
324 int32_t last_aid = 0;
325 ARTICLE *p_article;
326 ARTICLE *p_topic_head;
327 ARTICLE *p_topic_tail;
328
329 if (p_section == NULL || p_article_src == NULL)
330 {
331 log_error("section_data_append_article() NULL pointer error\n");
332 return -1;
333 }
334
335 if (p_section_data_pool == NULL)
336 {
337 log_error("section_data not initialized\n");
338 return -1;
339 }
340
341 if (p_section->p_tail_block == NULL || p_section->p_tail_block->article_count >= BBS_article_limit_per_block)
342 {
343 if (p_section->block_count >= BBS_article_block_limit_per_section)
344 {
345 log_error("section block count %d reach limit\n", p_section->block_count);
346 return -2;
347 }
348
349 if ((p_block = pop_free_article_block()) == NULL)
350 {
351 log_error("pop_free_article_block() error\n");
352 return -2;
353 }
354
355 p_block->article_count = 0;
356 p_block->p_next_block = NULL;
357
358 if (p_section->p_tail_block == NULL)
359 {
360 p_section->p_head_block = p_block;
361 last_aid = 0;
362 }
363 else
364 {
365 p_section->p_tail_block->p_next_block = p_block;
366 last_aid = p_section->p_article_tail->aid;
367 }
368 p_section->p_tail_block = p_block;
369 p_section->p_block[p_section->block_count] = p_block;
370 p_section->block_count++;
371 }
372 else
373 {
374 p_block = p_section->p_tail_block;
375 last_aid = p_section->p_article_tail->aid;
376 }
377
378 // AID of articles should be strictly ascending
379 if (p_article_src->aid <= last_aid)
380 {
381 log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
382 return -3;
383 }
384
385 if (p_block->article_count == 0)
386 {
387 p_section->block_head_aid[p_section->block_count - 1] = p_article_src->aid;
388 }
389
390 if (p_article_src->tid != 0)
391 {
392 p_topic_head = section_data_find_article_by_aid(p_section, p_article_src->tid);
393 if (p_topic_head == NULL)
394 {
395 log_error("search head of topic (aid=%d) error\n", p_article_src->tid);
396 return -4;
397 }
398
399 p_topic_tail = p_topic_head->p_topic_prior;
400 if (p_topic_tail == NULL)
401 {
402 log_error("tail of topic (aid=%d) is NULL\n", p_article_src->tid);
403 return -4;
404 }
405 }
406 else
407 {
408 p_topic_head = &(p_block->articles[p_block->article_count]);
409 p_topic_tail = p_topic_head;
410 }
411
412 p_article = &(p_block->articles[p_block->article_count]);
413
414 // Copy article data
415 *p_article = *p_article_src;
416
417 // Link appended article as tail node of article bi-directional list
418 if (p_section->p_article_head == NULL)
419 {
420 p_section->p_article_head = p_article;
421 p_section->p_article_tail = p_article;
422 }
423 p_article->p_prior = p_section->p_article_tail;
424 p_article->p_next = p_section->p_article_head;
425 p_section->p_article_head->p_prior = p_article;
426 p_section->p_article_tail->p_next = p_article;
427 p_section->p_article_tail = p_article;
428
429 // Link appended article as tail node of topic bi-directional list
430 p_article->p_topic_prior = p_topic_tail;
431 p_article->p_topic_next = p_topic_head;
432 p_topic_head->p_topic_prior = p_article;
433 p_topic_tail->p_topic_next = p_article;
434
435 p_block->article_count++;
436 p_section->article_count++;
437
438 return 0;
439 }
440
441 ARTICLE *section_data_find_article_by_aid(SECTION_DATA *p_section, int32_t aid)
442 {
443 ARTICLE *p_article;
444 ARTICLE_BLOCK *p_block;
445 int left;
446 int right;
447 int mid;
448
449 if (p_section == NULL)
450 {
451 log_error("section_data_find_article_by_aid() NULL pointer error\n");
452 return NULL;
453 }
454
455 if (p_section->block_count == 0) // empty section
456 {
457 return NULL;
458 }
459
460 left = 0;
461 right = p_section->block_count;
462
463 // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
464 while (left < right - 1)
465 {
466 // get block offset no less than mid value of left and right block offsets
467 mid = (left + right) / 2 + (right - left) % 2;
468
469 if (mid >= BBS_article_block_limit_per_section)
470 {
471 log_error("block_m(%d) is out of boundary\n", mid);
472 return NULL;
473 }
474
475 if (aid < p_section->block_head_aid[mid])
476 {
477 right = mid;
478 }
479 else
480 {
481 left = mid;
482 }
483 }
484
485 p_block = p_section->p_block[left];
486
487 left = 0;
488 right = p_block->article_count - 1;
489
490 // aid in the range [ aid of articles[left], aid of articles[right] ]
491 while (left < right)
492 {
493 mid = (left + right) / 2;
494
495 if (aid <= p_block->articles[mid].aid)
496 {
497 right = mid;
498 }
499 else
500 {
501 left = mid + 1;
502 }
503 }
504
505 p_article = &(p_block->articles[left]);
506
507 return p_article;
508 }
509
510 ARTICLE *section_data_find_article_by_index(SECTION_DATA *p_section, int index)
511 {
512 ARTICLE *p_article;
513 ARTICLE_BLOCK *p_block;
514
515 if (p_section == NULL)
516 {
517 log_error("section_data_find_article_by_index() NULL pointer error\n");
518 return NULL;
519 }
520
521 if (index < 0 || index >= p_section->article_count)
522 {
523 log_error("section_data_find_article_by_index(%d) is out of boundary [0, %d)\n", index, p_section->article_count);
524 return NULL;
525 }
526
527 p_block = p_section->p_block[index / BBS_article_limit_per_block];
528 p_article = &(p_block->articles[index % BBS_article_limit_per_block]);
529
530 return p_article;
531 }
532
533 int section_data_mark_del_article(SECTION_DATA *p_section, int32_t aid)
534 {
535 ARTICLE *p_article;
536
537 if (p_section == NULL)
538 {
539 log_error("section_data_mark_del_article() NULL pointer error\n");
540 return -2;
541 }
542
543 p_article = section_data_find_article_by_aid(p_section, aid);
544 if (p_article == NULL)
545 {
546 return -1; // Not found
547 }
548
549 if (p_article->visible == 0)
550 {
551 return 0; // Already deleted
552 }
553
554 p_article->visible = 0;
555 p_section->delete_count++;
556
557 return 1;
558 }

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