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/endian.h> 32 #include <sys/types.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include "citrus_namespace.h" 42 #include "citrus_bcs.h" 43 #include "citrus_region.h" 44 #include "citrus_memstream.h" 45 #include "citrus_mmap.h" 46 #include "citrus_db.h" 47 #include "citrus_db_factory.h" 48 #include "citrus_db_file.h" 49 50 struct _citrus_db { 51 struct _region db_region; 52 _citrus_db_hash_func_t db_hashfunc; 53 void *db_hashfunc_closure; 54 }; 55 56 int 57 _citrus_db_open(struct _citrus_db **rdb, struct _region *r, const char *magic, 58 _citrus_db_hash_func_t hashfunc, void *hashfunc_closure) 59 { 60 struct _citrus_db *db; 61 struct _citrus_db_header_x *dhx; 62 struct _memstream ms; 63 64 _memstream_bind(&ms, r); 65 66 /* sanity check */ 67 dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 68 if (dhx == NULL) 69 return (EFTYPE); 70 if (strncmp(dhx->dhx_magic, magic, _CITRUS_DB_MAGIC_SIZE) != 0) 71 return (EFTYPE); 72 if (_memstream_seek(&ms, be32toh(dhx->dhx_entry_offset), SEEK_SET)) 73 return (EFTYPE); 74 75 if (be32toh(dhx->dhx_num_entries)*_CITRUS_DB_ENTRY_SIZE > 76 _memstream_remainder(&ms)) 77 return (EFTYPE); 78 79 db = malloc(sizeof(*db)); 80 if (db == NULL) 81 return (errno); 82 db->db_region = *r; 83 db->db_hashfunc = hashfunc; 84 db->db_hashfunc_closure = hashfunc_closure; 85 *rdb = db; 86 87 return (0); 88 } 89 90 void 91 _citrus_db_close(struct _citrus_db *db) 92 { 93 94 free(db); 95 } 96 97 int 98 _citrus_db_lookup(struct _citrus_db *db, struct _citrus_region *key, 99 struct _citrus_region *data, struct _citrus_db_locator *dl) 100 { 101 struct _citrus_db_entry_x *dex; 102 struct _citrus_db_header_x *dhx; 103 struct _citrus_region r; 104 struct _memstream ms; 105 uint32_t hashval, num_entries; 106 size_t offset; 107 108 _memstream_bind(&ms, &db->db_region); 109 110 dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 111 num_entries = be32toh(dhx->dhx_num_entries); 112 if (num_entries == 0) 113 return (ENOENT); 114 115 if (dl != NULL && dl->dl_offset>0) { 116 hashval = dl->dl_hashval; 117 offset = dl->dl_offset; 118 if (offset >= _region_size(&db->db_region)) 119 return (ENOENT); 120 } else { 121 hashval = db->db_hashfunc(key)%num_entries; 122 offset = be32toh(dhx->dhx_entry_offset) + 123 hashval * _CITRUS_DB_ENTRY_SIZE; 124 if (dl) 125 dl->dl_hashval = hashval; 126 } 127 do { 128 /* seek to the next entry */ 129 if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 130 return (EFTYPE); 131 /* get the entry record */ 132 dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 133 if (dex == NULL) 134 return (EFTYPE); 135 136 /* jump to next entry having the same hash value. */ 137 offset = be32toh(dex->dex_next_offset); 138 139 /* save the current position */ 140 if (dl) { 141 dl->dl_offset = offset; 142 if (offset == 0) 143 dl->dl_offset = _region_size(&db->db_region); 144 } 145 146 /* compare hash value. */ 147 if (be32toh(dex->dex_hash_value) != hashval) 148 /* not found */ 149 break; 150 /* compare key length */ 151 if (be32toh(dex->dex_key_size) == _region_size(key)) { 152 /* seek to the head of the key. */ 153 if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), 154 SEEK_SET)) 155 return (EFTYPE); 156 /* get the region of the key */ 157 if (_memstream_getregion(&ms, &r, 158 _region_size(key)) == NULL) 159 return (EFTYPE); 160 /* compare key byte stream */ 161 if (memcmp(_region_head(&r), _region_head(key), 162 _region_size(key)) == 0) { 163 /* match */ 164 if (_memstream_seek( 165 &ms, be32toh(dex->dex_data_offset), 166 SEEK_SET)) 167 return (EFTYPE); 168 if (_memstream_getregion( 169 &ms, data, 170 be32toh(dex->dex_data_size)) == NULL) 171 return (EFTYPE); 172 return (0); 173 } 174 } 175 } while (offset != 0); 176 177 return (ENOENT); 178 } 179 180 int 181 _citrus_db_lookup_by_string(struct _citrus_db *db, const char *key, 182 struct _citrus_region *data, struct _citrus_db_locator *dl) 183 { 184 struct _region r; 185 186 _region_init(&r, __DECONST(void *, key), strlen(key)); 187 188 return (_citrus_db_lookup(db, &r, data, dl)); 189 } 190 191 int 192 _citrus_db_lookup8_by_string(struct _citrus_db *db, const char *key, 193 uint8_t *rval, struct _citrus_db_locator *dl) 194 { 195 struct _region r; 196 int ret; 197 198 ret = _citrus_db_lookup_by_string(db, key, &r, dl); 199 if (ret) 200 return (ret); 201 202 if (_region_size(&r) != 1) 203 return (EFTYPE); 204 205 if (rval) 206 memcpy(rval, _region_head(&r), 1); 207 208 return (0); 209 } 210 211 int 212 _citrus_db_lookup16_by_string(struct _citrus_db *db, const char *key, 213 uint16_t *rval, struct _citrus_db_locator *dl) 214 { 215 struct _region r; 216 int ret; 217 uint16_t val; 218 219 ret = _citrus_db_lookup_by_string(db, key, &r, dl); 220 if (ret) 221 return (ret); 222 223 if (_region_size(&r) != 2) 224 return (EFTYPE); 225 226 if (rval) { 227 memcpy(&val, _region_head(&r), 2); 228 *rval = be16toh(val); 229 } 230 231 return (0); 232 } 233 234 int 235 _citrus_db_lookup32_by_string(struct _citrus_db *db, const char *key, 236 uint32_t *rval, struct _citrus_db_locator *dl) 237 { 238 struct _region r; 239 uint32_t val; 240 int ret; 241 242 ret = _citrus_db_lookup_by_string(db, key, &r, dl); 243 if (ret) 244 return (ret); 245 246 if (_region_size(&r) != 4) 247 return (EFTYPE); 248 249 if (rval) { 250 memcpy(&val, _region_head(&r), 4); 251 *rval = be32toh(val); 252 } 253 254 return (0); 255 } 256 257 int 258 _citrus_db_lookup_string_by_string(struct _citrus_db *db, const char *key, 259 const char **rdata, struct _citrus_db_locator *dl) 260 { 261 struct _region r; 262 int ret; 263 264 ret = _citrus_db_lookup_by_string(db, key, &r, dl); 265 if (ret) 266 return (ret); 267 268 /* check whether the string is null terminated */ 269 if (_region_size(&r) == 0) 270 return (EFTYPE); 271 if (*((const char*)_region_head(&r)+_region_size(&r)-1) != '\0') 272 return (EFTYPE); 273 274 if (rdata) 275 *rdata = _region_head(&r); 276 277 return (0); 278 } 279 280 int 281 _citrus_db_get_number_of_entries(struct _citrus_db *db) 282 { 283 struct _citrus_db_header_x *dhx; 284 struct _memstream ms; 285 286 _memstream_bind(&ms, &db->db_region); 287 288 dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 289 return ((int)be32toh(dhx->dhx_num_entries)); 290 } 291 292 int 293 _citrus_db_get_entry(struct _citrus_db *db, int idx, struct _region *key, 294 struct _region *data) 295 { 296 struct _citrus_db_entry_x *dex; 297 struct _citrus_db_header_x *dhx; 298 struct _memstream ms; 299 uint32_t num_entries; 300 size_t offset; 301 302 _memstream_bind(&ms, &db->db_region); 303 304 dhx = _memstream_getregion(&ms, NULL, sizeof(*dhx)); 305 num_entries = be32toh(dhx->dhx_num_entries); 306 if (idx < 0 || (uint32_t)idx >= num_entries) 307 return (EINVAL); 308 309 /* seek to the next entry */ 310 offset = be32toh(dhx->dhx_entry_offset) + idx * _CITRUS_DB_ENTRY_SIZE; 311 if (_citrus_memory_stream_seek(&ms, offset, SEEK_SET)) 312 return (EFTYPE); 313 /* get the entry record */ 314 dex = _memstream_getregion(&ms, NULL, _CITRUS_DB_ENTRY_SIZE); 315 if (dex == NULL) 316 return (EFTYPE); 317 /* seek to the head of the key. */ 318 if (_memstream_seek(&ms, be32toh(dex->dex_key_offset), SEEK_SET)) 319 return (EFTYPE); 320 /* get the region of the key. */ 321 if (_memstream_getregion(&ms, key, be32toh(dex->dex_key_size))==NULL) 322 return (EFTYPE); 323 /* seek to the head of the data. */ 324 if (_memstream_seek(&ms, be32toh(dex->dex_data_offset), SEEK_SET)) 325 return (EFTYPE); 326 /* get the region of the data. */ 327 if (_memstream_getregion(&ms, data, be32toh(dex->dex_data_size))==NULL) 328 return (EFTYPE); 329 330 return (0); 331 } 332