#include "xmlrpc_config.h"

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "int.h"

#define KEY_ERROR_BUFFER_SZ (32)



static uint32_t
hashStructKey(const char * const key, 
              size_t       const keyLen) {

    uint32_t hash;
    size_t i;

    XMLRPC_ASSERT(key != NULL);
    
    /* This is the Bernstein hash, optimized for lower case ASCII
       keys.  Note that the bytes of such a key differ only in their
       lower 5 bits.
    */
    for (hash = 0, i = 0; i < keyLen; ++i)
        hash = hash + key[i] + (hash << 5);

    return hash;
}



static void
changeMemberValue(xmlrpc_value * const structP,
                  unsigned int   const mbrIndex,
                  xmlrpc_value * const newValueP) {
/*----------------------------------------------------------------------------
  Change the value of an existing member. (But be careful--the original and
  new values might be the same object, so watch the order of INCREF and DECREF
  calls!)
-----------------------------------------------------------------------------*/
    _struct_member * const members =
        XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
    _struct_member * const memberP = &members[mbrIndex];
    xmlrpc_value * const oldValueP = memberP->value;

    /* Juggle our references. */
    memberP->value = newValueP;
    xmlrpc_INCREF(memberP->value);
    xmlrpc_DECREF(oldValueP);
}



static void
addNewMember(xmlrpc_env *   const envP,
             xmlrpc_value * const structP,
             xmlrpc_value * const keyvalP,
             xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
   Add a new member.  Assume no member already exists with this key.
-----------------------------------------------------------------------------*/
    const char * const key =
        XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
    size_t const keyLen =
        XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP) - 1;

    _struct_member newMember;

    newMember.keyHash = hashStructKey(key, keyLen);
    newMember.key     = keyvalP;
    newMember.value   = valueP;

    XMLRPC_MEMBLOCK_APPEND(_struct_member, envP, structP->blockP,
                           &newMember, 1);

    if (!envP->fault_occurred) {
        xmlrpc_INCREF(keyvalP);
        xmlrpc_INCREF(valueP);
    }
}



void
xmlrpc_destroyStruct(xmlrpc_value * const structP) {
/*----------------------------------------------------------------------------
   Dispose of the contents of struct *structP (but not the struct value
   itself).  The value is not valid after this.
-----------------------------------------------------------------------------*/
    _struct_member * const members = 
        XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
    size_t const size = 
        XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);

    unsigned int i;

    for (i = 0; i < size; ++i) {
        xmlrpc_DECREF(members[i].key);
        xmlrpc_DECREF(members[i].value);
    }
    XMLRPC_MEMBLOCK_FREE(_struct_member, structP->blockP);
}



/*=========================================================================
**  xmlrpc_struct_new
**=========================================================================
**  Create a new <struct> value. The corresponding destructor code
**  currently lives in xmlrpc_DECREF.
**
**  We store the individual members in an array of _struct_member. This
**  contains a key, a hash code, and a value. We look up keys by doing
**  a linear search of the hash codes.
*/

xmlrpc_value *
xmlrpc_struct_new(xmlrpc_env * const envP) {

    xmlrpc_value * valP;

    XMLRPC_ASSERT_ENV_OK(envP);

    xmlrpc_createXmlrpcValue(envP, &valP);
    if (!envP->fault_occurred) {
        valP->_type = XMLRPC_TYPE_STRUCT;

        valP->blockP = XMLRPC_MEMBLOCK_NEW(_struct_member, envP, 0);

        if (envP->fault_occurred)
            free(valP);
    }
    return valP;
}



xmlrpc_value *
xmlrpc_struct_new_value(xmlrpc_env *   const envP,
                        xmlrpc_value * const valueP) {

    xmlrpc_value * structP;

    if (valueP->_type != XMLRPC_TYPE_STRUCT) {
        xmlrpc_env_set_fault_formatted(envP, XMLRPC_TYPE_ERROR,
                                       "Value is not a structure.  "
                                       "It is type #%d", valueP->_type);
        structP = NULL;
    } else {
        size_t const size = 
            XMLRPC_MEMBLOCK_SIZE(xmlrpc_value *, structP->blockP);

        xmlrpc_createXmlrpcValue(envP, &structP);
        if (!envP->fault_occurred) {
            structP->_type = XMLRPC_TYPE_STRUCT;

            structP->blockP = XMLRPC_MEMBLOCK_NEW(xmlrpc_value *, envP, 0);

            if (envP->fault_occurred)
                free(structP);
            else {
                _struct_member * const srcMemberList =
                    XMLRPC_MEMBLOCK_CONTENTS(_struct_member, valueP->blockP);
                
                unsigned int i;
            
                for (i = 0; i < size && !envP->fault_occurred; ++i) {
                    const _struct_member * const thisMemberP =
                        &srcMemberList[i];

                    xmlrpc_value * keyValP =
                        xmlrpc_string_new_value(envP, thisMemberP->key);
                    if (!envP->fault_occurred) {
                        xmlrpc_value * valueP =
                            xmlrpc_value_new(envP, thisMemberP->value);

                        if (!envP->fault_occurred) {
                            addNewMember(envP, structP, keyValP, valueP);

                            xmlrpc_DECREF(valueP);
                        }
                        xmlrpc_DECREF(keyValP);
                    }
                }
                if (envP->fault_occurred)
                    xmlrpc_destroyStruct(structP);
            }

            if (envP->fault_occurred)
                free(structP);
        }
    }
    return structP;
}



