/*      $NetBSD: prop_data.c,v 1.6 2007/03/04 22:31:43 dillo Exp $      */

/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed by the NetBSD
*      Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
*    contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <prop/prop_data.h>
#include "prop_object_impl.h"

#if defined(_KERNEL)
#include <sys/systm.h>
#elif defined(_STANDALONE)
#include <sys/param.h>
#include <lib/libkern/libkern.h>
#else
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#endif

_PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")

_PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
                   "property data container object")

static void             _prop_data_free(void *);
static boolean_t        _prop_data_equals(void *, void *);

static const struct _prop_object_type _prop_object_type_data = {
       .pot_type       =       PROP_TYPE_DATA,
       .pot_free       =       _prop_data_free,
       .pot_equals     =       _prop_data_equals,
};

#define prop_object_is_data(x)          \
       ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)

static void
_prop_data_free(void *v)
{
       prop_data_t pd = v;

       if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
               _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
       _PROP_POOL_PUT(_prop_data_pool, v);
}

static boolean_t
_prop_data_equals(void *v1, void *v2)
{
       prop_data_t pd1 = v1;
       prop_data_t pd2 = v2;

       if (! (prop_object_is_data(pd1) &&
              prop_object_is_data(pd2)))
               return (FALSE);

       if (pd1 == pd2)
               return (TRUE);
       if (pd1->pd_size != pd2->pd_size)
               return (FALSE);
       if (pd1->pd_size == 0) {
               _PROP_ASSERT(pd1->pd_immutable == NULL);
               _PROP_ASSERT(pd2->pd_immutable == NULL);
               return (TRUE);
       }
       return (memcmp(pd1->pd_immutable, pd2->pd_immutable,
                      pd1->pd_size) == 0);
}

prop_data_t
_prop_data_alloc(void)
{
       prop_data_t pd;

       pd = _PROP_POOL_GET(_prop_data_pool);
       if (pd != NULL) {
               _prop_object_init(&pd->pd_obj, &_prop_object_type_data);

               pd->pd_mutable = NULL;
               pd->pd_size = 0;
               pd->pd_flags = 0;
       }

       return (pd);
}

/*
* prop_data_create_data --
*      Create a data container that contains a copy of the data.
*/
prop_data_t
prop_data_create_data(const void *v, size_t size)
{
       prop_data_t pd;
       void *nv;

       pd = _prop_data_alloc();
       if (pd != NULL) {
               nv = _PROP_MALLOC(size, M_PROP_DATA);
               if (nv == NULL) {
                       _prop_data_free(pd);
                       return (NULL);
               }
               memcpy(nv, v, size);
               pd->pd_mutable = nv;
               pd->pd_size = size;
       }
       return (pd);
}

/*
* prop_data_create_data_nocopy --
*      Create an immutable data container that contains a refrence to the
*      provided external data.
*/
prop_data_t
prop_data_create_data_nocopy(const void *v, size_t size)
{
       prop_data_t pd;

       pd = _prop_data_alloc();
       if (pd != NULL) {
               pd->pd_immutable = v;
               pd->pd_size = size;
               pd->pd_flags |= PD_F_NOCOPY;
       }
       return (pd);
}

/*
* prop_data_copy --
*      Copy a data container.  If the original data is external, then
*      the copy is also references the same external data.
*/
prop_data_t
prop_data_copy(prop_data_t opd)
{
       prop_data_t pd;

       if (! prop_object_is_data(opd))
               return (NULL);

       pd = _prop_data_alloc();
       if (pd != NULL) {
               pd->pd_size = opd->pd_size;
               pd->pd_flags = opd->pd_flags;
               if (opd->pd_flags & PD_F_NOCOPY)
                       pd->pd_immutable = opd->pd_immutable;
               else if (opd->pd_size != 0) {
                       void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
                       if (nv == NULL) {
                               _prop_data_free(pd);
                               return (NULL);
                       }
                       memcpy(nv, opd->pd_immutable, opd->pd_size);
                       pd->pd_mutable = nv;
               }
       }
       return (pd);
}

/*
* prop_data_size --
*      Return the size of the data.
*/
size_t
prop_data_size(prop_data_t pd)
{

       if (! prop_object_is_data(pd))
               return (0);

       return (pd->pd_size);
}

/*
* prop_data_data --
*      Return a copy of the contents of the data container.
*      The data is allocated with the M_TEMP malloc type.
*      If the data container is empty, NULL is returned.
*/
void *
prop_data_data(prop_data_t pd)
{
       void *v;

       if (! prop_object_is_data(pd))
               return (NULL);

       if (pd->pd_size == 0) {
               _PROP_ASSERT(pd->pd_immutable == NULL);
               return (NULL);
       }

       _PROP_ASSERT(pd->pd_immutable != NULL);

       v = _PROP_MALLOC(pd->pd_size, M_TEMP);
       if (v != NULL)
               memcpy(v, pd->pd_immutable, pd->pd_size);

       return (v);
}

/*
* prop_data_data_nocopy --
*      Return an immutable reference to the contents of the data
*      container.
*/
const void *
prop_data_data_nocopy(prop_data_t pd)
{

       if (! prop_object_is_data(pd))
               return (NULL);

       _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) ||
                    (pd->pd_size != 0 && pd->pd_immutable != NULL));

       return (pd->pd_immutable);
}

/*
* prop_data_equals --
*      Return TRUE if two strings are equivalent.
*/
boolean_t
prop_data_equals(prop_data_t pd1, prop_data_t pd2)
{

       return (_prop_data_equals(pd1, pd2));
}

/*
* prop_data_equals_data --
*      Return TRUE if the contained data is equivalent to the specified
*      external data.
*/
boolean_t
prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
{

       if (! prop_object_is_data(pd))
               return (FALSE);

       if (pd->pd_size != size)
               return (FALSE);
       return (memcmp(pd->pd_immutable, v, size) == 0);
}