00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00025 #include <glib.h>
00026 #include <mowgli.h>
00027
00028 #include "tuple.h"
00029 #include "audstrings.h"
00030 #include "stringpool.h"
00031
00034 const TupleBasicType tuple_fields[FIELD_LAST] = {
00035 { "artist", TUPLE_STRING },
00036 { "title", TUPLE_STRING },
00037 { "album", TUPLE_STRING },
00038 { "comment", TUPLE_STRING },
00039 { "genre", TUPLE_STRING },
00040
00041 { "track", TUPLE_STRING },
00042 { "track-number", TUPLE_INT },
00043 { "length", TUPLE_INT },
00044 { "year", TUPLE_INT },
00045 { "quality", TUPLE_STRING },
00046
00047 { "codec", TUPLE_STRING },
00048 { "file-name", TUPLE_STRING },
00049 { "file-path", TUPLE_STRING },
00050 { "file-ext", TUPLE_STRING },
00051 { "song-artist", TUPLE_STRING },
00052
00053 { "mtime", TUPLE_INT },
00054 { "formatter", TUPLE_STRING },
00055 { "performer", TUPLE_STRING },
00056 { "copyright", TUPLE_STRING },
00057 { "date", TUPLE_STRING },
00058
00059 { "subsong-id", TUPLE_INT },
00060 { "subsong-num", TUPLE_INT },
00061 { "mime-type", TUPLE_STRING },
00062 { "bitrate", TUPLE_INT },
00063
00064 { "segment-start", TUPLE_INT },
00065 { "segment-end", TUPLE_INT },
00066
00067 { "gain-album-gain", TUPLE_INT },
00068 { "gain-album-peak", TUPLE_INT },
00069 { "gain-track-gain", TUPLE_INT },
00070 { "gain-track-peak", TUPLE_INT },
00071 { "gain-gain-unit", TUPLE_INT },
00072 { "gain-peak-unit", TUPLE_INT },
00073
00074 { "composer", TUPLE_STRING },
00075 };
00076
00077
00079 static mowgli_heap_t *tuple_heap = NULL;
00080
00082 static mowgli_heap_t *tuple_value_heap = NULL;
00083 static mowgli_object_class_t tuple_klass;
00084
00086 static GStaticRWLock tuple_rwlock = G_STATIC_RW_LOCK_INIT;
00087
00089
00093 #define TUPLE_LOCK_WRITE(X) g_static_rw_lock_writer_lock(&tuple_rwlock)
00094 #define TUPLE_UNLOCK_WRITE(X) g_static_rw_lock_writer_unlock(&tuple_rwlock)
00095 #define TUPLE_LOCK_READ(X) g_static_rw_lock_reader_lock(&tuple_rwlock)
00096 #define TUPLE_UNLOCK_READ(X) g_static_rw_lock_reader_unlock(&tuple_rwlock)
00097
00098
00099
00100 static void
00101 tuple_value_destroy(mowgli_dictionary_elem_t *delem, gpointer privdata)
00102 {
00103 TupleValue *value = (TupleValue *) delem->data;
00104
00105 if (value->type == TUPLE_STRING) {
00106 stringpool_unref(value->value.string);
00107 value->value.string = NULL;
00108 }
00109
00110 mowgli_heap_free(tuple_value_heap, value);
00111 }
00112
00113 static void
00114 tuple_destroy(gpointer data)
00115 {
00116 Tuple *tuple = (Tuple *) data;
00117 gint i;
00118
00119 TUPLE_LOCK_WRITE();
00120 mowgli_dictionary_destroy(tuple->dict, tuple_value_destroy, NULL);
00121
00122 for (i = 0; i < FIELD_LAST; i++)
00123 if (tuple->values[i]) {
00124 TupleValue *value = tuple->values[i];
00125
00126 if (value->type == TUPLE_STRING) {
00127 stringpool_unref(value->value.string);
00128 value->value.string = NULL;
00129 }
00130
00131 mowgli_heap_free(tuple_value_heap, value);
00132 }
00133
00134 g_free(tuple->subtunes);
00135
00136 mowgli_heap_free(tuple_heap, tuple);
00137 TUPLE_UNLOCK_WRITE();
00138 }
00139
00140 static Tuple *
00141 tuple_new_unlocked(void)
00142 {
00143 Tuple *tuple;
00144
00145 if (tuple_heap == NULL)
00146 {
00147 tuple_heap = mowgli_heap_create(sizeof(Tuple), 512, BH_NOW);
00148 tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 1024, BH_NOW);
00149 mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE);
00150 }
00151
00152
00153
00154 tuple = mowgli_heap_alloc(tuple_heap);
00155 memset(tuple, 0, sizeof(Tuple));
00156 mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL);
00157
00158 tuple->dict = mowgli_dictionary_create(g_ascii_strcasecmp);
00159
00160 return tuple;
00161 }
00162
00168 Tuple *
00169 tuple_new(void)
00170 {
00171 Tuple *tuple;
00172
00173 TUPLE_LOCK_WRITE();
00174
00175 tuple = tuple_new_unlocked();
00176
00177 TUPLE_UNLOCK_WRITE();
00178 return tuple;
00179 }
00180
00181 static TupleValue *
00182 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype);
00183
00184
00193 void
00194 tuple_set_filename(Tuple *tuple, const gchar *filename)
00195 {
00196 gchar *local = g_strdup(filename);
00197 gchar *slash, *period, *question;
00198
00199 string_decode_percent(local);
00200
00201
00202 if (! g_utf8_validate (local, -1, NULL))
00203 {
00204 gchar * utf8 = str_to_utf8 (local);
00205 g_free (local);
00206 local = utf8;
00207 }
00208
00209 slash = strrchr(local, '/');
00210 period = strrchr(local, '.');
00211 question = strrchr(local, '?');
00212
00213 if (slash != NULL)
00214 {
00215 gchar temp = *(slash + 1);
00216
00217 *(slash + 1) = 0;
00218 tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, local);
00219 *(slash + 1) = temp;
00220 tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, slash + 1);
00221 }
00222
00223 if (question != NULL)
00224 {
00225 gint subtune;
00226
00227 *question = 0;
00228
00229 if (sscanf(question + 1, "%d", &subtune) == 1)
00230 tuple_associate_int(tuple, FIELD_SUBSONG_ID, NULL, subtune);
00231 }
00232
00233 if (period != NULL)
00234 tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, period + 1);
00235
00236 g_free(local);
00237 }
00238
00246 static TupleValue *
00247 tuple_copy_value(TupleValue *src)
00248 {
00249 TupleValue *res;
00250
00251 if (src == NULL) return NULL;
00252
00253 res = mowgli_heap_alloc(tuple_value_heap);
00254 res->type = src->type;
00255
00256 switch (src->type) {
00257 case TUPLE_STRING:
00258 res->value.string = stringpool_get(src->value.string);
00259 break;
00260 case TUPLE_INT:
00261 res->value.integer = src->value.integer;
00262 break;
00263 default:
00264 mowgli_heap_free (tuple_value_heap, res);
00265 return NULL;
00266 }
00267 return res;
00268 }
00269
00276 Tuple *
00277 tuple_copy(const Tuple *src)
00278 {
00279 Tuple *dst;
00280 TupleValue * tv, * copied;
00281 mowgli_dictionary_iteration_state_t state;
00282 gint i;
00283
00284 g_return_val_if_fail(src != NULL, NULL);
00285
00286 TUPLE_LOCK_WRITE();
00287
00288 dst = tuple_new_unlocked();
00289
00290
00291 for (i = 0; i < FIELD_LAST; i++)
00292 dst->values[i] = tuple_copy_value(src->values[i]);
00293
00294
00295 MOWGLI_DICTIONARY_FOREACH (tv, & state, src->dict)
00296 {
00297 if ((copied = tuple_copy_value (tv)) != NULL)
00298 mowgli_dictionary_add (dst->dict, state.cur->key, copied);
00299 }
00300
00301
00302 if (src->subtunes && src->nsubtunes > 0)
00303 {
00304 dst->nsubtunes = src->nsubtunes;
00305 dst->subtunes = g_new(gint, dst->nsubtunes);
00306 memcpy(dst->subtunes, src->subtunes, sizeof(gint) * dst->nsubtunes);
00307 }
00308
00309 TUPLE_UNLOCK_WRITE();
00310 return dst;
00311 }
00312
00320 Tuple *
00321 tuple_new_from_filename(const gchar *filename)
00322 {
00323 Tuple *tuple = tuple_new();
00324
00325 tuple_set_filename(tuple, filename);
00326 return tuple;
00327 }
00328
00329
00330 static gint
00331 tuple_get_nfield(const gchar *field)
00332 {
00333 gint i;
00334 for (i = 0; i < FIELD_LAST; i++)
00335 if (!strcmp(field, tuple_fields[i].name))
00336 return i;
00337 return -1;
00338 }
00339
00340
00359 static TupleValue *
00360 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype)
00361 {
00362 const gchar *tfield = field;
00363 gint nfield = cnfield;
00364 TupleValue *value = NULL;
00365
00366 g_return_val_if_fail(tuple != NULL, NULL);
00367 g_return_val_if_fail(cnfield < FIELD_LAST, NULL);
00368
00369
00370 if (nfield < 0) {
00371 nfield = tuple_get_nfield(field);
00372 if (nfield >= 0)
00373 g_warning("Tuple FIELD_* not used for '%s'!\n", field);
00374 }
00375
00376
00377 if (nfield >= 0) {
00378 tfield = tuple_fields[nfield].name;
00379 value = tuple->values[nfield];
00380
00381 if (ftype != tuple_fields[nfield].type) {
00382 g_warning("Invalid type for [%s](%d->%d), %d != %d\n",
00383 tfield, cnfield, nfield, ftype, tuple_fields[nfield].type);
00384
00385 TUPLE_UNLOCK_WRITE();
00386 return NULL;
00387 }
00388 } else {
00389 value = mowgli_dictionary_retrieve(tuple->dict, tfield);
00390 }
00391
00392 if (value != NULL) {
00393
00394 if (value->type == TUPLE_STRING) {
00395 stringpool_unref(value->value.string);
00396 value->value.string = NULL;
00397 }
00398 } else {
00399
00400 value = mowgli_heap_alloc(tuple_value_heap);
00401 value->type = ftype;
00402 if (nfield >= 0)
00403 tuple->values[nfield] = value;
00404 else
00405 mowgli_dictionary_add(tuple->dict, tfield, value);
00406 }
00407
00408 return value;
00409 }
00410
00424 gboolean
00425 tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string)
00426 {
00427 TupleValue *value;
00428
00429 TUPLE_LOCK_WRITE();
00430 if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_STRING)) == NULL)
00431 return FALSE;
00432
00433 if (string == NULL)
00434 value->value.string = NULL;
00435 else if (g_utf8_validate (string, -1, NULL))
00436 value->value.string = stringpool_get (string);
00437 else
00438 {
00439 fprintf (stderr, "Invalid UTF-8: %s.\n", string);
00440 gchar * copy = str_to_utf8 (string);
00441 value->value.string = stringpool_get (copy);
00442 g_free (copy);
00443 }
00444
00445 TUPLE_UNLOCK_WRITE();
00446 return TRUE;
00447 }
00448
00464 gboolean tuple_associate_string_rel (Tuple * tuple, const gint nfield,
00465 const gchar * field, gchar * string)
00466 {
00467 gboolean ret = tuple_associate_string (tuple, nfield, field, string);
00468 g_free (string);
00469 return ret;
00470 }
00471
00485 gboolean
00486 tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer)
00487 {
00488 TupleValue *value;
00489
00490 TUPLE_LOCK_WRITE();
00491 if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_INT)) == NULL)
00492 return FALSE;
00493
00494 value->value.integer = integer;
00495
00496 TUPLE_UNLOCK_WRITE();
00497 return TRUE;
00498 }
00499
00509 void
00510 tuple_disassociate(Tuple *tuple, const gint cnfield, const gchar *field)
00511 {
00512 TupleValue *value;
00513 gint nfield = cnfield;
00514
00515 g_return_if_fail(tuple != NULL);
00516 g_return_if_fail(nfield < FIELD_LAST);
00517
00518 if (nfield < 0)
00519 nfield = tuple_get_nfield(field);
00520
00521 TUPLE_LOCK_WRITE();
00522 if (nfield < 0)
00523
00524 value = mowgli_dictionary_delete(tuple->dict, field);
00525 else {
00526 value = tuple->values[nfield];
00527 tuple->values[nfield] = NULL;
00528 }
00529
00530 if (value == NULL) {
00531 TUPLE_UNLOCK_WRITE();
00532 return;
00533 }
00534
00535
00536 if (value->type == TUPLE_STRING) {
00537 stringpool_unref(value->value.string);
00538 value->value.string = NULL;
00539 }
00540
00541 mowgli_heap_free(tuple_value_heap, value);
00542 TUPLE_UNLOCK_WRITE();
00543 }
00544
00555 TupleValueType tuple_get_value_type (const Tuple * tuple, gint cnfield,
00556 const gchar * field)
00557 {
00558 TupleValueType type = TUPLE_UNKNOWN;
00559 gint nfield = cnfield;
00560
00561 g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN);
00562 g_return_val_if_fail(nfield < FIELD_LAST, TUPLE_UNKNOWN);
00563
00564 if (nfield < 0)
00565 nfield = tuple_get_nfield(field);
00566
00567 TUPLE_LOCK_READ();
00568 if (nfield < 0) {
00569 TupleValue *value;
00570 if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL)
00571 type = value->type;
00572 } else {
00573 if (tuple->values[nfield])
00574 type = tuple->values[nfield]->type;
00575 }
00576
00577 TUPLE_UNLOCK_READ();
00578 return type;
00579 }
00580
00592 const gchar * tuple_get_string (const Tuple * tuple, gint cnfield, const gchar *
00593 field)
00594 {
00595 TupleValue *value;
00596 gint nfield = cnfield;
00597
00598 g_return_val_if_fail(tuple != NULL, NULL);
00599 g_return_val_if_fail(nfield < FIELD_LAST, NULL);
00600
00601 if (nfield < 0)
00602 nfield = tuple_get_nfield(field);
00603
00604 TUPLE_LOCK_READ();
00605 if (nfield < 0)
00606 value = mowgli_dictionary_retrieve(tuple->dict, field);
00607 else
00608 value = tuple->values[nfield];
00609
00610 if (value) {
00611 if (value->type != TUPLE_STRING)
00612 mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL);
00613
00614 TUPLE_UNLOCK_READ();
00615 return value->value.string;
00616 } else {
00617 TUPLE_UNLOCK_READ();
00618 return NULL;
00619 }
00620 }
00621
00634 gint tuple_get_int (const Tuple * tuple, gint cnfield, const gchar * field)
00635 {
00636 TupleValue *value;
00637 gint nfield = cnfield;
00638
00639 g_return_val_if_fail(tuple != NULL, 0);
00640 g_return_val_if_fail(nfield < FIELD_LAST, 0);
00641
00642 if (nfield < 0)
00643 nfield = tuple_get_nfield(field);
00644
00645 TUPLE_LOCK_READ();
00646 if (nfield < 0)
00647 value = mowgli_dictionary_retrieve(tuple->dict, field);
00648 else
00649 value = tuple->values[nfield];
00650
00651 if (value) {
00652 if (value->type != TUPLE_INT)
00653 mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
00654
00655 TUPLE_UNLOCK_READ();
00656 return value->value.integer;
00657 } else {
00658 TUPLE_UNLOCK_READ();
00659 return 0;
00660 }
00661 }