Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-stream-mmap.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #ifdef HAVE_SYS_MMAN_H
30 #include <sys/mman.h>
31 #endif
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35
36 #include "gmime-stream-mmap.h"
37
38
39 /**
40  * SECTION: gmime-stream-mmap
41  * @title: GMimeStreamMmap
42  * @short_description: A memory-mapped file stream
43  * @see_also: #GMimeStream
44  *
45  * A #GMimeStream implementation using a memory-mapped file backing
46  * store. This may be faster than #GMimeStreamFs or #GMimeStreamFile
47  * but you'll have to do your own performance checking to be sure for
48  * your particular application/platform.
49  **/
50
51
52 static void g_mime_stream_mmap_class_init (GMimeStreamMmapClass *klass);
53 static void g_mime_stream_mmap_init (GMimeStreamMmap *stream, GMimeStreamMmapClass *klass);
54 static void g_mime_stream_mmap_finalize (GObject *object);
55
56 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
57 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
58 static int stream_flush (GMimeStream *stream);
59 static int stream_close (GMimeStream *stream);
60 static gboolean stream_eos (GMimeStream *stream);
61 static int stream_reset (GMimeStream *stream);
62 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
63 static gint64 stream_tell (GMimeStream *stream);
64 static gint64 stream_length (GMimeStream *stream);
65 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
66
67
68 static GMimeStreamClass *parent_class = NULL;
69
70
71 GType
72 g_mime_stream_mmap_get_type (void)
73 {
74         static GType type = 0;
75         
76         if (!type) {
77                 static const GTypeInfo info = {
78                         sizeof (GMimeStreamMmapClass),
79                         NULL, /* base_class_init */
80                         NULL, /* base_class_finalize */
81                         (GClassInitFunc) g_mime_stream_mmap_class_init,
82                         NULL, /* class_finalize */
83                         NULL, /* class_data */
84                         sizeof (GMimeStreamMmap),
85                         0,    /* n_preallocs */
86                         (GInstanceInitFunc) g_mime_stream_mmap_init,
87                 };
88                 
89                 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamMmap", &info, 0);
90         }
91         
92         return type;
93 }
94
95
96 static void
97 g_mime_stream_mmap_class_init (GMimeStreamMmapClass *klass)
98 {
99         GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
100         GObjectClass *object_class = G_OBJECT_CLASS (klass);
101         
102         parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
103         
104         object_class->finalize = g_mime_stream_mmap_finalize;
105         
106         stream_class->read = stream_read;
107         stream_class->write = stream_write;
108         stream_class->flush = stream_flush;
109         stream_class->close = stream_close;
110         stream_class->eos = stream_eos;
111         stream_class->reset = stream_reset;
112         stream_class->seek = stream_seek;
113         stream_class->tell = stream_tell;
114         stream_class->length = stream_length;
115         stream_class->substream = stream_substream;
116 }
117
118 static void
119 g_mime_stream_mmap_init (GMimeStreamMmap *stream, GMimeStreamMmapClass *klass)
120 {
121         stream->owner = TRUE;
122         stream->eos = FALSE;
123         stream->fd = -1;
124         stream->map = NULL;
125         stream->maplen = 0;
126 }
127
128 static void
129 g_mime_stream_mmap_finalize (GObject *object)
130 {
131         GMimeStreamMmap *stream = (GMimeStreamMmap *) object;
132         
133         if (stream->owner) {
134 #ifdef HAVE_MUNMAP
135                 if (stream->map)
136                         munmap (stream->map, stream->maplen);
137 #endif
138                 
139                 if (stream->fd != -1)
140                         close (stream->fd);
141         }
142         
143         G_OBJECT_CLASS (parent_class)->finalize (object);
144 }
145
146
147 static ssize_t
148 stream_read (GMimeStream *stream, char *buf, size_t len)
149 {
150         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
151         register char *mapptr;
152         ssize_t nread;
153         
154         if (mstream->fd == -1) {
155                 errno = EBADF;
156                 return -1;
157         }
158         
159         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
160                 errno = EINVAL;
161                 return -1;
162         }
163         
164         /* make sure we are at the right position */
165         mapptr = mstream->map + stream->position;
166         
167         if (stream->bound_end == -1)
168                 nread = MIN ((gint64) ((mstream->map + mstream->maplen) - mapptr), (gint64) len);
169         else
170                 nread = MIN (stream->bound_end - stream->position, (gint64) len);
171         
172         if (nread > 0) {
173                 memcpy (buf, mapptr, nread);
174                 stream->position += nread;
175         } else
176                 mstream->eos = TRUE;
177         
178         return nread;
179 }
180
181 static ssize_t
182 stream_write (GMimeStream *stream, const char *buf, size_t len)
183 {
184         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
185         register char *mapptr;
186         ssize_t nwritten;
187         
188         if (mstream->fd == -1) {
189                 errno = EBADF;
190                 return -1;
191         }
192         
193         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
194                 errno = EINVAL;
195                 return -1;
196         }
197         
198         /* make sure we are at the right position */
199         mapptr = mstream->map + stream->position;
200         
201         if (stream->bound_end == -1)
202                 nwritten = MIN ((gint64) ((mstream->map + mstream->maplen) - mapptr), (gint64) len);
203         else
204                 nwritten = MIN (stream->bound_end - stream->position, (gint64) len);
205         
206         if (nwritten > 0) {
207                 memcpy (mapptr, buf, nwritten);
208                 stream->position += nwritten;
209         }
210         
211         return nwritten;
212 }
213
214 static int
215 stream_flush (GMimeStream *stream)
216 {
217         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
218         
219         if (mstream->fd == -1) {
220                 errno = EBADF;
221                 return -1;
222         }
223         
224 #ifdef HAVE_MSYNC
225         return msync (mstream->map, mstream->maplen, MS_SYNC /* | MS_INVALIDATE */);
226 #else
227         return 0;
228 #endif
229 }
230
231 static int
232 stream_close (GMimeStream *stream)
233 {
234         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
235         int ret = 0;
236         
237         if (mstream->owner && mstream->map) {
238 #ifdef HAVE_MUNMAP
239                 munmap (mstream->map, mstream->maplen);
240                 mstream->map = NULL;
241 #endif
242         }
243         
244         if (mstream->owner && mstream->fd != -1) {
245                 if ((ret = close (mstream->fd)) != -1)
246                         mstream->fd = -1;
247         }
248         
249         return ret;
250 }
251
252 static gboolean
253 stream_eos (GMimeStream *stream)
254 {
255         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
256         
257         if (mstream->fd == -1)
258                 return TRUE;
259         
260         return mstream->eos;
261 }
262
263 static int
264 stream_reset (GMimeStream *stream)
265 {
266         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
267         
268         if (mstream->fd == -1) {
269                 errno = EBADF;
270                 return -1;
271         }
272         
273         mstream->eos = FALSE;
274         
275         return 0;
276 }
277
278 static gint64
279 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
280 {
281         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
282         gint64 real = stream->position;
283         
284         if (mstream->fd == -1) {
285                 errno = EBADF;
286                 return -1;
287         }
288         
289         switch (whence) {
290         case GMIME_STREAM_SEEK_SET:
291                 real = offset;
292                 break;
293         case GMIME_STREAM_SEEK_CUR:
294                 real = stream->position + offset;
295                 break;
296         case GMIME_STREAM_SEEK_END:
297                 if (stream->bound_end == -1) {
298                         real = offset <= 0 ? stream->bound_start + (gint64) mstream->maplen + offset : -1;
299                         if (real != -1) {
300                                 if (real < stream->bound_start)
301                                         real = stream->bound_start;
302                                 stream->position = real;
303                         }
304                         
305                         return real;
306                 }
307                 real = stream->bound_end + offset;
308                 break;
309         }
310         
311         /* sanity check the resultant offset */
312         if (real < stream->bound_start) {
313                 errno = EINVAL;
314                 return -1;
315         }
316         
317         if (stream->bound_end != -1 && real > stream->bound_end) {
318                 errno = EINVAL;
319                 return -1;
320         }
321         
322         /* reset eos if appropriate */
323         if ((stream->bound_end != -1 && real < stream->bound_end) ||
324             (mstream->eos && real < stream->position))
325                 mstream->eos = FALSE;
326         
327         stream->position = real;
328         
329         return real;
330 }
331
332 static gint64
333 stream_tell (GMimeStream *stream)
334 {
335         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
336         
337         if (mstream->fd == -1) {
338                 errno = EBADF;
339                 return -1;
340         }
341         
342         return stream->position;
343 }
344
345 static gint64
346 stream_length (GMimeStream *stream)
347 {
348         GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
349         
350         if (mstream->fd == -1) {
351                 errno = EBADF;
352                 return -1;
353         }
354         
355         if (stream->bound_start != -1 && stream->bound_end != -1)
356                 return stream->bound_end - stream->bound_start;
357         
358         return mstream->maplen - stream->bound_start;
359 }
360
361 static GMimeStream *
362 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
363 {
364         /* FIXME: maybe we should return a GMimeStreamFs? */
365         GMimeStreamMmap *mstream;
366         
367         mstream = g_object_newv (GMIME_TYPE_STREAM_MMAP, 0, NULL);
368         g_mime_stream_construct (GMIME_STREAM (mstream), start, end);
369         mstream->fd = GMIME_STREAM_MMAP (stream)->fd;
370         mstream->owner = FALSE;
371         
372         mstream->maplen = GMIME_STREAM_MMAP (stream)->maplen;
373         mstream->map = GMIME_STREAM_MMAP (stream)->map;
374         
375         return (GMimeStream *) mstream;
376 }
377
378
379 /**
380  * g_mime_stream_mmap_new:
381  * @fd: file descriptor
382  * @prot: protection flags
383  * @flags: map flags
384  *
385  * Creates a new #GMimeStreamMmap object around @fd.
386  *
387  * Returns: a stream using @fd.
388  **/
389 GMimeStream *
390 g_mime_stream_mmap_new (int fd, int prot, int flags)
391 {
392 #ifdef HAVE_MMAP
393         GMimeStreamMmap *mstream;
394         struct stat st;
395         gint64 start;
396         char *map;
397         
398         if ((start = lseek (fd, 0, SEEK_CUR)) == -1)
399                 return NULL;
400         
401         if (fstat (fd, &st) == -1)
402                 return NULL;
403         
404         map = mmap (NULL, st.st_size, prot, flags, fd, 0);
405         if (map == MAP_FAILED)
406                 return NULL;
407         
408         mstream = g_object_newv (GMIME_TYPE_STREAM_MMAP, 0, NULL);
409         g_mime_stream_construct ((GMimeStream *) mstream, start, -1);
410         mstream->owner = TRUE;
411         mstream->eos = FALSE;
412         mstream->fd = fd;
413         mstream->map = map;
414         mstream->maplen = st.st_size;
415         
416         return (GMimeStream *) mstream;
417 #else
418         return NULL;
419 #endif /* HAVE_MMAP */
420 }
421
422
423 /**
424  * g_mime_stream_mmap_new_with_bounds:
425  * @fd: file descriptor
426  * @prot: protection flags
427  * @flags: map flags
428  * @start: start boundary
429  * @end: end boundary
430  *
431  * Creates a new #GMimeStreamMmap object around @fd with bounds @start
432  * and @end.
433  *
434  * Returns: a stream using @fd with bounds @start and @end.
435  **/
436 GMimeStream *
437 g_mime_stream_mmap_new_with_bounds (int fd, int prot, int flags, gint64 start, gint64 end)
438 {
439 #ifdef HAVE_MMAP
440         GMimeStreamMmap *mstream;
441         struct stat st;
442         size_t len;
443         char *map;
444         
445         if (end == -1) {
446                 if (fstat (fd, &st) == -1)
447                         return NULL;
448                 
449                 len = st.st_size;
450         } else
451                 len = (size_t) end;
452         
453         if ((map = mmap (NULL, len, prot, flags, fd, 0)) == MAP_FAILED)
454                 return NULL;
455         
456         mstream = g_object_newv (GMIME_TYPE_STREAM_MMAP, 0, NULL);
457         g_mime_stream_construct ((GMimeStream *) mstream, start, end);
458         mstream->owner = TRUE;
459         mstream->eos = FALSE;
460         mstream->fd = fd;
461         mstream->map = map;
462         mstream->maplen = len;
463         
464         return (GMimeStream *) mstream;
465 #else
466         return NULL;
467 #endif /* HAVE_MMAP */
468 }