]> git.ipfire.org Git - people/ms/libloc.git/blame - src/stringpool.c
stringpool: Use mmap to make reading the pool more efficient
[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>
26#include "libloc-private.h"
27#include "stringpool.h"
28
29struct loc_stringpool {
30 struct loc_ctx* ctx;
31
32 int refcount;
33 char* data;
34 char* pos;
35
36 ssize_t max_length;
37};
38
6809d5ac
MT
39static int loc_stringpool_deallocate(struct loc_stringpool* pool) {
40 if (pool->data) {
41 int r = munmap(pool->data, pool->max_length);
42 if (r) {
43 ERROR(pool->ctx, "Could not unmap data at %p: %s\n",
44 pool->data, strerror(errno));
45
46 return r;
47 }
48 }
49
50 return 0;
51}
52
2601e83e
MT
53static int loc_stringpool_allocate(struct loc_stringpool* pool, size_t length) {
54 // Drop old data
6809d5ac
MT
55 int r = loc_stringpool_deallocate(pool);
56 if (r)
57 return r;
2601e83e
MT
58
59 pool->max_length = length;
6809d5ac
MT
60
61 // Align to page size
62 while (pool->max_length % sysconf(_SC_PAGE_SIZE) > 0)
63 pool->max_length++;
64
2601e83e
MT
65 DEBUG(pool->ctx, "Allocating pool of %zu bytes\n", pool->max_length);
66
6809d5ac
MT
67 // Allocate some memory
68 pool->data = pool->pos = mmap(NULL, pool->max_length,
69 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
70
71 if (pool->data == MAP_FAILED) {
72 DEBUG(pool->ctx, "%s\n", strerror(errno));
73 return -errno;
2601e83e 74 }
62b83e6d 75
6809d5ac 76 DEBUG(pool->ctx, "Allocated pool at %p\n", pool->data);
2601e83e
MT
77
78 return 0;
79}
80
81LOC_EXPORT int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool, size_t max_length) {
62b83e6d
MT
82 struct loc_stringpool* p = calloc(1, sizeof(*p));
83 if (!p)
84 return -ENOMEM;
85
86 p->ctx = loc_ref(ctx);
87 p->refcount = 1;
62b83e6d
MT
88
89 // Allocate the data section
6809d5ac
MT
90 if (max_length > 0) {
91 int r = loc_stringpool_allocate(p, max_length);
92 if (r) {
93 loc_stringpool_unref(p);
94 return r;
95 }
2601e83e 96 }
62b83e6d
MT
97
98 DEBUG(p->ctx, "String pool allocated at %p\n", p);
99 DEBUG(p->ctx, " Maximum size: %zu bytes\n", p->max_length);
100 *pool = p;
101
102 return 0;
103}
104
105LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
106 pool->refcount++;
107
108 return pool;
109}
110
111static void loc_stringpool_free(struct loc_stringpool* pool) {
112 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
113
6809d5ac 114 loc_stringpool_deallocate(pool);
62b83e6d 115 loc_unref(pool->ctx);
62b83e6d
MT
116 free(pool);
117}
118
119LOC_EXPORT struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
120 if (--pool->refcount > 0)
121 return NULL;
122
123 loc_stringpool_free(pool);
124
125 return NULL;
126}
127
128static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
129 if (pos < pool->data)
130 return -EFAULT;
131
132 if (pos > (pool->data + pool->max_length))
133 return -EFAULT;
134
135 return pos - pool->data;
136}
137
138static off_t loc_stringpool_get_next_offset(struct loc_stringpool* pool, off_t offset) {
139 const char* string = loc_stringpool_get(pool, offset);
140
141 return offset + strlen(string) + 1;
142}
143
144static size_t loc_stringpool_space_left(struct loc_stringpool* pool) {
2601e83e 145 return pool->max_length - loc_stringpool_get_size(pool);
62b83e6d
MT
146}
147
148LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
149 if (offset >= (ssize_t)pool->max_length)
150 return NULL;
151
152 const char* string = pool->data + offset;
153
154 // If the string is empty, we have reached the end
155 if (!*string)
156 return NULL;
157
158 return string;
159}
160
2601e83e
MT
161LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
162 return loc_stringpool_get_offset(pool, pool->pos);
163}
164
62b83e6d
MT
165static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
166 if (!s || !*s)
167 return -EINVAL;
168
169 off_t offset = 0;
170 while (offset < pool->max_length) {
171 const char* string = loc_stringpool_get(pool, offset);
172 if (!string)
173 break;
174
175 int r = strcmp(s, string);
176 if (r == 0)
177 return offset;
178
179 offset = loc_stringpool_get_next_offset(pool, offset);
180 }
181
182 return -ENOENT;
183}
184
185static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
186 if (!string || !*string)
187 return -EINVAL;
188
189 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
190
191 // Check if we have enough space left
192 size_t l = strlen(string) + 1;
193 if (l > loc_stringpool_space_left(pool)) {
194 DEBUG(pool->ctx, "Not enough space to append '%s'\n", string);
195 DEBUG(pool->ctx, " Need %zu bytes but only have %zu\n", l, loc_stringpool_space_left(pool));
196 return -ENOSPC;
197 }
198
199 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
200
201 // Copy string byte by byte
202 while (*string && loc_stringpool_space_left(pool) > 1) {
203 *pool->pos++ = *string++;
204 }
205
206 // Terminate the string
207 *pool->pos++ = '\0';
208
209 return offset;
210}
211
212LOC_EXPORT off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
213 off_t offset = loc_stringpool_find(pool, string);
214 if (offset >= 0) {
215 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, offset);
216 return offset;
217 }
218
219 return loc_stringpool_append(pool, string);
220}
221
222LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
223 off_t offset = 0;
224
225 while (offset < pool->max_length) {
226 const char* string = loc_stringpool_get(pool, offset);
227 if (!string)
228 break;
229
230 printf("%jd (%zu): %s\n", offset, strlen(string), string);
231
232 offset = loc_stringpool_get_next_offset(pool, offset);
233 }
234}
2601e83e 235
6809d5ac 236#include <assert.h>
3f35869a 237
6809d5ac
MT
238LOC_EXPORT int loc_stringpool_read(struct loc_stringpool* pool, FILE* f, off_t offset, size_t length) {
239 DEBUG(pool->ctx, "Reading string pool from %zu (%zu bytes)\n", offset, length);
2601e83e 240
6809d5ac
MT
241 pool->data = pool->pos = mmap(NULL, length, PROT_READ,
242 MAP_PRIVATE, fileno(f), offset);
243 pool->max_length = length;
2601e83e 244
6809d5ac
MT
245 if (pool->data == MAP_FAILED)
246 return -errno;
2601e83e
MT
247
248 return 0;
249}
250
251LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
252 size_t size = loc_stringpool_get_size(pool);
253
254 return fwrite(pool->data, sizeof(*pool->data), size, f);
255}