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

Contents of /lbbs/src/article_favor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show 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 /***************************************************************************
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 if ((rs = mysql_use_result(db)) == NULL)
69 {
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 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 }
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 p_favor->aid_base_cnt = 0;
110
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 int32_t aid_new[MAX_FAVOR_AID_BASE_CNT];
229 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 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 {
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 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
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