1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gsf-config.h>
24 #include <gsf/gsf-input-memory.h>
25 #include <gsf/gsf-input-impl.h>
26 #include <gsf/gsf-impl-utils.h>
27 #include <gsf/gsf-utils.h>
28 #include <gsf/gsf-shared-memory.h>
29 #include <glib/gstdio.h>
33 #if defined(FREEBSD) || defined(__FreeBSD__)
34 /* We must keep the file open while pages are mapped. */
35 /* http://www.freebsd.org/cgi/query-pr.cgi?pr=48291 */
36 #define HAVE_BROKEN_MMAP
37 #endif /* defined(FREEBSD) || defined(__FreeBSD__) */
39 #elif defined(G_OS_WIN32)
42 #include <sys/types.h>
48 #define MAP_FAILED NULL
49 #endif /* HAVE_MMAP */
55 static GObjectClass *parent_class;
57 struct _GsfInputMemory {
59 GsfSharedMemory *shared;
60 #ifdef HAVE_BROKEN_MMAP
64 typedef GsfInputClass GsfInputMemoryClass;
67 * gsf_input_memory_new:
68 * @buf: The input bytes
69 * @length: The length of @buf
70 * @needs_free: Whether you want this memory to be free'd at object destruction
72 * Returns: A new #GsfInputMemory
75 gsf_input_memory_new (guint8 const *buf, gsf_off_t length, gboolean needs_free)
77 GsfInputMemory *mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
78 if (G_UNLIKELY (NULL == mem)) return NULL;
80 mem->shared = gsf_shared_memory_new ((void *)buf, length, needs_free);
81 gsf_input_set_size (GSF_INPUT (mem), length);
82 return GSF_INPUT (mem);
86 * gsf_input_memory_new_clone:
87 * @buf: The input bytes
88 * @length: The length of @buf
90 * Returns: A new #GsfInputMemory
93 gsf_input_memory_new_clone (guint8 const *buf, gsf_off_t length)
95 GsfInputMemory *mem = NULL;
98 g_return_val_if_fail (buf != NULL || length == 0, NULL);
99 g_return_val_if_fail (length >= 0, NULL);
101 mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
102 if (G_UNLIKELY (NULL == mem)) return NULL;
104 cpy = g_try_malloc (MAX (1, length) * sizeof (guint8));
106 g_object_unref (mem);
109 memcpy (cpy, buf, length);
110 mem->shared = gsf_shared_memory_new (cpy, length, TRUE);
111 gsf_input_set_size (GSF_INPUT (mem), length);
112 return GSF_INPUT (mem);
116 gsf_input_memory_finalize (GObject *obj)
118 GsfInputMemory *mem = (GsfInputMemory *) (obj);
121 g_object_unref (G_OBJECT (mem->shared));
123 #ifdef HAVE_BROKEN_MMAP
128 parent_class->finalize (obj);
132 gsf_input_memory_dup (GsfInput *src_input, G_GNUC_UNUSED GError **err)
134 GsfInputMemory const *src = (GsfInputMemory *) (src_input);
135 GsfInputMemory *dst = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
136 if (G_UNLIKELY (NULL == dst)) return NULL;
138 dst->shared = src->shared;
139 g_object_ref (G_OBJECT (dst->shared));
140 gsf_input_set_size (GSF_INPUT (dst), src->shared->size);
142 #ifdef HAVE_BROKEN_MMAP
144 dst->fd = dup (src->fd);
147 return GSF_INPUT (dst);
150 static guint8 const *
151 gsf_input_memory_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
153 GsfInputMemory *mem = (GsfInputMemory *) (input);
154 guchar const *src = mem->shared->buf;
158 if (optional_buffer) {
159 memcpy (optional_buffer, src + input->cur_offset, num_bytes);
160 return optional_buffer;
162 return src + input->cur_offset;
166 gsf_input_memory_seek (G_GNUC_UNUSED GsfInput *input,
167 G_GNUC_UNUSED gsf_off_t offset,
168 G_GNUC_UNUSED GSeekType whence)
174 gsf_input_memory_init (GObject *obj)
176 GsfInputMemory *mem = (GsfInputMemory *) (obj);
178 #ifdef HAVE_BROKEN_MMAP
184 gsf_input_memory_class_init (GObjectClass *gobject_class)
186 GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
188 gobject_class->finalize = gsf_input_memory_finalize;
189 input_class->Dup = gsf_input_memory_dup;
190 input_class->Read = gsf_input_memory_read;
191 input_class->Seek = gsf_input_memory_seek;
193 parent_class = g_type_class_peek_parent (gobject_class);
196 GSF_CLASS (GsfInputMemory, gsf_input_memory,
197 gsf_input_memory_class_init, gsf_input_memory_init,
200 /***************************************************************************/
205 #include <sys/mman.h>
206 #include <sys/types.h>
207 #include <sys/stat.h>
212 #define PROT_READ 0x1
213 #endif /* PROT_READ */
215 #if !defined(MAP_FAILED) || defined(__osf__)
216 /* Someone needs their head examined - BSD ? */
217 # define MAP_FAILED ((void *)-1)
218 #endif /* !defined(MAP_FAILED) || defined(__osf__) */
220 #endif /* HAVE_MMAP */
223 * gsf_input_mmap_new:
224 * @filename: The file on disk that you want to mmap
225 * @err: A #GError, or optionally %null
227 * Returns: A new #GsfInputMemory
230 gsf_input_mmap_new (char const *filename, GError **err)
232 #if defined(HAVE_MMAP) || defined(G_OS_WIN32)
239 mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
240 if (G_UNLIKELY (NULL == mem)) return NULL;
242 fd = g_open (filename, O_RDONLY | O_BINARY, 0);
243 if (fd < 0 || fstat (fd, &st) < 0) {
245 int save_errno = errno;
246 char *utf8name = g_filename_display_name (filename);
247 *err = g_error_new (gsf_input_error_id (), 0,
249 utf8name, g_strerror (save_errno));
252 if (fd >= 0) close (fd);
256 if (!S_ISREG (st.st_mode)) {
258 char *utf8name = g_filename_display_name (filename);
259 *err = g_error_new (gsf_input_error_id (), 0,
260 "%s: Is not a regular file",
268 size = (size_t) st.st_size;
269 if ((off_t) size != st.st_size) { /* Check for overflow */
271 char *utf8name = g_filename_display_name (filename);
272 *err = g_error_new (gsf_input_error_id (), 0,
273 "%s: File too large to be memory mapped",
283 HANDLE handle = CreateFileMapping ((HANDLE)_get_osfhandle (fd), NULL, PAGE_READONLY, 0, 0, NULL);
284 buf = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, 0);
285 CloseHandle (handle);
288 buf = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
291 if (buf == MAP_FAILED) {
293 int save_errno = errno;
294 char *utf8name = g_filename_display_name (filename);
295 *err = g_error_new (gsf_input_error_id (), 0,
297 utf8name, g_strerror (save_errno));
304 mem->shared = gsf_shared_memory_mmapped_new (buf, (gsf_off_t) size);
305 gsf_input_set_size (GSF_INPUT (mem), (gsf_off_t) size);
306 gsf_input_set_name (GSF_INPUT (mem), filename);
308 #ifdef HAVE_BROKEN_MMAP
314 return GSF_INPUT (mem);
318 *err = g_error_new (gsf_input_error_id (), 0,
319 "mmap not supported");