# HG changeset patch # User Matti Hamalainen # Date 1188619701 -10800 # Node ID 9580bb3e58facc6d3f8ed19d87426260d36e979c # Parent abfa5f49ff07123d631193a90dd9741cfdbad4da Tuple handling API changed to include support for "hardcoded" fields. diff -r abfa5f49ff07 -r 9580bb3e58fa src/audacious/tuple.c --- a/src/audacious/tuple.c Fri Aug 31 22:54:13 2007 +0100 +++ b/src/audacious/tuple.c Sat Sep 01 07:08:21 2007 +0300 @@ -23,6 +23,33 @@ #include "tuple.h" + +const gchar *tuple_fields[FIELD_LAST] = { + "artist", + "title", + "album", + "comment", + "genre", + + "track", + "track-number", + "length", + "year", + "quality", + + "codec", + "file-name", + "file-path", + "file-ext", + "song-artist", + + "mtime", + "formatter", + "performer", + "copyright", +}; + + static mowgli_heap_t *tuple_heap = NULL; static mowgli_heap_t *tuple_value_heap = NULL; static mowgli_object_class_t tuple_klass; @@ -55,7 +82,7 @@ if (tuple_heap == NULL) { - tuple_heap = mowgli_heap_create(sizeof(Tuple), 256, BH_NOW); + tuple_heap = mowgli_heap_create(sizeof(Tuple), 32, BH_NOW); tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 512, BH_NOW); mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE); } @@ -85,11 +112,11 @@ realfn = g_filename_from_uri(filename, NULL, NULL); scratch = g_path_get_basename(realfn ? realfn : filename); - tuple_associate_string(tuple, "file-name", scratch); + tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, scratch); g_free(scratch); scratch = g_path_get_dirname(realfn ? realfn : filename); - tuple_associate_string(tuple, "file-path", scratch); + tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, scratch); g_free(scratch); g_free(realfn); realfn = NULL; @@ -97,21 +124,29 @@ ext = strrchr(filename, '.'); if (ext != NULL) { ++ext; - tuple_associate_string(tuple, "file-ext", scratch); + tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, scratch); } return tuple; } gboolean -tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string) +tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string) { TupleValue *value; + const gchar *tfield; g_return_val_if_fail(tuple != NULL, FALSE); - g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(nfield < FIELD_LAST, FALSE); - if ((value = mowgli_dictionary_delete(tuple->dict, field))) + if (nfield < 0) + tfield = field; + else { + tfield = tuple_fields[nfield]; + tuple->values[nfield] = NULL; + } + + if ((value = mowgli_dictionary_delete(tuple->dict, tfield))) tuple_disassociate_now(value); if (string == NULL) @@ -121,27 +156,41 @@ value->type = TUPLE_STRING; value->value.string = g_strdup(string); - mowgli_dictionary_add(tuple->dict, field, value); + mowgli_dictionary_add(tuple->dict, tfield, value); + + if (nfield >= 0) + tuple->values[nfield] = value; return TRUE; } gboolean -tuple_associate_int(Tuple *tuple, const gchar *field, gint integer) +tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer) { TupleValue *value; + const gchar *tfield; g_return_val_if_fail(tuple != NULL, FALSE); - g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(nfield < FIELD_LAST, FALSE); - if ((value = mowgli_dictionary_delete(tuple->dict, field))) + if (nfield < 0) + tfield = field; + else { + tfield = tuple_fields[nfield]; + tuple->values[nfield] = NULL; + } + + if ((value = mowgli_dictionary_delete(tuple->dict, tfield))) tuple_disassociate_now(value); value = mowgli_heap_alloc(tuple_value_heap); value->type = TUPLE_INT; value->value.integer = integer; - mowgli_dictionary_add(tuple->dict, field, value); + mowgli_dictionary_add(tuple->dict, tfield, value); + + if (nfield >= 0) + tuple->values[nfield] = value; return TRUE; } @@ -156,44 +205,64 @@ } void -tuple_disassociate(Tuple *tuple, const gchar *field) +tuple_disassociate(Tuple *tuple, const gint nfield, const gchar *field) { TupleValue *value; + const gchar *tfield; g_return_if_fail(tuple != NULL); - g_return_if_fail(field != NULL); + g_return_if_fail(nfield < FIELD_LAST); + + if (nfield < 0) + tfield = field; + else { + tfield = tuple_fields[nfield]; + tuple->values[nfield] = NULL; + } /* why _delete()? because _delete() returns the dictnode's data on success */ - if ((value = mowgli_dictionary_delete(tuple->dict, field)) == NULL) + if ((value = mowgli_dictionary_delete(tuple->dict, tfield)) == NULL) return; - + tuple_disassociate_now(value); } TupleValueType -tuple_get_value_type(Tuple *tuple, const gchar *field) +tuple_get_value_type(Tuple *tuple, const gint nfield, const gchar *field) { TupleValue *value; g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN); - g_return_val_if_fail(field != NULL, TUPLE_UNKNOWN); + g_return_val_if_fail(nfield < FIELD_LAST, TUPLE_UNKNOWN); + + if (nfield < 0) { + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL) + return value->type; + } else { + if (tuple->values[nfield]) + return tuple->values[nfield]->type; + } - if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) - return TUPLE_UNKNOWN; - - return value->type; + return TUPLE_UNKNOWN; } const gchar * -tuple_get_string(Tuple *tuple, const gchar *field) +tuple_get_string(Tuple *tuple, const gint nfield, const gchar *field) { TupleValue *value; g_return_val_if_fail(tuple != NULL, NULL); - g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(nfield < FIELD_LAST, NULL); - if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) - return NULL; + if (nfield < 0) { + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) + return NULL; + } else { + if (tuple->values[nfield]) + value = tuple->values[nfield]; + else + return NULL; + } if (value->type != TUPLE_STRING) mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL); @@ -201,16 +270,23 @@ return value->value.string; } -int -tuple_get_int(Tuple *tuple, const gchar *field) +gint +tuple_get_int(Tuple *tuple, const gint nfield, const gchar *field) { TupleValue *value; g_return_val_if_fail(tuple != NULL, 0); - g_return_val_if_fail(field != NULL, 0); + g_return_val_if_fail(nfield < FIELD_LAST, 0); - if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) - return 0; + if (nfield < 0) { + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) + return 0; + } else { + if (tuple->values[nfield]) + value = tuple->values[nfield]; + else + return 0; + } if (value->type != TUPLE_INT) mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0); diff -r abfa5f49ff07 -r 9580bb3e58fa src/audacious/tuple.h --- a/src/audacious/tuple.h Fri Aug 31 22:54:13 2007 +0100 +++ b/src/audacious/tuple.h Sat Sep 01 07:08:21 2007 +0300 @@ -24,11 +24,36 @@ #include #include -typedef struct _Tuple { - mowgli_object_t parent; - mowgli_dictionary_t *dict; -} Tuple; + +enum { + FIELD_ARTIST = 0, + FIELD_TITLE, + FIELD_ALBUM, + FIELD_COMMENT, + FIELD_GENRE, + + FIELD_TRACK, + FIELD_TRACK_NUMBER, + FIELD_LENGTH, + FIELD_YEAR, + FIELD_QUALITY, + FIELD_CODEC, + FIELD_FILE_NAME, + FIELD_FILE_PATH, + FIELD_FILE_EXT, + FIELD_SONG_ARTIST, + + FIELD_MTIME, + FIELD_FORMATTER, + FIELD_PERFORMER, + FIELD_COPYRIGHT, + + /* special field, must always be last */ + FIELD_LAST +}; + +extern const gchar *tuple_fields[FIELD_LAST]; typedef enum { TUPLE_STRING, @@ -44,15 +69,22 @@ } value; } TupleValue; +typedef struct _Tuple { + mowgli_object_t parent; + mowgli_dictionary_t *dict; + TupleValue *values[FIELD_LAST]; +} Tuple; + + Tuple *tuple_new(void); Tuple *tuple_new_from_filename(const gchar *filename); -gboolean tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string); -gboolean tuple_associate_int(Tuple *tuple, const gchar *field, gint integer); -void tuple_disassociate(Tuple *tuple, const gchar *field); +gboolean tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string); +gboolean tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer); +void tuple_disassociate(Tuple *tuple, const gint nfield, const gchar *field); void tuple_disassociate_now(TupleValue *value); -TupleValueType tuple_get_value_type(Tuple *tuple, const gchar *field); -const gchar *tuple_get_string(Tuple *tuple, const gchar *field); -int tuple_get_int(Tuple *tuple, const gchar *field); +TupleValueType tuple_get_value_type(Tuple *tuple, const gint nfield, const gchar *field); +const gchar *tuple_get_string(Tuple *tuple, const gint nfield, const gchar *field); +gint tuple_get_int(Tuple *tuple, const gint nfield, const gchar *field); #define tuple_free(x) mowgli_object_unref(x); #endif diff -r abfa5f49ff07 -r 9580bb3e58fa src/audacious/tuple_compiler.c --- a/src/audacious/tuple_compiler.c Fri Aug 31 22:54:13 2007 +0100 +++ b/src/audacious/tuple_compiler.c Sat Sep 01 07:08:21 2007 +0300 @@ -19,28 +19,13 @@ */ /* - * What's this? - * ------------ - * Nothing really. A prototype / pseudo-C for an improved Tuple formatting - * system, where the format string is "compiled" into a tree structure, - * which can then be traversed fast while "evaluating". This file does - * not represent anything but some of my (ccr) ideas for the concept. - * - * The basic ideas are: - * 1) compiled structure for faster traversing - * 2) sub-expression removal / constant elimination - * 3) indexes and/or hashes for tuple entries for faster access - * 4) avoid expensive memory re-allocation - * - * and possibly 5) caching of certain things - * - * * TODO: * - implement definitions (${=foo,"baz"} ${=foo,1234}) * - implement functions * - implement handling of external expressions * - error handling issues? * - evaluation context: how local variables should REALLY work? + * - global context needed (?) */ #include "config.h" @@ -56,14 +41,14 @@ va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); - exit(5); +// exit(5); } static void tuple_evalctx_free_var(TupleEvalVar *var) { assert(var != NULL); - + var->fieldidx = -1; g_free(var->defval); g_free(var->name); g_free(var); @@ -87,22 +72,19 @@ } -/* "Reset" the evaluation context, clean up locally set variables, - * but leave globals. +/* "Reset" the evaluation context, clean up temporary variables. */ void tuple_evalctx_reset(TupleEvalContext *ctx) { gint i; - /* Free local variables */ for (i = 0; i < ctx->nvariables; i++) if (ctx->variables[i]) { - ctx->variables[i]->dictref = NULL; + ctx->variables[i]->fieldref = NULL; - if (ctx->variables[i]->islocal) + if (ctx->variables[i]->istemp) tuple_evalctx_free_var(ctx->variables[i]); } - } @@ -133,15 +115,24 @@ } -gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type) +gint tuple_evalctx_add_var(TupleEvalContext *ctx, const gchar *name, const gboolean istemp, const gint type) { - gint i; + gint i, ref = -1; TupleEvalVar * tmp = g_new0(TupleEvalVar, 1); tmp->name = g_strdup(name); - tmp->islocal = islocal; + tmp->istemp = istemp; tmp->type = type; - + tmp->fieldidx = ref; + + /* Find fieldidx, if any */ + if (type == VAR_FIELD) { + for (i = 0; i < FIELD_LAST && ref < 0; i++) + if (strcmp(tuple_fields[i], name) == 0) ref = i; + + tmp->fieldidx = ref; + } + /* Find a free slot */ for (i = 0; i < ctx->nvariables; i++) if (!ctx->variables[i]) { @@ -379,7 +370,7 @@ /* Integer */ } - tuple_error("definitions not yet supported!\n"); + tuple_error("Definitions are not yet supported!\n"); goto ret_error; } else goto ret_error; @@ -543,17 +534,25 @@ } -/* Evaluate tuple in given TupleEval expression in given - * context and return resulting string. +/* Fetch a reference to a tuple field for given variable by fieldidx or dict. + * Return pointer to field, NULL if not available. */ -static TupleValue * tf_get_dictref(TupleEvalVar *var, Tuple *tuple) +static TupleValue * tf_get_fieldref(TupleEvalVar *var, Tuple *tuple) { - if (var->type == VAR_FIELD && var->dictref == NULL) - var->dictref = mowgli_dictionary_retrieve(tuple->dict, var->name); + if (var->type == VAR_FIELD && var->fieldref == NULL) { + if (var->fieldidx < 0) + var->fieldref = mowgli_dictionary_retrieve(tuple->dict, var->name); + else + var->fieldref = tuple->values[var->fieldidx]; + } - return var->dictref; + return var->fieldref; } + +/* Fetch string or int value of given variable, whatever type it might be. + * Return VAR_* type for the variable. + */ static TupleValueType tf_get_var(gchar **tmps, gint *tmpi, TupleEvalVar *var, Tuple *tuple) { TupleValueType type = TUPLE_UNKNOWN; @@ -562,12 +561,12 @@ case VAR_DEF: *tmps = var->defval; type = TUPLE_STRING; break; case VAR_CONST: *tmps = var->name; type = TUPLE_STRING; break; case VAR_FIELD: - if (tf_get_dictref(var, tuple)) { - if (var->dictref->type == TUPLE_STRING) - *tmps = var->dictref->value.string; + if (tf_get_fieldref(var, tuple)) { + if (var->fieldref->type == TUPLE_STRING) + *tmps = var->fieldref->value.string; else - *tmpi = var->dictref->value.integer; - type = var->dictref->type; + *tmpi = var->fieldref->value.integer; + type = var->fieldref->type; } break; default: @@ -579,6 +578,9 @@ } +/* Evaluate tuple in given TupleEval expression in given + * context and return resulting string. + */ static gboolean tuple_formatter_eval_do(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple, gchar **res, size_t *resmax, size_t *reslen) { TupleEvalNode *curr = expr; @@ -609,14 +611,14 @@ break; case VAR_FIELD: - if (tf_get_dictref(var0, tuple)) { - switch (var0->dictref->type) { + if (tf_get_fieldref(var0, tuple)) { + switch (var0->fieldref->type) { case TUPLE_STRING: - str = var0->dictref->value.string; + str = var0->fieldref->value.string; break; case TUPLE_INT: - snprintf(tmps, sizeof(tmps), "%d", var0->dictref->value.integer); + snprintf(tmps, sizeof(tmps), "%d", var0->fieldref->value.integer); str = tmps; break; @@ -669,17 +671,16 @@ case OP_IS_EMPTY: var0 = ctx->variables[curr->var[0]]; - if (var0->dictref == NULL) - var0->dictref = mowgli_dictionary_retrieve(tuple->dict, var0->name); + if (tf_get_fieldref(var0, tuple)) { - switch (var0->dictref->type) { + switch (var0->fieldref->type) { case TUPLE_INT: - result = (var0->dictref->value.integer == 0); + result = (var0->fieldref->value.integer == 0); break; case TUPLE_STRING: result = TRUE; - tmps2 = var0->dictref->value.string; + tmps2 = var0->fieldref->value.string; while (result && *tmps2 != '\0') { if (*tmps2 == ' ') @@ -692,6 +693,8 @@ default: result = TRUE; } + } else + result = TRUE; if (result) { if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) @@ -739,10 +742,7 @@ if (!expr) return NULL; - if (!tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen)) { - g_free(res); - return NULL; - } + tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen); return res; } diff -r abfa5f49ff07 -r 9580bb3e58fa src/audacious/tuple_compiler.h --- a/src/audacious/tuple_compiler.h Fri Aug 31 22:54:13 2007 +0100 +++ b/src/audacious/tuple_compiler.h Sat Sep 01 07:08:21 2007 +0300 @@ -55,7 +55,7 @@ VAR_CONST, VAR_DEF }; - + /* Caching structure for deterministic functions */ @@ -68,10 +68,12 @@ typedef struct { gchar *name; - gboolean islocal; /* Local? true = will be cleaned with tuple_evalctx_reset() */ - gint type; /* Type of this "variable", see VAR_* */ - gchar *defval; - TupleValue *dictref; /* Cached tuple value ref */ + gboolean istemp; /* Scope of variable - TRUE = temporary */ + gint type; /* Type of variable, see VAR_* */ + gchar *defval; /* Defined value ${=foo,bar} */ + + gint fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */ + TupleValue *fieldref; /* Cached tuple field ref */ } TupleEvalVar; @@ -95,7 +97,7 @@ TupleEvalContext * tuple_evalctx_new(void); void tuple_evalctx_reset(TupleEvalContext *ctx); void tuple_evalctx_free(TupleEvalContext *ctx); -gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type); +gint tuple_evalctx_add_var(TupleEvalContext *ctx, const gchar *name, const gboolean istemp, const gint type); void tuple_evalnode_free(TupleEvalNode *expr);