database: Copy the file pointer so we can keep the file open
[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>
22
23#include <loc/libloc.h>
24#include "libloc-private.h"
25#include "stringpool.h"
26
27struct 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
2601e83e
MT
37static 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 }
62b83e6d 50
2601e83e
MT
51 pool->pos = pool->data;
52
53 return 0;
54}
55
56LOC_EXPORT int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool, size_t max_length) {
62b83e6d
MT
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;
62b83e6d
MT
63
64 // Allocate the data section
2601e83e
MT
65 int r = loc_stringpool_allocate(p, max_length);
66 if (r) {
67 loc_stringpool_unref(p);
68 return r;
69 }
62b83e6d
MT
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
78LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
79 pool->refcount++;
80
81 return pool;
82}
83
84static 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
2601e83e
MT
89 if (pool->data)
90 free(pool->data);
91
62b83e6d
MT
92 free(pool);
93}
94
95LOC_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
104static 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
114static 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
120static size_t loc_stringpool_space_left(struct loc_stringpool* pool) {
2601e83e 121 return pool->max_length - loc_stringpool_get_size(pool);
62b83e6d
MT
122}
123
124LOC_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
2601e83e
MT
137LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
138 return loc_stringpool_get_offset(pool, pool->pos);
139}
140
62b83e6d
MT
141static 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
161static 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
188LOC_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
198LOC_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}
2601e83e
MT
211
212LOC_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
3f35869a
MT
218 DEBUG(pool->ctx, "Reading string pool from %zu\n", offset);
219
2601e83e
MT
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
236LOC_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}