//////////////////////////////////////////////////////////////////////////////// /// @brief json shaper used to compute the shape of an json object /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Martin Schoenert /// @author Copyright 2006-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "voc-shaper.h" #include "BasicsC/associative.h" #include "BasicsC/hashes.h" #include "BasicsC/locks.h" #include "BasicsC/logging.h" #include "BasicsC/tri-strings.h" #include "BasicsC/utf8-helper.h" #include "VocBase/document-collection.h" // ----------------------------------------------------------------------------- // --SECTION-- private types // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief attribute weight //////////////////////////////////////////////////////////////////////////////// typedef struct attribute_weight_s { TRI_shape_aid_t _aid; int64_t _weight; char* _attribute; struct attribute_weight_s* _next; } attribute_weight_t; //////////////////////////////////////////////////////////////////////////////// /// @brief attribute weights //////////////////////////////////////////////////////////////////////////////// typedef struct attribute_weights_s { attribute_weight_t* _first; attribute_weight_t* _last; size_t _length; } attribute_weights_t; //////////////////////////////////////////////////////////////////////////////// /// @brief weighted attribute //////////////////////////////////////////////////////////////////////////////// typedef struct weighted_attribute_s { TRI_shape_aid_t _aid; int64_t _weight; TRI_shaped_json_t _value; } weighted_attribute_t; //////////////////////////////////////////////////////////////////////////////// /// @brief collection-based shaper //////////////////////////////////////////////////////////////////////////////// typedef struct voc_shaper_s { TRI_shaper_t base; TRI_associative_synced_t _attributeNames; TRI_associative_synced_t _attributeIds; TRI_associative_synced_t _shapeDictionary; TRI_associative_synced_t _shapeIds; TRI_associative_pointer_t _accessors; TRI_vector_pointer_t _sortedAttributes; TRI_associative_pointer_t _weightedAttributes; attribute_weights_t _weights; TRI_shape_aid_t _nextAid; TRI_shape_sid_t _nextSid; TRI_document_collection_t* _collection; TRI_mutex_t _shapeLock; TRI_mutex_t _attributeLock; TRI_mutex_t _accessorLock; } voc_shaper_t; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute name of a key //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyAttributeName (TRI_associative_synced_t* array, void const* key) { char const* k = (char const*) key; return TRI_FnvHashString(k); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute name of an element //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAttributeName (TRI_associative_synced_t* array, void const* element) { char const* e = (char const*) element; return TRI_FnvHashString(e + sizeof(TRI_df_attribute_marker_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute name and an attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyAttributeName (TRI_associative_synced_t* array, void const* key, void const* element) { char const* k = (char const*) key; char const* e = (char const*) element; return TRI_EqualString(k, e + sizeof(TRI_df_attribute_marker_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares two attribute strings stored in the attribute marker /// /// returns 0 if the strings are equal /// returns < 0 if the left string compares less than the right string /// returns > 0 if the left string compares more than the right string //////////////////////////////////////////////////////////////////////////////// static int CompareNameAttributeWeight (const void* leftItem, const void* rightItem) { const attribute_weight_t* l = (const attribute_weight_t*)(leftItem); const attribute_weight_t* r = (const attribute_weight_t*)(rightItem); assert(l); assert(r); return TRI_compare_utf8(l->_attribute, r->_attribute); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares two attribute strings stored in the attribute marker //////////////////////////////////////////////////////////////////////////////// static int CompareNameAttributeWeightPointer (const void* leftItem, const void* rightItem) { const attribute_weight_t* l = *((const attribute_weight_t**)(leftItem)); const attribute_weight_t* r = *((const attribute_weight_t**)(rightItem)); assert(l); assert(r); return TRI_compare_utf8(l->_attribute, r->_attribute); } //////////////////////////////////////////////////////////////////////////////// /// @brief search for attribute /// /// Performs a binary search on a list of attributes (attribute markers) and /// returns the position where a given attribute would be inserted //////////////////////////////////////////////////////////////////////////////// static int64_t SortedIndexOf (voc_shaper_t* shaper, attribute_weight_t* item) { int64_t leftPos; int64_t rightPos; int64_t midPos; int compareResult; leftPos = 0; rightPos = ((int64_t) shaper->_sortedAttributes._length) - 1; while (leftPos <= rightPos) { midPos = (leftPos + rightPos) / 2; compareResult = CompareNameAttributeWeight(TRI_AtVectorPointer(&(shaper->_sortedAttributes), midPos), (void*)(item)); if (compareResult < 0) { leftPos = midPos + 1; } else if (compareResult > 0) { rightPos = midPos - 1; } else { // should never happen since we do not allow duplicates here return -1; } } return leftPos; // insert it to the left of this position } //////////////////////////////////////////////////////////////////////////////// /// @brief sets the attribute weight /// /// helper method to store a list of attributes and their corresponding weights. //////////////////////////////////////////////////////////////////////////////// static void SetAttributeWeight (voc_shaper_t* shaper, attribute_weight_t* item, int64_t* searchResult, bool* weighted) { int64_t itemWeight; attribute_weight_t* leftItem; // nearest neighbour items attribute_weight_t* rightItem; // nearest neighbour items const int64_t resolution = 100; void* result; *weighted = false; assert(item != NULL); assert(shaper != NULL); *searchResult = SortedIndexOf(shaper, item); if (*searchResult < 0 || *searchResult > (int64_t) shaper->_sortedAttributes._length) { // oops assert(false); return; } switch ( (shaper->_sortedAttributes)._length) { case 0: { item->_weight = 0; *weighted = true; break; } case 1: { if (*searchResult == 0) { item->_weight = 0; rightItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), 0)); rightItem->_weight = resolution; } else { leftItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), 0)); leftItem->_weight = 0; item->_weight = resolution; } *weighted = true; break; } default: { if (*searchResult == 0) { rightItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), 0)); item->_weight = rightItem->_weight - resolution; *weighted = true; } else if (*searchResult == (int64_t) (shaper->_sortedAttributes)._length) { leftItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), (shaper->_sortedAttributes)._length - 1)); item->_weight = leftItem->_weight + resolution; *weighted = true; } else { leftItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), *searchResult - 1)); rightItem = (attribute_weight_t*)(TRI_AtVectorPointer(&(shaper->_sortedAttributes), *searchResult)); itemWeight = (rightItem->_weight + leftItem->_weight) / 2; if (leftItem->_weight != itemWeight && rightItem->_weight != itemWeight) { item->_weight = itemWeight; *weighted = true; } } break; } } // end of switch statement result = TRI_InsertKeyAssociativePointer(&(shaper->_weightedAttributes), &(item->_aid), item, false); if (result != NULL) { LOG_ERROR("attribute weight could not be inserted into associative array"); *searchResult = -1; return; } // ........................................................................... // Obtain the pointer of the weighted attribute structure which is stored // in the associative array. We need to pass this pointer to the Vector Pointer // ........................................................................... item = static_cast(TRI_LookupByKeyAssociativePointer(&(shaper->_weightedAttributes), &(item->_aid))); if (item == NULL) { LOG_ERROR("attribute weight could not be located immediately after insert into associative array"); *searchResult = -1; return; } TRI_InsertVectorPointer(&(shaper->_sortedAttributes), item, (size_t) (*searchResult)); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets the attribute weight //////////////////////////////////////////////////////////////////////////////// static void FullSetAttributeWeight (voc_shaper_t* shaper) { int64_t startWeight; attribute_weight_t* item; size_t j; startWeight = 0; for (j = 0; j < shaper->_sortedAttributes._length; ++j) { item = (attribute_weight_t*) TRI_AtVectorPointer(&(shaper->_sortedAttributes), j); item->_weight = startWeight; startWeight += 100; } } //////////////////////////////////////////////////////////////////////////////// /// @brief finds an attribute identifier by name //////////////////////////////////////////////////////////////////////////////// static TRI_shape_aid_t LookupAttributeByName (TRI_shaper_t* shaper, char const* name) { voc_shaper_t* s; void const* p; assert(name != NULL); s = (voc_shaper_t*) shaper; p = TRI_LookupByKeyAssociativeSynced(&s->_attributeNames, name); if (p != NULL) { return ((TRI_df_attribute_marker_t const*) p)->_aid; } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds an attribute identifier by name //////////////////////////////////////////////////////////////////////////////// static TRI_shape_aid_t FindOrCreateAttributeByName (TRI_shaper_t* shaper, char const* name, bool isLocked) { char* mem; TRI_df_attribute_marker_t* marker; TRI_df_marker_t* result; TRI_df_attribute_marker_t* markerResult; TRI_doc_datafile_info_t* dfi; TRI_voc_fid_t fid; int res; size_t n; voc_shaper_t* s; TRI_shape_aid_t aid; TRI_voc_size_t totalSize; void const* p; void* f; int64_t searchResult; bool weighted; assert(name != NULL); s = (voc_shaper_t*) shaper; p = TRI_LookupByKeyAssociativeSynced(&s->_attributeNames, name); if (p != NULL) { return ((TRI_df_attribute_marker_t const*) p)->_aid; } // create a new attribute name n = strlen(name) + 1; totalSize = (TRI_voc_size_t) (sizeof(TRI_df_attribute_marker_t) + n); mem = (char*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, totalSize, false); if (mem == NULL) { TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); return 0; } // marker points to mem, but has a different type marker = (TRI_df_attribute_marker_t*) mem; assert(marker != NULL); // init attribute marker TRI_InitMarker(mem, TRI_DF_MARKER_ATTRIBUTE, totalSize); // copy attribute name into marker memcpy(mem + sizeof(TRI_df_attribute_marker_t), name, n); marker->_size = n; // lock the index and check that the element is still missing TRI_LockMutex(&s->_attributeLock); p = TRI_LookupByKeyAssociativeSynced(&s->_attributeNames, name); // if the element appeared, return the aid if (p != NULL) { TRI_UnlockMutex(&s->_attributeLock); TRI_Free(TRI_UNKNOWN_MEM_ZONE, mem); return ((TRI_df_attribute_marker_t const*) p)->_aid; } // get next attribute id and write into marker aid = s->_nextAid++; marker->_aid = aid; if (! isLocked) { // write-lock s->_collection->base.beginWrite(&s->_collection->base); } // write attribute into the collection res = TRI_WriteMarkerDocumentCollection(s->_collection, &marker->base, totalSize, &fid, &result, false); if (! isLocked) { s->_collection->base.endWrite(&s->_collection->base); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, mem); if (res != TRI_ERROR_NO_ERROR) { TRI_UnlockMutex(&s->_attributeLock); LOG_ERROR("an error occurred while writing attribute data into shapes collection: %s", TRI_errno_string(res)); return 0; } assert(result != NULL); // update datafile info dfi = TRI_FindDatafileInfoPrimaryCollection(&s->_collection->base, fid, true); if (dfi != NULL) { dfi->_numberAttributes++; dfi->_sizeAttributes += (int64_t) TRI_DF_ALIGN_BLOCK(totalSize); } f = TRI_InsertKeyAssociativeSynced(&s->_attributeIds, &aid, result, false); assert(f == NULL); // enter into the dictionaries f = TRI_InsertKeyAssociativeSynced(&s->_attributeNames, name, result, false); assert(f == NULL); // ........................................................................... // Each attribute has an associated integer as a weight. This // weight corresponds to the natural ordering of the attribute strings // ........................................................................... markerResult = (TRI_df_attribute_marker_t*) result; attribute_weight_t* weightedAttribute = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(attribute_weight_t), false)); if (weightedAttribute != NULL) { weightedAttribute->_aid = markerResult->_aid; weightedAttribute->_weight = TRI_VOC_UNDEFINED_ATTRIBUTE_WEIGHT; weightedAttribute->_attribute = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, name); weightedAttribute->_next = NULL; // .......................................................................... // Save the new attribute weight in the linked list. // .......................................................................... if ((s->_weights)._last == NULL) { (s->_weights)._first = weightedAttribute; } else { ((s->_weights)._last)->_next = weightedAttribute; } (s->_weights)._last = weightedAttribute; (s->_weights)._length += 1; SetAttributeWeight(s, weightedAttribute, &searchResult, &weighted); assert(searchResult > -1); if (! weighted) { FullSetAttributeWeight(s); } } else { LOG_WARNING("FindAttributeByName could not allocate memory, attribute is NOT weighted"); } // ........................................................................... // and release the lock // ........................................................................... TRI_UnlockMutex(&s->_attributeLock); return aid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the attribute id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyAttributeId (TRI_associative_synced_t* array, void const* key) { TRI_shape_aid_t const* k = static_cast(key); return TRI_FnvHashPointer(k, sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the attribute //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAttributeId (TRI_associative_synced_t* array, void const* element) { TRI_df_attribute_marker_t const* e = static_cast(element); return TRI_FnvHashPointer(&e->_aid, sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute name and an attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyAttributeId (TRI_associative_synced_t* array, void const* key, void const* element) { TRI_shape_aid_t const* k = static_cast(key); TRI_df_attribute_marker_t const* e = static_cast(element); return *k == e->_aid; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute name by identifier //////////////////////////////////////////////////////////////////////////////// static char const* LookupAttributeId (TRI_shaper_t* shaper, TRI_shape_aid_t aid) { voc_shaper_t* s = (voc_shaper_t*) shaper; void const* p = TRI_LookupByKeyAssociativeSynced(&s->_attributeIds, &aid); if (p == NULL) { return NULL; } return static_cast(p) + sizeof(TRI_df_attribute_marker_t); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute weight by identifier //////////////////////////////////////////////////////////////////////////////// static int64_t LookupAttributeWeight (TRI_shaper_t* shaper, TRI_shape_aid_t aid) { voc_shaper_t* s = (voc_shaper_t*) shaper; const attribute_weight_t* item; item = (const attribute_weight_t*)(TRI_LookupByKeyAssociativePointer(&s->_weightedAttributes, &aid)); if (item == NULL) { // ........................................................................ // return -9223372036854775807L 2^63-1 to indicate that the attribute is // weighted to be the lowest possible // ........................................................................ LOG_WARNING("LookupAttributeWeight returned NULL weight"); return TRI_VOC_UNDEFINED_ATTRIBUTE_WEIGHT; } if (item->_aid != aid) { // ........................................................................ // return -9223372036854775807L 2^63-1 to indicate that the attribute is // weighted to be the lowest possible // ........................................................................ LOG_WARNING("LookupAttributeWeight returned an UNDEFINED weight"); return TRI_VOC_UNDEFINED_ATTRIBUTE_WEIGHT; } return item->_weight; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shapes //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementShape (TRI_associative_synced_t* array, void const* element) { char const* e = static_cast(element); TRI_shape_t const* ee = static_cast(element); return TRI_FnvHashPointer(e + + sizeof(TRI_shape_sid_t), ee->_size - sizeof(TRI_shape_sid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares shapes //////////////////////////////////////////////////////////////////////////////// static bool EqualElementShape (TRI_associative_synced_t* array, void const* left, void const* right) { char const* l = static_cast(left); char const* r = static_cast(right); TRI_shape_t const* ll = static_cast(left); TRI_shape_t const* rr = static_cast(right); return (ll->_size == rr->_size) && memcmp(l + sizeof(TRI_shape_sid_t), r + sizeof(TRI_shape_sid_t), ll->_size - sizeof(TRI_shape_sid_t)) == 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds a shape /// if the function returns non-NULL, the return value is a pointer to an /// already existing shape and the value must not be freed /// if the function returns NULL, it has not found the shape and was not able /// to create it. The value must then be freed by the caller //////////////////////////////////////////////////////////////////////////////// static TRI_shape_t const* FindShape (TRI_shaper_t* shaper, TRI_shape_t* shape, bool create, bool isLocked) { char* mem; TRI_df_marker_t* result; TRI_df_shape_marker_t* marker; TRI_doc_datafile_info_t* dfi; TRI_voc_fid_t fid; TRI_voc_size_t totalSize; TRI_shape_t const* found; TRI_shape_t* l; int res; voc_shaper_t* s; void* f; s = (voc_shaper_t*) shaper; found = TRI_LookupBasicShapeShaper(shape); if (found == NULL) { found = static_cast(TRI_LookupByElementAssociativeSynced(&s->_shapeDictionary, shape)); } // shape found, free argument and return if (found != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); return found; } // not found if (! create) { return 0; } // initialise a new shape marker totalSize = (TRI_voc_size_t) (sizeof(TRI_df_shape_marker_t) + shape->_size); mem = (char*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, totalSize, false); if (mem == NULL) { TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); return NULL; } marker = (TRI_df_shape_marker_t*) mem; assert(marker != NULL); TRI_InitMarker(mem, TRI_DF_MARKER_SHAPE, totalSize); // copy shape into the marker memcpy(mem + sizeof(TRI_df_shape_marker_t), shape, shape->_size); // lock the index and check the element is still missing TRI_LockMutex(&s->_shapeLock); found = static_cast(TRI_LookupByElementAssociativeSynced(&s->_shapeDictionary, shape)); if (found != 0) { TRI_UnlockMutex(&s->_shapeLock); TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); TRI_Free(TRI_UNKNOWN_MEM_ZONE, mem); return found; } // get next shape number and write into marker ((TRI_shape_t*) (mem + sizeof(TRI_df_shape_marker_t)))->_sid = s->_nextSid++; if (! isLocked) { // write-lock s->_collection->base.beginWrite(&s->_collection->base); } // write shape into the collection res = TRI_WriteMarkerDocumentCollection(s->_collection, &marker->base, totalSize, &fid, &result, false); if (! isLocked) { // write-unlock s->_collection->base.endWrite(&s->_collection->base); } if (res != TRI_ERROR_NO_ERROR) { TRI_UnlockMutex(&s->_shapeLock); LOG_ERROR("an error occurred while writing shape data into shapes collection: %s", TRI_errno_string(res)); TRI_Free(TRI_UNKNOWN_MEM_ZONE, mem); return NULL; } assert(result != NULL); // update datafile info dfi = TRI_FindDatafileInfoPrimaryCollection(&s->_collection->base, fid, true); if (dfi != NULL) { dfi->_numberShapes++; dfi->_sizeShapes += (int64_t) TRI_DF_ALIGN_BLOCK(totalSize); } // enter into the dictionaries l = (TRI_shape_t*) (((char*) result) + sizeof(TRI_df_shape_marker_t)); f = TRI_InsertKeyAssociativeSynced(&s->_shapeIds, &l->_sid, l, false); assert(f == NULL); f = TRI_InsertElementAssociativeSynced(&s->_shapeDictionary, l, false); assert(f == NULL); TRI_UnlockMutex(&s->_shapeLock); TRI_Free(TRI_UNKNOWN_MEM_ZONE, mem); TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); return l; } //////////////////////////////////////////////////////////////////////////////// /// @brief compares to weighted attributes //////////////////////////////////////////////////////////////////////////////// static int AttributeWeightCompareFunction (const void* leftItem, const void* rightItem) { const weighted_attribute_t* l = (const weighted_attribute_t*) leftItem; const weighted_attribute_t* r = (const weighted_attribute_t*) rightItem; if (l->_weight < r->_weight) { return -1; } if (l->_weight > r->_weight) { return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief free weighted attribute /// /// Helper method to deal with freeing memory associated with the weight /// comparisions. //////////////////////////////////////////////////////////////////////////////// static void FreeShapeTypeJsonArrayHelper (weighted_attribute_t** leftWeightedList, weighted_attribute_t** rightWeightedList) { if (*leftWeightedList != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, *leftWeightedList); *leftWeightedList = NULL; } if (*rightWeightedList != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, *rightWeightedList); *rightWeightedList = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the number of entries //////////////////////////////////////////////////////////////////////////////// static int CompareShapeTypeJsonArrayHelper (const TRI_shape_t* shape, const TRI_shaper_t* shaper, const TRI_shaped_json_t* shapedJson, weighted_attribute_t** attributeArray) { char* charShape = (char*)(shape); TRI_shape_size_t fixedEntries; // the number of entries in the JSON array whose value is of a fixed size TRI_shape_size_t variableEntries; // the number of entries in the JSON array whose value is not of a known fixed size TRI_shape_size_t j; const TRI_shape_aid_t* aids; const TRI_shape_sid_t* sids; const TRI_shape_size_t* offsets; // ............................................................................. // Ensure we return an empty array - in case of funny business below // ............................................................................. *attributeArray = NULL; // ............................................................................. // Determine the number of fixed sized values // ............................................................................. charShape = charShape + sizeof(TRI_shape_t); fixedEntries = *((TRI_shape_size_t*)(charShape)); // ............................................................................. // Determine the number of variable sized values // ............................................................................. charShape = charShape + sizeof(TRI_shape_size_t); variableEntries = *((TRI_shape_size_t*)(charShape)); // ............................................................................. // It may happen that the shaped_json_array is 'empty {}' // ............................................................................. if ((fixedEntries + variableEntries) == 0) { return 0; } // ............................................................................. // Allocate memory to hold the attribute information required for comparison // ............................................................................. *attributeArray = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (sizeof(weighted_attribute_t) * (fixedEntries + variableEntries)), false)); if (*attributeArray == NULL) { return -1; } // ............................................................................. // Determine the list of shape identifiers // ............................................................................. charShape = charShape + sizeof(TRI_shape_size_t); sids = (const TRI_shape_sid_t*)(charShape); charShape = charShape + (sizeof(TRI_shape_sid_t) * (fixedEntries + variableEntries)); aids = (const TRI_shape_aid_t*)(charShape); charShape = charShape + (sizeof(TRI_shape_aid_t) * (fixedEntries + variableEntries)); offsets = (const TRI_shape_size_t*)(charShape); for (j = 0; j < fixedEntries; ++j) { (*attributeArray)[j]._aid = aids[j]; (*attributeArray)[j]._weight = shaper->lookupAttributeWeight((TRI_shaper_t*)(shaper),aids[j]); (*attributeArray)[j]._value._sid = sids[j]; (*attributeArray)[j]._value._data.data = shapedJson->_data.data + offsets[j]; (*attributeArray)[j]._value._data.length = (uint32_t) (offsets[j + 1] - offsets[j]); } offsets = (const TRI_shape_size_t*)(shapedJson->_data.data); for (j = 0; j < variableEntries; ++j) { TRI_shape_size_t jj = j + fixedEntries; (*attributeArray)[jj]._aid = aids[jj]; (*attributeArray)[jj]._weight = shaper->lookupAttributeWeight((TRI_shaper_t*)(shaper),aids[jj]); (*attributeArray)[jj]._value._sid = sids[jj]; (*attributeArray)[jj]._value._data.data = shapedJson->_data.data + offsets[j]; (*attributeArray)[jj]._value._data.length = (uint32_t) (offsets[j + 1] - offsets[j]); } return (int) (fixedEntries + variableEntries); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shape id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyShapeId (TRI_associative_synced_t* array, void const* key) { TRI_shape_sid_t const* k = static_cast(key); return TRI_FnvHashPointer(k, sizeof(TRI_shape_sid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shape //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementShapeId (TRI_associative_synced_t* array, void const* element) { TRI_shape_t const* e = static_cast(element); return TRI_FnvHashPointer(&e->_sid, sizeof(TRI_shape_sid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a shape id and a shape //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyShapeId (TRI_associative_synced_t* array, void const* key, void const* element) { TRI_shape_sid_t const* k = static_cast(key); TRI_shape_t const* e = static_cast(element); return *k == e->_sid; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a shape by identifier //////////////////////////////////////////////////////////////////////////////// static TRI_shape_t const* LookupShapeId (TRI_shaper_t* shaper, TRI_shape_sid_t sid) { TRI_shape_t const* shape = TRI_LookupSidBasicShapeShaper(sid); if (shape == NULL) { voc_shaper_t* s = (voc_shaper_t*) shaper; shape = static_cast(TRI_LookupByKeyAssociativeSynced(&s->_shapeIds, &sid)); } return shape; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the accessor //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAccessor (TRI_associative_pointer_t* array, void const* element) { TRI_shape_access_t const* ee = static_cast(element); uint64_t v[2]; v[0] = ee->_sid; v[1] = ee->_pid; return TRI_FnvHashPointer(v, sizeof(v)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an accessor //////////////////////////////////////////////////////////////////////////////// static bool EqualElementAccessor (TRI_associative_pointer_t* array, void const* left, void const* right) { TRI_shape_access_t const* ll = static_cast(left); TRI_shape_access_t const* rr = static_cast(right); return ll->_sid == rr->_sid && ll->_pid == rr->_pid; } //////////////////////////////////////////////////////////////////////////////// /// @brief Hashes a weighted attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyElementWeightedAttribute (TRI_associative_pointer_t* array, const void* key, const void* element) { TRI_shape_aid_t* aid = (TRI_shape_aid_t*)(key); attribute_weight_t* item = (attribute_weight_t*)(element); return (*aid == item->_aid); } static uint64_t HashKeyWeightedAttribute (TRI_associative_pointer_t* array, const void* key) { TRI_shape_aid_t* aid = (TRI_shape_aid_t*)(key); return TRI_FnvHashBlock(TRI_FnvHashBlockInitial(), (char*) aid, sizeof(TRI_shape_aid_t)); } static uint64_t HashElementWeightedAttribute (TRI_associative_pointer_t* array, const void* element) { attribute_weight_t* item = (attribute_weight_t*)(element); TRI_shape_aid_t* aid = &(item->_aid); return TRI_FnvHashBlock(TRI_FnvHashBlockInitial(), (char*) aid, sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a shaper //////////////////////////////////////////////////////////////////////////////// static int InitStep1VocShaper (voc_shaper_t* shaper) { int res; shaper->base.findOrCreateAttributeByName = FindOrCreateAttributeByName; shaper->base.lookupAttributeByName = LookupAttributeByName; shaper->base.lookupAttributeId = LookupAttributeId; shaper->base.findShape = FindShape; shaper->base.lookupShapeId = LookupShapeId; res = TRI_InitAssociativeSynced(&shaper->_attributeNames, TRI_UNKNOWN_MEM_ZONE, HashKeyAttributeName, HashElementAttributeName, EqualKeyAttributeName, 0); if (res != TRI_ERROR_NO_ERROR) { return res; } res = TRI_InitAssociativeSynced(&shaper->_attributeIds, TRI_UNKNOWN_MEM_ZONE, HashKeyAttributeId, HashElementAttributeId, EqualKeyAttributeId, 0); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativeSynced(&shaper->_attributeNames); return res; } res = TRI_InitAssociativeSynced(&shaper->_shapeDictionary, TRI_UNKNOWN_MEM_ZONE, 0, HashElementShape, 0, EqualElementShape); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativeSynced(&shaper->_attributeIds); TRI_DestroyAssociativeSynced(&shaper->_attributeNames); return res; } res = TRI_InitAssociativeSynced(&shaper->_shapeIds, TRI_UNKNOWN_MEM_ZONE, HashKeyShapeId, HashElementShapeId, EqualKeyShapeId, 0); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativeSynced(&shaper->_shapeDictionary); TRI_DestroyAssociativeSynced(&shaper->_attributeIds); TRI_DestroyAssociativeSynced(&shaper->_attributeNames); return res; } res = TRI_InitAssociativePointer(&shaper->_accessors, TRI_UNKNOWN_MEM_ZONE, 0, HashElementAccessor, 0, EqualElementAccessor); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativeSynced(&shaper->_shapeIds); TRI_DestroyAssociativeSynced(&shaper->_shapeDictionary); TRI_DestroyAssociativeSynced(&shaper->_attributeIds); TRI_DestroyAssociativeSynced(&shaper->_attributeNames); return res; } res = TRI_InitAssociativePointer(&shaper->_weightedAttributes, TRI_UNKNOWN_MEM_ZONE, HashKeyWeightedAttribute, HashElementWeightedAttribute, EqualKeyElementWeightedAttribute, NULL); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativePointer(&shaper->_accessors); TRI_DestroyAssociativeSynced(&shaper->_shapeIds); TRI_DestroyAssociativeSynced(&shaper->_shapeDictionary); TRI_DestroyAssociativeSynced(&shaper->_attributeIds); TRI_DestroyAssociativeSynced(&shaper->_attributeNames); return res; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a shaper //////////////////////////////////////////////////////////////////////////////// static int InitStep2VocShaper (voc_shaper_t* shaper) { TRI_InitMutex(&shaper->_shapeLock); TRI_InitMutex(&shaper->_attributeLock); TRI_InitMutex(&shaper->_accessorLock); shaper->_nextAid = 1; shaper->_nextSid = 7; // TODO!!!!!!!!!!!!!!!!! // .......................................................................... // Attribute weight // .......................................................................... shaper->base.lookupAttributeWeight = LookupAttributeWeight; TRI_InitVectorPointer(&shaper->_sortedAttributes, TRI_UNKNOWN_MEM_ZONE); // .......................................................................... // We require a place to store the weights -- a linked list is as good as // anything for now. Later try and store in a smart 2D array. // .......................................................................... (shaper->_weights)._first = NULL; (shaper->_weights)._last = NULL; (shaper->_weights)._length = 0; return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a shaper //////////////////////////////////////////////////////////////////////////////// static int InitStep3VocShaper (voc_shaper_t* shaper) { // ............................................................................. // Sort all the attributes using the attribute string // ............................................................................. qsort(shaper->_sortedAttributes._buffer, shaper->_sortedAttributes._length, sizeof(attribute_weight_t*), CompareNameAttributeWeightPointer); // ............................................................................. // re-weigh all of the attributes // ............................................................................. FullSetAttributeWeight(shaper); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief creates a shaper //////////////////////////////////////////////////////////////////////////////// TRI_shaper_t* TRI_CreateVocShaper (TRI_vocbase_t* vocbase, TRI_document_collection_t* document) { voc_shaper_t* shaper = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(voc_shaper_t), false)); if (shaper == NULL) { // out of memory return NULL; } shaper->_collection = document; int res = TRI_InitShaper(&shaper->base, TRI_UNKNOWN_MEM_ZONE); if (res != TRI_ERROR_NO_ERROR) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, shaper); return NULL; } res = InitStep1VocShaper(shaper); if (res != TRI_ERROR_NO_ERROR) { TRI_FreeShaper(&shaper->base); return NULL; } res = InitStep2VocShaper(shaper); if (res != TRI_ERROR_NO_ERROR) { TRI_FreeVocShaper(&shaper->base); return NULL; } // and return return &shaper->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a shaper, but does not free the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_DestroyVocShaper (TRI_shaper_t* s) { voc_shaper_t* shaper = (voc_shaper_t*) s; attribute_weight_t* weightedAttribute; attribute_weight_t* nextWeightedAttribute; size_t i; assert(shaper != NULL); TRI_DestroyAssociativeSynced(&shaper->_attributeNames); TRI_DestroyAssociativeSynced(&shaper->_attributeIds); TRI_DestroyAssociativeSynced(&shaper->_shapeDictionary); TRI_DestroyAssociativeSynced(&shaper->_shapeIds); for (i = 0; i < shaper->_accessors._nrAlloc; ++i) { TRI_shape_access_t* accessor = (TRI_shape_access_t*) shaper->_accessors._table[i]; if (accessor != NULL) { TRI_FreeShapeAccessor(accessor); } } TRI_DestroyAssociativePointer(&shaper->_accessors); TRI_DestroyMutex(&shaper->_shapeLock); TRI_DestroyMutex(&shaper->_attributeLock); // .......................................................................... // Attribute weight // .......................................................................... TRI_DestroyVectorPointer(&shaper->_sortedAttributes); TRI_DestroyAssociativePointer(&shaper->_weightedAttributes); weightedAttribute = (shaper->_weights)._first; while (weightedAttribute != NULL) { nextWeightedAttribute = weightedAttribute->_next; // free attribute name if (weightedAttribute->_attribute != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, weightedAttribute->_attribute); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, weightedAttribute); weightedAttribute = nextWeightedAttribute; } TRI_DestroyShaper(s); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a shaper and frees the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_FreeVocShaper (TRI_shaper_t* shaper) { TRI_DestroyVocShaper(shaper); TRI_Free(TRI_UNKNOWN_MEM_ZONE, shaper); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a shaper, but does not free the pointer //////////////////////////////////////////////////////////////////////////////// int TRI_InitVocShaper (TRI_shaper_t* s) { voc_shaper_t* shaper = (voc_shaper_t*) s; return InitStep3VocShaper(shaper); } //////////////////////////////////////////////////////////////////////////////// /// @brief move a shape marker, called during compaction //////////////////////////////////////////////////////////////////////////////// int TRI_MoveMarkerVocShaper (TRI_shaper_t* s, TRI_df_marker_t* marker) { voc_shaper_t* shaper = (voc_shaper_t*) s; if (marker->_type == TRI_DF_MARKER_SHAPE) { char* p = ((char*) marker) + sizeof(TRI_df_shape_marker_t); TRI_shape_t* l = (TRI_shape_t*) p; void* f; TRI_LockMutex(&shaper->_shapeLock); // remove the old marker // and re-insert the marker with the new pointer f = TRI_InsertKeyAssociativeSynced(&shaper->_shapeIds, &l->_sid, l, true); assert(f != NULL); // same for the shape dictionary // delete and re-insert f = TRI_InsertElementAssociativeSynced(&shaper->_shapeDictionary, l, true); assert(f != NULL); TRI_UnlockMutex(&shaper->_shapeLock); } else if (marker->_type == TRI_DF_MARKER_ATTRIBUTE) { TRI_df_attribute_marker_t* m = (TRI_df_attribute_marker_t*) marker; char* p = ((char*) m) + sizeof(TRI_df_attribute_marker_t); void* f; TRI_LockMutex(&shaper->_attributeLock); // remove attribute by name (p points to new location of name, but names // are identical in old and new marker) // and re-insert same attribute with adjusted pointer f = TRI_InsertKeyAssociativeSynced(&shaper->_attributeNames, p, m, true); assert(f != NULL); // same for attribute ids // delete and re-insert same attribute with adjusted pointer f = TRI_InsertKeyAssociativeSynced(&shaper->_attributeIds, &m->_aid, m, true); assert(f != NULL); TRI_UnlockMutex(&shaper->_attributeLock); } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief insert a shape, called when opening a collection //////////////////////////////////////////////////////////////////////////////// int TRI_InsertShapeVocShaper (TRI_shaper_t* s, TRI_df_marker_t const* marker) { voc_shaper_t* shaper = (voc_shaper_t*) s; char* p = ((char*) marker) + sizeof(TRI_df_shape_marker_t); TRI_shape_t* l = (TRI_shape_t*) p; void* f; LOG_TRACE("found shape %lu", (unsigned long) l->_sid); f = TRI_InsertElementAssociativeSynced(&shaper->_shapeDictionary, l, false); assert(f == NULL); f = TRI_InsertKeyAssociativeSynced(&shaper->_shapeIds, &l->_sid, l, false); assert(f == NULL); if (shaper->_nextSid <= l->_sid) { shaper->_nextSid = l->_sid + 1; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief insert an attribute, called when opening a collection //////////////////////////////////////////////////////////////////////////////// int TRI_InsertAttributeVocShaper (TRI_shaper_t* s, TRI_df_marker_t const* marker) { voc_shaper_t* shaper = (voc_shaper_t*) s; TRI_df_attribute_marker_t* m = (TRI_df_attribute_marker_t*) marker; char* p = ((char*) m) + sizeof(TRI_df_attribute_marker_t); void* f; LOG_TRACE("found attribute '%s', aid: %lu", p, (unsigned long) m->_aid); f = TRI_InsertKeyAssociativeSynced(&shaper->_attributeNames, p, m, false); if (f != NULL) { char const* name = shaper->_collection->base.base._info._name; #ifdef TRI_ENABLE_MAINTAINER_MODE LOG_WARNING("found duplicate attribute name '%s' in collection '%s'", p, name); #else LOG_TRACE("found duplicate attribute name '%s' in collection '%s'", p, name); #endif } f = TRI_InsertKeyAssociativeSynced(&shaper->_attributeIds, &m->_aid, m, false); if (f != NULL) { char const* name = shaper->_collection->base.base._info._name; #ifdef TRI_ENABLE_MAINTAINER_MODE LOG_WARNING("found duplicate attribute id '%llu' in collection '%s'", (unsigned long long) m->_aid, name); #else LOG_TRACE("found duplicate attribute id '%llu' in collection '%s'", (unsigned long long) m->_aid, name); #endif } // no lock is necessary here as we are the only users of the shaper at this time if (shaper->_nextAid <= m->_aid) { shaper->_nextAid = m->_aid + 1; } // ......................................................................... // Add the attributes to the 'sorted' vector. These are in random order // at the moment, however they will be sorted once all the attributes // have been loaded into memory. // ......................................................................... attribute_weight_t* weightedAttribute = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(attribute_weight_t), false)); if (weightedAttribute != NULL) { attribute_weight_t* result; weightedAttribute->_aid = m->_aid; weightedAttribute->_weight = TRI_VOC_UNDEFINED_ATTRIBUTE_WEIGHT; weightedAttribute->_attribute = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, (char*) m + sizeof(TRI_df_attribute_marker_t)); weightedAttribute->_next = NULL; // .......................................................................... // Save the new attribute weight in the linked list. // .......................................................................... if ((shaper->_weights)._last == NULL) { (shaper->_weights)._first = weightedAttribute; } else { ((shaper->_weights)._last)->_next = weightedAttribute; } (shaper->_weights)._last = weightedAttribute; (shaper->_weights)._length += 1; result = (attribute_weight_t*) TRI_InsertKeyAssociativePointer(&(shaper->_weightedAttributes), &(weightedAttribute->_aid), weightedAttribute, false); if (result == NULL) { attribute_weight_t* weightedItem = static_cast(TRI_LookupByKeyAssociativePointer(&(shaper->_weightedAttributes), &(weightedAttribute->_aid))); if (weightedItem == NULL || weightedItem->_aid != weightedAttribute->_aid) { LOG_ERROR("attribute weight could not be located immediately after insert into associative array"); } else { TRI_PushBackVectorPointer(&shaper->_sortedAttributes, weightedItem); } return TRI_ERROR_NO_ERROR; } LOG_WARNING("weighted attribute could not be inserted into associative array"); } else { LOG_WARNING("OpenIterator could not allocate memory, attribute is NOT weighted"); } return TRI_ERROR_OUT_OF_MEMORY; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds an accessor for a shaper //////////////////////////////////////////////////////////////////////////////// TRI_shape_access_t const* TRI_FindAccessorVocShaper (TRI_shaper_t* s, TRI_shape_sid_t sid, TRI_shape_pid_t pid) { voc_shaper_t* shaper = (voc_shaper_t*) s; TRI_shape_access_t search; TRI_shape_access_t* accessor; search._sid = sid; search._pid = pid; TRI_LockMutex(&shaper->_accessorLock); TRI_shape_access_t const* found = static_cast(TRI_LookupByElementAssociativePointer(&shaper->_accessors, &search)); if (found == NULL) { found = accessor = TRI_ShapeAccessor(&shaper->base, sid, pid); // TRI_ShapeAccessor can return a NULL pointer if (found != NULL) { TRI_InsertElementAssociativePointer(&shaper->_accessors, accessor, true); } } TRI_UnlockMutex(&shaper->_accessorLock); return found; } //////////////////////////////////////////////////////////////////////////////// /// @brief extracts a sub-shape //////////////////////////////////////////////////////////////////////////////// bool TRI_ExtractShapedJsonVocShaper (TRI_shaper_t* shaper, TRI_shaped_json_t const* document, TRI_shape_sid_t sid, TRI_shape_pid_t pid, TRI_shaped_json_t* result, TRI_shape_t const** shape) { TRI_shape_access_t const* accessor; bool ok; accessor = TRI_FindAccessorVocShaper(shaper, document->_sid, pid); if (accessor == NULL) { LOG_TRACE("failed to get accessor for sid %lu and path %lu", (unsigned long) document->_sid, (unsigned long) pid); return false; } *shape = accessor->_shape; if (accessor->_shape == NULL) { LOG_TRACE("expecting any object for path %lu, got nothing", (unsigned long) pid); return sid == 0; } if (sid != 0 && sid != accessor->_shape->_sid) { LOG_TRACE("expecting sid %lu for path %lu, got sid %lu", (unsigned long) sid, (unsigned long) pid, (unsigned long) accessor->_shape->_sid); return false; } ok = TRI_ExecuteShapeAccessor(accessor, document, result); if (! ok) { LOG_TRACE("failed to get accessor for sid %lu and path %lu", (unsigned long) document->_sid, (unsigned long) pid); return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief helper method for recursion for comparison /// /// You must either supply (leftDocument, leftObject) or leftShaped. /// You must either supply (rightDocument, rightObject) or rightShaped. //////////////////////////////////////////////////////////////////////////////// int TRI_CompareShapeTypes (TRI_doc_mptr_t* leftDocument, TRI_shaped_sub_t* leftObject, TRI_shaped_json_t const* leftShaped, TRI_doc_mptr_t* rightDocument, TRI_shaped_sub_t* rightObject, TRI_shaped_json_t const* rightShaped, TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper) { TRI_shape_t const* leftShape; TRI_shape_t const* rightShape; TRI_shaped_json_t left; TRI_shape_type_t leftType; TRI_shape_type_t rightType; TRI_shaped_json_t leftElement; TRI_shaped_json_t right; TRI_shaped_json_t rightElement; char const* ptr; char* leftString; char* rightString; int i; int leftNumWeightedList; int numWeightedList; int result; int rightNumWeightedList; size_t j; size_t leftListLength; size_t listLength; size_t rightListLength; weighted_attribute_t* leftWeightedList; weighted_attribute_t* rightWeightedList; // left is either a shaped json or a shaped sub object if (leftDocument != NULL) { ptr = (char const*) leftDocument->_data; left._sid = leftObject->_sid; left._data.length = (uint32_t) leftObject->_length; left._data.data = const_cast(ptr) + leftObject->_offset; } else { left = *leftShaped; } // right is either a shaped json or a shaped sub object if (rightDocument != NULL) { ptr = (char const*) rightDocument->_data; right._sid = rightObject->_sid; right._data.length = (uint32_t) rightObject->_length; right._data.data = const_cast(ptr) + rightObject->_offset; } else { right = *rightShaped; } // get shape and type if (leftShaper == rightShaper && left._sid == right._sid) { // identical collection and shape leftShape = rightShape = leftShaper->lookupShapeId(leftShaper, left._sid); } else { // different shapes leftShape = leftShaper->lookupShapeId(leftShaper, left._sid); rightShape = rightShaper->lookupShapeId(rightShaper, right._sid); } if (leftShape == NULL || rightShape == NULL) { LOG_ERROR("shape not found"); assert(false); } leftType = leftShape->_type; rightType = rightShape->_type; // ............................................................................. // check ALL combinations of leftType and rightType // ............................................................................. switch (leftType) { // ............................................................................. // illegal type // ............................................................................. case TRI_SHAPE_ILLEGAL: { switch (rightType) { case TRI_SHAPE_ILLEGAL: { return 0; } case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_ILLEGAL // ............................................................................. // NULL // ............................................................................. case TRI_SHAPE_NULL: { switch (rightType) { case TRI_SHAPE_ILLEGAL: { return 1; } case TRI_SHAPE_NULL: { return 0; } case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_NULL // ............................................................................. // BOOLEAN // ............................................................................. case TRI_SHAPE_BOOLEAN: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: { return 1; } case TRI_SHAPE_BOOLEAN: { // check which is false and which is true! if ( *((TRI_shape_boolean_t*)(left._data.data)) == *((TRI_shape_boolean_t*)(right._data.data)) ) { return 0; } if ( *((TRI_shape_boolean_t*)(left._data.data)) < *((TRI_shape_boolean_t*)(right._data.data)) ) { return -1; } return 1; } case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_BOOLEAN // ............................................................................. // NUMBER // ............................................................................. case TRI_SHAPE_NUMBER: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: { return 1; } case TRI_SHAPE_NUMBER: { // compare the numbers if ( *((TRI_shape_number_t*)(left._data.data)) == *((TRI_shape_number_t*)(right._data.data)) ) { return 0; } if ( *((TRI_shape_number_t*)(left._data.data)) < *((TRI_shape_number_t*)(right._data.data)) ) { return -1; } return 1; } case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_NUMBER // ............................................................................. // STRING // ............................................................................. case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: { return 1; } case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { // compare strings // extract the strings if (leftType == TRI_SHAPE_SHORT_STRING) { leftString = (char*)(sizeof(TRI_shape_length_short_string_t) + left._data.data); } else { leftString = (char*)(sizeof(TRI_shape_length_long_string_t) + left._data.data); } if (rightType == TRI_SHAPE_SHORT_STRING) { rightString = (char*)(sizeof(TRI_shape_length_short_string_t) + right._data.data); } else { rightString = (char*)(sizeof(TRI_shape_length_long_string_t) + right._data.data); } return TRI_compare_utf8(leftString,rightString); } case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_LONG/SHORT_STRING // ............................................................................. // HOMOGENEOUS LIST // ............................................................................. case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { return 1; } case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { // unfortunately recursion: check the types of all the entries leftListLength = *((TRI_shape_length_list_t*)(left._data.data)); rightListLength = *((TRI_shape_length_list_t*)(right._data.data)); // determine the smallest list if (leftListLength > rightListLength) { listLength = rightListLength; } else { listLength = leftListLength; } for (j = 0; j < listLength; ++j) { if (leftType == TRI_SHAPE_HOMOGENEOUS_LIST) { TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(leftShape), &left, j, &leftElement); } else if (leftType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(leftShape), &left, j, &leftElement); } else { TRI_AtListShapedJson((const TRI_list_shape_t*)(leftShape), &left, j, &leftElement); } if (rightType == TRI_SHAPE_HOMOGENEOUS_LIST) { TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(rightShape), &right, j, &rightElement); } else if (rightType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(rightShape), &right, j, &rightElement); } else { TRI_AtListShapedJson((const TRI_list_shape_t*)(rightShape), &right, j, &rightElement); } result = TRI_CompareShapeTypes(NULL, NULL, &leftElement, NULL, NULL, &rightElement, leftShaper, rightShaper); if (result != 0) { return result; } } // up to listLength everything matches if (leftListLength < rightListLength) { return -1; } else if (leftListLength > rightListLength) { return 1; } return 0; } case TRI_SHAPE_ARRAY: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_LIST ... // ............................................................................. // ARRAY // ............................................................................. case TRI_SHAPE_ARRAY: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { return 1; } case TRI_SHAPE_ARRAY: { // ............................................................................ // We are comparing a left JSON array with another JSON array on the right // The comparison works as follows: // // Suppose that leftShape has m key/value pairs and that the // rightShape has n key/value pairs // // Extract the m key aids (attribute identifiers) from the leftShape // Extract the n key aids (attribute identifiers) from the rightShape // // Sort the key aids for both the left and right shape // according to the weight of the key (attribute) // // Let lw_j denote the weight of the jth key from the sorted leftShape key list // and rw_j the corresponding rightShape. // // If lw_j < rw_j return -1 // If lw_j > rw_j return 1 // If lw_j == rw_j, then we extract the values and compare the values // using recursion. // // If lv_j < rv_j return -1 // If lv_j > rv_j return 1 // If lv_j == rv_j, then repeat the process with j+1. // ............................................................................ // ............................................................................ // generate the left and right lists. // ............................................................................ leftNumWeightedList = CompareShapeTypeJsonArrayHelper(leftShape, leftShaper, &left, &leftWeightedList); rightNumWeightedList = CompareShapeTypeJsonArrayHelper(rightShape, rightShaper, &right, &rightWeightedList); // ............................................................................ // If the left and right both resulted in errors, we return equality for want // of something better. // ............................................................................ if ( (leftNumWeightedList < 0) && (rightNumWeightedList < 0) ) { // probably out of memory error FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return 0; } // ............................................................................ // If the left had an error, we rank the left as the smallest item in the order // ............................................................................ if (leftNumWeightedList < 0) { // probably out of memory error FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return -1; // attempt to compare as low as possible } // ............................................................................ // If the right had an error, we rank the right as the largest item in the order // ............................................................................ if (rightNumWeightedList < 0) { FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return 1; } // ............................................................................ // Are we comparing two empty shaped_json_arrays? // ............................................................................ if ( (leftNumWeightedList == 0) && (rightNumWeightedList == 0) ) { FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return 0; } // ............................................................................ // If the left is empty, then it is smaller than the right, right? // ............................................................................ if (leftNumWeightedList == 0) { FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return -1; } // ............................................................................ // ...and the opposite of the above. // ............................................................................ if (rightNumWeightedList == 0) { FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return 1; } // .............................................................................. // We now have to sort the left and right weighted list according to attribute weight // .............................................................................. qsort(leftWeightedList, leftNumWeightedList, sizeof(weighted_attribute_t), AttributeWeightCompareFunction); qsort(rightWeightedList, rightNumWeightedList, sizeof(weighted_attribute_t), AttributeWeightCompareFunction); // .............................................................................. // check the weight and if equal check the values. Notice that numWeightedList // below MUST be greater or equal to 1. // .............................................................................. numWeightedList = (leftNumWeightedList < rightNumWeightedList ? leftNumWeightedList: rightNumWeightedList); result = 0; for (i = 0; i < numWeightedList; ++i) { if (leftWeightedList[i]._weight != rightWeightedList[i]._weight) { result = (leftWeightedList[i]._weight < rightWeightedList[i]._weight ? -1: 1); break; } result = TRI_CompareShapeTypes(NULL, NULL, &(leftWeightedList[i]._value), NULL, NULL, &(rightWeightedList[i]._value), leftShaper, rightShaper); if (result != 0) { break; } // the attributes are equal now check for the values /* start oreste debug const char* name = leftShaper->lookupAttributeId(leftShaper,leftWeightedList[i]._aid); printf("%s:%u:w=%ld:%s\n",__FILE__,__LINE__,leftWeightedList[i]._weight,name); const char* name = rightShaper->lookupAttributeId(rightShaper,rightWeightedList[i]._aid); printf("%s:%u:w=%ld:%s\n",__FILE__,__LINE__,rightWeightedList[i]._weight,name); end oreste debug */ } if (result == 0) { // ............................................................................ // The comparisions above indicate that the shaped_json_arrays are equal, // however one more check to determine if the number of elements in the arrays // are equal. // ............................................................................ if (leftNumWeightedList < rightNumWeightedList) { result = -1; } else if (leftNumWeightedList > rightNumWeightedList) { result = 1; } } // .............................................................................. // Deallocate any memory for the comparisions and return the result // .............................................................................. FreeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); return result; } } // end of switch (rightType) } // end of case TRI_SHAPE_ARRAY } // end of switch (leftType) assert(false); return 0; //shut the vc++ up } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: