stringpool: Make them initializable right from the file
[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 "libloc-private.h"
28 #include "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 r = loc_stringpool_mmap(*pool, f, length, offset);
94 if (r)
95 return r;
96
97 return 0;
98 }
99
100 LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
101 pool->refcount++;
102
103 return pool;
104 }
105
106 static void loc_stringpool_free(struct loc_stringpool* pool) {
107 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
108 int r;
109
110 switch (pool->mode) {
111 case STRINGPOOL_DEFAULT:
112 if (pool->data)
113 free(pool->data);
114 break;
115
116 case STRINGPOOL_MMAP:
117 if (pool->data) {
118 r = munmap(pool->data, pool->length);
119 if (r)
120 ERROR(pool->ctx, "Could not unmap data at %p: %s\n",
121 pool->data, strerror(errno));
122 }
123 break;
124 }
125
126 loc_unref(pool->ctx);
127 free(pool);
128 }
129
130 LOC_EXPORT struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
131 if (--pool->refcount > 0)
132 return NULL;
133
134 loc_stringpool_free(pool);
135
136 return NULL;
137 }
138
139 static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
140 if (pos < pool->data)
141 return -EFAULT;
142
143 if (pos > (pool->data + pool->length))
144 return -EFAULT;
145
146 return pos - pool->data;
147 }
148
149 static off_t loc_stringpool_get_next_offset(struct loc_stringpool* pool, off_t offset) {
150 const char* string = loc_stringpool_get(pool, offset);
151
152 return offset + strlen(string) + 1;
153 }
154
155 static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
156 if (offset < 0 || offset >= pool->length)
157 return NULL;
158
159 return pool->data + offset;
160 }
161
162 LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
163 return __loc_stringpool_get(pool, offset);
164 }
165
166 LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
167 return loc_stringpool_get_offset(pool, pool->pos);
168 }
169
170 static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
171 if (!s || !*s)
172 return -EINVAL;
173
174 off_t offset = 0;
175 while (offset < pool->length) {
176 const char* string = loc_stringpool_get(pool, offset);
177 if (!string)
178 break;
179
180 int r = strcmp(s, string);
181 if (r == 0)
182 return offset;
183
184 offset = loc_stringpool_get_next_offset(pool, offset);
185 }
186
187 return -ENOENT;
188 }
189
190 static int loc_stringpool_grow(struct loc_stringpool* pool, size_t length) {
191 DEBUG(pool->ctx, "Growing string pool to %zu bytes\n", length);
192
193 // Save pos pointer
194 off_t pos = loc_stringpool_get_offset(pool, pool->pos);
195
196 // Reallocate data section
197 pool->data = realloc(pool->data, length);
198 if (!pool->data)
199 return -ENOMEM;
200
201 pool->length = length;
202
203 // Restore pos
204 pool->pos = __loc_stringpool_get(pool, pos);
205
206 return 0;
207 }
208
209 static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
210 if (!string || !*string)
211 return -EINVAL;
212
213 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
214
215 // Make sure we have enough space
216 int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1);
217 if (r) {
218 errno = r;
219 return -1;
220 }
221
222 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
223
224 // Copy string byte by byte
225 while (*string)
226 *pool->pos++ = *string++;
227
228 // Terminate the string
229 *pool->pos++ = '\0';
230
231 return offset;
232 }
233
234 LOC_EXPORT off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
235 off_t offset = loc_stringpool_find(pool, string);
236 if (offset >= 0) {
237 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, offset);
238 return offset;
239 }
240
241 return loc_stringpool_append(pool, string);
242 }
243
244 LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
245 off_t offset = 0;
246
247 while (offset < pool->length) {
248 const char* string = loc_stringpool_get(pool, offset);
249 if (!string)
250 break;
251
252 printf("%jd (%zu): %s\n", offset, strlen(string), string);
253
254 offset = loc_stringpool_get_next_offset(pool, offset);
255 }
256 }
257
258 LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
259 size_t size = loc_stringpool_get_size(pool);
260
261 return fwrite(pool->data, sizeof(*pool->data), size, f);
262 }