summaryrefslogtreecommitdiffstats
path: root/server/cdb_find.c
blob: b7c2fc96c8cb2e62cb43a99f183f0a215908b216 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "cdbpriv.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>

#ifndef SEEK_SET
#define SEEK_SET 0
#endif

RCSID("$Id: cdb_find.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");

static int
cdb_bread(char **ptr, char *endptr, unsigned char *buf, unsigned int len)
{
	if ((*ptr)+len > endptr) {
		errno = EIO;
		return -1;
	}

	memcpy(buf, *ptr, len);
	*ptr += len;
	return 0;
}

static int
match(char **ptr, char *endptr, const unsigned char *key, unsigned int len)
{
	unsigned char buf[32];
	int n;
	int i;

	n = sizeof(buf);
	if (n > len)
		n = len;

	while (len > 0) {
		if (cdb_bread(ptr, endptr, buf, n) == -1)
			return -1;

		for (i = 0; i < n; ++i)
			if (buf[i] != key[i])
				return 0;
		key += n;
		len -= n;
	}

	return 1;
}

int
cdb_find(char *buff, off_t bufflen, const unsigned char *key,
         unsigned int len, char **ret, uint32_t *retlen)
{
	char *cur, *end;
	unsigned char packbuf[8];
	uint32_t pos;
	uint32_t h;
	uint32_t lenhash;
	uint32_t h2;
	uint32_t loop;
	uint32_t poskd;

	cur = buff;
	end = buff + bufflen;

	h = cdb_hash(key, len);

	pos = 8 * (h & 255);
	cur += pos;
	if (cur > end) {
		errno = EIO;
		return -1;
	}

	if (cdb_bread(&cur, end, packbuf, 8) == -1)
		return -1;

	pos = cdb_unpack(packbuf);
	lenhash = cdb_unpack(packbuf + 4);

	if (!lenhash) return 0;
	h2 = (h >> 8) % lenhash;

	for (loop = 0; loop < lenhash; ++loop) {
		cur = buff + (pos + 8 * h2);
		if (cur > end) {
			errno = EIO;
			return -1;
		}
		if (cdb_bread(&cur, end, packbuf, 8) == -1)
			return -1;
		poskd = cdb_unpack(packbuf + 4);
		if (!poskd)
			return 0;

		if (cdb_unpack(packbuf) == h) {
			cur = buff + poskd;
			if (cur > end) {
				errno = EIO;
				return -1;
			}
			if (cdb_bread(&cur, end, packbuf, 8) == -1)
				return -1;
			if (cdb_unpack(packbuf) == len) {
				switch(match(&cur, end, key, len)) {
				case -1:
					return -1;
				case 1:
					*retlen = cdb_unpack(&packbuf[4]);
					*ret = cur;
					return 1;
				}
			}
		}
		if (++h2 == lenhash)
			h2 = 0;
	}

	return 0;
}