/[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.13 - (hide annotations)
Fri May 23 14:04:05 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.12: +67 -18 lines
Content type: text/x-csrc
Move section list to SHM

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 sysadm 1.7 #include <sys/param.h>
27 sysadm 1.1 #include <sys/shm.h>
28     #include <sys/ipc.h>
29    
30 sysadm 1.7 #define ARTICLE_BLOCK_PER_SHM 400 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
31 sysadm 1.3 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
32 sysadm 1.7 #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
33    
34 sysadm 1.12 #define CALCULATE_PAGE_THRESHOLD 100 // Adjust to tune performance of move topic
35    
36 sysadm 1.7 struct article_block_t
37     {
38     ARTICLE articles[ARTICLE_PER_BLOCK];
39     int32_t article_count;
40     struct article_block_t *p_next_block;
41     };
42     typedef struct article_block_t ARTICLE_BLOCK;
43 sysadm 1.1
44     struct article_block_shm_t
45     {
46     int shmid;
47     void *p_shm;
48     };
49     typedef struct article_block_shm_t ARTICLE_BLOCK_SHM;
50    
51 sysadm 1.7 struct article_block_pool_t
52     {
53     ARTICLE_BLOCK_SHM shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
54     int shm_count;
55     ARTICLE_BLOCK *p_block_free_list;
56     ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
57     int32_t block_count;
58     };
59     typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
60    
61     static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
62 sysadm 1.1
63 sysadm 1.13 static int section_list_pool_shmid;
64 sysadm 1.7 static SECTION_LIST *p_section_list_pool = NULL;
65     static int section_list_count = 0;
66     static TRIE_NODE *p_trie_dict_section_list = NULL;
67    
68     int article_block_init(const char *filename, int block_count)
69 sysadm 1.1 {
70     int shmid;
71     int proj_id;
72     key_t key;
73     size_t size;
74     void *p_shm;
75     int i;
76 sysadm 1.7 int block_count_in_shm;
77     ARTICLE_BLOCK *p_block_in_shm;
78     ARTICLE_BLOCK **pp_block_next;
79    
80     if (p_article_block_pool != NULL)
81 sysadm 1.1 {
82 sysadm 1.7 log_error("article_block_pool already initialized\n");
83 sysadm 1.1 return -1;
84     }
85    
86 sysadm 1.7 if (block_count > ARTICLE_BLOCK_PER_POOL)
87 sysadm 1.1 {
88 sysadm 1.7 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
89 sysadm 1.1 return -2;
90     }
91    
92 sysadm 1.7 p_article_block_pool = calloc(1, sizeof(ARTICLE_BLOCK_POOL));
93     if (p_article_block_pool == NULL)
94 sysadm 1.1 {
95 sysadm 1.7 log_error("calloc(ARTICLE_BLOCK_POOL) OOM\n");
96 sysadm 1.1 return -2;
97     }
98    
99     // Allocate shared memory
100 sysadm 1.7 p_article_block_pool->shm_count = 0;
101     pp_block_next = &(p_article_block_pool->p_block_free_list);
102 sysadm 1.1
103 sysadm 1.7 while (block_count > 0)
104 sysadm 1.1 {
105 sysadm 1.7 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
106     block_count -= block_count_in_shm;
107 sysadm 1.1
108 sysadm 1.7 proj_id = getpid() + p_article_block_pool->shm_count;
109 sysadm 1.1 key = ftok(filename, proj_id);
110     if (key == -1)
111     {
112     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
113 sysadm 1.7 article_block_cleanup();
114 sysadm 1.1 return -3;
115     }
116    
117 sysadm 1.7 size = sizeof(shmid) + sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
118 sysadm 1.1 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
119     if (shmid == -1)
120     {
121 sysadm 1.7 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
122     article_block_cleanup();
123 sysadm 1.1 return -3;
124     }
125     p_shm = shmat(shmid, NULL, 0);
126     if (p_shm == (void *)-1)
127     {
128     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
129 sysadm 1.7 article_block_cleanup();
130 sysadm 1.1 return -3;
131     }
132    
133 sysadm 1.7 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
134     (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
135     p_article_block_pool->shm_count++;
136 sysadm 1.1
137 sysadm 1.7 p_block_in_shm = p_shm;
138     *pp_block_next = p_block_in_shm;
139 sysadm 1.1
140 sysadm 1.7 for (i = 0; i < block_count_in_shm; i++)
141 sysadm 1.1 {
142 sysadm 1.7 if (i < block_count_in_shm - 1)
143 sysadm 1.1 {
144 sysadm 1.7 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
145 sysadm 1.1 }
146     else
147     {
148 sysadm 1.7 (p_block_in_shm + i)->p_next_block = NULL;
149     pp_block_next = &((p_block_in_shm + i)->p_next_block);
150 sysadm 1.1 }
151     }
152     }
153    
154 sysadm 1.7 p_article_block_pool->block_count = 0;
155    
156 sysadm 1.1 return 0;
157     }
158    
159 sysadm 1.7 void article_block_cleanup(void)
160     {
161     if (p_article_block_pool != NULL)
162     {
163     for (int i = 0; i < p_article_block_pool->shm_count; i++)
164     {
165     if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
166     {
167     log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
168     }
169    
170     if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
171     {
172     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
173     }
174     }
175    
176     free(p_article_block_pool);
177     p_article_block_pool = NULL;
178     }
179     }
180    
181     inline static ARTICLE_BLOCK *pop_free_article_block(void)
182     {
183     ARTICLE_BLOCK *p_block = NULL;
184    
185     if (p_article_block_pool->p_block_free_list != NULL)
186     {
187     p_block = p_article_block_pool->p_block_free_list;
188     p_article_block_pool->p_block_free_list = p_block->p_next_block;
189     p_block->p_next_block = NULL;
190     p_block->article_count = 0;
191     }
192    
193     return p_block;
194     }
195    
196     inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
197 sysadm 1.1 {
198 sysadm 1.7 p_block->p_next_block = p_article_block_pool->p_block_free_list;
199     p_article_block_pool->p_block_free_list = p_block;
200     }
201    
202     int article_block_reset(void)
203     {
204     ARTICLE_BLOCK *p_block;
205    
206     if (p_article_block_pool == NULL)
207     {
208     log_error("article_block_pool not initialized\n");
209     return -1;
210     }
211 sysadm 1.1
212 sysadm 1.7 while (p_article_block_pool->block_count > 0)
213 sysadm 1.1 {
214 sysadm 1.7 p_article_block_pool->block_count--;
215     p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
216     push_free_article_block(p_block);
217 sysadm 1.1 }
218    
219 sysadm 1.7 return 0;
220     }
221    
222     ARTICLE *article_block_find_by_aid(int32_t aid)
223     {
224     ARTICLE_BLOCK *p_block;
225     int left;
226     int right;
227     int mid;
228    
229     if (p_article_block_pool == NULL)
230 sysadm 1.1 {
231 sysadm 1.7 log_error("article_block_pool not initialized\n");
232     return NULL;
233 sysadm 1.1 }
234    
235 sysadm 1.7 if (p_article_block_pool->block_count == 0) // empty
236 sysadm 1.1 {
237 sysadm 1.7 return NULL;
238 sysadm 1.1 }
239    
240 sysadm 1.7 left = 0;
241     right = p_article_block_pool->block_count;
242    
243     // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
244     while (left < right - 1)
245 sysadm 1.1 {
246 sysadm 1.7 // get block offset no less than mid value of left and right block offsets
247     mid = (left + right) / 2 + (right - left) % 2;
248    
249     if (mid >= p_article_block_pool->block_count)
250 sysadm 1.1 {
251 sysadm 1.7 log_error("block(mid = %d) is out of boundary\n", mid);
252     return NULL;
253     }
254 sysadm 1.1
255 sysadm 1.7 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
256     {
257     right = mid;
258     }
259     else
260     {
261     left = mid;
262 sysadm 1.1 }
263 sysadm 1.7 }
264    
265     p_block = p_article_block_pool->p_block[left];
266    
267     left = 0;
268     right = p_block->article_count - 1;
269    
270     // aid in the range [ aid of articles[left], aid of articles[right] ]
271     while (left < right)
272     {
273     mid = (left + right) / 2;
274 sysadm 1.1
275 sysadm 1.7 if (aid <= p_block->articles[mid].aid)
276     {
277     right = mid;
278     }
279     else
280     {
281     left = mid + 1;
282     }
283 sysadm 1.1 }
284 sysadm 1.7
285     return (p_block->articles + left);
286 sysadm 1.1 }
287    
288 sysadm 1.7 ARTICLE *article_block_find_by_index(int index)
289 sysadm 1.1 {
290 sysadm 1.7 ARTICLE_BLOCK *p_block;
291    
292     if (p_article_block_pool == NULL)
293     {
294     log_error("article_block_pool not initialized\n");
295     return NULL;
296     }
297    
298     if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)
299     {
300     log_error("section_data_find_article_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
301     return NULL;
302     }
303    
304     p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
305 sysadm 1.1
306 sysadm 1.7 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
307 sysadm 1.1 {
308 sysadm 1.7 log_error("section_data_find_article_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
309     return NULL;
310 sysadm 1.1 }
311    
312 sysadm 1.7 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
313 sysadm 1.1 }
314    
315 sysadm 1.13 extern int section_list_pool_init(const char *filename)
316 sysadm 1.1 {
317 sysadm 1.13 int shmid;
318     int proj_id;
319     key_t key;
320     size_t size;
321     void *p_shm;
322    
323     if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
324     {
325     section_list_pool_cleanup();
326     }
327 sysadm 1.7
328 sysadm 1.13 p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));
329 sysadm 1.7 if (p_section_list_pool == NULL)
330     {
331 sysadm 1.13 log_error("calloc(%d SECTION_LIST) OOM\n", BBS_max_section);
332     return -1;
333     }
334    
335     proj_id = (int)(time(NULL) % getpid());
336     key = ftok(filename, proj_id);
337     if (key == -1)
338     {
339     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
340     return -3;
341     }
342 sysadm 1.1
343 sysadm 1.13 size = sizeof(shmid) + sizeof(SECTION_LIST) * BBS_max_section;
344     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
345     if (shmid == -1)
346     {
347     log_error("shmget(section_list_pool, size = %d) error (%d)\n", size, errno);
348     return -3;
349     }
350     p_shm = shmat(shmid, NULL, 0);
351     if (p_shm == (void *)-1)
352     {
353     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
354     return -3;
355 sysadm 1.7 }
356 sysadm 1.1
357 sysadm 1.13 section_list_pool_shmid = shmid;
358     p_section_list_pool = p_shm;
359     section_list_count = 0;
360    
361     p_trie_dict_section_list = trie_dict_create();
362 sysadm 1.7 if (p_trie_dict_section_list == NULL)
363 sysadm 1.1 {
364 sysadm 1.13 log_error("trie_dict_create() OOM\n", BBS_max_section);
365     return -2;
366     }
367    
368     return 0;
369     }
370    
371     SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_name)
372     {
373     SECTION_LIST *p_section;
374    
375     if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
376     {
377     log_error("session_list_pool not initialized\n");
378     return NULL;
379 sysadm 1.1 }
380    
381 sysadm 1.7 if (section_list_count >= BBS_max_section)
382 sysadm 1.1 {
383 sysadm 1.13 log_error("section_list_count exceed limit %d >= %d\n", section_list_count, BBS_max_section);
384 sysadm 1.1 return NULL;
385     }
386    
387 sysadm 1.7 p_section = p_section_list_pool + section_list_count;
388 sysadm 1.1
389 sysadm 1.11 p_section->sid = sid;
390    
391 sysadm 1.1 strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));
392     p_section->sname[sizeof(p_section->sname - 1)] = '\0';
393    
394     strncpy(p_section->stitle, stitle, sizeof(p_section->stitle - 1));
395     p_section->stitle[sizeof(p_section->stitle - 1)] = '\0';
396    
397     strncpy(p_section->master_name, master_name, sizeof(p_section->master_name - 1));
398     p_section->master_name[sizeof(p_section->master_name - 1)] = '\0';
399    
400 sysadm 1.7 if (trie_dict_set(p_trie_dict_section_list, sname, section_list_count) != 1)
401 sysadm 1.1 {
402 sysadm 1.7 log_error("trie_dict_set(section_data, %s, %d) error\n", sname, section_list_count);
403 sysadm 1.1 return NULL;
404     }
405    
406 sysadm 1.7 section_list_reset_articles(p_section);
407    
408     section_list_count++;
409 sysadm 1.1
410     return p_section;
411     }
412    
413 sysadm 1.7 void section_list_reset_articles(SECTION_LIST *p_section)
414 sysadm 1.1 {
415 sysadm 1.7 p_section->article_count = 0;
416 sysadm 1.8 p_section->topic_count = 0;
417     p_section->visible_article_count = 0;
418     p_section->visible_topic_count = 0;
419 sysadm 1.7 p_section->p_article_head = NULL;
420     p_section->p_article_tail = NULL;
421 sysadm 1.1
422 sysadm 1.7 p_section->page_count = 0;
423 sysadm 1.8 p_section->last_page_visible_article_count = 0;
424 sysadm 1.7 }
425 sysadm 1.1
426 sysadm 1.13 void section_list_pool_cleanup(void)
427 sysadm 1.7 {
428     if (p_trie_dict_section_list != NULL)
429 sysadm 1.1 {
430 sysadm 1.7 trie_dict_destroy(p_trie_dict_section_list);
431     p_trie_dict_section_list = NULL;
432 sysadm 1.1 }
433    
434 sysadm 1.7 if (p_section_list_pool != NULL)
435 sysadm 1.1 {
436 sysadm 1.13 if (shmdt(p_section_list_pool) == -1)
437     {
438     log_error("shmdt(shmid = %d) error (%d)\n", section_list_pool_shmid, errno);
439     }
440 sysadm 1.7 p_section_list_pool = NULL;
441 sysadm 1.13
442     if (shmctl(section_list_pool_shmid, IPC_RMID, NULL) == -1)
443     {
444     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", section_list_pool_shmid, errno);
445     }
446 sysadm 1.1 }
447    
448 sysadm 1.7 section_list_count = 0;
449 sysadm 1.1 }
450    
451 sysadm 1.7 SECTION_LIST *section_list_find_by_name(const char *sname)
452 sysadm 1.1 {
453     int64_t index;
454    
455 sysadm 1.7 if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
456 sysadm 1.1 {
457 sysadm 1.7 log_error("section_list not initialized\n");
458 sysadm 1.1 return NULL;
459     }
460    
461 sysadm 1.7 if (trie_dict_get(p_trie_dict_section_list, sname, &index) != 1)
462 sysadm 1.1 {
463     log_error("trie_dict_get(section_data, %s) error\n", sname);
464     return NULL;
465     }
466    
467 sysadm 1.7 return (p_section_list_pool + index);
468 sysadm 1.1 }
469    
470 sysadm 1.7 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
471 sysadm 1.1 {
472     ARTICLE_BLOCK *p_block;
473     int32_t last_aid = 0;
474 sysadm 1.6 ARTICLE *p_article;
475 sysadm 1.1 ARTICLE *p_topic_head;
476     ARTICLE *p_topic_tail;
477    
478 sysadm 1.6 if (p_section == NULL || p_article_src == NULL)
479 sysadm 1.1 {
480 sysadm 1.7 log_error("section_list_append_article() NULL pointer error\n");
481 sysadm 1.1 return -1;
482     }
483    
484 sysadm 1.7 if (p_article_block_pool == NULL)
485 sysadm 1.1 {
486 sysadm 1.7 log_error("article_block_pool not initialized\n");
487 sysadm 1.1 return -1;
488     }
489    
490 sysadm 1.9 if (p_section->article_count >= BBS_article_limit_per_section)
491     {
492     log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
493     return -2;
494     }
495    
496 sysadm 1.7 if (p_article_block_pool->block_count == 0 ||
497     p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
498 sysadm 1.1 {
499     if ((p_block = pop_free_article_block()) == NULL)
500     {
501     log_error("pop_free_article_block() error\n");
502     return -2;
503     }
504    
505 sysadm 1.7 if (p_article_block_pool->block_count > 0)
506 sysadm 1.1 {
507 sysadm 1.7 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
508 sysadm 1.1 }
509 sysadm 1.7
510     p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
511     p_article_block_pool->block_count++;
512 sysadm 1.1 }
513     else
514     {
515 sysadm 1.7 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
516     last_aid = p_block->articles[p_block->article_count - 1].aid;
517 sysadm 1.1 }
518    
519     // AID of articles should be strictly ascending
520 sysadm 1.6 if (p_article_src->aid <= last_aid)
521 sysadm 1.1 {
522 sysadm 1.6 log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
523 sysadm 1.1 return -3;
524     }
525    
526 sysadm 1.7 p_article = (p_block->articles + p_block->article_count);
527     p_block->article_count++;
528     p_section->article_count++;
529    
530     // Copy article data
531     *p_article = *p_article_src;
532 sysadm 1.1
533 sysadm 1.8 if (p_article->visible)
534     {
535     p_section->visible_article_count++;
536     }
537    
538 sysadm 1.7 // Link appended article as tail node of topic bi-directional list
539     if (p_article->tid != 0)
540 sysadm 1.1 {
541 sysadm 1.7 p_topic_head = article_block_find_by_aid(p_article->tid);
542 sysadm 1.1 if (p_topic_head == NULL)
543     {
544 sysadm 1.7 log_error("search head of topic (aid=%d) error\n", p_article->tid);
545 sysadm 1.1 return -4;
546     }
547    
548 sysadm 1.6 p_topic_tail = p_topic_head->p_topic_prior;
549 sysadm 1.1 if (p_topic_tail == NULL)
550     {
551 sysadm 1.7 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
552 sysadm 1.1 return -4;
553     }
554     }
555     else
556     {
557 sysadm 1.8 p_section->topic_count++;
558    
559     if (p_article->visible)
560     {
561     p_section->visible_topic_count++;
562     }
563    
564 sysadm 1.7 p_topic_head = p_article;
565     p_topic_tail = p_article;
566 sysadm 1.1 }
567    
568 sysadm 1.7 p_article->p_topic_prior = p_topic_tail;
569     p_article->p_topic_next = p_topic_head;
570     p_topic_head->p_topic_prior = p_article;
571     p_topic_tail->p_topic_next = p_article;
572 sysadm 1.1
573 sysadm 1.6 // Link appended article as tail node of article bi-directional list
574     if (p_section->p_article_head == NULL)
575     {
576     p_section->p_article_head = p_article;
577     p_section->p_article_tail = p_article;
578     }
579     p_article->p_prior = p_section->p_article_tail;
580     p_article->p_next = p_section->p_article_head;
581     p_section->p_article_head->p_prior = p_article;
582     p_section->p_article_tail->p_next = p_article;
583     p_section->p_article_tail = p_article;
584    
585 sysadm 1.7 // Update page
586 sysadm 1.11 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
587     p_section->article_count == 1)
588 sysadm 1.1 {
589 sysadm 1.7 p_section->p_page_first_article[p_section->page_count] = p_article;
590     p_section->page_count++;
591 sysadm 1.8 p_section->last_page_visible_article_count = 0;
592 sysadm 1.1 }
593 sysadm 1.9
594     if (p_article->visible)
595     {
596     p_section->last_page_visible_article_count++;
597     }
598 sysadm 1.1
599 sysadm 1.7 return 0;
600 sysadm 1.2 }
601    
602 sysadm 1.7 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
603 sysadm 1.1 {
604     ARTICLE *p_article;
605 sysadm 1.8 ARTICLE *p_reply;
606     int affected_count = 0;
607 sysadm 1.1
608 sysadm 1.2 if (p_section == NULL)
609     {
610 sysadm 1.7 log_error("section_list_set_article_visible() NULL pointer error\n");
611 sysadm 1.2 return -2;
612     }
613    
614 sysadm 1.7 p_article = article_block_find_by_aid(aid);
615 sysadm 1.1 if (p_article == NULL)
616     {
617     return -1; // Not found
618     }
619    
620 sysadm 1.7 if (p_article->visible == visible)
621 sysadm 1.1 {
622 sysadm 1.7 return 0; // Already set
623 sysadm 1.1 }
624    
625 sysadm 1.8 if (visible == 0) // 1 -> 0
626     {
627     p_section->visible_article_count--;
628    
629     if (p_article->tid == 0)
630     {
631     p_section->visible_topic_count--;
632    
633     // Set related visible replies to invisible
634     for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
635     {
636     if (p_reply->tid != aid)
637     {
638     log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
639     continue;
640     }
641    
642     if (p_reply->visible == 1)
643     {
644     p_reply->visible = 0;
645     p_section->visible_article_count--;
646     affected_count++;
647     }
648     }
649     }
650     }
651     else // 0 -> 1
652     {
653     p_section->visible_article_count++;
654    
655     if (p_article->tid == 0)
656     {
657     p_section->visible_topic_count++;
658     }
659     }
660    
661 sysadm 1.7 p_article->visible = visible;
662 sysadm 1.8 affected_count++;
663    
664     return affected_count;
665     }
666    
667 sysadm 1.11 ARTICLE *section_list_find_article_with_offset(SECTION_LIST *p_section, int32_t aid, int32_t *p_page, int32_t *p_offset, ARTICLE **pp_next)
668 sysadm 1.8 {
669     ARTICLE *p_article;
670     int left;
671     int right;
672     int mid;
673    
674     *p_page = -1;
675     *p_offset = -1;
676 sysadm 1.11 *pp_next = NULL;
677 sysadm 1.8
678     if (p_section == NULL)
679     {
680     log_error("section_list_find_article_with_offset() NULL pointer error\n");
681     return NULL;
682     }
683 sysadm 1.7
684 sysadm 1.8 if (p_section->article_count == 0) // empty
685     {
686     *p_page = 0;
687     *p_offset = 0;
688     return NULL;
689     }
690    
691     left = 0;
692     right = p_section->page_count;
693    
694     // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]
695     while (left < right - 1)
696     {
697     // get page id no less than mid value of left page id and right page id
698     mid = (left + right) / 2 + (right - left) % 2;
699 sysadm 1.1
700 sysadm 1.8 if (mid >= p_section->page_count)
701     {
702     log_error("page id (mid = %d) is out of boundary\n", mid);
703     return NULL;
704     }
705    
706     if (aid < p_section->p_page_first_article[mid]->aid)
707     {
708     right = mid;
709     }
710     else
711     {
712     left = mid;
713     }
714     }
715    
716     *p_page = left;
717    
718     p_article = p_section->p_page_first_article[*p_page];
719    
720     // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
721 sysadm 1.11 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
722 sysadm 1.8
723     // left will be the offset of article found or offset to insert
724     left = 0;
725    
726 sysadm 1.10 while (aid > p_article->aid)
727 sysadm 1.8 {
728     p_article = p_article->p_next;
729     left++;
730    
731 sysadm 1.11 if (aid == p_article->aid)
732     {
733     *pp_next = p_article->p_next;
734     break;
735     }
736    
737 sysadm 1.8 // over last article in the page
738     if (p_article == p_section->p_article_head || p_article->aid >= right)
739     {
740 sysadm 1.11 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
741     *p_offset = left;
742     return NULL; // not found
743 sysadm 1.8 }
744     }
745    
746 sysadm 1.11 if (aid < p_article->aid)
747     {
748     *pp_next = p_article;
749     p_article = NULL; // not found
750     }
751     else // aid == p_article->aid
752 sysadm 1.10 {
753 sysadm 1.11 *pp_next = p_article->p_next;
754 sysadm 1.10 }
755    
756 sysadm 1.8 *p_offset = left;
757    
758     return p_article;
759     }
760    
761     int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
762     {
763 sysadm 1.9 ARTICLE *p_article;
764 sysadm 1.11 ARTICLE *p_next;
765 sysadm 1.9 int32_t page;
766     int32_t offset;
767     int visible_article_count;
768     int page_head_set;
769 sysadm 1.8
770     if (p_section == NULL)
771     {
772     log_error("section_list_calculate_page() NULL pointer error\n");
773     return -1;
774     }
775    
776 sysadm 1.11 if (p_section->article_count == 0) // empty
777     {
778     p_section->page_count = 0;
779     p_section->last_page_visible_article_count = 0;
780    
781     return 0;
782     }
783    
784     if (start_aid > 0)
785 sysadm 1.9 {
786 sysadm 1.11 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
787     if (p_article == NULL)
788 sysadm 1.9 {
789 sysadm 1.11 if (page < 0)
790     {
791     return -1;
792     }
793     log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
794     start_aid, p_section->sid);
795     return -2;
796 sysadm 1.9 }
797    
798 sysadm 1.11 if (offset > 0)
799     {
800     p_article = p_section->p_page_first_article[page];
801     }
802 sysadm 1.9 }
803 sysadm 1.11 else
804 sysadm 1.9 {
805 sysadm 1.11 p_article = p_section->p_article_head;
806     page = 0;
807     offset = 0;
808 sysadm 1.9 }
809    
810     visible_article_count = 0;
811     page_head_set = 0;
812    
813     do
814     {
815     if (!page_head_set && visible_article_count == 0)
816     {
817     p_section->p_page_first_article[page] = p_article;
818     page_head_set = 1;
819     }
820    
821     if (p_article->visible)
822     {
823     visible_article_count++;
824     }
825    
826     p_article = p_article->p_next;
827    
828     // skip remaining invisible articles
829     while (p_article->visible == 0 && p_article != p_section->p_article_head)
830     {
831     p_article = p_article->p_next;
832     }
833    
834     if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
835     {
836     page++;
837     visible_article_count = 0;
838     page_head_set = 0;
839    
840     if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
841     {
842     log_error("Count of page exceed limit in section %d\n", p_section->sid);
843     break;
844     }
845     }
846     } while (p_article != p_section->p_article_head);
847    
848     p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
849     p_section->last_page_visible_article_count = visible_article_count;
850    
851 sysadm 1.8 return 0;
852 sysadm 1.1 }
853 sysadm 1.11
854     int section_list_count_of_topic_articles(int32_t aid)
855     {
856     ARTICLE *p_article;
857     int article_count;
858    
859     p_article = article_block_find_by_aid(aid);
860     if (p_article == NULL)
861     {
862     return 0; // Not found
863     }
864    
865     article_count = 0;
866    
867     do
868     {
869     article_count++;
870     p_article = p_article->p_topic_next;
871     } while (p_article->aid != aid);
872    
873     return article_count;
874     }
875    
876     int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
877     {
878     ARTICLE *p_article;
879     ARTICLE *p_next;
880     int32_t page;
881     int32_t offset;
882     int32_t move_article_count;
883     int32_t dest_article_count_old;
884     int32_t last_unaffected_aid_src;
885 sysadm 1.12 int32_t first_inserted_aid_dest;
886     int move_counter;
887 sysadm 1.11
888     if (p_section_dest == NULL)
889     {
890     log_error("section_list_move_topic() NULL pointer error\n");
891     return -1;
892     }
893    
894     if ((p_article = section_list_find_article_with_offset(p_section_src, aid, &page, &offset, &p_next)) == NULL)
895     {
896     log_error("section_list_move_topic() error: article %d not found in section %d\n", aid, p_section_src->sid);
897     return -2;
898     }
899    
900     if (p_article->tid != 0)
901     {
902     log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
903     return -2;
904     }
905    
906     last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
907    
908     move_article_count = section_list_count_of_topic_articles(aid);
909     if (move_article_count <= 0)
910     {
911     log_error("section_list_count_of_topic_articles(aid = %d) <= 0\n", aid);
912     return -2;
913     }
914    
915     if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
916     {
917     log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
918     p_section_dest->article_count + move_article_count, p_section_dest->sid);
919     return -3;
920     }
921    
922     dest_article_count_old = p_section_dest->article_count;
923 sysadm 1.12 move_counter = 0;
924     first_inserted_aid_dest = p_article->aid;
925 sysadm 1.11
926     do
927     {
928     if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
929     {
930     log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
931     return -4;
932     }
933    
934     // Remove from bi-directional article list of src section
935     if (p_section_src->p_article_head == p_article)
936     {
937     p_section_src->p_article_head = p_article->p_next;
938     }
939     if (p_section_src->p_article_tail == p_article)
940     {
941     p_section_src->p_article_tail = p_article->p_prior;
942     }
943     if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
944     {
945     p_section_src->p_article_head = NULL;
946     p_section_src->p_article_tail = NULL;
947     }
948    
949     p_article->p_prior->p_next = p_article->p_next;
950     p_article->p_next->p_prior = p_article->p_prior;
951    
952     // Insert into bi-directional article list of dest section
953     if (p_next == NULL) // empty section
954     {
955     p_section_dest->p_article_head = p_article;
956     p_section_dest->p_article_tail = p_article;
957     p_article->p_prior = p_article;
958     p_article->p_next = p_article;
959     }
960     else
961     {
962     if (p_section_dest->p_article_head == p_next)
963     {
964     if (p_article->aid < p_next->aid)
965     {
966     p_section_dest->p_article_head = p_article;
967     }
968     else // p_article->aid > p_next->aid
969     {
970     p_section_dest->p_article_tail = p_article;
971     }
972     }
973    
974     p_article->p_prior = p_next->p_prior;
975     p_article->p_next = p_next;
976     p_next->p_prior->p_next = p_article;
977     p_next->p_prior = p_article;
978     }
979    
980     // Update article / topic counter of src / desc section
981     p_section_src->article_count--;
982     p_section_dest->article_count++;
983     if (p_article->tid == 0)
984     {
985     p_section_src->topic_count--;
986     p_section_dest->topic_count++;
987     }
988    
989     // Update visible article / topic counter of src / desc section
990     if (p_article->visible)
991     {
992     p_section_src->visible_article_count--;
993     p_section_dest->visible_article_count++;
994     if (p_article->tid == 0)
995     {
996     p_section_src->visible_topic_count--;
997     p_section_dest->visible_topic_count++;
998     }
999     }
1000    
1001     // Update page for empty dest section
1002     if (p_section_dest->article_count == 1)
1003     {
1004     p_section_dest->p_page_first_article[0] = p_article;
1005     p_section_dest->page_count = 1;
1006     p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1007     }
1008    
1009     p_article = p_article->p_topic_next;
1010 sysadm 1.12
1011     move_counter++;
1012     if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1013     {
1014     // Re-calculate pages of desc section
1015     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1016     {
1017     log_error("section_list_calculate_page(section = %d, aid = %d) error\n",
1018     p_section_dest->sid, first_inserted_aid_dest);
1019     }
1020    
1021     first_inserted_aid_dest = p_article->aid;
1022     }
1023 sysadm 1.11 } while (p_article->aid != aid);
1024    
1025     if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1026     {
1027     log_error("section_list_move_topic() error: count of moved articles %d != %d\n",
1028     p_section_dest->article_count - dest_article_count_old, move_article_count);
1029     }
1030    
1031 sysadm 1.12 // Re-calculate pages of src section
1032 sysadm 1.11 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1033     {
1034 sysadm 1.12 log_error("section_list_calculate_page(section = %d, aid = %d) error at aid = %d\n",
1035     p_section_src->sid, last_unaffected_aid_src, aid);
1036 sysadm 1.11 }
1037 sysadm 1.12
1038     if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1039 sysadm 1.11 {
1040 sysadm 1.12 // Re-calculate pages of desc section
1041     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1042     {
1043     log_error("section_list_calculate_page(section = %d, aid = %d) error\n",
1044     p_section_dest->sid, first_inserted_aid_dest);
1045     }
1046 sysadm 1.11 }
1047    
1048     return move_article_count;
1049     }

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