/src/nss/lib/util/secitem.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | /* |
6 | | * Support routines for SECItem data structure. |
7 | | */ |
8 | | |
9 | | #include "seccomon.h" |
10 | | #include "secitem.h" |
11 | | #include "secerr.h" |
12 | | #include "secport.h" |
13 | | |
14 | | SECItem * |
15 | | SECITEM_AllocItem(PLArenaPool *arena, SECItem *item, unsigned int len) |
16 | 2.61M | { |
17 | 2.61M | SECItem *result = NULL; |
18 | 2.61M | void *mark = NULL; |
19 | | |
20 | 2.61M | if (arena != NULL) { |
21 | 1.15M | mark = PORT_ArenaMark(arena); |
22 | 1.15M | } |
23 | | |
24 | 2.61M | if (item == NULL) { |
25 | 1.11M | if (arena != NULL) { |
26 | 46.5k | result = PORT_ArenaZAlloc(arena, sizeof(SECItem)); |
27 | 1.06M | } else { |
28 | 1.06M | result = PORT_ZAlloc(sizeof(SECItem)); |
29 | 1.06M | } |
30 | 1.11M | if (result == NULL) { |
31 | 0 | goto loser; |
32 | 0 | } |
33 | 1.50M | } else { |
34 | 1.50M | PORT_Assert(item->data == NULL); |
35 | 1.50M | result = item; |
36 | 1.50M | } |
37 | | |
38 | 2.61M | result->len = len; |
39 | 2.61M | if (len) { |
40 | 2.61M | if (arena != NULL) { |
41 | 1.15M | result->data = PORT_ArenaAlloc(arena, len); |
42 | 1.45M | } else { |
43 | 1.45M | result->data = PORT_Alloc(len); |
44 | 1.45M | } |
45 | 2.61M | if (result->data == NULL) { |
46 | 0 | goto loser; |
47 | 0 | } |
48 | 2.61M | } else { |
49 | 6.76k | result->data = NULL; |
50 | 6.76k | } |
51 | | |
52 | 2.61M | if (mark) { |
53 | 1.15M | PORT_ArenaUnmark(arena, mark); |
54 | 1.15M | } |
55 | 2.61M | return (result); |
56 | | |
57 | 0 | loser: |
58 | 0 | if (arena != NULL) { |
59 | 0 | if (mark) { |
60 | 0 | PORT_ArenaRelease(arena, mark); |
61 | 0 | } |
62 | 0 | if (item != NULL) { |
63 | 0 | item->data = NULL; |
64 | 0 | item->len = 0; |
65 | 0 | } |
66 | 0 | } else { |
67 | 0 | if (result != NULL) { |
68 | 0 | SECITEM_FreeItem(result, (item == NULL) ? PR_TRUE : PR_FALSE); |
69 | 0 | } |
70 | | /* |
71 | | * If item is not NULL, the above has set item->data and |
72 | | * item->len to 0. |
73 | | */ |
74 | 0 | } |
75 | 0 | return (NULL); |
76 | 2.61M | } |
77 | | |
78 | | SECStatus |
79 | | SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, const unsigned char *data, |
80 | | unsigned int len) |
81 | 51.4k | { |
82 | 51.4k | SECItem it = { siBuffer, (unsigned char *)data, len }; |
83 | | |
84 | 51.4k | return SECITEM_CopyItem(arena, dest, &it); |
85 | 51.4k | } |
86 | | |
87 | | SECStatus |
88 | | SECITEM_ReallocItem(PLArenaPool *arena, SECItem *item, unsigned int oldlen, |
89 | | unsigned int newlen) |
90 | 0 | { |
91 | 0 | PORT_Assert(item != NULL); |
92 | 0 | if (item == NULL) { |
93 | | /* XXX Set error. But to what? */ |
94 | 0 | return SECFailure; |
95 | 0 | } |
96 | | |
97 | | /* |
98 | | * If no old length, degenerate to just plain alloc. |
99 | | */ |
100 | 0 | if (oldlen == 0) { |
101 | 0 | PORT_Assert(item->data == NULL || item->len == 0); |
102 | 0 | if (newlen == 0) { |
103 | | /* Nothing to do. Weird, but not a failure. */ |
104 | 0 | return SECSuccess; |
105 | 0 | } |
106 | 0 | item->len = newlen; |
107 | 0 | if (arena != NULL) { |
108 | 0 | item->data = PORT_ArenaAlloc(arena, newlen); |
109 | 0 | } else { |
110 | 0 | item->data = PORT_Alloc(newlen); |
111 | 0 | } |
112 | 0 | } else { |
113 | 0 | if (arena != NULL) { |
114 | 0 | item->data = PORT_ArenaGrow(arena, item->data, oldlen, newlen); |
115 | 0 | } else { |
116 | 0 | item->data = PORT_Realloc(item->data, newlen); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | 0 | if (item->data == NULL) { |
121 | 0 | return SECFailure; |
122 | 0 | } |
123 | | |
124 | 0 | return SECSuccess; |
125 | 0 | } |
126 | | |
127 | | SECStatus |
128 | | SECITEM_ReallocItemV2(PLArenaPool *arena, SECItem *item, unsigned int newlen) |
129 | 0 | { |
130 | 0 | unsigned char *newdata = NULL; |
131 | |
|
132 | 0 | PORT_Assert(item); |
133 | 0 | if (!item) { |
134 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
135 | 0 | return SECFailure; |
136 | 0 | } |
137 | | |
138 | 0 | if (item->len == newlen) { |
139 | 0 | return SECSuccess; |
140 | 0 | } |
141 | | |
142 | 0 | if (!newlen) { |
143 | 0 | if (!arena) { |
144 | 0 | PORT_Free(item->data); |
145 | 0 | } |
146 | 0 | item->data = NULL; |
147 | 0 | item->len = 0; |
148 | 0 | return SECSuccess; |
149 | 0 | } |
150 | | |
151 | 0 | if (!item->data) { |
152 | | /* allocate fresh block of memory */ |
153 | 0 | PORT_Assert(!item->len); |
154 | 0 | if (arena) { |
155 | 0 | newdata = PORT_ArenaAlloc(arena, newlen); |
156 | 0 | } else { |
157 | 0 | newdata = PORT_Alloc(newlen); |
158 | 0 | } |
159 | 0 | } else { |
160 | | /* reallocate or adjust existing block of memory */ |
161 | 0 | if (arena) { |
162 | 0 | if (item->len > newlen) { |
163 | | /* There's no need to realloc a shorter block from the arena, |
164 | | * because it would result in using even more memory! |
165 | | * Therefore we'll continue to use the old block and |
166 | | * set the item to the shorter size. |
167 | | */ |
168 | 0 | item->len = newlen; |
169 | 0 | return SECSuccess; |
170 | 0 | } |
171 | 0 | newdata = PORT_ArenaGrow(arena, item->data, item->len, newlen); |
172 | 0 | } else { |
173 | 0 | newdata = PORT_Realloc(item->data, newlen); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | if (!newdata) { |
178 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
179 | 0 | return SECFailure; |
180 | 0 | } |
181 | | |
182 | 0 | item->len = newlen; |
183 | 0 | item->data = newdata; |
184 | 0 | return SECSuccess; |
185 | 0 | } |
186 | | |
187 | | SECComparison |
188 | | SECITEM_CompareItem(const SECItem *a, const SECItem *b) |
189 | 1.65M | { |
190 | 1.65M | unsigned m; |
191 | 1.65M | int rv; |
192 | | |
193 | 1.65M | if (a == b) |
194 | 76.8k | return SECEqual; |
195 | 1.58M | if (!a || !a->len || !a->data) |
196 | 2.59k | return (!b || !b->len || !b->data) ? SECEqual : SECLessThan; |
197 | 1.57M | if (!b || !b->len || !b->data) |
198 | 18 | return SECGreaterThan; |
199 | | |
200 | 1.57M | m = ((a->len < b->len) ? a->len : b->len); |
201 | | |
202 | 1.57M | rv = PORT_Memcmp(a->data, b->data, m); |
203 | 1.57M | if (rv) { |
204 | 1.33M | return rv < 0 ? SECLessThan : SECGreaterThan; |
205 | 1.33M | } |
206 | 248k | if (a->len < b->len) { |
207 | 1.08k | return SECLessThan; |
208 | 1.08k | } |
209 | 247k | if (a->len == b->len) { |
210 | 246k | return SECEqual; |
211 | 246k | } |
212 | 1.06k | return SECGreaterThan; |
213 | 247k | } |
214 | | |
215 | | PRBool |
216 | | SECITEM_ItemsAreEqual(const SECItem *a, const SECItem *b) |
217 | 1.88M | { |
218 | 1.88M | if (a->len != b->len) |
219 | 28.8k | return PR_FALSE; |
220 | 1.85M | if (!a->len) |
221 | 757 | return PR_TRUE; |
222 | 1.85M | if (!a->data || !b->data) { |
223 | | /* avoid null pointer crash. */ |
224 | 0 | return (PRBool)(a->data == b->data); |
225 | 0 | } |
226 | 1.85M | return (PRBool)!PORT_Memcmp(a->data, b->data, a->len); |
227 | 1.85M | } |
228 | | |
229 | | SECItem * |
230 | | SECITEM_DupItem(const SECItem *from) |
231 | 879k | { |
232 | 879k | return SECITEM_ArenaDupItem(NULL, from); |
233 | 879k | } |
234 | | |
235 | | SECItem * |
236 | | SECITEM_ArenaDupItem(PLArenaPool *arena, const SECItem *from) |
237 | 925k | { |
238 | 925k | SECItem *to; |
239 | | |
240 | 925k | if (from == NULL) { |
241 | 12 | return NULL; |
242 | 12 | } |
243 | | |
244 | 925k | to = SECITEM_AllocItem(arena, NULL, from->len); |
245 | 925k | if (to == NULL) { |
246 | 0 | return NULL; |
247 | 0 | } |
248 | | |
249 | 925k | to->type = from->type; |
250 | 925k | if (to->len) { |
251 | 918k | PORT_Memcpy(to->data, from->data, to->len); |
252 | 918k | } |
253 | | |
254 | 925k | return to; |
255 | 925k | } |
256 | | |
257 | | SECStatus |
258 | | SECITEM_CopyItem(PLArenaPool *arena, SECItem *to, const SECItem *from) |
259 | 1.96M | { |
260 | 1.96M | to->type = from->type; |
261 | 1.96M | if (from->data && from->len) { |
262 | 1.69M | if (arena) { |
263 | 1.26M | to->data = (unsigned char *)PORT_ArenaAlloc(arena, from->len); |
264 | 1.26M | } else { |
265 | 433k | to->data = (unsigned char *)PORT_Alloc(from->len); |
266 | 433k | } |
267 | | |
268 | 1.69M | if (!to->data) { |
269 | 0 | return SECFailure; |
270 | 0 | } |
271 | 1.69M | PORT_Memcpy(to->data, from->data, from->len); |
272 | 1.69M | to->len = from->len; |
273 | 1.69M | } else { |
274 | | /* |
275 | | * If from->data is NULL but from->len is nonzero, this function |
276 | | * will succeed. Is this right? |
277 | | */ |
278 | 272k | to->data = 0; |
279 | 272k | to->len = 0; |
280 | 272k | } |
281 | 1.96M | return SECSuccess; |
282 | 1.96M | } |
283 | | |
284 | | void |
285 | | SECITEM_FreeItem(SECItem *zap, PRBool freeit) |
286 | 3.41M | { |
287 | 3.41M | if (zap) { |
288 | 3.31M | PORT_Free(zap->data); |
289 | 3.31M | zap->data = 0; |
290 | 3.31M | zap->len = 0; |
291 | 3.31M | if (freeit) { |
292 | 1.30M | PORT_Free(zap); |
293 | 1.30M | } |
294 | 3.31M | } |
295 | 3.41M | } |
296 | | |
297 | | void |
298 | | SECITEM_ZfreeItem(SECItem *zap, PRBool freeit) |
299 | 547k | { |
300 | 547k | if (zap) { |
301 | 547k | PORT_ZFree(zap->data, zap->len); |
302 | 547k | zap->data = 0; |
303 | 547k | zap->len = 0; |
304 | 547k | if (freeit) { |
305 | 114k | PORT_ZFree(zap, sizeof(SECItem)); |
306 | 114k | } |
307 | 547k | } |
308 | 547k | } |
309 | | /* these reroutines were taken from pkix oid.c, which is supposed to |
310 | | * replace this file some day */ |
311 | | /* |
312 | | * This is the hash function. We simply XOR the encoded form with |
313 | | * itself in sizeof(PLHashNumber)-byte chunks. Improving this |
314 | | * routine is left as an excercise for the more mathematically |
315 | | * inclined student. |
316 | | */ |
317 | | PLHashNumber PR_CALLBACK |
318 | | SECITEM_Hash(const void *key) |
319 | 2.27M | { |
320 | 2.27M | const SECItem *item = (const SECItem *)key; |
321 | 2.27M | PLHashNumber rv = 0; |
322 | | |
323 | 2.27M | PRUint8 *data = (PRUint8 *)item->data; |
324 | 2.27M | PRUint32 i; |
325 | 2.27M | PRUint8 *rvc = (PRUint8 *)&rv; |
326 | | |
327 | 22.8M | for (i = 0; i < item->len; i++) { |
328 | 20.5M | rvc[i % sizeof(rv)] ^= *data; |
329 | 20.5M | data++; |
330 | 20.5M | } |
331 | | |
332 | 2.27M | return rv; |
333 | 2.27M | } |
334 | | |
335 | | /* |
336 | | * This is the key-compare function. It simply does a lexical |
337 | | * comparison on the item data. This does not result in |
338 | | * quite the same ordering as the "sequence of numbers" order, |
339 | | * but heck it's only used internally by the hash table anyway. |
340 | | */ |
341 | | PRIntn PR_CALLBACK |
342 | | SECITEM_HashCompare(const void *k1, const void *k2) |
343 | 1.79M | { |
344 | 1.79M | const SECItem *i1 = (const SECItem *)k1; |
345 | 1.79M | const SECItem *i2 = (const SECItem *)k2; |
346 | | |
347 | 1.79M | return SECITEM_ItemsAreEqual(i1, i2); |
348 | 1.79M | } |
349 | | |
350 | | SECItemArray * |
351 | | SECITEM_AllocArray(PLArenaPool *arena, SECItemArray *array, unsigned int len) |
352 | 19 | { |
353 | 19 | SECItemArray *result = NULL; |
354 | 19 | void *mark = NULL; |
355 | | |
356 | 19 | if (array != NULL && array->items != NULL) { |
357 | 0 | PORT_Assert(0); |
358 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
359 | 0 | return NULL; |
360 | 0 | } |
361 | | |
362 | 19 | if (arena != NULL) { |
363 | 0 | mark = PORT_ArenaMark(arena); |
364 | 0 | } |
365 | | |
366 | 19 | if (array == NULL) { |
367 | 0 | if (arena != NULL) { |
368 | 0 | result = PORT_ArenaZAlloc(arena, sizeof(SECItemArray)); |
369 | 0 | } else { |
370 | 0 | result = PORT_ZAlloc(sizeof(SECItemArray)); |
371 | 0 | } |
372 | 0 | if (result == NULL) { |
373 | 0 | goto loser; |
374 | 0 | } |
375 | 19 | } else { |
376 | 19 | result = array; |
377 | 19 | } |
378 | | |
379 | 19 | result->len = len; |
380 | 19 | if (len) { |
381 | 19 | if (arena != NULL) { |
382 | 0 | result->items = PORT_ArenaZNewArray(arena, SECItem, len); |
383 | 19 | } else { |
384 | 19 | result->items = PORT_ZNewArray(SECItem, len); |
385 | 19 | } |
386 | 19 | if (result->items == NULL) { |
387 | 0 | goto loser; |
388 | 0 | } |
389 | 19 | } else { |
390 | 0 | result->items = NULL; |
391 | 0 | } |
392 | | |
393 | 19 | if (mark) { |
394 | 0 | PORT_ArenaUnmark(arena, mark); |
395 | 0 | } |
396 | 19 | return result; |
397 | | |
398 | 0 | loser: |
399 | 0 | if (arena != NULL) { |
400 | 0 | if (mark) { |
401 | 0 | PORT_ArenaRelease(arena, mark); |
402 | 0 | } |
403 | 0 | } else { |
404 | 0 | if (result != NULL && array == NULL) { |
405 | 0 | PORT_Free(result); |
406 | 0 | } |
407 | 0 | } |
408 | 0 | if (array != NULL) { |
409 | 0 | array->items = NULL; |
410 | 0 | array->len = 0; |
411 | 0 | } |
412 | 0 | return NULL; |
413 | 19 | } |
414 | | |
415 | | static void |
416 | | secitem_FreeArray(SECItemArray *array, PRBool zero_items, PRBool freeit) |
417 | 19 | { |
418 | 19 | unsigned int i; |
419 | | |
420 | 19 | if (!array) |
421 | 0 | return; |
422 | | |
423 | 19 | if (array->items) { |
424 | 38 | for (i = 0; i < array->len; ++i) { |
425 | 19 | SECItem *item = &array->items[i]; |
426 | | |
427 | 19 | if (item->data) { |
428 | 19 | if (zero_items) { |
429 | 0 | SECITEM_ZfreeItem(item, PR_FALSE); |
430 | 19 | } else { |
431 | 19 | SECITEM_FreeItem(item, PR_FALSE); |
432 | 19 | } |
433 | 19 | } |
434 | 19 | } |
435 | 19 | PORT_Free(array->items); |
436 | 19 | array->items = NULL; |
437 | 19 | array->len = 0; |
438 | 19 | } |
439 | | |
440 | 19 | if (freeit) |
441 | 0 | PORT_Free(array); |
442 | 19 | } |
443 | | |
444 | | void |
445 | | SECITEM_FreeArray(SECItemArray *array, PRBool freeit) |
446 | 19 | { |
447 | 19 | secitem_FreeArray(array, PR_FALSE, freeit); |
448 | 19 | } |
449 | | |
450 | | void |
451 | | SECITEM_ZfreeArray(SECItemArray *array, PRBool freeit) |
452 | 0 | { |
453 | 0 | secitem_FreeArray(array, PR_TRUE, freeit); |
454 | 0 | } |
455 | | |
456 | | SECItemArray * |
457 | | SECITEM_DupArray(PLArenaPool *arena, const SECItemArray *from) |
458 | 0 | { |
459 | 0 | SECItemArray *result = NULL; |
460 | 0 | unsigned int i; |
461 | 0 | void *mark = NULL; |
462 | | |
463 | | /* Require a "from" array. |
464 | | * Reject an inconsistent "from" array with NULL data and nonzero length. |
465 | | * However, allow a "from" array of zero length. |
466 | | */ |
467 | 0 | if (!from || (!from->items && from->len)) |
468 | 0 | return NULL; |
469 | | |
470 | 0 | if (arena != NULL) { |
471 | 0 | mark = PORT_ArenaMark(arena); |
472 | 0 | } |
473 | |
|
474 | 0 | result = SECITEM_AllocArray(arena, NULL, from->len); |
475 | 0 | if (!result) { |
476 | 0 | goto loser; |
477 | 0 | } |
478 | | |
479 | 0 | for (i = 0; i < from->len; ++i) { |
480 | 0 | SECStatus rv = SECITEM_CopyItem(arena, |
481 | 0 | &result->items[i], &from->items[i]); |
482 | 0 | if (rv != SECSuccess) { |
483 | 0 | goto loser; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | 0 | if (mark) { |
488 | 0 | PORT_ArenaUnmark(arena, mark); |
489 | 0 | } |
490 | 0 | return result; |
491 | | |
492 | 0 | loser: |
493 | 0 | if (arena != NULL) { |
494 | | /* Release rolls back all allocations made since the mark. */ |
495 | 0 | if (mark) { |
496 | 0 | PORT_ArenaZRelease(arena, mark); |
497 | 0 | } |
498 | 0 | } else if (result != NULL) { |
499 | | /* Non-arena path: heap-free is correct here. */ |
500 | 0 | SECITEM_ZfreeArray(result, PR_TRUE); |
501 | 0 | } |
502 | | return NULL; |
503 | 0 | } |