summaryrefslogtreecommitdiffstats
path: root/src/cdb/cdb_init.c
blob: b962c43fa876b06bcf3e93fd8f37f20760da2127 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* $Id: cdb_init.c,v 1.12 2008-11-06 18:07:04 mjt Exp $
 * cdb_init, cdb_free and cdb_read routines
 *
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
 * Public domain.
 */

#include "cdb.h"

unsigned
cdb_hash(const void *buf, unsigned len)
{
	register const unsigned char *p = (const unsigned char *) buf;
	register const unsigned char *end = p + len;
	register unsigned hash = 5381; /* start value */
	while (p < end)
		hash = (hash + (hash << 5)) ^ *p++;
	return hash;
}

int
cdb_init(struct cdb *cdbp, int fd)
{
	struct stat st;
	unsigned char *mem;
	unsigned fsize, dend;
#ifdef _WIN32
	HANDLE hFile, hMapping;
#endif

	/* get file size */
	if (fstat (fd, &st) < 0)
		return -1;
	/* trivial sanity check: at least toc should be here */
	if (st.st_size < 2048)
		return errno = EPROTO, -1;
	fsize = (unsigned) (st.st_size & 0xffffffffu);
	/* memory-map file */
#ifdef _WIN32
	hFile = (HANDLE) _get_osfhandle(fd);
	if (hFile == (HANDLE) -1)
	return -1;
	hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
	if (!hMapping)
	return -1;
	mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
	CloseHandle(hMapping);
	if (!mem)
	return -1;
#else
	mem = (unsigned char*) mmap (NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
	if (mem == MAP_FAILED)
		return -1;
#endif /* _WIN32 */

	cdbp->cdb_fd = fd;
	cdbp->cdb_fsize = fsize;
	cdbp->cdb_mem = mem;
	cdbp->mtime = st.st_mtime;
	cdbp->check_timer_ev = NULL;
	cdbp->check_timer_tv = NULL;

	cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
	cdbp->cdb_kpos = cdbp->cdb_klen = 0;
	dend = cdb_unpack (mem);
	if (dend < 2048)
		dend = 2048;
	else if (dend >= fsize)
		dend = fsize;
	cdbp->cdb_dend = dend;

	return 0;
}

void
cdb_free(struct cdb *cdbp)
{
	if (cdbp->cdb_mem) {
#ifdef _WIN32
		UnmapViewOfFile((void*) cdbp->cdb_mem);
#else
		munmap ((void*) cdbp->cdb_mem, cdbp->cdb_fsize);
#endif /* _WIN32 */
		cdbp->cdb_mem = NULL;
	}
	cdbp->cdb_fsize = 0;

	if (cdbp->check_timer_ev) {
		evtimer_del (cdbp->check_timer_ev);
		g_free (cdbp->check_timer_ev);
		g_free (cdbp->check_timer_tv);
	}
}

const void *
cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos)
{
	if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) {
		errno = EPROTO;
		return NULL;
	}
	return cdbp->cdb_mem + pos;
}

int
cdb_read(const struct cdb *cdbp, void *buf, unsigned len, unsigned pos)
{
	const void *data = cdb_get (cdbp, len, pos);
	if (!data)
		return -1;
	memcpy (buf, data, len);
	return 0;
}

static void
cdb_timer_callback (int fd, short what, gpointer ud)
{
	struct cdb *cdbp = ud;
	gint nfd;
	struct stat st;

	/* Check cdb file for modifications */
	if (stat (cdbp->filename, &st) != -1) {
		if (st.st_mtime > cdbp->mtime) {
			if ((nfd = open (cdbp->filename, O_RDONLY)) != -1) {
				if (cdbp->cdb_mem) {
#ifdef _WIN32
					UnmapViewOfFile((void*) cdbp->cdb_mem);
#else
					munmap ((void*) cdbp->cdb_mem, cdbp->cdb_fsize);
#endif /* _WIN32 */
					cdbp->cdb_mem = NULL;
				}
				(void)close (cdbp->cdb_fd);
				cdbp->cdb_fsize = 0;
				(void)cdb_init (cdbp, nfd);
			}
		}
	}

	evtimer_add (cdbp->check_timer_ev, cdbp->check_timer_tv);
}

void
cdb_add_timer(struct cdb *cdbp, unsigned seconds)
{
	cdbp->check_timer_ev = g_malloc (sizeof (struct event));
	cdbp->check_timer_tv = g_malloc (sizeof (struct timeval));

	cdbp->check_timer_tv->tv_sec = seconds;
	cdbp->check_timer_tv->tv_usec = 0;

	evtimer_set (cdbp->check_timer_ev, cdb_timer_callback, cdbp);
	evtimer_add (cdbp->check_timer_ev, cdbp->check_timer_tv);
}