/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the PKIX-C library.
 *
 * The Initial Developer of the Original Code is
 * Sun Microsystems, Inc.
 * Portions created by the Initial Developer are
 * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Contributor(s):
 *   Sun Microsystems, Inc.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/*
 * 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: <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)object;

        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(&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(&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);
}
