/* COLLECTION LIBRARY Implementation of the collection library iterator functions. Copyright (C) Dmitri Pal 2009 Collection Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Collection Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Collection Library. If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include "config.h" #include "trace.h" /* The collection should use the real structures */ #include "collection_priv.h" #include "collection.h" /* Grow iteration stack */ static int col_grow_stack(struct collection_iterator *iterator, unsigned desired) { int grow_by = 0; struct collection_item **temp; TRACE_FLOW_STRING("col_grow_stack", "Entry."); if (desired > iterator->stack_size) { grow_by = (((desired - iterator->stack_size) / STACK_DEPTH_BLOCK) + 1) * STACK_DEPTH_BLOCK; temp = (struct collection_item **)realloc(iterator->stack, grow_by * sizeof(struct collection_item *)); if (temp == NULL) { TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM); return ENOMEM; } iterator->stack = temp; iterator->stack_size += grow_by; } TRACE_FLOW_STRING("col_grow_stack", "Exit."); return EOK; } /* Bind iterator to a collection */ int col_bind_iterator(struct collection_iterator **iterator, struct collection_item *ci, int mode_flags) { int error; struct collection_header *header; struct collection_iterator *iter = NULL; TRACE_FLOW_STRING("col_bind_iterator", "Entry."); /* Do some argument checking first */ if ((iterator == NULL) || (ci == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } iter = (struct collection_iterator *)malloc(sizeof(struct collection_iterator)); if (iter == NULL) { TRACE_ERROR_NUMBER("Error allocating memory for the iterator.", ENOMEM); return ENOMEM; } /* Allocate memory for the stack */ iter->stack = NULL; iter->stack_size = 0; iter->stack_depth = 0; iter->item_level = 0; iter->flags = mode_flags; iter->pin_level = 0; iter->can_break = 0; TRACE_INFO_NUMBER("Iterator flags", iter->flags); /* Allocate memory for stack */ error = col_grow_stack(iter, 1); if(error) { free(iter); TRACE_ERROR_NUMBER("Error growing stack.", error); return error; } /* Create a special end item */ error = col_allocate_item(&(iter->end_item), "", NULL, 0, COL_TYPE_END); if(error) { free(iter); TRACE_ERROR_NUMBER("Error allocating end item.", error); return error; } /* Make sure that we tie iterator to the collection */ header = (struct collection_header *)ci->data; header->reference_count++; iter->top = ci; iter->pin = ci; *(iter->stack) = ci; iter->stack_depth++; *iterator = iter; TRACE_FLOW_STRING("col_bind_iterator", "Exit"); return EOK; } /* Stop processing this subcollection and move to the next item in the * collection 'level' levels up.*/ int col_iterate_up(struct collection_iterator *iterator, unsigned level) { TRACE_FLOW_STRING("iterate_up", "Entry"); if (iterator == NULL) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } TRACE_INFO_NUMBER("Going up:", level); TRACE_INFO_NUMBER("Current stack depth:", iterator->stack_depth); /* If level is big just move to the top, * that will end the iteration process. */ if (level >= iterator->stack_depth) iterator->stack_depth = 0; else iterator->stack_depth -= level; TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); TRACE_FLOW_STRING("col_iterate_up", "Exit"); return EOK; } /* How deep are we relative to the top level.*/ int col_get_iterator_depth(struct collection_iterator *iterator, int *depth) { TRACE_FLOW_STRING("col_get_iterator_depth", "Entry"); if ((iterator == NULL) || (depth == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } *depth = iterator->stack_depth - 1; TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); TRACE_FLOW_STRING("col_get_iterator_depth","Exit"); return EOK; } /* What was the level of the last item we got? */ int col_get_item_depth(struct collection_iterator *iterator, int *depth) { TRACE_FLOW_STRING("col_get_item_depth", "Entry"); if ((iterator == NULL) || (depth == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } *depth = iterator->item_level; TRACE_INFO_NUMBER("Item level at the end:", iterator->item_level); TRACE_FLOW_STRING("col_get_item_depth","Exit"); return EOK; } /* Unbind the iterator from the collection */ void col_unbind_iterator(struct collection_iterator *iterator) { TRACE_FLOW_STRING("col_unbind_iterator", "Entry."); if (iterator != NULL) { col_destroy_collection(iterator->top); col_delete_item(iterator->end_item); free(iterator->stack); free(iterator); } TRACE_FLOW_STRING("col_unbind_iterator", "Exit"); } /* Get items from the collection one by one following the tree */ int col_iterate_collection(struct collection_iterator *iterator, struct collection_item **item) { int error; struct collection_item *current; struct collection_item *other; TRACE_FLOW_STRING("col_iterate_collection", "Entry."); /* Check if we have storage for item */ if (item == NULL) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } while (1) { TRACE_INFO_NUMBER("Stack depth:", iterator->stack_depth); if (iterator->stack_depth == 0) { /* Re-init so if we continue looping we start over */ iterator->stack[0] = iterator->top; iterator->stack_depth++; iterator->item_level = 0; } /* Is current item available */ current = iterator->stack[iterator->stack_depth - 1]; iterator->item_level = iterator->stack_depth - 1; /* Are we done? */ if (((iterator->stack_depth - 1) == iterator->pin_level) && (iterator->pin == current)) { if (iterator->can_break) { TRACE_FLOW_STRING("We are done.", ""); *item = NULL; iterator->can_break = 0; return EOK; } else iterator->can_break = 1; } /* We are not done so check if we have an item */ if (current != NULL) { TRACE_INFO_STRING("Current item:", current->property); TRACE_INFO_NUMBER("Current item type:", current->type); /* Is this a collection reference */ if (current->type == COL_TYPE_COLLECTIONREF) { /* We do follow references? */ TRACE_INFO_STRING("Current item:", "collection reference"); if ((iterator->flags & COL_TRAVERSE_IGNORE) == 0) { /* We should not ignore - then move on */ TRACE_INFO_STRING("Collection references are not ignored", ""); error = col_grow_stack(iterator, iterator->stack_depth + 1); if (error) { TRACE_ERROR_NUMBER("Error growing stack.", error); return error; } /* Do we need to go deeper than one level ? */ if ((iterator->flags & COL_TRAVERSE_ONELEVEL) == 0) { TRACE_INFO_STRING("Need to go deeper", ""); /* We need to go deeper... */ /* Do we need to show headers but not reference? */ if ((iterator->flags & COL_TRAVERSE_ONLYSUB) != 0) { TRACE_INFO_STRING("Instructed to show header not reference", ""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->item_level = iterator->stack_depth; *item = other; } /* Do we need to show both? */ else if ((iterator->flags & COL_TRAVERSE_SHOWSUB) != 0) { TRACE_INFO_STRING("Instructed to show header and reference",""); iterator->stack[iterator->stack_depth] = *((struct collection_item **)(current->data)); *item = current; /* Do not need to adjust level here */ } /* Do not show either */ else if ((iterator->flags & COL_TRAVERSE_FLAT) != 0) { TRACE_INFO_STRING("Instructed not to show header and reference",""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; /* Do not need to adjust level here */ continue; } /* We need to show reference only */ else { TRACE_INFO_STRING("Instructed to show reference only", ""); other = *((struct collection_item **)current->data); TRACE_INFO_STRING("Sub collection:", other->property); TRACE_INFO_NUMBER("Sub collection type:", other->type); iterator->stack[iterator->stack_depth] = other->next; if (other->next != NULL) { TRACE_INFO_STRING("Will show this item next time:", other->next->property); TRACE_INFO_NUMBER("Will show this item next time type:", other->next->type); } *item = current; TRACE_INFO_NUMBER("Level of the reference:", iterator->item_level); /* Do not need to adjust level here */ } TRACE_INFO_STRING("We return item:", (*item)->property); TRACE_INFO_NUMBER("We return item type:", (*item)->type); TRACE_INFO_STRING("Moving to the next item on the previous item in stack", ""); iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; } else { TRACE_INFO_STRING("Instructed to parse just one level", ""); /* On one level - just return current */ *item = current; TRACE_INFO_STRING("Moving to the next item on one level", ""); iterator->stack[iterator->stack_depth - 1] = current->next; } break; } else { /* We need to ignore references so move to the next item */ TRACE_INFO_STRING("Stepping over the reference", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } } else { /* Got a normal item - return it and move to the next one */ if ((current->type == COL_TYPE_COLLECTION) && ((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth > 1)) { TRACE_INFO_STRING("Header of the sub collection in flat case ", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } else { TRACE_INFO_STRING("Simple item", ""); *item = current; iterator->stack[iterator->stack_depth - 1] = current->next; } break; } } else { /* Item is NULL */ TRACE_INFO_STRING("Finished level", "moving to upper level"); iterator->stack_depth--; /* Remember that item_level is zero based while depth is size * so we decrease and then assign. */ TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); if ((iterator->flags & COL_TRAVERSE_END) != 0) { /* Show end element * a) If we are flattening but at the top * b) We are not flattening */ if ((((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth == 0)) || ((iterator->flags & COL_TRAVERSE_FLAT) == 0)) { /* Return dummy entry to indicate the end of the collection */ TRACE_INFO_STRING("Finished level", "told to return END"); *item = iterator->end_item; break; } } else { /* Move to next level */ continue; } } } TRACE_FLOW_STRING("col_iterate_collection", "Exit"); return EOK; } /* Pins down the iterator to loop around this point */ void col_pin_iterator(struct collection_iterator *iterator) { TRACE_FLOW_STRING("col_iterator_add_pin", "Entry"); if ((!iterator) || (!iterator->stack)) { TRACE_FLOW_STRING("Invalid itertor", "Ingoring"); return; } while ((iterator->stack_depth) && (iterator->stack[iterator->stack_depth - 1] == NULL)) { iterator->stack_depth--; } if (iterator->stack_depth == 0) { iterator->pin = iterator->top; iterator->pin_level = 0; } else { iterator->pin = iterator->stack[iterator->stack_depth - 1]; iterator->pin_level = iterator->stack_depth - 1; } iterator->can_break = 0; TRACE_FLOW_STRING("col_iterator_add_pin", "Exit"); } /* Rewinds iterator to the beginning */ void col_rewind_iterator(struct collection_iterator *iterator) { TRACE_FLOW_STRING("col_rewind_iterator", "Entry"); if ((!iterator) || (!iterator->stack)) { TRACE_FLOW_STRING("Invalid itertor", "Ingoring"); return; } iterator->pin = iterator->top; iterator->stack[0] = iterator->top; iterator->stack_depth = 1; iterator->item_level = 0; iterator->pin_level = 0; iterator->can_break = 0; TRACE_FLOW_STRING("col_rewind_iterator", "Exit"); }