/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "mas/mas_common.h"

static void  _extract_header_info( struct mas_package* package );
static int32 _realloc_if_necessary( struct mas_package *package, int size_needed );
static int32 _extract_header_if_necessary( struct mas_package *package );
static int32 _pkgerr( struct mas_package *p, int32 err );


int32
masc_pushk_int8( struct mas_package* package, char* key, int8 val )
{
    int klen = strlen( key ) + 1;
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_INT8;
    strcpy( package->p, key );
    package->p += klen;
    *(int8*)package->p = val; /* can't put ++ on this line - ISO C forbids */
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_uint8( struct mas_package* package, char* key, uint8 val )
{
    int klen = strlen( key ) + 1;
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_UINT8;
    strcpy( package->p, key );
    package->p += klen;
    *(uint8*)package->p = val;
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_int16( struct mas_package* package, char* key, int16 val )
{
    int klen = strlen( key ) + 1;
    int16 v = (package->nbo)?mas_htons(val):mas_htoles(val);
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_INT16;
    strcpy( package->p, key );
    package->p += klen;
/*    *(int16*)package->p = mas_htons(val);  */ /* unaligned */
    memcpy(package->p, &v, sizeof v);
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_uint16( struct mas_package* package, char* key, uint16 val )
{
    int klen = strlen( key ) + 1;
    uint16 v = (package->nbo)?mas_htons(val):mas_htoles(val);
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_UINT16;
    strcpy( package->p, key );
    package->p += klen;
/*    *(uint16*)package->p = mas_htons(val);  */
    memcpy(package->p, &v, sizeof v);
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_int32( struct mas_package* package, char* key, int32 val )
{
    int klen = strlen( key ) + 1;
    int32 v = (package->nbo)?mas_htonl(val):mas_htolel(val);
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_INT32;
    strcpy( package->p, key );
    package->p += klen;
/*    *(int32*)package->p = mas_htonl(val); */ /* unaligned */
    memcpy( package->p, &v, sizeof v );
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_uint32( struct mas_package* package, char* key, uint32 val )
{
    int klen = strlen( key ) + 1;
    uint32 v = (package->nbo)?mas_htonl(val):mas_htolel(val);
    int size_needed = klen + sizeof val + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + klen + sizeof val;
    *(package->p++) = MAS_UINT32;
    strcpy( package->p, key );
    package->p += klen;
/*     *(uint32*)package->p = mas_htonl(val);*/ /* unaligned */
    memcpy( package->p, &v, sizeof v );
    package->p += sizeof val;

    return 0;
}

int32
masc_pushk_float( struct mas_package* package, char* key, float val )
{
    int klen = strlen( key ) + 1;
    int size_needed;

    /* We experienced byte ordering problems when going i386<->sparc.
       Until we figure out all the ways different architectures arrange
       their IEEE floats, here's a fix that transfers the number as a string. */
    int len;
    int error;
    char string[MAX_STRING_LENGTH];
    error= snprintf( string, MAX_STRING_LENGTH, "%f", val );
    mas_assert(  error < MAX_STRING_LENGTH, "number representation too long" );
    len = strlen(string) + 1;

    size_needed = klen + len + 1;
    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += size_needed;
    *(package->p++) = MAS_FLOAT;
    strcpy( package->p, key );
    package->p += klen;
    strcpy( package->p, string );
    package->p += len;

    return 0;
}

int32
masc_pushk_double( struct mas_package* package, char* key, double val )
{
    int klen = strlen( key ) + 1;
    int         size_needed;
    
    /* We experienced byte ordering problems when going i386<->sparc.
       Until we figure out all the ways different architectures arrange
       their IEEE floats, here's a fix that transfers the number as a string. */
    int len;
    int error;
    char string[MAX_STRING_LENGTH];
    error= snprintf( string, MAX_STRING_LENGTH, "%.16f", val );
    mas_assert(  error < MAX_STRING_LENGTH, "number representation too long" );
    len = strlen(string) + 1;
    
    size_needed = klen + len + 1;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += size_needed;
    *(package->p++) = MAS_DOUBLE;
    strcpy( package->p, key );
    package->p += klen;
    strcpy( package->p, string );
    package->p += len;

    return 0;
}

int32
masc_pushk_string( struct mas_package* package, char* key, char* string )
{
    int         klen = strlen( key ) + 1;
    int		len;
    char	null_string[] = "\0";
    int         size_needed;

    /* If the pointer passed in is NULL, then use a null string to push */
    if(!string)
	string = null_string;

    len = strlen(string);
    size_needed = klen + len + 2;

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 2 + len + klen;
    *(package->p++) = MAS_STRING;
    strcpy( package->p, key );
    package->p += klen;
    strcpy( package->p, string );
    package->p += len + 1;

    return 0;
}

int32
masc_pushk_package( struct mas_package* package, char* key, struct mas_package* ppack )
{
    int    klen = strlen( key ) + 1;
    uint32 l;
    int size_needed = 0;

    if ( ppack == NULL )
        return _pkgerr(package, MERR_NULLPTR);
    
    l = (package->nbo)?mas_htonl( ppack->size ):mas_htolel(ppack->size);
    size_needed = klen + ppack->size + 1 + sizeof (uint32);

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + sizeof (uint32) + klen + ppack->size;
    *(package->p++) = MAS_PACKAGE;
    strcpy( package->p, key );
    package->p += klen;
/*    *(uint32*)package->p = len;*/ /* unaligned */
    memcpy( package->p, &l, sizeof l );
    package->p += sizeof (uint32);

    /* if ppack->size is 0, the memcpy isn't done. */
    if ( ppack->size > 0 && ppack->contents != NULL )
        memcpy( package->p, ppack->contents, ppack->size );
    package->p += ppack->size;

    return 0;
}

int32
masc_pushk_payload( struct mas_package* package, char* key, void* payload, uint32 len )
{
    int    klen = strlen( key ) + 1;
    uint32 l = (package->nbo)?mas_htonl( len ):mas_htolel(len);
    int size_needed = klen + len + 1 + sizeof (uint32);

    _realloc_if_necessary( package, size_needed );
    if ( package->error < 0 )
        return package->error;
    
    package->members++;
    package->size += 1 + sizeof (uint32) + klen + len;
    *(package->p++) = MAS_PAYLOAD;
    strcpy( package->p, key );
    package->p += klen;
/*    *(uint32*)package->p = len;*/ /* unaligned */
    memcpy( package->p, &l, sizeof l );
    package->p += sizeof (uint32);

    /* if payload is NULL, the memcpy isn't done. */
    if ( len > 0 && payload != NULL )
        memcpy( package->p, payload, len );
    package->p += len;

    return 0;
}

int32
masc_push_int8( struct mas_package* package, int8 val )
{
    return masc_pushk_int8( package, "", val );
}

int32
masc_push_uint8( struct mas_package* package, uint8 val )
{
    return masc_pushk_uint8( package, "", val );
}

int32
masc_push_int16( struct mas_package* package, int16 val )
{
    return masc_pushk_int16( package, "", val );
}

int32
masc_push_uint16( struct mas_package* package, uint16 val )
{
    return masc_pushk_uint16( package, "", val );
}

int32
masc_push_int32( struct mas_package* package, int32 val )
{
    return masc_pushk_int32( package, "", val );
}

int32
masc_push_uint32( struct mas_package* package, uint32 val )
{
    return masc_pushk_uint32( package, "", val );
}

int32
masc_push_float( struct mas_package* package, float val )
{
    return masc_pushk_float( package, "", val );
}

int32
masc_push_double( struct mas_package* package, double val )
{
    return masc_pushk_double( package, "", val );
}

int32
masc_push_string( struct mas_package* package, char* string )
{
    return masc_pushk_string( package, "", string );
}

int32
masc_push_strings( struct mas_package* package, char** strings, int num )
{
    int i;
    int32 err;
    
    for (i=0; i<num; i++)
    {
        err = masc_push_string( package, strings[i] );
        if ( err < 0 ) return err;
    }

    return 0;
}

int32
masc_push_package( struct mas_package* package, struct mas_package *ppack )
{
    return masc_pushk_package( package, "", ppack );
}

int32
masc_push_payload( struct mas_package* package, void* payload, uint32 len )
{
    return masc_pushk_payload( package, "", payload, len );
}

int32
masc_pullk_int8( struct mas_package* package, char* key, int8* retval )
{
    int32 err;

    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_int8( package, retval );
}

int32
masc_pullk_uint8( struct mas_package* package, char* key, uint8* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_uint8( package, retval );
}


int32
masc_pullk_int16( struct mas_package* package, char* key, int16* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_int16( package, retval );
}


int32
masc_pullk_uint16( struct mas_package* package, char* key, uint16* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_uint16( package, retval );
}


int32
masc_pullk_int32( struct mas_package* package, char* key, int32* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_int32( package, retval );
}


int32
masc_pullk_uint32( struct mas_package* package, char* key, uint32* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_uint32( package, retval );
}


int32
masc_pullk_float( struct mas_package* package, char* key, float* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_float( package, retval );
}

int32
masc_pullk_double( struct mas_package* package, char* key, double* retval )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_double( package, retval );
}

int32
masc_pullk_string( struct mas_package* package, char* key, char** string_retval, int copy )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_string( package, string_retval, copy );
}

int32
masc_pullk_payload( struct mas_package* package, char* key, void** payload_retval, uint32* len_retval, int copy )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_payload( package, payload_retval, len_retval, copy );
}

int32
masc_pullk_package( struct mas_package* package, char* key, struct mas_package *ppack, int copy )
{
    int32 err;
    
    if ( key != NULL )
    {
        err = masc_find_key( package, key );
        mas_assert(  err >= 0, "key not found in package" );
        if ( err < 0 ) return _pkgerr( package, MERR_NOTDEF);
    }
    
    return masc_pull_package( package, ppack, copy );
}

int32
masc_pull_int8( struct mas_package* package, int8* retval )
{
    int c = 0;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_INT8, "can't pull wrong type" );
    if ( *(package->p++) != MAS_INT8 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");

    *retval = *(int8*)package->p;
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_uint8( struct mas_package* package, uint8* retval )
{
    int c = 0;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_UINT8, "can't pull wrong type" );
    if ( *(package->p++) != MAS_UINT8 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    *retval = *(uint8*)package->p;
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_int16( struct mas_package* package, int16* retval )
{
    int c = 0;
    int16 v;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_INT16, "can't pull wrong type" );
    if ( *(package->p++) != MAS_INT16 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( &v, package->p, sizeof v );
/*    *retval = mas_ntohs( *(int16*)package->p ); */ 
    *retval = (package->nbo)?mas_ntohs( v ):mas_letohs( v );
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_uint16( struct mas_package* package, uint16* retval )
{
    int c = 0;
    uint16 v;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_UINT16, "can't pull wrong type" );
    if ( *(package->p++) != MAS_UINT16 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( &v, package->p, sizeof v );
/*    *retval = mas_ntohs( *(uint16*)package->p ); */ /* unaligned */
    *retval = (package->nbo)?mas_ntohs( v ):mas_letohs( v );
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_int32( struct mas_package* package, int32* retval )
{
    int c = 0;
    int32 v;

    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_INT32, "can't pull wrong type" );
    if ( *(package->p++) != MAS_INT32 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( &v, package->p, sizeof v );
/*    *retval = mas_ntohl( *(int32*)package->p ); */ /* unaligned */
    *retval = (package->nbo)?mas_ntohl( v ):mas_letohl( v );
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_uint32( struct mas_package* package, uint32* retval )
{
    int c = 0;
    uint32 v;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_UINT32, "can't pull wrong type" );
    if ( *(package->p++) != MAS_UINT32 )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( &v, package->p, sizeof v );
/*     *retval = mas_ntohl( *(uint32*)package->p ); */ /* unaligned */
    *retval = (package->nbo)?mas_ntohl( v ):mas_letohl( v );
    package->p += sizeof *retval;

    return 0;
}


int32
masc_pull_float( struct mas_package* package, float* retval )
{
    int c = 0;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_FLOAT, "can't pull wrong type" );
    if ( *(package->p++) != MAS_FLOAT )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    /* the following caused byte ordering problems when going i386<->sparc
       until we figure out all the ways different architectures arrange
       their IEEE floats, here's a fix that transfers the number as a string. */
    /*      memcpy( &v, package->p, sizeof v ); */
    /*      *retval = v; */
    sscanf( (char*)(package->p), "%f", retval );
    package->p += strlen((char*)(package->p)) + 1;

    return 0;
}

int32
masc_pull_double( struct mas_package* package, double* retval )
{
    int c = 0;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_DOUBLE, "can't pull wrong type" );
    if ( *(package->p++) != MAS_DOUBLE )
	return _pkgerr( package, MERR_INVALID);

    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    /* the following caused byte ordering problems when going i386<->sparc
       until we figure out all the ways different architectures arrange
       their IEEE floats, here's a fix that transfers the number as a string. */
    /*      memcpy( &v, package->p, sizeof v ); */
    /*      *retval = v; */
    sscanf( (char*)(package->p), "%lf", retval );
    package->p += strlen((char*)(package->p)) + 1;

    return 0;
}

int32
masc_pull_string( struct mas_package* package, char** string_retval, int copy )
{
    int c = 0;
    int len;

    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_STRING, "can't pull wrong type" );
    if ( *(package->p++) != MAS_STRING )
	return _pkgerr( package, MERR_INVALID);
    
    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    len = strlen(package->p) + 1;

    if ( copy )
    {
        *string_retval = masc_rtalloc( len );
        strcpy( *string_retval, package->p );
    }
    else
    {
        *string_retval = package->p;
    }
    
    package->p += len;

    return 0;
}

int32
masc_pull_payload( struct mas_package* package, void** payload_retval, uint32* len_retval, int copy )
{
    int c = 0;

    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;

    /* Pull payload can pull either package or payload types.  If you
     * use it to pull a package out, you will need to setup a package
     * out of the bytesequence using some other means. */
    mas_assert( ( *(package->p) == MAS_PAYLOAD || *(package->p) == MAS_PACKAGE ), "can't pull wrong type" );
    if ( *(package->p) != MAS_PAYLOAD && *(package->p) != MAS_PACKAGE ) 
	return _pkgerr( package, MERR_INVALID);
    package->p++;
    
    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( len_retval, package->p, sizeof (uint32) );
    *len_retval = (package->nbo)?mas_ntohl( *len_retval ):mas_letohl( *len_retval );
/*    *len_retval = *(uint32*)package->p; */ /* unaligned */
    package->p += sizeof (uint32);
    if ( *len_retval > 0 )
    {
        if (copy)
        {
            *payload_retval = masc_rtalloc( *len_retval );
            if ( *payload_retval == 0 ) return _pkgerr( package, MERR_MEMORY);
            memcpy( *payload_retval, package->p, *len_retval );
        }
        else
        {
            *payload_retval = package->p;
        }
        
    }
    else /* *len_retval <= 0 */
    {
        *payload_retval = 0;
    }
    
    package->p += *len_retval;

    return 0;
}


int32
masc_pull_package( struct mas_package* package, struct mas_package* retpack, int copy )
{
    int c = 0;
    uint32 len;
    void *contents;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    _extract_header_if_necessary( package );
    if ( package->error < 0 )
        return package->error;
    
    mas_assert( *(package->p) == MAS_PACKAGE, "can't pull wrong type" );
    if ( *(package->p++) != MAS_PACKAGE ) 
	return _pkgerr( package, MERR_INVALID);
    
    /* skip over the key */
    while (*package->p != 0 && c++ < MAX_STRING_LENGTH ) package->p++;
    package->p++; /* and the terminator */
    mas_assert( c < MAX_STRING_LENGTH, "package key string is too long");
    
    memcpy( &len, package->p, sizeof (uint32) );
    len = (package->nbo)?mas_ntohl( len ):mas_letohl( len );
    package->p += sizeof (uint32);
    if ( len > 0 )
    {
        if (copy)
        {
            /* bytecopy the package contents */
            contents = masc_rtalloc( len );
            if ( contents == NULL ) return _pkgerr( package, MERR_MEMORY);
            memcpy( contents, package->p, len );
            masc_setup_package( retpack, contents, len, MASC_PACKAGE_EXTRACT);
        }
        else
        {
            masc_setup_package( retpack, package->p, len, MASC_PACKAGE_STATIC|MASC_PACKAGE_EXTRACT );
        }
        
    }
    else
    {
        retpack->contents = NULL;
    }
    
    package->p += len;

    return 0;
}


int32
masc_finalize_package( struct mas_package* package )
{
    uint32 hmc = 0; /* header and member count */

    if ( package->error < 0 )
        return package->error;
    
#ifdef MAS_LITTLE_ENDIAN
    hmc |= package->v << 4;
    hmc |= 0x07 & package->aw;
    if (package->nbo)
    {
        hmc |= mas_htons(package->members) << 16;
    }
    else
    {
        hmc |= 0x08; /* set B flag */
        hmc |= package->members << 16;
    }
#else /* BIG ENDIAN */
    hmc |= package->v << 28;
    hmc |= (0x07 & package->aw) << 24;
    if (package->nbo)
    {
        hmc |= package->members;
    }
    else
    {
        hmc |= 0x08000000; /* set B flag */
        hmc |= mas_htoles(package->members);
    }    
#endif

    /* the beginning of package->contents should be okay for alignment
     * of 32-bit ints. */
    memcpy(package->contents, &hmc, sizeof hmc);

    /* reset the p */
    package->p = package->contents + sizeof hmc;

    return 0;
}

/* masc_setup_package
 *
 * If "buffer" is NULL, memory will be allocated for the package's
 * contents.  The amount of memory allocated is the maximum of
 * MIN_PACKAGE_SIZE and "size".
 * 
 * If "buffer" and "size" are non-NULL, "buffer" is used as the
 * package's contents and "size" is the allocated size of the buffer.
 *
 * If pflags has the MASC_PACKAGE_STATIC flag set, the package's
 * contents are considered to be statically allocated.  The contents
 * will not be free()d by masc_strike_package().  The contents may
 * still be realloc()ed, however (see MASC_PACKAGE_NOREALLOC below),
 * in which case the MASC_PACKAGE_STATIC flag in the package structure
 * will be cleared.
 *
 * If pflags has the MASC_PACKAGE_EXTRACT flag set, the contents of
 * "buffer" will be interpreted as a package byte-sequence, and the
 * package's header information will be extracted from it.  The buffer
 * is unchanged.
 *
 * If pflags has the MASC_PACKAGE_NOFREE flag set, the package's
 * contents will not be free()d by masc_strike_package().
 *
 * If pflags has the MASC_PACKAGE_NOREALLOC flag set, the package's
 * contents will not be realloced() if more memory is needed during a
 * push operation.  In this case, the push will fail and the error
 * member of the package struct will be set to the status code.
 *
 */

int32
masc_setup_package( struct mas_package* package, char *buffer, int size, int pflags )
{
    memset( package, 0, sizeof *package );
    
    package->flags = pflags;

    /* default: if STATIC is set, also set NOFREE */
    if ( package->flags & MASC_PACKAGE_STATIC )
        package->flags |= MASC_PACKAGE_NOFREE;
    
    if ( buffer )
    {
        package->contents = buffer;
        package->allocated_size = size;
    }
    else if ( ! (pflags & MASC_PACKAGE_STATIC) )
    {
        package->allocated_size = size?size:MIN_PACKAGE_SIZE;
        package->contents = masc_rtalloc( package->allocated_size );
        if ( package->contents == 0 )
            return _pkgerr( package, MERR_MEMORY);
    }
    else
    {
        /* static flag set, but no buffer specified -- can't allocate
           memory! */
        return _pkgerr( package, MERR_INVALID);
    }
    
    package->p = package->contents + sizeof (uint32);
    package->size = sizeof (uint32);
        
    /* by default, the packet's byte ordering is the sender's byte
       order.  The recipient gets to convert.  */
#ifdef MAS_LITTLE_ENDIAN
    package->nbo = FALSE;
#else
    package->nbo = TRUE;
#endif

    if ( pflags & MASC_PACKAGE_EXTRACT )
    {
        _extract_header_info( package );

        /* use supplied size, if given */
        if ( size > 0 )
            package->size = size;
    }
    

    return 0;
}

int32
masc_strike_package( struct mas_package* package )
{
    /* only free contents member if nofree isn't true and it's not
       statically allocated */
    if ( package->contents && ! ( package->flags & ( MASC_PACKAGE_NOFREE | MASC_PACKAGE_STATIC ) ) )
        masc_rtfree( package->contents );

    /* free the key array, but not each element individually, since
     * those were part of contents. */
    if ( package->key ) masc_rtfree( package->key );
    
    return 0;
}

int32
masc_pushk_members( struct mas_package* package, char* format, ... )
{
    int i;
    int temp;
    int size;
    int members;
    double dtmp;
    char* key;
    char* strtmp;
    struct mas_package *packtmp;
    char* f;
    int argsize[64];
    va_list ap;
    
    members = 0;

    members = strlen(format);
    mas_assert( members <= 64, "masc_push_members only supports 64 members" );

    i=0;
    size = sizeof (uint32);
    
    va_start( ap, format );
    /* walk the format string */
    for (f=format; (*f)!=0; f++)
    {
        /* just pick up the sizes and pointers to the locations */
        /* compute the exact memory requirements */
        switch (*f)
        {
        case MAS_INT8: key = va_arg(ap, char *);
            temp = va_arg(ap, int);
            argsize[i] = sizeof (int8);
            break;
        case MAS_UINT8: key = va_arg(ap, char *);
            temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint8);
            break;
        case MAS_INT16:  key = va_arg(ap, char *);
            temp = va_arg(ap, int); 
            argsize[i] = sizeof (int16);
            break;
        case MAS_UINT16: key = va_arg(ap, char *);
            temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint16);
            break;
        case MAS_INT32: key = va_arg(ap, char *);
            temp = va_arg(ap, int); 
            argsize[i] = sizeof (int32);
            break;
        case MAS_UINT32: key = va_arg(ap, char *);
            temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint32);
            break;
        case MAS_FLOAT: key = va_arg(ap, char *);
            dtmp = va_arg(ap, double); 
            argsize[i] = sizeof (float);
            break;
        case MAS_DOUBLE: key = va_arg(ap, char *);
            dtmp = va_arg(ap, double); 
            argsize[i] = sizeof (double);
            break;
        case MAS_STRING: key = va_arg(ap, char *);
            strtmp = va_arg(ap, char *); 
            argsize[i] = strlen(strtmp) + 1;
            break;
        case MAS_PACKAGE: key = va_arg(ap, char *);
            packtmp = va_arg(ap, void *);
            argsize[i] = packtmp->size;
            break;
        case MAS_PAYLOAD: key = va_arg(ap, char *);
            strtmp = va_arg(ap, char *);
            size += sizeof (uint32);
            argsize[i] = (int)va_arg(ap, uint32);
            break;
        default: mas_assert( 0, "invalid format code" ); break;
        }
        size += argsize[i] + 2; /* one for type, one for null key */
        i++;
    }
    va_end( ap );    
    
    package->contents = masc_rtalloc( size );
    if ( package->contents == 0 ) return _pkgerr( package, MERR_MEMORY);
    package->allocated_size = size;
    package->size = sizeof (uint32);
    package->members = 0;
    package->v = 0;
    package->aw = 0;
    /* by default, the packet's byte ordering is the sender's byte
       order.  The recipient gets to convert.  */
#ifdef MAS_LITTLE_ENDIAN
    package->nbo = FALSE;
#else
    package->nbo = TRUE;
#endif
    /* package data starts after the header and "package->aw"
       additional 32-bit words */
    package->p = package->contents + sizeof (uint32) + ( package->aw * sizeof (uint32) );
    memset( package->contents, 0, size);

    
    va_start( ap, format );
    for (i=0; i<members; i++)
    {
        switch (format[i])
        {
        case MAS_INT8:
            key = va_arg(ap, char *);
            masc_pushk_int8( package, key, (int8) va_arg(ap, int) ); break;
        case MAS_UINT8:
            key = va_arg(ap, char *);
            masc_pushk_uint8( package, key, (uint8) va_arg(ap, int) ); break;
        case MAS_INT16:
            key = va_arg(ap, char *);
            masc_pushk_int16( package, key, (int16) va_arg(ap, int) ); break;
        case MAS_UINT16:
            key = va_arg(ap, char *);
            masc_pushk_uint16( package, key, (uint16) va_arg(ap, int) ); break;
        case MAS_INT32: 
            key = va_arg(ap, char *);
            masc_pushk_int32( package, key, (int32) va_arg(ap, int) ); break;
        case MAS_UINT32:
            key = va_arg(ap, char *);
            masc_pushk_uint32( package, key, (uint32) va_arg(ap, int) ); break;
        case MAS_FLOAT: 
            key = va_arg(ap, char *);
            masc_pushk_float( package, key, (float) va_arg(ap, double) ); break;
        case MAS_DOUBLE:
            key = va_arg(ap, char *);
            masc_pushk_double( package, key, (double) va_arg(ap, double) ); break;
        case MAS_STRING: 
            key = va_arg(ap, char *);
            masc_pushk_string( package, key, va_arg(ap, char *) ); break;
        case MAS_PACKAGE:
            key = va_arg(ap, char *);
            masc_pushk_package( package, key, va_arg(ap, void *) );
        case MAS_PAYLOAD: 
            key = va_arg(ap, char *);
            masc_pushk_payload( package, key, va_arg(ap, char *), argsize[i] );
            temp = va_arg(ap, int); break;
        default: break;
        }
    }
    va_end( ap );    

    masc_finalize_package( package );

    return 0;
    
}

