stringpool: Make them initializable right from the file
[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>
62b83e6d
MT
27#include "libloc-private.h"
28#include "stringpool.h"
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
MT
92 // Map data into memory
93 r = loc_stringpool_mmap(*pool, f, length, offset);
94 if (r)
95 return r;
62b83e6d
MT
96
97 return 0;
98}
99
100LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
101 pool->refcount++;
102
103 return pool;
104}
105
106static void loc_stringpool_free(struct loc_stringpool* pool) {
107 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
0e974d4b
MT
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 }
62b83e6d
MT
125
126 loc_unref(pool->ctx);
62b83e6d
MT
127 free(pool);
128}
129
130LOC_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
139static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
140 if (pos < pool->data)
141 return -EFAULT;
142
0e974d4b 143 if (pos > (pool->data + pool->length))
62b83e6d
MT
144 return -EFAULT;
145
146 return pos - pool->data;
147}
148
149static 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
0e974d4b
MT
155static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
156 if (offset < 0 || offset >= pool->length)
62b83e6d
MT
157 return NULL;
158
0e974d4b
MT
159 return pool->data + offset;
160}
62b83e6d 161
0e974d4b
MT
162LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
163 return __loc_stringpool_get(pool, offset);
62b83e6d
MT
164}
165
2601e83e
MT
166LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
167 return loc_stringpool_get_offset(pool, pool->pos);
168}
169
62b83e6d
MT
170static 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;
0e974d4b 175 while (offset < pool->length) {
62b83e6d
MT
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
0e974d4b
MT
190static 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
62b83e6d
MT
209static 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
0e974d4b
MT
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;
62b83e6d
MT
220 }
221
222 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
223
224 // Copy string byte by byte
0e974d4b 225 while (*string)
62b83e6d 226 *pool->pos++ = *string++;
62b83e6d
MT
227
228 // Terminate the string
229 *pool->pos++ = '\0';
230
231 return offset;
232}
233
234LOC_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
244LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
245 off_t offset = 0;
246
0e974d4b 247 while (offset < pool->length) {
62b83e6d
MT
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}
2601e83e 257
2601e83e
MT
258LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
259 size_t size = loc_stringpool_get_size(pool);
260
8f5b676a 261 return fwrite(pool->data, sizeof(*pool->data), size, f);
2601e83e 262}