1219019Sgabor/* $FreeBSD: releng/10.3/lib/libc/iconv/citrus_db.c 219019 2011-02-25 00:04:39Z gabor $ */ 2219019Sgabor/* $NetBSD: citrus_db.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */ 3219019Sgabor 4219019Sgabor/*- 5219019Sgabor * Copyright (c)2003 Citrus Project, 6219019Sgabor * All rights reserved. 7219019Sgabor * 8219019Sgabor * Redistribution and use in source and binary forms, with or without 9219019Sgabor * modification, are permitted provided that the following conditions 10219019Sgabor * are met: 11219019Sgabor * 1. Redistributions of source code must retain the above copyright 12219019Sgabor * notice, this list of conditions and the following disclaimer. 13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 14219019Sgabor * notice, this list of conditions and the following disclaimer in the 15219019Sgabor * documentation and/or other materials provided with the distribution. 16219019Sgabor * 17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219019Sgabor * SUCH DAMAGE. 28219019Sgabor */ 29219019Sgabor 30219019Sgabor#include <sys/cdefs.h> 31219019Sgabor#include <sys/endian.h> 32219019Sgabor#include <sys/types.h> 33219019Sgabor 34219019Sgabor#include <assert.h> 35219019Sgabor#include <errno.h> 36219019Sgabor#include <limits.h> 37219019Sgabor#include <stdio.h> 38219019Sgabor#include <stdlib.h> 39219019Sgabor#include <string.h> 40219019Sgabor 41219019Sgabor#include "citrus_namespace.h" 42219019Sgabor#include "citrus_bcs.h" 43219019Sgabor#include "citrus_region.h" 44219019Sgabor#include "citrus_memstream.h" 45219019Sgabor#include "citrus_mmap.h" 46219019Sgabor#include "citrus_db.h" 47219019Sgabor#include "citrus_db_factory.h" 48219019Sgabor#include "citrus_db_file.h" 49219019Sgabor 50219019Sgaborstruct _citrus_db { 51219019Sgabor struct _region db_region; 52219019Sgabor _citrus_db_hash_func_t db_hashfunc; 53219019Sgabor void *db_hashfunc_closure; 54219019Sgabor}; 55219019Sgabor 56219019Sgaborint 57219019Sgabor_citrus_db_open(struct _citrus_db **rdb, struct _region *r, const char *magic, 58219019Sgabor _citrus_db_hash_func_t hashfunc, void *hashfunc_closure) 59219019Sgabor{ 60219019Sgabor struct _citrus_db *db; 61219019Sgabor struct _citrus_db_header_x *dhx; 62219019Sgabor struct _memstream ms; 63219019Sgabor 64219019Sgabor _memstream_bind(&ms, r); 65219019Sgabor 66219019Sgabor /* sanity check */ 67219019Sgabor dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 68219019Sgabor if (dhx == NULL) 69219019Sgabor return (EFTYPE); 70219019Sgabor if (strncmp(dhx->dhx_magic, magic, _CITRUS_DB_MAGIC_SIZE) != 0) 71219019Sgabor return (EFTYPE); 72219019Sgabor if (_memstream_seek(&ms, be32toh(dhx->dhx_entry_offset), SEEK_SET)) 73219019Sgabor return (EFTYPE); 74219019Sgabor 75219019Sgabor if (be32toh(dhx->dhx_num_entries)*_CITRUS_DB_ENTRY_SIZE > 76219019Sgabor _memstream_remainder(&ms)) 77219019Sgabor return (EFTYPE); 78219019Sgabor 79219019Sgabor db = malloc(sizeof(*db)); 80219019Sgabor if (db == NULL) 81219019Sgabor return (errno); 82219019Sgabor db->db_region = *r; 83219019Sgabor db->db_hashfunc = hashfunc; 84219019Sgabor db->db_hashfunc_closure = hashfunc_closure; 85219019Sgabor *rdb = db; 86219019Sgabor 87219019Sgabor return (0); 88219019Sgabor} 89219019Sgabor 90219019Sgaborvoid 91219019Sgabor_citrus_db_close(struct _citrus_db *db) 92219019Sgabor{ 93219019Sgabor 94219019Sgabor free(db); 95219019Sgabor} 96219019Sgabor 97219019Sgaborint 98219019Sgabor_citrus_db_lookup(struct _citrus_db *db, struct _citrus_region *key, 99219019Sgabor struct _citrus_region *data, struct _citrus_db_locator *dl) 100219019Sgabor{ 101219019Sgabor struct _citrus_db_entry_x *dex; 102219019Sgabor struct _citrus_db_header_x *dhx; 103219019Sgabor struct _citrus_region r; 104219019Sgabor struct _memstream ms; 105219019Sgabor uint32_t hashval, num_entries; 106219019Sgabor size_t offset; 107219019Sgabor 108219019Sgabor _memstream_bind(&ms, &db->db_region); 109219019Sgabor 110219019Sgabor dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 111219019Sgabor num_entries = be32toh(dhx->dhx_num_entries); 112219019Sgabor if (num_entries == 0) 113219019Sgabor return (ENOENT); 114219019Sgabor 115219019Sgabor if (dl != NULL && dl->dl_offset>0) { 116219019Sgabor hashval = dl->dl_hashval; 117219019Sgabor offset = dl->dl_offset; 118219019Sgabor if (offset >= _region_size(&db->db_region)) 119219019Sgabor return (ENOENT); 120219019Sgabor } else { 121219019Sgabor hashval = db->db_hashfunc(key)%num_entries; 122219019Sgabor offset = be32toh(dhx->dhx_entry_offset) + 123219019Sgabor hashval * _CITRUS_DB_ENTRY_SIZE; 124219019Sgabor if (dl) 125219019Sgabor dl->dl_hashval = hashval; 126219019Sgabor } 127219019Sgabor do { 128219019Sgabor /* seek to the next entry */ 129219019Sgabor if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 130219019Sgabor return (EFTYPE); 131219019Sgabor /* get the entry record */ 132219019Sgabor dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 133219019Sgabor if (dex == NULL) 134219019Sgabor return (EFTYPE); 135219019Sgabor 136219019Sgabor /* jump to next entry having the same hash value. */ 137219019Sgabor offset = be32toh(dex->dex_next_offset); 138219019Sgabor 139219019Sgabor /* save the current position */ 140219019Sgabor if (dl) { 141219019Sgabor dl->dl_offset = offset; 142219019Sgabor if (offset == 0) 143219019Sgabor dl->dl_offset = _region_size(&db->db_region); 144219019Sgabor } 145219019Sgabor 146219019Sgabor /* compare hash value. */ 147219019Sgabor if (be32toh(dex->dex_hash_value) != hashval) 148219019Sgabor /* not found */ 149219019Sgabor break; 150219019Sgabor /* compare key length */ 151219019Sgabor if (be32toh(dex->dex_key_size) == _region_size(key)) { 152219019Sgabor /* seek to the head of the key. */ 153219019Sgabor if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), 154219019Sgabor SEEK_SET)) 155219019Sgabor return (EFTYPE); 156219019Sgabor /* get the region of the key */ 157219019Sgabor if (_memstream_getregion(&ms, &r, 158219019Sgabor _region_size(key)) == NULL) 159219019Sgabor return (EFTYPE); 160219019Sgabor /* compare key byte stream */ 161219019Sgabor if (memcmp(_region_head(&r), _region_head(key), 162219019Sgabor _region_size(key)) == 0) { 163219019Sgabor /* match */ 164219019Sgabor if (_memstream_seek( 165219019Sgabor &ms, be32toh(dex->dex_data_offset), 166219019Sgabor SEEK_SET)) 167219019Sgabor return (EFTYPE); 168219019Sgabor if (_memstream_getregion( 169219019Sgabor &ms, data, 170219019Sgabor be32toh(dex->dex_data_size)) == NULL) 171219019Sgabor return (EFTYPE); 172219019Sgabor return (0); 173219019Sgabor } 174219019Sgabor } 175219019Sgabor } while (offset != 0); 176219019Sgabor 177219019Sgabor return (ENOENT); 178219019Sgabor} 179219019Sgabor 180219019Sgaborint 181219019Sgabor_citrus_db_lookup_by_string(struct _citrus_db *db, const char *key, 182219019Sgabor struct _citrus_region *data, struct _citrus_db_locator *dl) 183219019Sgabor{ 184219019Sgabor struct _region r; 185219019Sgabor 186219019Sgabor _region_init(&r, __DECONST(void *, key), strlen(key)); 187219019Sgabor 188219019Sgabor return (_citrus_db_lookup(db, &r, data, dl)); 189219019Sgabor} 190219019Sgabor 191219019Sgaborint 192219019Sgabor_citrus_db_lookup8_by_string(struct _citrus_db *db, const char *key, 193219019Sgabor uint8_t *rval, struct _citrus_db_locator *dl) 194219019Sgabor{ 195219019Sgabor struct _region r; 196219019Sgabor int ret; 197219019Sgabor 198219019Sgabor ret = _citrus_db_lookup_by_string(db, key, &r, dl); 199219019Sgabor if (ret) 200219019Sgabor return (ret); 201219019Sgabor 202219019Sgabor if (_region_size(&r) != 1) 203219019Sgabor return (EFTYPE); 204219019Sgabor 205219019Sgabor if (rval) 206219019Sgabor memcpy(rval, _region_head(&r), 1); 207219019Sgabor 208219019Sgabor return (0); 209219019Sgabor} 210219019Sgabor 211219019Sgaborint 212219019Sgabor_citrus_db_lookup16_by_string(struct _citrus_db *db, const char *key, 213219019Sgabor uint16_t *rval, struct _citrus_db_locator *dl) 214219019Sgabor{ 215219019Sgabor struct _region r; 216219019Sgabor int ret; 217219019Sgabor uint16_t val; 218219019Sgabor 219219019Sgabor ret = _citrus_db_lookup_by_string(db, key, &r, dl); 220219019Sgabor if (ret) 221219019Sgabor return (ret); 222219019Sgabor 223219019Sgabor if (_region_size(&r) != 2) 224219019Sgabor return (EFTYPE); 225219019Sgabor 226219019Sgabor if (rval) { 227219019Sgabor memcpy(&val, _region_head(&r), 2); 228219019Sgabor *rval = be16toh(val); 229219019Sgabor } 230219019Sgabor 231219019Sgabor return (0); 232219019Sgabor} 233219019Sgabor 234219019Sgaborint 235219019Sgabor_citrus_db_lookup32_by_string(struct _citrus_db *db, const char *key, 236219019Sgabor uint32_t *rval, struct _citrus_db_locator *dl) 237219019Sgabor{ 238219019Sgabor struct _region r; 239219019Sgabor uint32_t val; 240219019Sgabor int ret; 241219019Sgabor 242219019Sgabor ret = _citrus_db_lookup_by_string(db, key, &r, dl); 243219019Sgabor if (ret) 244219019Sgabor return (ret); 245219019Sgabor 246219019Sgabor if (_region_size(&r) != 4) 247219019Sgabor return (EFTYPE); 248219019Sgabor 249219019Sgabor if (rval) { 250219019Sgabor memcpy(&val, _region_head(&r), 4); 251219019Sgabor *rval = be32toh(val); 252219019Sgabor } 253219019Sgabor 254219019Sgabor return (0); 255219019Sgabor} 256219019Sgabor 257219019Sgaborint 258219019Sgabor_citrus_db_lookup_string_by_string(struct _citrus_db *db, const char *key, 259219019Sgabor const char **rdata, struct _citrus_db_locator *dl) 260219019Sgabor{ 261219019Sgabor struct _region r; 262219019Sgabor int ret; 263219019Sgabor 264219019Sgabor ret = _citrus_db_lookup_by_string(db, key, &r, dl); 265219019Sgabor if (ret) 266219019Sgabor return (ret); 267219019Sgabor 268219019Sgabor /* check whether the string is null terminated */ 269219019Sgabor if (_region_size(&r) == 0) 270219019Sgabor return (EFTYPE); 271219019Sgabor if (*((const char*)_region_head(&r)+_region_size(&r)-1) != '\0') 272219019Sgabor return (EFTYPE); 273219019Sgabor 274219019Sgabor if (rdata) 275219019Sgabor *rdata = _region_head(&r); 276219019Sgabor 277219019Sgabor return (0); 278219019Sgabor} 279219019Sgabor 280219019Sgaborint 281219019Sgabor_citrus_db_get_number_of_entries(struct _citrus_db *db) 282219019Sgabor{ 283219019Sgabor struct _citrus_db_header_x *dhx; 284219019Sgabor struct _memstream ms; 285219019Sgabor 286219019Sgabor _memstream_bind(&ms, &db->db_region); 287219019Sgabor 288219019Sgabor dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 289219019Sgabor return ((int)be32toh(dhx->dhx_num_entries)); 290219019Sgabor} 291219019Sgabor 292219019Sgaborint 293219019Sgabor_citrus_db_get_entry(struct _citrus_db *db, int idx, struct _region *key, 294219019Sgabor struct _region *data) 295219019Sgabor{ 296219019Sgabor struct _citrus_db_entry_x *dex; 297219019Sgabor struct _citrus_db_header_x *dhx; 298219019Sgabor struct _memstream ms; 299219019Sgabor uint32_t num_entries; 300219019Sgabor size_t offset; 301219019Sgabor 302219019Sgabor _memstream_bind(&ms, &db->db_region); 303219019Sgabor 304219019Sgabor dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 305219019Sgabor num_entries = be32toh(dhx->dhx_num_entries); 306219019Sgabor if (idx < 0 || (uint32_t)idx >= num_entries) 307219019Sgabor return (EINVAL); 308219019Sgabor 309219019Sgabor /* seek to the next entry */ 310219019Sgabor offset = be32toh(dhx->dhx_entry_offset) + idx * _CITRUS_DB_ENTRY_SIZE; 311219019Sgabor if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 312219019Sgabor return (EFTYPE); 313219019Sgabor /* get the entry record */ 314219019Sgabor dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 315219019Sgabor if (dex == NULL) 316219019Sgabor return (EFTYPE); 317219019Sgabor /* seek to the head of the key. */ 318219019Sgabor if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), SEEK_SET)) 319219019Sgabor return (EFTYPE); 320219019Sgabor /* get the region of the key. */ 321219019Sgabor if (_memstream_getregion(&ms, key, be32toh(dex->dex_key_size))==NULL) 322219019Sgabor return (EFTYPE); 323219019Sgabor /* seek to the head of the data. */ 324219019Sgabor if (_memstream_seek(&ms, be32toh(dex->dex_data_offset), SEEK_SET)) 325219019Sgabor return (EFTYPE); 326219019Sgabor /* get the region of the data. */ 327219019Sgabor if (_memstream_getregion(&ms, data, be32toh(dex->dex_data_size))==NULL) 328219019Sgabor return (EFTYPE); 329219019Sgabor 330219019Sgabor return (0); 331219019Sgabor} 332