int32
masc_push_members( struct mas_package* package, char* format, ... )
{
    int i;
    int temp;
    int size;
    int members;
    double dtmp;
    struct mas_package *packtmp;
    char* strtmp;
    char* f;
    int argsize[64];
    va_list ap;
    
    members = 0;

    members = strlen(format);
    mas_assert( members <= 64, "masc_push_members only supports 64 members" );

    i=0;
    size = sizeof (uint32);
    
    va_start( ap, format );
    /* walk the format string */
    for (f=format; (*f)!=0; f++)
    {
        /* just pick up the sizes and pointers to the locations */
        /* compute the exact memory requirements */
        switch (*f)
        {
        case MAS_INT8: temp = va_arg(ap, int);
            argsize[i] = sizeof (int8);
            break;
        case MAS_UINT8: temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint8);
            break;
        case MAS_INT16:  temp = va_arg(ap, int); 
            argsize[i] = sizeof (int16);
            break;
        case MAS_UINT16: temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint16);
            break;
        case MAS_INT32: temp = va_arg(ap, int); 
            argsize[i] = sizeof (int32);
            break;
        case MAS_UINT32: temp = va_arg(ap, int); 
            argsize[i] = sizeof (uint32);
            break;
        case MAS_FLOAT: dtmp = va_arg(ap, double); 
            argsize[i] = sizeof (float);
            break;
        case MAS_DOUBLE: dtmp = va_arg(ap, double); 
            argsize[i] = sizeof (double);
            break;
        case MAS_STRING: strtmp = va_arg(ap, char *); 
            argsize[i] = strlen(strtmp) + 1;
            break;
        case MAS_PACKAGE: packtmp = va_arg(ap, void *);
            argsize[i] = packtmp->size;
            break;
        case MAS_PAYLOAD: strtmp = va_arg(ap, char *);
            size += sizeof (uint32);
            argsize[i] = (int)va_arg(ap, uint32);
            break;
        default: mas_assert( 0, "invalid format code" ); break;
        }
        size += argsize[i] + 2; /* one for type, one for null key */
        i++;
    }
    va_end( ap );    
    
    package->contents = masc_rtalloc( size );
    if ( package->contents == 0 ) return _pkgerr( package, MERR_MEMORY);
    package->allocated_size = size;
    package->size = sizeof (uint32);
    package->members = 0;
    package->v = 0;
    package->aw = 0;
    /* by default, the packet's byte ordering is the sender's byte
       order.  The recipient gets to convert.  */
