changeset 86:e1ff9cd27a84

Initial port of songlength database (SLDB) handling code from XMMS-SID to here. Needs refactoring.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 11 Feb 2016 23:21:26 +0200
parents 4c0ecb078591
children 1e89b757f8a1
files sidlib.c sidlib.h
diffstat 2 files changed, 357 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/sidlib.c	Thu Feb 11 23:18:58 2016 +0200
+++ b/sidlib.c	Thu Feb 11 23:21:26 2016 +0200
@@ -195,3 +195,337 @@
         default                : return "?";
     }
 }
+
+
+// Free memory allocated for given SLDB node
+//
+static void si_sldb_node_free(SIDLibSLDBNode *node)
+{
+    if (node)
+    {
+        th_free_r(&node->lengths);
+        th_free_r(&node);
+    }
+}
+
+
+// Insert given node to db linked list
+//
+static void si_sldb_node_insert(SIDLibSLDB *dbh, SIDLibSLDBNode *node)
+{
+    if (dbh->nodes)
+    {
+        node->prev = dbh->nodes->prev;
+        dbh->nodes->prev->next = node;
+        dbh->nodes->prev = node;
+    }
+    else
+    {
+        dbh->nodes = node;
+        node->prev = node;
+    }
+    node->next = NULL;
+}
+
+
+// Parse a time-entry in SLDB format
+//
+static int si_sldb_get_value(const char *str, size_t *pos)
+{
+    int result = 0;
+
+    while (isdigit(str[*pos]))
+        result = (result * 10) + (str[(*pos)++] - '0');
+
+    return result;
+}
+
+
+static int si_sldb_get_hex_value(const char *str, size_t *pos)
+{
+    int result = 0;
+
+    do
+    {
+        char ch = str[*pos];
+        result <<= 4;
+        if (ch >= 'A' && ch <= 'F')
+            result |= ch - 'A';
+        else
+        if (ch >= 'a' && ch <= 'f')
+            result |= ch - 'a';
+        else
+        if (ch >= '0' && ch <= '9')
+            result |= ch - '0';
+        else
+            break;
+    }
+    while (1);
+
+    return result;
+}
+
+
+static int si_sldb_gettime(const char *str, size_t *pos)
+{
+    int result;
+
+    // Check if it starts with a digit
+    if (isdigit(str[*pos]))
+    {
+        // Get minutes-field
+        result = si_sldb_get_value(str, pos) * 60;
+
+        // Check the field separator char
+        if (str[*pos] == ':')
+        {
+            // Get seconds-field
+            (*pos)++;
+            result += si_sldb_get_value(str, pos);
+        }
+        else
+            result = -2;
+    }
+    else
+        result = -1;
+
+    // Ignore and skip the possible attributes
+    while (str[*pos] && !isspace(str[*pos]))
+        (*pos)++;
+
+    return result;
+}
+
+
+// Parse one SLDB definition line, return SLDB node
+//
+SIDLibSLDBNode *si_sldb_parse_entry(th_ioctx *ctx, const char *line)
+{
+    SIDLibSLDBNode *node = NULL;
+    size_t pos, tmpLen, savePos;
+    BOOL isOK;
+    int i;
+
+    // Allocate new node
+    node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode));
+    if (node == NULL)
+    {
+        th_io_error(ctx, THERR_MALLOC,
+            "Error allocating new node.\n");
+        return NULL;
+    }
+
+    // Get hash value
+    pos = 0;
+    for (i = 0; i < TH_MD5HASH_LENGTH; i++, pos += 2)
+    {
+        unsigned int tmpu;
+        sscanf(&line[pos], "%2x", &tmpu);
+        node->hash[i] = tmpu;
+    }
+
+    // Get playtimes
+    th_findnext(line, &pos);
+    if (line[pos] != '=')
+    {
+        th_io_error(ctx, THERR_INVALID_DATA,
+            "'=' expected on column #%d.\n", pos);
+        goto error;
+    }
+
+    // First playtime is after '='
+    savePos = ++pos;
+    tmpLen = strlen(line);
+
+    // Get number of sub-tune lengths
+    isOK = TRUE;
+    while (pos < tmpLen && isOK)
+    {
+        th_findnext(line, &pos);
+
+        if (si_sldb_gettime(line, &pos) >= 0)
+            node->nlengths++;
+        else
+            isOK = FALSE;
+    }
+
+    // Allocate memory for lengths
+    if (node->nlengths == 0)
+        goto error;
+
+    node->lengths = (int *) th_malloc0(node->nlengths * sizeof(int));
+    if (node->lengths == NULL)
+    {
+        th_io_error(ctx, THERR_MALLOC,
+            "Could not allocate memory for node.\n");
+        goto error;
+    }
+
+    // Read lengths in
+    for (i = 0, pos = savePos, isOK = TRUE;
+         pos < tmpLen && i < node->nlengths && isOK; i++)
+    {
+        int l;
+        th_findnext(line, &pos);
+
+        l = si_sldb_gettime(line, &pos);
+        if (l >= 0)
+            node->lengths[i] = l;
+        else
+            isOK = FALSE;
+    }
+
+    return node;
+
+error:
+    si_sldb_node_free(node);
+    return NULL;
+}
+
+
+// Read SLDB database to memory
+//
+int si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh)
+{
+    char line[PSID_BUFFER2_SIZE];
+
+    while (thfgets(line, PSID_BUFFER2_SIZE, ctx) != NULL)
+    {
+        SIDLibSLDBNode *tmnode;
+        size_t pos = 0;
+        ctx->line++;
+
+        th_findnext(line, &pos);
+
+        // Check if it is datafield
+        if (th_isxdigit(line[pos]))
+        {
+            // Check the length of the hash
+            int hashLen;
+            for (hashLen = 0; line[pos] && th_isxdigit(line[pos]); hashLen++, pos++);
+
+            if (hashLen != TH_MD5HASH_LENGTH_CH)
+            {
+                th_io_error(ctx, THERR_INVALID_DATA,
+                    "Invalid MD5-hash in SongLengthDB file '%s' line #%d:\n%s\n",
+                    ctx->filename, ctx->line, line);
+            }
+            else
+            {
+                // Parse and add node to db
+                if ((tmnode = si_sldb_parse_entry(ctx, line)) != NULL)
+                {
+                    si_sldb_node_insert(dbh, tmnode);
+                }
+                else
+                {
+                    th_io_error(ctx, THERR_INVALID_DATA,
+                        "Invalid entry in SongLengthDB file '%s' line #%d:\n%s\n",
+                        ctx->filename, ctx->line, line);
+                }
+            }
+        }
+        else
+        if (line[pos] != ';' && line[pos] != '[' && line[pos] != 0)
+        {
+            th_io_error(ctx, THERR_INVALID_DATA,
+                "Invalid line in SongLengthDB file '%s' line #%d:\n%s\n",
+                ctx->filename, ctx->line, line);
+        }
+    }
+
+    return THERR_OK;
+}
+
+
+// Compare two given MD5-hashes.
+// Return: 0 if equal
+//         negative if testHash1 < testHash2
+//         positive if testHash1 > testHash2
+//
+static int si_sldb_compare_hash(th_md5hash_t testHash1, th_md5hash_t testHash2)
+{
+    int i, delta;
+
+    for (i = delta = 0; i < TH_MD5HASH_LENGTH && !delta; i++)
+        delta = testHash1[i] - testHash2[i];
+
+    return delta;
+}
+
+
+// Compare two nodes.
+// We assume here that we never ever get NULL-pointers.
+static int si_sldb_compare_nodes(const void *node1, const void *node2)
+{
+    return si_sldb_compare_hash(
+        (*(SIDLibSLDBNode **) node1)->hash,
+        (*(SIDLibSLDBNode **) node2)->hash);
+}
+
+
+// (Re)create index
+//
+int si_sldb_build_index(SIDLibSLDB * dbh)
+{
+    SIDLibSLDBNode *node;
+
+    // Free old index
+    th_free_r(&dbh->pindex);
+
+    // Get size of db
+    for (node = dbh->nodes, dbh->n = 0; node != NULL; node = node->next)
+        dbh->n++;
+
+    // Check number of nodes
+    if (dbh->n > 0)
+    {
+        size_t i;
+
+        // Allocate memory for index-table
+        dbh->pindex = (SIDLibSLDBNode * *) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->n);
+        if (dbh->pindex == NULL)
+            return -1;
+
+        // Get node-pointers to table
+        for (i = 0, node = dbh->nodes; node && i < dbh->n; node = node->next)
+            dbh->pindex[i++] = node;
+
+        // Sort the indexes
+        qsort(dbh->pindex, dbh->n, sizeof(SIDLibSLDBNode *), si_sldb_compare_nodes);
+    }
+
+    return 0;
+}
+
+
+// Free a given song-length database
+//
+void si_sldb_free(SIDLibSLDB *dbh)
+{
+    SIDLibSLDBNode *node = dbh->nodes;
+    while (node != NULL)
+    {
+        SIDLibSLDBNode *next = node->next;
+        si_sldb_node_free(node);
+        node = next;
+    }
+
+    dbh->nodes = NULL;
+    dbh->n = 0;
+
+    th_free_r(&dbh->pindex);
+    th_free(dbh);
+}
+
+
+SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash)
+{
+    SIDLibSLDBNode keyItem, *key, **item;
+
+    memcpy(&keyItem.hash, &hash, sizeof(th_md5hash_t));
+    key = &keyItem;
+    item = bsearch(&key, dbh->pindex, dbh->n, sizeof(dbh->pindex[0]), si_sldb_compare_nodes);
+
+    return (item != NULL) ? *item : NULL;
+}
--- a/sidlib.h	Thu Feb 11 23:18:58 2016 +0200
+++ b/sidlib.h	Thu Feb 11 23:21:26 2016 +0200
@@ -20,6 +20,24 @@
 #define PSID_MAGIC_LEN    4
 #define PSID_STR_LEN      32
 #define PSID_BUFFER_SIZE  (1024 * 16)
+#define PSID_BUFFER2_SIZE (1024)
+
+
+typedef struct _SIDLibSLDBNode
+{
+    th_md5hash_t    hash;       // MD5 hash-digest
+    int             nlengths;   // Number of lengths
+    int             *lengths;   // Lengths in seconds
+    struct _SIDLibSLDBNode *prev, *next;
+} SIDLibSLDBNode;
+
+
+typedef struct
+{
+    SIDLibSLDBNode  *nodes,
+                    **pindex;
+    size_t          n;
+} SIDLibSLDB;
 
 
 typedef struct
@@ -78,6 +96,11 @@
 const char *    si_get_sid_clock_str(const int flags);
 const char *    si_get_sid_model_str(const int flags);
 
+int             si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh);
+int             si_sldb_build_index(SIDLibSLDB *dbh);
+void            si_sldb_free(SIDLibSLDB *dbh);
+SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash);
+
 
 #ifdef __cplusplus
 }