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

Diff of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.7 by sysadm, Thu May 22 06:20:47 2025 UTC Revision 1.13 by sysadm, Fri May 23 14:04:05 2025 UTC
# Line 31  Line 31 
31  #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)  #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)  #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  struct article_block_t
37  {  {
38          ARTICLE articles[ARTICLE_PER_BLOCK];          ARTICLE articles[ARTICLE_PER_BLOCK];
# Line 58  typedef struct article_block_pool_t ARTI Line 60  typedef struct article_block_pool_t ARTI
60    
61  static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;  static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
62    
63    static int section_list_pool_shmid;
64  static SECTION_LIST *p_section_list_pool = NULL;  static SECTION_LIST *p_section_list_pool = NULL;
65  static int section_list_count = 0;  static int section_list_count = 0;
66  static TRIE_NODE *p_trie_dict_section_list = NULL;  static TRIE_NODE *p_trie_dict_section_list = NULL;
# Line 309  ARTICLE *article_block_find_by_index(int Line 312  ARTICLE *article_block_find_by_index(int
312          return (p_block->articles + (index % ARTICLE_PER_BLOCK));          return (p_block->articles + (index % ARTICLE_PER_BLOCK));
313  }  }
314    
315  SECTION_LIST *section_list_create(const char *sname, const char *stitle, const char *master_name)  extern int section_list_pool_init(const char *filename)
316  {  {
317          SECTION_LIST *p_section;          int shmid;
318            int proj_id;
319            key_t key;
320            size_t size;
321            void *p_shm;
322    
323            if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
324            {
325                    section_list_pool_cleanup();
326            }
327    
328            p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));
329          if (p_section_list_pool == NULL)          if (p_section_list_pool == NULL)
330          {          {
331                  p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));                  log_error("calloc(%d SECTION_LIST) OOM\n", BBS_max_section);
332                  if (p_section_list_pool == NULL)                  return -1;
333                  {          }
334                          log_error("calloc(%d SECTION_LIST) OOM\n", BBS_max_section);  
335                          return NULL;          proj_id = (int)(time(NULL) % getpid());
336                  }          key = ftok(filename, proj_id);
337            if (key == -1)
338            {
339                    log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
340                    return -3;
341            }
342    
343                  section_list_count = 0;          size = sizeof(shmid) + sizeof(SECTION_LIST) * BBS_max_section;
344            shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
345            if (shmid == -1)
346            {
347                    log_error("shmget(section_list_pool, size = %d) error (%d)\n", size, errno);
348                    return -3;
349            }
350            p_shm = shmat(shmid, NULL, 0);
351            if (p_shm == (void *)-1)
352            {
353                    log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
354                    return -3;
355          }          }
356    
357            section_list_pool_shmid = shmid;
358            p_section_list_pool = p_shm;
359            section_list_count = 0;
360    
361            p_trie_dict_section_list = trie_dict_create();
362          if (p_trie_dict_section_list == NULL)          if (p_trie_dict_section_list == NULL)
363          {          {
364                  p_trie_dict_section_list = trie_dict_create();                  log_error("trie_dict_create() OOM\n", BBS_max_section);
365                  if (p_trie_dict_section_list == NULL)                  return -2;
366                  {          }
367                          log_error("trie_dict_create() OOM\n", BBS_max_section);  
368                          return NULL;          return 0;
369                  }  }
370    
371    SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_name)
372    {
373            SECTION_LIST *p_section;
374    
375            if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
376            {
377                    log_error("session_list_pool not initialized\n");
378                    return NULL;
379          }          }
380    
381          if (section_list_count >= BBS_max_section)          if (section_list_count >= BBS_max_section)
382          {          {
383                  log_error("section_list_count exceed limit %d\n", BBS_max_section);                  log_error("section_list_count exceed limit %d >= %d\n", section_list_count, BBS_max_section);
384                  return NULL;                  return NULL;
385          }          }
386    
387          p_section = p_section_list_pool + section_list_count;          p_section = p_section_list_pool + section_list_count;
388    
389            p_section->sid = sid;
390    
391          strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));          strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));
392          p_section->sname[sizeof(p_section->sname - 1)] = '\0';          p_section->sname[sizeof(p_section->sname - 1)] = '\0';
393    
# Line 368  SECTION_LIST *section_list_create(const Line 413  SECTION_LIST *section_list_create(const
413  void section_list_reset_articles(SECTION_LIST *p_section)  void section_list_reset_articles(SECTION_LIST *p_section)
414  {  {
415          p_section->article_count = 0;          p_section->article_count = 0;
416            p_section->topic_count = 0;
417            p_section->visible_article_count = 0;
418            p_section->visible_topic_count = 0;
419          p_section->p_article_head = NULL;          p_section->p_article_head = NULL;
420          p_section->p_article_tail = NULL;          p_section->p_article_tail = NULL;
421    
422          p_section->page_count = 0;          p_section->page_count = 0;
423          p_section->last_page_article_count = 0;          p_section->last_page_visible_article_count = 0;
424  }  }
425    
426  void section_list_cleanup(void)  void section_list_pool_cleanup(void)
427  {  {
428          if (p_trie_dict_section_list != NULL)          if (p_trie_dict_section_list != NULL)
429          {          {
# Line 385  void section_list_cleanup(void) Line 433  void section_list_cleanup(void)
433    
434          if (p_section_list_pool != NULL)          if (p_section_list_pool != NULL)
435          {          {
436                  free(p_section_list_pool);                  if (shmdt(p_section_list_pool) == -1)
437                    {
438                            log_error("shmdt(shmid = %d) error (%d)\n", section_list_pool_shmid, errno);
439                    }
440                  p_section_list_pool = NULL;                  p_section_list_pool = NULL;
441    
442                    if (shmctl(section_list_pool_shmid, IPC_RMID, NULL) == -1)
443                    {
444                            log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", section_list_pool_shmid, errno);
445                    }
446          }          }
447    
448          section_list_count = 0;          section_list_count = 0;
# Line 431  int section_list_append_article(SECTION_ Line 487  int section_list_append_article(SECTION_
487                  return -1;                  return -1;
488          }          }
489    
490            if (p_section->article_count >= BBS_article_limit_per_section)
491            {
492                    log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
493                    return -2;
494            }
495    
496          if (p_article_block_pool->block_count == 0 ||          if (p_article_block_pool->block_count == 0 ||
497                  p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)                  p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
498          {          {
# Line 468  int section_list_append_article(SECTION_ Line 530  int section_list_append_article(SECTION_
530          // Copy article data          // Copy article data
531          *p_article = *p_article_src;          *p_article = *p_article_src;
532    
533            if (p_article->visible)
534            {
535                    p_section->visible_article_count++;
536            }
537    
538          // Link appended article as tail node of topic bi-directional list          // Link appended article as tail node of topic bi-directional list
539          if (p_article->tid != 0)          if (p_article->tid != 0)
540          {          {
# Line 487  int section_list_append_article(SECTION_ Line 554  int section_list_append_article(SECTION_
554          }          }
555          else          else
556          {          {
557                    p_section->topic_count++;
558    
559                    if (p_article->visible)
560                    {
561                            p_section->visible_topic_count++;
562                    }
563    
564                  p_topic_head = p_article;                  p_topic_head = p_article;
565                  p_topic_tail = p_article;                  p_topic_tail = p_article;
566          }          }
# Line 509  int section_list_append_article(SECTION_ Line 583  int section_list_append_article(SECTION_
583          p_section->p_article_tail = p_article;          p_section->p_article_tail = p_article;
584    
585          // Update page          // Update page
586          if (p_section->last_page_article_count % BBS_article_limit_per_page == 0)          if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
587                    p_section->article_count == 1)
588          {          {
589                  p_section->p_page_first_article[p_section->page_count] = p_article;                  p_section->p_page_first_article[p_section->page_count] = p_article;
590                  p_section->page_count++;                  p_section->page_count++;
591                  p_section->last_page_article_count = 0;                  p_section->last_page_visible_article_count = 0;
592            }
593    
594            if (p_article->visible)
595            {
596                    p_section->last_page_visible_article_count++;
597          }          }
         p_section->last_page_article_count++;  
598    
599          return 0;          return 0;
600  }  }
# Line 523  int section_list_append_article(SECTION_ Line 602  int section_list_append_article(SECTION_
602  int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)  int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
603  {  {
604          ARTICLE *p_article;          ARTICLE *p_article;
605            ARTICLE *p_reply;
606            int affected_count = 0;
607    
608          if (p_section == NULL)          if (p_section == NULL)
609          {          {
# Line 541  int section_list_set_article_visible(SEC Line 622  int section_list_set_article_visible(SEC
622                  return 0; // Already set                  return 0; // Already set
623          }          }
624    
625            if (visible == 0) // 1 -> 0
626            {
627                    p_section->visible_article_count--;
628    
629                    if (p_article->tid == 0)
630                    {
631                            p_section->visible_topic_count--;
632    
633                            // Set related visible replies to invisible
634                            for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
635                            {
636                                    if (p_reply->tid != aid)
637                                    {
638                                            log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
639                                            continue;
640                                    }
641    
642                                    if (p_reply->visible == 1)
643                                    {
644                                            p_reply->visible = 0;
645                                            p_section->visible_article_count--;
646                                            affected_count++;
647                                    }
648                            }
649                    }
650            }
651            else // 0 -> 1
652            {
653                    p_section->visible_article_count++;
654    
655                    if (p_article->tid == 0)
656                    {
657                            p_section->visible_topic_count++;
658                    }
659            }
660    
661          p_article->visible = visible;          p_article->visible = visible;
662            affected_count++;
663    
664            return affected_count;
665    }
666    
667    ARTICLE *section_list_find_article_with_offset(SECTION_LIST *p_section, int32_t aid, int32_t *p_page, int32_t *p_offset, ARTICLE **pp_next)
668    {
669            ARTICLE *p_article;
670            int left;
671            int right;
672            int mid;
673    
674            *p_page = -1;
675            *p_offset = -1;
676            *pp_next = NULL;
677    
678            if (p_section == NULL)
679            {
680                    log_error("section_list_find_article_with_offset() NULL pointer error\n");
681                    return NULL;
682            }
683    
684            if (p_section->article_count == 0) // empty
685            {
686                    *p_page = 0;
687                    *p_offset = 0;
688                    return NULL;
689            }
690    
691            left = 0;
692            right = p_section->page_count;
693    
694            // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]
695            while (left < right - 1)
696            {
697                    // get page id no less than mid value of left page id and right page id
698                    mid = (left + right) / 2 + (right - left) % 2;
699    
700                    if (mid >= p_section->page_count)
701                    {
702                            log_error("page id (mid = %d) is out of boundary\n", mid);
703                            return NULL;
704                    }
705    
706                    if (aid < p_section->p_page_first_article[mid]->aid)
707                    {
708                            right = mid;
709                    }
710                    else
711                    {
712                            left = mid;
713                    }
714            }
715    
716            *p_page = left;
717    
718            p_article = p_section->p_page_first_article[*p_page];
719    
720            // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
721            right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
722    
723            // left will be the offset of article found or offset to insert
724            left = 0;
725    
726            while (aid > p_article->aid)
727            {
728                    p_article = p_article->p_next;
729                    left++;
730    
731                    if (aid == p_article->aid)
732                    {
733                            *pp_next = p_article->p_next;
734                            break;
735                    }
736    
737                    // over last article in the page
738                    if (p_article == p_section->p_article_head || p_article->aid >= right)
739                    {
740                            *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
741                            *p_offset = left;
742                            return NULL; // not found
743                    }
744            }
745    
746            if (aid < p_article->aid)
747            {
748                    *pp_next = p_article;
749                    p_article = NULL; // not found
750            }
751            else // aid == p_article->aid
752            {
753                    *pp_next = p_article->p_next;
754            }
755    
756            *p_offset = left;
757    
758            return p_article;
759    }
760    
761    int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
762    {
763            ARTICLE *p_article;
764            ARTICLE *p_next;
765            int32_t page;
766            int32_t offset;
767            int visible_article_count;
768            int page_head_set;
769    
770            if (p_section == NULL)
771            {
772                    log_error("section_list_calculate_page() NULL pointer error\n");
773                    return -1;
774            }
775    
776            if (p_section->article_count == 0) // empty
777            {
778                    p_section->page_count = 0;
779                    p_section->last_page_visible_article_count = 0;
780    
781                    return 0;
782            }
783    
784            if (start_aid > 0)
785            {
786                    p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
787                    if (p_article == NULL)
788                    {
789                            if (page < 0)
790                            {
791                                    return -1;
792                            }
793                            log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
794                                              start_aid, p_section->sid);
795                            return -2;
796                    }
797    
798                    if (offset > 0)
799                    {
800                            p_article = p_section->p_page_first_article[page];
801                    }
802            }
803            else
804            {
805                    p_article = p_section->p_article_head;
806                    page = 0;
807                    offset = 0;
808            }
809    
810            visible_article_count = 0;
811            page_head_set = 0;
812    
813            do
814            {
815                    if (!page_head_set && visible_article_count == 0)
816                    {
817                            p_section->p_page_first_article[page] = p_article;
818                            page_head_set = 1;
819                    }
820    
821                    if (p_article->visible)
822                    {
823                            visible_article_count++;
824                    }
825    
826                    p_article = p_article->p_next;
827    
828                    // skip remaining invisible articles
829                    while (p_article->visible == 0 && p_article != p_section->p_article_head)
830                    {
831                            p_article = p_article->p_next;
832                    }
833    
834                    if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
835                    {
836                            page++;
837                            visible_article_count = 0;
838                            page_head_set = 0;
839    
840                            if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
841                            {
842                                    log_error("Count of page exceed limit in section %d\n", p_section->sid);
843                                    break;
844                            }
845                    }
846            } while (p_article != p_section->p_article_head);
847    
848            p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
849            p_section->last_page_visible_article_count = visible_article_count;
850    
851            return 0;
852    }
853    
854    int section_list_count_of_topic_articles(int32_t aid)
855    {
856            ARTICLE *p_article;
857            int article_count;
858    
859            p_article = article_block_find_by_aid(aid);
860            if (p_article == NULL)
861            {
862                    return 0; // Not found
863            }
864    
865            article_count = 0;
866    
867            do
868            {
869                    article_count++;
870                    p_article = p_article->p_topic_next;
871            } while (p_article->aid != aid);
872    
873            return article_count;
874    }
875    
876    int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
877    {
878            ARTICLE *p_article;
879            ARTICLE *p_next;
880            int32_t page;
881            int32_t offset;
882            int32_t move_article_count;
883            int32_t dest_article_count_old;
884            int32_t last_unaffected_aid_src;
885            int32_t first_inserted_aid_dest;
886            int move_counter;
887    
888            if (p_section_dest == NULL)
889            {
890                    log_error("section_list_move_topic() NULL pointer error\n");
891                    return -1;
892            }
893    
894            if ((p_article = section_list_find_article_with_offset(p_section_src, aid, &page, &offset, &p_next)) == NULL)
895            {
896                    log_error("section_list_move_topic() error: article %d not found in section %d\n", aid, p_section_src->sid);
897                    return -2;
898            }
899    
900            if (p_article->tid != 0)
901            {
902                    log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
903                    return -2;
904            }
905    
906            last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
907    
908            move_article_count = section_list_count_of_topic_articles(aid);
909            if (move_article_count <= 0)
910            {
911                    log_error("section_list_count_of_topic_articles(aid = %d) <= 0\n", aid);
912                    return -2;
913            }
914    
915            if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
916            {
917                    log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
918                                      p_section_dest->article_count + move_article_count, p_section_dest->sid);
919                    return -3;
920            }
921    
922            dest_article_count_old = p_section_dest->article_count;
923            move_counter = 0;
924            first_inserted_aid_dest = p_article->aid;
925    
926            do
927            {
928                    if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
929                    {
930                            log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
931                            return -4;
932                    }
933    
934                    // Remove from bi-directional article list of src section
935                    if (p_section_src->p_article_head == p_article)
936                    {
937                            p_section_src->p_article_head = p_article->p_next;
938                    }
939                    if (p_section_src->p_article_tail == p_article)
940                    {
941                            p_section_src->p_article_tail = p_article->p_prior;
942                    }
943                    if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
944                    {
945                            p_section_src->p_article_head = NULL;
946                            p_section_src->p_article_tail = NULL;
947                    }
948    
949                    p_article->p_prior->p_next = p_article->p_next;
950                    p_article->p_next->p_prior = p_article->p_prior;
951    
952                    // Insert into bi-directional article list of dest section
953                    if (p_next == NULL) // empty section
954                    {
955                            p_section_dest->p_article_head = p_article;
956                            p_section_dest->p_article_tail = p_article;
957                            p_article->p_prior = p_article;
958                            p_article->p_next = p_article;
959                    }
960                    else
961                    {
962                            if (p_section_dest->p_article_head == p_next)
963                            {
964                                    if (p_article->aid < p_next->aid)
965                                    {
966                                            p_section_dest->p_article_head = p_article;
967                                    }
968                                    else // p_article->aid > p_next->aid
969                                    {
970                                            p_section_dest->p_article_tail = p_article;
971                                    }
972                            }
973    
974                            p_article->p_prior = p_next->p_prior;
975                            p_article->p_next = p_next;
976                            p_next->p_prior->p_next = p_article;
977                            p_next->p_prior = p_article;
978                    }
979    
980                    // Update article / topic counter of src / desc section
981                    p_section_src->article_count--;
982                    p_section_dest->article_count++;
983                    if (p_article->tid == 0)
984                    {
985                            p_section_src->topic_count--;
986                            p_section_dest->topic_count++;
987                    }
988    
989                    // Update visible article / topic counter of src / desc section
990                    if (p_article->visible)
991                    {
992                            p_section_src->visible_article_count--;
993                            p_section_dest->visible_article_count++;
994                            if (p_article->tid == 0)
995                            {
996                                    p_section_src->visible_topic_count--;
997                                    p_section_dest->visible_topic_count++;
998                            }
999                    }
1000    
1001                    // Update page for empty dest section
1002                    if (p_section_dest->article_count == 1)
1003                    {
1004                            p_section_dest->p_page_first_article[0] = p_article;
1005                            p_section_dest->page_count = 1;
1006                            p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1007                    }
1008    
1009          // TODO:                  p_article = p_article->p_topic_next;
1010    
1011                    move_counter++;
1012                    if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1013                    {
1014                            // Re-calculate pages of desc section
1015                            if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1016                            {
1017                                    log_error("section_list_calculate_page(section = %d, aid = %d) error\n",
1018                                                      p_section_dest->sid, first_inserted_aid_dest);
1019                            }
1020    
1021                            first_inserted_aid_dest = p_article->aid;
1022                    }
1023            } while (p_article->aid != aid);
1024    
1025            if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1026            {
1027                    log_error("section_list_move_topic() error: count of moved articles %d != %d\n",
1028                                      p_section_dest->article_count - dest_article_count_old, move_article_count);
1029            }
1030    
1031            // Re-calculate pages of src section
1032            if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1033            {
1034                    log_error("section_list_calculate_page(section = %d, aid = %d) error at aid = %d\n",
1035                                      p_section_src->sid, last_unaffected_aid_src, aid);
1036            }
1037    
1038            if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1039            {
1040                    // Re-calculate pages of desc section
1041                    if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1042                    {
1043                            log_error("section_list_calculate_page(section = %d, aid = %d) error\n",
1044                                              p_section_dest->sid, first_inserted_aid_dest);
1045                    }
1046            }
1047    
1048          return 1;          return move_article_count;
1049  }  }


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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