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