1 /*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2002 - DINH Viet Hoa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the libEtPan! project nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * $Id$
34 */
35
36 #include "mmapstring.h"
37
38 #include "chash.h"
39
40 #include <glib.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <sys/mman.h>
44 #include <string.h>
45 #include <pthread.h>
46 #include <limits.h>
47
48 #include "utils.h"
49
50 #define MMAP_STRING_DEFAULT_CEIL (8 * 1024 * 1024)
51
52 #define DEFAULT_TMP_PATH "/tmp"
53
54 static char tmpdir[PATH_MAX] = DEFAULT_TMP_PATH;
55
56 static size_t mmap_string_ceil = MMAP_STRING_DEFAULT_CEIL;
57
58 /* MMAPString references */
59
60 static pthread_mutex_t mmapstring_lock = PTHREAD_MUTEX_INITIALIZER;
61 static chash * mmapstring_hashtable = NULL;
62
mmapstring_hashtable_init()63 static void mmapstring_hashtable_init()
64 {
65 mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
66 }
67
mmap_string_set_tmpdir(char * directory)68 void mmap_string_set_tmpdir(char * directory)
69 {
70 strncpy(tmpdir, directory, PATH_MAX);
71 tmpdir[PATH_MAX - 1] = 0;
72 }
73
74
mmap_string_ref(MMAPString * string)75 int mmap_string_ref(MMAPString * string)
76 {
77 chash * ht;
78 int r;
79 chashdatum key;
80 chashdatum data;
81
82 pthread_mutex_lock(&mmapstring_lock);
83 if (mmapstring_hashtable == NULL) {
84 mmapstring_hashtable_init();
85 }
86 ht = mmapstring_hashtable;
87
88 if (ht == NULL) {
89 pthread_mutex_unlock(&mmapstring_lock);
90 return -1;
91 }
92
93 key.data = &string->str;
94 key.len = sizeof(string->str);
95 data.data = string;
96 data.len = 0;
97
98 r = chash_set(mmapstring_hashtable, &key, &data, NULL);
99 pthread_mutex_unlock(&mmapstring_lock);
100
101 if (r < 0)
102 return r;
103
104 return 0;
105 }
106
mmap_string_unref(char * str)107 int mmap_string_unref(char * str)
108 {
109 MMAPString * string;
110 chash * ht;
111 chashdatum key;
112 chashdatum data;
113 int r;
114
115 pthread_mutex_lock(&mmapstring_lock);
116 ht = mmapstring_hashtable;
117
118 if (ht == NULL) {
119 pthread_mutex_unlock(&mmapstring_lock);
120 return -1;
121 }
122
123 key.data = &str;
124 key.len = sizeof(str);
125
126 r = chash_get(ht, &key, &data);
127 if (r < 0)
128 string = NULL;
129 else
130 string = data.data;
131
132 if (string != NULL) {
133 chash_delete(ht, &key, NULL);
134 if (chash_count(ht) == 0) {
135 chash_free(ht);
136 mmapstring_hashtable = NULL;
137 }
138 }
139
140 pthread_mutex_unlock(&mmapstring_lock);
141
142 if (string != NULL) {
143 mmap_string_free(string);
144 return 0;
145 }
146 else
147 return -1;
148 }
149
150
151
152 /* MMAPString */
153
154 #define MY_MAXSIZE ((size_t) -1)
155
156 static inline size_t
nearest_power(size_t base,size_t num)157 nearest_power (size_t base, size_t num)
158 {
159 if (num > MY_MAXSIZE / 2) {
160 return MY_MAXSIZE;
161 }
162 else {
163 size_t n = base;
164
165 while (n < num)
166 n <<= 1;
167
168 return n;
169 }
170 }
171
mmap_string_set_ceil(size_t ceil)172 void mmap_string_set_ceil(size_t ceil)
173 {
174 mmap_string_ceil = ceil;
175 }
176
177 /* Strings.
178 */
179
mmap_string_realloc_file(MMAPString * string)180 static MMAPString * mmap_string_realloc_file(MMAPString * string)
181 {
182 char * data;
183
184 if (string->fd == -1) {
185 char tmpfilename[PATH_MAX];
186 int fd;
187
188 * tmpfilename = 0;
189 strcat(tmpfilename, tmpdir);
190 strcat(tmpfilename, "/libetpan-mmapstring-XXXXXX");
191
192 fd = g_mkstemp(tmpfilename);
193 if (fd == -1)
194 return NULL;
195
196 if (unlink(tmpfilename) == -1) {
197 close(fd);
198 return NULL;
199 }
200
201 if (ftruncate(fd, string->allocated_len) == -1) {
202 close(fd);
203 return NULL;
204 }
205
206 data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
207 MAP_SHARED, fd, 0);
208
209 if (data == MAP_FAILED) {
210 close(fd);
211 return NULL;
212 }
213
214 if (string->str != NULL)
215 memcpy(data, string->str, string->len + 1);
216
217 string->fd = fd;
218 string->mmapped_size = string->allocated_len;
219 free(string->str);
220 string->str = data;
221 }
222 else {
223 if (munmap(string->str, string->mmapped_size) == -1)
224 return NULL;
225
226 if (ftruncate(string->fd, string->allocated_len) == -1)
227 return NULL;
228
229 data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
230 MAP_SHARED, string->fd, 0);
231
232 if (data == MAP_FAILED)
233 return NULL;
234
235 string->mmapped_size = string->allocated_len;
236 string->str = data;
237 }
238
239 return string;
240 }
241
mmap_string_realloc_memory(MMAPString * string)242 static MMAPString * mmap_string_realloc_memory(MMAPString * string)
243 {
244 char * tmp;
245
246 tmp = realloc (string->str, string->allocated_len);
247
248 if (tmp == NULL)
249 string = NULL;
250 else
251 string->str = tmp;
252
253 return string;
254 }
255
256 static MMAPString *
mmap_string_maybe_expand(MMAPString * string,size_t len)257 mmap_string_maybe_expand (MMAPString* string,
258 size_t len)
259 {
260 if (string->len + len >= string->allocated_len)
261 {
262 size_t old_size;
263 MMAPString * newstring;
264
265 old_size = string->allocated_len;
266
267 string->allocated_len = nearest_power (1, string->len + len + 1);
268
269 #ifndef MMAP_UNAVAILABLE
270 if (string->allocated_len > mmap_string_ceil)
271 newstring = mmap_string_realloc_file(string);
272 else {
273 #endif
274 newstring = mmap_string_realloc_memory(string);
275 #ifndef MMAP_UNAVAILABLE
276 if (newstring == NULL)
277 newstring = mmap_string_realloc_file(string);
278 }
279 #endif
280
281 if (newstring == NULL)
282 string->allocated_len = old_size;
283 }
284
285 return string;
286 }
287
288 MMAPString*
mmap_string_sized_new(size_t dfl_size)289 mmap_string_sized_new (size_t dfl_size)
290 {
291 MMAPString *string;
292
293 string = malloc(sizeof(* string));
294 if (string == NULL)
295 return NULL;
296
297 string->allocated_len = 0;
298 string->len = 0;
299 string->str = NULL;
300 string->fd = -1;
301 string->mmapped_size = 0;
302
303 if (mmap_string_maybe_expand (string, MAX (dfl_size, 2)) == NULL) {
304 free(string);
305 return NULL;
306 }
307
308 if (string->str == NULL) {
309 free(string);
310 return NULL;
311 }
312
313 string->str[0] = '\0';
314
315 return string;
316 }
317
318 MMAPString*
mmap_string_new(const char * init)319 mmap_string_new (const char *init)
320 {
321 MMAPString *string;
322
323 string = mmap_string_sized_new (init ? strlen (init) + 2 : 2);
324 if (string == NULL)
325 return NULL;
326
327 if (init)
328 mmap_string_append (string, init);
329
330 return string;
331 }
332
333 MMAPString*
mmap_string_new_len(const char * init,size_t len)334 mmap_string_new_len (const char *init,
335 size_t len)
336 {
337 MMAPString *string;
338
339 if (len <= 0)
340 return mmap_string_new (init);
341 else
342 {
343 string = mmap_string_sized_new (len);
344
345 if (init)
346 mmap_string_append_len (string, init, len);
347
348 return string;
349 }
350 }
351
352 void
mmap_string_free(MMAPString * string)353 mmap_string_free (MMAPString *string)
354 {
355 if (string == NULL)
356 return;
357
358 if (string->fd != -1) {
359 munmap(string->str, string->mmapped_size);
360 close(string->fd);
361 }
362 else {
363 free (string->str);
364 }
365 free(string);
366 }
367
368 MMAPString*
mmap_string_assign(MMAPString * string,const char * rval)369 mmap_string_assign (MMAPString *string,
370 const char *rval)
371 {
372 mmap_string_truncate (string, 0);
373 if (mmap_string_append (string, rval) == NULL)
374 return NULL;
375
376 return string;
377 }
378
379 MMAPString*
mmap_string_truncate(MMAPString * string,size_t len)380 mmap_string_truncate (MMAPString *string,
381 size_t len)
382 {
383 string->len = MIN (len, string->len);
384 string->str[string->len] = 0;
385
386 return string;
387 }
388
389 /**
390 * mmap_string_set_size:
391 * @string: a #MMAPString
392 * @len: the new length
393 *
394 * Sets the length of a #MMAPString. If the length is less than
395 * the current length, the string will be truncated. If the
396 * length is greater than the current length, the contents
397 * of the newly added area are undefined. (However, as
398 * always, string->str[string->len] will be a nul byte.)
399 *
400 * Return value: @string
401 **/
402 MMAPString*
mmap_string_set_size(MMAPString * string,size_t len)403 mmap_string_set_size (MMAPString *string,
404 size_t len)
405 {
406 if (len >= string->allocated_len)
407 if (mmap_string_maybe_expand (string, len - string->len) == NULL)
408 return NULL;
409
410 string->len = len;
411 string->str[len] = 0;
412
413 return string;
414 }
415
416 /*
417 static int in_mapped_zone(MMAPString * string, char * val)
418 {
419 return (val >= string->str) && (val < string->str + string->mmapped_size);
420 }
421 */
422
423 MMAPString*
mmap_string_insert_len(MMAPString * string,size_t pos,const char * val,size_t len)424 mmap_string_insert_len (MMAPString *string,
425 size_t pos,
426 const char *val,
427 size_t len)
428 {
429 if (mmap_string_maybe_expand (string, len) == NULL)
430 return NULL;
431
432 if (pos < string->len)
433 memmove (string->str + pos + len, string->str + pos, string->len - pos);
434
435 /* insert the new string */
436 memmove (string->str + pos, val, len);
437
438 string->len += len;
439
440 string->str[string->len] = 0;
441
442 return string;
443 }
444
445 MMAPString*
mmap_string_append(MMAPString * string,const char * val)446 mmap_string_append (MMAPString *string,
447 const char *val)
448 {
449 return mmap_string_insert_len (string, string->len, val, strlen(val));
450 }
451
452 MMAPString*
mmap_string_append_len(MMAPString * string,const char * val,size_t len)453 mmap_string_append_len (MMAPString *string,
454 const char *val,
455 size_t len)
456 {
457 return mmap_string_insert_len (string, string->len, val, len);
458 }
459
460 MMAPString*
mmap_string_append_c(MMAPString * string,char c)461 mmap_string_append_c (MMAPString *string,
462 char c)
463 {
464 return mmap_string_insert_c (string, string->len, c);
465 }
466
467 MMAPString*
mmap_string_prepend(MMAPString * string,const char * val)468 mmap_string_prepend (MMAPString *string,
469 const char *val)
470 {
471 return mmap_string_insert_len (string, 0, val, strlen(val));
472 }
473
474 MMAPString*
mmap_string_prepend_len(MMAPString * string,const char * val,size_t len)475 mmap_string_prepend_len (MMAPString *string,
476 const char *val,
477 size_t len)
478 {
479 return mmap_string_insert_len (string, 0, val, len);
480 }
481
482 MMAPString*
mmap_string_prepend_c(MMAPString * string,char c)483 mmap_string_prepend_c (MMAPString *string,
484 char c)
485 {
486 return mmap_string_insert_c (string, 0, c);
487 }
488
489 MMAPString*
mmap_string_insert(MMAPString * string,size_t pos,const char * val)490 mmap_string_insert (MMAPString *string,
491 size_t pos,
492 const char *val)
493 {
494 return mmap_string_insert_len (string, pos, val, strlen(val));
495 }
496
497 MMAPString*
mmap_string_insert_c(MMAPString * string,size_t pos,char c)498 mmap_string_insert_c (MMAPString *string,
499 size_t pos,
500 char c)
501 {
502 if (mmap_string_maybe_expand (string, 1) == NULL)
503 return NULL;
504
505 /* If not just an append, move the old stuff */
506 if (pos < string->len)
507 memmove (string->str + pos + 1, string->str + pos, string->len - pos);
508
509 string->str[pos] = c;
510
511 string->len += 1;
512
513 string->str[string->len] = 0;
514
515 return string;
516 }
517
518 MMAPString*
mmap_string_erase(MMAPString * string,size_t pos,size_t len)519 mmap_string_erase (MMAPString *string,
520 size_t pos,
521 size_t len)
522 {
523 if ((pos + len) < string->len)
524 memmove (string->str + pos, string->str + pos + len,
525 string->len - (pos + len));
526
527 string->len -= len;
528
529 string->str[string->len] = 0;
530
531 return string;
532 }
533