#ifdef MAS_LITTLE_ENDIAN
    package->nbo = FALSE;
#else
    package->nbo = TRUE;
#endif
    /* package data starts after the header and "package->aw"
       additional 32-bit words */
    package->p = package->contents + sizeof (uint32) + ( package->aw * sizeof (uint32) );
    memset( package->contents, 0, size);

    
    va_start( ap, format );
    for (i=0; i<members; i++)
    {
        switch (format[i])
        {
        case MAS_INT8: masc_push_int8( package, (int8) va_arg(ap, int) ); break;
        case MAS_UINT8: masc_push_uint8( package, (uint8) va_arg(ap, int) ); break;
        case MAS_INT16:masc_push_int16( package, (int16) va_arg(ap, int) ); break;
        case MAS_UINT16:masc_push_uint16( package, (uint16) va_arg(ap, int) ); break;
        case MAS_INT32: masc_push_int32( package, (int32) va_arg(ap, int) ); break;
        case MAS_UINT32:masc_push_uint32( package, (uint32) va_arg(ap, int) ); break;
        case MAS_FLOAT: masc_push_float( package, (float) va_arg(ap, double) ); break;
        case MAS_DOUBLE:masc_push_double( package, (double) va_arg(ap, double) ); break;
        case MAS_STRING: masc_push_string( package, va_arg(ap, char *) ); break;
        case MAS_PACKAGE: masc_push_package( package, va_arg(ap, void *) ); break;
        case MAS_PAYLOAD: masc_push_payload( package, va_arg(ap, char *), argsize[i] );
            temp = va_arg(ap, int); break;
        default: break;
        }
    }
    va_end( ap );    

    masc_finalize_package( package );

    return 0;
    
}

