1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
27 #include <sys/types.h>
29 #ifdef HAVE_SYS_MMAN_H
36 #include "gmime-stream-mmap.h"
40 * SECTION: gmime-stream-mmap
41 * @title: GMimeStreamMmap
42 * @short_description: A memory-mapped file stream
43 * @see_also: #GMimeStream
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.
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);
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);
68 static GMimeStreamClass *parent_class = NULL;
72 g_mime_stream_mmap_get_type (void)
74 static GType type = 0;
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),
86 (GInstanceInitFunc) g_mime_stream_mmap_init,
89 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamMmap", &info, 0);
97 g_mime_stream_mmap_class_init (GMimeStreamMmapClass *klass)
99 GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
100 GObjectClass *object_class = G_OBJECT_CLASS (klass);
102 parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
104 object_class->finalize = g_mime_stream_mmap_finalize;
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;
119 g_mime_stream_mmap_init (GMimeStreamMmap *stream, GMimeStreamMmapClass *klass)
121 stream->owner = TRUE;
129 g_mime_stream_mmap_finalize (GObject *object)
131 GMimeStreamMmap *stream = (GMimeStreamMmap *) object;
136 munmap (stream->map, stream->maplen);
139 if (stream->fd != -1)
143 G_OBJECT_CLASS (parent_class)->finalize (object);
148 stream_read (GMimeStream *stream, char *buf, size_t len)
150 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
151 register char *mapptr;
154 if (mstream->fd == -1) {
159 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
164 /* make sure we are at the right position */
165 mapptr = mstream->map + stream->position;
167 if (stream->bound_end == -1)
168 nread = MIN ((gint64) ((mstream->map + mstream->maplen) - mapptr), (gint64) len);
170 nread = MIN (stream->bound_end - stream->position, (gint64) len);
173 memcpy (buf, mapptr, nread);
174 stream->position += nread;
182 stream_write (GMimeStream *stream, const char *buf, size_t len)
184 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
185 register char *mapptr;
188 if (mstream->fd == -1) {
193 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
198 /* make sure we are at the right position */
199 mapptr = mstream->map + stream->position;
201 if (stream->bound_end == -1)
202 nwritten = MIN ((gint64) ((mstream->map + mstream->maplen) - mapptr), (gint64) len);
204 nwritten = MIN (stream->bound_end - stream->position, (gint64) len);
207 memcpy (mapptr, buf, nwritten);
208 stream->position += nwritten;
215 stream_flush (GMimeStream *stream)
217 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
219 if (mstream->fd == -1) {
225 return msync (mstream->map, mstream->maplen, MS_SYNC /* | MS_INVALIDATE */);
232 stream_close (GMimeStream *stream)
234 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
237 if (mstream->owner && mstream->map) {
239 munmap (mstream->map, mstream->maplen);
244 if (mstream->owner && mstream->fd != -1) {
245 if ((ret = close (mstream->fd)) != -1)
253 stream_eos (GMimeStream *stream)
255 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
257 if (mstream->fd == -1)
264 stream_reset (GMimeStream *stream)
266 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
268 if (mstream->fd == -1) {
273 mstream->eos = FALSE;
279 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
281 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
282 gint64 real = stream->position;
284 if (mstream->fd == -1) {
290 case GMIME_STREAM_SEEK_SET:
293 case GMIME_STREAM_SEEK_CUR:
294 real = stream->position + offset;
296 case GMIME_STREAM_SEEK_END:
297 if (stream->bound_end == -1) {
298 real = offset <= 0 ? stream->bound_start + (gint64) mstream->maplen + offset : -1;
300 if (real < stream->bound_start)
301 real = stream->bound_start;
302 stream->position = real;
307 real = stream->bound_end + offset;
311 /* sanity check the resultant offset */
312 if (real < stream->bound_start) {
317 if (stream->bound_end != -1 && real > stream->bound_end) {
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;
327 stream->position = real;
333 stream_tell (GMimeStream *stream)
335 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
337 if (mstream->fd == -1) {
342 return stream->position;
346 stream_length (GMimeStream *stream)
348 GMimeStreamMmap *mstream = (GMimeStreamMmap *) stream;
350 if (mstream->fd == -1) {
355 if (stream->bound_start != -1 && stream->bound_end != -1)
356 return stream->bound_end - stream->bound_start;
358 return mstream->maplen - stream->bound_start;
362 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
364 /* FIXME: maybe we should return a GMimeStreamFs? */
365 GMimeStreamMmap *mstream;
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;
372 mstream->maplen = GMIME_STREAM_MMAP (stream)->maplen;
373 mstream->map = GMIME_STREAM_MMAP (stream)->map;
375 return (GMimeStream *) mstream;
380 * g_mime_stream_mmap_new:
381 * @fd: file descriptor
382 * @prot: protection flags
385 * Creates a new #GMimeStreamMmap object around @fd.
387 * Returns: a stream using @fd.
390 g_mime_stream_mmap_new (int fd, int prot, int flags)
393 GMimeStreamMmap *mstream;
398 if ((start = lseek (fd, 0, SEEK_CUR)) == -1)
401 if (fstat (fd, &st) == -1)
404 map = mmap (NULL, st.st_size, prot, flags, fd, 0);
405 if (map == MAP_FAILED)
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;
414 mstream->maplen = st.st_size;
416 return (GMimeStream *) mstream;
419 #endif /* HAVE_MMAP */
424 * g_mime_stream_mmap_new_with_bounds:
425 * @fd: file descriptor
426 * @prot: protection flags
428 * @start: start boundary
431 * Creates a new #GMimeStreamMmap object around @fd with bounds @start
434 * Returns: a stream using @fd with bounds @start and @end.
437 g_mime_stream_mmap_new_with_bounds (int fd, int prot, int flags, gint64 start, gint64 end)
440 GMimeStreamMmap *mstream;
446 if (fstat (fd, &st) == -1)
453 if ((map = mmap (NULL, len, prot, flags, fd, 0)) == MAP_FAILED)
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;
462 mstream->maplen = len;
464 return (GMimeStream *) mstream;
467 #endif /* HAVE_MMAP */