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