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