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