1/* 2 * Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org> 3 * Copyright (C) 2010 Kai Elwert <elwertk@googlemail.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20#ifdef HAVE_CONFIG_H 21# include <config.h> 22#endif 23 24#include <string.h> 25#include <stdint.h> 26 27#include <unistr.h> 28#include <unictype.h> 29#include <unicase.h> 30 31#include <sqlite3ext.h> 32SQLITE_EXTENSION_INIT1 33 34 35/* 36 * MurmurHash2, 64-bit versions, by Austin Appleby 37 * 38 * Code released under the public domain, as per 39 * <http://murmurhash.googlepages.com/> 40 * as of 2010-01-03. 41 */ 42 43#if SIZEOF_VOID_P == 8 /* 64bit platforms */ 44 45static uint64_t 46murmur_hash64(const void *key, int len, uint32_t seed) 47{ 48 const int r = 47; 49 const uint64_t m = 0xc6a4a7935bd1e995; 50 51 const uint64_t *data; 52 const uint64_t *end; 53 const unsigned char *data_tail; 54 uint64_t h; 55 uint64_t k; 56 57 h = seed ^ (len * m); 58 data = (const uint64_t *)key; 59 end = data + (len / 8); 60 61 while (data != end) 62 { 63 k = *data++; 64 65 k *= m; 66 k ^= k >> r; 67 k *= m; 68 69 h ^= k; 70 h *= m; 71 } 72 73 data_tail = (const unsigned char *)data; 74 75 switch (len & 7) 76 { 77 case 7: 78 h ^= (uint64_t)(data_tail[6]) << 48; 79 case 6: 80 h ^= (uint64_t)(data_tail[5]) << 40; 81 case 5: 82 h ^= (uint64_t)(data_tail[4]) << 32; 83 case 4: 84 h ^= (uint64_t)(data_tail[3]) << 24; 85 case 3: 86 h ^= (uint64_t)(data_tail[2]) << 16; 87 case 2: 88 h ^= (uint64_t)(data_tail[1]) << 8; 89 case 1: 90 h ^= (uint64_t)(data_tail[0]); 91 h *= m; 92 } 93 94 h ^= h >> r; 95 h *= m; 96 h ^= h >> r; 97 98 return h; 99} 100 101#elif SIZEOF_VOID_P == 4 /* 32bit platforms */ 102 103static uint64_t 104murmur_hash64(const void *key, int len, uint32_t seed) 105{ 106 const int r = 24; 107 const uint32_t m = 0x5bd1e995; 108 109 const uint32_t *data; 110 const unsigned char *data_tail; 111 uint32_t k1; 112 uint32_t h1; 113 uint32_t k2; 114 uint32_t h2; 115 116 uint64_t h; 117 118 h1 = seed ^ len; 119 h2 = 0; 120 121 data = (const uint32_t *)key; 122 123 while (len >= 8) 124 { 125 k1 = *data++; 126 k1 *= m; k1 ^= k1 >> r; k1 *= m; 127 h1 *= m; h1 ^= k1; 128 129 k2 = *data++; 130 k2 *= m; k2 ^= k2 >> r; k2 *= m; 131 h2 *= m; h2 ^= k2; 132 133 len -= 8; 134 } 135 136 if (len >= 4) 137 { 138 k1 = *data++; 139 k1 *= m; k1 ^= k1 >> r; k1 *= m; 140 h1 *= m; h1 ^= k1; 141 len -= 4; 142 } 143 144 data_tail = (const unsigned char *)data; 145 146 switch(len) 147 { 148 case 3: 149 h2 ^= (uint32_t)(data_tail[2]) << 16; 150 case 2: 151 h2 ^= (uint32_t)(data_tail[1]) << 8; 152 case 1: 153 h2 ^= (uint32_t)(data_tail[0]); 154 h2 *= m; 155 }; 156 157 h1 ^= h2 >> 18; h1 *= m; 158 h2 ^= h1 >> 22; h2 *= m; 159 h1 ^= h2 >> 17; h1 *= m; 160 h2 ^= h1 >> 19; h2 *= m; 161 162 h = h1; 163 h = (h << 32) | h2; 164 165 return h; 166} 167 168#else 169# error Platform not supported 170#endif 171 172static void 173sqlext_daap_songalbumid_xfunc(sqlite3_context *pv, int n, sqlite3_value **ppv) 174{ 175 const char *album_artist; 176 const char *album; 177 char *hashbuf; 178 sqlite3_int64 result; 179 180 if (n != 2) 181 { 182 sqlite3_result_error(pv, "daap_songalbumid() requires 2 parameters, album_artist and album", -1); 183 return; 184 } 185 186 if ((sqlite3_value_type(ppv[0]) != SQLITE_TEXT) 187 || (sqlite3_value_type(ppv[1]) != SQLITE_TEXT)) 188 { 189 sqlite3_result_error(pv, "daap_songalbumid() requires 2 text parameters", -1); 190 return; 191 } 192 193 album_artist = (const char *)sqlite3_value_text(ppv[0]); 194 album = (const char *)sqlite3_value_text(ppv[1]); 195 196 hashbuf = sqlite3_mprintf("%s==%s", (album_artist) ? album_artist : "", (album) ? album : ""); 197 if (!hashbuf) 198 { 199 sqlite3_result_error(pv, "daap_songalbumid() out of memory for hashbuf", -1); 200 return; 201 } 202 203 /* Limit hash length to 63 bits, due to signed type in sqlite */ 204 result = murmur_hash64(hashbuf, strlen(hashbuf), 0) >> 1; 205 206 sqlite3_free(hashbuf); 207 208 sqlite3_result_int64(pv, result); 209} 210 211static int 212sqlext_daap_unicode_xcollation(void *notused, int llen, const void *left, int rlen, const void *right) 213{ 214 ucs4_t lch; 215 ucs4_t rch; 216 int lalpha; 217 int ralpha; 218 int rpp; 219 int ret; 220 221 /* Extract first utf-8 character */ 222 ret = u8_mbtoucr(&lch, (const uint8_t *)left, llen); 223 if (ret < 0) 224 return 0; 225 226 ret = u8_mbtoucr(&rch, (const uint8_t *)right, rlen); 227 if (ret < 0) 228 return 0; 229 230 /* Ensure digits and other non-alphanum sort to tail */ 231 lalpha = uc_is_alpha(lch); 232 ralpha = uc_is_alpha(rch); 233 234 if (!lalpha && ralpha) 235 return 1; 236 else if (lalpha && !ralpha) 237 return -1; 238 239 /* Compare case and normalization insensitive */ 240 ret = u8_casecmp((const uint8_t *)left, llen, (const uint8_t*)right, rlen, NULL, UNINORM_NFD, &rpp); 241 if (ret < 0) 242 return 0; 243 244 return rpp; 245} 246 247 248int 249sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) 250{ 251 SQLITE_EXTENSION_INIT2(pApi); 252 int ret; 253 254 ret = sqlite3_create_function(db, "daap_songalbumid", 2, SQLITE_UTF8, NULL, sqlext_daap_songalbumid_xfunc, NULL, NULL); 255 if (ret != SQLITE_OK) 256 { 257 if (pzErrMsg) 258 *pzErrMsg = sqlite3_mprintf("Could not create daap_songalbumid function: %s\n", sqlite3_errmsg(db)); 259 260 return -1; 261 } 262 263 ret = sqlite3_create_collation(db, "DAAP", SQLITE_UTF8, NULL, sqlext_daap_unicode_xcollation); 264 if (ret != SQLITE_OK) 265 { 266 if (pzErrMsg) 267 *pzErrMsg = sqlite3_mprintf("Could not create sqlite3 custom collation DAAP: %s\n", sqlite3_errmsg(db)); 268 269 return -1; 270 } 271 272 return 0; 273} 274