1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
/* SPDX-License-Identifier: GPL-2.0 */
/** \file score.c Scoring functions to determine the audio file streaming order. */
#include <osl.h>
#include <lopsub.h>
#include "para.h"
#include "error.h"
#include "string.h"
#include "afh.h"
#include "afs.h"
#include "list.h"
static int ptr_compare(const struct osl_object *obj1, const struct osl_object *obj2)
{
void *d1 = *(void **)obj1->data;
void *d2 = *(void **)obj2->data;
return NUM_COMPARE(d1, d2);
}
/*
* This function first compares the score values. If they are equal, the
* addresses of the two objects are compared. Thus, the function returns
* "equal" only if the two objects alias each other, i.e., point to the same
* memory address.
*/
static int score_compare(const struct osl_object *obj1, const struct osl_object *obj2)
{
long d1 = *(long *)obj1->data;
long d2 = *(long *)obj2->data;
int ret = NUM_COMPARE(d2, d1);
if (ret)
return ret;
return NUM_COMPARE(obj2->data, obj1->data);
}
/**
* The score table consists of two columns: The \a aft_row column contains
* pointers to the rows of the audio file table, and the score column contains
* the current score of the audio file associated with that row.
*/
enum score_table_columns {
/** The row of the audio file. */
SCORECOL_AFT_ROW,
/** The score */
SCORECOL_SCORE,
/** This table has two columns */
NUM_SCORE_COLUMNS
};
static struct osl_column_description score_cols[] = {
[SCORECOL_AFT_ROW] = {
.storage_type = OSL_NO_STORAGE,
.storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE | OSL_DONT_FREE,
.name = "aft_row",
.compare_function = ptr_compare,
.data_size = sizeof(void *)
},
[SCORECOL_SCORE] = {
.storage_type = OSL_NO_STORAGE,
.storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
.name = "score",
.compare_function = score_compare,
.data_size = sizeof(long)
}
};
static struct osl_table_description score_table_desc = {
.name = "score",
.num_columns = NUM_SCORE_COLUMNS,
.flags = 0,
.column_descriptions = score_cols
};
/* On errors (negative return value) the content of score is undefined. */
static int decode_row(struct osl_table *t, void *score_row,
struct osl_row **aft_row, long *score)
{
struct osl_object obj;
int ret = osl(osl_get_object(t, score_row, SCORECOL_SCORE, &obj));
if (ret < 0)
return ret;
*score = *(long *)obj.data;
ret = osl(osl_get_object(t, score_row, SCORECOL_AFT_ROW, &obj));
if (ret < 0)
return ret;
*aft_row = obj.data;
return 1;
}
/**
* Add a (row, score) pair to the score table.
*
* \param aft_row Identifies the audio file to be added.
* \param score The score value of the audio file.
* \param t The score table to operate on.
*
* \return The return value of the underlying call to osl_add_row().
*/
int score_add(const struct osl_row *aft_row, long score, struct osl_table *t)
{
int ret;
struct osl_object score_objs[NUM_SCORE_COLUMNS];
size_t size;
assert(aft_row);
size = score_table_desc.column_descriptions[SCORECOL_AFT_ROW].data_size;
score_objs[SCORECOL_AFT_ROW].data = (struct osl_row *)aft_row;
score_objs[SCORECOL_AFT_ROW].size = size;
size = score_table_desc.column_descriptions[SCORECOL_SCORE].data_size;
score_objs[SCORECOL_SCORE].data = alloc(size);
score_objs[SCORECOL_SCORE].size = size;
*(long *)(score_objs[SCORECOL_SCORE].data) = score;
// PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
ret = osl(osl_add_row(t, score_objs));
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
free(score_objs[SCORECOL_SCORE].data);
}
return ret;
}
/**
* Set the score of an admissible file to a low value.
*
* \param aft_row Determines the score row to update.
* \param t The score table to operate on.
*
* If the audio file determined by the given row of the audio file table is
* not admissible, the function does nothing. Otherwise it sets the score
* to one less than the current lowest score value. This function always
* operates on the global score table, i.e., the one that corresponds to
* the currently open mood or playlist.
*
* \return Zero if the file is not admissible, one if the score was updated
* successfully, negative on errors.
*/
int score_move_to_end(const struct osl_row *aft_row, struct osl_table *t)
{
struct osl_row *score_row, *rrow; /* score row, reference row */
long score;
struct osl_object obj = {.data = (struct osl_row *)aft_row,
.size = sizeof(aft_row)};
int ret = osl(osl_get_row(t, SCORECOL_AFT_ROW, &obj, &score_row));
if (ret == osl(-E_OSL_RB_KEY_NOT_FOUND)) /* not an error */
return 0;
if (ret < 0)
return ret;
ret = osl(osl_rbtree_first_row(t, SCORECOL_SCORE, &rrow));
if (ret < 0)
return ret;
ret = osl(osl_get_object(t, rrow, SCORECOL_SCORE, &obj));
if (ret < 0)
return ret;
score = *(long *)obj.data - 1;
PARA_DEBUG_LOG("new score: %ld\n", score);
obj.size = sizeof(long);
obj.data = alloc(obj.size);
*(long *)obj.data = score;
ret = osl(osl_update_object(t, score_row, SCORECOL_SCORE, &obj));
if (ret < 0)
return ret;
return 1;
}
static int get_score_row_from_aft_row(struct osl_table *t,
const struct osl_row *aft_row, struct osl_row **score_row)
{
struct osl_object obj = {.data = (struct osl_row *)aft_row,
.size = sizeof(aft_row)};
return osl(osl_get_row(t, SCORECOL_AFT_ROW, &obj, score_row));
}
struct score_callback_data {
int (*cb)(struct osl_row *aft_row, long score, void *data);
struct osl_table *t;
void *data;
};
static int call_callback(struct osl_row *score_row, void *data)
{
struct score_callback_data *scd = data;
long score;
struct osl_row *aft_row;
int ret = decode_row(scd->t, score_row, &aft_row, &score);
if (ret < 0)
return ret;
return scd->cb(aft_row, score, scd->data);
}
/**
* Call the given function for each row of the score table.
*
* \param cb Callback, called once per row.
* \param t The score table to operate on.
* \param data Passed verbatim to the callback.
*
* \return The return value of the underlying call to osl_rbtree_loop(). The
* loop terminates early if the callback returns negative.
*/
int score_loop(int (*cb)(struct osl_row *, long, void *),
struct osl_table *t, void *data)
{
struct score_callback_data scd = {.cb = cb, .t = t, .data = data};
return osl(osl_rbtree_loop(t, SCORECOL_SCORE, &scd, call_callback));
}
/**
* Get the admissible audio file with highest score.
*
* \param aft_row Points to the row in the aft of the "best" audio file.
* \param score Highest score value in the score table.
* \param t The score table to operate on.
*
* \return Standard.
*/
int score_get_best(struct osl_row **aft_row, long *score, struct osl_table *t)
{
struct osl_row *score_row;
int ret = osl(osl_rbtree_last_row(t, SCORECOL_SCORE, &score_row));
if (ret < 0)
return ret;
return decode_row(t, score_row, aft_row, score);
}
/**
* Make an admissible file non-admissible.
*
* \param aft_row Identifies the file which has become non-admissible.
* \param t The score table to operate on.
*
* \return One if the row which corresponds to the given file was removed
* from the score table. Zero if there was nothing to do because the given
* file was not admissible. Negative error code otherwise.
*
* \sa \ref score_add().
*/
int score_delete(const struct osl_row *aft_row, struct osl_table *t)
{
struct osl_row *score_row;
int ret = get_score_row_from_aft_row(t, aft_row, &score_row);
if (ret == osl(-E_OSL_RB_KEY_NOT_FOUND))
return 0;
if (ret < 0)
return ret;
ret = osl(osl_del_row(t, score_row));
if (ret < 0)
return ret;
return 1;
}
/**
* Free all volatile objects, then close the table.
*
* \param t As returned from \ref score_open().
*
* This either succeeds or terminates the calling process.
*/
void score_close(struct osl_table *t)
{
if (!t)
return;
assert(osl_close_table(t, OSL_FREE_VOLATILE) >= 0);
}
/**
* Allocate a score table instance.
*
* \param result NULL means to open the currently active score table.
*
* Since the score table does no filesystem I/O, this function always succeeds.
* \sa \ref score_close().
*/
void score_open(struct osl_table **result)
{
assert(osl(osl_open_table(&score_table_desc, result)) >= 0);
}
|