/*=========================================================================
**  xmlrpc_struct_size
**=========================================================================
**  Return the number of key-value pairs contained in the struct. If the
**  value is not a struct, return -1 and set a fault.
*/

int 
xmlrpc_struct_size(xmlrpc_env *   const envP,
                   xmlrpc_value * const structP) {

    int retval;

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);

    if (structP->_type != XMLRPC_TYPE_STRUCT) {
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Value is not a struct.  It is type #%d",
            structP->_type);
        retval = -1;
    } else {
        size_t const size =
            XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);

        assert((size_t)(int)size == size);
            /* Because structs are defined to have few enough members */

        retval = (int)size;
    }
    return retval;
}



static void
findMember(xmlrpc_value * const structP, 
           const char *   const key, 
           size_t         const keyLen,
           bool *         const foundP,
           unsigned int * const indexP) {

    size_t size, i;
    uint32_t searchHash;
    _struct_member * contents;  /* array */
    bool found;
    size_t foundIndex;  /* Meaningful only when 'found' is true */

    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT(key != NULL);
    foundIndex = 0;  /* defeat used-before-set compiler warning */

    /* Look for our key. */
    searchHash = hashStructKey(key, keyLen);
    size = XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);
    contents = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
    for (i = 0, found = false; i < size && !found; ++i) {
        if (contents[i].keyHash == searchHash) {
            xmlrpc_value * const keyvalP = contents[i].key;
            const char * const keystr =
                XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
            size_t const keystrSize =
                XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP)-1;
            if (keystrSize == keyLen && memcmp(key, keystr, keyLen) == 0) {
                found = true;
                foundIndex = i;
            }
        }   
    }
    if (found) {
        assert((size_t)(int)foundIndex == foundIndex);
            /* Definition of structure says it has few enough members */

        if (indexP)
            *indexP = foundIndex;
    }
    *foundP = found;
}



/*=========================================================================
**  xmlrpc_struct_has_key
**=========================================================================
*/

int 
xmlrpc_struct_has_key(xmlrpc_env *   const envP,
                      xmlrpc_value * const strctP,
                      const char *   const key) {

    XMLRPC_ASSERT(key != NULL);
    return xmlrpc_struct_has_key_n(envP, strctP, key, strlen(key));
}



int 
xmlrpc_struct_has_key_n(xmlrpc_env   * const envP,
                        xmlrpc_value * const structP,
                        const char *   const key, 
                        size_t         const keyLen) {
    int retval;

    /* Suppress a compiler warning about uninitialized variables. */
    retval = 0;

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT(key != NULL);
    
    if (structP->_type != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
                             "Value is not a struct");
    else {
        bool found;

        findMember(structP, key, keyLen, &found, NULL);

        retval = found ? 1 : 0;
    }
    return retval;
}



/*=========================================================================
**  xmlrpc_struct_find_value...
**=========================================================================
**  These functions look up a specified key value in a specified struct.
**  If it exists, they return the value of the struct member.  If not,
**  they return a NULL to indicate such.
*/

/* It would be a nice extension to be able to look up a key that is
   not a text string.
*/

void
xmlrpc_struct_find_value(xmlrpc_env *    const envP,
                         xmlrpc_value *  const structP,
                         const char *    const key,
                         xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
  Given a key, retrieve a value from the struct.  If the key is not
  present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT_PTR_OK(key);
    
    if (structP->_type != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Value is not a struct.  It is type #%d",
            structP->_type);
    else {
        bool found;
        unsigned int index;

        /* Get our member index. */
        findMember(structP, key, strlen(key), &found, &index);
        if (!found)
            *valuePP = NULL;
        else {
            _struct_member * const members =
                XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
            *valuePP = members[index].value;
            
            XMLRPC_ASSERT_VALUE_OK(*valuePP);
            
            xmlrpc_INCREF(*valuePP);
        }
    }
}



