/[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.6 - (hide annotations)
Wed May 21 12:43:04 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.5: +39 -18 lines
Content type: text/x-csrc
Add bi-directional list for section articles

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 sysadm 1.6 p_section->p_article_head = NULL;
255     p_section->p_article_tail = NULL;
256    
257 sysadm 1.1 if (trie_dict_set(p_trie_dict_section_data, sname, index) != 1)
258     {
259     log_error("trie_dict_set(section_data, %s, %d) error\n", sname, index);
260     return NULL;
261     }
262    
263     section_data_count++;
264    
265     return p_section;
266     }
267    
268     int section_data_free_block(SECTION_DATA *p_section)
269     {
270     ARTICLE_BLOCK *p_block;
271    
272     if (p_section == NULL)
273     {
274     log_error("section_data_free_block() NULL pointer error\n");
275     return -1;
276     }
277    
278     if (p_section_data_pool == NULL)
279     {
280     log_error("section_data not initialized\n");
281     return -1;
282     }
283    
284     while (p_section->p_head_block != NULL)
285     {
286     p_block = p_section->p_head_block;
287     p_section->p_head_block = p_block->p_next_block;
288     push_free_article_block(p_block);
289     }
290    
291     p_section->p_tail_block = NULL;
292     p_section->block_count = 0;
293     p_section->article_count = 0;
294     p_section->delete_count = 0;
295    
296 sysadm 1.6 p_section->p_article_head = NULL;
297     p_section->p_article_tail = NULL;
298    
299 sysadm 1.1 return 0;
300     }
301    
302 sysadm 1.5 SECTION_DATA *section_data_find_section_by_name(const char *sname)
303 sysadm 1.1 {
304     int64_t index;
305    
306     if (p_section_data_pool == NULL || p_trie_dict_section_data == NULL)
307     {
308     log_error("section_data not initialized\n");
309     return NULL;
310     }
311    
312     if (trie_dict_get(p_trie_dict_section_data, sname, &index) != 1)
313     {
314     log_error("trie_dict_get(section_data, %s) error\n", sname);
315     return NULL;
316     }
317    
318     return (p_section_data_pool + index);
319     }
320    
321 sysadm 1.6 int section_data_append_article(SECTION_DATA *p_section, const ARTICLE *p_article_src)
322 sysadm 1.1 {
323     ARTICLE_BLOCK *p_block;
324     int32_t last_aid = 0;
325 sysadm 1.6 ARTICLE *p_article;
326 sysadm 1.1 ARTICLE *p_topic_head;
327     ARTICLE *p_topic_tail;
328    
329 sysadm 1.6 if (p_section == NULL || p_article_src == NULL)
330 sysadm 1.1 {
331     log_error("section_data_append_article() NULL pointer error\n");
332     return -1;
333     }
334    
335     if (p_section_data_pool == NULL)
336     {
337     log_error("section_data not initialized\n");
338     return -1;
339     }
340    
341     if (p_section->p_tail_block == NULL || p_section->p_tail_block->article_count >= BBS_article_limit_per_block)
342     {
343     if (p_section->block_count >= BBS_article_block_limit_per_section)
344     {
345     log_error("section block count %d reach limit\n", p_section->block_count);
346     return -2;
347     }
348    
349     if ((p_block = pop_free_article_block()) == NULL)
350     {
351     log_error("pop_free_article_block() error\n");
352     return -2;
353     }
354    
355     p_block->article_count = 0;
356     p_block->p_next_block = NULL;
357    
358     if (p_section->p_tail_block == NULL)
359     {
360     p_section->p_head_block = p_block;
361     last_aid = 0;
362     }
363     else
364     {
365     p_section->p_tail_block->p_next_block = p_block;
366 sysadm 1.6 last_aid = p_section->p_article_tail->aid;
367 sysadm 1.1 }
368     p_section->p_tail_block = p_block;
369     p_section->p_block[p_section->block_count] = p_block;
370     p_section->block_count++;
371     }
372     else
373     {
374     p_block = p_section->p_tail_block;
375 sysadm 1.6 last_aid = p_section->p_article_tail->aid;
376 sysadm 1.1 }
377    
378     // AID of articles should be strictly ascending
379 sysadm 1.6 if (p_article_src->aid <= last_aid)
380 sysadm 1.1 {
381 sysadm 1.6 log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
382 sysadm 1.1 return -3;
383     }
384    
385     if (p_block->article_count == 0)
386     {
387 sysadm 1.6 p_section->block_head_aid[p_section->block_count - 1] = p_article_src->aid;
388 sysadm 1.1 }
389    
390 sysadm 1.6 if (p_article_src->tid != 0)
391 sysadm 1.1 {
392 sysadm 1.6 p_topic_head = section_data_find_article_by_aid(p_section, p_article_src->tid);
393 sysadm 1.1 if (p_topic_head == NULL)
394     {
395 sysadm 1.6 log_error("search head of topic (aid=%d) error\n", p_article_src->tid);
396 sysadm 1.1 return -4;
397     }
398    
399 sysadm 1.6 p_topic_tail = p_topic_head->p_topic_prior;
400 sysadm 1.1 if (p_topic_tail == NULL)
401     {
402 sysadm 1.6 log_error("tail of topic (aid=%d) is NULL\n", p_article_src->tid);
403 sysadm 1.1 return -4;
404     }
405     }
406     else
407     {
408     p_topic_head = &(p_block->articles[p_block->article_count]);
409     p_topic_tail = p_topic_head;
410     }
411    
412 sysadm 1.6 p_article = &(p_block->articles[p_block->article_count]);
413    
414 sysadm 1.1 // Copy article data
415 sysadm 1.6 *p_article = *p_article_src;
416 sysadm 1.1
417 sysadm 1.6 // Link appended article as tail node of article bi-directional list
418     if (p_section->p_article_head == NULL)
419     {
420     p_section->p_article_head = p_article;
421     p_section->p_article_tail = p_article;
422     }
423     p_article->p_prior = p_section->p_article_tail;
424     p_article->p_next = p_section->p_article_head;
425     p_section->p_article_head->p_prior = p_article;
426     p_section->p_article_tail->p_next = p_article;
427     p_section->p_article_tail = p_article;
428    
429     // Link appended article as tail node of topic bi-directional list
430     p_article->p_topic_prior = p_topic_tail;
431     p_article->p_topic_next = p_topic_head;
432     p_topic_head->p_topic_prior = p_article;
433     p_topic_tail->p_topic_next = p_article;
434 sysadm 1.1
435     p_block->article_count++;
436     p_section->article_count++;
437    
438     return 0;
439     }
440    
441 sysadm 1.2 ARTICLE *section_data_find_article_by_aid(SECTION_DATA *p_section, int32_t aid)
442 sysadm 1.1 {
443     ARTICLE *p_article;
444     ARTICLE_BLOCK *p_block;
445     int left;
446     int right;
447     int mid;
448    
449     if (p_section == NULL)
450     {
451 sysadm 1.2 log_error("section_data_find_article_by_aid() NULL pointer error\n");
452 sysadm 1.1 return NULL;
453     }
454    
455     if (p_section->block_count == 0) // empty section
456     {
457     return NULL;
458     }
459    
460     left = 0;
461     right = p_section->block_count;
462    
463     // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
464     while (left < right - 1)
465     {
466     // get block offset no less than mid value of left and right block offsets
467     mid = (left + right) / 2 + (right - left) % 2;
468    
469     if (mid >= BBS_article_block_limit_per_section)
470     {
471     log_error("block_m(%d) is out of boundary\n", mid);
472     return NULL;
473     }
474    
475     if (aid < p_section->block_head_aid[mid])
476     {
477     right = mid;
478     }
479     else
480     {
481     left = mid;
482     }
483     }
484    
485     p_block = p_section->p_block[left];
486    
487     left = 0;
488     right = p_block->article_count - 1;
489    
490     // aid in the range [ aid of articles[left], aid of articles[right] ]
491     while (left < right)
492     {
493     mid = (left + right) / 2;
494    
495     if (aid <= p_block->articles[mid].aid)
496     {
497     right = mid;
498     }
499     else
500     {
501     left = mid + 1;
502     }
503     }
504    
505     p_article = &(p_block->articles[left]);
506    
507     return p_article;
508     }
509    
510 sysadm 1.2 ARTICLE *section_data_find_article_by_index(SECTION_DATA *p_section, int index)
511     {
512     ARTICLE *p_article;
513     ARTICLE_BLOCK *p_block;
514    
515     if (p_section == NULL)
516     {
517     log_error("section_data_find_article_by_index() NULL pointer error\n");
518     return NULL;
519     }
520    
521     if (index < 0 || index >= p_section->article_count)
522     {
523     log_error("section_data_find_article_by_index(%d) is out of boundary [0, %d)\n", index, p_section->article_count);
524     return NULL;
525     }
526    
527     p_block = p_section->p_block[index / BBS_article_limit_per_block];
528     p_article = &(p_block->articles[index % BBS_article_limit_per_block]);
529    
530     return p_article;
531     }
532    
533 sysadm 1.1 int section_data_mark_del_article(SECTION_DATA *p_section, int32_t aid)
534     {
535     ARTICLE *p_article;
536    
537 sysadm 1.2 if (p_section == NULL)
538     {
539     log_error("section_data_mark_del_article() NULL pointer error\n");
540     return -2;
541     }
542    
543     p_article = section_data_find_article_by_aid(p_section, aid);
544 sysadm 1.1 if (p_article == NULL)
545     {
546     return -1; // Not found
547     }
548    
549     if (p_article->visible == 0)
550     {
551     return 0; // Already deleted
552     }
553    
554     p_article->visible = 0;
555     p_section->delete_count++;
556    
557     return 1;
558     }

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