]> git.ipfire.org Git - people/ms/libloc.git/blob - src/stringpool.c
importer: Drop EDROP as it has been merged into DROP
[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 <libloc/libloc.h>
26 #include <libloc/format.h>
27 #include <libloc/private.h>
28 #include <libloc/stringpool.h>
29
30 #define LOC_STRINGPOOL_BLOCK_SIZE (512 * 1024)
31
32 struct loc_stringpool {
33 struct loc_ctx* ctx;
34 int refcount;
35
36 // Reference to any mapped data
37 const char* data;
38 ssize_t length;
39
40 // Reference to own storage
41 char* blocks;
42 size_t size;
43 };
44
45 static int loc_stringpool_grow(struct loc_stringpool* pool, const size_t size) {
46 DEBUG(pool->ctx, "Growing string pool by %zu byte(s)\n", size);
47
48 // Increment size
49 pool->size += size;
50
51 // Reallocate blocks
52 pool->blocks = realloc(pool->blocks, pool->size);
53 if (!pool->blocks) {
54 ERROR(pool->ctx, "Could not grow string pool: %m\n");
55 return 1;
56 }
57
58 // Update data pointer
59 pool->data = pool->blocks;
60
61 return 0;
62 }
63
64 static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
65 if (!string) {
66 errno = EINVAL;
67 return -1;
68 }
69
70 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
71
72 // How much space to we need?
73 const size_t length = strlen(string) + 1;
74
75 // Make sure we have enough space
76 if (pool->length + length > pool->size) {
77 int r = loc_stringpool_grow(pool, LOC_STRINGPOOL_BLOCK_SIZE);
78 if (r)
79 return r;
80 }
81
82 off_t offset = pool->length;
83
84 // Copy the string
85 memcpy(pool->blocks + offset, string, length);
86
87 // Update the length of the pool
88 pool->length += length;
89
90 return offset;
91 }
92
93 static void loc_stringpool_free(struct loc_stringpool* pool) {
94 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
95
96 // Free any data
97 if (pool->blocks)
98 free(pool->blocks);
99
100 loc_unref(pool->ctx);
101 free(pool);
102 }
103
104 int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
105 struct loc_stringpool* p = calloc(1, sizeof(*p));
106 if (!p)
107 return 1;
108
109 p->ctx = loc_ref(ctx);
110 p->refcount = 1;
111
112 *pool = p;
113
114 return 0;
115 }
116
117 int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
118 const char* data, const size_t length) {
119 struct loc_stringpool* p = NULL;
120
121 // Allocate a new stringpool
122 int r = loc_stringpool_new(ctx, &p);
123 if (r)
124 goto ERROR;
125
126 // Store data and length
127 p->data = data;
128 p->length = length;
129
130 DEBUG(p->ctx, "Opened string pool at %p (%zu bytes)\n", p->data, p->length);
131
132 *pool = p;
133 return 0;
134
135 ERROR:
136 if (p)
137 loc_stringpool_free(p);
138
139 return r;
140 }
141
142 struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
143 pool->refcount++;
144
145 return pool;
146 }
147
148 struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
149 if (--pool->refcount > 0)
150 return NULL;
151
152 loc_stringpool_free(pool);
153
154 return NULL;
155 }
156
157 const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
158 // Check boundaries
159 if (offset < 0 || offset >= pool->length) {
160 errno = ERANGE;
161 return NULL;
162 }
163
164 // Return any data that we have in memory
165 return pool->data + offset;
166 }
167
168 size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
169 return pool->length;
170 }
171
172 static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
173 if (!s || !*s) {
174 errno = EINVAL;
175 return -1;
176 }
177
178 off_t offset = 0;
179 while (offset < pool->length) {
180 const char* string = loc_stringpool_get(pool, offset);
181
182 // Error!
183 if (!string)
184 return 1;
185
186 // Is this a match?
187 if (strcmp(s, string) == 0)
188 return offset;
189
190 // Shift offset
191 offset += strlen(string) + 1;
192 }
193
194 // Nothing found
195 errno = ENOENT;
196 return -1;
197 }
198
199 off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
200 off_t offset = loc_stringpool_find(pool, string);
201 if (offset >= 0) {
202 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, (intmax_t)offset);
203 return offset;
204 }
205
206 return loc_stringpool_append(pool, string);
207 }
208
209 void loc_stringpool_dump(struct loc_stringpool* pool) {
210 off_t offset = 0;
211
212 while (offset < pool->length) {
213 const char* string = loc_stringpool_get(pool, offset);
214 if (!string)
215 return;
216
217 printf("%jd (%zu): %s\n", (intmax_t)offset, strlen(string), string);
218
219 // Shift offset
220 offset += strlen(string) + 1;
221 }
222 }
223
224 size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
225 size_t size = loc_stringpool_get_size(pool);
226
227 return fwrite(pool->data, sizeof(*pool->data), size, f);
228 }