changeset 101:9d4d1783800b

Add si_sldb_{read,write}_bin() functions to write and read a binary format SLDB database, which is faster than parsing Songlengths.txt.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 15 Feb 2016 01:20:37 +0200
parents be62e4abe3a9
children 1362f00e4368
files sidlib.c sidlib.h
diffstat 2 files changed, 183 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/sidlib.c	Mon Feb 15 00:28:50 2016 +0200
+++ b/sidlib.c	Mon Feb 15 01:20:37 2016 +0200
@@ -480,6 +480,186 @@
 }
 
 
+#define SIDLIB_DB_MAGIC "SIDLibDB"
+#define SIDLIB_DB_VERSION 0x0100
+
+typedef struct
+{
+    char magic[8];      // File magic ID
+    uint16_t version;   // Version
+    uint32_t nnodes;    // Number of song nodes
+//  struct {
+//      th_md5hash_t hash;         // Hash for this SID file
+//      uint16_t nlengths;         // Number of song (lengths)
+//      uint16_t length[nlengths]; // Length in seconds
+//  } * nnodes;
+    th_md5hash_t hash;
+} PSIDLibHdr;
+
+
+int si_sldb_read_bin(th_ioctx *ctx, SIDLibSLDB *dbh)
+{
+    PSIDLibHdr hdr;
+    th_md5state_t state;
+    th_md5hash_t hash;
+    size_t n;
+
+    // Check pointers
+    if (ctx == NULL || dbh == NULL)
+        return THERR_NULLPTR;
+
+    if (!thfread_str(ctx, &hdr.magic, sizeof(hdr.magic)) ||
+        !thfread_le16(ctx, &hdr.version) ||
+        !thfread_le32(ctx, &hdr.nnodes))
+        return THERR_FREAD;
+
+    // Check magic and version
+    if (memcmp(hdr.magic, SIDLIB_DB_MAGIC, sizeof(hdr.magic)) != 0)
+        return THERR_NOT_SUPPORTED;
+
+    if (hdr.version != SIDLIB_DB_VERSION)
+        return THERR_NOT_SUPPORTED;
+
+    // Make some reasonable assumptions about validity
+    if (hdr.nnodes == 0 || hdr.nnodes > 256*1024)
+        return THERR_INVALID_DATA;
+
+    th_md5_init(&state);
+    th_md5_append_ne16(&state, hdr.version);
+    th_md5_append_ne32(&state, hdr.nnodes);
+
+    // Allocate index
+    dbh->nnodes = hdr.nnodes;
+    dbh->pindex = (SIDLibSLDBNode **) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->nnodes);
+    if (dbh->pindex == NULL)
+        return THERR_MALLOC;
+
+    // Read nodes
+    for (n = 0; n < dbh->nnodes; n++)
+    {
+        SIDLibSLDBNode *node;
+        th_md5hash_t mhash;
+        uint16_t tmpn;
+        int index;
+
+        // Read node hash and nlengths
+        if (!thfread_str(ctx, mhash, TH_MD5HASH_LENGTH) ||
+            !thfread_le16(ctx, &tmpn))
+            return THERR_FREAD;
+
+        // Sanity check
+        if (tmpn == 0 || tmpn > 2048)
+            return THERR_INVALID_DATA;
+
+        // Append to hash
+        th_md5_append(&state, mhash, TH_MD5HASH_LENGTH);
+        th_md5_append_ne16(&state, tmpn);
+
+        // Allocate node
+        node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode));
+        if (node == NULL)
+            return THERR_MALLOC;
+
+        node->nlengths = tmpn;
+        memcpy(node->hash, mhash, sizeof(mhash));
+
+        node->lengths = (int *) th_calloc(node->nlengths, sizeof(int));
+        if (node->lengths == NULL)
+        {
+            th_free(node);
+            th_io_error(ctx, THERR_MALLOC,
+                "Could not allocate memory for node.\n");
+            return ctx->errno;
+        }
+
+        // Read node lenghts
+        for (index = 0; index < node->nlengths; index++)
+        {
+            uint16_t tmpl;
+            if (!thfread_le16(ctx, &tmpl))
+                return THERR_FREAD;
+
+            th_md5_append_ne16(&state, tmpl);
+            node->lengths[index] = tmpl;
+        }
+
+        si_sldb_node_insert(dbh, node);
+        dbh->pindex[n] = node;
+    }
+
+    // Read stored checksum hash
+    if (!thfread_str(ctx, hdr.hash, sizeof(hdr.hash)))
+        return THERR_FREAD;
+
+    // Compare to what we get
+    th_md5_finish(&state, hash);
+    if (memcmp(hash, hdr.hash, sizeof(hdr.hash)) != 0)
+        return THERR_INVALID_DATA;
+
+    return THERR_OK;
+}
+
+
+int si_sldb_write_bin(th_ioctx *ctx, SIDLibSLDB *dbh)
+{
+    th_md5state_t state;
+    th_md5hash_t hash;
+    size_t n;
+
+    // Check pointers
+    if (ctx == NULL || dbh == NULL)
+        return THERR_NULLPTR;
+
+    // Write header
+    if (!thfwrite_str(ctx, SIDLIB_DB_MAGIC, 8) ||
+        !thfwrite_le16(ctx, SIDLIB_DB_VERSION) ||
+        !thfwrite_le32(ctx, dbh->nnodes))
+        return THERR_FWRITE;
+
+    th_md5_init(&state);
+    th_md5_append_ne16(&state, SIDLIB_DB_VERSION);
+    th_md5_append_ne32(&state, dbh->nnodes);
+
+    // Write nodes
+    for (n = 0; n < dbh->nnodes; n++)
+    {
+        SIDLibSLDBNode *node = dbh->pindex[n];
+        uint16_t tmpn;
+        int index;
+
+        // Sanity check
+        if (node->nlengths <= 0 || node->nlengths > 2048)
+            return THERR_INVALID_DATA;
+
+        tmpn = node->nlengths;
+        th_md5_append(&state, node->hash, TH_MD5HASH_LENGTH);
+        th_md5_append_ne16(&state, tmpn);
+
+        // Write node data
+        if (!thfwrite_str(ctx, node->hash, TH_MD5HASH_LENGTH) ||
+            !thfwrite_le16(ctx, tmpn))
+            return THERR_FWRITE;
+
+        for (index = 0; index < node->nlengths; index++)
+        {
+            if (node->lengths[index] > 16378)
+                return THERR_INVALID_DATA;
+
+            if (!thfwrite_le16(ctx, node->lengths[index]))
+                return THERR_FWRITE;
+
+            th_md5_append_ne16(&state, node->lengths[index]);
+        }
+    }
+
+    th_md5_finish(&state, hash);
+    if (!thfwrite_str(ctx, hash, sizeof(hash)))
+        return THERR_FWRITE;
+
+    return THERR_OK;
+}
+
+
 // Free a given song-length database
 //
 void si_sldb_free(SIDLibSLDB *dbh)
--- a/sidlib.h	Mon Feb 15 00:28:50 2016 +0200
+++ b/sidlib.h	Mon Feb 15 01:20:37 2016 +0200
@@ -103,6 +103,9 @@
 void            si_sldb_free(SIDLibSLDB *dbh);
 SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash);
 
+int si_sldb_read_bin(th_ioctx *ctx, SIDLibSLDB *dbh);
+int si_sldb_write_bin(th_ioctx *ctx, SIDLibSLDB *dbh);
+
 
 #ifdef __cplusplus
 }