int32
masc_pull_members( struct mas_package* package, char* format, ... )
{
    char* f;
    int members;
    void** vtmp;
    va_list ap;
    
    members = strlen(format);
    _extract_header_info( package );

    va_start( ap, format );
    /* walk the format string */
    for (f=format; (*f)!=0; f++)
    {
        switch (*f)
        {
        case MAS_INT8: masc_pull_int8(package, va_arg(ap, int8 *)); break;
        case MAS_UINT8: masc_pull_uint8(package, va_arg(ap, uint8 *)); break;
        case MAS_INT16: masc_pull_int16(package, va_arg(ap, int16 *)); break;
        case MAS_UINT16: masc_pull_uint16(package, va_arg(ap, uint16 *)); break;
        case MAS_INT32: masc_pull_int32(package, va_arg(ap, int32 *)); break;
        case MAS_UINT32: masc_pull_uint32(package, va_arg(ap, uint32 *)); break;
        case MAS_FLOAT: masc_pull_float(package, va_arg(ap, float *)); break;
        case MAS_DOUBLE: masc_pull_double(package, va_arg(ap, double *)); break;
        case MAS_STRING: masc_pull_string(package, va_arg(ap, char **), TRUE); break;
        case MAS_PACKAGE: masc_pull_package(package, va_arg(ap, struct mas_package *), TRUE); break;
        case MAS_PAYLOAD: vtmp = va_arg(ap, void**);
            masc_pull_payload(package, vtmp, va_arg(ap, uint32 *), TRUE); break;
        default: mas_assert( 0, "invalid format code" ); break;
        }
    }
    va_end( ap );    

    return 0;
}