void
xmlrpc_struct_find_value_v(xmlrpc_env *    const envP,
                           xmlrpc_value *  const structP,
                           xmlrpc_value *  const keyP,
                           xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
  Given a key, retrieve a value from the struct.  If the key is not
  present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT_VALUE_OK(keyP);
    
    if (structP->_type != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Value is not a struct.  It is type #%d",
            structP->_type);
    else {
        if (keyP->_type != XMLRPC_TYPE_STRING)
            xmlrpc_env_set_fault_formatted(
                envP, XMLRPC_TYPE_ERROR, "Key value is not a string.  "
                "It is type #%d",
                keyP->_type);
        else {
            bool found;
            unsigned int index;

            /* Get our member index. */
            findMember(structP, 
                       XMLRPC_MEMBLOCK_CONTENTS(char, keyP->blockP),
                       XMLRPC_MEMBLOCK_SIZE(char, keyP->blockP)-1,
                       &found, &index);
            if (!found)
                *valuePP = NULL;
            else {
                _struct_member * const members =
                    XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
                *valuePP = members[index].value;
                
                XMLRPC_ASSERT_VALUE_OK(*valuePP);
                
                xmlrpc_INCREF(*valuePP);
            }
        }
    }
}



/*=========================================================================
**  xmlrpc_struct_read_value...
**=========================================================================
**  These fail if no member with the specified key exists.
**  Otherwise, they are the same as xmlrpc_struct_find_value...
*/

void
xmlrpc_struct_read_value_v(xmlrpc_env *    const envP,
                           xmlrpc_value *  const structP,
                           xmlrpc_value *  const keyP,
                           xmlrpc_value ** const valuePP) {

    xmlrpc_struct_find_value_v(envP, structP, keyP, valuePP);

    if (!envP->fault_occurred) {
        if (*valuePP == NULL) {
            xmlrpc_env_set_fault_formatted(
                envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%.*s'",
                (int)XMLRPC_MEMBLOCK_SIZE(char, keyP->blockP),
                XMLRPC_MEMBLOCK_CONTENTS(char, keyP->blockP));
        }
    }
}



void
xmlrpc_struct_read_value(xmlrpc_env *    const envP,
                         xmlrpc_value *  const structP,
                         const char *    const key,
                         xmlrpc_value ** const valuePP) {

    xmlrpc_struct_find_value(envP, structP, key, valuePP);
    
    if (!envP->fault_occurred) {
        if (*valuePP == NULL) {
            xmlrpc_env_set_fault_formatted(
                envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%s'",
                key);
            /* We should fix the error message to format the key for display */
        }
    }
}



/*=========================================================================
**  xmlrpc_struct_get_value...
**=========================================================================
**  These are for backward compatibility.  They used to be the only ones.
**  They're deprecated because they don't acquire a reference to the
**  value they return.
*/

xmlrpc_value * 
xmlrpc_struct_get_value_n(xmlrpc_env *   const envP,
                          xmlrpc_value * const structP,
                          const char *   const key, 
                          size_t         const keyLen) {

    xmlrpc_value * retval;
    xmlrpc_value * keyP;  /* 'key' as an XML-RPC string */
    
    keyP = xmlrpc_string_new_lp(envP, keyLen, key);
    if (!envP->fault_occurred) {
        xmlrpc_struct_find_value_v(envP, structP, keyP, &retval);

        if (!envP->fault_occurred) {
            if (retval == NULL) {
                xmlrpc_env_set_fault_formatted(
                    envP, XMLRPC_INDEX_ERROR, 
                    "No member of struct has key '%.*s'",
                    (int)keyLen, key);
                /* We should fix the error message to format the key
                   for display */
            } else
                /* For backward compatibility.  */
                xmlrpc_DECREF(retval);
        }
        xmlrpc_DECREF(keyP);
    }
    return retval;
}



xmlrpc_value * 
xmlrpc_struct_get_value(xmlrpc_env *   const envP,
                        xmlrpc_value * const strctP,
                        const char *   const key) {

    XMLRPC_ASSERT(key != NULL);
    return xmlrpc_struct_get_value_n(envP, strctP, key, strlen(key));
}



/*=========================================================================
**  xmlrpc_struct_set_value
**=========================================================================
*/

