/[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.1 - (show annotations)
Wed May 21 04:07:42 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Content type: text/x-csrc
Add section_list

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

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