/[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.3 - (show annotations)
Wed May 21 06:17:52 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.2: +4 -1 lines
Content type: text/x-csrc
Update

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

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