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