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


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

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