changeset 3489:9580bb3e58fa trunk

Tuple handling API changed to include support for "hardcoded" fields.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 01 Sep 2007 07:08:21 +0300
parents abfa5f49ff07
children 602ec8c40d0d
files src/audacious/tuple.c src/audacious/tuple.h src/audacious/tuple_compiler.c src/audacious/tuple_compiler.h
diffstat 4 files changed, 210 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- 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);
--- 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 <glib.h>
 #include <mowgli.h>
 
-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
--- 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;
 }
--- 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);