xref: /freebsd/lib/libc/iconv/citrus_db.c (revision db70ff37a051dfa19f6f3f0f0c5e3571aba91982)
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 
52 struct _citrus_db {
53 	struct _region		 db_region;
54 	_citrus_db_hash_func_t	 db_hashfunc;
55 	void			*db_hashfunc_closure;
56 };
57 
58 int
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 
92 void
93 _citrus_db_close(struct _citrus_db *db)
94 {
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 	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 
182 int
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 
193 int
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 
213 int
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 
236 int
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 
259 int
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 
282 int
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 
294 int
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