/*
* Copyright (c) 2010, Oracle America, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * 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.
*     * Neither the name of the "Oracle America, Inc." 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 COPYRIGHT HOLDERS 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
*   COPYRIGHT HOLDER 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.
*/
/*
* xdr_sizeof.c
*
* Copyright 1990 Sun Microsystems, Inc.
*
* General purpose routine to see how much space something will use
* when serialized using XDR.
*/

#include <sys/cdefs.h>
#if 0
__FBSDID("$FreeBSD: src/lib/libc/xdr/xdr_sizeof.c,v 1.5.38.1 2010/12/21 17:10:29 kensmith Exp $");
#else
__RCSID("$NetBSD: xdr_sizeof.c,v 1.5 2013/03/11 20:19:30 tron Exp $");
#endif

#include "namespace.h"
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <sys/types.h>
#include <stdlib.h>

#ifdef __weak_alias
__weak_alias(xdr_sizeof,_xdr_sizeof)
#endif

static bool_t x_putlong(XDR *, const long *);
static bool_t x_putbytes(XDR *, const char *, u_int);
static u_int x_getpostn(XDR *);
static bool_t x_setpostn(XDR *, u_int);
static int32_t *x_inline(XDR *, u_int);
static int harmless(void);
static void x_destroy(XDR *);

/* ARGSUSED */
static bool_t
x_putlong(XDR *xdrs, const long *longp)
{
       xdrs->x_handy += BYTES_PER_XDR_UNIT;
       return (TRUE);
}

/* ARGSUSED */
static bool_t
x_putbytes(XDR *xdrs, const char *bp, u_int len)
{
       xdrs->x_handy += len;
       return (TRUE);
}

static u_int
x_getpostn(XDR *xdrs)
{
       return (xdrs->x_handy);
}

/* ARGSUSED */
static bool_t
x_setpostn(XDR *xdrs, u_int pos)
{
       /* This is not allowed */
       return (FALSE);
}

static int32_t *
x_inline(XDR *xdrs, u_int len)
{
       if (len == 0) {
               return (NULL);
       }
       if (xdrs->x_op != XDR_ENCODE) {
               return (NULL);
       }
       if (len < (u_int)(uintptr_t)xdrs->x_base) {
               /* x_private was already allocated */
               xdrs->x_handy += len;
               return ((int32_t *) xdrs->x_private);
       } else {
               /* Free the earlier space and allocate new area */
               if (xdrs->x_private)
                       free(xdrs->x_private);
               if ((xdrs->x_private = malloc(len)) == NULL) {
                       xdrs->x_base = 0;
                       return (NULL);
               }
               xdrs->x_base = (caddr_t)(uintptr_t)len;
               xdrs->x_handy += len;
               return ((int32_t *) xdrs->x_private);
       }
}

static int
harmless(void)
{
       /* Always return FALSE/NULL, as the case may be */
       return (0);
}

static void
x_destroy(XDR *xdrs)
{
       xdrs->x_handy = 0;
       xdrs->x_base = 0;
       if (xdrs->x_private) {
               free(xdrs->x_private);
               xdrs->x_private = NULL;
       }
       return;
}

unsigned long
xdr_sizeof(xdrproc_t func, void *data)
{
       XDR x;
       struct xdr_ops ops;
       bool_t stat;
       /* to stop ANSI-C compiler from complaining */
       typedef  bool_t (* dummyfunc1)(XDR *, long *);
       typedef  bool_t (* dummyfunc2)(XDR *, caddr_t, u_int);

       ops.x_putlong = x_putlong;
       ops.x_putbytes = x_putbytes;
       ops.x_inline = x_inline;
       ops.x_getpostn = x_getpostn;
       ops.x_setpostn = x_setpostn;
       ops.x_destroy = x_destroy;

       /* the other harmless ones */
       ops.x_getlong =  (dummyfunc1) harmless;
       ops.x_getbytes = (dummyfunc2) harmless;

       x.x_op = XDR_ENCODE;
       x.x_ops = &ops;
       x.x_handy = 0;
       x.x_private = (caddr_t) NULL;
       x.x_base = (caddr_t) 0;

       stat = func(&x, data);
       if (x.x_private)
               free(x.x_private);
       return (stat == TRUE ? (unsigned) x.x_handy: 0);
}