/[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.1 - (hide annotations)
Wed Oct 15 00:45:52 2025 UTC (5 months ago) by sysadm
Branch: MAIN
Content type: text/x-csrc
Add article_favor

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

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