/[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.7 - (show annotations)
Thu May 22 06:20:47 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.6: +285 -294 lines
Content type: text/x-csrc
Use single article_block_pool for all sections

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

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