2 * Copyright 2004-2005 Timo Hirvonen
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
26 #include <sys/types.h>
31 #include <netinet/in.h>
40 /* always sorted by key */
41 struct db_entry
*entries
;
42 unsigned int nr_entries
;
43 unsigned int nr_allocated
;
45 /* insert queue, not sorted */
46 struct db_entry
*iq_entries
;
56 unsigned int index_dirty
: 1;
59 static int db_entry_cmp(const void *a
, const void *b
)
61 const struct db_entry
*ae
= a
;
62 const struct db_entry
*be
= b
;
64 return strcmp(ae
->key
, be
->key
);
67 static void array_remove(void *array
, size_t nmemb
, size_t size
, int idx
)
75 c
= size
* (nmemb
- idx
- 1);
82 static uint32_t nbuf2l(const char *buffer
)
84 const unsigned char *buf
= (const unsigned char *)buffer
;
94 static int index_load(struct db
*db
)
96 int fd
, size
, pos
, rc
, i
;
99 fd
= open(db
->idx_fn
, O_RDONLY
);
105 size
= lseek(fd
, 0, SEEK_END
);
108 if (lseek(fd
, 0, SEEK_SET
) == -1)
112 buf
= xnew(char, size
);
113 rc
= read_all(fd
, buf
, size
);
122 /* format: nr_rows, nr_rows * (pos, size, key_size, key) */
124 db
->nr_entries
= nbuf2l((char *)(buf
+ pos
)); pos
+= 4;
125 db
->entries
= xnew(struct db_entry
, db
->nr_entries
);
126 db
->nr_allocated
= db
->nr_entries
;
127 for (i
= 0; i
< db
->nr_entries
; i
++) {
128 struct db_entry
*e
= &db
->entries
[i
];
131 if (size
- pos
< 3 * 4)
133 e
->data_pos
= nbuf2l(buf
+ pos
); pos
+= 4;
134 e
->data_size
= nbuf2l(buf
+ pos
); pos
+= 4;
135 key_size
= nbuf2l(buf
+ pos
); pos
+= 4;
136 if (size
- pos
< key_size
)
138 e
->key
= xmalloc(key_size
);
139 memcpy(e
->key
, buf
+ pos
, key_size
);
149 db
->nr_allocated
= 0;
153 static int index_save(struct db
*db
)
158 fd
= creat(db
->idx_fn
, 0666);
161 data
= htonl(db
->nr_entries
);
162 if (write_all(fd
, &data
, 4) != 4)
164 for (i
= 0; i
< db
->nr_entries
; i
++) {
165 uint32_t key_size
= strlen(db
->entries
[i
].key
) + 1;
167 data
= htonl(db
->entries
[i
].data_pos
);
168 if (write_all(fd
, &data
, 4) != 4)
170 data
= htonl(db
->entries
[i
].data_size
);
171 if (write_all(fd
, &data
, 4) != 4)
173 data
= htonl(key_size
);
174 if (write_all(fd
, &data
, 4) != 4)
176 if (write_all(fd
, db
->entries
[i
].key
, key_size
) != key_size
)
187 static void index_free(struct db
*db
)
191 for (i
= 0; i
< db
->nr_entries
; i
++)
192 free(db
->entries
[i
].key
);
196 db
->nr_allocated
= 0;
199 static struct db_entry
*index_search(struct db
*db
, const char *key
)
204 return bsearch(&k
, db
->entries
, db
->nr_entries
, sizeof(struct db_entry
), db_entry_cmp
);
207 static int index_remove(struct db
*db
, const char *key
)
211 e
= index_search(db
, key
);
217 array_remove(db
->entries
, db
->nr_entries
, sizeof(struct db_entry
), e
- db
->entries
);
227 static int iq_flush(struct db
*db
)
232 pos
= lseek(db
->dat_fd
, 0, SEEK_END
);
235 for (i
= 0; i
< db
->iq_fill
; i
++) {
236 rc
= write_all(db
->dat_fd
, db
->iq_datas
[i
], db
->iq_entries
[i
].data_size
);
242 for (i
= 0; i
< db
->iq_fill
; i
++)
243 free(db
->iq_datas
[i
]);
246 if (db
->iq_fill
+ db
->nr_entries
> db
->nr_allocated
) {
247 db
->nr_allocated
= db
->iq_fill
+ db
->nr_entries
;
248 db
->entries
= xrenew(struct db_entry
, db
->entries
, db
->nr_allocated
);
250 for (i
= 0; i
< db
->iq_fill
; i
++) {
251 struct db_entry
*s
= &db
->iq_entries
[i
];
252 struct db_entry
*d
= &db
->entries
[db
->nr_entries
];
255 d
->data_size
= s
->data_size
;
261 qsort(db
->entries
, db
->nr_entries
, sizeof(struct db_entry
), db_entry_cmp
);
266 static int iq_search(struct db
*db
, const void *key
)
270 for (i
= 0; i
< db
->iq_fill
; i
++) {
271 if (strcmp(db
->iq_entries
[i
].key
, key
) == 0)
277 static int iq_remove(struct db
*db
, const char *key
)
281 i
= iq_search(db
, key
);
285 free(db
->iq_entries
[i
].key
);
286 free(db
->iq_datas
[i
]);
288 array_remove(db
->iq_entries
, db
->iq_fill
, sizeof(struct db_entry
), i
);
289 array_remove(db
->iq_datas
, db
->iq_fill
, sizeof(void *), i
);
296 struct db
*db_new(const char *filename_base
)
300 db
= xnew(struct db
, 1);
302 db
->idx_fn
= xstrjoin(filename_base
, ".idx");
303 db
->dat_fn
= xstrjoin(filename_base
, ".dat");
306 db
->nr_allocated
= 0;
311 db
->iq_entries
= xnew(struct db_entry
, db
->iq_size
);
312 db
->iq_datas
= xnew(void *, db
->iq_size
);
316 int db_load(struct db
*db
)
324 db
->dat_fd
= open(db
->dat_fn
, O_RDWR
| O_CREAT
, 0666);
325 if (db
->dat_fd
== -1) {
332 int db_close(struct db
*db
)
343 free(db
->iq_entries
);
351 int db_insert(struct db
*db
, char *key
, void *data
, unsigned int data_size
)
355 if (db
->iq_fill
== db
->iq_size
) {
356 int rc
= iq_flush(db
);
361 db
->iq_entries
[i
].data_pos
= 0;
362 db
->iq_entries
[i
].data_size
= data_size
;
363 db
->iq_entries
[i
].key
= key
;
364 db
->iq_datas
[i
] = data
;
369 int db_remove(struct db
*db
, const char *key
)
371 if (index_remove(db
, key
))
373 if (iq_remove(db
, key
))
378 int db_query(struct db
*db
, const char *key
, void **datap
, unsigned int *data_sizep
)
387 e
= index_search(db
, key
);
391 i
= iq_search(db
, key
);
394 *data_sizep
= db
->iq_entries
[i
].data_size
;
395 *datap
= xmalloc(*data_sizep
);
396 memcpy(*datap
, db
->iq_datas
[i
], *data_sizep
);
400 if (lseek(db
->dat_fd
, e
->data_pos
, SEEK_SET
) == -1) {
404 buf
= xmalloc(e
->data_size
);
405 rc
= read_all(db
->dat_fd
, buf
, e
->data_size
);
410 if (rc
!= e
->data_size
) {
415 *data_sizep
= e
->data_size
;