int32
masc_test_members( struct mas_package* package, char* format, ... )
{
    char* f;
    int members;
    va_list ap;
    int32 retval = 0;
    char tfmt;
    
    members = strlen(format);
    _extract_header_info( package );

    va_start( ap, format );
    /* walk the format string */
    for (f=format; (*f)!=0; f++)
    {
        if ( masc_test_key( package, va_arg(ap, char *) ) != 0 )
        {
            retval = _pkgerr( package, MERR_INVALID);
            goto fail;
        }

        masc_peek_format( package, &tfmt );
        if ( tfmt != *f )
        {
            retval = _pkgerr( package, MERR_INVALID);
            goto fail;
        }
    }

 fail:
    va_end( ap );    
    return retval;
}


int32
masc_peek_format( struct mas_package* package, char* retval_fmt )
{
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    *retval_fmt = *package->p;
    return 0;
}

int32
masc_peek_key( struct mas_package* package, char** key_ptr )
{
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr( package, MERR_INVALID);

    /* doesn't bytecopy the key */
    *key_ptr = package->p+1;
    return 0;
}

int32
masc_test_key( struct mas_package* package, char* key )
{
    /* this is a "safe" error, do not set error in package struct! */
    if ( masc_find_key( package, key ) < 0 )
        return mas_error(MERR_NOTDEF);

    return 0;
}


