/[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.2 - (hide annotations)
Wed Oct 15 02:25:13 2025 UTC (5 months ago) by sysadm
Branch: MAIN
Changes since 1.1: +31 -36 lines
Content type: text/x-csrc
Use stack allocated variables instead of malloc

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

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