1/*
2 * Copyright (c) 2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2011 - 2013 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "baselocl.h"
37
38#include <CoreFoundation/CoreFoundation.h>
39#include <CoreFoundation/CFRuntime.h>
40#include <dispatch/dispatch.h>
41#include <syslog.h>
42
43static void *
44_heim_create_cf_instance(CFTypeID typeID, CFIndex size, char *category)
45{
46    heim_assert(size >= sizeof(CFRuntimeBase), "cf runtime size too small");
47    CFTypeRef type = _CFRuntimeCreateInstance(NULL, typeID, size - sizeof(struct heim_base), (unsigned char *)category);
48    if (type)
49	memset(((uint8_t *)type) + sizeof(struct heim_base), 0, size - sizeof(struct heim_base));
50    return (void *)type;
51}
52/*
53 *
54 */
55
56const char *__crashreporter_info__ = NULL;
57asm(".desc ___crashreporter_info__, 0x10");
58
59
60/**
61 * Abort and log the failure (using syslog)
62 */
63
64void
65heim_abort(const char *fmt, ...)
66    HEIMDAL_NORETURN_ATTRIBUTE
67    HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 2))
68{
69    va_list ap;
70    va_start(ap, fmt);
71    heim_abortv(fmt, ap);
72    va_end(ap);
73}
74
75/**
76 * Abort and log the failure (using syslog)
77 */
78
79void
80heim_abortv(const char *fmt, va_list ap)
81    HEIMDAL_NORETURN_ATTRIBUTE
82    HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 0))
83{
84    char *str = NULL;
85    int ret;
86
87    ret = vasprintf(&str, fmt, ap);
88    if (ret > 0 && str) {
89	syslog(LOG_ERR, "heim_abort: %s", str);
90
91	__crashreporter_info__ = str;
92    }
93    abort();
94}
95
96void
97heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
98{
99    dispatch_once_f(once, ctx, func);
100}
101
102
103void *
104heim_retain(void *ptr)
105{
106    if (ptr)
107	CFRetain(ptr);
108    return ptr;
109}
110
111void
112heim_release(void *ptr)
113{
114    if (ptr)
115	CFRelease(ptr);
116}
117
118heim_tid_t
119heim_get_tid(heim_object_t ptr)
120{
121    return (heim_tid_t)CFGetTypeID(ptr);
122}
123
124unsigned long
125heim_get_hash(heim_object_t ptr)
126{
127    return CFHash(ptr);
128}
129
130int
131heim_cmp(heim_object_t a, heim_object_t b)
132{
133    if (CFEqual(a, b))
134	return 0;
135
136    uintptr_t ai = (uintptr_t)a;
137    uintptr_t bi = (uintptr_t)b;
138
139    heim_assert(ai != bi, "pointers are the same ?");
140
141    while (((int)(ai - bi)) == 0) {
142	ai = ai >> 8;
143	bi = bi >> 8;
144    }
145    int diff = (int)(ai - bi);
146    heim_assert(diff != 0, "pointers are the same ?");
147
148    return diff;
149
150}
151
152heim_array_t
153heim_array_create(void)
154{
155    return (heim_array_t)CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
156}
157
158heim_tid_t
159heim_array_get_type_id(void)
160{
161    return (heim_tid_t)CFDictionaryGetTypeID();
162}
163
164int
165heim_array_append_value(heim_array_t array, heim_object_t object)
166{
167    CFArrayAppendValue((CFMutableArrayRef)array, object);
168    return 0;
169}
170
171void
172heim_array_iterate_f(heim_array_t array,
173		     void *ctx,
174		     heim_array_iterator_f_t fn)
175{
176    CFIndex n, count = CFArrayGetCount((CFArrayRef)array);
177    int stop = 0;
178    for (n = 0; !stop && n < count; n++)
179	fn((heim_array_t)CFArrayGetValueAtIndex((CFArrayRef)array, n), &stop, ctx);
180}
181
182#if __BLOCKS__
183void
184heim_array_iterate(heim_array_t array, heim_array_iterator_t fn)
185{
186    CFIndex n, count = CFArrayGetCount((CFArrayRef)array);
187    int stop = 0;
188    for (n = 0; !stop && n < count; n++)
189	fn((heim_array_t)CFArrayGetValueAtIndex((CFArrayRef)array, n), &stop);
190}
191#endif
192
193size_t
194heim_array_get_length(heim_array_t array)
195{
196    return CFArrayGetCount((CFArrayRef)array);
197}
198
199heim_object_t
200heim_array_copy_value(heim_array_t array, size_t idx)
201{
202    CFTypeRef value = CFArrayGetValueAtIndex((CFArrayRef)array, idx);
203    if (value)
204	CFRetain(value);
205    return (heim_object_t)value;
206}
207
208void
209heim_array_delete_value(heim_array_t array, size_t idx)
210{
211    CFArrayRemoveValueAtIndex((CFMutableArrayRef)array, idx);
212}
213
214#if __BLOCKS__
215void
216heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
217{
218    size_t n = 0;
219
220    while (n < CFArrayGetCount((CFArrayRef)array)) {
221	if (block((heim_array_t)CFArrayGetValueAtIndex((CFArrayRef)array, n))) {
222	    heim_array_delete_value(array, n);
223	} else {
224	    n++;
225	}
226    }
227}
228#endif
229
230int
231heim_array_contains_value(heim_array_t array, heim_object_t value)
232{
233    return CFArrayContainsValue((CFArrayRef)array, CFRangeMake(0, CFArrayGetCount((CFArrayRef)array)), value);
234}
235
236heim_dict_t
237heim_dict_create(size_t size)
238{
239    return (heim_dict_t)CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
240}
241
242heim_tid_t
243heim_dict_get_type_id(void)
244{
245    return (heim_tid_t)CFDictionaryGetTypeID();
246}
247
248heim_object_t
249heim_dict_copy_value(heim_dict_t dict, heim_object_t key)
250{
251    heim_object_t obj = (heim_object_t)CFDictionaryGetValue((CFDictionaryRef)dict, key);
252    if (obj)
253	heim_retain(obj);
254    return obj;
255}
256
257int
258heim_dict_add_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
259{
260    CFDictionarySetValue((CFMutableDictionaryRef)dict, key, value);
261    return 0;
262}
263
264void
265heim_dict_delete_key(heim_dict_t dict, heim_object_t key)
266{
267    CFDictionaryRemoveValue((CFMutableDictionaryRef)dict, key);
268}
269
270struct dict_iter {
271    heim_dict_t dict;
272    union {
273	heim_dict_iterator_f_t func;
274	void (^block)(heim_object_t, heim_object_t);
275    } u;
276    void *arg;
277};
278
279static void
280dict_iterate_f(const void *key, const void *value, void *context)
281{
282    struct dict_iter *ctx = context;
283    ctx->u.func(ctx->dict, (heim_object_t)key, (heim_object_t)value, ctx->arg);
284
285}
286
287void
288heim_dict_iterate_f(heim_dict_t dict, heim_dict_iterator_f_t func, void *arg)
289{
290    struct dict_iter ctx = {
291	.dict = dict,
292	.u.func = func,
293	.arg = arg
294    };
295    CFDictionaryApplyFunction((CFDictionaryRef)dict, dict_iterate_f, &ctx);
296}
297
298#ifdef __BLOCKS__
299
300static void
301dict_iterate_b(const void *key, const void *value, void *context)
302{
303    struct dict_iter *ctx = context;
304    ctx->u.block((heim_object_t)key, (heim_object_t)value);
305
306}
307
308void
309heim_dict_iterate(heim_dict_t dict, void (^func)(heim_object_t, heim_object_t))
310{
311    struct dict_iter ctx = {
312	.u.block = func
313    };
314    CFDictionaryApplyFunction((CFDictionaryRef)dict, dict_iterate_b, &ctx);
315
316}
317#endif
318
319heim_string_t
320heim_string_create(const char *string)
321{
322    return (heim_string_t)CFStringCreateWithCString(NULL, string, kCFStringEncodingUTF8);
323}
324
325heim_tid_t
326heim_string_get_type_id(void)
327{
328    return (heim_tid_t)CFStringGetTypeID();
329}
330
331char *
332heim_string_copy_utf8(heim_string_t string)
333{
334    CFIndex len;
335    char *str;
336
337    str = (char *) CFStringGetCStringPtr((CFStringRef)string, kCFStringEncodingUTF8);
338    if (str)
339	return strdup(str);
340
341    len = CFStringGetLength((CFStringRef)string);
342    len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
343    str = malloc(len);
344    if (str == NULL)
345	return NULL;
346
347    if (!CFStringGetCString ((CFStringRef)string, str, len, kCFStringEncodingUTF8)) {
348	free (str);
349	return NULL;
350    }
351    return str;
352}
353
354heim_data_t
355heim_data_create(void *indata, size_t len)
356{
357    return (heim_data_t)CFDataCreate(NULL, indata, len);
358}
359
360heim_tid_t
361heim_data_get_type_id(void)
362{
363    return (heim_tid_t)CFDataGetTypeID();
364}
365
366const void *
367heim_data_get_bytes(heim_data_t data)
368{
369    return CFDataGetBytePtr((CFDataRef)data);
370}
371
372size_t
373heim_data_get_length(heim_data_t data)
374{
375    return CFDataGetLength((CFDataRef)data);
376}
377
378static void
379heim_alloc_release(CFTypeRef type)
380{
381    struct heim_base_uniq *mem = (struct heim_base_uniq *)type;
382    if (mem->dealloc)
383	mem->dealloc(mem);
384}
385
386static CFTypeID
387uniq_get_type_id(void)
388{
389    static CFTypeID haid = _kCFRuntimeNotATypeID;
390    static dispatch_once_t inited;
391
392    dispatch_once(&inited, ^{
393	    static const CFRuntimeClass naclass = {
394		0,
395		"heim-alloc",
396		NULL,
397		NULL,
398		heim_alloc_release,
399		NULL,
400		NULL,
401		NULL,
402		NULL
403	    };
404	    haid = _CFRuntimeRegisterClass(&naclass);
405	});
406
407    return haid;
408}
409
410heim_object_t
411heim_uniq_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
412{
413    CFTypeID id = uniq_get_type_id();
414    struct heim_base_uniq *mem;
415
416    heim_assert(size >= sizeof(struct heim_base_uniq), "uniq: size too small");
417
418    if (id == _kCFRuntimeNotATypeID)
419	return NULL;
420
421    mem = _heim_create_cf_instance(id, size, "base-uniq");
422    if (mem) {
423	mem->dealloc = dealloc;
424	mem->name = name;
425    }
426
427    return mem;
428}
429
430struct heim_error {
431    struct heim_base base;
432    int error_code;
433    heim_string_t msg;
434    struct heim_error *next;
435};
436
437static void
438heim_error_release(CFTypeRef ptr)
439{
440    struct heim_error *p = (struct heim_error *)ptr;
441    heim_release(p->msg);
442    heim_release(p->next);
443}
444
445
446static CFTypeID
447heim_error_get_type_id(void)
448{
449    static CFTypeID haid = _kCFRuntimeNotATypeID;
450    static dispatch_once_t inited;
451
452    dispatch_once(&inited, ^{
453	static const CFRuntimeClass naclass = {
454	    0,
455	    "heim-error",
456	    NULL,
457	    NULL,
458	    heim_error_release,
459	    NULL,
460	    NULL,
461	    NULL,
462	    NULL
463	};
464	haid = _CFRuntimeRegisterClass(&naclass);
465    });
466
467    return haid;
468}
469
470
471heim_error_t
472heim_error_create(int error_code, const char *fmt, ...)
473{
474    heim_error_t e;
475    va_list ap;
476
477    va_start(ap, fmt);
478    e = heim_error_createv(error_code, fmt, ap);
479    va_end(ap);
480
481    return e;
482}
483
484heim_error_t
485heim_error_createv(int error_code, const char *fmt, va_list ap)
486{
487    CFTypeID id = heim_error_get_type_id();
488    heim_error_t e;
489    char *str;
490    int len;
491
492    if (id == _kCFRuntimeNotATypeID)
493	return NULL;
494
495    str = malloc(1024);
496    if (str == NULL)
497        return NULL;
498    len = vsnprintf(str, 1024, fmt, ap);
499    if (len < 0) {
500        free(str);
501	return NULL;
502    }
503
504    e = _heim_create_cf_instance(id, sizeof(struct heim_error), "heim-error");
505    if (e) {
506	e->msg = heim_string_create(str);
507	e->error_code = error_code;
508	e->next = NULL;
509    }
510    free(str);
511
512    return e;
513}
514
515heim_string_t
516heim_error_copy_string(heim_error_t error)
517{
518    /* XXX concat all strings */
519    return heim_retain(error->msg);
520}
521
522int
523heim_error_get_code(heim_error_t error)
524{
525    return error->error_code;
526}
527
528heim_error_t
529heim_error_append(heim_error_t top, heim_error_t append)
530{
531    if (top->next)
532	heim_release(top->next);
533    top->next = heim_retain(append);
534    return top;
535}
536
537heim_number_t
538heim_number_create(int number)
539{
540    return (heim_number_t)CFNumberCreate(NULL, kCFNumberIntType, &number);
541}
542
543
544heim_tid_t
545heim_number_get_type_id(void)
546{
547    return (heim_tid_t)CFNumberGetTypeID();
548}
549
550int
551heim_number_get_int(heim_number_t number)
552{
553    int num = 0;
554    CFNumberGetValue((CFNumberRef)number, kCFNumberIntType, &num);
555    return num;
556}
557
558heim_queue_t
559heim_queue_create(const char *name, heim_queue_attr_t attr)
560{
561    return (heim_queue_t)dispatch_queue_create(name, NULL);
562}
563
564void
565heim_async_f(heim_queue_t queue, void *ctx, void (*callback)(void *data))
566{
567    dispatch_async_f((dispatch_queue_t)queue, ctx, callback);
568}
569
570heim_sema_t
571heim_sema_create(long count)
572{
573    return (heim_sema_t)dispatch_semaphore_create(count);
574}
575
576void
577heim_sema_signal(heim_sema_t sema)
578{
579    dispatch_semaphore_signal((dispatch_semaphore_t)sema);
580}
581
582void
583heim_sema_wait(heim_sema_t sema, time_t t)
584{
585    dispatch_semaphore_wait((dispatch_semaphore_t)sema,
586			    dispatch_time(DISPATCH_TIME_NOW, ((long long)t) * NSEC_PER_SEC));
587}
588