]> git.ipfire.org Git - location/libloc.git/blame - src/stringpool.c
tests: Add some simple database tests
[location/libloc.git] / src / stringpool.c
CommitLineData
62b83e6d
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#include <errno.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
6809d5ac
MT
22#include <sys/mman.h>
23#include <unistd.h>
62b83e6d 24
c12a1385
MT
25#include <libloc/libloc.h>
26#include <libloc/format.h>
27#include <libloc/private.h>
28#include <libloc/stringpool.h>
62b83e6d
MT
29
30struct loc_stringpool {
31 struct loc_ctx* ctx;
62b83e6d 32 int refcount;
0e974d4b 33
e4b6ca42
MT
34 // A file descriptor when we open an existing stringpool
35 int fd;
0e974d4b 36
e4b6ca42 37 off_t offset;
0e974d4b 38 ssize_t length;
62b83e6d 39
e4b6ca42
MT
40 // Mapped data (from mmap())
41 char* mmapped_data;
42
43 char* data;
0e974d4b 44 char* pos;
e4b6ca42
MT
45
46 char buffer[LOC_DATABASE_PAGE_SIZE];
62b83e6d
MT
47};
48
010fe9b6 49static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
198e382c
MT
50 if (pos < pool->data) {
51 errno = EFAULT;
52 return -1;
53 }
010fe9b6 54
198e382c
MT
55 if (pos > (pool->data + pool->length)) {
56 errno = EFAULT;
57 return -1;
58 }
010fe9b6
MT
59
60 return pos - pool->data;
61}
62
63static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
e4b6ca42
MT
64 ssize_t bytes_read;
65
66 // Check boundaries
67 if (offset < 0 || offset >= pool->length) {
68 errno = ERANGE;
010fe9b6 69 return NULL;
e4b6ca42
MT
70 }
71
72 // Return any data that we have in memory
73 if (pool->data)
74 return pool->data + offset;
010fe9b6 75
e4b6ca42
MT
76 // Otherwise read a block from file
77 bytes_read = pread(pool->fd, pool->buffer, sizeof(pool->buffer),
78 pool->offset + offset);
79
80 // Break on error
81 if (bytes_read < 0) {
82 ERROR(pool->ctx, "Could not read from string pool: %m\n");
83 return NULL;
84 }
85
86 // It is okay, if we did not read as much as we wanted, since we might be reading
87 // the last block which might be of an unknown size.
88
89 // Search for a complete string. If there is no NULL byte, the block is garbage.
90 char* end = memchr(pool->buffer, bytes_read, '\0');
91 if (!end)
92 return NULL;
93
94 // Return what's in the buffer
95 return pool->buffer;
010fe9b6
MT
96}
97
98static int loc_stringpool_grow(struct loc_stringpool* pool, size_t length) {
99 DEBUG(pool->ctx, "Growing string pool to %zu bytes\n", length);
100
101 // Save pos pointer
102 off_t pos = loc_stringpool_get_offset(pool, pool->pos);
103
104 // Reallocate data section
105 pool->data = realloc(pool->data, length);
106 if (!pool->data)
198e382c 107 return 1;
010fe9b6
MT
108
109 pool->length = length;
110
111 // Restore pos
112 pool->pos = __loc_stringpool_get(pool, pos);
113
114 return 0;
115}
116
117static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
198e382c
MT
118 if (!string) {
119 errno = EINVAL;
120 return -1;
121 }
010fe9b6
MT
122
123 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
124
125 // Make sure we have enough space
126 int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1);
198e382c 127 if (r)
010fe9b6 128 return -1;
010fe9b6
MT
129
130 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
131
132 // Copy string byte by byte
133 while (*string)
134 *pool->pos++ = *string++;
135
136 // Terminate the string
137 *pool->pos++ = '\0';
138
139 return offset;
140}
141
acf44a73
MT
142static void loc_stringpool_free(struct loc_stringpool* pool) {
143 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
144 int r;
145
e4b6ca42
MT
146 // Close file
147 if (pool->fd > 0)
148 close(pool->fd);
149
150 // Unmap any mapped memory
151 if (pool->mmapped_data) {
152 r = munmap(pool->mmapped_data, pool->length);
153 if (r)
154 ERROR(pool->ctx, "Error unmapping string pool: %m\n");
155
156 if (pool->mmapped_data == pool->data)
157 pool->data = NULL;
acf44a73
MT
158 }
159
e4b6ca42
MT
160 // Free any data
161 if (pool->data)
162 free(pool->data);
163
acf44a73
MT
164 loc_unref(pool->ctx);
165 free(pool);
166}
167
22d8abdc 168int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
0e974d4b
MT
169 struct loc_stringpool* p = calloc(1, sizeof(*p));
170 if (!p)
8d10d064 171 return 1;
0e974d4b
MT
172
173 p->ctx = loc_ref(ctx);
174 p->refcount = 1;
6809d5ac 175
0e974d4b 176 *pool = p;
6809d5ac
MT
177
178 return 0;
179}
180
e4b6ca42
MT
181static int loc_stringpool_mmap(struct loc_stringpool* pool) {
182 // Try mmap()
183 char* p = mmap(NULL, pool->length, PROT_READ, MAP_PRIVATE, pool->fd, pool->offset);
6809d5ac 184
e4b6ca42
MT
185 if (p == MAP_FAILED) {
186 // Ignore if data hasn't been aligned correctly
187 if (errno == EINVAL)
188 return 0;
6809d5ac 189
e4b6ca42 190 ERROR(pool->ctx, "Could not mmap stringpool: %m\n");
acf44a73 191 return 1;
e4b6ca42
MT
192 }
193
194 // Store mapped memory area
195 pool->data = pool->mmapped_data = pool->pos = p;
2601e83e
MT
196
197 return 0;
198}
199
22d8abdc 200int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
0e974d4b 201 FILE* f, size_t length, off_t offset) {
acf44a73
MT
202 struct loc_stringpool* p = NULL;
203
204 // Allocate a new stringpool
205 int r = loc_stringpool_new(ctx, &p);
0e974d4b 206 if (r)
e4b6ca42
MT
207 goto ERROR;
208
209 // Store offset and length
210 p->offset = offset;
211 p->length = length;
212
213 DEBUG(p->ctx, "Reading string pool starting from %jd (%zu bytes)\n",
214 (intmax_t)p->offset, p->length);
62b83e6d 215
e4b6ca42
MT
216 int fd = fileno(f);
217
218 // Copy the file descriptor
219 p->fd = dup(fd);
220 if (p->fd < 0) {
221 ERROR(ctx, "Could not duplicate file the file descriptor: %m\n");
222 r = 1;
223 goto ERROR;
224 }
8d10d064 225
0e974d4b 226 // Map data into memory
e4b6ca42
MT
227 if (p->length > 0) {
228 r = loc_stringpool_mmap(p);
229 if (r)
230 goto ERROR;
2a30e4de 231 }
62b83e6d 232
acf44a73 233 *pool = p;
62b83e6d 234 return 0;
e4b6ca42
MT
235
236ERROR:
237 if (p)
238 loc_stringpool_free(p);
239
240 return r;
62b83e6d
MT
241}
242
22d8abdc 243struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
62b83e6d
MT
244 pool->refcount++;
245
246 return pool;
247}
248
22d8abdc 249struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
62b83e6d
MT
250 if (--pool->refcount > 0)
251 return NULL;
252
253 loc_stringpool_free(pool);
254
255 return NULL;
256}
257
62b83e6d
MT
258static off_t loc_stringpool_get_next_offset(struct loc_stringpool* pool, off_t offset) {
259 const char* string = loc_stringpool_get(pool, offset);
1afe5545
MT
260 if (!string)
261 return offset;
62b83e6d
MT
262
263 return offset + strlen(string) + 1;
264}
265
22d8abdc 266const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
0e974d4b 267 return __loc_stringpool_get(pool, offset);
62b83e6d
MT
268}
269
22d8abdc 270size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
2601e83e
MT
271 return loc_stringpool_get_offset(pool, pool->pos);
272}
273
62b83e6d 274static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
198e382c
MT
275 if (!s || !*s) {
276 errno = EINVAL;
277 return -1;
278 }
62b83e6d
MT
279
280 off_t offset = 0;
0e974d4b 281 while (offset < pool->length) {
62b83e6d
MT
282 const char* string = loc_stringpool_get(pool, offset);
283 if (!string)
284 break;
285
286 int r = strcmp(s, string);
287 if (r == 0)
288 return offset;
289
290 offset = loc_stringpool_get_next_offset(pool, offset);
291 }
292
198e382c
MT
293 // Nothing found
294 errno = ENOENT;
295 return -1;
62b83e6d
MT
296}
297
22d8abdc 298off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
62b83e6d
MT
299 off_t offset = loc_stringpool_find(pool, string);
300 if (offset >= 0) {
5c57de03 301 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, (intmax_t)offset);
62b83e6d
MT
302 return offset;
303 }
304
305 return loc_stringpool_append(pool, string);
306}
307
22d8abdc 308void loc_stringpool_dump(struct loc_stringpool* pool) {
62b83e6d
MT
309 off_t offset = 0;
310
0e974d4b 311 while (offset < pool->length) {
62b83e6d
MT
312 const char* string = loc_stringpool_get(pool, offset);
313 if (!string)
314 break;
315
5c57de03 316 printf("%jd (%zu): %s\n", (intmax_t)offset, strlen(string), string);
62b83e6d
MT
317
318 offset = loc_stringpool_get_next_offset(pool, offset);
319 }
320}
2601e83e 321
22d8abdc 322size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
2601e83e
MT
323 size_t size = loc_stringpool_get_size(pool);
324
8f5b676a 325 return fwrite(pool->data, sizeof(*pool->data), size, f);
2601e83e 326}