• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

tuple.c

Go to the documentation of this file.
00001 /*
00002  * Audacious
00003  * Copyright (c) 2006-2007 Audacious team
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; under version 3 of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses>.
00016  *
00017  * The Audacious team does not consider modular code linking to
00018  * Audacious or using our public API to be a derived work.
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 /* iterative destructor of tuple values. */
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     /* FIXME: use mowgli_object_bless_from_class() in mowgli 0.4
00153        when it is released --nenolod */
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     /* Convert invalid UTF-8 URI's quietly. */
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     /* Copy basic fields */
00291     for (i = 0; i < FIELD_LAST; i++)
00292         dst->values[i] = tuple_copy_value(src->values[i]);
00293 
00294     /* Copy dictionary contents */
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     /* Copy subtune number information */
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     /* Check for known fields */
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     /* Check if field was known */
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             //mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
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         /* Value exists, just delete old associated data */
00394         if (value->type == TUPLE_STRING) {
00395             stringpool_unref(value->value.string);
00396             value->value.string = NULL;
00397         }
00398     } else {
00399         /* Allocate a new value */
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         /* why _delete()? because _delete() returns the dictnode's data on success */
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     /* Free associated data */
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 }

Generated on Wed Apr 6 2011 for Audacious by  doxygen 1.7.1