/* 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_pl_object.c * * Object Construction, Destruction and Callback Functions * */ #include "pkix_pl_object.h" #ifdef PKIX_USER_OBJECT_TYPE /* --Class-Table-Initializers------------------------------------ */ /* * Create storage space for 20 Class Table buckets. * These are only for user-defined types. System types are registered * separately by PKIX_PL_Initialize. */ static pkix_pl_HT_Elem* pkix_Raw_ClassTable_Buckets[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* * Allocate static memory for a ClassTable. * XXX This assumes the bucket pointer will fit into a PKIX_UInt32 */ static pkix_pl_PrimHashTable pkix_Raw_ClassTable = { (void *)pkix_Raw_ClassTable_Buckets, /* Buckets */ 20 /* Number of Buckets */ }; static pkix_pl_PrimHashTable * classTable = &pkix_Raw_ClassTable; #endif /* PKIX_USER_OBJECT_TYPE */ /* --Private-Functions-------------------------------------------- */ /* * FUNCTION: pkix_pl_Object_GetHeader * DESCRIPTION: * * Shifts Object pointed to by "object" by the sizeof(PKIX_PL_Object) and * stores the value at "pObjectHeader". * * PARAMETERS: * "object" * Address of Object to shift. Must be non-NULL. * "pObjectHeader" * 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_pl_Object_GetHeader( PKIX_PL_Object *object, PKIX_PL_Object **pObjectHeader, void *plContext) { PKIX_PL_Object *header = NULL; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "pkix_pl_Object_GetHeader"); PKIX_NULLCHECK_TWO(object, pObjectHeader); PKIX_OBJECT_DEBUG("\tShifting object pointer).\n"); /* The header is sizeof(PKIX_PL_Object) before the object pointer */ header = (PKIX_PL_Object *)((char *)object - sizeof(PKIX_PL_Object)); objType = header->type; if (objType >= PKIX_NUMTYPES) { /* if this is a user-defined type */ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); PKIX_CHECK(pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext), PKIX_ERRORGETTINGCLASSTABLEENTRY); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (ctEntry == NULL) { PKIX_ERROR_FATAL(PKIX_UNKNOWNOBJECTTYPE); } #else PORT_Assert(objType < PKIX_NUMTYPES); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } #ifdef PKIX_OBJECT_LEAK_TEST PORT_Assert(header && header->magicHeader == PKIX_MAGIC_HEADER); #endif /* PKIX_OBJECT_LEAK_TEST */ if ((header == NULL)|| (header->magicHeader != PKIX_MAGIC_HEADER)) { PKIX_ERROR_ALLOC_ERROR(); } *pObjectHeader = header; cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: pkix_Destroy_Object * DESCRIPTION: * * Destroys and deallocates Object pointed to by "object". The caller is * assumed to hold the Object's lock, which is acquired in * PKIX_PL_Object_DecRef(). * * PARAMETERS: * "object" * Address of Object to destroy. 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_pl_Object_Destroy( PKIX_PL_Object *object, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_ENTER(OBJECT, "pkix_pl_Object_Destroy"); PKIX_NULLCHECK_ONE(object); #ifdef PKIX_OBJECT_LEAK_TEST PKIX_CHECK_FATAL(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); #else PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); #endif /* PKIX_OBJECT_LEAK_TEST */ /* Attempt to delete an object still being used */ if (objectHeader->references != 0) { PKIX_ERROR_FATAL(PKIX_OBJECTSTILLREFERENCED); } PKIX_DECREF(objectHeader->stringRep); /* Destroy this object's lock */ PKIX_OBJECT_DEBUG("\tCalling PR_DestroyLock).\n"); PR_DestroyLock(objectHeader->lock); objectHeader->lock = NULL; object = NULL; objectHeader->magicHeader = PKIX_MAGIC_HEADER_DESTROYED; #ifdef PKIX_OBJECT_LEAK_TEST memset(objectHeader, 0xbf, systemClasses[PKIX_OBJECT_TYPE].typeObjectSize); #endif PKIX_FREE(objectHeader); cleanup: #ifdef PKIX_OBJECT_LEAK_TEST fatal: #endif PKIX_RETURN(OBJECT); } /* --Default-Callbacks-------------------------------------------- */ /* * FUNCTION: pkix_pl_Object_Equals_Default * DESCRIPTION: * * Default Object_Equals callback: Compares the address of the Object pointed * to by "firstObject" with the address of the Object pointed to by * "secondObject" and stores the Boolean result at "pResult". * * PARAMETERS: * "firstObject" * Address of first Object to compare. Must be non-NULL. * "secondObject" * Address of second Object to compare. Must be non-NULL. * "pResult" * Address where Boolean result 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_pl_Object_Equals_Default( PKIX_PL_Object *firstObject, PKIX_PL_Object *secondObject, PKIX_Boolean *pResult, void *plContext) { PKIX_ENTER(OBJECT, "pkix_pl_Object_Equals_Default"); PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); /* Just compare pointer values */ *pResult = (firstObject == secondObject)?PKIX_TRUE:PKIX_FALSE; PKIX_RETURN(OBJECT); } /* * FUNCTION: pkix_pl_Object_ToString_Default * DESCRIPTION: * * Default Object_ToString callback: Creates a string consisting of the * typename and address of the Object pointed to by "object" and stores * the result at "pString". The format for the string is * "TypeName@Address:
", where the default typename is "Object". * * PARAMETERS: * "object" * Address of Object to convert to a string. Must be non-NULL. * "pString" * 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 an Object 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_pl_Object_ToString_Default( PKIX_PL_Object *object, PKIX_PL_String **pString, void *plContext) { PKIX_PL_String *formatString = NULL; PKIX_PL_String *descString = NULL; char *format = "%s@Address: %x"; char *description = NULL; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "pkix_pl_Object_ToString_Default"); PKIX_NULLCHECK_TWO(object, pString); PKIX_CHECK(PKIX_PL_Object_GetType(object, &objType, plContext), PKIX_OBJECTGETTYPEFAILED); if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_ERRORGETTINGCLASSTABLEENTRY); } if (ctEntry == NULL){ PKIX_ERROR_FATAL(PKIX_UNDEFINEDCLASSTABLEENTRY); } else { description = ctEntry->description; if (description == NULL) { description = "User Type Object"; } } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { description = systemClasses[objType].description; } PKIX_CHECK(PKIX_PL_String_Create (PKIX_ESCASCII, (void *)format, 0, &formatString, plContext), PKIX_STRINGCREATEFAILED); PKIX_CHECK(PKIX_PL_String_Create (PKIX_ESCASCII, (void *)description, 0, &descString, plContext), PKIX_STRINGCREATEFAILED); PKIX_CHECK(PKIX_PL_Sprintf (pString, plContext, formatString, descString, object), PKIX_SPRINTFFAILED); cleanup: PKIX_DECREF(formatString); PKIX_DECREF(descString); PKIX_RETURN(OBJECT); } /* * FUNCTION: pkix_pl_Object_Hashcode_Default * DESCRIPTION: * * Default Object_Hashcode callback. Creates the a hashcode value using the * address of the Object pointed to by "object" and stores the result at * "pValue". * * XXX This isn't great since addresses are not uniformly distributed. * * PARAMETERS: * "object" * Address of Object to compute hashcode for. Must be non-NULL. * "pValue" * Address where PKIX_UInt32 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_pl_Object_Hashcode_Default( PKIX_PL_Object *object, PKIX_UInt32 *pValue, void *plContext) { PKIX_ENTER(OBJECT, "pkix_pl_Object_Hashcode_Default"); PKIX_NULLCHECK_TWO(object, pValue); *pValue = (PKIX_UInt32)((char *)object - (char *)NULL); PKIX_RETURN(OBJECT); } /* * FUNCTION: pkix_pl_Object_RetrieveEqualsCallback * DESCRIPTION: * * Retrieves Equals callback function of Object pointed to by "object and * stores it at "pEqualsCallback". If the object's type is one of the system * types, its callback function is retrieved from the systemClasses array; * otherwise, its callback function is retrieve from the classTable hash * table where user-defined types are stored. * * PARAMETERS: * "object" * Address of Object whose equals callback is desired. Must be non-NULL. * "pEqualsCallback" * Address where EqualsCallback function 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 an Object Error if the function fails in a non-fatal way. * Returns a Fatal Error if the function fails in an unrecoverable way. */ PKIX_Error * pkix_pl_Object_RetrieveEqualsCallback( PKIX_PL_Object *object, PKIX_PL_EqualsCallback *pEqualsCallback, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_PL_EqualsCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "pkix_pl_Object_RetrieveEqualsCallback"); PKIX_NULLCHECK_TWO(object, pEqualsCallback); PKIX_CHECK(pkix_pl_Object_GetHeader (object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); objType = objectHeader->type; if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR(PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->equalsFunction == NULL)) { PKIX_ERROR(PKIX_UNDEFINEDEQUALSCALLBACK); } else { *pEqualsCallback = ctEntry->equalsFunction; } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { entry = systemClasses[objType]; func = entry.equalsFunction; if (func == NULL){ func = pkix_pl_Object_Equals_Default; } *pEqualsCallback = func; } cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: pkix_pl_Object_RegisterSelf * DESCRIPTION: * Registers PKIX_OBJECT_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_PL_Object should have all function pointes to be to NULL: they * work as proxy function to a real objects. * */ PKIX_Error * pkix_pl_Object_RegisterSelf(void *plContext) { pkix_ClassTable_Entry entry; PKIX_ENTER(ERROR, "pkix_pl_Object_RegisterSelf"); entry.description = "Object"; entry.objCounter = 0; entry.typeObjectSize = sizeof(PKIX_PL_Object); entry.destructor = NULL; entry.equalsFunction = NULL; entry.hashcodeFunction = NULL; entry.toStringFunction = NULL; entry.comparator = NULL; entry.duplicateFunction = NULL; systemClasses[PKIX_OBJECT_TYPE] = entry; PKIX_RETURN(ERROR); } /* --Public-Functions------------------------------------------------------- */ /* * FUNCTION: PKIX_PL_Object_Alloc (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Alloc( PKIX_TYPENUM objType, PKIX_UInt32 size, PKIX_PL_Object **pObject, void *plContext) { PKIX_PL_Object *object = NULL; pkix_ClassTable_Entry *ctEntry = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_Alloc"); PKIX_NULLCHECK_ONE(pObject); /* * We need to ensure that user-defined types have been registered. * All system types have already been registered by PKIX_PL_Initialize. */ if (objType >= PKIX_NUMTYPES) { /* i.e. if this is a user-defined type */ #ifdef PKIX_USER_OBJECT_TYPE PKIX_Boolean typeRegistered; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_COULDNOTLOOKUPINHASHTABLE); } typeRegistered = (ctEntry != NULL); if (!typeRegistered) { PKIX_ERROR_FATAL(PKIX_UNKNOWNTYPEARGUMENT); } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { ctEntry = &systemClasses[objType]; } PORT_Assert(size == ctEntry->typeObjectSize); /* Allocate space for the object header and the requested size */ #ifdef PKIX_OBJECT_LEAK_TEST PKIX_CHECK(PKIX_PL_Calloc (1, ((PKIX_UInt32)sizeof (PKIX_PL_Object))+size, (void **)&object, plContext), PKIX_MALLOCFAILED); #else PKIX_CHECK(PKIX_PL_Malloc (((PKIX_UInt32)sizeof (PKIX_PL_Object))+size, (void **)&object, plContext), PKIX_MALLOCFAILED); #endif /* PKIX_OBJECT_LEAK_TEST */ /* Initialize all object fields */ object->magicHeader = PKIX_MAGIC_HEADER; object->type = objType; object->references = 1; /* Default to a single reference */ object->stringRep = NULL; object->hashcode = 0; object->hashcodeCached = 0; /* Cannot use PKIX_PL_Mutex because it depends on Object */ /* Using NSPR Locks instead */ PKIX_OBJECT_DEBUG("\tCalling PR_NewLock).\n"); object->lock = PR_NewLock(); if (object->lock == NULL) { PKIX_ERROR_ALLOC_ERROR(); } PKIX_OBJECT_DEBUG("\tShifting object pointer).\n"); /* Return a pointer to the user data. Need to offset by object size */ *pObject = object + 1; object = NULL; /* Atomically increment object counter */ PR_ATOMIC_INCREMENT((PRInt32*)&ctEntry->objCounter); cleanup: PKIX_FREE(object); PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_IsTypeRegistered (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_IsTypeRegistered( PKIX_UInt32 objType, PKIX_Boolean *pBool, void *plContext) { #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; #endif PKIX_ENTER(OBJECT, "PKIX_PL_Object_IsTypeRegistered"); PKIX_NULLCHECK_ONE(pBool); /* first, we handle the system types */ if (objType < PKIX_NUMTYPES) { *pBool = PKIX_TRUE; goto cleanup; } #ifndef PKIX_USER_OBJECT_TYPE PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; #else PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_COULDNOTLOOKUPINHASHTABLE); } *pBool = (ctEntry != NULL); #endif /* PKIX_USER_OBJECT_TYPE */ cleanup: PKIX_RETURN(OBJECT); } #ifdef PKIX_USER_OBJECT_TYPE /* * FUNCTION: PKIX_PL_Object_RegisterType (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_RegisterType( PKIX_UInt32 objType, char *description, PKIX_PL_DestructorCallback destructor, PKIX_PL_EqualsCallback equalsFunction, PKIX_PL_HashcodeCallback hashcodeFunction, PKIX_PL_ToStringCallback toStringFunction, PKIX_PL_ComparatorCallback comparator, PKIX_PL_DuplicateCallback duplicateFunction, void *plContext) { pkix_ClassTable_Entry *ctEntry = NULL; pkix_pl_Integer *key = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_RegisterType"); /* * System types are registered on startup by PKIX_PL_Initialize. * These can not be overwritten. */ if (objType < PKIX_NUMTYPES) { /* if this is a system type */ PKIX_ERROR(PKIX_CANTREREGISTERSYSTEMTYPE); } PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); PKIX_CHECK(pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext), PKIX_PRIMHASHTABLELOOKUPFAILED); /* If the type is already registered, throw an error */ if (ctEntry) { PKIX_ERROR(PKIX_TYPEALREADYREGISTERED); } PKIX_CHECK(PKIX_PL_Malloc (((PKIX_UInt32)sizeof (pkix_ClassTable_Entry)), (void **)&ctEntry, plContext), PKIX_MALLOCFAILED); /* Set Default Values if none specified */ if (description == NULL){ description = "Object"; } if (equalsFunction == NULL) { equalsFunction = pkix_pl_Object_Equals_Default; } if (toStringFunction == NULL) { toStringFunction = pkix_pl_Object_ToString_Default; } if (hashcodeFunction == NULL) { hashcodeFunction = pkix_pl_Object_Hashcode_Default; } ctEntry->destructor = destructor; ctEntry->equalsFunction = equalsFunction; ctEntry->toStringFunction = toStringFunction; ctEntry->hashcodeFunction = hashcodeFunction; ctEntry->comparator = comparator; ctEntry->duplicateFunction = duplicateFunction; ctEntry->description = description; PKIX_CHECK(PKIX_PL_Malloc (((PKIX_UInt32)sizeof (pkix_pl_Integer)), (void **)&key, plContext), PKIX_COULDNOTMALLOCNEWKEY); key->ht_int = objType; PKIX_CHECK(pkix_pl_PrimHashTable_Add (classTable, (void *)key, (void *)ctEntry, objType, NULL, plContext), PKIX_PRIMHASHTABLEADDFAILED); cleanup: PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); PKIX_RETURN(OBJECT); } #endif /* PKIX_USER_OBJECT_TYPE */ /* * FUNCTION: PKIX_PL_Object_IncRef (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_IncRef( PKIX_PL_Object *object, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_PL_NssContext *context = NULL; PKIX_Int32 refCount = 0; PKIX_ENTER(OBJECT, "PKIX_PL_Object_IncRef"); PKIX_NULLCHECK_ONE(object); if (plContext){ /* * PKIX_PL_NssContext is not a complete PKIX Type, it doesn't * have a header therefore we cannot verify its type before * casting. */ context = (PKIX_PL_NssContext *) plContext; if (context->arena != NULL) { goto cleanup; } } if (object == (PKIX_PL_Object*)PKIX_ALLOC_ERROR()) { goto cleanup; } /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); /* This object should never have zero references */ refCount = PR_ATOMIC_INCREMENT(&objectHeader->references); if (refCount <= 1) { PKIX_THROW(FATAL, PKIX_OBJECTWITHNONPOSITIVEREFERENCES); } cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_DecRef (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_DecRef( PKIX_PL_Object *object, void *plContext) { PKIX_Int32 refCount = 0; PKIX_PL_Object *objectHeader = NULL; PKIX_PL_NssContext *context = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_DecRef"); PKIX_NULLCHECK_ONE(object); if (plContext){ /* * PKIX_PL_NssContext is not a complete PKIX Type, it doesn't * have a header therefore we cannot verify its type before * casting. */ context = (PKIX_PL_NssContext *) plContext; if (context->arena != NULL) { goto cleanup; } } if (object == (PKIX_PL_Object*)PKIX_ALLOC_ERROR()) { goto cleanup; } /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); refCount = PR_ATOMIC_DECREMENT(&objectHeader->references); if (refCount == 0) { PKIX_PL_DestructorCallback destructor = NULL; pkix_ClassTable_Entry *ctEntry = NULL; PKIX_UInt32 objType = objectHeader->type; /* first, special handling for system types */ if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG ("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL (PKIX_ERRORINGETTINGDESTRUCTOR); } if (ctEntry != NULL){ destructor = ctEntry->destructor; } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { ctEntry = &systemClasses[objType]; destructor = ctEntry->destructor; } if (destructor != NULL){ /* Call destructor on user data if necessary */ pkixErrorResult = destructor(object, plContext); if (pkixErrorResult) { pkixErrorClass = PKIX_FATAL_ERROR; PKIX_DoAddError(stdVarsPtr, pkixErrorResult, plContext); pkixErrorResult = NULL; } } /* Atomically decrement object counter */ PR_ATOMIC_DECREMENT((PRInt32*)&ctEntry->objCounter); /* pkix_pl_Object_Destroy assumes the lock is held */ /* It will call unlock and destroy the object */ pkixErrorResult = pkix_pl_Object_Destroy(object, plContext); goto cleanup; } if (refCount < 0) { PKIX_ERROR_ALLOC_ERROR(); } cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Equals (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Equals( PKIX_PL_Object *firstObject, PKIX_PL_Object *secondObject, PKIX_Boolean *pResult, void *plContext) { PKIX_PL_Object *firstObjectHeader = NULL; PKIX_PL_Object *secondObjectHeader = NULL; PKIX_PL_EqualsCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "PKIX_PL_Object_Equals"); PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); PKIX_CHECK(pkix_pl_Object_GetHeader (firstObject, &firstObjectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); PKIX_CHECK(pkix_pl_Object_GetHeader (secondObject, &secondObjectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); /* if hashcodes are cached but not equal, objects can't be equal */ if (firstObjectHeader->hashcodeCached && secondObjectHeader->hashcodeCached){ if (firstObjectHeader->hashcode != secondObjectHeader->hashcode){ *pResult = PKIX_FALSE; goto cleanup; } } objType = firstObjectHeader->type; if (objType >= PKIX_NUMTYPES) { #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&firstObjectHeader->type, firstObjectHeader->type, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->equalsFunction == NULL)) { PKIX_ERROR_FATAL(PKIX_UNDEFINEDCALLBACK); } else { func = ctEntry->equalsFunction; } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { entry = systemClasses[objType]; func = entry.equalsFunction; if (func == NULL){ func = pkix_pl_Object_Equals_Default; } } PKIX_CHECK(func(firstObject, secondObject, pResult, plContext), PKIX_OBJECTSPECIFICFUNCTIONFAILED); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Duplicate (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Duplicate( PKIX_PL_Object *firstObject, PKIX_PL_Object **pNewObject, void *plContext) { PKIX_PL_Object *firstObjectHeader = NULL; PKIX_PL_DuplicateCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "PKIX_PL_Object_Duplicate"); PKIX_NULLCHECK_TWO(firstObject, pNewObject); PKIX_CHECK(pkix_pl_Object_GetHeader (firstObject, &firstObjectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); objType = firstObjectHeader->type; if (objType >= PKIX_NUMTYPES) { #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->duplicateFunction == NULL)) { PKIX_ERROR_FATAL(PKIX_UNDEFINEDCALLBACK); } else { func = ctEntry->duplicateFunction; } #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { entry = systemClasses[objType]; func = entry.duplicateFunction; if (!func){ PKIX_ERROR_FATAL(PKIX_UNDEFINEDDUPLICATEFUNCTION); } } PKIX_CHECK(func(firstObject, pNewObject, plContext), PKIX_OBJECTSPECIFICFUNCTIONFAILED); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Hashcode (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Hashcode( PKIX_PL_Object *object, PKIX_UInt32 *pValue, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_PL_HashcodeCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_UInt32 objectHash; PKIX_ENTER(OBJECT, "PKIX_PL_Object_Hashcode"); PKIX_NULLCHECK_TWO(object, pValue); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); /* if we don't have a cached copy from before, we create one */ if (!objectHeader->hashcodeCached){ PKIX_UInt32 objType = objectHeader->type; /* first, special handling for system types */ if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL (PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->hashcodeFunction == NULL)) { PKIX_ERROR_FATAL(PKIX_UNDEFINEDCALLBACK); } func = ctEntry->hashcodeFunction; #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { entry = systemClasses[objType]; func = entry.hashcodeFunction; if (func == NULL){ func = pkix_pl_Object_Hashcode_Default; } } PKIX_CHECK(func(object, &objectHash, plContext), PKIX_OBJECTSPECIFICFUNCTIONFAILED); if (!objectHeader->hashcodeCached){ PKIX_CHECK(pkix_LockObject(object, plContext), PKIX_ERRORLOCKINGOBJECT); if (!objectHeader->hashcodeCached){ /* save cached copy in case we need it again */ objectHeader->hashcode = objectHash; objectHeader->hashcodeCached = PKIX_TRUE; } PKIX_CHECK(pkix_UnlockObject(object, plContext), PKIX_ERRORUNLOCKINGOBJECT); } } *pValue = objectHeader->hashcode; cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_ToString (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_ToString( PKIX_PL_Object *object, PKIX_PL_String **pString, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_PL_ToStringCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_PL_String *objectString = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_ToString"); PKIX_NULLCHECK_TWO(object, pString); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); /* if we don't have a cached copy from before, we create one */ if (!objectHeader->stringRep){ PKIX_UInt32 objType = objectHeader->type; if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL (PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->toStringFunction == NULL)) { PKIX_ERROR_FATAL(PKIX_UNDEFINEDCALLBACK); } func = ctEntry->toStringFunction; #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { entry = systemClasses[objType]; func = entry.toStringFunction; if (func == NULL){ func = pkix_pl_Object_ToString_Default; } } PKIX_CHECK(func(object, &objectString, plContext), PKIX_OBJECTSPECIFICFUNCTIONFAILED); if (!objectHeader->stringRep){ PKIX_CHECK(pkix_LockObject(object, plContext), PKIX_ERRORLOCKINGOBJECT); if (!objectHeader->stringRep){ /* save a cached copy */ objectHeader->stringRep = objectString; objectString = NULL; } PKIX_CHECK(pkix_UnlockObject(object, plContext), PKIX_ERRORUNLOCKINGOBJECT); } } *pString = objectHeader->stringRep; objectHeader->stringRep = NULL; cleanup: if (objectHeader) { PKIX_DECREF(objectHeader->stringRep); } PKIX_DECREF(objectString); PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_InvalidateCache (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_InvalidateCache( PKIX_PL_Object *object, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_InvalidateCache"); PKIX_NULLCHECK_ONE(object); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); PKIX_CHECK(pkix_LockObject(object, plContext), PKIX_ERRORLOCKINGOBJECT); /* invalidate hashcode */ objectHeader->hashcode = 0; objectHeader->hashcodeCached = PKIX_FALSE; PKIX_DECREF(objectHeader->stringRep); PKIX_CHECK(pkix_UnlockObject(object, plContext), PKIX_ERRORUNLOCKINGOBJECT); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Compare (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Compare( PKIX_PL_Object *firstObject, PKIX_PL_Object *secondObject, PKIX_Int32 *pResult, void *plContext) { PKIX_PL_Object *firstObjectHeader = NULL; PKIX_PL_Object *secondObjectHeader = NULL; PKIX_PL_ComparatorCallback func = NULL; pkix_ClassTable_Entry entry; PKIX_UInt32 objType; PKIX_ENTER(OBJECT, "PKIX_PL_Object_Compare"); PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader (firstObject, &firstObjectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader (secondObject, &secondObjectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); objType = firstObjectHeader->type; if (objType >= PKIX_NUMTYPES){ #ifdef PKIX_USER_OBJECT_TYPE pkix_ClassTable_Entry *ctEntry = NULL; PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); PR_Lock(classTableLock); pkixErrorResult = pkix_pl_PrimHashTable_Lookup (classTable, (void *)&objType, objType, NULL, (void **)&ctEntry, plContext); PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); PR_Unlock(classTableLock); if (pkixErrorResult){ PKIX_ERROR_FATAL(PKIX_ERRORGETTINGCLASSTABLEENTRY); } if ((ctEntry == NULL) || (ctEntry->comparator == NULL)) { PKIX_ERROR_FATAL(PKIX_UNDEFINEDCOMPARATOR); } func = ctEntry->comparator; #else PORT_Assert (0); pkixErrorCode = PKIX_UNKNOWNOBJECTTYPE; pkixErrorClass = PKIX_FATAL_ERROR; goto cleanup; #endif /* PKIX_USER_OBJECT_TYPE */ } else { /* special handling for system types */ entry = systemClasses[objType]; func = entry.comparator; if (!func){ PKIX_ERROR(PKIX_UNDEFINEDCOMPARATOR); } } PKIX_CHECK(func(firstObject, secondObject, pResult, plContext), PKIX_OBJECTSPECIFICFUNCTIONFAILED); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Lock (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Lock( PKIX_PL_Object *object, void *plContext) { PKIX_ENTER(OBJECT, "PKIX_PL_Object_Lock"); PKIX_NULLCHECK_ONE(object); PKIX_CHECK(pkix_LockObject(object, plContext), PKIX_LOCKOBJECTFAILED); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_Unlock (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_Unlock( PKIX_PL_Object *object, void *plContext) { PKIX_ENTER(OBJECT, "PKIX_PL_Object_Unlock"); PKIX_NULLCHECK_ONE(object); PKIX_CHECK(pkix_UnlockObject(object, plContext), PKIX_UNLOCKOBJECTFAILED); cleanup: PKIX_RETURN(OBJECT); } /* * FUNCTION: PKIX_PL_Object_GetType (see comments in pkix_pl_system.h) */ PKIX_Error * PKIX_PL_Object_GetType( PKIX_PL_Object *object, PKIX_UInt32 *pType, void *plContext) { PKIX_PL_Object *objectHeader = NULL; PKIX_ENTER(OBJECT, "PKIX_PL_Object_GetType"); PKIX_NULLCHECK_TWO(object, pType); /* Shift pointer from user data to object header */ PKIX_CHECK(pkix_pl_Object_GetHeader(object, &objectHeader, plContext), PKIX_RECEIVEDCORRUPTEDOBJECTARGUMENT); *pType = objectHeader->type; cleanup: PKIX_RETURN(OBJECT); }