//////////////////////////////////////////////////////////////////////////////// /// @brief bitarray index /// /// @file /// /// DISCLAIMER /// /// Copyright by triAGENS GmbH - All rights reserved. /// /// The Programs (which include both the software and documentation) /// contain proprietary information of triAGENS GmbH; they are /// provided under a license agreement containing restrictions on use and /// disclosure and are also protected by copyright, patent and other /// intellectual and industrial property laws. Reverse engineering, /// disassembly or decompilation of the Programs, except to the extent /// required to obtain interoperability with other independently created /// software or as specified by law, is prohibited. /// /// The Programs are not intended for use in any nuclear, aviation, mass /// transit, medical, or other inherently dangerous applications. It shall /// be the licensee's responsibility to take all appropriate fail-safe, /// backup, redundancy, and other measures to ensure the safe use of such /// applications if the Programs are used for such purposes, and triAGENS /// GmbH disclaims liability for any damages caused by such use of /// the Programs. /// /// This software is the confidential and proprietary information of /// triAGENS GmbH. You shall not disclose such confidential and /// proprietary information and shall use it only in accordance with the /// terms of the license agreement you entered into with triAGENS GmbH. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. O /// @author Copyright 2011, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "bitarrayIndex.h" #include "BasicsC/string-buffer.h" #include "ShapedJson/json-shaper.h" #include "ShapedJson/shaped-json.h" #include "VocBase/document-collection.h" #include "BasicsC/logging.h" // ............................................................................. // forward declaration of static functions used for iterator callbacks // ............................................................................. static void BitarrayIndexDestroyIterator (TRI_index_iterator_t*); static bool BitarrayIndexHasNextIterationCallback (TRI_index_iterator_t*); static void* BitarrayIndexNextIterationCallback (TRI_index_iterator_t*); static void* BitarrayIndexNextsIterationCallback (TRI_index_iterator_t*, int64_t); static bool BitarrayIndexHasPrevIterationCallback (TRI_index_iterator_t*); static void* BitarrayIndexPrevIterationCallback (TRI_index_iterator_t*); static void* BitarrayIndexPrevsIterationCallback (TRI_index_iterator_t*, int64_t); static void BitarrayIndexResetIterator (TRI_index_iterator_t*, bool); // ............................................................................. // forward declaration of static functions used here // ............................................................................. static int BitarrayIndex_findHelper (BitarrayIndex*, TRI_vector_t*, TRI_index_operator_t*, TRI_index_iterator_t*); static int generateBitMask (BitarrayIndex*, const BitarrayIndexElement*, TRI_bitarray_mask_t*); static int generateEqualBitMask (BitarrayIndex*, const TRI_relation_index_operator_t*, TRI_bitarray_mask_t*); // static void debugPrintMask (BitarrayIndex*, uint64_t); // ----------------------------------------------------------------------------- // --SECTION-- bitarrayIndex common public methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup bitarrayIndex /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a bitarray index , but does not free the pointer //////////////////////////////////////////////////////////////////////////////// void BitarrayIndex_destroy(BitarrayIndex* baIndex) { size_t j; if (baIndex == NULL) { return; } for (j = 0; j < baIndex->_values._length; ++j) { TRI_DestroyJson(TRI_UNKNOWN_MEM_ZONE, (TRI_json_t*)(TRI_AtVector(&(baIndex->_values),j))); } TRI_DestroyVector(&baIndex->_values); TRI_FreeBitarray(baIndex->_bitarray); baIndex->_bitarray = NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a bitarray index and frees the pointer //////////////////////////////////////////////////////////////////////////////// void BitarrayIndex_free(BitarrayIndex* baIndex) { if (baIndex == NULL) { return; } BitarrayIndex_destroy(baIndex); TRI_Free(TRI_UNKNOWN_MEM_ZONE, baIndex); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ // Public Methods //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// /// @brief Creates a new bitarray index. Failure will return an appropriate error code //////////////////////////////////////////////////////////////////////////////// int BitarrayIndex_new(BitarrayIndex** baIndex, TRI_memory_zone_t* memoryZone, size_t cardinality, TRI_vector_t* values, bool supportUndef, void* context) { int result; size_t numArrays; int j; // ........................................................................... // Sime simple checks // ........................................................................... if (baIndex == NULL) { assert(false); return TRI_ERROR_INTERNAL; } // ........................................................................... // If the bit array index has arealdy been created, return internal error // ........................................................................... if (*baIndex != NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // If the memory zone is invalid, then return an internal error // ........................................................................... if (memoryZone == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Create the bit array index structure // ........................................................................... *baIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(BitarrayIndex), true); if (*baIndex == NULL) { return TRI_ERROR_OUT_OF_MEMORY; } // ........................................................................... // Copy the values into this index // ........................................................................... TRI_InitVector(&((*baIndex)->_values), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_json_t)); for (j = 0; j < values->_length; ++j) { TRI_json_t value; TRI_CopyToJson(TRI_UNKNOWN_MEM_ZONE, &value, (TRI_json_t*)(TRI_AtVector(values,j))); TRI_PushBackVector(&((*baIndex)->_values), &value); } // ........................................................................... // Store whether or not the index supports 'undefined' documents (that is // documents with attributes which do not match those of the index // ........................................................................... (*baIndex)->_supportUndef = supportUndef; // ........................................................................... // Determine the number of bit columns which will comprise the bit array index. // ........................................................................... numArrays = cardinality; // ........................................................................... // Create the bit arrays // ........................................................................... result = TRI_InitBitarray(&((*baIndex)->_bitarray), memoryZone, numArrays, NULL); // ........................................................................... // return the result of creating the bit arrays // ........................................................................... return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds (inserts) a data element into one or more bit arrays //////////////////////////////////////////////////////////////////////////////// int BitarrayIndex_add(BitarrayIndex* baIndex, BitarrayIndexElement* element) { int result; TRI_bitarray_mask_t mask; // .......................................................................... // At the current time we have no way in which to store undefined documents // need some sort of parameter passed here. // .......................................................................... // .......................................................................... // generate bit mask // .......................................................................... result = generateBitMask (baIndex, element, &mask); if (result != TRI_ERROR_NO_ERROR) { return result; } // .......................................................................... // insert the bit mask into the bit array // .......................................................................... result = TRI_InsertBitMaskElementBitarray(baIndex->_bitarray, &mask, element->data); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief attempts to locate one or more documents which match an index operator //////////////////////////////////////////////////////////////////////////////// TRI_index_iterator_t* BitarrayIndex_find(BitarrayIndex* baIndex, TRI_index_operator_t* indexOperator, TRI_vector_t* shapeList, void* collectionIndex, bool (*filter) (TRI_index_iterator_t*) ) { TRI_index_iterator_t* iterator; // ........................................................................... // Attempt to allocate memory for the index iterator which stores the results // if any of the lookup. // ........................................................................... iterator = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_index_iterator_t), true); if (iterator == NULL) { TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); return NULL; // calling procedure needs to care when the iterator is null } // ........................................................................... // We now initialise the index iterator with all the call back functions. // ........................................................................... TRI_InitVector(&(iterator->_intervals), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_index_iterator_interval_t)); iterator->_index = collectionIndex; iterator->_currentInterval = 0; iterator->_cursor = NULL; iterator->_filter = filter; iterator->_hasNext = BitarrayIndexHasNextIterationCallback; iterator->_next = BitarrayIndexNextIterationCallback; iterator->_nexts = BitarrayIndexNextsIterationCallback; iterator->_hasPrev = BitarrayIndexHasPrevIterationCallback; iterator->_prev = BitarrayIndexPrevIterationCallback; iterator->_prevs = BitarrayIndexPrevsIterationCallback; iterator->_destroyIterator = BitarrayIndexDestroyIterator; iterator->_reset = BitarrayIndexResetIterator; BitarrayIndex_findHelper(baIndex, shapeList, indexOperator, iterator); return iterator; } ////////////////////////////////////////////////////////////////////////////////// /// @brief alias for add Index item ////////////////////////////////////////////////////////////////////////////////// int BitarrayIndex_insert(BitarrayIndex* baIndex, BitarrayIndexElement* element) { return BitarrayIndex_add(baIndex,element); } ////////////////////////////////////////////////////////////////////////////////// /// @brief removes an entry from the bit arrays and master table ////////////////////////////////////////////////////////////////////////////////// int BitarrayIndex_remove(BitarrayIndex* baIndex, BitarrayIndexElement* element) { int result; result = TRI_RemoveElementBitarray(baIndex->_bitarray, element->data); return result; } ////////////////////////////////////////////////////////////////////////////////// /// @brief updates a bit array index entry ////////////////////////////////////////////////////////////////////////////////// int BitarrayIndex_update(BitarrayIndex* baIndex, const BitarrayIndexElement* oldElement, const BitarrayIndexElement* newElement) { assert(false); return TRI_ERROR_INTERNAL; } // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Implementation of static functions forward declared above // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // ............................................................................. // forward declaration of static functions used for iterator callbacks // ............................................................................. void BitarrayIndexDestroyIterator(TRI_index_iterator_t* iterator) { TRI_DestroyVector(&(iterator->_intervals)); } bool BitarrayIndexHasNextIterationCallback(TRI_index_iterator_t* iterator) { if (iterator->_intervals._length == 0) { return false; } if (iterator->_currentInterval >= iterator->_intervals._length) { return false; } return true; } void* BitarrayIndexNextIterationCallback(TRI_index_iterator_t* iterator) { TRI_index_iterator_interval_t* interval; iterator->_currentDocument = NULL; if (iterator->_cursor == NULL) { iterator->_currentInterval = 0; } if (iterator->_intervals._length == 0) { return NULL; } if (iterator->_currentInterval >= iterator->_intervals._length) { return NULL; } interval = (TRI_index_iterator_interval_t*)(TRI_AtVector(&(iterator->_intervals),iterator->_currentInterval)); if (interval == NULL) { // should not occur -- something is wrong LOG_WARNING("internal error in BitarrayIndexNextIterationCallback"); return NULL; } iterator->_cursor = interval->_leftEndPoint; iterator->_currentDocument = interval->_leftEndPoint; ++iterator->_currentInterval; return iterator->_currentDocument; } void* BitarrayIndexNextsIterationCallback(TRI_index_iterator_t* iterator, int64_t jumpSize) { int64_t j; void* result = NULL; void* lastValidResult = NULL; for (j = 0; j < jumpSize; ++j) { result = BitarrayIndexNextIterationCallback(iterator); if (result != NULL) { lastValidResult = result; } if (result == NULL) { break; } } return lastValidResult; } bool BitarrayIndexHasPrevIterationCallback(TRI_index_iterator_t* iterator) { if (iterator->_intervals._length == 0) { return false; } if (iterator->_currentInterval >= iterator->_intervals._length) { return false; } return true; } void* BitarrayIndexPrevIterationCallback(TRI_index_iterator_t* iterator) { TRI_index_iterator_interval_t* interval; iterator->_currentDocument = NULL; if (iterator->_cursor == NULL) { iterator->_currentInterval = iterator->_intervals._length - 1; } if (iterator->_currentInterval >= iterator->_intervals._length) { return NULL; } if (iterator->_intervals._length == 0) { return NULL; } interval = (TRI_index_iterator_interval_t*)(TRI_AtVector(&(iterator->_intervals),iterator->_currentInterval)); if (interval == NULL) { // should not occur -- something is wrong LOG_WARNING("internal error in BitarrayIndexPrevIterationCallback"); return NULL; } iterator->_cursor = interval->_leftEndPoint; iterator->_currentDocument = interval->_leftEndPoint; if (iterator->_currentInterval == 0) { iterator->_currentInterval = iterator->_intervals._length; } else { --iterator->_currentInterval; } return iterator->_currentDocument; } void* BitarrayIndexPrevsIterationCallback(TRI_index_iterator_t* iterator, int64_t jumpSize) { int64_t j; void* result = NULL; void* lastValidResult = NULL; for (j = 0; j < jumpSize; ++j) { result = BitarrayIndexPrevIterationCallback(iterator); if (result != NULL) { lastValidResult = result; } if (result == NULL) { break; } } return lastValidResult; } void BitarrayIndexResetIterator(TRI_index_iterator_t* iterator, bool beginning) { if (beginning) { iterator->_cursor = NULL; iterator->_currentInterval = 0; iterator->_currentDocument = NULL; return; } iterator->_cursor = NULL; iterator->_currentInterval = 0; iterator->_currentDocument = NULL; if (iterator->_intervals._length > 0) { iterator->_currentInterval = iterator->_intervals._length - 1; } } // ............................................................................. // forward declaration of static functions used internally here // ............................................................................. int BitarrayIndex_findHelper(BitarrayIndex* baIndex, TRI_vector_t* shapeList, TRI_index_operator_t* indexOperator, TRI_index_iterator_t* iterator) { // BitarrayIndexElement element; TRI_bitarray_mask_t mask; int result; /* element.fields = NULL; element.numFields = 0; element.collection = NULL; element.data = NULL; */ mask._mask = 0; mask._ignoreMask = 0; // ............................................................................ // Prepare the values variable with details about the fields and collection // these may be required later. These values make sense only for relational // type operators. // ............................................................................ switch (indexOperator->_type) { case TRI_EQ_INDEX_OPERATOR: case TRI_LE_INDEX_OPERATOR: case TRI_LT_INDEX_OPERATOR: case TRI_GE_INDEX_OPERATOR: case TRI_GT_INDEX_OPERATOR: { /* TRI_relation_index_operator_t* relationOperator = (TRI_relation_index_operator_t*) (indexOperator); */ /* element.fields = relationOperator->_fields; element.numFields = relationOperator->_numFields; element.collection = relationOperator->_collection; */ break; } default: { // .......................................................................... // If it is not a relational index operator we may not have access // access _fields and _collection // .......................................................................... break; } } // ............................................................................ // Process the indexOperator recursively // ............................................................................ switch (indexOperator->_type) { case TRI_AND_INDEX_OPERATOR: { assert(false); break; } case TRI_OR_INDEX_OPERATOR: { assert(false); break; } case TRI_EQ_INDEX_OPERATOR: { TRI_relation_index_operator_t* relationOperator = (TRI_relation_index_operator_t*)(indexOperator); // ............................................................................ // for bitarray indexes, the number of attribute values ALWAYS matches the // the number of parameters for a TRI_EQ_INDEX_OPERATOR. However, the client // may wish some attributes to be ignored, so some values will be '{}'. // ............................................................................ if (relationOperator->_numFields != shapeList->_length) { assert(false); } // ............................................................................ // generate the bit mask // ............................................................................ result = generateEqualBitMask(baIndex, relationOperator, &mask); //debugPrintMask(baIndex, mask._mask); //debugPrintMask(baIndex, mask._ignoreMask); result = TRI_LookupBitMaskBitarray(baIndex->_bitarray, &mask, iterator); break; } case TRI_LE_INDEX_OPERATOR: { assert(false); break; } case TRI_LT_INDEX_OPERATOR: { assert(false); break; } case TRI_GE_INDEX_OPERATOR: { assert(false); break; } case TRI_GT_INDEX_OPERATOR: { assert(false); break; } default: { assert(0); } } // end of switch statement return result; } //////////////////////////////////////////////////////////////////////////////// // Given the index structure and the list of shaped json values which came from // some document we generate a bit mask. //////////////////////////////////////////////////////////////////////////////// static bool isEqualJson(TRI_json_t* left, TRI_json_t* right) { if (left == NULL && right == NULL) { return true; } if (left == NULL || right == NULL) { return false; } if (left->_type != right->_type) { return false; } switch (left->_type) { case TRI_JSON_UNUSED: { return true; } case TRI_JSON_NULL: { return true; } case TRI_JSON_BOOLEAN: { return (left->_value._boolean == right->_value._boolean); } case TRI_JSON_NUMBER: { return (left->_value._number == right->_value._number); } case TRI_JSON_STRING: { return (strcmp(left->_value._string.data, right->_value._string.data) == 0); } case TRI_JSON_ARRAY: { int j; if (left->_value._objects._length != right->_value._objects._length) { return false; } for (j = 0; j < (left->_value._objects._length / 2); ++j) { TRI_json_t* leftName; TRI_json_t* leftValue; TRI_json_t* rightValue; leftName = (TRI_json_t*)(TRI_AtVector(&(left->_value._objects),2*j)); if (leftName == NULL) { return false; } leftValue = (TRI_json_t*)(TRI_AtVector(&(left->_value._objects),(2*j) + 1)); rightValue = TRI_LookupArrayJson(right, leftName->_value._string.data); if (isEqualJson(leftValue, rightValue)) { continue; } return false; } return true; } case TRI_JSON_LIST: { int j; if (left->_value._objects._length != right->_value._objects._length) { return false; } for (j = 0; j < left->_value._objects._length; ++j) { TRI_json_t* subLeft; TRI_json_t* subRight; subLeft = (TRI_json_t*)(TRI_AtVector(&(left->_value._objects),j)); subRight = (TRI_json_t*)(TRI_AtVector(&(right->_value._objects),j)); if (isEqualJson(subLeft, subRight)) { continue; } return false; } return true; } default: { assert(false); } } } int generateBitMask(BitarrayIndex* baIndex, const BitarrayIndexElement* element, TRI_bitarray_mask_t* mask) { TRI_shaper_t* shaper; int j; int shiftLeft; // ........................................................................... // some safety checks first // ........................................................................... if (baIndex == NULL || element == NULL) { return TRI_ERROR_INTERNAL; } if (element->collection == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // We could be trying to store an 'undefined' document into the bitarray // We determine this implicitly. If element->numFields b == 0, then we // assume that the document did not have any matching attributes, yet since // we are here we wish to store this fact. // ........................................................................... if (!baIndex->_supportUndef && (element->numFields == 0 || element->fields == NULL)) { return TRI_ERROR_INTERNAL; } if (baIndex->_supportUndef && element->numFields == 0) { mask->_mask = 1; mask->_ignoreMask = 0; return TRI_ERROR_NO_ERROR; } // ........................................................................... // attempt to convert the stored TRI_shaped_json_t into TRI_Json_t so that // we can make a comparison between what values the bitarray index requires // and what values the document has sent. // ........................................................................... shaper = ((TRI_doc_collection_t*)(element->collection))->_shaper; mask->_mask = 0; shiftLeft = 0; for (j = 0; j < baIndex->_values._length; ++j) { TRI_json_t* valueList; TRI_json_t* value; uint64_t other; int i; uint64_t tempMask; value = TRI_JsonShapedJson(shaper, &(element->fields[j])); // from shaped json to simple json valueList = (TRI_json_t*)(TRI_AtVector(&(baIndex->_values),j)); other = 0; tempMask = 0; for (i = 0; i < valueList->_value._objects._length; ++i) { TRI_json_t* listEntry = (TRI_json_t*)(TRI_AtVector(&(valueList->_value._objects), i)); // ....................................................................... // We need to take special care if the json object is a list // ....................................................................... if (listEntry->_type == TRI_JSON_LIST) { // an empty list if (listEntry->_value._objects._length == 0) { other = (1 << i); } else { int k; for (k = 0; k < listEntry->_value._objects._length; k++) { TRI_json_t* subListEntry; subListEntry = (TRI_json_t*)(TRI_AtVector(&(listEntry->_value._objects), i)); if (isEqualJson(value, subListEntry)) { tempMask = tempMask | (1 << i); } } } } else { if (isEqualJson(value, listEntry)) { tempMask = tempMask | (1 << i); } } } // ............................................................................ // remove the json entry created from the shaped json // ............................................................................ TRI_FreeJson(shaper->_memoryZone, value); // ............................................................................ // When we create a bitarray index, for example: ensureBitarray("x",[0,[],1,2,3]) // and we insert doc with {"x" : "hello world"}, then, since the value of "x" // does not match 0,1,2 or 3 and [] appears as a valid list item, the doc is // inserted with a mask of 01000 // This is what other means below // ............................................................................ if (tempMask == 0) { tempMask = other; } mask->_mask = mask->_mask | (tempMask << shiftLeft); shiftLeft += valueList->_value._objects._length; } return TRI_ERROR_NO_ERROR; } int generateEqualBitMask(BitarrayIndex* baIndex, const TRI_relation_index_operator_t* relationOperator, TRI_bitarray_mask_t* mask) { int j; int shiftLeft; int ignoreShiftLeft; // ........................................................................... // some safety checks first // ........................................................................... if (baIndex == NULL || relationOperator == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // supportUndef -- refers to where the document does not have 1 or more // attributes which are defined in the index. (Not related to whether or // not the attribute has a value defined in the set of supported values.) // ........................................................................... // ........................................................................... // if the number of attributes is 0, then something must be wrong // ........................................................................... if (relationOperator->_numFields == 0) { return TRI_ERROR_INTERNAL; } // ........................................................................... // if an attribute which was defined in the index, was not sent by the client, // then that bitarray column is ignored. // ........................................................................... mask->_mask = 0; mask->_ignoreMask = 0; shiftLeft = 0; ignoreShiftLeft = 0; for (j = 0; j < baIndex->_values._length; ++j) { // loop over the number of attributes defined in the index TRI_json_t* valueList; TRI_json_t* value; uint64_t other; int i; uint64_t tempMask; value = (TRI_json_t*)(TRI_AtVector(&(relationOperator->_parameters->_value._objects), j)); valueList = (TRI_json_t*)(TRI_AtVector(&(baIndex->_values),j)); ignoreShiftLeft += valueList->_value._objects._length; // ......................................................................... // client did not send us this attribute (hence undefined value), therefore // this particular column we ignore // ......................................................................... if (value->_type == TRI_JSON_UNUSED) { tempMask = ~((~(uint64_t)(0)) << ignoreShiftLeft); other = (~(uint64_t)(0)) << (ignoreShiftLeft - valueList->_value._objects._length); mask->_ignoreMask = mask->_ignoreMask | (tempMask & other); other = 0; tempMask = 0; } else { other = 0; tempMask = 0; for (i = 0; i < valueList->_value._objects._length; ++i) { TRI_json_t* listEntry = (TRI_json_t*)(TRI_AtVector(&(valueList->_value._objects), i)); int k; // ....................................................................... // if the ith possible set of values is not a list, do comparison // ....................................................................... if (listEntry->_type != TRI_JSON_LIST) { if (isEqualJson(value, listEntry)) { tempMask = tempMask | (1 << i); } continue; } // ....................................................................... // ith entry in the set of possible values is a list // ....................................................................... // ....................................................................... // Special case of an empty list -- this means all other values // ....................................................................... if (listEntry->_value._objects._length == 0) { // special case other = (1 << i); continue; } for (k = 0; k < listEntry->_value._objects._length; k++) { TRI_json_t* subListEntry; subListEntry = (TRI_json_t*)(TRI_AtVector(&(listEntry->_value._objects), i)); if (isEqualJson(value, subListEntry)) { tempMask = tempMask | (1 << i); break; } } } } // ............................................................................ // When we create a bitarray index, for example: ensureBitarray("x",[0,[],1,2,3]) // and we insert doc with {"x" : "hello world"}, then, since the value of "x" // does not match 0,1,2 or 3 and [] appears as a valid list item, the doc is // inserted with a mask of 01000 // This is what other means below // ............................................................................ if (tempMask == 0) { tempMask = other; } mask->_mask = mask->_mask | (tempMask << shiftLeft); shiftLeft += valueList->_value._objects._length; } // ................................................................................ // check whether we actually ignore everything! // ................................................................................ if (mask->_mask == 0 && !baIndex->_supportUndef) { return TRI_ERROR_INTERNAL; } return TRI_ERROR_NO_ERROR; } /* void debugPrintMask(BitarrayIndex* baIndex, uint64_t mask) { int j; printf("------------------- mask --------------------------\n"); for (j = 0; j < baIndex->_bitarray->_numColumns; ++j) { if ((mask & ((uint64_t)(1) << j)) == 0) { printf("0"); } else { printf("1"); } } printf("\n\n"); } */ //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: