Do not redefine MIN and MAX
[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 <stdlib.h>
41 #include <unistd.h>
42 #include <sys/mman.h>
43 #include <string.h>
44 #include <pthread.h>
45 #include <limits.h>
46
47 #include "utils.h"
48
49 #define MMAP_STRING_DEFAULT_CEIL (8 * 1024 * 1024)
50
51 #define DEFAULT_TMP_PATH "/tmp"
52
53 static char tmpdir[PATH_MAX] = DEFAULT_TMP_PATH;
54
55 static size_t mmap_string_ceil = MMAP_STRING_DEFAULT_CEIL;
56
57 /* MMAPString references */
58
59 static pthread_mutex_t mmapstring_lock = PTHREAD_MUTEX_INITIALIZER;
60 static chash * mmapstring_hashtable = NULL;
61
62 static void mmapstring_hashtable_init()
63 {
64   mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
65 }
66
67 void mmap_string_set_tmpdir(char * directory)
68 {
69   strncpy(tmpdir, directory, PATH_MAX);
70   tmpdir[PATH_MAX - 1] = 0;
71 }
72
73
74 int mmap_string_ref(MMAPString * string)
75 {
76   chash * ht;
77   int r;
78   chashdatum key;
79   chashdatum data;
80   
81   pthread_mutex_lock(&mmapstring_lock);
82   if (mmapstring_hashtable == NULL) {
83     mmapstring_hashtable_init();
84   }
85   ht = mmapstring_hashtable;
86   
87   if (ht == NULL) {
88     pthread_mutex_unlock(&mmapstring_lock);
89     return -1;
90   }
91   
92   key.data = &string->str;
93   key.len = sizeof(string->str);
94   data.data = string;
95   data.len = 0;
96   
97   r = chash_set(mmapstring_hashtable, &key, &data, NULL);
98   pthread_mutex_unlock(&mmapstring_lock);
99   
100   if (r < 0)
101     return r;
102
103   return 0;
104 }
105
106 int mmap_string_unref(char * str)
107 {
108   MMAPString * string;
109   chash * ht;
110   chashdatum key;
111   chashdatum data;
112   int r;
113
114   pthread_mutex_lock(&mmapstring_lock);
115   ht = mmapstring_hashtable;
116   
117   if (ht == NULL) {
118     pthread_mutex_unlock(&mmapstring_lock);
119     return -1;
120   }
121   
122   key.data = &str;
123   key.len = sizeof(str);
124
125   r = chash_get(ht, &key, &data);
126   if (r < 0)
127     string = NULL;
128   else
129     string = data.data;
130   
131   if (string != NULL) {
132     chash_delete(ht, &key, NULL);
133     if (chash_count(ht) == 0) {
134       chash_free(ht);
135       mmapstring_hashtable = NULL;
136     }
137   }
138   
139   pthread_mutex_unlock(&mmapstring_lock);
140
141   if (string != NULL) {
142     mmap_string_free(string);
143     return 0;
144   }
145   else
146     return -1;
147 }
148
149
150
151 /* MMAPString */
152
153 #define MY_MAXSIZE ((size_t) -1)
154
155 static inline size_t
156 nearest_power (size_t base, size_t num)    
157 {
158   if (num > MY_MAXSIZE / 2) {
159     return MY_MAXSIZE;
160   }
161   else {
162     size_t n = base;
163     
164     while (n < num)
165       n <<= 1;
166     
167     return n;
168   }
169 }
170
171 void mmap_string_set_ceil(size_t ceil)
172 {
173   mmap_string_ceil = ceil;
174 }
175
176 /* Strings.
177  */
178
179 static MMAPString * mmap_string_realloc_file(MMAPString * string)
180 {
181   char * data;
182
183   if (string->fd == -1) {
184     char tmpfilename[PATH_MAX];
185     int fd;
186
187     * tmpfilename = 0;
188     strcat(tmpfilename, tmpdir);
189     strcat(tmpfilename, "/libetpan-mmapstring-XXXXXX");
190
191     fd = mkstemp(tmpfilename);
192     if (fd == -1)
193       return NULL;
194
195     if (unlink(tmpfilename) == -1) {
196       close(fd);
197       return NULL;
198     }
199
200     if (ftruncate(fd, string->allocated_len) == -1) {
201       close(fd);
202       return NULL;
203     }
204
205     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
206                 MAP_SHARED, fd, 0);
207
208     if (data == MAP_FAILED) {
209       close(fd);
210       return NULL;
211     }
212
213     if (string->str != NULL)
214       memcpy(data, string->str, string->len + 1);
215
216     string->fd = fd;
217     string->mmapped_size = string->allocated_len;
218     free(string->str);
219     string->str = data;
220   }
221   else {
222     if (munmap(string->str, string->mmapped_size) == -1)
223       return NULL;
224
225     if (ftruncate(string->fd, string->allocated_len) == -1)
226       return NULL;
227     
228     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
229                 MAP_SHARED, string->fd, 0);
230
231     if (data == MAP_FAILED)
232       return NULL;
233
234     string->mmapped_size = string->allocated_len;
235     string->str = data;
236   }
237   
238   return string;
239 }
240
241 static MMAPString * mmap_string_realloc_memory(MMAPString * string)
242 {
243   char * tmp;
244
245   tmp =  realloc (string->str, string->allocated_len);
246   
247   if (tmp == NULL)
248     string = NULL;
249   else
250     string->str = tmp;
251
252   return string;
253 }
254
255 static MMAPString *
256 mmap_string_maybe_expand (MMAPString* string,
257                           size_t len) 
258 {
259   if (string->len + len >= string->allocated_len)
260     {
261       size_t old_size;
262       MMAPString * newstring;
263
264       old_size = string->allocated_len;
265
266       string->allocated_len = nearest_power (1, string->len + len + 1);
267       
268 #ifndef MMAP_UNAVAILABLE
269       if (string->allocated_len > mmap_string_ceil)
270         newstring = mmap_string_realloc_file(string);
271       else {
272 #endif
273         newstring = mmap_string_realloc_memory(string);
274 #ifndef MMAP_UNAVAILABLE
275         if (newstring == NULL)
276           newstring = mmap_string_realloc_file(string);
277       }
278 #endif
279
280       if (newstring == NULL)
281         string->allocated_len = old_size;
282     }
283
284   return string;
285 }
286
287 MMAPString*
288 mmap_string_sized_new (size_t dfl_size)
289 {
290   MMAPString *string;
291  
292   string = malloc(sizeof(* string));
293   if (string == NULL)
294     return NULL;
295
296   string->allocated_len = 0;
297   string->len   = 0;
298   string->str   = NULL;
299   string->fd    = -1;
300   string->mmapped_size = 0;
301
302   if (mmap_string_maybe_expand (string, MAX (dfl_size, 2)) == NULL)
303     return NULL;
304
305   string->str[0] = 0;
306
307   return string;
308 }
309
310 MMAPString*
311 mmap_string_new (const char *init)
312 {
313   MMAPString *string;
314
315   string = mmap_string_sized_new (init ? strlen (init) + 2 : 2);
316   if (string == NULL)
317     return NULL;
318
319   if (init)
320     mmap_string_append (string, init);
321
322   return string;
323 }
324
325 MMAPString*
326 mmap_string_new_len (const char *init,
327                      size_t len)    
328 {
329   MMAPString *string;
330
331   if (len <= 0)
332     return mmap_string_new (init);
333   else
334     {
335       string = mmap_string_sized_new (len);
336       
337       if (init)
338         mmap_string_append_len (string, init, len);
339       
340       return string;
341     }
342 }
343
344 void
345 mmap_string_free (MMAPString *string)
346 {
347   if (string == NULL)
348     return;
349
350   if (string->fd != -1) {
351     munmap(string->str, string->mmapped_size);
352     close(string->fd);
353   }
354   else {
355     free (string->str);
356   }
357   free(string);
358 }
359
360 MMAPString*
361 mmap_string_assign (MMAPString     *string,
362                     const char *rval)
363 {
364   mmap_string_truncate (string, 0);
365   if (mmap_string_append (string, rval) == NULL)
366     return NULL;
367
368   return string;
369 }
370
371 MMAPString*
372 mmap_string_truncate (MMAPString *string,
373                       size_t    len)    
374 {
375   string->len = MIN (len, string->len);
376   string->str[string->len] = 0;
377
378   return string;
379 }
380
381 /**
382  * mmap_string_set_size:
383  * @string: a #MMAPString
384  * @len: the new length
385  * 
386  * Sets the length of a #MMAPString. If the length is less than
387  * the current length, the string will be truncated. If the
388  * length is greater than the current length, the contents
389  * of the newly added area are undefined. (However, as
390  * always, string->str[string->len] will be a nul byte.) 
391  * 
392  * Return value: @string
393  **/
394 MMAPString*
395 mmap_string_set_size (MMAPString *string,
396                       size_t    len)    
397 {
398   if (len >= string->allocated_len)
399     if (mmap_string_maybe_expand (string, len - string->len) == NULL)
400       return NULL;
401   
402   string->len = len;
403   string->str[len] = 0;
404
405   return string;
406 }
407
408 /*
409 static int in_mapped_zone(MMAPString * string, char * val)
410 {
411   return (val >= string->str) && (val < string->str + string->mmapped_size);
412 }
413 */
414
415 MMAPString*
416 mmap_string_insert_len (MMAPString     *string,
417                         size_t       pos,    
418                         const char *val,
419                         size_t       len)    
420 {
421   if (mmap_string_maybe_expand (string, len) == NULL)
422     return NULL;
423     
424   if (pos < string->len)
425     memmove (string->str + pos + len, string->str + pos, string->len - pos);
426
427   /* insert the new string */
428   memmove (string->str + pos, val, len);
429
430   string->len += len;
431
432   string->str[string->len] = 0;
433
434   return string;
435 }
436
437 MMAPString*
438 mmap_string_append (MMAPString     *string,
439                     const char *val)
440 {  
441   return mmap_string_insert_len (string, string->len, val, strlen(val));
442 }
443
444 MMAPString*
445 mmap_string_append_len (MMAPString       *string,
446                         const char *val,
447                         size_t       len)    
448 {
449   return mmap_string_insert_len (string, string->len, val, len);
450 }
451
452 MMAPString*
453 mmap_string_append_c (MMAPString *string,
454                       char    c)
455 {
456   return mmap_string_insert_c (string, string->len, c);
457 }
458
459 MMAPString*
460 mmap_string_prepend (MMAPString     *string,
461                      const char *val)
462 {
463   return mmap_string_insert_len (string, 0, val, strlen(val));
464 }
465
466 MMAPString*
467 mmap_string_prepend_len (MMAPString       *string,
468                          const char *val,
469                          size_t       len)    
470 {
471   return mmap_string_insert_len (string, 0, val, len);
472 }
473
474 MMAPString*
475 mmap_string_prepend_c (MMAPString *string,
476                        char    c)
477 {  
478   return mmap_string_insert_c (string, 0, c);
479 }
480
481 MMAPString*
482 mmap_string_insert (MMAPString     *string,
483                     size_t       pos,    
484                     const char *val)
485 {
486   return mmap_string_insert_len (string, pos, val, strlen(val));
487 }
488
489 MMAPString*
490 mmap_string_insert_c (MMAPString *string,
491                       size_t   pos,    
492                       char    c)
493 {
494   if (mmap_string_maybe_expand (string, 1) == NULL)
495     return NULL;
496   
497   /* If not just an append, move the old stuff */
498   if (pos < string->len)
499     memmove (string->str + pos + 1, string->str + pos, string->len - pos);
500
501   string->str[pos] = c;
502
503   string->len += 1;
504
505   string->str[string->len] = 0;
506
507   return string;
508 }
509
510 MMAPString*
511 mmap_string_erase (MMAPString *string,
512                    size_t    pos,    
513                    size_t    len)    
514 {
515   if ((pos + len) < string->len)
516     memmove (string->str + pos, string->str + pos + len,
517              string->len - (pos + len));
518   
519   string->len -= len;
520   
521   string->str[string->len] = 0;
522
523   return string;
524 }