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