/[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.16 - (show annotations)
Sat May 24 07:32:46 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.15: +151 -0 lines
Content type: text/x-csrc
Add read / write lock for sections

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

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