]> git.ipfire.org Git - people/ms/libloc.git/blame - src/stringpool.c
database: Implement lookup
[people/ms/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
MT
24
25#include <loc/libloc.h>
c34e76f1 26#include <loc/format.h>
9fc7f001
MT
27#include <loc/private.h>
28#include <loc/stringpool.h>
62b83e6d 29
0e974d4b
MT
30enum loc_stringpool_mode {
31 STRINGPOOL_DEFAULT,
32 STRINGPOOL_MMAP,
33};
34
62b83e6d
MT
35struct loc_stringpool {
36 struct loc_ctx* ctx;
62b83e6d 37 int refcount;
0e974d4b
MT
38
39 enum loc_stringpool_mode mode;
40
62b83e6d 41 char* data;
0e974d4b 42 ssize_t length;
62b83e6d 43
0e974d4b 44 char* pos;
62b83e6d
MT
45};
46
0e974d4b
MT
47static 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;
6809d5ac 54
0e974d4b
MT
55 // Save mode
56 p->mode = mode;
57
58 *pool = p;
6809d5ac
MT
59
60 return 0;
61}
62
0e974d4b
MT
63LOC_EXPORT int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
64 return __loc_stringpool_new(ctx, pool, STRINGPOOL_DEFAULT);
65}
2601e83e 66
0e974d4b
MT
67static 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;
6809d5ac 70
0e974d4b 71 DEBUG(pool->ctx, "Reading string pool starting from %zu (%zu bytes)\n", offset, length);
6809d5ac 72
0e974d4b
MT
73 // Map file content into memory
74 pool->data = pool->pos = mmap(NULL, length, PROT_READ,
75 MAP_PRIVATE, fileno(f), offset);
2601e83e 76
0e974d4b
MT
77 // Store size of section
78 pool->length = length;
6809d5ac 79
0e974d4b 80 if (pool->data == MAP_FAILED)
6809d5ac 81 return -errno;
2601e83e
MT
82
83 return 0;
84}
85
0e974d4b
MT
86LOC_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;
62b83e6d 91
0e974d4b 92 // Map data into memory
2a30e4de
MT
93 if (length > 0) {
94 r = loc_stringpool_mmap(*pool, f, length, offset);
95 if (r)
96 return r;
97 }
62b83e6d
MT
98
99 return 0;
100}
101
102LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
103 pool->refcount++;
104
105 return pool;
106}
107
108static void loc_stringpool_free(struct loc_stringpool* pool) {
109 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
0e974d4b
MT
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 }
62b83e6d
MT
127
128 loc_unref(pool->ctx);
62b83e6d
MT
129 free(pool);
130}
131
132LOC_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
141static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
142 if (pos < pool->data)
143 return -EFAULT;
144
0e974d4b 145 if (pos > (pool->data + pool->length))
62b83e6d
MT
146 return -EFAULT;
147
148 return pos - pool->data;
149}
150
151static 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
0e974d4b
MT
157static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
158 if (offset < 0 || offset >= pool->length)
62b83e6d
MT
159 return NULL;
160
0e974d4b
MT
161 return pool->data + offset;
162}
62b83e6d 163
0e974d4b
MT
164LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
165 return __loc_stringpool_get(pool, offset);
62b83e6d
MT
166}
167
2601e83e
MT
168LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
169 return loc_stringpool_get_offset(pool, pool->pos);
170}
171
62b83e6d
MT
172static 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;
0e974d4b 177 while (offset < pool->length) {
62b83e6d
MT
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
0e974d4b
MT
192static 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
62b83e6d
MT
211static 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
0e974d4b
MT
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;
62b83e6d
MT
222 }
223
224 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
225
226 // Copy string byte by byte
0e974d4b 227 while (*string)
62b83e6d 228 *pool->pos++ = *string++;
62b83e6d
MT
229
230 // Terminate the string
231 *pool->pos++ = '\0';
232
233 return offset;
234}
235
236LOC_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
246LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
247 off_t offset = 0;
248
0e974d4b 249 while (offset < pool->length) {
62b83e6d
MT
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}
2601e83e 259
2601e83e
MT
260LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
261 size_t size = loc_stringpool_get_size(pool);
262
8f5b676a 263 return fwrite(pool->data, sizeof(*pool->data), size, f);
2601e83e 264}