/[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.5 - (hide annotations)
Wed May 21 09:18:17 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.4: +1 -1 lines
Content type: text/x-csrc
Add test for section_data_find_section_by_name

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

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