# HG changeset patch # User Matti Hamalainen # Date 1455492037 -7200 # Node ID 9d4d1783800ba1ecdcacd37fac7dcfb3c634850b # Parent be62e4abe3a9d8287523d4a068fd5fa8568e93aa Add si_sldb_{read,write}_bin() functions to write and read a binary format SLDB database, which is faster than parsing Songlengths.txt. diff -r be62e4abe3a9 -r 9d4d1783800b sidlib.c --- 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) diff -r be62e4abe3a9 -r 9d4d1783800b sidlib.h --- 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 }