/*
 * Copyright © 2009, 2010 Codethink Limited
 *
 * 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>
 */

#include "config.h"

#include "gbufferprivate.h"

#include <glib/gstrfuncs.h>
#include <glib/gatomic.h>
#include <glib/gslice.h>
#include <glib/gmem.h>

typedef struct
{
  GBuffer buffer;

  GDestroyNotify user_destroy;
  gpointer user_data;
} GUserNotifyBuffer;

static void
g_buffer_free_gfree (GBuffer *buffer)
{
  g_free ((gpointer) buffer->data);
  g_slice_free (GBuffer, buffer);
}

/* < private >
 * g_buffer_new_from_data:
 * @data: the data to be used for the buffer
 * @size: the size of @data
 * @returns: a reference to a new #GBuffer
 *
 * Creates a new #GBuffer from @data.
 *
 * @data is copied.
 */

GBuffer *
g_buffer_new_from_data (gconstpointer data,
                        gsize         size)
{
  GBuffer *buffer;

  buffer = g_slice_new (GBuffer);
  buffer->data = g_memdup (data, size);
  buffer->size = size;
  buffer->free_func = g_buffer_free_gfree;
  buffer->ref_count = 1;

  return buffer;
}

/* < private >
 * g_buffer_new_take_data:
 * @data: the data to be used for the buffer
 * @size: the size of @data
 * returns: a reference to a new #GBuffer
 *
 * Creates a new #GBuffer from @data.
 *
 * @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).
 *
 * After this call, @data belongs to the buffer and may no longer be
 * modified by the caller.  g_free() will be called on @data when the
 * buffer is no longer in use.
 */
GBuffer *
g_buffer_new_take_data (gpointer data,
                        gsize    size)
{
  GBuffer *buffer;

  buffer = g_slice_new (GBuffer);
  buffer->data = data;
  buffer->size = size;
  buffer->free_func = g_buffer_free_gfree;
  buffer->ref_count = 1;

  return buffer;
}

static void
g_buffer_free (GBuffer *buffer)
{
  g_slice_free (GBuffer, buffer);
}

/* < private >
 * g_buffer_new_from_static_data:
 * @data: the data to be used for the buffer
 * @size: the size of @data
 * @returns: a reference to a new #GBuffer
 *
 * Creates a new #GBuffer from static data.
 *
 * @data must be static (ie: never modified or freed).
 */
GBuffer *
g_buffer_new_from_static_data (gconstpointer data,
                               gsize         size)
{
  GBuffer *buffer;

  buffer = g_slice_new (GBuffer);
  buffer->data = data;
  buffer->size = size;
  buffer->free_func = g_buffer_free;
  buffer->ref_count = 1;

  return buffer;
}

static void
g_buffer_free_usernotify (GBuffer *buffer)
{
  GUserNotifyBuffer *ubuffer = (GUserNotifyBuffer *) buffer;

  ubuffer->user_destroy (ubuffer->user_data);
  g_slice_free (GUserNotifyBuffer, ubuffer);
}

/* < private >
 * g_buffer_new_from_pointer:
 * @data: the data to be used for the buffer
 * @size: the size of @data
 * @notify: the function to call to release the data
 * @user_data: the data to pass to @notify
 * @returns: a reference to a new #GBuffer
 *
 * Creates a #GBuffer from @data.
 *
 * When the last reference is dropped, @notify will be called on
 * @user_data.
 *
 * @data must not be modified after this call is made, until @notify has
 * been called to indicate that the buffer is no longer in use.
 */
GBuffer *
g_buffer_new_from_pointer (gconstpointer  data,
                           gsize          size,
                           GDestroyNotify notify,
                           gpointer       user_data)
{
  GUserNotifyBuffer *ubuffer;

  ubuffer = g_slice_new (GUserNotifyBuffer);
  ubuffer->buffer.data = data;
  ubuffer->buffer.size = size;
  ubuffer->buffer.free_func = g_buffer_free_usernotify;
  ubuffer->buffer.ref_count = 1;
  ubuffer->user_destroy = notify;
  ubuffer->user_data = user_data;

  return (GBuffer *) ubuffer;
}

/* < private >
 * g_buffer_ref:
 * @buffer: a #GBuffer
 * @returns: @buffer
 *
 * Increase the reference count on @buffer.
 */
GBuffer *
g_buffer_ref (GBuffer *buffer)
{
  g_atomic_int_inc (&buffer->ref_count);

  return buffer;
}

/* < private >
 * g_buffer_unref:
 * @buffer: a #GBuffer
 *
 * Releases a reference on @buffer.  This may result in the buffer being
 * freed.
 */
void
g_buffer_unref (GBuffer *buffer)
{
  if (g_atomic_int_dec_and_test (&buffer->ref_count))
    if (buffer->free_func != NULL)
      buffer->free_func (buffer);
}