void 
xmlrpc_struct_set_value(xmlrpc_env *   const envP,
                        xmlrpc_value * const strctP,
                        const char *   const key,
                        xmlrpc_value * const valueP) {

    XMLRPC_ASSERT(key != NULL);
    xmlrpc_struct_set_value_n(envP, strctP, key, strlen(key), valueP);
}



void 
xmlrpc_struct_set_value_n(xmlrpc_env *    const envP,
                          xmlrpc_value *  const strctP,
                          const char *    const key, 
                          size_t          const keyLen,
                          xmlrpc_value *  const valueP) {

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT(key != NULL);

    if (xmlrpc_value_type(strctP) != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR,
            "Trying to set value in something not a struct.  "
            "Type is %d; struct is %d",
            xmlrpc_value_type(strctP), XMLRPC_TYPE_STRUCT);
    else {
        xmlrpc_value * keyvalP;  /* 'key' as an XML-RPC string */

        keyvalP = xmlrpc_string_new_lp(envP, keyLen, key);
        if (!envP->fault_occurred)
            xmlrpc_struct_set_value_v(envP, strctP, keyvalP, valueP);

        xmlrpc_DECREF(keyvalP);
    }
}



void 
xmlrpc_struct_set_value_v(xmlrpc_env *   const envP,
                          xmlrpc_value * const structP,
                          xmlrpc_value * const keyvalP,
                          xmlrpc_value * const valueP) {

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT_VALUE_OK(keyvalP);
    XMLRPC_ASSERT_VALUE_OK(valueP);

    if (structP->_type != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
                             "Value is not a struct");
    else if (keyvalP->_type != XMLRPC_TYPE_STRING)
        xmlrpc_env_set_fault(envP, XMLRPC_TYPE_ERROR,
                             "Key value is not a string");
    else {
        const char * const key =
            XMLRPC_MEMBLOCK_CONTENTS(char, keyvalP->blockP);
        size_t const keyLen =
            XMLRPC_MEMBLOCK_SIZE(char, keyvalP->blockP) - 1;

        bool found;
        unsigned int index;

        findMember(structP, key, keyLen, &found, &index);

        if (found)
            changeMemberValue(structP, index, valueP);
        else
            addNewMember(envP, structP, keyvalP, valueP);
    }
}



/* Note that the order of keys and values is undefined, and may change
   when you modify the struct.
*/

void 
xmlrpc_struct_read_member(xmlrpc_env *    const envP,
                          xmlrpc_value *  const structP,
                          unsigned int    const index,
                          xmlrpc_value ** const keyvalP,
                          xmlrpc_value ** const valueP) {

    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT_PTR_OK(keyvalP);
    XMLRPC_ASSERT_PTR_OK(valueP);

    if (structP->_type != XMLRPC_TYPE_STRUCT)
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Attempt to read a struct member "
            "of something that is not a struct");
    else {
        _struct_member * const members =
            XMLRPC_MEMBLOCK_CONTENTS(_struct_member, structP->blockP);
        size_t const size = 
            XMLRPC_MEMBLOCK_SIZE(_struct_member, structP->blockP);

        if (index >= size)
            xmlrpc_env_set_fault_formatted(
                envP, XMLRPC_INDEX_ERROR, "Index %u is beyond the end of "
                "the %u-member structure", index, (unsigned int)size);
        else {
            _struct_member * const memberP = &members[index];
            *keyvalP = memberP->key;
            xmlrpc_INCREF(memberP->key);
            *valueP = memberP->value;
            xmlrpc_INCREF(memberP->value);
        }
    }
}



void 
xmlrpc_struct_get_key_and_value(xmlrpc_env *    const envP,
                                xmlrpc_value *  const structP,
                                int             const index,
                                xmlrpc_value ** const keyvalP,
                                xmlrpc_value ** const valueP) {
/*----------------------------------------------------------------------------
   Same as xmlrpc_struct_read_member(), except doesn't take a reference
   to the returned value.

   This is obsolete.
-----------------------------------------------------------------------------*/
    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_VALUE_OK(structP);
    XMLRPC_ASSERT_PTR_OK(keyvalP);
    XMLRPC_ASSERT_PTR_OK(valueP);

    if (index < 0)
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_INDEX_ERROR, "Index %d is negative.", index);
    else {
        xmlrpc_struct_read_member(envP, structP, index, keyvalP, valueP);
        if (!envP->fault_occurred) {
            xmlrpc_DECREF(*keyvalP);
            xmlrpc_DECREF(*valueP);
        }
    }
    if (envP->fault_occurred) {
        *keyvalP = NULL;
        *valueP = NULL;
    }
}



/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** 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. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission. 
**  
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */

