| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * pkix_list.c |
| * |
| * List Object Functions |
| * |
| */ |
| |
| #include "pkix_list.h" |
| |
| /* --Private-Functions-------------------------------------------- */ |
| |
| /* |
| * FUNCTION: pkix_List_Create_Internal |
| * DESCRIPTION: |
| * |
| * Creates a new List, using the Boolean value of "isHeader" to determine |
| * whether the new List should be a header, and stores it at "pList". The |
| * List is initially empty and holds no items. To initially add items to |
| * the List, use PKIX_List_AppendItem. |
| * |
| * PARAMETERS: |
| * "isHeader" |
| * Boolean value indicating whether new List should be a header. |
| * "pList" |
| * Address where object pointer will be stored. Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a Fatal Error if the function fails in an unrecoverable way. |
| */ |
| static PKIX_Error * |
| pkix_List_Create_Internal( |
| PKIX_Boolean isHeader, |
| PKIX_List **pList, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| |
| PKIX_ENTER(LIST, "pkix_List_Create_Internal"); |
| PKIX_NULLCHECK_ONE(pList); |
| |
| PKIX_CHECK(PKIX_PL_Object_Alloc |
| (PKIX_LIST_TYPE, |
| ((PKIX_UInt32)(sizeof (PKIX_List))), |
| (PKIX_PL_Object **)&list, plContext), |
| PKIX_ERRORCREATINGLISTITEM); |
| |
| list->item = NULL; |
| list->next = NULL; |
| list->immutable = PKIX_FALSE; |
| list->length = 0; |
| list->isHeader = isHeader; |
| |
| *pList = list; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Destroy |
| * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_List_Destroy( |
| PKIX_PL_Object *object, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| PKIX_List *nextItem = NULL; |
| |
| PKIX_ENTER(LIST, "pkix_List_Destroy"); |
| PKIX_NULLCHECK_ONE(object); |
| |
| /* Check that this object is a list */ |
| PKIX_CHECK(pkix_CheckType(object, PKIX_LIST_TYPE, plContext), |
| PKIX_OBJECTNOTLIST); |
| |
| list = (PKIX_List *)object; |
| |
| /* We have a valid list. DecRef its item and recurse on next */ |
| PKIX_DECREF(list->item); |
| while ((nextItem = list->next) != NULL) { |
| list->next = nextItem->next; |
| nextItem->next = NULL; |
| PKIX_DECREF(nextItem); |
| } |
| list->immutable = PKIX_FALSE; |
| list->length = 0; |
| list->isHeader = PKIX_FALSE; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_ToString_Helper |
| * DESCRIPTION: |
| * |
| * Helper function that creates a string representation of the List pointed |
| * to by "list" and stores its address in the object pointed to by "pString". |
| * |
| * PARAMETERS |
| * "list" |
| * Address of List whose string representation is desired. |
| * Must be non-NULL. |
| * "pString" |
| * Address of object pointer's destination. Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Conditionally Thread Safe |
| * (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a List Error if the function fails in a non-fatal way. |
| * Returns a Fatal Error if the function fails in an unrecoverable way. |
| */ |
| static PKIX_Error * |
| pkix_List_ToString_Helper( |
| PKIX_List *list, |
| PKIX_PL_String **pString, |
| void *plContext) |
| { |
| PKIX_PL_String *itemString = NULL; |
| PKIX_PL_String *nextString = NULL; |
| PKIX_PL_String *format = NULL; |
| PKIX_Boolean empty; |
| |
| PKIX_ENTER(LIST, "pkix_List_ToString_Helper"); |
| PKIX_NULLCHECK_TWO(list, pString); |
| |
| /* special case when list is the header */ |
| if (list->isHeader){ |
| |
| PKIX_CHECK(PKIX_List_IsEmpty(list, &empty, plContext), |
| PKIX_LISTISEMPTYFAILED); |
| |
| if (empty){ |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, |
| "EMPTY", |
| 0, |
| &itemString, |
| plContext), |
| PKIX_ERRORCREATINGITEMSTRING); |
| (*pString) = itemString; |
| PKIX_DEBUG_EXIT(LIST); |
| return (NULL); |
| } else { |
| PKIX_CHECK(pkix_List_ToString_Helper |
| (list->next, &itemString, plContext), |
| PKIX_LISTTOSTRINGHELPERFAILED); |
| } |
| |
| /* Create a string object from the format */ |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, "%s", 0, &format, plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_PL_Sprintf |
| (pString, plContext, format, itemString), |
| PKIX_SPRINTFFAILED); |
| } else { |
| /* Get a string for this list's item */ |
| if (list->item == NULL) { |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, |
| "(null)", |
| 0, |
| &itemString, |
| plContext), |
| PKIX_STRINGCREATEFAILED); |
| } else { |
| PKIX_CHECK(PKIX_PL_Object_ToString |
| ((PKIX_PL_Object*)list->item, |
| &itemString, |
| plContext), |
| PKIX_OBJECTTOSTRINGFAILED); |
| } |
| if (list->next == NULL) { |
| /* Just return the itemstring */ |
| (*pString) = itemString; |
| PKIX_DEBUG_EXIT(LIST); |
| return (NULL); |
| } |
| |
| /* Recursive call to get string for this list's next pointer */ |
| PKIX_CHECK(pkix_List_ToString_Helper |
| (list->next, &nextString, plContext), |
| PKIX_LISTTOSTRINGHELPERFAILED); |
| |
| /* Create a string object from the format */ |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, |
| "%s, %s", |
| 0, |
| &format, |
| plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_PL_Sprintf |
| (pString, |
| plContext, |
| format, |
| itemString, |
| nextString), |
| PKIX_SPRINTFFAILED); |
| } |
| |
| cleanup: |
| |
| PKIX_DECREF(itemString); |
| PKIX_DECREF(nextString); |
| PKIX_DECREF(format); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_ToString |
| * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_List_ToString( |
| PKIX_PL_Object *object, |
| PKIX_PL_String **pString, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| PKIX_PL_String *listString = NULL; |
| PKIX_PL_String *format = NULL; |
| |
| PKIX_ENTER(LIST, "pkix_List_ToString"); |
| PKIX_NULLCHECK_TWO(object, pString); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_LIST_TYPE, plContext), |
| PKIX_OBJECTNOTLIST); |
| |
| list = (PKIX_List *)object; |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| PKIX_CHECK(pkix_List_ToString_Helper(list, &listString, plContext), |
| PKIX_LISTTOSTRINGHELPERFAILED); |
| |
| PKIX_CHECK(PKIX_PL_String_Create |
| (PKIX_ESCASCII, "(%s)", 0, &format, plContext), |
| PKIX_STRINGCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_PL_Sprintf(pString, plContext, format, listString), |
| PKIX_SPRINTFFAILED); |
| |
| cleanup: |
| |
| PKIX_DECREF(listString); |
| PKIX_DECREF(format); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Equals |
| * (see comments for PKIX_PL_EqualsCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_List_Equals( |
| PKIX_PL_Object *first, |
| PKIX_PL_Object *second, |
| PKIX_Boolean *pResult, |
| void *plContext) |
| { |
| PKIX_UInt32 secondType; |
| PKIX_Boolean cmpResult; |
| PKIX_List *firstList = NULL; |
| PKIX_List *secondList = NULL; |
| PKIX_UInt32 firstLength = 0; |
| PKIX_UInt32 secondLength = 0; |
| PKIX_PL_Object *firstItem = NULL; |
| PKIX_PL_Object *secondItem = NULL; |
| PKIX_UInt32 i = 0; |
| |
| PKIX_ENTER(LIST, "pkix_List_Equals"); |
| PKIX_NULLCHECK_THREE(first, second, pResult); |
| |
| /* test that first is a List */ |
| PKIX_CHECK(pkix_CheckType(first, PKIX_LIST_TYPE, plContext), |
| PKIX_FIRSTOBJECTNOTLIST); |
| |
| /* |
| * Since we know first is a List, if both references are |
| * identical, they must be equal |
| */ |
| if (first == second){ |
| *pResult = PKIX_TRUE; |
| goto cleanup; |
| } |
| |
| /* |
| * If second isn't a List, we don't throw an error. |
| * We simply return a Boolean result of FALSE |
| */ |
| *pResult = PKIX_FALSE; |
| PKIX_CHECK(PKIX_PL_Object_GetType(second, &secondType, plContext), |
| PKIX_COULDNOTGETTYPEOFSECONDARGUMENT); |
| if (secondType != PKIX_LIST_TYPE) goto cleanup; |
| |
| firstList = (PKIX_List *)first; |
| secondList = (PKIX_List *)second; |
| |
| if ((!firstList->isHeader) && (!secondList->isHeader)){ |
| PKIX_ERROR(PKIX_INPUTLISTSMUSTBELISTHEADERS); |
| } |
| |
| firstLength = firstList->length; |
| secondLength = secondList->length; |
| |
| cmpResult = PKIX_FALSE; |
| if (firstLength == secondLength){ |
| for (i = 0, cmpResult = PKIX_TRUE; |
| ((i < firstLength) && cmpResult); |
| i++){ |
| PKIX_CHECK(PKIX_List_GetItem |
| (firstList, i, &firstItem, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (secondList, i, &secondItem, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if ((!firstItem && secondItem) || |
| (firstItem && !secondItem)){ |
| cmpResult = PKIX_FALSE; |
| } else if (!firstItem && !secondItem){ |
| continue; |
| } else { |
| PKIX_CHECK(PKIX_PL_Object_Equals |
| (firstItem, |
| secondItem, |
| &cmpResult, |
| plContext), |
| PKIX_OBJECTEQUALSFAILED); |
| |
| PKIX_DECREF(firstItem); |
| PKIX_DECREF(secondItem); |
| } |
| } |
| } |
| |
| *pResult = cmpResult; |
| |
| cleanup: |
| |
| PKIX_DECREF(firstItem); |
| PKIX_DECREF(secondItem); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Hashcode |
| * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_List_Hashcode( |
| PKIX_PL_Object *object, |
| PKIX_UInt32 *pHashcode, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| PKIX_PL_Object *element = NULL; |
| PKIX_UInt32 hash = 0; |
| PKIX_UInt32 tempHash = 0; |
| PKIX_UInt32 length, i; |
| |
| PKIX_ENTER(LIST, "pkix_List_Hashcode"); |
| PKIX_NULLCHECK_TWO(object, pHashcode); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_LIST_TYPE, plContext), |
| PKIX_OBJECTNOTLIST); |
| |
| list = (PKIX_List *)object; |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| length = list->length; |
| |
| for (i = 0; i < length; i++){ |
| PKIX_CHECK(PKIX_List_GetItem(list, i, &element, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if (!element){ |
| tempHash = 100; |
| } else { |
| PKIX_CHECK(PKIX_PL_Object_Hashcode |
| (element, &tempHash, plContext), |
| PKIX_LISTHASHCODEFAILED); |
| } |
| |
| hash = 31 * hash + tempHash; |
| |
| PKIX_DECREF(element); |
| } |
| |
| *pHashcode = hash; |
| |
| cleanup: |
| |
| PKIX_DECREF(element); |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Duplicate |
| * (see comments for PKIX_PL_DuplicateCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_List_Duplicate( |
| PKIX_PL_Object *object, |
| PKIX_PL_Object **pNewObject, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| PKIX_List *listDuplicate = NULL; |
| |
| PKIX_ENTER(LIST, "pkix_List_Duplicate"); |
| PKIX_NULLCHECK_TWO(object, pNewObject); |
| |
| PKIX_CHECK(pkix_CheckType(object, PKIX_LIST_TYPE, plContext), |
| PKIX_OBJECTNOTLIST); |
| |
| list = (PKIX_List *)object; |
| |
| if (list->immutable){ |
| PKIX_CHECK(pkix_duplicateImmutable |
| (object, pNewObject, plContext), |
| PKIX_DUPLICATEIMMUTABLEFAILED); |
| } else { |
| |
| PKIX_CHECK(pkix_List_Create_Internal |
| (list->isHeader, &listDuplicate, plContext), |
| PKIX_LISTCREATEINTERNALFAILED); |
| |
| listDuplicate->length = list->length; |
| |
| PKIX_INCREF(list->item); |
| listDuplicate->item = list->item; |
| |
| if (list->next == NULL){ |
| listDuplicate->next = NULL; |
| } else { |
| /* Recursively Duplicate list */ |
| PKIX_CHECK(pkix_List_Duplicate |
| ((PKIX_PL_Object *)list->next, |
| (PKIX_PL_Object **)&listDuplicate->next, |
| plContext), |
| PKIX_LISTDUPLICATEFAILED); |
| } |
| |
| *pNewObject = (PKIX_PL_Object *)listDuplicate; |
| } |
| |
| cleanup: |
| |
| if (PKIX_ERROR_RECEIVED){ |
| PKIX_DECREF(listDuplicate); |
| } |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| |
| /* |
| * FUNCTION: pkix_List_GetElement |
| * DESCRIPTION: |
| * |
| * Copies the "list"'s element at "index" into "element". The input List must |
| * be the header of the List (as opposed to being an element of the List). The |
| * index counts from zero and must be less than the List's length. This |
| * function does NOT increment the reference count of the List element since |
| * the returned element's reference will not be stored by the calling |
| * function. |
| * |
| * PARAMETERS: |
| * "list" |
| * Address of List (must be header) to get element from. Must be non-NULL. |
| * "index" |
| * Index of list to get element from. Must be less than List's length. |
| * "pElement" |
| * Address where object pointer will be stored. Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Conditionally Thread Safe |
| * (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a Fatal Error if the function fails in an unrecoverable way. |
| */ |
| static PKIX_Error * |
| pkix_List_GetElement( |
| PKIX_List *list, |
| PKIX_UInt32 index, |
| PKIX_List **pElement, |
| void *plContext) |
| { |
| PKIX_List *iterator = NULL; |
| PKIX_UInt32 length; |
| PKIX_UInt32 position = 0; |
| |
| PKIX_ENTER(LIST, "pkix_List_GetElement"); |
| PKIX_NULLCHECK_TWO(list, pElement); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| length = list->length; |
| |
| if (index >= length) { |
| PKIX_ERROR(PKIX_INDEXOUTOFBOUNDS); |
| } |
| |
| for (iterator = list; position++ <= index; iterator = iterator->next) |
| ; |
| |
| (*pElement) = iterator; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| |
| /* |
| * FUNCTION: pkix_List_RegisterSelf |
| * DESCRIPTION: |
| * Registers PKIX_LIST_TYPE and its related functions with systemClasses[] |
| * THREAD SAFETY: |
| * Not Thread Safe - for performance and complexity reasons |
| * |
| * Since this function is only called by PKIX_PL_Initialize, which should |
| * only be called once, it is acceptable that this function is not |
| * thread-safe. |
| */ |
| PKIX_Error * |
| pkix_List_RegisterSelf(void *plContext) |
| { |
| extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; |
| pkix_ClassTable_Entry entry; |
| |
| PKIX_ENTER(LIST, "pkix_List_RegisterSelf"); |
| |
| entry.description = "List"; |
| entry.objCounter = 0; |
| entry.typeObjectSize = sizeof(PKIX_List); |
| entry.destructor = pkix_List_Destroy; |
| entry.equalsFunction = pkix_List_Equals; |
| entry.hashcodeFunction = pkix_List_Hashcode; |
| entry.toStringFunction = pkix_List_ToString; |
| entry.comparator = NULL; |
| entry.duplicateFunction = pkix_List_Duplicate; |
| |
| systemClasses[PKIX_LIST_TYPE] = entry; |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Contains |
| * DESCRIPTION: |
| * |
| * Checks a List pointed to by "list", to determine whether it includes |
| * an entry that is equal to the Object pointed to by "object", and stores |
| * the result in "pFound". |
| * |
| * PARAMETERS: |
| * "list" |
| * List to be searched; may be empty; must be non-NULL |
| * "object" |
| * Object to be checked for; must be non-NULL |
| * "pFound" |
| * Address where the result of the search will be stored. Must |
| * be non-NULL |
| * "plContext" |
| * platform-specific context pointer |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_Contains( |
| PKIX_List *list, |
| PKIX_PL_Object *object, |
| PKIX_Boolean *pFound, |
| void *plContext) |
| { |
| PKIX_PL_Object *current = NULL; |
| PKIX_UInt32 numEntries = 0; |
| PKIX_UInt32 index = 0; |
| PKIX_Boolean match = PKIX_FALSE; |
| |
| PKIX_ENTER(LIST, "pkix_List_Contains"); |
| PKIX_NULLCHECK_THREE(list, object, pFound); |
| |
| PKIX_CHECK(PKIX_List_GetLength(list, &numEntries, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| for (index = 0; index < numEntries; index++) { |
| PKIX_CHECK(PKIX_List_GetItem |
| (list, index, ¤t, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if (current) { |
| PKIX_CHECK(PKIX_PL_Object_Equals |
| (object, current, &match, plContext), |
| PKIX_OBJECTEQUALSFAILED); |
| |
| PKIX_DECREF(current); |
| } |
| |
| if (match) { |
| break; |
| } |
| } |
| |
| *pFound = match; |
| |
| cleanup: |
| |
| PKIX_DECREF(current); |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_Remove |
| * DESCRIPTION: |
| * |
| * Traverses the List pointed to by "list", to find and delete an entry |
| * that is equal to the Object pointed to by "object". If no such entry |
| * is found the function does not return an error. |
| * |
| * PARAMETERS: |
| * "list" |
| * List to be searched; may be empty; must be non-NULL |
| * "object" |
| * Object to be checked for and deleted, if found; must be non-NULL |
| * "plContext" |
| * platform-specific context pointer |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Validate Error if the functions fails in a non-fatal way |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_Remove( |
| PKIX_List *list, |
| PKIX_PL_Object *object, |
| void *plContext) |
| { |
| PKIX_PL_Object *current = NULL; |
| PKIX_UInt32 numEntries = 0; |
| PKIX_UInt32 index = 0; |
| PKIX_Boolean match = PKIX_FALSE; |
| |
| PKIX_ENTER(LIST, "pkix_List_Remove"); |
| PKIX_NULLCHECK_TWO(list, object); |
| |
| PKIX_CHECK(PKIX_List_GetLength(list, &numEntries, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| for (index = 0; index < numEntries; index++) { |
| PKIX_CHECK(PKIX_List_GetItem |
| (list, index, ¤t, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if (current) { |
| PKIX_CHECK(PKIX_PL_Object_Equals |
| (object, current, &match, plContext), |
| PKIX_OBJECTEQUALSFAILED); |
| |
| PKIX_DECREF(current); |
| } |
| |
| if (match) { |
| PKIX_CHECK(PKIX_List_DeleteItem |
| (list, index, plContext), |
| PKIX_LISTDELETEITEMFAILED); |
| break; |
| } |
| } |
| |
| cleanup: |
| |
| PKIX_DECREF(current); |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_RemoveItems |
| * DESCRIPTION: |
| * |
| * Traverses the List pointed to by "list", to find and delete an entry |
| * that is equal to the Object in the "deleteList". If no such entry |
| * is found the function does not return an error. |
| * |
| * PARAMETERS: |
| * "list" |
| * Object in "list" is checked for object in "deleteList" and deleted if |
| * found; may be empty; must be non-NULL |
| * "deleteList" |
| * List of objects to be searched ; may be empty; must be non-NULL |
| * "plContext" |
| * platform-specific context pointer |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Validate Error if the functions fails in a non-fatal way |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_RemoveItems( |
| PKIX_List *list, |
| PKIX_List *deleteList, |
| void *plContext) |
| { |
| PKIX_PL_Object *current = NULL; |
| PKIX_UInt32 numEntries = 0; |
| PKIX_UInt32 index = 0; |
| |
| PKIX_ENTER(LIST, "pkix_List_RemoveItems"); |
| PKIX_NULLCHECK_TWO(list, deleteList); |
| |
| PKIX_CHECK(PKIX_List_GetLength(deleteList, &numEntries, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| for (index = 0; index < numEntries; index++) { |
| PKIX_CHECK(PKIX_List_GetItem |
| (deleteList, index, ¤t, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if (current) { |
| PKIX_CHECK(pkix_List_Remove |
| (list, current, plContext), |
| PKIX_OBJECTEQUALSFAILED); |
| |
| PKIX_DECREF(current); |
| } |
| } |
| |
| cleanup: |
| |
| PKIX_DECREF(current); |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_MergeLists |
| * DESCRIPTION: |
| * |
| * Creates a new list consisting of the items from "firstList", followed by |
| * the items on "secondList", returns the new list at "pMergedList". If |
| * both input lists are NULL or empty, the result is an empty list. If an error |
| * occurs, the result is NULL. |
| * |
| * PARAMETERS: |
| * "firstList" |
| * Address of list to be merged from. May be NULL or empty. |
| * "secondList" |
| * Address of list to be merged from. May be NULL or empty. |
| * "pMergedList" |
| * Address where returned object is stored. |
| * "plContext" |
| * platform-specific context pointer * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a List Error if the functions fails in a non-fatal way |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_MergeLists( |
| PKIX_List *firstList, |
| PKIX_List *secondList, |
| PKIX_List **pMergedList, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| PKIX_PL_Object *item = NULL; |
| PKIX_UInt32 numItems = 0; |
| PKIX_UInt32 i; |
| |
| PKIX_ENTER(LIST, "pkix_List_MergeLists"); |
| PKIX_NULLCHECK_ONE(pMergedList); |
| |
| *pMergedList = NULL; |
| |
| PKIX_CHECK(PKIX_List_Create(&list, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| if (firstList != NULL) { |
| |
| PKIX_CHECK(PKIX_List_GetLength(firstList, &numItems, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| } |
| |
| for (i = 0; i < numItems; i++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem(firstList, i, &item, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(PKIX_List_AppendItem(list, item, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| |
| PKIX_DECREF(item); |
| } |
| |
| numItems = 0; |
| if (secondList != NULL) { |
| |
| PKIX_CHECK(PKIX_List_GetLength |
| (secondList, |
| &numItems, |
| plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| } |
| |
| for (i = 0; i < numItems; i++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (secondList, i, &item, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(PKIX_List_AppendItem |
| (list, item, plContext), PKIX_LISTAPPENDITEMFAILED); |
| |
| PKIX_DECREF(item); |
| } |
| |
| *pMergedList = list; |
| list = NULL; |
| |
| cleanup: |
| PKIX_DECREF(list); |
| PKIX_DECREF(item); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_AppendList |
| * DESCRIPTION: |
| * |
| * Append items on "fromList" to the "toList". Item reference count on |
| * "toList" is not incremented, but items appended from "fromList" are |
| * incremented. |
| * |
| * PARAMETERS: |
| * "toList" |
| * Address of list to be appended to. Must be non-NULL. |
| * "fromList" |
| * Address of list to be appended from. May be NULL or empty. |
| * "plContext" |
| * platform-specific context pointer |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a List Error if the functions fails in a non-fatal way |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_AppendList( |
| PKIX_List *toList, |
| PKIX_List *fromList, |
| void *plContext) |
| { |
| PKIX_PL_Object *item = NULL; |
| PKIX_UInt32 numItems = 0; |
| PKIX_UInt32 i; |
| |
| PKIX_ENTER(LIST, "pkix_List_AppendList"); |
| PKIX_NULLCHECK_ONE(toList); |
| |
| /* if fromList is NULL or is an empty list, no action */ |
| |
| if (fromList == NULL) { |
| goto cleanup; |
| } |
| |
| PKIX_CHECK(PKIX_List_GetLength(fromList, &numItems, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| if (numItems == 0) { |
| goto cleanup; |
| } |
| |
| for (i = 0; i < numItems; i++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (fromList, i, &item, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(PKIX_List_AppendItem(toList, item, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| |
| PKIX_DECREF(item); |
| } |
| |
| cleanup: |
| |
| PKIX_DECREF(item); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_AppendUnique |
| * DESCRIPTION: |
| * |
| * Adds each Object in the List pointed to by "fromList" to the List pointed |
| * to by "toList", if it is not already a member of that List. In other words, |
| * "toList" becomes the union of the two sets. |
| * |
| * PARAMETERS: |
| * "toList" |
| * Address of a List of Objects to be augmented by "fromList". Must be |
| * non-NULL, but may be empty. |
| * "fromList" |
| * Address of a List of Objects to be added, if not already present, to |
| * "toList". Must be non-NULL, but may be empty. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Not Thread Safe - assumes exclusive access to "toList" |
| * (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_AppendUnique( |
| PKIX_List *toList, |
| PKIX_List *fromList, |
| void *plContext) |
| { |
| PKIX_Boolean isContained = PKIX_FALSE; |
| PKIX_UInt32 listLen = 0; |
| PKIX_UInt32 listIx = 0; |
| PKIX_PL_Object *object = NULL; |
| |
| PKIX_ENTER(BUILD, "pkix_List_AppendUnique"); |
| PKIX_NULLCHECK_TWO(fromList, toList); |
| |
| PKIX_CHECK(PKIX_List_GetLength(fromList, &listLen, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| for (listIx = 0; listIx < listLen; listIx++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (fromList, listIx, &object, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(pkix_List_Contains |
| (toList, object, &isContained, plContext), |
| PKIX_LISTCONTAINSFAILED); |
| |
| if (isContained == PKIX_FALSE) { |
| PKIX_CHECK(PKIX_List_AppendItem |
| (toList, object, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| } |
| |
| PKIX_DECREF(object); |
| } |
| |
| cleanup: |
| |
| PKIX_DECREF(object); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_QuickSort |
| * DESCRIPTION: |
| * |
| * Sorts List of Objects "fromList" using "comparatorCallback"'s result as |
| * comasrison key and returns the sorted List at "pSortedList". The sorting |
| * algorithm used is quick sort (n*logn). |
| * |
| * PARAMETERS: |
| * "fromList" |
| * Address of a List of Objects to be sorted. Must be non-NULL, but may be |
| * empty. |
| * "comparatorCallback" |
| * Address of callback function that will compare two Objects on the List. |
| * It should return -1 for less, 0 for equal and 1 for greater. The |
| * callback implementation chooses what in Objects to be compared. Must be |
| * non-NULL. |
| * "pSortedList" |
| * Address of a List of Objects that shall be sorted and returned. Must be |
| * non-NULL, but may be empty. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Not Thread Safe - assumes exclusive access to "toList" |
| * (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_QuickSort( |
| PKIX_List *fromList, |
| PKIX_List_SortComparatorCallback comparator, |
| PKIX_List **pSortedList, |
| void *plContext) |
| { |
| PKIX_List *sortedList = NULL; |
| PKIX_List *lessList = NULL; |
| PKIX_List *greaterList = NULL; |
| PKIX_List *sortedLessList = NULL; |
| PKIX_List *sortedGreaterList = NULL; |
| PKIX_PL_Object *object = NULL; |
| PKIX_PL_Object *cmpObj = NULL; |
| PKIX_Int32 cmpResult = 0; |
| PKIX_UInt32 size = 0; |
| PKIX_UInt32 i; |
| |
| PKIX_ENTER(BUILD, "pkix_List_QuickSort"); |
| PKIX_NULLCHECK_THREE(fromList, comparator, pSortedList); |
| |
| PKIX_CHECK(PKIX_List_GetLength(fromList, &size, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| PKIX_CHECK(PKIX_List_Create(&lessList, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_List_Create(&greaterList, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (fromList, 0, &object, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| /* |
| * Pick the first item on the list as the one to be compared. |
| * Separate rest of the itmes into two lists: less-than or greater- |
| * than lists. Sort those two lists recursively. Insert sorted |
| * less-than list before the picked item and append the greater- |
| * than list after the picked item. |
| */ |
| for (i = 1; i < size; i++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (fromList, i, &cmpObj, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(comparator(object, cmpObj, &cmpResult, plContext), |
| PKIX_COMPARATORCALLBACKFAILED); |
| |
| if (cmpResult >= 0) { |
| PKIX_CHECK(PKIX_List_AppendItem |
| (lessList, cmpObj, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| } else { |
| PKIX_CHECK(PKIX_List_AppendItem |
| (greaterList, cmpObj, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| } |
| PKIX_DECREF(cmpObj); |
| } |
| |
| PKIX_CHECK(PKIX_List_Create(&sortedList, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| PKIX_CHECK(PKIX_List_GetLength(lessList, &size, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| if (size > 1) { |
| |
| PKIX_CHECK(pkix_List_QuickSort |
| (lessList, comparator, &sortedLessList, plContext), |
| PKIX_LISTQUICKSORTFAILED); |
| |
| PKIX_CHECK(pkix_List_AppendList |
| (sortedList, sortedLessList, plContext), |
| PKIX_LISTAPPENDLISTFAILED); |
| } else { |
| PKIX_CHECK(pkix_List_AppendList |
| (sortedList, lessList, plContext), |
| PKIX_LISTAPPENDLISTFAILED); |
| } |
| |
| PKIX_CHECK(PKIX_List_AppendItem(sortedList, object, plContext), |
| PKIX_LISTAPPENDFAILED); |
| |
| PKIX_CHECK(PKIX_List_GetLength(greaterList, &size, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| if (size > 1) { |
| |
| PKIX_CHECK(pkix_List_QuickSort |
| (greaterList, comparator, &sortedGreaterList, plContext), |
| PKIX_LISTQUICKSORTFAILED); |
| |
| PKIX_CHECK(pkix_List_AppendList |
| (sortedList, sortedGreaterList, plContext), |
| PKIX_LISTAPPENDLISTFAILED); |
| } else { |
| PKIX_CHECK(pkix_List_AppendList |
| (sortedList, greaterList, plContext), |
| PKIX_LISTAPPENDLISTFAILED); |
| } |
| |
| *pSortedList = sortedList; |
| |
| cleanup: |
| |
| PKIX_DECREF(cmpObj); |
| PKIX_DECREF(object); |
| PKIX_DECREF(sortedGreaterList); |
| PKIX_DECREF(sortedLessList); |
| PKIX_DECREF(greaterList); |
| PKIX_DECREF(lessList); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: pkix_List_BubbleSort |
| * DESCRIPTION: |
| * |
| * Sorts List of Objects "fromList" using "comparatorCallback"'s result as |
| * comasrison key and returns the sorted List at "pSortedList". The sorting |
| * algorithm used is bubble sort (n*n). |
| * |
| * PARAMETERS: |
| * "fromList" |
| * Address of a List of Objects to be sorted. Must be non-NULL, but may be |
| * empty. |
| * "comparatorCallback" |
| * Address of callback function that will compare two Objects on the List. |
| * It should return -1 for less, 0 for equal and 1 for greater. The |
| * callback implementation chooses what in Objects to be compared. Must be |
| * non-NULL. |
| * "pSortedList" |
| * Address of a List of Objects that shall be sorted and returned. Must be |
| * non-NULL, but may be empty. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Not Thread Safe - assumes exclusive access to "toList" |
| * (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds |
| * Returns a Fatal Error if the function fails in an unrecoverable way |
| */ |
| PKIX_Error * |
| pkix_List_BubbleSort( |
| PKIX_List *fromList, |
| PKIX_List_SortComparatorCallback comparator, |
| PKIX_List **pSortedList, |
| void *plContext) |
| { |
| PKIX_List *sortedList = NULL; |
| PKIX_PL_Object *cmpObj = NULL; |
| PKIX_PL_Object *leastObj = NULL; |
| PKIX_Int32 cmpResult = 0; |
| PKIX_UInt32 size = 0; |
| PKIX_UInt32 i, j; |
| |
| PKIX_ENTER(BUILD, "pkix_List_BubbleSort"); |
| PKIX_NULLCHECK_THREE(fromList, comparator, pSortedList); |
| |
| if (fromList->immutable) { |
| PKIX_ERROR(PKIX_CANNOTSORTIMMUTABLELIST); |
| } |
| PKIX_CHECK(pkix_List_Duplicate |
| ((PKIX_PL_Object *) fromList, |
| (PKIX_PL_Object **) &sortedList, |
| plContext), |
| PKIX_LISTDUPLICATEFAILED); |
| |
| PKIX_CHECK(PKIX_List_GetLength(sortedList, &size, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| if (size > 1) { |
| |
| /* |
| * Move from the first of the item on the list, For each iteration, |
| * compare and swap the least value to the head of the comparisoning |
| * sub-list. |
| */ |
| for (i = 0; i < size - 1; i++) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (sortedList, i, &leastObj, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| for (j = i + 1; j < size; j++) { |
| PKIX_CHECK(PKIX_List_GetItem |
| (sortedList, j, &cmpObj, plContext), |
| PKIX_LISTGETITEMFAILED); |
| PKIX_CHECK(comparator |
| (leastObj, cmpObj, &cmpResult, plContext), |
| PKIX_COMPARATORCALLBACKFAILED); |
| if (cmpResult > 0) { |
| PKIX_CHECK(PKIX_List_SetItem |
| (sortedList, j, leastObj, plContext), |
| PKIX_LISTSETITEMFAILED); |
| |
| PKIX_DECREF(leastObj); |
| leastObj = cmpObj; |
| cmpObj = NULL; |
| } else { |
| PKIX_DECREF(cmpObj); |
| } |
| } |
| PKIX_CHECK(PKIX_List_SetItem |
| (sortedList, i, leastObj, plContext), |
| PKIX_LISTSETITEMFAILED); |
| |
| PKIX_DECREF(leastObj); |
| } |
| |
| } |
| |
| *pSortedList = sortedList; |
| sortedList = NULL; |
| cleanup: |
| |
| PKIX_DECREF(sortedList); |
| PKIX_DECREF(leastObj); |
| PKIX_DECREF(cmpObj); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* --Public-List-Functions--------------------------------------------- */ |
| |
| /* |
| * FUNCTION: PKIX_List_Create (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_Create( |
| PKIX_List **pList, |
| void *plContext) |
| { |
| PKIX_List *list = NULL; |
| |
| PKIX_ENTER(LIST, "PKIX_List_Create"); |
| PKIX_NULLCHECK_ONE(pList); |
| |
| PKIX_CHECK(pkix_List_Create_Internal(PKIX_TRUE, &list, plContext), |
| PKIX_LISTCREATEINTERNALFAILED); |
| |
| *pList = list; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_SetImmutable (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_SetImmutable( |
| PKIX_List *list, |
| void *plContext) |
| { |
| PKIX_ENTER(LIST, "PKIX_List_SetImmutable"); |
| PKIX_NULLCHECK_ONE(list); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| list->immutable = PKIX_TRUE; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_IsImmutable (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_IsImmutable( |
| PKIX_List *list, |
| PKIX_Boolean *pImmutable, |
| void *plContext) |
| { |
| PKIX_ENTER(LIST, "PKIX_List_IsImmutable"); |
| PKIX_NULLCHECK_TWO(list, pImmutable); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| *pImmutable = list->immutable; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_GetLength (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_GetLength( |
| PKIX_List *list, |
| PKIX_UInt32 *pLength, |
| void *plContext) |
| { |
| PKIX_ENTER(LIST, "PKIX_List_GetLength"); |
| PKIX_NULLCHECK_TWO(list, pLength); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| *pLength = list->length; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_IsEmpty (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_IsEmpty( |
| PKIX_List *list, |
| PKIX_Boolean *pEmpty, |
| void *plContext) |
| { |
| PKIX_UInt32 length; |
| |
| PKIX_ENTER(LIST, "PKIX_List_IsEmpty"); |
| PKIX_NULLCHECK_TWO(list, pEmpty); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| length = list->length; |
| |
| if (length == 0){ |
| *pEmpty = PKIX_TRUE; |
| } else { |
| *pEmpty = PKIX_FALSE; |
| } |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_AppendItem (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_AppendItem( |
| PKIX_List *list, |
| PKIX_PL_Object *item, |
| void *plContext) |
| { |
| PKIX_List *lastElement = NULL; |
| PKIX_List *newElement = NULL; |
| PKIX_UInt32 length, i; |
| |
| PKIX_ENTER(LIST, "PKIX_List_AppendItem"); |
| PKIX_NULLCHECK_ONE(list); |
| |
| if (list->immutable){ |
| PKIX_ERROR(PKIX_OPERATIONNOTPERMITTEDONIMMUTABLELIST); |
| } |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| length = list->length; |
| |
| /* find last element of list and create new element there */ |
| |
| lastElement = list; |
| for (i = 0; i < length; i++){ |
| lastElement = lastElement->next; |
| } |
| |
| PKIX_CHECK(pkix_List_Create_Internal |
| (PKIX_FALSE, &newElement, plContext), |
| PKIX_LISTCREATEINTERNALFAILED); |
| |
| PKIX_INCREF(item); |
| newElement->item = item; |
| |
| PKIX_CHECK(PKIX_PL_Object_InvalidateCache |
| ((PKIX_PL_Object *)list, plContext), |
| PKIX_OBJECTINVALIDATECACHEFAILED); |
| |
| lastElement->next = newElement; |
| newElement = NULL; |
| list->length += 1; |
| |
| cleanup: |
| |
| PKIX_DECREF(newElement); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_InsertItem (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_InsertItem( |
| PKIX_List *list, |
| PKIX_UInt32 index, |
| PKIX_PL_Object *item, |
| void *plContext) |
| { |
| PKIX_List *element = NULL; |
| PKIX_List *newElem = NULL; |
| |
| PKIX_ENTER(LIST, "PKIX_List_InsertItem"); |
| PKIX_NULLCHECK_ONE(list); |
| |
| |
| if (list->immutable){ |
| PKIX_ERROR(PKIX_OPERATIONNOTPERMITTEDONIMMUTABLELIST); |
| } |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| /* Create a new list object */ |
| PKIX_CHECK(pkix_List_Create_Internal(PKIX_FALSE, &newElem, plContext), |
| PKIX_LISTCREATEINTERNALFAILED); |
| |
| if (list->length) { |
| PKIX_CHECK(pkix_List_GetElement(list, index, &element, plContext), |
| PKIX_LISTGETELEMENTFAILED); |
| /* Copy the old element's contents into the new element */ |
| newElem->item = element->item; |
| /* Add new item to the list */ |
| PKIX_INCREF(item); |
| element->item = item; |
| /* Set the new element's next pointer to the old element's next */ |
| newElem->next = element->next; |
| /* Set the old element's next pointer to the new element */ |
| element->next = newElem; |
| newElem = NULL; |
| } else { |
| PKIX_INCREF(item); |
| newElem->item = item; |
| newElem->next = NULL; |
| list->next = newElem; |
| newElem = NULL; |
| } |
| list->length++; |
| |
| PKIX_CHECK(PKIX_PL_Object_InvalidateCache |
| ((PKIX_PL_Object *)list, plContext), |
| PKIX_OBJECTINVALIDATECACHEFAILED); |
| cleanup: |
| PKIX_DECREF(newElem); |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_GetItem (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_GetItem( |
| PKIX_List *list, |
| PKIX_UInt32 index, |
| PKIX_PL_Object **pItem, |
| void *plContext) |
| { |
| PKIX_List *element = NULL; |
| |
| PKIX_ENTER(LIST, "PKIX_List_GetItem"); |
| PKIX_NULLCHECK_TWO(list, pItem); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| PKIX_CHECK(pkix_List_GetElement(list, index, &element, plContext), |
| PKIX_LISTGETELEMENTFAILED); |
| |
| PKIX_INCREF(element->item); |
| *pItem = element->item; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_SetItem (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_SetItem( |
| PKIX_List *list, |
| PKIX_UInt32 index, |
| PKIX_PL_Object *item, |
| void *plContext) |
| { |
| PKIX_List *element; |
| |
| PKIX_ENTER(LIST, "PKIX_List_SetItem"); |
| PKIX_NULLCHECK_ONE(list); |
| |
| if (list->immutable){ |
| PKIX_ERROR(PKIX_OPERATIONNOTPERMITTEDONIMMUTABLELIST); |
| } |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| PKIX_CHECK(pkix_List_GetElement(list, index, &element, plContext), |
| PKIX_LISTGETELEMENTFAILED); |
| |
| /* DecRef old contents */ |
| PKIX_DECREF(element->item); |
| |
| /* Set New Contents */ |
| PKIX_INCREF(item); |
| element->item = item; |
| |
| PKIX_CHECK(PKIX_PL_Object_InvalidateCache |
| ((PKIX_PL_Object *)list, plContext), |
| PKIX_OBJECTINVALIDATECACHEFAILED); |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_DeleteItem (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_DeleteItem( |
| PKIX_List *list, |
| PKIX_UInt32 index, |
| void *plContext) |
| { |
| PKIX_List *element = NULL; |
| PKIX_List *prevElement = NULL; |
| PKIX_List *nextElement = NULL; |
| |
| PKIX_ENTER(LIST, "PKIX_List_DeleteItem"); |
| PKIX_NULLCHECK_ONE(list); |
| |
| if (list->immutable){ |
| PKIX_ERROR(PKIX_OPERATIONNOTPERMITTEDONIMMUTABLELIST); |
| } |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| PKIX_CHECK(pkix_List_GetElement(list, index, &element, plContext), |
| PKIX_LISTGETELEMENTFAILED); |
| |
| /* DecRef old contents */ |
| PKIX_DECREF(element->item); |
| |
| nextElement = element->next; |
| |
| if (nextElement != NULL) { |
| /* If the next element exists, splice it out. */ |
| |
| /* Don't need to change ref counts for targets of next */ |
| element->item = nextElement->item; |
| nextElement->item = NULL; |
| |
| /* Don't need to change ref counts for targets of next */ |
| element->next = nextElement->next; |
| nextElement->next = NULL; |
| |
| PKIX_DECREF(nextElement); |
| |
| } else { /* The element is at the tail of the list */ |
| if (index != 0) { |
| PKIX_CHECK(pkix_List_GetElement |
| (list, index-1, &prevElement, plContext), |
| PKIX_LISTGETELEMENTFAILED); |
| } else if (index == 0){ /* prevElement must be header */ |
| prevElement = list; |
| } |
| prevElement->next = NULL; |
| |
| /* Delete the element */ |
| PKIX_DECREF(element); |
| } |
| |
| PKIX_CHECK(PKIX_PL_Object_InvalidateCache |
| ((PKIX_PL_Object *)list, plContext), |
| PKIX_OBJECTINVALIDATECACHEFAILED); |
| |
| list->length = list->length - 1; |
| |
| cleanup: |
| |
| PKIX_RETURN(LIST); |
| } |
| |
| /* |
| * FUNCTION: PKIX_List_ReverseList (see comments in pkix_util.h) |
| */ |
| PKIX_Error * |
| PKIX_List_ReverseList( |
| PKIX_List *list, |
| PKIX_List **pReversedList, |
| void *plContext) |
| { |
| PKIX_List *reversedList = NULL; |
| PKIX_PL_Object *item = NULL; |
| PKIX_PL_Object *duplicateItem = NULL; |
| PKIX_UInt32 length, i; |
| |
| PKIX_ENTER(LIST, "pkix_List_ReverseList"); |
| PKIX_NULLCHECK_TWO(list, pReversedList); |
| |
| if (!list->isHeader){ |
| PKIX_ERROR(PKIX_INPUTLISTMUSTBEHEADER); |
| } |
| |
| length = list->length; |
| |
| /* Create a new list object */ |
| PKIX_CHECK(PKIX_List_Create(&reversedList, plContext), |
| PKIX_LISTCREATEINTERNALFAILED); |
| |
| /* |
| * Starting with the last item and traversing backwards (from |
| * the original list), append each item to the reversed list |
| */ |
| |
| for (i = 1; i <= length; i++){ |
| PKIX_CHECK(PKIX_List_GetItem |
| (list, (length - i), &item, plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_CHECK(PKIX_PL_Object_Duplicate |
| (item, &duplicateItem, plContext), |
| PKIX_LISTDUPLICATEFAILED); |
| |
| PKIX_CHECK(PKIX_List_AppendItem |
| (reversedList, duplicateItem, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| |
| PKIX_DECREF(item); |
| PKIX_DECREF(duplicateItem); |
| } |
| |
| *pReversedList = reversedList; |
| |
| cleanup: |
| |
| PKIX_DECREF(item); |
| PKIX_DECREF(duplicateItem); |
| |
| if (PKIX_ERROR_RECEIVED){ |
| PKIX_DECREF(reversedList); |
| } |
| |
| PKIX_RETURN(LIST); |
| } |