X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Fxmlrpc_struct.c;fp=src%2Fxmlrpc_struct.c;h=7fabb044a4f122aee90583d946d6146008ba34f1;hb=ce67d0cdeaa37c3e856e23ae4010480887165630;hp=0000000000000000000000000000000000000000;hpb=e355d4e7962400470f467b88f5568de9c8324475;p=xmlrpc-c diff --git a/src/xmlrpc_struct.c b/src/xmlrpc_struct.c new file mode 100644 index 0000000..7fabb04 --- /dev/null +++ b/src/xmlrpc_struct.c @@ -0,0 +1,610 @@ +/* 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. */ + +#include "xmlrpc_config.h" + +#include +#include +#include + +#include "xmlrpc-c/base.h" +#include "xmlrpc-c/base_int.h" + +#define KEY_ERROR_BUFFER_SZ (32) + + +void +xmlrpc_destroyStruct(xmlrpc_value * const structP) { + + _struct_member * const members = + XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block); + size_t const size = + XMLRPC_MEMBLOCK_SIZE(_struct_member, &structP->_block); + + unsigned int i; + + for (i = 0; i < size; ++i) { + xmlrpc_DECREF(members[i].key); + xmlrpc_DECREF(members[i].value); + } + XMLRPC_MEMBLOCK_CLEAN(_struct_member, &structP->_block); +} + + + +/*========================================================================= +** xmlrpc_struct_new +**========================================================================= +** Create a new 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; + + XMLRPC_MEMBLOCK_INIT(_struct_member, envP, &valP->_block, 0); + + if (envP->fault_occurred) + free(valP); + } + return valP; +} + + + +/*========================================================================= +** 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* env, xmlrpc_value* strct) +{ + int retval; + + /* Suppress a compiler warning about uninitialized variables. */ + retval = 0; + + XMLRPC_ASSERT_ENV_OK(env); + XMLRPC_ASSERT_VALUE_OK(strct); + + XMLRPC_TYPE_CHECK(env, strct, XMLRPC_TYPE_STRUCT); + retval = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strct->_block); + + cleanup: + if (env->fault_occurred) + return -1; + return retval; +} + + + +/*========================================================================= +** get_hash +**========================================================================= +** A mindlessly simple hash function. Please feel free to write something +** more clever if this produces bad results. +*/ + +static unsigned char +get_hash(const char * const key, + size_t const key_len) { + + unsigned char retval; + size_t i; + + XMLRPC_ASSERT(key != NULL); + + retval = 0; + for (i = 0; i < key_len; i++) + retval += key[i]; + return retval; +} + + + +/*========================================================================= +** find_member +**========================================================================= +** Get the index of the member with the specified key, or -1 if no such +** member exists. +*/ + +static int +find_member(xmlrpc_value * const strctP, + const char * const key, + size_t const key_len) { + + size_t size, i; + unsigned char hash; + _struct_member *contents; + xmlrpc_value *keyval; + char *keystr; + size_t keystr_size; + + XMLRPC_ASSERT_VALUE_OK(strctP); + XMLRPC_ASSERT(key != NULL); + + /* Look for our key. */ + hash = get_hash(key, key_len); + size = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strctP->_block); + contents = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block); + for (i = 0; i < size; i++) { + if (contents[i].key_hash == hash) { + keyval = contents[i].key; + keystr = XMLRPC_MEMBLOCK_CONTENTS(char, &keyval->_block); + keystr_size = XMLRPC_MEMBLOCK_SIZE(char, &keyval->_block)-1; + if (key_len == keystr_size && memcmp(key, keystr, key_len) == 0) + return i; + } + } + return -1; +} + + + +/*========================================================================= +** 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 strctP, + const char * const key, + size_t const key_len) { + int index; + + /* Suppress a compiler warning about uninitialized variables. */ + index = 0; + + XMLRPC_ASSERT_ENV_OK(envP); + XMLRPC_ASSERT_VALUE_OK(strctP); + XMLRPC_ASSERT(key != NULL); + + XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT); + index = find_member(strctP, key, key_len); + + cleanup: + if (envP->fault_occurred) + return 0; + return (index >= 0); +} + + + +/*========================================================================= +** 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 { + int index; + + /* Get our member index. */ + index = find_member(structP, key, strlen(key)); + if (index < 0) + *valuePP = NULL; + else { + _struct_member * const members = + XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block); + *valuePP = members[index].value; + + XMLRPC_ASSERT_VALUE_OK(*valuePP); + + xmlrpc_INCREF(*valuePP); + } + } +} + + + +static void +findValueVNoRef(xmlrpc_env * const envP, + xmlrpc_value * const structP, + xmlrpc_value * const keyP, + xmlrpc_value ** const valuePP) { +/*---------------------------------------------------------------------------- + Same as xmlrpc_find_value_v(), except we don't increment the reference + count on the xmlrpc_value we return. +-----------------------------------------------------------------------------*/ + 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 { + int index; + + /* Get our member index. */ + index = find_member(structP, + XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block), + XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block)-1); + if (index < 0) + *valuePP = NULL; + else { + _struct_member * const members = + XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block); + *valuePP = members[index].value; + + XMLRPC_ASSERT_VALUE_OK(*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. +-----------------------------------------------------------------------------*/ + findValueVNoRef(envP, structP, keyP, valuePP); + + if (!envP->fault_occurred && *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->_block), + XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block)); + } + } +} + + + +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; + + keyP = xmlrpc_build_value(envP, "s#", key, keyLen); + if (!envP->fault_occurred) { + /* We cannot use xmlrpc_find_value_v here because + some legacy code uses xmlrpc_struct_get_value() from multiple + simultaneous threads and xmlrpc_find_value isn't thread safe + due to its manipulation of the reference count. + */ + findValueVNoRef(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 */ + } + } + 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; + + /* Get the key as an xmlrpc_value */ + keyvalP = xmlrpc_build_value(envP, "s#", key, keyLen); + 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 strctP, + xmlrpc_value * const keyvalP, + xmlrpc_value * const valueP) { + + char *key; + size_t key_len; + int index; + _struct_member *members, *member, new_member; + xmlrpc_value *old_value; + + XMLRPC_ASSERT_ENV_OK(envP); + XMLRPC_ASSERT_VALUE_OK(strctP); + XMLRPC_ASSERT_VALUE_OK(keyvalP); + XMLRPC_ASSERT_VALUE_OK(valueP); + + XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT); + XMLRPC_TYPE_CHECK(envP, keyvalP, XMLRPC_TYPE_STRING); + + key = XMLRPC_MEMBLOCK_CONTENTS(char, &keyvalP->_block); + key_len = XMLRPC_MEMBLOCK_SIZE(char, &keyvalP->_block) - 1; + index = find_member(strctP, key, key_len); + + if (index >= 0) { + /* 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!) */ + members = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block); + member = &members[index]; + + /* Juggle our references. */ + old_value = member->value; + member->value = valueP; + xmlrpc_INCREF(member->value); + xmlrpc_DECREF(old_value); + } else { + /* Add a new member. */ + new_member.key_hash = get_hash(key, key_len); + new_member.key = keyvalP; + new_member.value = valueP; + XMLRPC_MEMBLOCK_APPEND(_struct_member, envP, &strctP->_block, + &new_member, 1); + XMLRPC_FAIL_IF_FAULT(envP); + xmlrpc_INCREF(keyvalP); + xmlrpc_INCREF(valueP); + } + +cleanup: + return; +} + + + +/* 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->_block); + size_t const size = + XMLRPC_MEMBLOCK_SIZE(_struct_member, &structP->_block); + + 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; + } +}