int32
masc_debug_package( struct mas_package* package, int recdep )
{
    int i;
    char type;
    char* a;
    int8 c;
    uint8 C;
    int16 s;
    uint16 S;
    int32  l;
    uint32 L;
    float  f;
    double d;
    void *p;
    uint32 plen;
    char *key;
    int32 err;
    int bail = FALSE;
    
    /* Rewind package: package data starts after the header and
       "package->aw" additional 32-bit words */
    package->p = package->contents + sizeof (uint32) + ( package->aw * sizeof (uint32) );
    for (i=0; i<package->members && !bail; i++)
    {
        masc_peek_key( package, &key );
        masc_peek_format( package, &type );
        switch (type)
        {
        case MAS_INT8: masc_pull_int8(package, &c);
            masc_log_message(0, "%d: \"%s\" (int8) %d '%c'", i, key, c, c);
            break;
        case MAS_UINT8: masc_pull_uint8(package, &C); 
            masc_log_message(0, "%d: \"%s\" (uint8) %u", i, key, C);
            break;
        case MAS_INT16: masc_pull_int16(package, &s); 
            masc_log_message(0, "%d: \"%s\" (int16) %d", i, key, s);
            break;
        case MAS_UINT16: masc_pull_uint16(package, &S); 
            masc_log_message(0, "%d: \"%s\" (uint16) %u", i, key, S);
            break;
        case MAS_INT32: masc_pull_int32(package, &l); 
            masc_log_message(0, "%d: \"%s\" (int32) %d", i, key, l);
            break;
        case MAS_UINT32: masc_pull_uint32(package, &L); 
            masc_log_message(0, "%d: \"%s\" (uint32) %u", i, key, L);
            break;
        case MAS_FLOAT: masc_pull_float(package, &f); 
            masc_log_message(0, "%d: \"%s\" (float) %f", i, key, f);
            break;
        case MAS_DOUBLE: masc_pull_double(package, &d); 
            masc_log_message(0, "%d: \"%s\" (double) %f", i, key, d);
            break;
        case MAS_STRING: masc_pull_string(package, &a, TRUE); 
            masc_log_message(0, "%d: \"%s\" (string) \"%s\"", i, key, a);
            masc_rtfree( a );
            break;
        case MAS_PACKAGE: masc_pull_payload(package, &p, &plen, TRUE); 
            masc_log_message(0, "%d: \"%s\" (package: %u bytes)", i, key, plen);
            if ( recdep > 0 )
            {
                struct mas_package newpack;
                err = masc_setup_package( &newpack, p, plen, MASC_PACKAGE_STATIC|MASC_PACKAGE_EXTRACT );
                if ( err == 0 )
                {
                    masc_debug_package( &newpack, recdep - 1 );
                }
                masc_strike_package( &newpack );
            }
            masc_rtfree( p );
            break;
        case MAS_PAYLOAD: masc_pull_payload(package, &p, &plen, TRUE); 
            masc_log_message(0, "%d: \"%s\" (payload: %u bytes)", i, key, plen);
            masc_rtfree( p );
            break;
        default:
            bail = TRUE;
            masc_log_message(0,  "%d \"%s\" *** invalid format code", i, key );
            break;
        }
    }

    if ( bail ) return _pkgerr( package, MERR_INVALID);
    else return 0;
}

