/[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.11 - (hide annotations)
Fri May 23 07:06:57 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.10: +224 -16 lines
Content type: text/x-csrc
Add section_list_move_topic()

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

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