Merge topic branch t/openssl-3 into master
[paraslash.git] / score.c
1 /* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file score.c Scoring functions to determine the audio file streaming order. */
4 #include <regex.h>
5 #include <osl.h>
6 #include <lopsub.h>
7
8 #include "para.h"
9 #include "error.h"
10 #include "string.h"
11 #include "afh.h"
12 #include "afs.h"
13 #include "list.h"
14
15 static struct osl_table *score_table;
16
17 static int ptr_compare(const struct osl_object *obj1, const struct osl_object *obj2)
18 {
19 void *d1 = *(void **)obj1->data;
20 void *d2 = *(void **)obj2->data;
21 return NUM_COMPARE(d1, d2);
22 }
23
24 /*
25 * This function first compares the score values. If they are equal, the
26 * addresses of the two objects are compared. Thus, the function returns
27 * "equal" only if the two objects alias each other, i.e., point to the same
28 * memory address.
29 */
30 static int score_compare(const struct osl_object *obj1, const struct osl_object *obj2)
31 {
32 long d1 = *(long *)obj1->data;
33 long d2 = *(long *)obj2->data;
34 int ret = NUM_COMPARE(d2, d1);
35
36 if (ret)
37 return ret;
38 return NUM_COMPARE(obj2->data, obj1->data);
39 }
40
41 /**
42 * The score table consists of two columns: The \a aft_row column contains
43 * pointers to the rows of the audio file table, and the score column contains
44 * the current score of the audio file associated with that row.
45 */
46 enum score_table_columns {
47 /** The row of the audio file. */
48 SCORECOL_AFT_ROW,
49 /** The score */
50 SCORECOL_SCORE,
51 /** This table has two columns */
52 NUM_SCORE_COLUMNS
53 };
54
55 static struct osl_column_description score_cols[] = {
56 [SCORECOL_AFT_ROW] = {
57 .storage_type = OSL_NO_STORAGE,
58 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE | OSL_DONT_FREE,
59 .name = "aft_row",
60 .compare_function = ptr_compare,
61 .data_size = sizeof(void *)
62 },
63 [SCORECOL_SCORE] = {
64 .storage_type = OSL_NO_STORAGE,
65 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
66 .name = "score",
67 .compare_function = score_compare,
68 .data_size = sizeof(long)
69 }
70 };
71
72 static struct osl_table_description score_table_desc = {
73 .name = "score",
74 .num_columns = NUM_SCORE_COLUMNS,
75 .flags = 0,
76 .column_descriptions = score_cols
77 };
78
79 /* On errors (negative return value) the content of score is undefined. */
80 static int get_score_of_row(void *score_row, long *score)
81 {
82 struct osl_object obj;
83 int ret = osl(osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj));
84
85 if (ret >= 0)
86 *score = *(long *)obj.data;
87 return ret;
88 }
89
90 /**
91 * Add an entry to the table of admissible files.
92 *
93 * \param aft_row The audio file to be added.
94 * \param score The score for this file.
95 *
96 * \return The return value of the underlying call to osl_add_row().
97 */
98 int score_add(const struct osl_row *aft_row, long score)
99 {
100 int ret;
101 struct osl_object score_objs[NUM_SCORE_COLUMNS];
102 size_t size;
103
104 assert(aft_row);
105 size = score_table_desc.column_descriptions[SCORECOL_AFT_ROW].data_size;
106 score_objs[SCORECOL_AFT_ROW].data = (struct osl_row *)aft_row;
107 score_objs[SCORECOL_AFT_ROW].size = size;
108
109 size = score_table_desc.column_descriptions[SCORECOL_SCORE].data_size;
110 score_objs[SCORECOL_SCORE].data = alloc(size);
111 score_objs[SCORECOL_SCORE].size = size;
112 *(long *)(score_objs[SCORECOL_SCORE].data) = score;
113
114 // PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
115 ret = osl(osl_add_row(score_table, score_objs));
116 if (ret < 0) {
117 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
118 free(score_objs[SCORECOL_SCORE].data);
119 }
120 return ret;
121 }
122
123 /**
124 * Replace a row of the score table.
125 *
126 * \param aft_row Determines the audio file to change.
127 * \param percent The position to re-insert the audio file.
128 *
129 * The percent parameter must be between 0 and 100. A value of zero means to
130 * re-insert the audio file into the score table with a score lower than any
131 * other admissible file.
132 *
133 * \return Positive on success, negative on errors.
134 */
135 int score_update(const struct osl_row *aft_row, long percent)
136 {
137 struct osl_row *row, *rrow; /* score row, reference row */
138 long new_score;
139 unsigned n, new_pos;
140 struct osl_object obj = {.data = (struct osl_row *)aft_row,
141 .size = sizeof(aft_row)};
142 int ret = osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, &row));
143
144 if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* not an error */
145 return 1;
146 if (ret < 0)
147 return ret;
148 ret = osl(osl_get_num_rows(score_table, &n));
149 if (ret < 0)
150 return ret;
151 new_pos = 1 + (n - 1) * percent / 100;
152 ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
153 if (ret < 0)
154 return ret;
155 ret = get_score_of_row(rrow, &new_score);
156 if (ret < 0)
157 return ret;
158 new_score--;
159 obj.size = sizeof(long);
160 obj.data = alloc(obj.size);
161 *(long *)obj.data = new_score;
162 PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
163 return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj));
164 }
165
166 /**
167 * Given an admissible file, get the corresponding row in the aft and the score.
168 *
169 * \param score_row Determines the admissible file.
170 * \param score Result pointer.
171 * \param aft_row Result pointer.
172 *
173 * \return Standard.
174 */
175 int get_score_and_aft_row(struct osl_row *score_row, long *score,
176 struct osl_row **aft_row)
177 {
178 struct osl_object obj;
179 int ret = get_score_of_row(score_row, score);
180
181 if (ret < 0)
182 return ret;
183 ret = osl(osl_get_object(score_table, score_row, SCORECOL_AFT_ROW, &obj));
184 if (ret < 0)
185 return ret;
186 *aft_row = obj.data;
187 return 1;
188 }
189
190 static int get_score_row_from_aft_row(const struct osl_row *aft_row,
191 struct osl_row **score_row)
192 {
193 struct osl_object obj = {.data = (struct osl_row *)aft_row,
194 .size = sizeof(aft_row)};
195 return osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row));
196 }
197
198 /**
199 * Call the given function for each row of the score table.
200 *
201 * \param func Callback, called once per row.
202 * \param data Passed verbatim to the callback.
203 *
204 * \return The return value of the underlying call to osl_rbtree_loop(). The
205 * loop terminates early if the callback returns negative.
206 */
207 int score_loop(osl_rbtree_loop_func *func, void *data)
208 {
209 return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func));
210 }
211
212 /**
213 * Get the admissible audio file with highest score.
214 *
215 * \param aft_row Points to the row in the aft of the "best" audio file.
216 * \param score Highest score value in the score table.
217 *
218 * \return Standard.
219 */
220 int score_get_best(struct osl_row **aft_row, long *score)
221 {
222 struct osl_row *row;
223 struct osl_object obj;
224 int ret = osl(osl_rbtree_last_row(score_table, SCORECOL_SCORE, &row));
225
226 if (ret < 0)
227 return ret;
228 ret = osl(osl_get_object(score_table, row, SCORECOL_AFT_ROW, &obj));
229 if (ret < 0)
230 return ret;
231 *aft_row = obj.data;
232 return get_score_of_row(row, score);
233 }
234
235 /**
236 * Remove an entry from the rbtree of admissible files.
237 *
238 * \param aft_row The file which is no longer admissible.
239 *
240 * \return Standard.
241 *
242 * \sa \ref score_add().
243 */
244 int score_delete(const struct osl_row *aft_row)
245 {
246 struct osl_row *score_row;
247 int ret = get_score_row_from_aft_row(aft_row, &score_row);
248
249 if (ret < 0)
250 return ret;
251 return osl(osl_del_row(score_table, score_row));
252 }
253
254 /**
255 * Find out whether an audio file is contained in the score table.
256 *
257 * \param aft_row The row of the audio file table.
258 *
259 * \return If the lookup operation fails for any other reason than "not found",
260 * the function aborts the current process (afs), since this is considered a
261 * fatal error that should never happen.
262 */
263 bool row_belongs_to_score_table(const struct osl_row *aft_row)
264 {
265 struct osl_row *score_row;
266 int ret = get_score_row_from_aft_row(aft_row, &score_row);
267
268 if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
269 return false;
270 assert(ret >= 0);
271 return true;
272 }
273
274 static void score_close(void)
275 {
276 osl_close_table(score_table, OSL_FREE_VOLATILE);
277 score_table = NULL;
278 }
279
280 static int score_open(__a_unused const char *dir)
281 {
282 assert(osl_open_table(&score_table_desc, &score_table) >= 0);
283 return 1;
284 }
285
286 /**
287 * Remove all entries from the score table, but keep the table open.
288 */
289 void score_clear(void)
290 {
291 score_close();
292 score_open(NULL);
293 }
294
295 /** The score table stores (aft row, score) pairs in memory. */
296 const struct afs_table_operations score_ops = {
297 .open = score_open,
298 .close = score_close,
299 };