]> git.saurik.com Git - apple/cf.git/blob - CFUUID.c
31ea3e2e1dd7f73a14252618283ba19a35ce4b3d
[apple/cf.git] / CFUUID.c
1 /*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFUUID.c
24 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
25 Responsibility: Doug Davidson
26 */
27
28 #include <CoreFoundation/CFUUID.h>
29 #include "CFInternal.h"
30 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
31 #include <uuid/uuid.h>
32 #endif
33
34 static CFMutableDictionaryRef _uniquedUUIDs = NULL;
35 static CFSpinLock_t CFUUIDGlobalDataLock = CFSpinLockInit;
36
37 struct __CFUUID {
38 CFRuntimeBase _base;
39 CFUUIDBytes _bytes;
40 };
41
42 static Boolean __CFisEqualUUIDBytes(const void *ptr1, const void *ptr2) {
43 CFUUIDBytes *p1 = (CFUUIDBytes *)ptr1;
44 CFUUIDBytes *p2 = (CFUUIDBytes *)ptr2;
45
46 return (((p1->byte0 == p2->byte0) && (p1->byte1 == p2->byte1) && (p1->byte2 == p2->byte2) && (p1->byte3 == p2->byte3) && (p1->byte4 == p2->byte4) && (p1->byte5 == p2->byte5) && (p1->byte6 == p2->byte6) && (p1->byte7 == p2->byte7) && (p1->byte8 == p2->byte8) && (p1->byte9 == p2->byte9) && (p1->byte10 == p2->byte10) && (p1->byte11 == p2->byte11) && (p1->byte12 == p2->byte12) && (p1->byte13 == p2->byte13) && (p1->byte14 == p2->byte14) && (p1->byte15 == p2->byte15)) ? true : false);
47 }
48
49 static CFHashCode __CFhashUUIDBytes(const void *ptr) {
50 return CFHashBytes((uint8_t *)ptr, 16);
51 }
52
53 /*
54 * GC implementation of a weak set specifically designed for UUID
55 */
56
57 #define LOCK() __CFSpinLock(&CFUUIDGlobalDataLock)
58 #define UNLOCK() __CFSpinUnlock(&CFUUIDGlobalDataLock)
59
60 #define MALLOC(x) CFAllocatorAllocate(kCFAllocatorSystemDefault, x, 0)
61 #define FREE(x) CFAllocatorDeallocate(kCFAllocatorSystemDefault, x)
62 #define HASH(x) CFHashBytes((uint8_t *)x, 16)
63
64 #define READWEAK(location) auto_read_weak_reference(auto_zone(), (void**)location)
65 #define WRITEWEAK(location, value) auto_assign_weak_reference(auto_zone(), value, (void **)location, NULL)
66
67 typedef struct {
68 unsigned long count, size;
69 struct __CFUUID **weakPtrs;
70 } _UUIDWeakSet_t;
71
72 static _UUIDWeakSet_t _UUIDWeakSet;
73
74 static void grow_has_lock(void);
75
76 // enter if not already present
77 static void enter_has_lock(struct __CFUUID *candidate) {
78 if (!candidate) return;
79 _UUIDWeakSet_t *table = &_UUIDWeakSet;
80 if (!table->size) grow_has_lock();
81 unsigned long int hashValue = HASH(&candidate->_bytes) & (table->size-1);
82 struct __CFUUID *result = table->weakPtrs[hashValue];
83 while (1) {
84 if (result == (void *)0x1 || result == NULL) {
85 table->weakPtrs[hashValue] = NULL; // so that we don't try to unregister 0x1
86 WRITEWEAK(&table->weakPtrs[hashValue], (void *)candidate);
87 ++table->count;
88 break;
89 }
90 if (result) result = (struct __CFUUID *)READWEAK(&table->weakPtrs[hashValue]);
91 if (result) {
92 // see if it is equal to candidate
93 if (__CFisEqualUUIDBytes(&result->_bytes, &candidate->_bytes)) {
94 // keep first one. There is a race if two threads both fail to find
95 // a candidate uuid then both try decide to create and enter one.
96 // Under non-GC one of them simply leaks.
97 break;
98 }
99 } else {
100 // was zeroed by collector. Use this slot.
101 continue;
102 }
103 // move on
104 if (++hashValue >= table->size) hashValue = 0;
105 result = table->weakPtrs[hashValue];
106 }
107 }
108
109 static void *find_has_lock(CFUUIDBytes *bytes) {
110 if (!bytes) return NULL;
111 _UUIDWeakSet_t *table = &_UUIDWeakSet;
112 if (!table->size) return NULL; // no entries
113 unsigned long int hashValue = HASH(bytes) & (table->size-1);
114 struct __CFUUID *result = table->weakPtrs[hashValue];
115 while (1) {
116 if (result == (void *)0x1) break;
117 if (result) result = (struct __CFUUID *)READWEAK(&table->weakPtrs[hashValue]);
118 if (result) {
119 // see if it is equal to bytes
120 if (__CFisEqualUUIDBytes(&result->_bytes, bytes)) return result;
121 }
122 // move on
123 if (++hashValue >= table->size) hashValue = 0;
124 result = table->weakPtrs[hashValue];
125 }
126 return NULL;
127 }
128
129
130 static void grow_has_lock() {
131 _UUIDWeakSet_t *table = &_UUIDWeakSet;
132 if (table->size == 0) {
133 table->size = 16;
134 table->weakPtrs = (struct __CFUUID **)MALLOC(sizeof(struct __CFUUID *)*table->size);
135 for (int i = 0; i < table->size; ++i) table->weakPtrs[i] = (struct __CFUUID *)0x1;
136 table->count = 0;
137 return;
138 }
139 table->count = 0;
140 table->size = table->size*2;
141 struct __CFUUID **oldPtrs = table->weakPtrs;
142 table->weakPtrs = (struct __CFUUID **)MALLOC(sizeof(struct __CFUUID *)*table->size);
143 for (int i = 0; i < table->size; ++i) table->weakPtrs[i] = (struct __CFUUID *)0x1;
144 for (int i = 0; i < table->size / 2; ++i) {
145 if (oldPtrs[i] == (struct __CFUUID *)0x1) continue; // available field, ignore
146 if (oldPtrs[i] == NULL) continue; // zero'ed by collector, ignore
147 enter_has_lock((struct __CFUUID *)READWEAK(&oldPtrs[i])); // read, then enter (but enter must check for NULL)
148 WRITEWEAK(&oldPtrs[i], NULL); // unregister
149 }
150 FREE(oldPtrs);
151 }
152
153 /***** end of weak set */
154
155 static void __CFUUIDAddUniqueUUID(CFUUIDRef uuid) {
156 CFDictionaryKeyCallBacks __CFUUIDBytesDictionaryKeyCallBacks = {0, NULL, NULL, NULL, __CFisEqualUUIDBytes, __CFhashUUIDBytes};
157 CFDictionaryValueCallBacks __CFnonRetainedUUIDDictionaryValueCallBacks = {0, NULL, NULL, CFCopyDescription, CFEqual};
158
159 __CFSpinLock(&CFUUIDGlobalDataLock);
160 if (kCFUseCollectableAllocator) {
161 enter_has_lock((struct __CFUUID *)uuid);
162 if (_UUIDWeakSet.count > (3 * _UUIDWeakSet.size / 4)) grow_has_lock();
163 __CFSpinUnlock(&CFUUIDGlobalDataLock);
164 return;
165 }
166 if (!_uniquedUUIDs) _uniquedUUIDs = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &__CFUUIDBytesDictionaryKeyCallBacks, &__CFnonRetainedUUIDDictionaryValueCallBacks);
167 CFDictionarySetValue(_uniquedUUIDs, &(uuid->_bytes), uuid);
168 __CFSpinUnlock(&CFUUIDGlobalDataLock);
169 }
170
171 static void __CFUUIDRemoveUniqueUUID(CFUUIDRef uuid) {
172 __CFSpinLock(&CFUUIDGlobalDataLock);
173 if (_uniquedUUIDs) CFDictionaryRemoveValue(_uniquedUUIDs, &(uuid->_bytes));
174 __CFSpinUnlock(&CFUUIDGlobalDataLock);
175 }
176
177 static CFUUIDRef __CFUUIDGetUniquedUUID(CFUUIDBytes *bytes) {
178 CFUUIDRef uuid = NULL;
179 __CFSpinLock(&CFUUIDGlobalDataLock);
180 if (kCFUseCollectableAllocator) {
181 uuid = (CFUUIDRef)find_has_lock(bytes);
182 } else if (_uniquedUUIDs) {
183 uuid = (CFUUIDRef)CFDictionaryGetValue(_uniquedUUIDs, bytes);
184 }
185 __CFSpinUnlock(&CFUUIDGlobalDataLock);
186 return uuid;
187 }
188
189 static void __CFUUIDDeallocate(CFTypeRef cf) {
190 if (kCFUseCollectableAllocator) return;
191
192 struct __CFUUID *uuid = (struct __CFUUID *)cf;
193 __CFUUIDRemoveUniqueUUID(uuid);
194 }
195
196 static CFStringRef __CFUUIDCopyDescription(CFTypeRef cf) {
197 CFStringRef uuidStr = CFUUIDCreateString(CFGetAllocator(cf), (CFUUIDRef)cf);
198 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFUUID %p> %@"), cf, uuidStr);
199 CFRelease(uuidStr);
200 return desc;
201 }
202
203 static CFStringRef __CFUUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
204 return CFUUIDCreateString(CFGetAllocator(cf), (CFUUIDRef)cf);
205 }
206
207 static CFTypeID __kCFUUIDTypeID = _kCFRuntimeNotATypeID;
208
209 static const CFRuntimeClass __CFUUIDClass = {
210 0,
211 "CFUUID",
212 NULL, // init
213 NULL, // copy
214 __CFUUIDDeallocate,
215 NULL, // equal
216 NULL, // hash
217 __CFUUIDCopyFormattingDescription,
218 __CFUUIDCopyDescription
219 };
220
221 __private_extern__ void __CFUUIDInitialize(void) {
222 __kCFUUIDTypeID = _CFRuntimeRegisterClass(&__CFUUIDClass);
223 }
224
225 CFTypeID CFUUIDGetTypeID(void) {
226 return __kCFUUIDTypeID;
227 }
228
229 static CFUUIDRef __CFUUIDCreateWithBytesPrimitive(CFAllocatorRef allocator, CFUUIDBytes bytes, Boolean isConst) {
230 struct __CFUUID *uuid = (struct __CFUUID *)__CFUUIDGetUniquedUUID(&bytes);
231 if (!uuid) {
232 size_t size;
233 size = sizeof(struct __CFUUID) - sizeof(CFRuntimeBase);
234 uuid = (struct __CFUUID *)_CFRuntimeCreateInstance(kCFUseCollectableAllocator ? kCFAllocatorSystemDefault : allocator, __kCFUUIDTypeID, size, NULL);
235
236 if (!uuid) return NULL;
237
238 uuid->_bytes = bytes;
239 __CFUUIDAddUniqueUUID(uuid);
240 } else if (!isConst) {
241 CFRetain(uuid);
242 }
243 return (CFUUIDRef)uuid;
244 }
245
246 #if DEPLOYMENT_TARGET_WINDOWS
247 #include <Rpc.h>
248 #endif
249
250 CFUUIDRef CFUUIDCreate(CFAllocatorRef alloc) {
251 /* Create a new bytes struct and then call the primitive. */
252 CFUUIDBytes bytes;
253 uint32_t retval = 0;
254
255 __CFSpinLock(&CFUUIDGlobalDataLock);
256 #if DEPLOYMENT_TARGET_WINDOWS
257 UUID u;
258 long rStatus = UuidCreate(&u);
259 if (RPC_S_OK != rStatus && RPC_S_UUID_LOCAL_ONLY != rStatus) retval = 1;
260 memmove(&bytes, &u, sizeof(bytes));
261 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
262 static Boolean useV1UUIDs = false, checked = false;
263 uuid_t uuid;
264 if (!checked) {
265 const char *value = __CFgetenv("CFUUIDVersionNumber");
266 if (value) {
267 if (1 == strtoul_l(value, NULL, 0, NULL)) useV1UUIDs = true;
268 } else {
269 if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionTiger)) useV1UUIDs = true;
270 }
271 checked = true;
272 }
273 if (useV1UUIDs) uuid_generate_time(uuid); else uuid_generate_random(uuid);
274 memcpy(&bytes, uuid, sizeof(uuid));
275 #else
276 retval = 1;
277 #endif
278 __CFSpinUnlock(&CFUUIDGlobalDataLock);
279
280 return (retval == 0) ? __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false) : NULL;
281 }
282
283 CFUUIDRef CFUUIDCreateWithBytes(CFAllocatorRef alloc, uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4, uint8_t byte5, uint8_t byte6, uint8_t byte7, uint8_t byte8, uint8_t byte9, uint8_t byte10, uint8_t byte11, uint8_t byte12, uint8_t byte13, uint8_t byte14, uint8_t byte15) {
284 CFUUIDBytes bytes;
285 // CodeWarrior can't handle the structure assignment of bytes, so we must explode this - REW, 10/8/99
286 bytes.byte0 = byte0;
287 bytes.byte1 = byte1;
288 bytes.byte2 = byte2;
289 bytes.byte3 = byte3;
290 bytes.byte4 = byte4;
291 bytes.byte5 = byte5;
292 bytes.byte6 = byte6;
293 bytes.byte7 = byte7;
294 bytes.byte8 = byte8;
295 bytes.byte9 = byte9;
296 bytes.byte10 = byte10;
297 bytes.byte11 = byte11;
298 bytes.byte12 = byte12;
299 bytes.byte13 = byte13;
300 bytes.byte14 = byte14;
301 bytes.byte15 = byte15;
302
303 return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false);
304 }
305
306 static void _intToHexChars(UInt32 in, UniChar *out, int digits) {
307 int shift;
308 UInt32 d;
309
310 while (--digits >= 0) {
311 shift = digits << 2;
312 d = 0x0FL & (in >> shift);
313 if (d <= 9) {
314 *out++ = (UniChar)'0' + d;
315 } else {
316 *out++ = (UniChar)'A' + (d - 10);
317 }
318 }
319 }
320
321 static uint8_t _byteFromHexChars(UniChar *in) {
322 uint8_t result = 0;
323 UniChar c;
324 uint8_t d;
325 CFIndex i;
326
327 for (i=0; i<2; i++) {
328 c = in[i];
329 if ((c >= (UniChar)'0') && (c <= (UniChar)'9')) {
330 d = c - (UniChar)'0';
331 } else if ((c >= (UniChar)'a') && (c <= (UniChar)'f')) {
332 d = c - ((UniChar)'a' - 10);
333 } else if ((c >= (UniChar)'A') && (c <= (UniChar)'F')) {
334 d = c - ((UniChar)'A' - 10);
335 } else {
336 return 0;
337 }
338 result = (result << 4) | d;
339 }
340
341 return result;
342 }
343
344 CF_INLINE Boolean _isHexChar(UniChar c) {
345 return ((((c >= (UniChar)'0') && (c <= (UniChar)'9')) || ((c >= (UniChar)'a') && (c <= (UniChar)'f')) || ((c >= (UniChar)'A') && (c <= (UniChar)'F'))) ? true : false);
346 }
347
348 #define READ_A_BYTE(into) if (i+1 < len) { \
349 (into) = _byteFromHexChars(&(chars[i])); \
350 i+=2; \
351 }
352
353 CFUUIDRef CFUUIDCreateFromString(CFAllocatorRef alloc, CFStringRef uuidStr) {
354 /* Parse the string into a bytes struct and then call the primitive. */
355 CFUUIDBytes bytes;
356 UniChar chars[100];
357 CFIndex len;
358 CFIndex i = 0;
359
360 if (uuidStr == NULL) return NULL;
361
362 len = CFStringGetLength(uuidStr);
363 if (len > 100) {
364 len = 100;
365 } else if (len == 0) {
366 return NULL;
367 }
368 CFStringGetCharacters(uuidStr, CFRangeMake(0, len), chars);
369 memset((void *)&bytes, 0, sizeof(bytes));
370
371 /* Skip initial random stuff */
372 while (!_isHexChar(chars[i]) && i < len) i++;
373
374 READ_A_BYTE(bytes.byte0);
375 READ_A_BYTE(bytes.byte1);
376 READ_A_BYTE(bytes.byte2);
377 READ_A_BYTE(bytes.byte3);
378 i++;
379
380 READ_A_BYTE(bytes.byte4);
381 READ_A_BYTE(bytes.byte5);
382 i++;
383
384 READ_A_BYTE(bytes.byte6);
385 READ_A_BYTE(bytes.byte7);
386 i++;
387
388 READ_A_BYTE(bytes.byte8);
389 READ_A_BYTE(bytes.byte9);
390 i++;
391
392 READ_A_BYTE(bytes.byte10);
393 READ_A_BYTE(bytes.byte11);
394 READ_A_BYTE(bytes.byte12);
395 READ_A_BYTE(bytes.byte13);
396 READ_A_BYTE(bytes.byte14);
397 READ_A_BYTE(bytes.byte15);
398
399 return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false);
400 }
401
402 CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid) {
403 CFMutableStringRef str = CFStringCreateMutable(alloc, 0);
404 UniChar buff[12];
405
406 // First segment (4 bytes, 8 digits + 1 dash)
407 _intToHexChars(uuid->_bytes.byte0, buff, 2);
408 _intToHexChars(uuid->_bytes.byte1, &(buff[2]), 2);
409 _intToHexChars(uuid->_bytes.byte2, &(buff[4]), 2);
410 _intToHexChars(uuid->_bytes.byte3, &(buff[6]), 2);
411 buff[8] = (UniChar)'-';
412 CFStringAppendCharacters(str, buff, 9);
413
414 // Second segment (2 bytes, 4 digits + 1 dash)
415 _intToHexChars(uuid->_bytes.byte4, buff, 2);
416 _intToHexChars(uuid->_bytes.byte5, &(buff[2]), 2);
417 buff[4] = (UniChar)'-';
418 CFStringAppendCharacters(str, buff, 5);
419
420 // Third segment (2 bytes, 4 digits + 1 dash)
421 _intToHexChars(uuid->_bytes.byte6, buff, 2);
422 _intToHexChars(uuid->_bytes.byte7, &(buff[2]), 2);
423 buff[4] = (UniChar)'-';
424 CFStringAppendCharacters(str, buff, 5);
425
426 // Fourth segment (2 bytes, 4 digits + 1 dash)
427 _intToHexChars(uuid->_bytes.byte8, buff, 2);
428 _intToHexChars(uuid->_bytes.byte9, &(buff[2]), 2);
429 buff[4] = (UniChar)'-';
430 CFStringAppendCharacters(str, buff, 5);
431
432 // Fifth segment (6 bytes, 12 digits)
433 _intToHexChars(uuid->_bytes.byte10, buff, 2);
434 _intToHexChars(uuid->_bytes.byte11, &(buff[2]), 2);
435 _intToHexChars(uuid->_bytes.byte12, &(buff[4]), 2);
436 _intToHexChars(uuid->_bytes.byte13, &(buff[6]), 2);
437 _intToHexChars(uuid->_bytes.byte14, &(buff[8]), 2);
438 _intToHexChars(uuid->_bytes.byte15, &(buff[10]), 2);
439 CFStringAppendCharacters(str, buff, 12);
440
441 return str;
442 }
443
444 CFUUIDRef CFUUIDGetConstantUUIDWithBytes(CFAllocatorRef alloc, uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4, uint8_t byte5, uint8_t byte6, uint8_t byte7, uint8_t byte8, uint8_t byte9, uint8_t byte10, uint8_t byte11, uint8_t byte12, uint8_t byte13, uint8_t byte14, uint8_t byte15) {
445 CFUUIDBytes bytes;
446 // CodeWarrior can't handle the structure assignment of bytes, so we must explode this - REW, 10/8/99
447 bytes.byte0 = byte0;
448 bytes.byte1 = byte1;
449 bytes.byte2 = byte2;
450 bytes.byte3 = byte3;
451 bytes.byte4 = byte4;
452 bytes.byte5 = byte5;
453 bytes.byte6 = byte6;
454 bytes.byte7 = byte7;
455 bytes.byte8 = byte8;
456 bytes.byte9 = byte9;
457 bytes.byte10 = byte10;
458 bytes.byte11 = byte11;
459 bytes.byte12 = byte12;
460 bytes.byte13 = byte13;
461 bytes.byte14 = byte14;
462 bytes.byte15 = byte15;
463
464 return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, true);
465 }
466
467 CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) {
468 return uuid->_bytes;
469 }
470
471 CF_EXPORT CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) {
472 return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false);
473 }
474
475 #undef READ_A_BYTE
476