+
+typedef struct _GMemoryBuffer GMemoryBuffer;
+struct _GMemoryBuffer
+{
+ gsize len;
+ gsize valid_len;
+ gsize pos;
+ gchar *data;
+ GDataStreamByteOrder byte_order;
+};
+
+static gboolean
+g_memory_buffer_is_byteswapped (GMemoryBuffer *mbuf)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ return mbuf->byte_order == G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
+#else
+ return mbuf->byte_order == G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN;
+#endif
+}
+
+static guchar
+g_memory_buffer_read_byte (GMemoryBuffer *mbuf)
+{
+ if (mbuf->pos >= mbuf->valid_len)
+ return 0;
+ return mbuf->data [mbuf->pos++];
+}
+
+static gint16
+g_memory_buffer_read_int16 (GMemoryBuffer *mbuf)
+{
+ gint16 v;
+
+ if (mbuf->pos > mbuf->valid_len - 2)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 2);
+ mbuf->pos += 2;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT16_SWAP_LE_BE (v);
+
+ return v;
+}
+
+static guint16
+g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf)
+{
+ guint16 v;
+
+ if (mbuf->pos > mbuf->valid_len - 2)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 2);
+ mbuf->pos += 2;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT16_SWAP_LE_BE (v);
+
+ return v;
+}
+
+static gint32
+g_memory_buffer_read_int32 (GMemoryBuffer *mbuf)
+{
+ gint32 v;
+
+ if (mbuf->pos > mbuf->valid_len - 4)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 4);
+ mbuf->pos += 4;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT32_SWAP_LE_BE (v);
+
+ return v;
+}
+
+static guint32
+g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf)
+{
+ guint32 v;
+
+ if (mbuf->pos > mbuf->valid_len - 4)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 4);
+ mbuf->pos += 4;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT32_SWAP_LE_BE (v);
+
+ return v;
+}
+
+static gint64
+g_memory_buffer_read_int64 (GMemoryBuffer *mbuf)
+{
+ gint64 v;
+
+ if (mbuf->pos > mbuf->valid_len - 8)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 8);
+ mbuf->pos += 8;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT64_SWAP_LE_BE (v);
+
+ return v;
+}
+
+static guint64
+g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf)
+{
+ guint64 v;
+
+ if (mbuf->pos > mbuf->valid_len - 8)
+ {
+ mbuf->pos = mbuf->valid_len;
+ return 0;
+ }
+
+ memcpy (&v, mbuf->data + mbuf->pos, 8);
+ mbuf->pos += 8;
+
+ if (g_memory_buffer_is_byteswapped (mbuf))
+ v = GUINT64_SWAP_LE_BE (v);
+
+ return v;
+}
+
+#define MIN_ARRAY_SIZE 128
+
+static gint
+g_nearest_pow (gint num)
+{
+ gint n = 1;
+
+ while (n < num)
+ n <<= 1;
+
+ return n;
+}
+
+static void
+array_resize (GMemoryBuffer *mbuf,
+ gsize size)
+{
+ gpointer data;
+ gsize len;
+
+ if (mbuf->len == size)
+ return;
+
+ len = mbuf->len;
+ data = g_realloc (mbuf->data, size);
+
+ if (size > len)
+ memset ((guint8 *)data + len, 0, size - len);
+
+ mbuf->data = data;
+ mbuf->len = size;
+
+ if (mbuf->len < mbuf->valid_len)
+ mbuf->valid_len = mbuf->len;
+}
+
+static gboolean
+g_memory_buffer_write (GMemoryBuffer *mbuf,
+ const void *buffer,
+ gsize count)
+{
+ guint8 *dest;
+ gsize new_size;
+
+ if (count == 0)
+ return TRUE;
+
+ /* Check for address space overflow, but only if the buffer is resizable.
+ Otherwise we just do a short write and don't worry. */
+ if (mbuf->pos + count < mbuf->pos)
+ return FALSE;
+
+ if (mbuf->pos + count > mbuf->len)
+ {
+ /* At least enought to fit the write, rounded up
+ for greater than linear growth.
+ TODO: This wastes a lot of memory at large buffer sizes.
+ Figure out a more rational allocation strategy. */
+ new_size = g_nearest_pow (mbuf->pos + count);
+ /* Check for overflow again. We have only checked if
+ pos + count > G_MAXSIZE, but it only catches the case of writing
+ more than 4GiB total on a 32-bit system. There's still the problem
+ of g_nearest_pow overflowing above 0x7fffffff, so we're
+ effectively limited to 2GiB. */
+ if (new_size < mbuf->len)
+ return FALSE;
+
+ new_size = MAX (new_size, MIN_ARRAY_SIZE);
+ array_resize (mbuf, new_size);
+ }
+
+ dest = (guint8 *)mbuf->data + mbuf->pos;
+ memcpy (dest, buffer, count);
+ mbuf->pos += count;
+
+ if (mbuf->pos > mbuf->valid_len)
+ mbuf->valid_len = mbuf->pos;
+
+ return TRUE;
+}
+
+static gboolean
+g_memory_buffer_put_byte (GMemoryBuffer *mbuf,
+ guchar data)
+{
+ return g_memory_buffer_write (mbuf, &data, 1);
+}
+
+static gboolean
+g_memory_buffer_put_int16 (GMemoryBuffer *mbuf,
+ gint16 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GINT16_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GINT16_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 2);
+}
+
+static gboolean
+g_memory_buffer_put_uint16 (GMemoryBuffer *mbuf,
+ guint16 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GUINT16_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GUINT16_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 2);
+}
+
+static gboolean
+g_memory_buffer_put_int32 (GMemoryBuffer *mbuf,
+ gint32 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GINT32_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GINT32_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 4);
+}
+
+static gboolean
+g_memory_buffer_put_uint32 (GMemoryBuffer *mbuf,
+ guint32 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GUINT32_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GUINT32_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 4);
+}
+
+static gboolean
+g_memory_buffer_put_int64 (GMemoryBuffer *mbuf,
+ gint64 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GINT64_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GINT64_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 8);
+}
+
+static gboolean
+g_memory_buffer_put_uint64 (GMemoryBuffer *mbuf,
+ guint64 data)
+{
+ switch (mbuf->byte_order)
+ {
+ case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+ data = GUINT64_TO_BE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+ data = GUINT64_TO_LE (data);
+ break;
+ case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+ default:
+ break;
+ }
+
+ return g_memory_buffer_write (mbuf, &data, 8);
+}
+
+static gboolean
+g_memory_buffer_put_string (GMemoryBuffer *mbuf,
+ const char *str)
+{
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ return g_memory_buffer_write (mbuf, str, strlen (str));
+}
+