/[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.12 - (show annotations)
Fri May 23 10:45:54 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.11: +30 -4 lines
Content type: text/x-csrc
Update

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

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