comparison sidlib.c @ 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 c5ff71d64e53
comparison
equal deleted inserted replaced
85:4c0ecb078591 86:e1ff9cd27a84
193 case PSF_MODEL_MOS8580 : return "MOS8580"; 193 case PSF_MODEL_MOS8580 : return "MOS8580";
194 case PSF_MODEL_ANY : return "MOS6581 / MOS8580"; 194 case PSF_MODEL_ANY : return "MOS6581 / MOS8580";
195 default : return "?"; 195 default : return "?";
196 } 196 }
197 } 197 }
198
199
200 // Free memory allocated for given SLDB node
201 //
202 static void si_sldb_node_free(SIDLibSLDBNode *node)
203 {
204 if (node)
205 {
206 th_free_r(&node->lengths);
207 th_free_r(&node);
208 }
209 }
210
211
212 // Insert given node to db linked list
213 //
214 static void si_sldb_node_insert(SIDLibSLDB *dbh, SIDLibSLDBNode *node)
215 {
216 if (dbh->nodes)
217 {
218 node->prev = dbh->nodes->prev;
219 dbh->nodes->prev->next = node;
220 dbh->nodes->prev = node;
221 }
222 else
223 {
224 dbh->nodes = node;
225 node->prev = node;
226 }
227 node->next = NULL;
228 }
229
230
231 // Parse a time-entry in SLDB format
232 //
233 static int si_sldb_get_value(const char *str, size_t *pos)
234 {
235 int result = 0;
236
237 while (isdigit(str[*pos]))
238 result = (result * 10) + (str[(*pos)++] - '0');
239
240 return result;
241 }
242
243
244 static int si_sldb_get_hex_value(const char *str, size_t *pos)
245 {
246 int result = 0;
247
248 do
249 {
250 char ch = str[*pos];
251 result <<= 4;
252 if (ch >= 'A' && ch <= 'F')
253 result |= ch - 'A';
254 else
255 if (ch >= 'a' && ch <= 'f')
256 result |= ch - 'a';
257 else
258 if (ch >= '0' && ch <= '9')
259 result |= ch - '0';
260 else
261 break;
262 }
263 while (1);
264
265 return result;
266 }
267
268
269 static int si_sldb_gettime(const char *str, size_t *pos)
270 {
271 int result;
272
273 // Check if it starts with a digit
274 if (isdigit(str[*pos]))
275 {
276 // Get minutes-field
277 result = si_sldb_get_value(str, pos) * 60;
278
279 // Check the field separator char
280 if (str[*pos] == ':')
281 {
282 // Get seconds-field
283 (*pos)++;
284 result += si_sldb_get_value(str, pos);
285 }
286 else
287 result = -2;
288 }
289 else
290 result = -1;
291
292 // Ignore and skip the possible attributes
293 while (str[*pos] && !isspace(str[*pos]))
294 (*pos)++;
295
296 return result;
297 }
298
299
300 // Parse one SLDB definition line, return SLDB node
301 //
302 SIDLibSLDBNode *si_sldb_parse_entry(th_ioctx *ctx, const char *line)
303 {
304 SIDLibSLDBNode *node = NULL;
305 size_t pos, tmpLen, savePos;
306 BOOL isOK;
307 int i;
308
309 // Allocate new node
310 node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode));
311 if (node == NULL)
312 {
313 th_io_error(ctx, THERR_MALLOC,
314 "Error allocating new node.\n");
315 return NULL;
316 }
317
318 // Get hash value
319 pos = 0;
320 for (i = 0; i < TH_MD5HASH_LENGTH; i++, pos += 2)
321 {
322 unsigned int tmpu;
323 sscanf(&line[pos], "%2x", &tmpu);
324 node->hash[i] = tmpu;
325 }
326
327 // Get playtimes
328 th_findnext(line, &pos);
329 if (line[pos] != '=')
330 {
331 th_io_error(ctx, THERR_INVALID_DATA,
332 "'=' expected on column #%d.\n", pos);
333 goto error;
334 }
335
336 // First playtime is after '='
337 savePos = ++pos;
338 tmpLen = strlen(line);
339
340 // Get number of sub-tune lengths
341 isOK = TRUE;
342 while (pos < tmpLen && isOK)
343 {
344 th_findnext(line, &pos);
345
346 if (si_sldb_gettime(line, &pos) >= 0)
347 node->nlengths++;
348 else
349 isOK = FALSE;
350 }
351
352 // Allocate memory for lengths
353 if (node->nlengths == 0)
354 goto error;
355
356 node->lengths = (int *) th_malloc0(node->nlengths * sizeof(int));
357 if (node->lengths == NULL)
358 {
359 th_io_error(ctx, THERR_MALLOC,
360 "Could not allocate memory for node.\n");
361 goto error;
362 }
363
364 // Read lengths in
365 for (i = 0, pos = savePos, isOK = TRUE;
366 pos < tmpLen && i < node->nlengths && isOK; i++)
367 {
368 int l;
369 th_findnext(line, &pos);
370
371 l = si_sldb_gettime(line, &pos);
372 if (l >= 0)
373 node->lengths[i] = l;
374 else
375 isOK = FALSE;
376 }
377
378 return node;
379
380 error:
381 si_sldb_node_free(node);
382 return NULL;
383 }
384
385
386 // Read SLDB database to memory
387 //
388 int si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh)
389 {
390 char line[PSID_BUFFER2_SIZE];
391
392 while (thfgets(line, PSID_BUFFER2_SIZE, ctx) != NULL)
393 {
394 SIDLibSLDBNode *tmnode;
395 size_t pos = 0;
396 ctx->line++;
397
398 th_findnext(line, &pos);
399
400 // Check if it is datafield
401 if (th_isxdigit(line[pos]))
402 {
403 // Check the length of the hash
404 int hashLen;
405 for (hashLen = 0; line[pos] && th_isxdigit(line[pos]); hashLen++, pos++);
406
407 if (hashLen != TH_MD5HASH_LENGTH_CH)
408 {
409 th_io_error(ctx, THERR_INVALID_DATA,
410 "Invalid MD5-hash in SongLengthDB file '%s' line #%d:\n%s\n",
411 ctx->filename, ctx->line, line);
412 }
413 else
414 {
415 // Parse and add node to db
416 if ((tmnode = si_sldb_parse_entry(ctx, line)) != NULL)
417 {
418 si_sldb_node_insert(dbh, tmnode);
419 }
420 else
421 {
422 th_io_error(ctx, THERR_INVALID_DATA,
423 "Invalid entry in SongLengthDB file '%s' line #%d:\n%s\n",
424 ctx->filename, ctx->line, line);
425 }
426 }
427 }
428 else
429 if (line[pos] != ';' && line[pos] != '[' && line[pos] != 0)
430 {
431 th_io_error(ctx, THERR_INVALID_DATA,
432 "Invalid line in SongLengthDB file '%s' line #%d:\n%s\n",
433 ctx->filename, ctx->line, line);
434 }
435 }
436
437 return THERR_OK;
438 }
439
440
441 // Compare two given MD5-hashes.
442 // Return: 0 if equal
443 // negative if testHash1 < testHash2
444 // positive if testHash1 > testHash2
445 //
446 static int si_sldb_compare_hash(th_md5hash_t testHash1, th_md5hash_t testHash2)
447 {
448 int i, delta;
449
450 for (i = delta = 0; i < TH_MD5HASH_LENGTH && !delta; i++)
451 delta = testHash1[i] - testHash2[i];
452
453 return delta;
454 }
455
456
457 // Compare two nodes.
458 // We assume here that we never ever get NULL-pointers.
459 static int si_sldb_compare_nodes(const void *node1, const void *node2)
460 {
461 return si_sldb_compare_hash(
462 (*(SIDLibSLDBNode **) node1)->hash,
463 (*(SIDLibSLDBNode **) node2)->hash);
464 }
465
466
467 // (Re)create index
468 //
469 int si_sldb_build_index(SIDLibSLDB * dbh)
470 {
471 SIDLibSLDBNode *node;
472
473 // Free old index
474 th_free_r(&dbh->pindex);
475
476 // Get size of db
477 for (node = dbh->nodes, dbh->n = 0; node != NULL; node = node->next)
478 dbh->n++;
479
480 // Check number of nodes
481 if (dbh->n > 0)
482 {
483 size_t i;
484
485 // Allocate memory for index-table
486 dbh->pindex = (SIDLibSLDBNode * *) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->n);
487 if (dbh->pindex == NULL)
488 return -1;
489
490 // Get node-pointers to table
491 for (i = 0, node = dbh->nodes; node && i < dbh->n; node = node->next)
492 dbh->pindex[i++] = node;
493
494 // Sort the indexes
495 qsort(dbh->pindex, dbh->n, sizeof(SIDLibSLDBNode *), si_sldb_compare_nodes);
496 }
497
498 return 0;
499 }
500
501
502 // Free a given song-length database
503 //
504 void si_sldb_free(SIDLibSLDB *dbh)
505 {
506 SIDLibSLDBNode *node = dbh->nodes;
507 while (node != NULL)
508 {
509 SIDLibSLDBNode *next = node->next;
510 si_sldb_node_free(node);
511 node = next;
512 }
513
514 dbh->nodes = NULL;
515 dbh->n = 0;
516
517 th_free_r(&dbh->pindex);
518 th_free(dbh);
519 }
520
521
522 SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash)
523 {
524 SIDLibSLDBNode keyItem, *key, **item;
525
526 memcpy(&keyItem.hash, &hash, sizeof(th_md5hash_t));
527 key = &keyItem;
528 item = bsearch(&key, dbh->pindex, dbh->n, sizeof(dbh->pindex[0]), si_sldb_compare_nodes);
529
530 return (item != NULL) ? *item : NULL;
531 }