/[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.14 - (hide annotations)
Sat May 24 03:32:32 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.13: +129 -26 lines
Content type: text/x-csrc
Add section_list_find_by_sid
Add sid check in section / article related functions

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

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