/* Copyright (c) 2015 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "dynamictypes/DataArray.h" #include "dynamictypes/DataAssociativeArray.h" #include "dynamictypes/DataHashTable.h" #include "dynamictypes/DataValue.h" #include "utilities/IOUtilities.h" #include DataValue valueCreateBoolean(bool value) { return (DataValue) {{.boolean = value}, DATA_TYPE_BOOLEAN, false}; } DataValue valueCreateInt8(int8_t value) { return (DataValue) {{.int8 = value}, DATA_TYPE_INT8, false}; } DataValue valueCreateUInt8(uint8_t value) { return (DataValue) {{.uint8 = value}, DATA_TYPE_UINT8, false}; } DataValue valueCreateInt16(int16_t value) { return (DataValue) {{.int16 = value}, DATA_TYPE_INT16, false}; } DataValue valueCreateUInt16(uint16_t value) { return (DataValue) {{.uint16 = value}, DATA_TYPE_UINT16, false}; } DataValue valueCreateInt32(int32_t value) { return (DataValue) {{.int32 = value}, DATA_TYPE_INT32, false}; } DataValue valueCreateUInt32(uint32_t value) { return (DataValue) {{.uint32 = value}, DATA_TYPE_UINT32, false}; } DataValue valueCreateInt64(int64_t value) { return (DataValue) {{.int64 = value}, DATA_TYPE_INT64, false}; } DataValue valueCreateUInt64(uint64_t value) { return (DataValue) {{.uint64 = value}, DATA_TYPE_UINT64, false}; } DataValue valueCreateFloat(float value) { return (DataValue) {{.float32 = value}, DATA_TYPE_FLOAT, false}; } DataValue valueCreateDouble(double value) { return (DataValue) {{.float64 = value}, DATA_TYPE_DOUBLE, false}; } DataValue valueCreateFixed16_16(fixed16_16 value) { return (DataValue) {{.fixed = value}, DATA_TYPE_FIXED_16_16, false}; } DataValue valueCreatePointer(void * value) { return (DataValue) {{.pointer = value}, DATA_TYPE_POINTER, false}; } DataValue valueCreateString(const char * value, size_t length, bool takeOwnership, bool copy) { DataValue result; result.type = DATA_TYPE_STRING; if (copy) { if (length == DATA_USE_STRLEN) { length = strlen(value); } result.value.string = strndup(value, length); } else { result.value.string = value; } result.owned = takeOwnership; return result; } DataValue valueCreateBlob(const void * value, size_t length, bool takeOwnership, bool copy) { DataValue result; result.type = DATA_TYPE_BLOB; if (copy) { result.value.blob.bytes = memdup(value, length); } else { result.value.blob.bytes = value; } result.value.blob.length = length; result.owned = takeOwnership; return result; } DataValue valueCreateHashTable(DataHashTable * value, bool takeOwnership, bool copy) { DataValue result; result.type = DATA_TYPE_HASH_TABLE; if (copy) { result.value.hashTable = hashCopy(value); } else { result.value.hashTable = value; } result.owned = takeOwnership; return result; } DataValue valueCreateArray(DataArray * value, bool takeOwnership, bool copy) { DataValue result; result.type = DATA_TYPE_ARRAY; if (copy) { result.value.array = arrayCopy(value); } else { result.value.array = value; } result.owned = takeOwnership; return result; } DataValue valueCreateAssociativeArray(struct DataAssociativeArray * value, bool takeOwnership, bool copy) { DataValue result; result.type = DATA_TYPE_ASSOCIATIVE_ARRAY; if (copy) { result.value.associativeArray = associativeArrayCopy(value); } else { result.value.associativeArray = value; } result.owned = takeOwnership; return result; } DataValue valueCopy(DataValue * value) { DataValue copy = *value; if (copy.owned) { switch (copy.type) { case DATA_TYPE_STRING: copy.value.string = strdup(copy.value.string); break; case DATA_TYPE_BLOB: copy.value.blob.bytes = memdup(copy.value.blob.bytes, copy.value.blob.length); break; case DATA_TYPE_HASH_TABLE: copy.value.hashTable = hashCopy(copy.value.hashTable); break; case DATA_TYPE_ARRAY: copy.value.array = arrayCopy(copy.value.array); break; case DATA_TYPE_ASSOCIATIVE_ARRAY: copy.value.associativeArray = associativeArrayCopy(copy.value.associativeArray); break; default: break; } } return copy; } void valueDispose(DataValue * value) { if (value != NULL && value->owned) { switch (value->type) { case DATA_TYPE_STRING: free((char *) value->value.string); break; case DATA_TYPE_BLOB: free((void *) value->value.blob.bytes); break; case DATA_TYPE_HASH_TABLE: hashDispose(value->value.hashTable); break; case DATA_TYPE_ARRAY: arrayDispose(value->value.array); break; case DATA_TYPE_ASSOCIATIVE_ARRAY: associativeArrayDispose(value->value.associativeArray); break; default: break; } value->type = -1; value->owned = false; } } #define returnNumericValue(value, defaultValue) \ if (value == NULL) { \ return defaultValue; \ } \ switch (value->type) { \ case DATA_TYPE_BOOLEAN: \ return value->value.boolean; \ case DATA_TYPE_INT8: \ return value->value.int8; \ case DATA_TYPE_UINT8: \ return value->value.uint8; \ case DATA_TYPE_INT16: \ return value->value.int16; \ case DATA_TYPE_UINT16: \ return value->value.uint16; \ case DATA_TYPE_INT32: \ return value->value.int32; \ case DATA_TYPE_UINT32: \ return value->value.uint32; \ case DATA_TYPE_INT64: \ return value->value.int64; \ case DATA_TYPE_UINT64: \ return value->value.uint64; \ case DATA_TYPE_FLOAT: \ return value->value.float32; \ case DATA_TYPE_DOUBLE: \ return value->value.float64; \ case DATA_TYPE_FIXED_16_16: \ return xtod(value->value.fixed); \ default: \ return defaultValue; \ } #define returnNumericValueFixed(value, defaultValue) \ if (value == NULL) { \ return defaultValue; \ } \ switch (value->type) { \ case DATA_TYPE_BOOLEAN: \ return value->value.boolean << 16; \ case DATA_TYPE_INT8: \ return value->value.int8 << 16; \ case DATA_TYPE_UINT8: \ return value->value.uint8 << 16; \ case DATA_TYPE_INT16: \ return value->value.int16 << 16; \ case DATA_TYPE_UINT16: \ return value->value.uint16 << 16; \ case DATA_TYPE_INT32: \ return value->value.int32 << 16; \ case DATA_TYPE_UINT32: \ return value->value.uint32 << 16; \ case DATA_TYPE_INT64: \ return value->value.int64 << 16; \ case DATA_TYPE_UINT64: \ return value->value.uint64 << 16; \ case DATA_TYPE_FLOAT: \ return ftox(value->value.float32); \ case DATA_TYPE_DOUBLE: \ return dtox(value->value.float64); \ case DATA_TYPE_FIXED_16_16: \ return value->value.fixed; \ default: \ return defaultValue; \ } bool valueGetBoolean(DataValue * value) { returnNumericValue(value, false); } int8_t valueGetInt8(DataValue * value) { returnNumericValue(value, 0); } uint8_t valueGetUInt8(DataValue * value) { returnNumericValue(value, 0); } int16_t valueGetInt16(DataValue * value) { returnNumericValue(value, 0); } uint16_t valueGetUInt16(DataValue * value) { returnNumericValue(value, 0); } int32_t valueGetInt32(DataValue * value) { returnNumericValue(value, 0); } uint32_t valueGetUInt32(DataValue * value) { returnNumericValue(value, 0); } int64_t valueGetInt64(DataValue * value) { returnNumericValue(value, 0); } uint64_t valueGetUInt64(DataValue * value) { returnNumericValue(value, 0); } float valueGetFloat(DataValue * value) { returnNumericValue(value, 0.0f); } double valueGetDouble(DataValue * value) { returnNumericValue(value, 0.0); } fixed16_16 valueGetFixed16_16(DataValue * value) { returnNumericValueFixed(value, 0x00000); } void * valueGetPointer(DataValue * value) { if (value == NULL) { return NULL; } switch (value->type) { case DATA_TYPE_POINTER: return value->value.pointer; case DATA_TYPE_STRING: return (void *) value->value.string; case DATA_TYPE_BLOB: return (void *) value->value.blob.bytes; case DATA_TYPE_HASH_TABLE: return value->value.hashTable; case DATA_TYPE_ARRAY: return value->value.array; case DATA_TYPE_ASSOCIATIVE_ARRAY: return value->value.array; default: return NULL; } } const char * valueGetString(DataValue * value) { if (value == NULL || value->type != DATA_TYPE_STRING) { return NULL; } return value->value.string; } const void * valueGetBlob(DataValue * value, size_t * outLength) { if (value == NULL) { return NULL; } if (value->type == DATA_TYPE_STRING) { if (outLength != NULL && value != NULL) { *outLength = strlen(value->value.string); } return value->value.string; } if (value->type != DATA_TYPE_BLOB) { return NULL; } if (outLength != NULL) { *outLength = value->value.blob.length; } return value->value.blob.bytes; } DataHashTable * valueGetHashTable(DataValue * value) { if (value == NULL || value->type != DATA_TYPE_HASH_TABLE) { return NULL; } return value->value.hashTable; } DataArray * valueGetArray(DataValue * value) { if (value == NULL || value->type != DATA_TYPE_ARRAY) { return NULL; } return value->value.array; } struct DataAssociativeArray * valueGetAssociativeArray(DataValue * value) { if (value == NULL || value->type != DATA_TYPE_ASSOCIATIVE_ARRAY) { return NULL; } return value->value.associativeArray; } void valueSetString(DataValue * value, const char * string, size_t length, bool takeOwnership, bool copy) { if (value != NULL && value->type == DATA_TYPE_STRING) { if (value->owned) { free((char *) value->value.string); } if (length == DATA_USE_STRLEN) { length = strlen(string); } if (copy) { value->value.string = strndup(string, length); } else { value->value.string = string; } value->owned = takeOwnership; } } void valueSetBlob(DataValue * value, const void * bytes, size_t length, bool takeOwnership, bool copy) { if (value != NULL && value->type == DATA_TYPE_BLOB) { if (value->owned) { free((void *) value->value.blob.bytes); } if (copy) { value->value.blob.bytes = memdup(bytes, length); } else { value->value.blob.bytes = value; } value->value.blob.length = length; value->owned = takeOwnership; } } bool isDataValueEqual(DataValue * value1, DataValue * value2) { if (value1->type != value2->type) { return false; } switch (value1->type) { case DATA_TYPE_BOOLEAN: return value1->value.boolean == value2->value.boolean; case DATA_TYPE_INT8: return value1->value.int8 == value2->value.int8; case DATA_TYPE_UINT8: return value1->value.uint8 == value2->value.uint8; case DATA_TYPE_INT16: return value1->value.int16 == value2->value.int16; case DATA_TYPE_UINT16: return value1->value.uint16 == value2->value.uint16; case DATA_TYPE_INT32: return value1->value.int32 == value2->value.int32; case DATA_TYPE_UINT32: return value1->value.uint32 == value2->value.uint32; case DATA_TYPE_INT64: return value1->value.int64 == value2->value.int64; case DATA_TYPE_UINT64: return value1->value.uint64 == value2->value.uint64; case DATA_TYPE_FLOAT: return value1->value.float32 == value2->value.float32; case DATA_TYPE_DOUBLE: return value1->value.float64 == value2->value.float64; case DATA_TYPE_POINTER: return value1->value.pointer == value2->value.pointer; case DATA_TYPE_STRING: return !strcmp(value1->value.string, value2->value.string); case DATA_TYPE_BLOB: return value1->value.blob.length == value2->value.blob.length && !memcmp(value1->value.blob.bytes, value2->value.blob.bytes, value1->value.blob.length); case DATA_TYPE_HASH_TABLE: return isDataHashTableEqual(value1->value.hashTable, value2->value.hashTable); case DATA_TYPE_ARRAY: return isDataArrayEqual(value1->value.array, value2->value.array); case DATA_TYPE_ASSOCIATIVE_ARRAY: return isDataAssociativeArrayEqual(value1->value.associativeArray, value2->value.associativeArray); case DATA_TYPE_FIXED_16_16: return value1->value.fixed == value2->value.fixed; } return false; } static bool compareHashTableValuesCallback(HashTable * hashTable, HashTable_key key, void * value, void * context) { HashTable * hashTable2 = context; DataValue * value1 = value; DataValue * value2 = HashTable_get(hashTable2, key); if (value2 == NULL || !isDataValueEqual(value1, value2)) { return false; } return true; } bool isDataHashTableEqual(DataHashTable * hashTable1, DataHashTable * hashTable2) { if (hashTable1 == NULL) { if (hashTable2 == NULL) { return true; } return false; } if (hashTable2 == NULL) { return false; } size_t keyCount1 = hashGetCount(hashTable1), keyCount2 = hashGetCount(hashTable2); if (keyCount1 != keyCount2) { return false; } if (!HashTable_foreach(hashTable1->hashTable, compareHashTableValuesCallback, hashTable2->hashTable)) { return false; } return true; } bool isDataArrayEqual(DataArray * array1, DataArray * array2) { if (array1 == NULL) { if (array2 == NULL) { return true; } return false; } if (array2 == NULL) { return false; } if (array1->count != array2->count) { return false; } for (unsigned int itemIndex = 0; itemIndex < array1->count; itemIndex++) { if (!isDataValueEqual(&array1->values[itemIndex], &array2->values[itemIndex])) { return false; } } return true; } bool isDataAssociativeArrayEqual(DataAssociativeArray * associativeArray1, DataAssociativeArray * associativeArray2) { if (associativeArray1 == NULL) { if (associativeArray2 == NULL) { return true; } return false; } if (associativeArray2 == NULL) { return false; } if (associativeArray1->count != associativeArray2->count) { return false; } for (unsigned int itemIndex = 0; itemIndex < associativeArray1->count; itemIndex++) { if (strcmp(associativeArray1->keys[itemIndex], associativeArray2->keys[itemIndex]) || !isDataValueEqual(&associativeArray1->values[itemIndex], &associativeArray2->values[itemIndex])) { return false; } } return true; }