michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Support routines for SECItem data structure. michael@0: */ michael@0: michael@0: #include "seccomon.h" michael@0: #include "secitem.h" michael@0: #include "secerr.h" michael@0: #include "secport.h" michael@0: michael@0: SECItem * michael@0: SECITEM_AllocItem(PLArenaPool *arena, SECItem *item, unsigned int len) michael@0: { michael@0: SECItem *result = NULL; michael@0: void *mark = NULL; michael@0: michael@0: if (arena != NULL) { michael@0: mark = PORT_ArenaMark(arena); michael@0: } michael@0: michael@0: if (item == NULL) { michael@0: if (arena != NULL) { michael@0: result = PORT_ArenaZAlloc(arena, sizeof(SECItem)); michael@0: } else { michael@0: result = PORT_ZAlloc(sizeof(SECItem)); michael@0: } michael@0: if (result == NULL) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: PORT_Assert(item->data == NULL); michael@0: result = item; michael@0: } michael@0: michael@0: result->len = len; michael@0: if (len) { michael@0: if (arena != NULL) { michael@0: result->data = PORT_ArenaAlloc(arena, len); michael@0: } else { michael@0: result->data = PORT_Alloc(len); michael@0: } michael@0: if (result->data == NULL) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: result->data = NULL; michael@0: } michael@0: michael@0: if (mark) { michael@0: PORT_ArenaUnmark(arena, mark); michael@0: } michael@0: return(result); michael@0: michael@0: loser: michael@0: if ( arena != NULL ) { michael@0: if (mark) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: } michael@0: if (item != NULL) { michael@0: item->data = NULL; michael@0: item->len = 0; michael@0: } michael@0: } else { michael@0: if (result != NULL) { michael@0: SECITEM_FreeItem(result, (item == NULL) ? PR_TRUE : PR_FALSE); michael@0: } michael@0: /* michael@0: * If item is not NULL, the above has set item->data and michael@0: * item->len to 0. michael@0: */ michael@0: } michael@0: return(NULL); michael@0: } michael@0: michael@0: SECStatus michael@0: SECITEM_ReallocItem(PLArenaPool *arena, SECItem *item, unsigned int oldlen, michael@0: unsigned int newlen) michael@0: { michael@0: PORT_Assert(item != NULL); michael@0: if (item == NULL) { michael@0: /* XXX Set error. But to what? */ michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * If no old length, degenerate to just plain alloc. michael@0: */ michael@0: if (oldlen == 0) { michael@0: PORT_Assert(item->data == NULL || item->len == 0); michael@0: if (newlen == 0) { michael@0: /* Nothing to do. Weird, but not a failure. */ michael@0: return SECSuccess; michael@0: } michael@0: item->len = newlen; michael@0: if (arena != NULL) { michael@0: item->data = PORT_ArenaAlloc(arena, newlen); michael@0: } else { michael@0: item->data = PORT_Alloc(newlen); michael@0: } michael@0: } else { michael@0: if (arena != NULL) { michael@0: item->data = PORT_ArenaGrow(arena, item->data, oldlen, newlen); michael@0: } else { michael@0: item->data = PORT_Realloc(item->data, newlen); michael@0: } michael@0: } michael@0: michael@0: if (item->data == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: SECITEM_ReallocItemV2(PLArenaPool *arena, SECItem *item, unsigned int newlen) michael@0: { michael@0: unsigned char *newdata = NULL; michael@0: michael@0: PORT_Assert(item); michael@0: if (!item) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (item->len == newlen) { michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if (!newlen) { michael@0: if (!arena) { michael@0: PORT_Free(item->data); michael@0: } michael@0: item->data = NULL; michael@0: item->len = 0; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if (!item->data) { michael@0: /* allocate fresh block of memory */ michael@0: PORT_Assert(!item->len); michael@0: if (arena) { michael@0: newdata = PORT_ArenaAlloc(arena, newlen); michael@0: } else { michael@0: newdata = PORT_Alloc(newlen); michael@0: } michael@0: } else { michael@0: /* reallocate or adjust existing block of memory */ michael@0: if (arena) { michael@0: if (item->len > newlen) { michael@0: /* There's no need to realloc a shorter block from the arena, michael@0: * because it would result in using even more memory! michael@0: * Therefore we'll continue to use the old block and michael@0: * set the item to the shorter size. michael@0: */ michael@0: item->len = newlen; michael@0: return SECSuccess; michael@0: } michael@0: newdata = PORT_ArenaGrow(arena, item->data, item->len, newlen); michael@0: } else { michael@0: newdata = PORT_Realloc(item->data, newlen); michael@0: } michael@0: } michael@0: michael@0: if (!newdata) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: item->len = newlen; michael@0: item->data = newdata; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECComparison michael@0: SECITEM_CompareItem(const SECItem *a, const SECItem *b) michael@0: { michael@0: unsigned m; michael@0: int rv; michael@0: michael@0: if (a == b) michael@0: return SECEqual; michael@0: if (!a || !a->len || !a->data) michael@0: return (!b || !b->len || !b->data) ? SECEqual : SECLessThan; michael@0: if (!b || !b->len || !b->data) michael@0: return SECGreaterThan; michael@0: michael@0: m = ( ( a->len < b->len ) ? a->len : b->len ); michael@0: michael@0: rv = PORT_Memcmp(a->data, b->data, m); michael@0: if (rv) { michael@0: return rv < 0 ? SECLessThan : SECGreaterThan; michael@0: } michael@0: if (a->len < b->len) { michael@0: return SECLessThan; michael@0: } michael@0: if (a->len == b->len) { michael@0: return SECEqual; michael@0: } michael@0: return SECGreaterThan; michael@0: } michael@0: michael@0: PRBool michael@0: SECITEM_ItemsAreEqual(const SECItem *a, const SECItem *b) michael@0: { michael@0: if (a->len != b->len) michael@0: return PR_FALSE; michael@0: if (!a->len) michael@0: return PR_TRUE; michael@0: if (!a->data || !b->data) { michael@0: /* avoid null pointer crash. */ michael@0: return (PRBool)(a->data == b->data); michael@0: } michael@0: return (PRBool)!PORT_Memcmp(a->data, b->data, a->len); michael@0: } michael@0: michael@0: SECItem * michael@0: SECITEM_DupItem(const SECItem *from) michael@0: { michael@0: return SECITEM_ArenaDupItem(NULL, from); michael@0: } michael@0: michael@0: SECItem * michael@0: SECITEM_ArenaDupItem(PLArenaPool *arena, const SECItem *from) michael@0: { michael@0: SECItem *to; michael@0: michael@0: if ( from == NULL ) { michael@0: return(NULL); michael@0: } michael@0: michael@0: if ( arena != NULL ) { michael@0: to = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem)); michael@0: } else { michael@0: to = (SECItem *)PORT_Alloc(sizeof(SECItem)); michael@0: } michael@0: if ( to == NULL ) { michael@0: return(NULL); michael@0: } michael@0: michael@0: if ( arena != NULL ) { michael@0: to->data = (unsigned char *)PORT_ArenaAlloc(arena, from->len); michael@0: } else { michael@0: to->data = (unsigned char *)PORT_Alloc(from->len); michael@0: } michael@0: if ( to->data == NULL ) { michael@0: PORT_Free(to); michael@0: return(NULL); michael@0: } michael@0: michael@0: to->len = from->len; michael@0: to->type = from->type; michael@0: if ( to->len ) { michael@0: PORT_Memcpy(to->data, from->data, to->len); michael@0: } michael@0: michael@0: return(to); michael@0: } michael@0: michael@0: SECStatus michael@0: SECITEM_CopyItem(PLArenaPool *arena, SECItem *to, const SECItem *from) michael@0: { michael@0: to->type = from->type; michael@0: if (from->data && from->len) { michael@0: if ( arena ) { michael@0: to->data = (unsigned char*) PORT_ArenaAlloc(arena, from->len); michael@0: } else { michael@0: to->data = (unsigned char*) PORT_Alloc(from->len); michael@0: } michael@0: michael@0: if (!to->data) { michael@0: return SECFailure; michael@0: } michael@0: PORT_Memcpy(to->data, from->data, from->len); michael@0: to->len = from->len; michael@0: } else { michael@0: /* michael@0: * If from->data is NULL but from->len is nonzero, this function michael@0: * will succeed. Is this right? michael@0: */ michael@0: to->data = 0; michael@0: to->len = 0; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: void michael@0: SECITEM_FreeItem(SECItem *zap, PRBool freeit) michael@0: { michael@0: if (zap) { michael@0: PORT_Free(zap->data); michael@0: zap->data = 0; michael@0: zap->len = 0; michael@0: if (freeit) { michael@0: PORT_Free(zap); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: SECITEM_ZfreeItem(SECItem *zap, PRBool freeit) michael@0: { michael@0: if (zap) { michael@0: PORT_ZFree(zap->data, zap->len); michael@0: zap->data = 0; michael@0: zap->len = 0; michael@0: if (freeit) { michael@0: PORT_ZFree(zap, sizeof(SECItem)); michael@0: } michael@0: } michael@0: } michael@0: /* these reroutines were taken from pkix oid.c, which is supposed to michael@0: * replace this file some day */ michael@0: /* michael@0: * This is the hash function. We simply XOR the encoded form with michael@0: * itself in sizeof(PLHashNumber)-byte chunks. Improving this michael@0: * routine is left as an excercise for the more mathematically michael@0: * inclined student. michael@0: */ michael@0: PLHashNumber PR_CALLBACK michael@0: SECITEM_Hash ( const void *key) michael@0: { michael@0: const SECItem *item = (const SECItem *)key; michael@0: PLHashNumber rv = 0; michael@0: michael@0: PRUint8 *data = (PRUint8 *)item->data; michael@0: PRUint32 i; michael@0: PRUint8 *rvc = (PRUint8 *)&rv; michael@0: michael@0: for( i = 0; i < item->len; i++ ) { michael@0: rvc[ i % sizeof(rv) ] ^= *data; michael@0: data++; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * This is the key-compare function. It simply does a lexical michael@0: * comparison on the item data. This does not result in michael@0: * quite the same ordering as the "sequence of numbers" order, michael@0: * but heck it's only used internally by the hash table anyway. michael@0: */ michael@0: PRIntn PR_CALLBACK michael@0: SECITEM_HashCompare ( const void *k1, const void *k2) michael@0: { michael@0: const SECItem *i1 = (const SECItem *)k1; michael@0: const SECItem *i2 = (const SECItem *)k2; michael@0: michael@0: return SECITEM_ItemsAreEqual(i1,i2); michael@0: } michael@0: michael@0: SECItemArray * michael@0: SECITEM_AllocArray(PLArenaPool *arena, SECItemArray *array, unsigned int len) michael@0: { michael@0: SECItemArray *result = NULL; michael@0: void *mark = NULL; michael@0: michael@0: if (array != NULL && array->items != NULL) { michael@0: PORT_Assert(0); michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: michael@0: if (arena != NULL) { michael@0: mark = PORT_ArenaMark(arena); michael@0: } michael@0: michael@0: if (array == NULL) { michael@0: if (arena != NULL) { michael@0: result = PORT_ArenaZAlloc(arena, sizeof(SECItemArray)); michael@0: } else { michael@0: result = PORT_ZAlloc(sizeof(SECItemArray)); michael@0: } michael@0: if (result == NULL) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: result = array; michael@0: } michael@0: michael@0: result->len = len; michael@0: if (len) { michael@0: if (arena != NULL) { michael@0: result->items = PORT_ArenaZNewArray(arena, SECItem, len); michael@0: } else { michael@0: result->items = PORT_ZNewArray(SECItem, len); michael@0: } michael@0: if (result->items == NULL) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: result->items = NULL; michael@0: } michael@0: michael@0: if (mark) { michael@0: PORT_ArenaUnmark(arena, mark); michael@0: } michael@0: return result; michael@0: michael@0: loser: michael@0: if ( arena != NULL ) { michael@0: if (mark) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: } michael@0: } else { michael@0: if (result != NULL && array == NULL) { michael@0: PORT_Free(result); michael@0: } michael@0: } michael@0: if (array != NULL) { michael@0: array->items = NULL; michael@0: array->len = 0; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: static void michael@0: secitem_FreeArray(SECItemArray *array, PRBool zero_items, PRBool freeit) michael@0: { michael@0: unsigned int i; michael@0: michael@0: if (!array || !array->len || !array->items) michael@0: return; michael@0: michael@0: for (i = 0; i < array->len; ++i) { michael@0: SECItem *item = &array->items[i]; michael@0: michael@0: if (item->data) { michael@0: if (zero_items) { michael@0: SECITEM_ZfreeItem(item, PR_FALSE); michael@0: } else { michael@0: SECITEM_FreeItem(item, PR_FALSE); michael@0: } michael@0: } michael@0: } michael@0: PORT_Free(array->items); michael@0: array->items = NULL; michael@0: array->len = 0; michael@0: michael@0: if (freeit) michael@0: PORT_Free(array); michael@0: } michael@0: michael@0: void SECITEM_FreeArray(SECItemArray *array, PRBool freeit) michael@0: { michael@0: secitem_FreeArray(array, PR_FALSE, freeit); michael@0: } michael@0: michael@0: void SECITEM_ZfreeArray(SECItemArray *array, PRBool freeit) michael@0: { michael@0: secitem_FreeArray(array, PR_TRUE, freeit); michael@0: } michael@0: michael@0: SECItemArray * michael@0: SECITEM_DupArray(PLArenaPool *arena, const SECItemArray *from) michael@0: { michael@0: SECItemArray *result; michael@0: unsigned int i; michael@0: michael@0: /* Require a "from" array. michael@0: * Reject an inconsistent "from" array with NULL data and nonzero length. michael@0: * However, allow a "from" array of zero length. michael@0: */ michael@0: if (!from || (!from->items && from->len)) michael@0: return NULL; michael@0: michael@0: result = SECITEM_AllocArray(arena, NULL, from->len); michael@0: if (!result) michael@0: return NULL; michael@0: michael@0: for (i = 0; i < from->len; ++i) { michael@0: SECStatus rv = SECITEM_CopyItem(arena, michael@0: &result->items[i], &from->items[i]); michael@0: if (rv != SECSuccess) { michael@0: SECITEM_ZfreeArray(result, PR_TRUE); michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: }