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