Replace AVL tree in RSP query by a static hash using gperf

This commit is contained in:
Julien BLACHE 2011-03-31 18:30:18 +02:00
parent b8758a2aa2
commit cc0555d986
7 changed files with 73 additions and 166 deletions

1
src/.gitignore vendored
View File

@ -9,3 +9,4 @@ RSP2SQL.[ch]
*.u *.u
daap_query_hash.c daap_query_hash.c
rsp_query_hash.c

View File

@ -22,10 +22,12 @@ OSS4SRC=laudio_oss4.c
endif endif
GPERF_FILES = \ GPERF_FILES = \
daap_query.gperf daap_query.gperf \
rsp_query.gperf
GPERF_PRODUCTS = \ GPERF_PRODUCTS = \
daap_query_hash.c daap_query_hash.c \
rsp_query_hash.c
ANTLR_GRAMMARS = \ ANTLR_GRAMMARS = \
RSP.g RSP2SQL.g \ RSP.g RSP2SQL.g \

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org> * Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -139,7 +139,7 @@ strcrit returns [ pANTLR3_STRING result, int valid ]
: ^(o = strop f = FIELD s = STR) : ^(o = strop f = FIELD s = STR)
{ {
char *op; char *op;
struct rsp_query_field_map *rqfp; const struct rsp_query_field_map *rqfp;
pANTLR3_STRING field; pANTLR3_STRING field;
char *escaped; char *escaped;
ANTLR3_UINT32 optok; ANTLR3_UINT32 optok;
@ -164,7 +164,7 @@ strcrit returns [ pANTLR3_STRING result, int valid ]
field = $f->getText($f); field = $f->getText($f);
/* Field lookup */ /* Field lookup */
rqfp = rsp_query_field_lookup((char *)field->chars); rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars));
if (!rqfp) if (!rqfp)
{ {
DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars);
@ -225,7 +225,7 @@ intcrit returns [ pANTLR3_STRING result, int valid ]
: ^(o = intop f = FIELD i = INT) : ^(o = intop f = FIELD i = INT)
{ {
char *op; char *op;
struct rsp_query_field_map *rqfp; const struct rsp_query_field_map *rqfp;
pANTLR3_STRING field; pANTLR3_STRING field;
op = NULL; op = NULL;
@ -255,7 +255,7 @@ intcrit returns [ pANTLR3_STRING result, int valid ]
field = $f->getText($f); field = $f->getText($f);
/* Field lookup */ /* Field lookup */
rqfp = rsp_query_field_lookup((char *)field->chars); rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars));
if (!rqfp) if (!rqfp)
{ {
DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars);
@ -299,7 +299,7 @@ datecrit returns [ pANTLR3_STRING result, int valid ]
: ^(o = dateop f = FIELD d = datespec) : ^(o = dateop f = FIELD d = datespec)
{ {
char *op; char *op;
struct rsp_query_field_map *rqfp; const struct rsp_query_field_map *rqfp;
pANTLR3_STRING field; pANTLR3_STRING field;
char buf[32]; char buf[32];
int ret; int ret;
@ -319,7 +319,7 @@ datecrit returns [ pANTLR3_STRING result, int valid ]
field = $f->getText($f); field = $f->getText($f);
/* Field lookup */ /* Field lookup */
rqfp = rsp_query_field_lookup((char *)field->chars); rqfp = rsp_query_field_lookup((char *)field->chars, strlen((char *)field->chars));
if (!rqfp) if (!rqfp)
{ {
DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars); DPRINTF(E_LOG, L_RSP, "Field '\%s' is not a valid field in queries\n", field->chars);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org> * Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
* *
* Adapted from mt-daapd: * Adapted from mt-daapd:
* Copyright (C) 2006-2007 Ron Pedde <ron@pedde.com> * Copyright (C) 2006-2007 Ron Pedde <ron@pedde.com>
@ -903,10 +903,6 @@ rsp_init(void)
int i; int i;
int ret; int ret;
ret = rsp_query_init();
if (ret < 0)
return ret;
for (i = 0; rsp_handlers[i].handler; i++) for (i = 0; rsp_handlers[i].handler; i++)
{ {
ret = regcomp(&rsp_handlers[i].preg, rsp_handlers[i].regexp, REG_EXTENDED | REG_NOSUB); ret = regcomp(&rsp_handlers[i].preg, rsp_handlers[i].regexp, REG_EXTENDED | REG_NOSUB);
@ -915,16 +911,11 @@ rsp_init(void)
regerror(ret, &rsp_handlers[i].preg, buf, sizeof(buf)); regerror(ret, &rsp_handlers[i].preg, buf, sizeof(buf));
DPRINTF(E_FATAL, L_RSP, "RSP init failed; regexp error: %s\n", buf); DPRINTF(E_FATAL, L_RSP, "RSP init failed; regexp error: %s\n", buf);
goto regexp_fail; return -1;
} }
} }
return 0; return 0;
regexp_fail:
rsp_query_deinit();
return -1;
} }
void void
@ -932,8 +923,6 @@ rsp_deinit(void)
{ {
int i; int i;
rsp_query_deinit();
for (i = 0; rsp_handlers[i].handler; i++) for (i = 0; rsp_handlers[i].handler; i++)
regfree(&rsp_handlers[i].preg); regfree(&rsp_handlers[i].preg);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org> * Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -25,8 +25,6 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <avl.h>
#include "logger.h" #include "logger.h"
#include "misc.h" #include "misc.h"
#include "rsp_query.h" #include "rsp_query.h"
@ -36,85 +34,9 @@
#include "RSP2SQL.h" #include "RSP2SQL.h"
static struct rsp_query_field_map rsp_query_fields[] = /* gperf static hash, rsp_query.gperf */
{ #include "rsp_query_hash.c"
{ 0, RSP_TYPE_INT, "id" },
{ 0, RSP_TYPE_STRING, "path" },
{ 0, RSP_TYPE_STRING, "fname" },
{ 0, RSP_TYPE_STRING, "title" },
{ 0, RSP_TYPE_STRING, "artist" },
{ 0, RSP_TYPE_STRING, "album" },
{ 0, RSP_TYPE_STRING, "genre" },
{ 0, RSP_TYPE_STRING, "comment" },
{ 0, RSP_TYPE_STRING, "type" },
{ 0, RSP_TYPE_STRING, "composer" },
{ 0, RSP_TYPE_STRING, "orchestra" },
{ 0, RSP_TYPE_STRING, "grouping" },
{ 0, RSP_TYPE_STRING, "url" },
{ 0, RSP_TYPE_INT, "bitrate" },
{ 0, RSP_TYPE_INT, "samplerate" },
{ 0, RSP_TYPE_INT, "song_length" },
{ 0, RSP_TYPE_INT, "file_size" },
{ 0, RSP_TYPE_INT, "year" },
{ 0, RSP_TYPE_INT, "track" },
{ 0, RSP_TYPE_INT, "total_tracks" },
{ 0, RSP_TYPE_INT, "disc" },
{ 0, RSP_TYPE_INT, "total_discs" },
{ 0, RSP_TYPE_INT, "bpm" },
{ 0, RSP_TYPE_INT, "compilation" },
{ 0, RSP_TYPE_INT, "rating" },
{ 0, RSP_TYPE_INT, "play_count" },
{ 0, RSP_TYPE_INT, "data_kind" },
{ 0, RSP_TYPE_INT, "item_kind" },
{ 0, RSP_TYPE_STRING, "description" },
{ 0, RSP_TYPE_DATE, "time_added" },
{ 0, RSP_TYPE_DATE, "time_modified" },
{ 0, RSP_TYPE_DATE, "time_played" },
{ 0, RSP_TYPE_DATE, "db_timestamp" },
{ 0, RSP_TYPE_INT, "sample_count" },
{ 0, RSP_TYPE_STRING, "codectype" },
{ 0, RSP_TYPE_INT, "idx" },
{ 0, RSP_TYPE_INT, "has_video" },
{ 0, RSP_TYPE_INT, "contentrating" },
{ 0, RSP_TYPE_INT, "bits_per_sample" },
{ 0, RSP_TYPE_STRING, "album_artist" },
{ -1, -1, NULL }
};
static avl_tree_t *rsp_query_fields_hash;
static int
rsp_query_field_map_compare(const void *aa, const void *bb)
{
struct rsp_query_field_map *a = (struct rsp_query_field_map *)aa;
struct rsp_query_field_map *b = (struct rsp_query_field_map *)bb;
if (a->hash < b->hash)
return -1;
if (a->hash > b->hash)
return 1;
return 0;
}
struct rsp_query_field_map *
rsp_query_field_lookup(char *field)
{
struct rsp_query_field_map rqfm;
avl_node_t *node;
rqfm.hash = djb_hash(field, strlen(field));
node = avl_search(rsp_query_fields_hash, &rqfm);
if (!node)
return NULL;
return (struct rsp_query_field_map *)node->item;
}
char * char *
rsp_query_parse_sql(const char *rsp_query) rsp_query_parse_sql(const char *rsp_query)
@ -227,56 +149,3 @@ rsp_query_parse_sql(const char *rsp_query)
return ret; return ret;
} }
int
rsp_query_init(void)
{
avl_node_t *node;
struct rsp_query_field_map *rqfm;
int i;
rsp_query_fields_hash = avl_alloc_tree(rsp_query_field_map_compare, NULL);
if (!rsp_query_fields_hash)
{
DPRINTF(E_FATAL, L_RSP, "RSP query init could not allocate AVL tree\n");
return -1;
}
for (i = 0; rsp_query_fields[i].hash == 0; i++)
{
rsp_query_fields[i].hash = djb_hash(rsp_query_fields[i].rsp_field, strlen(rsp_query_fields[i].rsp_field));
node = avl_insert(rsp_query_fields_hash, &rsp_query_fields[i]);
if (!node)
{
if (errno != EEXIST)
DPRINTF(E_FATAL, L_RSP, "RSP query init failed; AVL insert error: %s\n", strerror(errno));
else
{
node = avl_search(rsp_query_fields_hash, &rsp_query_fields[i]);
rqfm = node->item;
DPRINTF(E_FATAL, L_RSP, "RSP query init failed; WARNING: duplicate hash key\n");
DPRINTF(E_FATAL, L_RSP, "Hash %x, string %s\n", rsp_query_fields[i].hash, rsp_query_fields[i].rsp_field);
DPRINTF(E_FATAL, L_RSP, "Hash %x, string %s\n", rqfm->hash, rqfm->rsp_field);
}
goto avl_insert_fail;
}
}
return 0;
avl_insert_fail:
avl_free_tree(rsp_query_fields_hash);
return -1;
}
void
rsp_query_deinit(void)
{
avl_free_tree(rsp_query_fields_hash);
}

52
src/rsp_query.gperf Normal file
View File

@ -0,0 +1,52 @@
%language=ANSI-C
%readonly-tables
%enum
%switch=1
%compare-lengths
%define hash-function-name rsp_query_field_hash
%define lookup-function-name rsp_query_field_lookup
%define slot-name rsp_field
%struct-type
%omit-struct-type
struct rsp_query_field_map;
%%
"id", RSP_TYPE_INT
"path", RSP_TYPE_STRING
"fname", RSP_TYPE_STRING
"title", RSP_TYPE_STRING
"artist", RSP_TYPE_STRING
"album", RSP_TYPE_STRING
"genre", RSP_TYPE_STRING
"comment", RSP_TYPE_STRING
"type", RSP_TYPE_STRING
"composer", RSP_TYPE_STRING
"orchestra", RSP_TYPE_STRING
"grouping", RSP_TYPE_STRING
"url", RSP_TYPE_STRING
"bitrate", RSP_TYPE_INT
"samplerate", RSP_TYPE_INT
"song_length", RSP_TYPE_INT
"file_size", RSP_TYPE_INT
"year", RSP_TYPE_INT
"track", RSP_TYPE_INT
"total_tracks", RSP_TYPE_INT
"disc", RSP_TYPE_INT
"total_discs", RSP_TYPE_INT
"bpm", RSP_TYPE_INT
"compilation", RSP_TYPE_INT
"rating", RSP_TYPE_INT
"play_count", RSP_TYPE_INT
"data_kind", RSP_TYPE_INT
"item_kind", RSP_TYPE_INT
"description", RSP_TYPE_STRING
"time_added", RSP_TYPE_DATE
"time_modified", RSP_TYPE_DATE
"time_played", RSP_TYPE_DATE
"db_timestamp", RSP_TYPE_DATE
"sample_count", RSP_TYPE_INT
"codectype", RSP_TYPE_STRING
"idx", RSP_TYPE_INT
"has_video", RSP_TYPE_INT
"contentrating", RSP_TYPE_INT
"bits_per_sample", RSP_TYPE_INT
"album_artist", RSP_TYPE_STRING

View File

@ -9,23 +9,17 @@
#define RSP_TYPE_DATE 2 #define RSP_TYPE_DATE 2
struct rsp_query_field_map { struct rsp_query_field_map {
uint32_t hash;
int field_type;
char *rsp_field; char *rsp_field;
int field_type;
/* RSP fields are named after the DB columns - or vice versa */ /* RSP fields are named after the DB columns - or vice versa */
}; };
struct rsp_query_field_map * /* Generated by gperf - keep in sync, don't alter */
rsp_query_field_lookup(char *field); const struct rsp_query_field_map *
rsp_query_field_lookup (register const char *str, register unsigned int len);
char * char *
rsp_query_parse_sql(const char *rsp_query); rsp_query_parse_sql(const char *rsp_query);
int
rsp_query_init(void);
void
rsp_query_deinit(void);
#endif /* !__RSP_QUERY_H__ */ #endif /* !__RSP_QUERY_H__ */