/[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.2 - (hide annotations)
Wed May 21 05:36:04 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.1: +34 -5 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     #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 sysadm 1.2 p_topic_head = section_data_find_article_by_aid(p_section, p_article->tid);
384 sysadm 1.1 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 sysadm 1.2 p_topic_tail = section_data_find_article_by_aid(p_section, p_topic_head->prior_aid);
391 sysadm 1.1 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 sysadm 1.2 ARTICLE *section_data_find_article_by_aid(SECTION_DATA *p_section, int32_t aid)
419 sysadm 1.1 {
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 sysadm 1.2 log_error("section_data_find_article_by_aid() NULL pointer error\n");
429 sysadm 1.1 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 sysadm 1.2 ARTICLE *section_data_find_article_by_index(SECTION_DATA *p_section, int index)
488     {
489     ARTICLE *p_article;
490     ARTICLE_BLOCK *p_block;
491    
492     if (p_section == NULL)
493     {
494     log_error("section_data_find_article_by_index() NULL pointer error\n");
495     return NULL;
496     }
497    
498     if (index < 0 || index >= p_section->article_count)
499     {
500     log_error("section_data_find_article_by_index(%d) is out of boundary [0, %d)\n", index, p_section->article_count);
501     return NULL;
502     }
503    
504     p_block = p_section->p_block[index / BBS_article_limit_per_block];
505     p_article = &(p_block->articles[index % BBS_article_limit_per_block]);
506    
507     return p_article;
508     }
509    
510 sysadm 1.1 int section_data_mark_del_article(SECTION_DATA *p_section, int32_t aid)
511     {
512     ARTICLE *p_article;
513    
514 sysadm 1.2 if (p_section == NULL)
515     {
516     log_error("section_data_mark_del_article() NULL pointer error\n");
517     return -2;
518     }
519    
520     p_article = section_data_find_article_by_aid(p_section, aid);
521 sysadm 1.1 if (p_article == NULL)
522     {
523     return -1; // Not found
524     }
525    
526     if (p_article->visible == 0)
527     {
528     return 0; // Already deleted
529     }
530    
531     p_article->visible = 0;
532     p_section->delete_count++;
533    
534     return 1;
535     }

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