Internally use the _stati64 API explicitly on Windows
[platform/upstream/glib.git] / glib / gmappedfile.c
index 33bbd1f..59a1a3e 100644 (file)
 #include <sys/mman.h>
 #endif
 
+#include "glibconfig.h"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <io.h>
+
+#define fstat(a,b) _fstati64(a,b)
+#define stat _stati64
+
+#endif
 
 #include "gconvert.h"
 #include "gerror.h"
 #include "gmessages.h"
 #include "gstdio.h"
 #include "gstrfuncs.h"
+#include "gatomic.h"
 
 #include "glibintl.h"
 
 #include "galias.h"
 
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
 #ifndef MAP_FAILED
 #define MAP_FAILED ((void *) -1)
 #endif
@@ -54,12 +69,16 @@ struct _GMappedFile
 {
   gsize  length;
   gchar *contents;
+  int    ref_count;
+#ifdef G_OS_WIN32
+  HANDLE mapping;
+#endif
 };
 
 /**
  * g_mapped_file_new:
  * @filename: The path of the file to load, in the GLib filename encoding
- * @writable: wether the mapping should be writable
+ * @writable: whether the mapping should be writable
  * @error: return location for a #GError, or %NULL
  *
  * Maps a file into memory. On UNIX, this is using the mmap() function.
@@ -74,8 +93,8 @@ struct _GMappedFile
  * will not be modified, or if all modifications of the file are done
  * atomically (e.g. using g_file_set_contents()). 
  *
- * Return value: a newly allocated #GMappedFile which must be freed
- *    with g_mapped_file_free(), or %NULL if the mapping failed. 
+ * Return value: a newly allocated #GMappedFile which must be unref'd
+ *    with g_mapped_file_unref(), or %NULL if the mapping failed.
  *
  * Since: 2.8
  */
@@ -91,7 +110,7 @@ g_mapped_file_new (const gchar  *filename,
   g_return_val_if_fail (filename != NULL, NULL);
   g_return_val_if_fail (!error || *error == NULL, NULL);
 
-  fd = g_open (filename, writable ? O_RDWR : O_RDONLY);
+  fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
   if (fd == -1)
     {
       int save_errno = errno;
@@ -107,7 +126,8 @@ g_mapped_file_new (const gchar  *filename,
       return NULL;
     }
 
-  file = g_new0 (GMappedFile, 1);
+  file = g_slice_new0 (GMappedFile);
+  file->ref_count = 1;
 
   if (fstat (fd, &st) == -1)
     {
@@ -124,14 +144,50 @@ g_mapped_file_new (const gchar  *filename,
       goto out;
     }
 
+  if (st.st_size == 0)
+    {
+      file->length = 0;
+      file->contents = NULL;
+      close (fd);
+      return file;
+    }
+
   file->contents = MAP_FAILED;
 
 #ifdef HAVE_MMAP
+  if (st.st_size > G_MAXSIZE)
+    {
+      errno = EINVAL;
+    }
+  else
+    {      
+      file->length = (gsize) st.st_size;
+      file->contents = (gchar *) mmap (NULL,  file->length,
+                                      writable ? PROT_READ|PROT_WRITE : PROT_READ,
+                                      MAP_PRIVATE, fd, 0);
+    }
+#endif
+#ifdef G_OS_WIN32
   file->length = st.st_size;
-  file->contents = (gchar *) mmap (NULL, st.st_size,
-                                  writable ? PROT_READ|PROT_WRITE : PROT_READ,
-                                  MAP_PRIVATE, fd, 0);
+  file->mapping = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL,
+                                    writable ? PAGE_WRITECOPY : PAGE_READONLY,
+                                    0, 0,
+                                    NULL);
+  if (file->mapping != NULL)
+    {
+      file->contents = MapViewOfFile (file->mapping,
+                                     writable ? FILE_MAP_COPY : FILE_MAP_READ,
+                                     0, 0,
+                                     0);
+      if (file->contents == NULL)
+       {
+         file->contents = MAP_FAILED;
+         CloseHandle (file->mapping);
+         file->mapping = NULL;
+       }
+    }
 #endif
+
   
   if (file->contents == MAP_FAILED)
     {
@@ -153,7 +209,7 @@ g_mapped_file_new (const gchar  *filename,
 
  out:
   close (fd);
-  g_free (file);
+  g_slice_free (GMappedFile, file);
 
   return NULL;
 }
@@ -185,7 +241,9 @@ g_mapped_file_get_length (GMappedFile *file)
  * Note that the contents may not be zero-terminated,
  * even if the #GMappedFile is backed by a text file.
  *
- * Returns: the contents of @file.
+ * If the file is empty then %NULL is returned.
+ *
+ * Returns: the contents of @file, or %NULL.
  *
  * Since: 2.8
  */
@@ -201,23 +259,73 @@ g_mapped_file_get_contents (GMappedFile *file)
  * g_mapped_file_free:
  * @file: a #GMappedFile
  *
- * Unmaps the buffer of @file and frees it. 
- *
- * For writable, shared mappings, the contents
- * will be written back to the file at this point.
+ * This call existed before #GMappedFile had refcounting and is currently
+ * exactly the same as g_mapped_file_unref().
  *
  * Since: 2.8
+ * Deprecated:2.22: Use g_mapped_file_unref() instead.
  */
 void
 g_mapped_file_free (GMappedFile *file)
 {
+  g_mapped_file_unref (file);
+}
+
+/**
+ * g_mapped_file_ref:
+ * @file: a #GMappedFile
+ *
+ * Increments the reference count of @file by one.  It is safe to call
+ * this function from any thread.
+ *
+ * Return value: the passed in #GMappedFile.
+ *
+ * Since: 2.22
+ **/
+GMappedFile *
+g_mapped_file_ref (GMappedFile *file)
+{
+  g_return_val_if_fail (file != NULL, NULL);
+  g_return_val_if_fail (file->ref_count > 0, file);
+
+  g_atomic_int_inc (&file->ref_count);
+
+  return file;
+}
+
+/**
+ * g_mapped_file_unref:
+ * @file: a #GMappedFile
+ *
+ * Decrements the reference count of @file by one.  If the reference count
+ * drops to 0, unmaps the buffer of @file and frees it.
+ *
+ * It is safe to call this function from any thread.
+ *
+ * Since 2.22
+ **/
+void
+g_mapped_file_unref (GMappedFile *file)
+{
   g_return_if_fail (file != NULL);
+  g_return_if_fail (file->ref_count > 0);
 
+  if (g_atomic_int_dec_and_test (&file->ref_count))
+    {
+      if (file->length)
+        {
 #ifdef HAVE_MMAP
-  munmap (file->contents, file->length);
+          munmap (file->contents, file->length);
 #endif
-}
+#ifdef G_OS_WIN32
+          UnmapViewOfFile (file->contents);
+          CloseHandle (file->mapping);
+#endif
+        }
 
+      g_slice_free (GMappedFile, file);
+    }
+}
 
 #define __G_MAPPED_FILE_C__
 #include "galiasdef.c"