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