/[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.15 - (show annotations)
Sat May 24 03:41:34 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.14: +6 -0 lines
Content type: text/x-csrc
Add error detection

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 #define SID_STR_LEN 5 // 32-bit + NULL
37
38 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
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 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
65 static int section_list_pool_shmid;
66 static SECTION_LIST *p_section_list_pool = NULL;
67 static int section_list_count = 0;
68 static TRIE_NODE *p_trie_dict_section_by_name = NULL;
69 static TRIE_NODE *p_trie_dict_section_by_sid = NULL;
70
71 int article_block_init(const char *filename, int block_count)
72 {
73 int shmid;
74 int proj_id;
75 key_t key;
76 size_t size;
77 void *p_shm;
78 int i;
79 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 {
85 log_error("article_block_pool already initialized\n");
86 return -1;
87 }
88
89 if (block_count > ARTICLE_BLOCK_PER_POOL)
90 {
91 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
92 return -2;
93 }
94
95 p_article_block_pool = calloc(1, sizeof(ARTICLE_BLOCK_POOL));
96 if (p_article_block_pool == NULL)
97 {
98 log_error("calloc(ARTICLE_BLOCK_POOL) OOM\n");
99 return -2;
100 }
101
102 // Allocate shared memory
103 p_article_block_pool->shm_count = 0;
104 pp_block_next = &(p_article_block_pool->p_block_free_list);
105
106 while (block_count > 0)
107 {
108 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
109 block_count -= block_count_in_shm;
110
111 proj_id = getpid() + p_article_block_pool->shm_count;
112 key = ftok(filename, proj_id);
113 if (key == -1)
114 {
115 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
116 article_block_cleanup();
117 return -3;
118 }
119
120 size = sizeof(shmid) + sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
121 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
122 if (shmid == -1)
123 {
124 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
125 article_block_cleanup();
126 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 article_block_cleanup();
133 return -3;
134 }
135
136 (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
140 p_block_in_shm = p_shm;
141 *pp_block_next = p_block_in_shm;
142
143 for (i = 0; i < block_count_in_shm; i++)
144 {
145 if (i < block_count_in_shm - 1)
146 {
147 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
148 }
149 else
150 {
151 (p_block_in_shm + i)->p_next_block = NULL;
152 pp_block_next = &((p_block_in_shm + i)->p_next_block);
153 }
154 }
155 }
156
157 p_article_block_pool->block_count = 0;
158
159 return 0;
160 }
161
162 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 {
201 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
215 while (p_article_block_pool->block_count > 0)
216 {
217 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 }
221
222 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 {
234 log_error("article_block_pool not initialized\n");
235 return NULL;
236 }
237
238 if (p_article_block_pool->block_count == 0) // empty
239 {
240 return NULL;
241 }
242
243 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 {
249 // 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 {
254 log_error("block(mid = %d) is out of boundary\n", mid);
255 return NULL;
256 }
257
258 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
259 {
260 right = mid;
261 }
262 else
263 {
264 left = mid;
265 }
266 }
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
278 if (aid <= p_block->articles[mid].aid)
279 {
280 right = mid;
281 }
282 else
283 {
284 left = mid + 1;
285 }
286 }
287
288 return (p_block->articles + left);
289 }
290
291 ARTICLE *article_block_find_by_index(int index)
292 {
293 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 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 return NULL;
305 }
306
307 p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
308
309 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
310 {
311 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
312 return NULL;
313 }
314
315 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
316 }
317
318 extern int section_list_pool_init(const char *filename)
319 {
320 int shmid;
321 int proj_id;
322 key_t key;
323 size_t size;
324 void *p_shm;
325
326 if (p_section_list_pool == NULL || p_trie_dict_section_by_name == NULL || p_trie_dict_section_by_sid == NULL)
327 {
328 section_list_pool_cleanup();
329 }
330
331 p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));
332 if (p_section_list_pool == NULL)
333 {
334 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
346 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 }
359
360 section_list_pool_shmid = shmid;
361 p_section_list_pool = p_shm;
362 section_list_count = 0;
363
364 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 {
374 log_error("trie_dict_create() OOM\n", BBS_max_section);
375 return -2;
376 }
377
378 return 0;
379 }
380
381 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 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 char sid_str[SID_STR_LEN];
399
400 if (p_section_list_pool == NULL || p_trie_dict_section_by_name == NULL || p_trie_dict_section_by_sid == NULL)
401 {
402 log_error("session_list_pool not initialized\n");
403 return NULL;
404 }
405
406 if (section_list_count >= BBS_max_section)
407 {
408 log_error("section_list_count exceed limit %d >= %d\n", section_list_count, BBS_max_section);
409 return NULL;
410 }
411
412 sid_to_str(sid, sid_str);
413
414 p_section = p_section_list_pool + section_list_count;
415
416 p_section->sid = sid;
417
418 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 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 {
435 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 return NULL;
438 }
439
440 section_list_reset_articles(p_section);
441
442 section_list_count++;
443
444 return p_section;
445 }
446
447 void section_list_reset_articles(SECTION_LIST *p_section)
448 {
449 p_section->article_count = 0;
450 p_section->topic_count = 0;
451 p_section->visible_article_count = 0;
452 p_section->visible_topic_count = 0;
453 p_section->p_article_head = NULL;
454 p_section->p_article_tail = NULL;
455
456 p_section->page_count = 0;
457 p_section->last_page_visible_article_count = 0;
458 }
459
460 void section_list_pool_cleanup(void)
461 {
462 if (p_trie_dict_section_by_name != NULL)
463 {
464 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 }
473
474 if (p_section_list_pool != NULL)
475 {
476 if (shmdt(p_section_list_pool) == -1)
477 {
478 log_error("shmdt(shmid = %d) error (%d)\n", section_list_pool_shmid, errno);
479 }
480 p_section_list_pool = NULL;
481
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 }
487
488 section_list_count = 0;
489 }
490
491 SECTION_LIST *section_list_find_by_name(const char *sname)
492 {
493 int64_t index;
494
495 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 {
517 log_error("section_list not initialized\n");
518 return NULL;
519 }
520
521 sid_to_str(sid, sid_str);
522
523 if (trie_dict_get(p_trie_dict_section_by_sid, sid_str, &index) != 1)
524 {
525 log_error("trie_dict_get(section, %d) error\n", sid);
526 return NULL;
527 }
528
529 return (p_section_list_pool + index);
530 }
531
532 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
533 {
534 ARTICLE_BLOCK *p_block;
535 int32_t last_aid = 0;
536 ARTICLE *p_article;
537 ARTICLE *p_topic_head;
538 ARTICLE *p_topic_tail;
539
540 if (p_section == NULL || p_article_src == NULL)
541 {
542 log_error("section_list_append_article() NULL pointer error\n");
543 return -1;
544 }
545
546 if (p_article_block_pool == NULL)
547 {
548 log_error("article_block_pool not initialized\n");
549 return -1;
550 }
551
552 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 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 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 {
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 if (p_article_block_pool->block_count > 0)
574 {
575 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
576 }
577
578 p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
579 p_article_block_pool->block_count++;
580 }
581 else
582 {
583 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 }
586
587 // AID of articles should be strictly ascending
588 if (p_article_src->aid <= last_aid)
589 {
590 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
591 return -3;
592 }
593
594 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
601 if (p_article->visible)
602 {
603 p_section->visible_article_count++;
604 }
605
606 // Link appended article as tail node of topic bi-directional list
607 if (p_article->tid != 0)
608 {
609 p_topic_head = article_block_find_by_aid(p_article->tid);
610 if (p_topic_head == NULL)
611 {
612 log_error("search head of topic (aid=%d) error\n", p_article->tid);
613 return -4;
614 }
615
616 p_topic_tail = p_topic_head->p_topic_prior;
617 if (p_topic_tail == NULL)
618 {
619 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
620 return -4;
621 }
622 }
623 else
624 {
625 p_section->topic_count++;
626
627 if (p_article->visible)
628 {
629 p_section->visible_topic_count++;
630 }
631
632 p_topic_head = p_article;
633 p_topic_tail = p_article;
634 }
635
636 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
641 // 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 // Update page
654 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
655 p_section->article_count == 1)
656 {
657 p_section->p_page_first_article[p_section->page_count] = p_article;
658 p_section->page_count++;
659 p_section->last_page_visible_article_count = 0;
660 }
661
662 if (p_article->visible)
663 {
664 p_section->last_page_visible_article_count++;
665 }
666
667 return 0;
668 }
669
670 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
671 {
672 ARTICLE *p_article;
673 ARTICLE *p_reply;
674 int affected_count = 0;
675
676 if (p_section == NULL)
677 {
678 log_error("section_list_set_article_visible() NULL pointer error\n");
679 return -2;
680 }
681
682 p_article = article_block_find_by_aid(aid);
683 if (p_article == NULL)
684 {
685 return -1; // Not found
686 }
687
688 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 if (p_article->visible == visible)
695 {
696 return 0; // Already set
697 }
698
699 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 p_article->visible = visible;
736 affected_count++;
737
738 return affected_count;
739 }
740
741 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 {
743 ARTICLE *p_article;
744 int left;
745 int right;
746 int mid;
747
748 *p_page = -1;
749 *p_offset = -1;
750 *pp_next = NULL;
751
752 if (p_section == NULL)
753 {
754 log_error("section_list_find_article_with_offset() NULL pointer error\n");
755 return NULL;
756 }
757
758 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
774 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 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
796
797 // left will be the offset of article found or offset to insert
798 left = 0;
799
800 while (aid > p_article->aid)
801 {
802 p_article = p_article->p_next;
803 left++;
804
805 if (aid == p_article->aid)
806 {
807 *pp_next = p_article->p_next;
808 break;
809 }
810
811 // over last article in the page
812 if (p_article == p_section->p_article_head || p_article->aid >= right)
813 {
814 *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 }
818 }
819
820 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 {
827 *pp_next = p_article->p_next;
828 }
829
830 *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 ARTICLE *p_article;
838 ARTICLE *p_next;
839 int32_t page;
840 int32_t offset;
841 int visible_article_count;
842 int page_head_set;
843
844 if (p_section == NULL)
845 {
846 log_error("section_list_calculate_page() NULL pointer error\n");
847 return -1;
848 }
849
850 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 {
860 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 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
873 if (p_article == NULL)
874 {
875 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 }
883
884 if (offset > 0)
885 {
886 p_article = p_section->p_page_first_article[page];
887 }
888 }
889 else
890 {
891 p_article = p_section->p_article_head;
892 page = 0;
893 offset = 0;
894 }
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 return 0;
938 }
939
940 int article_count_of_topic(int32_t aid)
941 {
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 if (p_article->tid != 0 && p_article->tid != aid)
956 {
957 log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
958 break;
959 }
960
961 article_count++;
962 p_article = p_article->p_topic_next;
963 } while (p_article->aid != aid);
964
965 return article_count;
966 }
967
968 int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
969 {
970 ARTICLE *p_article;
971 ARTICLE *p_next;
972 int32_t page;
973 int32_t offset;
974 int32_t move_article_count;
975 int32_t dest_article_count_old;
976 int32_t last_unaffected_aid_src;
977 int32_t first_inserted_aid_dest;
978 int move_counter;
979
980 if (p_section_dest == NULL)
981 {
982 log_error("section_list_move_topic() NULL pointer error\n");
983 return -1;
984 }
985
986 if ((p_article = article_block_find_by_aid(aid)) == NULL)
987 {
988 log_error("section_list_move_topic() error: article %d not found in block\n", aid);
989 return -2;
990 }
991
992 if (p_section_src->sid != p_article->sid)
993 {
994 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
995 p_section_src->sid, p_article->aid, p_article->sid);
996 return -2;
997 }
998
999 if (p_article->tid != 0)
1000 {
1001 log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1002 return -2;
1003 }
1004
1005 last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1006
1007 move_article_count = article_count_of_topic(aid);
1008 if (move_article_count <= 0)
1009 {
1010 log_error("section_list_count_of_topic_articles(aid = %d) <= 0\n", aid);
1011 return -2;
1012 }
1013
1014 if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1015 {
1016 log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1017 p_section_dest->article_count + move_article_count, p_section_dest->sid);
1018 return -3;
1019 }
1020
1021 dest_article_count_old = p_section_dest->article_count;
1022 move_counter = 0;
1023 first_inserted_aid_dest = p_article->aid;
1024
1025 do
1026 {
1027 if (p_section_src->sid != p_article->sid)
1028 {
1029 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1030 p_section_src->sid, p_article->aid, p_article->sid);
1031 return -2;
1032 }
1033
1034 // Remove from bi-directional article list of src section
1035 if (p_section_src->p_article_head == p_article)
1036 {
1037 p_section_src->p_article_head = p_article->p_next;
1038 }
1039 if (p_section_src->p_article_tail == p_article)
1040 {
1041 p_section_src->p_article_tail = p_article->p_prior;
1042 }
1043 if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1044 {
1045 p_section_src->p_article_head = NULL;
1046 p_section_src->p_article_tail = NULL;
1047 }
1048
1049 p_article->p_prior->p_next = p_article->p_next;
1050 p_article->p_next->p_prior = p_article->p_prior;
1051
1052 // Update sid of article
1053 p_article->sid = p_section_dest->sid;
1054
1055 if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1056 {
1057 log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1058 return -4;
1059 }
1060
1061 // Insert into bi-directional article list of dest section
1062 if (p_next == NULL) // empty section
1063 {
1064 p_section_dest->p_article_head = p_article;
1065 p_section_dest->p_article_tail = p_article;
1066 p_article->p_prior = p_article;
1067 p_article->p_next = p_article;
1068 }
1069 else
1070 {
1071 if (p_section_dest->p_article_head == p_next)
1072 {
1073 if (p_article->aid < p_next->aid)
1074 {
1075 p_section_dest->p_article_head = p_article;
1076 }
1077 else // p_article->aid > p_next->aid
1078 {
1079 p_section_dest->p_article_tail = p_article;
1080 }
1081 }
1082
1083 p_article->p_prior = p_next->p_prior;
1084 p_article->p_next = p_next;
1085 p_next->p_prior->p_next = p_article;
1086 p_next->p_prior = p_article;
1087 }
1088
1089 // Update article / topic counter of src / desc section
1090 p_section_src->article_count--;
1091 p_section_dest->article_count++;
1092 if (p_article->tid == 0)
1093 {
1094 p_section_src->topic_count--;
1095 p_section_dest->topic_count++;
1096 }
1097
1098 // Update visible article / topic counter of src / desc section
1099 if (p_article->visible)
1100 {
1101 p_section_src->visible_article_count--;
1102 p_section_dest->visible_article_count++;
1103 if (p_article->tid == 0)
1104 {
1105 p_section_src->visible_topic_count--;
1106 p_section_dest->visible_topic_count++;
1107 }
1108 }
1109
1110 // Update page for empty dest section
1111 if (p_section_dest->article_count == 1)
1112 {
1113 p_section_dest->p_page_first_article[0] = p_article;
1114 p_section_dest->page_count = 1;
1115 p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1116 }
1117
1118 p_article = p_article->p_topic_next;
1119
1120 move_counter++;
1121 if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1122 {
1123 // Re-calculate pages of desc section
1124 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1125 {
1126 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1127 p_section_dest->sid, first_inserted_aid_dest);
1128 }
1129
1130 first_inserted_aid_dest = p_article->aid;
1131 }
1132 } while (p_article->aid != aid);
1133
1134 if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1135 {
1136 log_error("section_list_move_topic() error: count of moved articles %d != %d\n",
1137 p_section_dest->article_count - dest_article_count_old, move_article_count);
1138 }
1139
1140 // Re-calculate pages of src section
1141 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1142 {
1143 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1144 p_section_src->sid, last_unaffected_aid_src, aid);
1145 }
1146
1147 if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1148 {
1149 // Re-calculate pages of desc section
1150 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1151 {
1152 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1153 p_section_dest->sid, first_inserted_aid_dest);
1154 }
1155 }
1156
1157 return move_article_count;
1158 }

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