/* locates key in package and leaves package->p set to the location */
int32
masc_find_key( struct mas_package* package, char* key )
{
    char* p;
    int   i = 0;
    int   len;
    uint32 plen;
    
    mas_assert(  package->p != 0, "null package" );
    if ( package->p == 0 ) 
	return _pkgerr(package, MERR_INVALID);
    
    /* if the key array isn't initialized, go grab all the keys */
    if ( package->key == 0 )
    {
        package->key = masc_rtalloc( package->members * sizeof (char*) );
        if ( package->key == 0 ) return _pkgerr(package, MERR_MEMORY);
        
        /* package data starts after the header and "package->aw"
           additional 32-bit words */
        p = package->contents + sizeof (uint32) + ( package->aw * sizeof (uint32) );

        /* populate the key array with the keys in this package */
        for ( i=0; i<package->members; i++ )
        {
            package->key[i] = p + 1;
            len = strlen( p + 1 ) + 1; /* length of this key including
                                    * terminator */
            
            /* figure out how much data to skip over */
            switch( *p )
            {
            case MAS_INT8: p += 1 + len + sizeof (int8); break;

            case MAS_UINT8: p += 1 + len + sizeof (uint8); break;
            case MAS_INT16: p += 1 + len + sizeof (int16); break;
            case MAS_UINT16: p += 1 + len + sizeof (uint16); break;
            case MAS_INT32: p += 1 + len + sizeof (int32); break;
            case MAS_UINT32: p += 1 + len + sizeof (uint32); break;
            case MAS_FLOAT:  p += 1 + len + strlen(p+1+len) + 1; break;
            case MAS_DOUBLE: p += 1 + len + strlen(p+1+len) + 1; break;
            case MAS_STRING: p += 1 + len + strlen(p+1+len) + 1; break;
            case MAS_PACKAGE: /* fallthrough */
            case MAS_PAYLOAD: memcpy( &plen, p + 1 + len, sizeof (uint32) );
                plen = (package->nbo)?mas_ntohl(plen):mas_letohl(plen);
                p += 1 + len + sizeof (uint32) + plen;
                break;
            default: mas_assert( 0, "invalid format code" ); break;
            }
        }
    }

    /* find a matching key */
    for (i=0; i<package->members; i++)
    {
        if ( strcmp( package->key[i], key ) == 0 )
        {
            /* set the current p to point at the beginning of this member */
            package->p = package->key[i] - 1;
            return 0;
        }
    }
    
    /* if we're here, we didn't find the key -- this is a "safe" error
       - do not set error flag in struct */
    return mas_error(MERR_INVALID);
}


