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

Annotation of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide 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 sysadm 1.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 sysadm 1.3 // 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 sysadm 1.1 #define ARTICLE_BLOCK_PER_SHM 50 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
34 sysadm 1.3 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
35 sysadm 1.1
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 sysadm 1.2 p_topic_head = section_data_find_article_by_aid(p_section, p_article->tid);
387 sysadm 1.1 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 sysadm 1.2 p_topic_tail = section_data_find_article_by_aid(p_section, p_topic_head->prior_aid);
394 sysadm 1.1 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 sysadm 1.2 ARTICLE *section_data_find_article_by_aid(SECTION_DATA *p_section, int32_t aid)
422 sysadm 1.1 {
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 sysadm 1.2 log_error("section_data_find_article_by_aid() NULL pointer error\n");
432 sysadm 1.1 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 sysadm 1.2 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 sysadm 1.1 int section_data_mark_del_article(SECTION_DATA *p_section, int32_t aid)
514     {
515     ARTICLE *p_article;
516    
517 sysadm 1.2 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 sysadm 1.1 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