[glib] GBytes: A new type for an immutable set of bytes.



commit fcc69fd318f55ca0edc52864b4c0488b304b8382
Author: Stef Walter <stefw collabora co uk>
Date:   Fri Nov 11 17:08:26 2011 +0100

    GBytes: A new type for an immutable set of bytes.
    
     * Represents an immutable reference counted block of memory.
     * This is basically the internal glib GBuffer structure exposed,
       renamed, and with some additional capabilities.
     * The GBytes name comes from python3's immutable 'bytes' type
     * GBytes can be safely used as keys in hash tables, and have
       functions for doing so: g_bytes_hash, g_bytes_equal
     * GByteArray is a mutable form of GBytes, and vice versa. There
       are functions for converting from one to the other efficiently:
       g_bytes_unref_to_array() and g_byte_array_free_to_bytes()
     * Adds g_byte_array_new_take() to support above functions
    
    https://bugzilla.gnome.org/show_bug.cgi?id=663291

 docs/reference/glib/Makefile.am             |    2 +-
 docs/reference/glib/glib-sections.txt       |   21 ++-
 docs/reference/gobject/gobject-sections.txt |    1 +
 glib/Makefile.am                            |    5 +-
 glib/garray.c                               |   77 ++++-
 glib/garray.h                               |    4 +
 glib/gbuffer.c                              |  210 ------------
 glib/gbufferprivate.h                       |   88 -----
 glib/gbytes.c                               |  478 +++++++++++++++++++++++++++
 glib/gbytes.h                               |   69 ++++
 glib/glib.h                                 |    1 +
 glib/glib.symbols                           |   16 +
 glib/gmappedfile.c                          |   11 -
 glib/gmappedfile.h                          |    1 +
 glib/gtimezone.c                            |   27 +-
 glib/gvariant-core.c                        |   50 ++--
 glib/gvariant-core.h                        |    6 +-
 glib/gvariant.c                             |   26 +-
 glib/tests/.gitignore                       |    1 +
 glib/tests/Makefile.am                      |    3 +
 glib/tests/array-test.c                     |   33 ++
 glib/tests/bytes.c                          |  331 ++++++++++++++++++
 gobject/gboxed.c                            |    1 +
 gobject/glib-types.h                        |   10 +
 gobject/gobject.symbols                     |    1 +
 25 files changed, 1102 insertions(+), 371 deletions(-)
---
diff --git a/docs/reference/glib/Makefile.am b/docs/reference/glib/Makefile.am
index 405ac16..4dd4ee9 100644
--- a/docs/reference/glib/Makefile.am
+++ b/docs/reference/glib/Makefile.am
@@ -46,7 +46,7 @@ IGNORE_HFILES = \
 	gnulib			\
 	pcre			\
 	update-pcre		\
-	gbufferprivate.h	\
+	gbytesprivate.h		\
 	gvariant-internal.h	\
 	gvariant-serialiser.h	\
 	gvariant-core.h		\
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 7044843..8e88d73 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2382,8 +2382,10 @@ g_ptr_array_foreach
 <SECTION>
 <TITLE>Byte Arrays</TITLE>
 <FILE>arrays_byte</FILE>
+<SUBSECTION>
 GByteArray
 g_byte_array_new
+g_byte_array_new_take
 g_byte_array_sized_new
 g_byte_array_ref
 g_byte_array_unref
@@ -2396,7 +2398,24 @@ g_byte_array_sort
 g_byte_array_sort_with_data
 g_byte_array_set_size
 g_byte_array_free
-
+g_byte_array_free_to_bytes
+
+<SUBSECTION>
+GBytes
+g_bytes_new
+g_bytes_new_take
+g_bytes_new_static
+g_bytes_new_with_free_func
+g_bytes_new_from_bytes
+g_bytes_get_data
+g_bytes_get_size
+g_bytes_hash
+g_bytes_equal
+g_bytes_compare
+g_bytes_ref
+g_bytes_unref
+g_bytes_unref_to_data
+g_bytes_unref_to_array
 </SECTION>
 
 <SECTION>
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index 2a5daa8..e466d57 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -349,6 +349,7 @@ G_TYPE_MATCH_INFO
 G_TYPE_ARRAY
 G_TYPE_BYTE_ARRAY
 G_TYPE_PTR_ARRAY
+G_TYPE_BYTES
 G_TYPE_VARIANT_TYPE
 G_TYPE_ERROR
 G_TYPE_DATE_TIME
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 5e9f31a..1b427c3 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -130,8 +130,8 @@ libglib_2_0_la_SOURCES = 	\
 	gbitlock.c		\
 	gbookmarkfile.c 	\
 	gbsearcharray.h		\
-	gbuffer.c		\
-	gbufferprivate.h	\
+	gbytes.c		\
+	gbytes.h		\
 	gcharset.c		\
 	gchecksum.c		\
 	gconvert.c		\
@@ -262,6 +262,7 @@ glibsubinclude_HEADERS = \
 	gbase64.h	\
 	gbitlock.h	\
 	gbookmarkfile.h	\
+	gbytes.h	\
 	gcharset.h	\
 	gchecksum.h	\
 	gconvert.h	\
diff --git a/glib/garray.c b/glib/garray.c
index 27bc7e4..f46656e 100644
--- a/glib/garray.c
+++ b/glib/garray.c
@@ -35,8 +35,10 @@
 
 #include "garray.h"
 
+#include "gbytes.h"
 #include "gslice.h"
 #include "gmem.h"
+#include "gtestutils.h"
 #include "gthread.h"
 #include "gmessages.h"
 #include "gqsort.h"
