One more leak (introduced in previous commit)
[claws.git] / src / plugins / mailmbox / mmapstring.c
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
63 static void mmapstring_hashtable_init()
64 {
65   mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
66 }
67
68 void mmap_string_set_tmpdir(char * directory)
69 {
70   strncpy(tmpdir, directory, PATH_MAX);
71   tmpdir[PATH_MAX - 1] = 0;
72 }
73
74
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
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
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
172 void mmap_string_set_ceil(size_t ceil)
173 {
174   mmap_string_ceil = ceil;
175 }
176
177 /* Strings.
178  */
179
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
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 *
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*
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*
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*
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
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*
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*
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*
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*
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*
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*
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*
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*
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*
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*
483 mmap_string_prepend_c (MMAPString *string,
484                        char    c)
485 {  
486   return mmap_string_insert_c (string, 0, c);
487 }
488
489 MMAPString*
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*
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*
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 }