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

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