@@ -1344,16 +1346,13 @@ g_ptr_array_foreach (GPtrArray *array,
 /**
  * SECTION:arrays_byte
  * @title: Byte Arrays
- * @short_description: arrays of bytes, which grow automatically as
- *                     elements are added
+ * @short_description: arrays of bytes
  *
- * #GByteArray is based on #GArray, to provide arrays of bytes which
- * grow automatically as elements are added.
+ * #GByteArray is a mutable array of bytes based on #GArray, to provide arrays
+ * of bytes which grow automatically as elements are added.
  *
- * To create a new #GByteArray use g_byte_array_new().
- *
- * To add elements to a #GByteArray, use g_byte_array_append(), and
- * g_byte_array_prepend().
+ * To create a new #GByteArray use g_byte_array_new(). To add elements to a
+ * #GByteArray, use g_byte_array_append(), and g_byte_array_prepend().
  *
  * To set the size of a #GByteArray, use g_byte_array_set_size().
  *
@@ -1380,6 +1379,9 @@ g_ptr_array_foreach (GPtrArray *array,
  *   g_byte_array_free (gbarray, TRUE);
  *  </programlisting>
  * </example>
+ *
+ * See #GBytes if you are interested in an immutable object representing a
+ * sequence of bytes.
  **/
 
 /**
@@ -1404,6 +1406,36 @@ GByteArray* g_byte_array_new (void)
 }
 
 /**
+ * g_byte_array_new_take:
+ * @data: (array length=len): byte data for the array
+ * @len: length of @data
+ *
+ * Create byte array containing the data. The data will be owned by the array
+ * and will be freed with g_free(), i.e. it could be allocated using g_strdup().
+ *
+ * Since: 2.32
+ *
+ * Returns: (transfer full): a new #GByteArray
+ */
+GByteArray *
+g_byte_array_new_take (guint8 *data,
+                       gsize   len)
+{
+  GByteArray *array;
+  GRealArray *real;
+
+  array = g_byte_array_new ();
+  real = (GRealArray *)array;
+  g_assert (real->data == NULL);
+  g_assert (real->len == 0);
+
+  real->data = data;
+  real->len = len;
+
+  return array;
+}
+
+/**
  * g_byte_array_sized_new:
  * @reserved_size: number of bytes preallocated.
  * @Returns: the new #GByteArray.
@@ -1437,6 +1469,35 @@ guint8*	    g_byte_array_free     (GByteArray *array,
 }
 
 /**
+ * g_byte_array_free_to_bytes:
+ * @array: (transfer full): a #GByteArray
+ *
+ * Transfers the data from the #GByteArray into a new immutable #GBytes.
+ *
+ * The #GByteArray is freed unless the reference count of @array is greater
+ * than one, the #GByteArray wrapper is preserved but the size of @array
+ * will be set to zero.
+ *
+ * This is identical to using g_bytes_new_take() and g_byte_array_free()
+ * together.
+ *
+ * Since: 2.32
+ *
+ * Returns: (transfer full): a new immutable #GBytes representing same byte
+ *          data that was in the array
+ */
+GBytes *
+g_byte_array_free_to_bytes (GByteArray *array)
+{
+  gsize length;
+
+  g_return_val_if_fail (array != NULL, NULL);
+
+  length = array->len;
+  return g_bytes_new_take (g_byte_array_free (array, FALSE), length);
+}
+
+/**
  * g_byte_array_ref:
  * @array: A #GByteArray.
  *
diff --git a/glib/garray.h b/glib/garray.h
index 3152b97..d96aade 100644
--- a/glib/garray.h
+++ b/glib/garray.h
@@ -35,6 +35,7 @@
 
 G_BEGIN_DECLS
 
+typedef struct _GBytes          GBytes;
 typedef struct _GArray		GArray;
 typedef struct _GByteArray	GByteArray;
 typedef struct _GPtrArray	GPtrArray;
@@ -150,9 +151,12 @@ void       g_ptr_array_foreach            (GPtrArray        *array,
  */
 
 GByteArray* g_byte_array_new               (void);
+GByteArray* g_byte_array_new_take          (guint8           *data,
+                                            gsize             len);
 GByteArray* g_byte_array_sized_new         (guint             reserved_size);
 guint8*     g_byte_array_free              (GByteArray       *array,
 					    gboolean          free_segment);
+GBytes*     g_byte_array_free_to_bytes     (GByteArray       *array);
 GByteArray *g_byte_array_ref               (GByteArray       *array);
 void        g_byte_array_unref             (GByteArray       *array);
 GByteArray* g_byte_array_append            (GByteArray       *array,
diff --git a/glib/gbytes.c b/glib/gbytes.c
new file mode 100644
index 0000000..dcf0dfb
--- /dev/null
+++ b/glib/gbytes.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright  2009, 2010 Codethink Limited
+ * Copyright  2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ *         Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gbytes.h"
+
+#include <glib/garray.h>
+#include <glib/gstrfuncs.h>
+#include <glib/gatomic.h>
+#include <glib/gslice.h>
+#include <glib/gtestutils.h>
+#include <glib/gmem.h>
+#include <glib/gmessages.h>
+
+#include <string.h>
+
+/**
+ * GBytes:
+ *
+ * A simple refcounted data type representing an immutable byte sequence
+ * from an unspecified origin.
+ *
+ * The purpose of a #GBytes is to keep the memory region that it holds
+ * alive for as long as anyone holds a reference to the bytes.  When
+ * the last reference count is dropped, the memory is released. Multiple
+ * unrelated callers can use byte data in the #GBytes without coordinating
+ * their activities, resting assured that the byte data will not change or
+ * move while they hold a reference.
+ *
+ * A #GBytes can come from many different origins that may have
+ * different procedures for freeing the memory region.  Examples are
+ * memory from g_malloc(), from memory slices, from a #GMappedFile or
+ * memory from other allocators.
+ *
+ * #GBytes work well as keys in #GHashTable. Use g_bytes_equal() and
+ * g_bytes_hash() as parameters to g_hash_table_new() or g_hash_table_new_full().
+ * #GBytes can also be used as keys in a #GTree by passing the g_bytes_compare()
+ * function to g_tree_new().
+ *
+ * The data pointed to by this bytes must not be modified. For a mutable
+ * array of bytes see #GByteArray. Use g_bytes_unref_to_array() to create a
+ * mutable array for a #GBytes sequence. To create an immutable #GBytes from
+ * a mutable #GByteArray, use the g_byte_array_free_to_bytes() function.
+ *
+ * Since: 2.32
+ **/
+
+struct _GBytes
+{
+  gconstpointer data;
+  gsize size;
+  gint ref_count;
+  GDestroyNotify free_func;
+  gpointer user_data;
+};
+
+/**
+ * g_bytes_new:
+ * @data: (array length=size): the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from @data.
+ *
+ * @data is copied.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new (gconstpointer data,
+             gsize         size)
+{
+  return g_bytes_new_take (g_memdup (data, size), size);
+}
+
+/**
+ * g_bytes_new_take:
+ * @data: (transfer full) (array length=size): the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from @data.
+ *
+ * After this call, @data belongs to the bytes and may no longer be
+ * modified by the caller.  g_free() will be called on @data when the
+ * bytes is no longer in use. Because of this @data must have been created by
+ * a call to g_malloc(), g_malloc0() or g_realloc() or by one of the many
+ * functions that wrap these calls (such as g_new(), g_strdup(), etc).
+ *
+ * For creating #GBytes with memory from other allocators, see
+ * g_bytes_new_with_free_func().
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_take (gpointer data,
+                  gsize    size)
+{
+  return g_bytes_new_with_free_func (data, size, g_free, data);
+}
+
+
+/**
+ * g_bytes_new_static:
+ * @data: (array length=size): the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from static data.
+ *
+ * @data must be static (ie: never modified or freed).
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_static (gconstpointer data,
+                    gsize         size)
+{
+  return g_bytes_new_with_free_func (data, size, NULL, NULL);
+}
+
+/**
+ * g_bytes_new_with_free_func:
+ * @data: (array length=size): the data to be used for the bytes
+ * @size: the size of @data
+ * @free_func: the function to call to release the data
+ * @user_data: data to pass to @free_func
+ *
+ * Creates a #GBytes from @data.
+ *
+ * When the last reference is dropped, @free_func will be called with the
+ * @user_data argument.
+ *
+ * @data must not be modified after this call is made until @free_func has
+ * been called to indicate that the bytes is no longer in use.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_with_free_func (gconstpointer  data,
+                            gsize          size,
+                            GDestroyNotify free_func,
+                            gpointer       user_data)
+{
+  GBytes *bytes;
+
+  bytes = g_slice_new (GBytes);
+  bytes->data = data;
+  bytes->size = size;
+  bytes->free_func = free_func;
+  bytes->user_data = user_data;
+  bytes->ref_count = 1;
+
+  return (GBytes *)bytes;
+}
+
+/**
+ * g_bytes_new_from_bytes:
+ * @bytes: a #GBytes
+ * @offset: offset which subsection starts at
+ * @length: length of subsection
+ *
+ * Creates a #GBytes which is a subsection of another #GBytes. The @offset +
+ * @length may not be longer than the size of @bytes.
+ *
+ * A reference to @bytes will be held by the newly created #GBytes until
+ * the byte data is no longer needed.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_from_bytes (GBytes  *bytes,
+                        gsize    offset,
+                        gsize    length)
+{
+  g_return_val_if_fail (bytes != NULL, NULL);
+  g_return_val_if_fail (offset <= bytes->size, NULL);
+  g_return_val_if_fail (offset + length <= bytes->size, NULL);
+
+  return g_bytes_new_with_free_func ((gchar *)bytes->data + offset, length,
+                                     (GDestroyNotify)g_bytes_unref, g_bytes_ref (bytes));
+}
+
+/**
+ * g_bytes_get_data:
+ * @bytes: a #GBytes
+ *
+ * Get the byte data in the #GBytes. This data should not be modified.
+ *
+ * This function will always return the same pointer for a given #GBytes.
+ *
+ * Returns: a pointer to the byte data
+ *
+ * Since: 2.32
+ */
+gconstpointer
+g_bytes_get_data (GBytes *bytes)
+{
+  g_return_val_if_fail (bytes != NULL, NULL);
+  return bytes->data;
+}
+
+/**
+ * g_bytes_get_size:
+ * @bytes: a #GBytes
+ *
+ * Get the size of the byte data in the #GBytes.
+ *
+ * This function will always return the same value for a given #GBytes.
+ *
+ * Returns: the size
+ *
+ * Since: 2.32
+ */
+gsize
+g_bytes_get_size (GBytes *bytes)
+{
+  g_return_val_if_fail (bytes != NULL, 0);
+  return bytes->size;
+}
+
+
+/**
+ * g_bytes_ref:
+ * @bytes: a #GBytes
+ *
+ * Increase the reference count on @bytes.
+ *
+ * Returns: the #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_ref (GBytes *bytes)
+{
+  g_return_val_if_fail (bytes != NULL, NULL);
+
+  g_atomic_int_inc (&bytes->ref_count);
+
+  return bytes;
+}
+
+/**
+ * g_bytes_unref:
+ * @bytes: (allow-none): a #GBytes
+ *
+ * Releases a reference on @bytes.  This may result in the bytes being
+ * freed.
+ *
+ * Since: 2.32
+ */
+void
+g_bytes_unref (GBytes *bytes)
+{
+  if (bytes == NULL)
+    return;
+
+  if (g_atomic_int_dec_and_test (&bytes->ref_count))
+    {
+      if (bytes->free_func != NULL)
+        bytes->free_func (bytes->user_data);
+      g_slice_free (GBytes, bytes);
+    }
+}
+
+/**
+ * g_bytes_equal:
+ * @bytes1: (type GLib.Bytes): a pointer to a #GBytes
+ * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1
+ *
+ * Compares the two #GBytes values being pointed to and returns
+ * %TRUE if they are equal.
+ *
+ * This function can be passed to g_hash_table_new() as the @key_equal_func
+ * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable.
+ *
+ * Returns: %TRUE if the two keys match.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_bytes_equal (gconstpointer bytes1,
+               gconstpointer bytes2)
+{
+  const GBytes *b1 = bytes1;
+  const GBytes *b2 = bytes2;
+
+  g_return_val_if_fail (bytes1 != NULL, FALSE);
+  g_return_val_if_fail (bytes2 != NULL, FALSE);
+
+  return b1->size == b2->size &&
+         memcmp (b1->data, b2->data, b1->size) == 0;
+}
+
+/**
+ * g_bytes_hash:
+ * @bytes: (type GLib.Bytes): a pointer to a #GBytes key
+ *
+ * Creates an integer hash code for the byte data in the #GBytes.
+ *
+ * This function can be passed to g_hash_table_new() as the @key_equal_func
+ * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable.
+ *
+ * Returns: a hash value corresponding to the key.
+ *
+ * Since: 2.32
+ */
+guint
+g_bytes_hash (gconstpointer bytes)
+{
+  const GBytes *a = bytes;
+  const signed char *p, *e;
+  guint32 h = 5381;
+
+  g_return_val_if_fail (bytes != NULL, 0);
+
+  for (p = (signed char *)a->data, e = (signed char *)a->data + a->size; p != e; p++)
+    h = (h << 5) + h + *p;
+
+  return h;
+}
+
+/**
+ * g_bytes_compare:
+ * @bytes1: (type GLib.Bytes): a pointer to a #GBytes
+ * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1
+ *
+ * Compares the two #GBytes values.
+ *
+ * This function can be used to sort GBytes instances in lexographical order.
+ *
+ * Returns: a negative value if bytes2 is lesser, a positive value if bytes2 is
+ *          greater, and zero if bytes2 is equal to bytes1
+ *
+ * Since: 2.32
+ */
+gint
+g_bytes_compare (gconstpointer bytes1,
+                 gconstpointer bytes2)
+{
+  const GBytes *b1 = bytes1;
+  const GBytes *b2 = bytes2;
+  gint ret;
+
+  g_return_val_if_fail (bytes1 != NULL, 0);
+  g_return_val_if_fail (bytes2 != NULL, 0);
+
+  ret = memcmp (b1->data, b2->data, MIN (b1->size, b2->size));
+  if (ret == 0 && b1->size != b2->size)
+      ret = b1->size < b2->size ? -1 : 1;
+  return ret;
+}
+
+static gpointer
+try_steal_and_unref (GBytes         *bytes,
+                     GDestroyNotify  free_func,
+                     gsize          *size)
+{
+  gpointer result;
+
+  if (bytes->free_func != free_func)
+    return NULL;
+
+  /* Are we the only reference? */
+  if (g_atomic_int_get (&bytes->ref_count) == 1)
+    {
+      *size = bytes->size;
+      result = (gpointer)bytes->data;
+      g_slice_free (GBytes, bytes);
+      return result;
+    }
+
+  return NULL;
+}
+
+
+/**
+ * g_bytes_unref_to_data:
+ * @bytes: (transfer full): a #GBytes
+ * @size: location to place the length of the returned data
+ *
+ * Unreferences the bytes, and returns a pointer the same byte data
+ * contents.
+ *
+ * As an optimization, the byte data is returned without copying if this was
+ * the last reference to bytes and bytes was created with g_bytes_new(),
+ * g_bytes_new_take() or g_byte_array_free_to_bytes(). In all other cases the
+ * data is copied.
+ *
+ * Returns: (transfer full): a pointer to the same byte data, which should
+ *          be freed with g_free()
+ *
+ * Since: 2.32
+ */
+gpointer
+g_bytes_unref_to_data (GBytes *bytes,
+                       gsize  *size)
+{
+  gpointer result;
+
+  g_return_val_if_fail (bytes != NULL, NULL);
+  g_return_val_if_fail (size != NULL, NULL);
+
+  /*
+   * Optimal path: if this is was the last reference, then we can return
+   * the data from this GBytes without copying.
+   */
+
+  result = try_steal_and_unref (bytes, g_free, size);
+  if (result == NULL)
+    {
+      /*
+       * Copy: Non g_malloc (or compatible) allocator, or static memory,
+       * so we have to copy, and then unref.
+       */
+      result = g_memdup (bytes->data, bytes->size);
+      *size = bytes->size;
+      g_bytes_unref (bytes);
+    }
+
+  return result;
+}
+
+/**
+ * g_bytes_unref_to_array:
+ * @bytes: (transfer full): a #GBytes
+ *
+ * Unreferences the bytes, and returns a new mutable #GByteArray containing
+ * the same byte data.
+ *
+ * As an optimization, the byte data is transferred to the array without copying
+ * if this was the last reference to bytes and bytes was created with
+ * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all
+ * other cases the data is copied.
+ *
+ * Returns: (transfer full): a new mutable #GByteArray containing the same byte data
+ *
+ * Since: 2.32
+ */
+GByteArray *
+g_bytes_unref_to_array (GBytes *bytes)
+{
+  gpointer data;
+  gsize size;
+
+  g_return_val_if_fail (bytes != NULL, NULL);
+
+  data = g_bytes_unref_to_data (bytes, &size);
+  return g_byte_array_new_take (data, size);
+}
diff --git a/glib/gbytes.h b/glib/gbytes.h
new file mode 100644
index 0000000..cf7b326
--- /dev/null
+++ b/glib/gbytes.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright  2009, 2010 Codethink Limited
+ * Copyright  2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ *         Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_BYTES_H__
+#define __G_BYTES_H__
+
+#include <glib/gtypes.h>
+#include <glib/garray.h>
+
+GBytes *        g_bytes_new                     (gconstpointer   data,
+                                                 gsize           size);
+
+GBytes *        g_bytes_new_take                (gpointer        data,
+                                                 gsize           size);
+
+GBytes *        g_bytes_new_static              (gconstpointer   data,
+                                                 gsize           size);
+
+GBytes *        g_bytes_new_with_free_func      (gconstpointer   data,
+                                                 gsize           size,
+                                                 GDestroyNotify  free_func,
+                                                 gpointer        user_data);
+
+GBytes *        g_bytes_new_from_bytes          (GBytes         *bytes,
+                                                 gsize           offset,
+                                                 gsize           length);
+
+gconstpointer   g_bytes_get_data                (GBytes         *bytes);
+
+gsize           g_bytes_get_size                (GBytes         *bytes);
+
+GBytes *        g_bytes_ref                     (GBytes         *bytes);
+
+void            g_bytes_unref                   (GBytes         *bytes);
+
+gpointer        g_bytes_unref_to_data           (GBytes         *bytes,
+                                                 gsize          *size);
+
+GByteArray *    g_bytes_unref_to_array          (GBytes         *bytes);
+
+guint           g_bytes_hash                    (gconstpointer   bytes);
+
+gboolean        g_bytes_equal                   (gconstpointer   bytes1,
+                                                 gconstpointer   bytes2);
+
+gint            g_bytes_compare                 (gconstpointer   bytes1,
+                                                 gconstpointer   bytes2);
+
+#endif /* __G_BYTES_H__ */
diff --git a/glib/glib.h b/glib/glib.h
index 5e4a528..06056a3 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -37,6 +37,7 @@
 #include <glib/gbase64.h>
 #include <glib/gbitlock.h>
 #include <glib/gbookmarkfile.h>
+#include <glib/gbytes.h>
 #include <glib/gcharset.h>
 #include <glib/gchecksum.h>
 #include <glib/gconvert.h>
diff --git a/glib/glib.symbols b/glib/glib.symbols
index c78d1f8..15d6616 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -18,9 +18,11 @@ g_array_sort
 g_array_sort_with_data
 g_byte_array_append
 g_byte_array_free
+g_byte_array_free_to_bytes
 g_byte_array_unref
 g_byte_array_ref
 g_byte_array_new
+g_byte_array_new_take
 g_byte_array_prepend
 g_byte_array_remove_index
 g_byte_array_remove_index_fast
@@ -29,6 +31,20 @@ g_byte_array_set_size
 g_byte_array_sized_new
 g_byte_array_sort
 g_byte_array_sort_with_data
+g_bytes_compare
+g_bytes_equal
+g_bytes_get_data
+g_bytes_get_size
+g_bytes_hash
+g_bytes_new
+g_bytes_new_from_bytes
+g_bytes_new_static
+g_bytes_new_take
+g_bytes_new_with_free_func
+g_bytes_ref
+g_bytes_unref
+g_bytes_unref_to_array
+g_bytes_unref_to_data
 g_ptr_array_add
 g_ptr_array_foreach
 g_ptr_array_free
diff --git a/glib/gmappedfile.c b/glib/gmappedfile.c
index 4a73827..a303a36 100644
--- a/glib/gmappedfile.c
+++ b/glib/gmappedfile.c
@@ -56,7 +56,6 @@
 #include "gstdio.h"
 #include "gstrfuncs.h"
 #include "gatomic.h"
-#include "gbufferprivate.h"
 
 #include "glibintl.h"
 
@@ -88,16 +87,6 @@ struct _GMappedFile
 #endif
 };
 
-/* Make sure the layout of GMappedFile is the same as GBuffer's */
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, contents) ==
-                 G_STRUCT_OFFSET (GBuffer, data));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, length) ==
-                 G_STRUCT_OFFSET (GBuffer, size));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, ref_count) ==
-                 G_STRUCT_OFFSET (GBuffer, ref_count));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, free_func) ==
-                 G_STRUCT_OFFSET (GBuffer, free_func));
-
 static void
 g_mapped_file_destroy (GMappedFile *file)
 {
diff --git a/glib/gmappedfile.h b/glib/gmappedfile.h
index f48b141..52ba31d 100644
--- a/glib/gmappedfile.h
+++ b/glib/gmappedfile.h
@@ -26,6 +26,7 @@
 #ifndef __G_MAPPED_FILE_H__
 #define __G_MAPPED_FILE_H__
 
+#include <glib/gbytes.h>
 #include <glib/gerror.h>
 
 G_BEGIN_DECLS
diff --git a/glib/gtimezone.c b/glib/gtimezone.c
index efb2733..19358b1 100644
--- a/glib/gtimezone.c
+++ b/glib/gtimezone.c
@@ -33,7 +33,7 @@
 #include "gstrfuncs.h"
 #include "ghash.h"
 #include "gthread.h"
-#include "gbufferprivate.h"
+#include "gbytes.h"
 #include "gslice.h"
 
 /**
@@ -118,7 +118,7 @@ struct _GTimeZone
 {
   gchar   *name;
 
-  GBuffer *zoneinfo;
+  GBytes *zoneinfo;
 
   const struct tzhead *header;
   const struct ttinfo *infos;
@@ -169,7 +169,7 @@ again:
         }
 
       if (tz->zoneinfo)
-        g_buffer_unref (tz->zoneinfo);
+        g_bytes_unref (tz->zoneinfo);
 
       g_free (tz->name);
 
@@ -269,7 +269,7 @@ parse_constant_offset (const gchar *name,
     }
 }
 
-static GBuffer *
+static GBytes *
 zone_for_constant_offset (const gchar *name)
 {
   const gchar fake_zoneinfo_headers[] =
@@ -296,7 +296,7 @@ zone_for_constant_offset (const gchar *name)
   fake->info.tt_abbrind = 0;
   strcpy (fake->abbr, name);
 
-  return g_buffer_new_take_data (fake, sizeof *fake);
+  return g_bytes_new_take (fake, sizeof *fake);
 }
 
 /* Construction {{{1 */
@@ -345,6 +345,7 @@ GTimeZone *
 g_time_zone_new (const gchar *identifier)
 {
   GTimeZone *tz;
+  GMappedFile *file;
 
   G_LOCK (time_zones);
   if (time_zones == NULL)
@@ -380,19 +381,27 @@ g_time_zone_new (const gchar *identifier)
           else
             filename = g_strdup ("/etc/localtime");
 
-          tz->zoneinfo = (GBuffer *) g_mapped_file_new (filename, FALSE, NULL);
+          file = g_mapped_file_new (filename, FALSE, NULL);
+          if (file != NULL)
+            {
+              tz->zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file),
+                                                         g_mapped_file_get_length (file),
+                                                         (GDestroyNotify)g_mapped_file_unref,
+                                                         g_mapped_file_ref (file));
+              g_mapped_file_unref (file);
+            }
           g_free (filename);
         }
 
       if (tz->zoneinfo != NULL)
         {
-          const struct tzhead *header = tz->zoneinfo->data;
-          gsize size = tz->zoneinfo->size;
+          const struct tzhead *header = g_bytes_get_data (tz->zoneinfo);
+          gsize size = g_bytes_get_size (tz->zoneinfo);
 
           /* we only bother to support version 2 */
           if (size < sizeof (struct tzhead) || memcmp (header, "TZif2", 5))
             {
-              g_buffer_unref (tz->zoneinfo);
+              g_bytes_unref (tz->zoneinfo);
               tz->zoneinfo = NULL;
             }
           else
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
index 6d1e2b3..436d4bf 100644
--- a/glib/gvariant-core.c
+++ b/glib/gvariant-core.c
@@ -24,7 +24,7 @@
 #include <glib/gtestutils.h>
 #include <glib/gbitlock.h>
 #include <glib/gatomic.h>
-#include <glib/gbufferprivate.h>
+#include <glib/gbytes.h>
 #include <glib/gslice.h>
 #include <glib/gmem.h>
 #include <string.h>
@@ -61,7 +61,7 @@ struct _GVariant
   {
     struct
     {
-      GBuffer *buffer;
+      GBytes *bytes;
       gconstpointer data;
     } serialised;
 
@@ -133,16 +133,16 @@ struct _GVariant
  *                never be changed.  It is therefore valid to access
  *                them without holding a lock.
  *
- *     .buffer: the #GBuffer that contains the memory pointed to by
+ *     .bytes:  the #GBytes that contains the memory pointed to by
  *              .data, or %NULL if .data is %NULL.  In the event that
  *              the instance was deserialised from another instance,
- *              then the buffer will be shared by both of them.  When
+ *              then the bytes will be shared by both of them.  When
  *              the instance is freed, this reference must be released
- *              with g_buffer_unref().
+ *              with g_bytes_unref().
  *
  *     .data: the serialised data (of size 'size') of the instance.
  *            This pointer should not be freed or modified in any way.
- *            #GBuffer is responsible for memory management.
+ *            #GBytes is responsible for memory management.
  *
  *            This pointer may be %NULL in two cases:
  *
@@ -438,7 +438,7 @@ g_variant_ensure_serialised (GVariant *value)
 
   if (~value->state & STATE_SERIALISED)
     {
-      GBuffer *buffer;
+      GBytes *bytes;
       gpointer data;
 
       g_variant_ensure_size (value);
@@ -447,9 +447,9 @@ g_variant_ensure_serialised (GVariant *value)
 
       g_variant_release_children (value);
 
-      buffer = g_buffer_new_take_data (data, value->size);
-      value->contents.serialised.data = buffer->data;
-      value->contents.serialised.buffer = buffer;
+      bytes = g_bytes_new_take (data, value->size);
+      value->contents.serialised.data = g_bytes_get_data (bytes);
+      value->contents.serialised.bytes = bytes;
       value->state |= STATE_SERIALISED;
     }
 }
@@ -486,23 +486,23 @@ g_variant_alloc (const GVariantType *type,
 
 /* -- internal -- */
 /* < internal >
- * g_variant_new_from_buffer:
+ * g_variant_new_from_bytes:
  * @type: a #GVariantType
- * @buffer: a #GBuffer
- * @trusted: if the contents of @buffer are trusted
+ * @bytes: a #GBytes
+ * @trusted: if the contents of @bytes are trusted
  *
  * Constructs a new serialised-mode #GVariant instance.  This is the
  * inner interface for creation of new serialised values that gets
  * called from various functions in gvariant.c.
  *
- * A reference is taken on @buffer.
+ * A reference is taken on @bytes.
  *
  * Returns: a new #GVariant with a floating reference
  */
 GVariant *
-g_variant_new_from_buffer (const GVariantType *type,
-                           GBuffer            *buffer,
-                           gboolean            trusted)
+g_variant_new_from_bytes (const GVariantType *type,
+                          GBytes             *bytes,
+                          gboolean            trusted)
 {
   GVariant *value;
   guint alignment;
@@ -510,14 +510,14 @@ g_variant_new_from_buffer (const GVariantType *type,
 
   value = g_variant_alloc (type, TRUE, trusted);
 
-  value->contents.serialised.buffer = g_buffer_ref (buffer);
+  value->contents.serialised.bytes = g_bytes_ref (bytes);
 
   g_variant_type_info_query (value->type_info,
                              &alignment, &size);
 
-  if (size && buffer->size != size)
+  if (size && g_bytes_get_size (bytes) != size)
     {
-      /* Creating a fixed-sized GVariant with a buffer of the wrong
+      /* Creating a fixed-sized GVariant with a bytes of the wrong
        * size.
        *
        * We should do the equivalent of pulling a fixed-sized child out
@@ -529,8 +529,8 @@ g_variant_new_from_buffer (const GVariantType *type,
     }
   else
     {
-      value->contents.serialised.data = buffer->data;
-      value->size = buffer->size;
+      value->contents.serialised.data = g_bytes_get_data (bytes);
+      value->size = g_bytes_get_size (bytes);
     }
 
   return value;
@@ -630,7 +630,7 @@ g_variant_unref (GVariant *value)
       g_variant_type_info_unref (value->type_info);
 
       if (value->state & STATE_SERIALISED)
-        g_buffer_unref (value->contents.serialised.buffer);
+        g_bytes_unref (value->contents.serialised.bytes);
       else
         g_variant_release_children (value);
 
@@ -963,8 +963,8 @@ g_variant_get_child_value (GVariant *value,
                    STATE_SERIALISED;
     child->size = s_child.size;
     child->ref_count = 1;
-    child->contents.serialised.buffer =
-      g_buffer_ref (value->contents.serialised.buffer);
+    child->contents.serialised.bytes =
+      g_bytes_ref (value->contents.serialised.bytes);
     child->contents.serialised.data = s_child.data;
 
     return child;
diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h
index a31212e..1fd4829 100644
--- a/glib/gvariant-core.h
+++ b/glib/gvariant-core.h
@@ -23,12 +23,12 @@
 
 #include <glib/gvarianttypeinfo.h>
 #include <glib/gvariant.h>
-#include <glib/gbufferprivate.h>
+#include <glib/gbytes.h>
 
 /* gvariant-core.c */
 G_GNUC_INTERNAL
-GVariant *              g_variant_new_from_buffer                       (const GVariantType *type,
-                                                                         GBuffer            *buffer,
+GVariant *              g_variant_new_from_bytes                        (const GVariantType *type,
+                                                                         GBytes             *bytes,
                                                                          gboolean            trusted);
 
 G_GNUC_INTERNAL
diff --git a/glib/gvariant.c b/glib/gvariant.c
index 6e15c41..c3d1947 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -321,11 +321,11 @@ g_variant_new_from_trusted (const GVariantType *type,
                             gsize               size)
 {
   GVariant *value;
-  GBuffer *buffer;
+  GBytes *bytes;
 
-  buffer = g_buffer_new_from_data (data, size);
-  value = g_variant_new_from_buffer (type, buffer, TRUE);
-  g_buffer_unref (buffer);
+  bytes = g_bytes_new (data, size);
+  value = g_variant_new_from_bytes (type, bytes, TRUE);
+  g_bytes_unref (bytes);
 
   return value;
 }
@@ -5098,7 +5098,7 @@ g_variant_byteswap (GVariant *value)
     {
       GVariantSerialised serialised;
       GVariant *trusted;
-      GBuffer *buffer;
+      GBytes *bytes;
 
       trusted = g_variant_get_normal_form (value);
       serialised.type_info = g_variant_get_type_info (trusted);
@@ -5109,9 +5109,9 @@ g_variant_byteswap (GVariant *value)
 
       g_variant_serialised_byteswap (serialised);
 
-      buffer = g_buffer_new_take_data (serialised.data, serialised.size);
-      new = g_variant_new_from_buffer (g_variant_get_type (value), buffer, TRUE);
-      g_buffer_unref (buffer);
+      bytes = g_bytes_new (serialised.data, serialised.size);
+      new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE);
+      g_bytes_unref (bytes);
     }
   else
     /* contains no multi-byte data */
@@ -5167,18 +5167,18 @@ g_variant_new_from_data (const GVariantType *type,
                          gpointer            user_data)
 {
   GVariant *value;
-  GBuffer *buffer;
+  GBytes *bytes;
 
   g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
   g_return_val_if_fail (data != NULL || size == 0, NULL);
 
   if (notify)
-    buffer = g_buffer_new_from_pointer (data, size, notify, user_data);
+    bytes = g_bytes_new_with_free_func (data, size, notify, user_data);
   else
-    buffer = g_buffer_new_from_static_data (data, size);
+    bytes = g_bytes_new_static (data, size);
 
-  value = g_variant_new_from_buffer (type, buffer, trusted);
-  g_buffer_unref (buffer);
+  value = g_variant_new_from_bytes (type, bytes, trusted);
+  g_bytes_unref (bytes);
 
   return value;
 }
diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore
index 49b3b83..1bc83c5 100644
--- a/glib/tests/.gitignore
+++ b/glib/tests/.gitignore
@@ -8,6 +8,7 @@ atomic
 base64
 bitlock
 bookmarkfile
+bytes
 cache
 checksum
 collate
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index 5d18189..3cfca22 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -67,6 +67,9 @@ markup_subparser_LDADD    = $(progs_ldadd)
 TEST_PROGS         += array-test
 array_test_LDADD    = $(progs_ldadd)
 
+TEST_PROGS         += bytes
+bytes_LDADD         = $(progs_ldadd)
+
 TEST_PROGS         += hostutils
 hostutils_LDADD     = $(progs_ldadd)
 
diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c
index 0e08cd1..de3a8ff 100644
--- a/glib/tests/array-test.c
+++ b/glib/tests/array-test.c
@@ -762,6 +762,37 @@ byte_array_sort_with_data (void)
   g_byte_array_free (gbarray, TRUE);
 }
 
+static void
+byte_array_new_take (void)
+{
+  GByteArray *gbarray;
+  guint8 *data;
+
+  data = g_memdup ("woooweeewow", 11);
+  gbarray = g_byte_array_new_take (data, 11);
+  g_assert (gbarray->data == data);
+  g_assert_cmpuint (gbarray->len, ==, 11);
+  g_byte_array_free (gbarray, TRUE);
+}
+
+static void
+byte_array_free_to_bytes (void)
+{
+  GByteArray *gbarray;
+  gpointer memory;
+  GBytes *bytes;
+
+  gbarray = g_byte_array_new ();
+  g_byte_array_append (gbarray, (guint8 *)"woooweeewow", 11);
+  memory = gbarray->data;
+
+  bytes = g_byte_array_free_to_bytes (gbarray);
+  g_assert (bytes != NULL);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, 11);
+  g_assert (g_bytes_get_data (bytes) == memory);
+
+  g_bytes_unref (bytes);
+}
 int
 main (int argc, char *argv[])
 {
@@ -796,6 +827,8 @@ main (int argc, char *argv[])
   g_test_add_func ("/bytearray/ref-count", byte_array_ref_count);
   g_test_add_func ("/bytearray/sort", byte_array_sort);
   g_test_add_func ("/bytearray/sort-with-data", byte_array_sort_with_data);
+  g_test_add_func ("/bytearray/new-take", byte_array_new_take);
+  g_test_add_func ("/bytearray/free-to-bytes", byte_array_free_to_bytes);
 
   return g_test_run ();
 }
diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
new file mode 100644
index 0000000..104bdce
--- /dev/null
+++ b/glib/tests/bytes.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2011 Collabora Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#undef G_DISABLE_ASSERT
+#undef G_LOG_DOMAIN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "glib.h"
+
+static const gchar *NYAN = "nyannyan";
+static const gsize N_NYAN = 8;
+
+static void
+test_new (void)
+{
+  const gchar *data;
+  GBytes *bytes;
+
+  data = "test";
+  bytes = g_bytes_new (data, 4);
+  g_assert (bytes != NULL);
+  g_assert (g_bytes_get_data (bytes) != data);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4);
+  g_assert (memcmp (data, g_bytes_get_data (bytes), g_bytes_get_size (bytes)) == 0);
+
+  g_bytes_unref (bytes);
+}
+
+static void
+test_new_take (void)
+{
+  gchar *data;
+  GBytes *bytes;
+
+  data = g_strdup ("test");
+  bytes = g_bytes_new_take (data, 4);
+  g_assert (bytes != NULL);
+  g_assert (g_bytes_get_data (bytes) == data);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4);
+
+  g_bytes_unref (bytes);
+}
+
+static void
+test_new_static (void)
+{
+  const gchar *data;
+  GBytes *bytes;
+
+  data = "test";
+  bytes = g_bytes_new_static (data, 4);
+  g_assert (bytes != NULL);
+  g_assert (g_bytes_get_data (bytes) == data);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4);
+
+  g_bytes_unref (bytes);
+}
+
+static void
+test_new_from_bytes (void)
+{
+  const gchar *data = "smile and wave";
+  GBytes *bytes;
+  GBytes *sub;
+
+  bytes = g_bytes_new (data, 14);
+  sub = g_bytes_new_from_bytes (bytes, 10, 4);
+
+  g_assert (sub != NULL);
+  g_assert (g_bytes_get_data (sub) == ((gchar *)g_bytes_get_data (bytes)) + 10);
+  g_assert (g_bytes_get_size (sub) == 4);
+  g_bytes_unref (bytes);
+
+  g_assert (memcmp (g_bytes_get_data (sub), "wave", 4) == 0);
+  g_bytes_unref (sub);
+}
+
+static void
+on_destroy_increment (gpointer data)
+{
+  gint *count = data;
+  g_assert (count != NULL);
+  (*count)++;
+}
+
+static void
+test_new_with_free_func (void)
+{
+  GBytes *bytes;
+  gchar *data;
+  gint count = 0;
+
+  data = "test";
+  bytes = g_bytes_new_with_free_func (data, 4, on_destroy_increment, &count);
+  g_assert (bytes != NULL);
+  g_assert_cmpint (count, ==, 0);
+  g_assert (g_bytes_get_data (bytes) == data);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4);
+
+  g_bytes_unref (bytes);
+  g_assert_cmpuint (count, ==, 1);
+}
+
+static void
+test_hash (void)
+{
+  GBytes *bytes1;
+  GBytes *bytes2;
+  guint hash1;
+  guint hash2;
+
+  bytes1 = g_bytes_new ("blah", 4);
+  bytes2 = g_bytes_new ("blah", 4);
+
+  hash1 = g_bytes_hash (bytes1);
+  hash2 = g_bytes_hash (bytes2);
+  g_assert (hash1 == hash2);
+
+  g_bytes_unref (bytes1);
+  g_bytes_unref (bytes2);
+}
+
+static void
+test_equal (void)
+{
+  GBytes *bytes;
+  GBytes *bytes2;
+
+  bytes = g_bytes_new ("blah", 4);
+
+  bytes2 = g_bytes_new ("blah", 4);
+  g_assert (g_bytes_equal (bytes, bytes2));
+  g_assert (g_bytes_equal (bytes2, bytes));
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("bla", 3);
+  g_assert (!g_bytes_equal (bytes, bytes2));
+  g_assert (!g_bytes_equal (bytes2, bytes));
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("true", 4);
+  g_assert (!g_bytes_equal (bytes, bytes2));
+  g_assert (!g_bytes_equal (bytes2, bytes));
+  g_bytes_unref (bytes2);
+
+  g_bytes_unref (bytes);
+}
+
+static void
+test_compare (void)
+{
+  GBytes *bytes;
+  GBytes *bytes2;
+
+  bytes = g_bytes_new ("blah", 4);
+
+  bytes2 = g_bytes_new ("blah", 4);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), ==, 0);
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("bla", 3);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0);
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("abcd", 4);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0);
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("blahblah", 8);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0);
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("zyx", 3);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0);
+  g_bytes_unref (bytes2);
+
+  bytes2 = g_bytes_new ("zyxw", 4);
+  g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0);
+  g_bytes_unref (bytes2);
+
+  g_bytes_unref (bytes);
+}
+
+static void
+test_to_data_transferred (void)
+{
+  gconstpointer memory;
+  gpointer data;
+  gsize size;
+  GBytes *bytes;
+
+  /* Memory transferred: one reference, and allocated with g_malloc */
+  bytes = g_bytes_new (NYAN, N_NYAN);
+  memory = g_bytes_get_data (bytes);
+  data = g_bytes_unref_to_data (bytes, &size);
+  g_assert (data == memory);
+  g_assert_cmpuint (size, ==, N_NYAN);
+  g_assert (memcmp (data, NYAN, N_NYAN) == 0);
+  g_free (data);
+}
+
+static void
+test_to_data_two_refs (void)
+{
+  gconstpointer memory;
+  gpointer data;
+  gsize size;
+  GBytes *bytes;
+
+  /* Memory copied: two references */
+  bytes = g_bytes_new (NYAN, N_NYAN);
+  bytes = g_bytes_ref (bytes);
+  memory = g_bytes_get_data (bytes);
+  data = g_bytes_unref_to_data (bytes, &size);
+  g_assert (data != memory);
+  g_assert_cmpuint (size, ==, N_NYAN);
+  g_assert (memcmp (data, NYAN, N_NYAN) == 0);
+  g_free (data);
+  g_assert (g_bytes_get_data (bytes) == memory);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN);
+  g_bytes_unref (bytes);
+}
+
+static void
+test_to_data_non_malloc (void)
+{
+  gpointer data;
+  gsize size;
+  GBytes *bytes;
+
+  /* Memory copied: non malloc memory */
+  bytes = g_bytes_new_static (NYAN, N_NYAN);
+  g_assert (g_bytes_get_data (bytes) == NYAN);
+  data = g_bytes_unref_to_data (bytes, &size);
+  g_assert (data != (gpointer)NYAN);
+  g_assert_cmpuint (size, ==, N_NYAN);
+  g_assert (memcmp (data, NYAN, N_NYAN) == 0);
+  g_free (data);
+}
+
+static void
+test_to_array_transferred (void)
+{
+  gconstpointer memory;
+  GByteArray *array;
+  GBytes *bytes;
+
+  /* Memory transferred: one reference, and allocated with g_malloc */
+  bytes = g_bytes_new (NYAN, N_NYAN);
+  memory = g_bytes_get_data (bytes);
+  array = g_bytes_unref_to_array (bytes);
+  g_assert (array != NULL);
+  g_assert (array->data == memory);
+  g_assert_cmpuint (array->len, ==, N_NYAN);
+  g_assert (memcmp (array->data, NYAN, N_NYAN) == 0);
+  g_byte_array_unref (array);
+}
+
+static void
+test_to_array_two_refs (void)
+{
+  gconstpointer memory;
+  GByteArray *array;
+  GBytes *bytes;
+
+  /* Memory copied: two references */
+  bytes = g_bytes_new (NYAN, N_NYAN);
+  bytes = g_bytes_ref (bytes);
+  memory = g_bytes_get_data (bytes);
+  array = g_bytes_unref_to_array (bytes);
+  g_assert (array != NULL);
+  g_assert (array->data != memory);
+  g_assert_cmpuint (array->len, ==, N_NYAN);
+  g_assert (memcmp (array->data, NYAN, N_NYAN) == 0);
+  g_byte_array_unref (array);
+  g_assert (g_bytes_get_data (bytes) == memory);
+  g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN);
+  g_bytes_unref (bytes);
+}
+
+static void
+test_to_array_non_malloc (void)
+{
+  GByteArray *array;
+  GBytes *bytes;
+
+  /* Memory copied: non malloc memory */
+  bytes = g_bytes_new_static (NYAN, N_NYAN);
+  g_assert (g_bytes_get_data (bytes) == NYAN);
+  array = g_bytes_unref_to_array (bytes);
+  g_assert (array != NULL);
+  g_assert (array->data != (gpointer)NYAN);
+  g_assert_cmpuint (array->len, ==, N_NYAN);
+  g_assert (memcmp (array->data, NYAN, N_NYAN) == 0);
+  g_byte_array_unref (array);
+}
+
+int
+main (int argc, char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_bug_base ("http://bugs.gnome.org/";);
+
+  g_test_add_func ("/bytes/new", test_new);
+  g_test_add_func ("/bytes/new-take", test_new_take);
+  g_test_add_func ("/bytes/new-static", test_new_static);
+  g_test_add_func ("/bytes/new-with-free-func", test_new_with_free_func);
+  g_test_add_func ("/bytes/new-from-bytes", test_new_from_bytes);
+  g_test_add_func ("/bytes/hash", test_hash);
+  g_test_add_func ("/bytes/equal", test_equal);
+  g_test_add_func ("/bytes/compare", test_compare);
+  g_test_add_func ("/bytes/to-data/transfered", test_to_data_transferred);
+  g_test_add_func ("/bytes/to-data/two-refs", test_to_data_two_refs);
+  g_test_add_func ("/bytes/to-data/non-malloc", test_to_data_non_malloc);
+  g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred);
+  g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
+  g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
+
+  return g_test_run ();
+}
diff --git a/gobject/gboxed.c b/gobject/gboxed.c
index 65ea908..755955c 100644
--- a/gobject/gboxed.c
+++ b/gobject/gboxed.c
@@ -127,6 +127,7 @@ G_DEFINE_BOXED_TYPE (GHashTable, g_hash_table, g_hash_table_ref, g_hash_table_un
 G_DEFINE_BOXED_TYPE (GArray, g_array, g_array_ref, g_array_unref)
 G_DEFINE_BOXED_TYPE (GPtrArray, g_ptr_array,g_ptr_array_ref, g_ptr_array_unref)
 G_DEFINE_BOXED_TYPE (GByteArray, g_byte_array, g_byte_array_ref, g_byte_array_unref)
+G_DEFINE_BOXED_TYPE (GBytes, g_bytes, g_bytes_ref, g_bytes_unref);
 
 #ifdef ENABLE_REGEX
 G_DEFINE_BOXED_TYPE (GRegex, g_regex, g_regex_ref, g_regex_unref)
diff --git a/gobject/glib-types.h b/gobject/glib-types.h
index 7bad68c..9ff9f60 100644
--- a/gobject/glib-types.h
+++ b/gobject/glib-types.h
@@ -133,6 +133,15 @@ typedef gsize GType;
 #define G_TYPE_PTR_ARRAY (g_ptr_array_get_type ())
 
 /**
+ * G_TYPE_BYTES:
+ *
+ * The #GType for #GBytes.
+ *
+ * Since: 2.32
+ */
+#define G_TYPE_BYTES (g_bytes_get_type ())
+
+/**
  * G_TYPE_VARIANT_TYPE:
  *
  * The #GType for a boxed type holding a #GVariantType.
@@ -225,6 +234,7 @@ GType   g_hash_table_get_type      (void) G_GNUC_CONST;
 GType   g_array_get_type           (void) G_GNUC_CONST;
 GType   g_byte_array_get_type      (void) G_GNUC_CONST;
 GType   g_ptr_array_get_type       (void) G_GNUC_CONST;
+GType   g_bytes_get_type           (void) G_GNUC_CONST;
 GType   g_variant_type_get_gtype   (void) G_GNUC_CONST;
 GType   g_regex_get_type           (void) G_GNUC_CONST;
 GType   g_match_info_get_type      (void) G_GNUC_CONST;
diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols
index fafa832..dda51d0 100644
--- a/gobject/gobject.symbols
+++ b/gobject/gobject.symbols
@@ -21,6 +21,7 @@ g_strv_get_type
 g_hash_table_get_type
 g_array_get_type
 g_byte_array_get_type
+g_bytes_get_type
 g_error_get_type
 g_ptr_array_get_type
 g_regex_get_type



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]