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