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

Annotation of /lbbs/src/article_favor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide annotations)
Wed Oct 15 03:02:17 2025 UTC (5 months ago) by sysadm
Branch: MAIN
Changes since 1.2: +22 -8 lines
Content type: text/x-csrc
Optimize with memcpy / memmove

1 sysadm 1.1 /***************************************************************************
2     article_favor.c - description
3     -------------------
4     Copyright : (C) 2004-2025 by Leaflet
5     Email : leaflet@leafok.com
6     ***************************************************************************/
7    
8     /***************************************************************************
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 3 of the License, or *
13     * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17     #include "article_favor.h"
18     #include "common.h"
19     #include "database.h"
20     #include "log.h"
21     #include <stdlib.h>
22     #include <string.h>
23 sysadm 1.3 #include <sys/param.h>
24 sysadm 1.1
25     ARTICLE_FAVOR BBS_article_favor;
26    
27     int article_favor_load(int uid, ARTICLE_FAVOR *p_favor, int keep_inc)
28     {
29     MYSQL *db;
30     MYSQL_RES *rs;
31     MYSQL_ROW row;
32     char sql[SQL_BUFFER_LEN];
33    
34     if (p_favor == NULL)
35     {
36     log_error("NULL pointer error\n");
37     return -1;
38     }
39    
40     p_favor->uid = uid;
41    
42     if (uid == 0)
43     {
44     p_favor->aid_base_cnt = 0;
45    
46     if (!keep_inc)
47     {
48     p_favor->aid_inc_cnt = 0;
49     }
50    
51     return 0;
52     }
53    
54     if ((db = db_open()) == NULL)
55     {
56     log_error("article_favor_load() error: Unable to open DB\n");
57     return -2;
58     }
59    
60     snprintf(sql, sizeof(sql),
61     "SELECT AID FROM article_favorite WHERE UID = %d "
62     "ORDER BY AID",
63     uid);
64     if (mysql_query(db, sql) != 0)
65     {
66     log_error("Query article_favorite error: %s\n", mysql_error(db));
67     return -3;
68     }
69 sysadm 1.2 if ((rs = mysql_use_result(db)) == NULL)
70 sysadm 1.1 {
71     log_error("Get article_favorite data failed\n");
72     return -3;
73     }
74    
75     p_favor->aid_base_cnt = 0;
76    
77     while ((row = mysql_fetch_row(rs)))
78     {
79 sysadm 1.2 p_favor->aid_base[p_favor->aid_base_cnt] = atoi(row[0]);
80     (p_favor->aid_base_cnt)++;
81     if (p_favor->aid_base_cnt >= MAX_FAVOR_AID_BASE_CNT)
82     {
83     log_error("Too many article_favorite records for uid=%d\n",
84     uid);
85     break;
86     }
87 sysadm 1.1 }
88     mysql_free_result(rs);
89    
90     mysql_close(db);
91    
92     log_common("Loaded %d article_favorite records for uid=%d\n", p_favor->aid_base_cnt, uid);
93    
94     if (!keep_inc)
95     {
96     p_favor->aid_inc_cnt = 0;
97     }
98    
99     return 0;
100     }
101    
102     int article_favor_unload(ARTICLE_FAVOR *p_favor)
103     {
104     if (p_favor == NULL)
105     {
106     log_error("NULL pointer error\n");
107     return -1;
108     }
109    
110 sysadm 1.2 p_favor->aid_base_cnt = 0;
111 sysadm 1.1
112     return 0;
113     }
114    
115     int article_favor_save_inc(const ARTICLE_FAVOR *p_favor)
116     {
117     MYSQL *db = NULL;
118     char sql_add[SQL_BUFFER_LEN];
119     char sql_del[SQL_BUFFER_LEN];
120     char tuple_tmp[LINE_BUFFER_LEN];
121     int i;
122     int j;
123     int cnt_pending_add = 0;
124     int cnt_pending_del = 0;
125     int cnt_total_add = 0;
126     int cnt_total_del = 0;
127    
128     if (p_favor == NULL)
129     {
130     log_error("NULL pointer error\n");
131     return -1;
132     }
133    
134     if (p_favor->uid <= 0 || p_favor->aid_inc_cnt == 0)
135     {
136     return 0;
137     }
138    
139     if ((db = db_open()) == NULL)
140     {
141     log_error("article_favor_load() error: Unable to open DB\n");
142     return -2;
143     }
144    
145     snprintf(sql_add, sizeof(sql_add),
146     "INSERT IGNORE INTO article_favorite(AID, UID) VALUES ");
147     snprintf(sql_del, sizeof(sql_add),
148     "DELETE FROM article_favorite WHERE UID = %d AND AID IN (",
149     p_favor->uid);
150    
151     for (i = 0, j = 0; i < p_favor->aid_base_cnt && j < p_favor->aid_inc_cnt;)
152     {
153     if (p_favor->aid_base[i] == p_favor->aid_inc[j]) // XOR - delete record
154     {
155     snprintf(tuple_tmp, sizeof(tuple_tmp), "%d, ", p_favor->aid_inc[j]);
156     strncat(sql_del, tuple_tmp, sizeof(sql_del) - 1 - strnlen(sql_del, sizeof(sql_del)));
157    
158     cnt_pending_del++;
159     i++;
160     j++;
161     }
162     else if (p_favor->aid_base[i] < p_favor->aid_inc[j]) // skip existing record
163     {
164     i++;
165     }
166     else // if (p_favor->aid_base[i] > p_favor->aid_inc[j])
167     {
168     snprintf(tuple_tmp, sizeof(tuple_tmp),
169     "(%d, %d), ",
170     p_favor->aid_inc[j], p_favor->uid);
171     strncat(sql_add, tuple_tmp, sizeof(sql_add) - 1 - strnlen(sql_add, sizeof(sql_add)));
172    
173     cnt_pending_add++;
174     j++;
175     }
176    
177     if ((cnt_pending_add >= 100 || (j + 1) >= p_favor->aid_inc_cnt) && cnt_pending_add > 0)
178     {
179     // Add
180     sql_add[strnlen(sql_add, sizeof(sql_add)) - 2] = '\0'; // remove last ", "
181    
182     if (mysql_query(db, sql_add) != 0)
183     {
184     log_error("Add article_favorite error: %s\n", mysql_error(db));
185     log_error("%s\n", sql_add);
186     mysql_close(db);
187     return -3;
188     }
189    
190     cnt_total_add += (int)mysql_affected_rows(db);
191     cnt_pending_add = 0;
192    
193     snprintf(sql_add, sizeof(sql_add),
194     "INSERT IGNORE INTO article_favorite(AID, UID) VALUES ");
195     }
196    
197     if ((cnt_pending_del >= 100 || (j + 1) >= p_favor->aid_inc_cnt) && cnt_pending_del > 0)
198     {
199     // Delete
200     sql_del[strnlen(sql_del, sizeof(sql_del)) - 2] = ')'; // replace last ", " with ") "
201    
202     if (mysql_query(db, sql_del) != 0)
203     {
204     log_error("Delete article_favorite error: %s\n", mysql_error(db));
205     log_error("%s\n", sql_del);
206     mysql_close(db);
207     return -3;
208     }
209    
210     cnt_total_del += (int)mysql_affected_rows(db);
211     cnt_pending_del = 0;
212    
213     snprintf(sql_del, sizeof(sql_add),
214     "DELETE FROM article_favorite WHERE UID = %d AND AID IN (",
215     p_favor->uid);
216     }
217     }
218    
219     log_common("Saved %d and deleted %d article_favorite records for uid=%d\n",
220     cnt_total_add, cnt_total_del, p_favor->uid);
221    
222     mysql_close(db);
223    
224     return 0;
225     }
226    
227     int article_favor_merge_inc(ARTICLE_FAVOR *p_favor)
228     {
229 sysadm 1.2 int32_t aid_new[MAX_FAVOR_AID_BASE_CNT];
230 sysadm 1.1 int i, j, k;
231 sysadm 1.3 int len;
232 sysadm 1.1
233     if (p_favor == NULL)
234     {
235     log_error("NULL pointer error\n");
236     return -1;
237     }
238    
239     if (p_favor->aid_inc_cnt == 0) // Nothing to be merged
240     {
241     return 0;
242     }
243    
244 sysadm 1.2 for (i = 0, j = 0, k = 0; i < p_favor->aid_base_cnt && j < p_favor->aid_inc_cnt && k < MAX_FAVOR_AID_BASE_CNT;)
245 sysadm 1.1 {
246     if (p_favor->aid_base[i] == p_favor->aid_inc[j]) // XOR - discard duplicate pair
247     {
248     i++;
249     j++;
250     }
251     else if (p_favor->aid_base[i] < p_favor->aid_inc[j])
252     {
253     aid_new[k++] = p_favor->aid_base[i++];
254     }
255     else // if (p_favor->aid_base[i] > p_favor->aid_inc[j])
256     {
257     aid_new[k++] = p_favor->aid_inc[j++];
258     }
259     }
260    
261 sysadm 1.3 len = MIN(p_favor->aid_base_cnt - i, MAX_FAVOR_AID_BASE_CNT - k);
262     if (len > 0)
263 sysadm 1.2 {
264 sysadm 1.3 memcpy(aid_new + k, p_favor->aid_base + i,
265     sizeof(int32_t) * (size_t)len);
266     i += len;
267     k += len;
268 sysadm 1.2 }
269     if (i < p_favor->aid_base_cnt)
270     {
271     log_error("Too many base aids, %d will be discarded\n", p_favor->aid_base_cnt - i);
272     }
273    
274 sysadm 1.3 len = MIN(p_favor->aid_inc_cnt - j, MAX_FAVOR_AID_BASE_CNT - k);
275     if (len > 0)
276 sysadm 1.2 {
277 sysadm 1.3 memcpy(aid_new + k, p_favor->aid_inc + j,
278     sizeof(int32_t) * (size_t)len);
279     j += len;
280     k += len;
281 sysadm 1.2 }
282     if (j < p_favor->aid_inc_cnt)
283     {
284     log_error("Too many inc aids, %d will be discarded\n", p_favor->aid_inc_cnt - j);
285     }
286    
287     memcpy(p_favor->aid_base, aid_new, sizeof(int32_t) * (size_t)k);
288 sysadm 1.1
289     p_favor->aid_base_cnt = k;
290     p_favor->aid_inc_cnt = 0;
291    
292     return 0;
293     }
294    
295     int article_favor_check(int32_t aid, const ARTICLE_FAVOR *p_favor)
296     {
297     int left;
298     int right;
299     int mid;
300     int i;
301     int is_set = 0;
302    
303     if (p_favor == NULL)
304     {
305     log_error("NULL pointer error\n");
306     return -1;
307     }
308    
309     for (i = 0; i < 2; i++)
310     {
311     left = 0;
312     right = (i == 0 ? p_favor->aid_base_cnt : p_favor->aid_inc_cnt) - 1;
313    
314     if (right < 0)
315     {
316     continue;
317     }
318    
319     while (left < right)
320     {
321     mid = (left + right) / 2;
322     if (aid < (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
323     {
324     right = mid;
325     }
326     else if (aid > (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
327     {
328     left = mid + 1;
329     }
330     else // if (aid == p_favor->aid_base[mid])
331     {
332     left = mid;
333     break;
334     }
335     }
336    
337     if (aid == (i == 0 ? p_favor->aid_base[left] : p_favor->aid_inc[left]))
338     {
339     is_set = (is_set ? 0 : 1);
340     }
341     }
342    
343     return is_set;
344     }
345    
346     int article_favor_set(int32_t aid, ARTICLE_FAVOR *p_favor, int state)
347     {
348     int left;
349     int right;
350     int mid;
351     int i;
352     int is_set = 0;
353    
354     if (p_favor == NULL)
355     {
356     log_error("NULL pointer error\n");
357     return -1;
358     }
359    
360     for (i = 0; i < 2; i++)
361     {
362     left = 0;
363     right = (i == 0 ? p_favor->aid_base_cnt : p_favor->aid_inc_cnt) - 1;
364    
365     if (right < 0)
366     {
367     continue;
368     }
369    
370     while (left < right)
371     {
372     mid = (left + right) / 2;
373     if (aid < (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
374     {
375     right = mid;
376     }
377     else if (aid > (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
378     {
379     left = mid + 1;
380     }
381     else // if (aid == p_favor->aid_base[mid])
382     {
383     left = mid;
384     break;
385     }
386     }
387    
388     if (aid == (i == 0 ? p_favor->aid_base[left] : p_favor->aid_inc[left]))
389     {
390     is_set = (is_set ? 0 : 1);
391     }
392     }
393    
394     if ((is_set ^ (state ? 1 : 0)) == 0) // No change
395     {
396     return 0;
397     }
398    
399     if (aid == p_favor->aid_inc[left] && p_favor->aid_inc_cnt > 0) // Unset
400     {
401 sysadm 1.3 if (p_favor->aid_inc_cnt > left + 1)
402 sysadm 1.1 {
403 sysadm 1.3 memmove(p_favor->aid_inc + left,
404     p_favor->aid_inc + left + 1,
405     sizeof(int32_t) * (size_t)(p_favor->aid_inc_cnt - left - 1));
406 sysadm 1.1 }
407    
408     (p_favor->aid_inc_cnt)--;
409    
410     return 2; // Unset complete
411     }
412    
413     // Merge if Inc is full
414     if (p_favor->aid_inc_cnt >= MAX_FAVOR_AID_INC_CNT)
415     {
416     // Save incremental article favorite
417     if (article_favor_save_inc(p_favor) < 0)
418     {
419     log_error("article_favor_save_inc() error\n");
420     return -2;
421     }
422    
423     article_favor_merge_inc(p_favor);
424    
425     p_favor->aid_inc[(p_favor->aid_inc_cnt)++] = aid;
426    
427     return 1; // Set complete
428     }
429    
430     if (right < 0)
431     {
432     right = 0;
433     }
434     else if (aid > p_favor->aid_inc[left])
435     {
436     right = left + 1;
437     }
438    
439 sysadm 1.3 if (p_favor->aid_inc_cnt > right)
440 sysadm 1.1 {
441 sysadm 1.3 memmove(p_favor->aid_inc + right + 1,
442     p_favor->aid_inc + right,
443     sizeof(int32_t) * (size_t)(p_favor->aid_inc_cnt - right));
444 sysadm 1.1 }
445    
446     p_favor->aid_inc[right] = aid;
447     (p_favor->aid_inc_cnt)++;
448    
449     return 1; // Set complete
450     }
451    
452     int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, ARTICLE **p_articles,
453     char p_snames[][BBS_section_name_max_len + 1], int *p_article_count, int *p_page_count)
454     {
455     SECTION_LIST *p_section;
456     int32_t aid;
457     int i;
458    
459     if (p_favor == NULL || p_articles == NULL || p_article_count == NULL || p_page_count == NULL)
460     {
461     log_error("NULL pointer error\n");
462     return -1;
463     }
464    
465     if (article_favor_save_inc(p_favor) < 0)
466     {
467     log_error("article_favor_save_inc() error\n");
468     return -2;
469     }
470     if (article_favor_merge_inc(p_favor) < 0)
471     {
472     log_error("article_favor_merge_inc() error\n");
473     return -2;
474     }
475    
476     *p_page_count = p_favor->aid_base_cnt / BBS_article_limit_per_page + (p_favor->aid_base_cnt % BBS_article_limit_per_page == 0 ? 0 : 1);
477     *p_article_count = 0;
478    
479     if (p_favor->aid_base_cnt == 0)
480     {
481     // empty list
482     return 0;
483     }
484    
485     if (page_id < 0 || page_id >= *p_page_count)
486     {
487     log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
488     return -1;
489     }
490    
491     for (i = 0;
492     i < BBS_article_limit_per_page &&
493     page_id * BBS_article_limit_per_page + i < p_favor->aid_base_cnt;
494     i++)
495     {
496     aid = p_favor->aid_base[page_id * BBS_article_limit_per_page + i];
497     p_articles[i] = article_block_find_by_aid(aid);
498     if (p_articles[i] == NULL)
499     {
500     log_error("article_block_find_by_aid(aid=%d) error: page_id=%d, i=%d\n", aid, page_id, i);
501     return -3;
502     }
503    
504     p_section = section_list_find_by_sid(p_articles[i]->sid);
505     if (p_section == NULL)
506     {
507     log_error("section_list_find_by_sid(%d) error\n", p_articles[i]->sid);
508     return -3;
509     }
510    
511     // acquire lock of section
512     if (section_list_rd_lock(p_section) < 0)
513     {
514     log_error("section_list_rd_lock(sid = %d) error\n", p_section->sid);
515     return -4;
516     }
517    
518     memcpy(p_snames[i], p_section->sname, sizeof(p_snames[i]));
519    
520     // release lock of section
521     if (section_list_rd_unlock(p_section) < 0)
522     {
523     log_error("section_list_rd_unlock(sid = %d) error\n", p_section->sid);
524     return -4;
525     }
526     }
527     *p_article_count = i;
528    
529     return 0;
530     }

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