/************************************************************************
 LOCAL FUNCTIONS
************************************************************************/

void
_extract_header_info( struct mas_package* package )
{
    uint32 hmc;

    /* Now that we can directly use nested packages, we can't cast the
     * 32-bit word, even though it's at the beginning */
    /* for clarity below.  "header and member count"  */
    memcpy(&hmc, package->contents, sizeof hmc);
    
#ifdef MAS_LITTLE_ENDIAN
    package->v  = ( hmc >> 4 ) & 0x0F;
    package->aw = 0x07 & hmc;
    package->nbo = !(0x08 & hmc);
    if (package->nbo)
    {
        package->members = mas_betohs( ( hmc >> 16 ) & 0xFFFF );
    }
    else
    {
        package->members = mas_letohs( ( hmc >> 16 ) & 0xFFFF );
    }
#else /* BIG ENDIAN */
    package->v = ( hmc >> 28 ) & 0x0F;
    package->aw = ( hmc >> 24 ) & 0x07;
    package->nbo = !( ( hmc >> 24 ) & 0x08 );
    if (package->nbo)
    {
        package->members = mas_betohs( hmc & 0xFFFF );
    }
    else
    {
        package->members = mas_letohs( hmc & 0xFFFF );
    }    
#endif

    /* package data starts after the header and "package->aw"
       additional 32-bit words */
    package->p = package->contents + sizeof (uint32) + ( package->aw * sizeof (uint32) );
}

int32
_realloc_if_necessary( struct mas_package *package, int size_needed )
{
    char *maxpos = package->contents + package->allocated_size - size_needed;
    void* ptr;
    int d, new_size;
    
    if ( package->p > maxpos)
    {
        /* if the "no realloc" flag is set, we can't realloc!  just
           return an error. */
        if ( package->flags & MASC_PACKAGE_NOREALLOC )
            return _pkgerr( package,  MERR_BOUNDS );
        
        /* keep track of delta from p to top of contents */
        d = package->p - package->contents; 

        /* the new allocated size is the BIGGER of twice the allocated
           size or the exact size needed.  By growing the size
           exponentially, we hope to reduce the number of times one
           has to grow the size on repeated calls to the masc_push*
           functions.  Sometimes, the exact size needed is bigger than
           twice the allocated size - in that case, why call realloc
           multiple times - just do it. */
        new_size = d + size_needed; /* this is exact */
        new_size = max( new_size, MIN_PACKAGE_SIZE );
        new_size = max( package->allocated_size * 2, new_size );
        package->allocated_size = new_size;
        
        if ( ! (package->flags & MASC_PACKAGE_STATIC) )
        {
            ptr = masc_rtrealloc( package->contents, package->allocated_size );
            if ( ptr == 0 ) return _pkgerr( package, MERR_MEMORY);
        }
        else
        {
            masc_log_message( MAS_VERBLVL_WARNING, "[warning] pushpull.c tried to use static buffer, not enough space - had to malloc and copy.");
            
            /* If the package was using static mem, we've gotta malloc
               and memcpy. */
            ptr = masc_rtalloc( package->allocated_size );
            if ( ptr == 0 ) return _pkgerr( package, MERR_MEMORY);
            memcpy( ptr, package->contents, d );

            /* the package's contents are no longer static, and will
               be free()d when masc_strike_package() is called.  Clear
               these flags. */
            package->flags &= ~MASC_PACKAGE_STATIC;
            package->flags &= ~MASC_PACKAGE_NOFREE;
        }

        /* contents has been realloced() */
        package->flags |= MASC_PACKAGE_REALLOCED;
        package->contents = ptr;
        package->p = package->contents + d; /* reset the p, in case
                                               contents has moved */
    }

    return package->error;
}

int32
_extract_header_if_necessary( struct mas_package *package )
{
    if ( package->members == 0 && package->error == 0 )
        _extract_header_info( package );

    return package->error;
}

int32
_pkgerr( struct mas_package *p, int32 err )
{
    p->error = mas_error( err );
    return p->error;
}
