]> git.ipfire.org Git - people/ms/libloc.git/blob - src/stringpool.c
database: Unmap all mapped sections when freeing database
[people/ms/libloc.git] / src / stringpool.c
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>
22 #include <sys/mman.h>
23 #include <unistd.h>
24
25 #include <loc/libloc.h>
26 #include <loc/format.h>
27 #include <loc/private.h>
28 #include <loc/stringpool.h>
29
30 enum loc_stringpool_mode {
31 STRINGPOOL_DEFAULT,
32 STRINGPOOL_MMAP,
33 };
34
35 struct loc_stringpool {
36 struct loc_ctx* ctx;
37 int refcount;
38
39 enum loc_stringpool_mode mode;
40
41 char* data;
42 ssize_t length;
43
44 char* pos;
45 };
46
47 static int __loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool, enum loc_stringpool_mode mode) {
48 struct loc_stringpool* p = calloc(1, sizeof(*p));
49 if (!p)
50 return -ENOMEM;
51
52 p->ctx = loc_ref(ctx);
53 p->refcount = 1;
54
55 // Save mode
56 p->mode = mode;
57
58 *pool = p;
59
60 return 0;
61 }
62
63 LOC_EXPORT int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
64 return __loc_stringpool_new(ctx, pool, STRINGPOOL_DEFAULT);
65 }
66
67 static int loc_stringpool_mmap(struct loc_stringpool* pool, FILE* f, size_t length, off_t offset) {
68 if (pool->mode != STRINGPOOL_MMAP)
69 return -EINVAL;
70
71 DEBUG(pool->ctx, "Reading string pool starting from %zu (%zu bytes)\n", offset, length);
72
73 // Map file content into memory
74 pool->data = pool->pos = mmap(NULL, length, PROT_READ,
75 MAP_PRIVATE, fileno(f), offset);
76
77 // Store size of section
78 pool->length = length;
79
80 if (pool->data == MAP_FAILED)
81 return -errno;
82
83 return 0;
84 }
85
86 LOC_EXPORT int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
87 FILE* f, size_t length, off_t offset) {
88 int r = __loc_stringpool_new(ctx, pool, STRINGPOOL_MMAP);
89 if (r)
90 return r;
91
92 // Map data into memory
93 if (length > 0) {
94 r = loc_stringpool_mmap(*pool, f, length, offset);
95 if (r)
96 return r;
97 }
98
99 return 0;
100 }
101
102 LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
103 pool->refcount++;
104
105 return pool;
106 }
107
108 static void loc_stringpool_free(struct loc_stringpool* pool) {
109 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
110 int r;
111
112 switch (pool->mode) {
113 case STRINGPOOL_DEFAULT:
114 if (pool->data)
115 free(pool->data);
116 break;
117
118 case STRINGPOOL_MMAP:
119 if (pool->data) {
120 r = munmap(pool->data, pool->length);
121 if (r)
122 ERROR(pool->ctx, "Could not unmap data at %p: %s\n",
123 pool->data, strerror(errno));
124 }
125 break;
126 }
127
128 loc_unref(pool->ctx);
129 free(pool);
130 }
131
132 LOC_EXPORT struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
133 if (--pool->refcount > 0)
134 return NULL;
135
136 loc_stringpool_free(pool);
137
138 return NULL;
139 }
140
141 static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
142 if (pos < pool->data)
143 return -EFAULT;
144
145 if (pos > (pool->data + pool->length))
146 return -EFAULT;
147
148 return pos - pool->data;
149 }
150
151 static off_t loc_stringpool_get_next_offset(struct loc_stringpool* pool, off_t offset) {
152 const char* string = loc_stringpool_get(pool, offset);
153
154 return offset + strlen(string) + 1;
155 }
156
157 static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
158 if (offset < 0 || offset >= pool->length)
159 return NULL;
160
161 return pool->data + offset;
162 }
163
164 LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
165 return __loc_stringpool_get(pool, offset);
166 }
167
168 LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
169 return loc_stringpool_get_offset(pool, pool->pos);
170 }
171
172 static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
173 if (!s || !*s)
174 return -EINVAL;
175
176 off_t offset = 0;
177 while (offset < pool->length) {
178 const char* string = loc_stringpool_get(pool, offset);
179 if (!string)
180 break;
181
182 int r = strcmp(s, string);
183 if (r == 0)
184 return offset;
185
186 offset = loc_stringpool_get_next_offset(pool, offset);
187 }
188
189 return -ENOENT;
190 }
191
192 static int loc_stringpool_grow(struct loc_stringpool* pool, size_t length) {
193 DEBUG(pool->ctx, "Growing string pool to %zu bytes\n", length);
194
195 // Save pos pointer
196 off_t pos = loc_stringpool_get_offset(pool, pool->pos);
197
198 // Reallocate data section
199 pool->data = realloc(pool->data, length);
200 if (!pool->data)
201 return -ENOMEM;
202
203 pool->length = length;
204
205 // Restore pos
206 pool->pos = __loc_stringpool_get(pool, pos);
207
208 return 0;
209 }
210
211 static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
212 if (!string || !*string)
213 return -EINVAL;
214
215 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
216
217 // Make sure we have enough space
218 int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1);
219 if (r) {
220 errno = r;
221 return -1;
222 }
223
224 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
225
226 // Copy string byte by byte
227 while (*string)
228 *pool->pos++ = *string++;
229
230 // Terminate the string
231 *pool->pos++ = '\0';
232
233 return offset;
234 }
235
236 LOC_EXPORT off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
237 off_t offset = loc_stringpool_find(pool, string);
238 if (offset >= 0) {
239 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, offset);
240 return offset;
241 }
242
243 return loc_stringpool_append(pool, string);
244 }
245
246 LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
247 off_t offset = 0;
248
249 while (offset < pool->length) {
250 const char* string = loc_stringpool_get(pool, offset);
251 if (!string)
252 break;
253
254 printf("%jd (%zu): %s\n", offset, strlen(string), string);
255
256 offset = loc_stringpool_get_next_offset(pool, offset);
257 }
258 }
259
260 LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
261 size_t size = loc_stringpool_get_size(pool);
262
263 return fwrite(pool->data, sizeof(*pool->data), size, f);
264 }