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