/[LeafOK_CVS]/lbbs/src/section_list.c
ViewVC logotype

Contents of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (show annotations)
Fri May 23 14:04:05 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.12: +67 -18 lines
Content type: text/x-csrc
Move section list to SHM

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

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