1 1.5 junyoung /* $NetBSD: citrus_db.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */ 2 1.1 tshiozak 3 1.1 tshiozak /*- 4 1.1 tshiozak * Copyright (c)2003 Citrus Project, 5 1.1 tshiozak * All rights reserved. 6 1.1 tshiozak * 7 1.1 tshiozak * Redistribution and use in source and binary forms, with or without 8 1.1 tshiozak * modification, are permitted provided that the following conditions 9 1.1 tshiozak * are met: 10 1.1 tshiozak * 1. Redistributions of source code must retain the above copyright 11 1.1 tshiozak * notice, this list of conditions and the following disclaimer. 12 1.1 tshiozak * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 tshiozak * notice, this list of conditions and the following disclaimer in the 14 1.1 tshiozak * documentation and/or other materials provided with the distribution. 15 1.1 tshiozak * 16 1.1 tshiozak * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 tshiozak * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 tshiozak * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 tshiozak * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 tshiozak * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 tshiozak * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 tshiozak * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 tshiozak * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 tshiozak * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 tshiozak * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 tshiozak * SUCH DAMAGE. 27 1.1 tshiozak */ 28 1.1 tshiozak 29 1.1 tshiozak #include <sys/cdefs.h> 30 1.1 tshiozak #if defined(LIBC_SCCS) && !defined(lint) 31 1.5 junyoung __RCSID("$NetBSD: citrus_db.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $"); 32 1.1 tshiozak #endif /* LIBC_SCCS and not lint */ 33 1.1 tshiozak 34 1.1 tshiozak #include "namespace.h" 35 1.1 tshiozak #include <assert.h> 36 1.1 tshiozak #include <stdio.h> 37 1.1 tshiozak #include <stdlib.h> 38 1.1 tshiozak #include <string.h> 39 1.1 tshiozak #include <errno.h> 40 1.1 tshiozak #include <limits.h> 41 1.1 tshiozak #include <sys/types.h> 42 1.1 tshiozak 43 1.1 tshiozak #include "citrus_namespace.h" 44 1.1 tshiozak #include "citrus_bcs.h" 45 1.1 tshiozak #include "citrus_region.h" 46 1.1 tshiozak #include "citrus_memstream.h" 47 1.1 tshiozak #include "citrus_mmap.h" 48 1.1 tshiozak #include "citrus_db.h" 49 1.1 tshiozak #include "citrus_db_file.h" 50 1.1 tshiozak 51 1.1 tshiozak struct _citrus_db { 52 1.1 tshiozak /* private */ 53 1.1 tshiozak struct _region db_region; 54 1.5 junyoung uint32_t (*db_hashfunc)(void *, struct _citrus_region *); 55 1.1 tshiozak void *db_hashfunc_closure; 56 1.1 tshiozak }; 57 1.1 tshiozak 58 1.1 tshiozak int 59 1.1 tshiozak _citrus_db_open(struct _citrus_db **rdb, struct _region *r, const char *magic, 60 1.5 junyoung uint32_t (*hashfunc)(void *, struct _citrus_region *), 61 1.1 tshiozak void *hashfunc_closure) 62 1.1 tshiozak { 63 1.1 tshiozak struct _memstream ms; 64 1.1 tshiozak struct _citrus_db *db; 65 1.1 tshiozak struct _citrus_db_header_x *dhx; 66 1.1 tshiozak 67 1.1 tshiozak _memstream_bind(&ms, r); 68 1.1 tshiozak 69 1.1 tshiozak /* sanity check */ 70 1.1 tshiozak dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 71 1.1 tshiozak if (dhx == NULL) 72 1.1 tshiozak return EFTYPE; 73 1.1 tshiozak if (strncmp(dhx->dhx_magic, magic, _CITRUS_DB_MAGIC_SIZE) != 0) 74 1.1 tshiozak return EFTYPE; 75 1.1 tshiozak if (_memstream_seek(&ms, be32toh(dhx->dhx_entry_offset), SEEK_SET)) 76 1.1 tshiozak return EFTYPE; 77 1.1 tshiozak 78 1.1 tshiozak if (be32toh(dhx->dhx_num_entries)*_CITRUS_DB_ENTRY_SIZE > 79 1.1 tshiozak _memstream_remainder(&ms)) 80 1.1 tshiozak return EFTYPE; 81 1.1 tshiozak 82 1.1 tshiozak db = malloc(sizeof(*db)); 83 1.1 tshiozak if (db==NULL) 84 1.1 tshiozak return errno; 85 1.1 tshiozak db->db_region = *r; 86 1.1 tshiozak db->db_hashfunc = hashfunc; 87 1.1 tshiozak db->db_hashfunc_closure = hashfunc_closure; 88 1.1 tshiozak *rdb = db; 89 1.1 tshiozak 90 1.1 tshiozak return 0; 91 1.1 tshiozak } 92 1.1 tshiozak 93 1.1 tshiozak void 94 1.1 tshiozak _citrus_db_close(struct _citrus_db *db) 95 1.1 tshiozak { 96 1.1 tshiozak free(db); 97 1.1 tshiozak } 98 1.1 tshiozak 99 1.1 tshiozak int 100 1.1 tshiozak _citrus_db_lookup(struct _citrus_db *db, struct _citrus_region *key, 101 1.1 tshiozak struct _citrus_region *data, struct _citrus_db_locator *dl) 102 1.1 tshiozak { 103 1.5 junyoung uint32_t hashval, num_entries; 104 1.1 tshiozak size_t offset; 105 1.1 tshiozak struct _memstream ms; 106 1.1 tshiozak struct _citrus_db_header_x *dhx; 107 1.1 tshiozak struct _citrus_db_entry_x *dex; 108 1.1 tshiozak struct _citrus_region r; 109 1.1 tshiozak 110 1.1 tshiozak _memstream_bind(&ms, &db->db_region); 111 1.1 tshiozak 112 1.1 tshiozak dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 113 1.1 tshiozak _DIAGASSERT(dhx); 114 1.1 tshiozak num_entries = be32toh(dhx->dhx_num_entries); 115 1.1 tshiozak if (num_entries == 0) 116 1.1 tshiozak return ENOENT; 117 1.1 tshiozak 118 1.1 tshiozak if (dl != NULL && dl->dl_offset>0) { 119 1.1 tshiozak hashval = dl->dl_hashval; 120 1.1 tshiozak offset = dl->dl_offset; 121 1.1 tshiozak if (offset >= _region_size(&db->db_region)) 122 1.1 tshiozak return ENOENT; 123 1.1 tshiozak } else { 124 1.1 tshiozak hashval = 125 1.1 tshiozak db->db_hashfunc(db->db_hashfunc_closure, key)%num_entries; 126 1.1 tshiozak offset = 127 1.1 tshiozak be32toh(dhx->dhx_entry_offset) + 128 1.1 tshiozak hashval * _CITRUS_DB_ENTRY_SIZE; 129 1.1 tshiozak if (dl) 130 1.1 tshiozak dl->dl_hashval = hashval; 131 1.1 tshiozak } 132 1.1 tshiozak do { 133 1.1 tshiozak /* seek to the next entry */ 134 1.1 tshiozak if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 135 1.1 tshiozak return EFTYPE; 136 1.1 tshiozak /* get the entry record */ 137 1.1 tshiozak dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 138 1.1 tshiozak if (dex == NULL) 139 1.1 tshiozak return EFTYPE; 140 1.1 tshiozak 141 1.1 tshiozak /* jump to next entry having the same hash value. */ 142 1.1 tshiozak offset = be32toh(dex->dex_next_offset); 143 1.1 tshiozak 144 1.1 tshiozak /* save the current position */ 145 1.1 tshiozak if (dl) { 146 1.1 tshiozak dl->dl_offset = offset; 147 1.1 tshiozak if (offset==0) 148 1.1 tshiozak dl->dl_offset = _region_size(&db->db_region); 149 1.1 tshiozak } 150 1.1 tshiozak 151 1.1 tshiozak /* compare hash value. */ 152 1.1 tshiozak if (be32toh(dex->dex_hash_value) != hashval) 153 1.1 tshiozak /* not found */ 154 1.1 tshiozak break; 155 1.1 tshiozak /* compare key length */ 156 1.1 tshiozak if (be32toh(dex->dex_key_size) == _region_size(key)) { 157 1.1 tshiozak /* seek to the head of the key. */ 158 1.1 tshiozak if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), 159 1.1 tshiozak SEEK_SET)) 160 1.1 tshiozak return EFTYPE; 161 1.1 tshiozak /* get the region of the key */ 162 1.1 tshiozak if (_memstream_getregion(&ms, &r, 163 1.1 tshiozak _region_size(key)) == NULL) 164 1.1 tshiozak return EFTYPE; 165 1.1 tshiozak /* compare key byte stream */ 166 1.1 tshiozak if (memcmp(_region_head(&r), _region_head(key), 167 1.1 tshiozak _region_size(key)) == 0) { 168 1.1 tshiozak /* match */ 169 1.1 tshiozak if (_memstream_seek( 170 1.1 tshiozak &ms, be32toh(dex->dex_data_offset), 171 1.1 tshiozak SEEK_SET)) 172 1.1 tshiozak return EFTYPE; 173 1.1 tshiozak if (_memstream_getregion( 174 1.1 tshiozak &ms, data, 175 1.1 tshiozak be32toh(dex->dex_data_size)) == NULL) 176 1.1 tshiozak return EFTYPE; 177 1.1 tshiozak return 0; 178 1.1 tshiozak } 179 1.1 tshiozak } 180 1.1 tshiozak } while (offset != 0); 181 1.1 tshiozak 182 1.1 tshiozak return ENOENT; 183 1.1 tshiozak } 184 1.1 tshiozak 185 1.1 tshiozak int 186 1.1 tshiozak _citrus_db_lookup_by_string(struct _citrus_db *db, const char *key, 187 1.1 tshiozak struct _citrus_region *data, 188 1.1 tshiozak struct _citrus_db_locator *dl) 189 1.1 tshiozak { 190 1.1 tshiozak struct _region r; 191 1.1 tshiozak 192 1.4 christos _region_init(&r, __UNCONST(key), strlen(key)); 193 1.1 tshiozak 194 1.1 tshiozak return _citrus_db_lookup(db, &r, data, dl); 195 1.1 tshiozak } 196 1.1 tshiozak 197 1.1 tshiozak int 198 1.1 tshiozak _citrus_db_lookup8_by_string(struct _citrus_db *db, const char *key, 199 1.5 junyoung uint8_t *rval, struct _citrus_db_locator *dl) 200 1.1 tshiozak { 201 1.1 tshiozak int ret; 202 1.1 tshiozak struct _region r; 203 1.1 tshiozak 204 1.1 tshiozak ret = _citrus_db_lookup_by_string(db, key, &r, dl); 205 1.1 tshiozak if (ret) 206 1.1 tshiozak return ret; 207 1.1 tshiozak 208 1.1 tshiozak if (_region_size(&r) != 1) 209 1.1 tshiozak return EFTYPE; 210 1.1 tshiozak 211 1.1 tshiozak if (rval) 212 1.1 tshiozak memcpy(rval, _region_head(&r), 1); 213 1.1 tshiozak 214 1.1 tshiozak return 0; 215 1.1 tshiozak } 216 1.1 tshiozak 217 1.1 tshiozak int 218 1.1 tshiozak _citrus_db_lookup16_by_string(struct _citrus_db *db, const char *key, 219 1.5 junyoung uint16_t *rval, struct _citrus_db_locator *dl) 220 1.1 tshiozak { 221 1.1 tshiozak int ret; 222 1.1 tshiozak struct _region r; 223 1.5 junyoung uint16_t val; 224 1.1 tshiozak 225 1.1 tshiozak ret = _citrus_db_lookup_by_string(db, key, &r, dl); 226 1.1 tshiozak if (ret) 227 1.1 tshiozak return ret; 228 1.1 tshiozak 229 1.1 tshiozak if (_region_size(&r) != 2) 230 1.1 tshiozak return EFTYPE; 231 1.1 tshiozak 232 1.1 tshiozak if (rval) { 233 1.1 tshiozak memcpy(&val, _region_head(&r), 2); 234 1.1 tshiozak *rval = be16toh(val); 235 1.1 tshiozak } 236 1.1 tshiozak 237 1.1 tshiozak return 0; 238 1.1 tshiozak } 239 1.1 tshiozak 240 1.1 tshiozak int 241 1.1 tshiozak _citrus_db_lookup32_by_string(struct _citrus_db *db, const char *key, 242 1.5 junyoung uint32_t *rval, struct _citrus_db_locator *dl) 243 1.1 tshiozak { 244 1.1 tshiozak int ret; 245 1.1 tshiozak struct _region r; 246 1.5 junyoung uint32_t val; 247 1.1 tshiozak 248 1.1 tshiozak ret = _citrus_db_lookup_by_string(db, key, &r, dl); 249 1.1 tshiozak if (ret) 250 1.1 tshiozak return ret; 251 1.1 tshiozak 252 1.1 tshiozak if (_region_size(&r) != 4) 253 1.1 tshiozak return EFTYPE; 254 1.1 tshiozak 255 1.1 tshiozak if (rval) { 256 1.1 tshiozak memcpy(&val, _region_head(&r), 4); 257 1.1 tshiozak *rval = be32toh(val); 258 1.1 tshiozak } 259 1.1 tshiozak 260 1.1 tshiozak return 0; 261 1.1 tshiozak } 262 1.1 tshiozak 263 1.1 tshiozak int 264 1.1 tshiozak _citrus_db_lookup_string_by_string(struct _citrus_db *db, const char *key, 265 1.1 tshiozak const char **rdata, 266 1.1 tshiozak struct _citrus_db_locator *dl) 267 1.1 tshiozak { 268 1.1 tshiozak int ret; 269 1.1 tshiozak struct _region r; 270 1.1 tshiozak 271 1.1 tshiozak ret = _citrus_db_lookup_by_string(db, key, &r, dl); 272 1.1 tshiozak if (ret) 273 1.1 tshiozak return ret; 274 1.1 tshiozak 275 1.2 tshiozak /* check whether the string is null terminated */ 276 1.1 tshiozak if (_region_size(&r) == 0) 277 1.1 tshiozak return EFTYPE; 278 1.2 tshiozak if (*((const char*)_region_head(&r)+_region_size(&r)-1) != '\0') 279 1.1 tshiozak return EFTYPE; 280 1.1 tshiozak 281 1.1 tshiozak if (rdata) 282 1.1 tshiozak *rdata = _region_head(&r); 283 1.1 tshiozak 284 1.1 tshiozak return 0; 285 1.1 tshiozak } 286 1.1 tshiozak 287 1.1 tshiozak int 288 1.1 tshiozak _citrus_db_get_number_of_entries(struct _citrus_db *db) 289 1.1 tshiozak { 290 1.1 tshiozak struct _memstream ms; 291 1.1 tshiozak struct _citrus_db_header_x *dhx; 292 1.1 tshiozak 293 1.1 tshiozak _memstream_bind(&ms, &db->db_region); 294 1.1 tshiozak 295 1.1 tshiozak dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 296 1.1 tshiozak _DIAGASSERT(dhx); 297 1.1 tshiozak return (int)be32toh(dhx->dhx_num_entries); 298 1.1 tshiozak } 299 1.1 tshiozak 300 1.1 tshiozak int 301 1.1 tshiozak _citrus_db_get_entry(struct _citrus_db *db, int idx, 302 1.1 tshiozak struct _region *key, struct _region *data) 303 1.1 tshiozak { 304 1.5 junyoung uint32_t num_entries; 305 1.1 tshiozak size_t offset; 306 1.1 tshiozak struct _memstream ms; 307 1.1 tshiozak struct _citrus_db_header_x *dhx; 308 1.1 tshiozak struct _citrus_db_entry_x *dex; 309 1.1 tshiozak 310 1.1 tshiozak _memstream_bind(&ms, &db->db_region); 311 1.1 tshiozak 312 1.1 tshiozak dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 313 1.1 tshiozak _DIAGASSERT(dhx); 314 1.1 tshiozak num_entries = be32toh(dhx->dhx_num_entries); 315 1.5 junyoung if (idx < 0 || (uint32_t)idx >= num_entries) 316 1.1 tshiozak return EINVAL; 317 1.1 tshiozak 318 1.1 tshiozak /* seek to the next entry */ 319 1.1 tshiozak offset = be32toh(dhx->dhx_entry_offset) + idx * _CITRUS_DB_ENTRY_SIZE; 320 1.1 tshiozak if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 321 1.1 tshiozak return EFTYPE; 322 1.1 tshiozak /* get the entry record */ 323 1.1 tshiozak dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 324 1.1 tshiozak if (dex == NULL) 325 1.1 tshiozak return EFTYPE; 326 1.1 tshiozak /* seek to the head of the key. */ 327 1.1 tshiozak if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), SEEK_SET)) 328 1.1 tshiozak return EFTYPE; 329 1.1 tshiozak /* get the region of the key. */ 330 1.1 tshiozak if (_memstream_getregion(&ms, key, be32toh(dex->dex_key_size))==NULL) 331 1.1 tshiozak return EFTYPE; 332 1.1 tshiozak /* seek to the head of the data. */ 333 1.1 tshiozak if (_memstream_seek(&ms, be32toh(dex->dex_data_offset), SEEK_SET)) 334 1.1 tshiozak return EFTYPE; 335 1.1 tshiozak /* get the region of the data. */ 336 1.1 tshiozak if (_memstream_getregion(&ms, data, be32toh(dex->dex_data_size))==NULL) 337 1.1 tshiozak return EFTYPE; 338 1.1 tshiozak 339 1.1 tshiozak return 0; 340 1.1 tshiozak } 341