ohmu
·
2026-10-15
1#include "cf_helpers.h"
2#include "common/debug.h"
3
4const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) {
5 if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
6 if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt64Type, result)) {
7 FF_DEBUG("CFNumberGetValue(kCFNumberSInt64Type) failed, the number is of type 0x%08lx",
8 (unsigned long) CFNumberGetType((CFNumberRef) cf));
9 return "Number type is not SInt64";
10 }
11 return nullptr;
12 } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
13 if (CFDataGetLength((CFDataRef) cf) != sizeof(int64_t)) {
14 FF_DEBUG("CFData holds %ld bytes, int64_t needs %zu",
15 (long) CFDataGetLength((CFDataRef) cf), sizeof(int64_t));
16 return "Data length is not sizeof(int64_t)";
17 }
18 CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*) result);
19 return nullptr;
20 }
21
22 FF_DEBUG("Expected a CFNumber or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
23 return "TypeID is neither 'CFNumber' nor 'CFData'";
24}
25
26const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
27 if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
28 if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt32Type, result)) {
29 FF_DEBUG("CFNumberGetValue(kCFNumberSInt32Type) failed, the number is of type 0x%08lx",
30 (unsigned long) CFNumberGetType((CFNumberRef) cf));
31 return "Number type is not SInt32";
32 }
33 return nullptr;
34 } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
35 if (CFDataGetLength((CFDataRef) cf) != sizeof(*result)) {
36 FF_DEBUG("CFData holds %ld bytes, int32_t needs %zu",
37 (long) CFDataGetLength((CFDataRef) cf), sizeof(*result));
38 return "Data length is not sizeof(int32_t)";
39 }
40 CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(*result)), (uint8_t*) result);
41 return nullptr;
42 }
43
44 FF_DEBUG("Expected a CFNumber or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
45 return "TypeID is neither 'CFNumber' nor 'CFData'";
46}
47
48const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
49 if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
50 if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberDoubleType, result) &&
51 !CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
52 FF_DEBUG("CFNumber is of type 0x%08lx, neither Double nor Float",
53 (unsigned long) CFNumberGetType((CFNumberRef) cf));
54 return "Number type is not Double or Float";
55 }
56 return nullptr;
57 }
58
59 FF_DEBUG("Expected a CFNumber, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
60 return "TypeID is neither 'CFNumber'";
61}
62
63const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
64 if (CFGetTypeID(cf) != CFDateGetTypeID()) {
65 FF_DEBUG("Expected a CFDate, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
66 return "TypeID is not 'CFDate'";
67 }
68
69 CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
70 // Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
71 *result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
72 return nullptr;
73}
74
75const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
76 ffStrbufClear(result);
77 if (!cf) {
78 return nullptr;
79 }
80
81 if (CFGetTypeID(cf) == CFStringGetTypeID()) {
82 CFStringRef cfStr = (CFStringRef) cf;
83
84 const char* cstr = CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8);
85 if (cstr) {
86 ffStrbufSetS(result, cstr);
87 } else {
88 uint32_t length = (uint32_t) CFStringGetLength(cfStr);
89 if (length == 0) {
90 return nullptr;
91 }
92 ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
93 if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) {
94 FF_DEBUG("CFStringGetCString() failed for a %u character string", (unsigned) length);
95 return "CFStringGetCString() failed";
96 }
97 // CFStringGetCString ensures the buffer is NUL terminated
98 // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring
99 result->length = (uint32_t) strnlen(result->chars, (uint32_t) result->allocated);
100 }
101 } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
102 CFDataRef cfData = (CFDataRef) cf;
103 uint32_t length = (uint32_t) CFDataGetLength(cfData);
104 if (length == 0) {
105 return nullptr;
106 }
107 ffStrbufEnsureFixedLengthFree(result, length + 1);
108 CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
109 result->length = (uint32_t) strnlen(result->chars, length);
110 result->chars[result->length] = '\0';
111 } else {
112 FF_DEBUG("Expected a CFString or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
113 return "TypeID is neither 'CFString' nor 'CFData'";
114 }
115
116 return nullptr;
117}
118
119const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
120 ffStrbufClear(result);
121 if (!cf) {
122 return nullptr;
123 }
124
125 if (CFGetTypeID(cf) == CFDataGetTypeID()) {
126 CFDataRef cfData = (CFDataRef) cf;
127 uint32_t length = (uint32_t) CFDataGetLength(cfData);
128 if (length == 0) {
129 return nullptr;
130 }
131 ffStrbufEnsureFixedLengthFree(result, length + 1);
132 CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
133 result->length = length;
134 result->chars[result->length] = '\0';
135 } else {
136 FF_DEBUG("Expected a CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
137 return "TypeID is not 'CFData'";
138 }
139
140 return nullptr;
141}
142
143const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
144 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
145 if (cf == nullptr) {
146 FF_DEBUG("The dictionary has no value for the key");
147 return "CFDictionaryGetValue() failed";
148 }
149
150 return ffCfStrGetString(cf, result);
151}
152
153const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
154 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
155 if (cf == nullptr) {
156 FF_DEBUG("The dictionary has no value for the key");
157 return "CFDictionaryGetValue() failed";
158 }
159
160 return ffCfDataGetDataAsString(cf, result);
161}
162
163const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) {
164 CFBooleanRef cf = (CFBooleanRef) CFDictionaryGetValue(dict, key);
165 if (cf == nullptr) {
166 FF_DEBUG("The dictionary has no value for the key");
167 return "CFDictionaryGetValue() failed";
168 }
169
170 if (CFGetTypeID(cf) != CFBooleanGetTypeID()) {
171 FF_DEBUG("Expected a CFBoolean, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
172 return "TypeID is not 'CFBoolean'";
173 }
174
175 *result = CFBooleanGetValue(cf);
176 return nullptr;
177}
178
179const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
180 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
181 if (cf == nullptr) {
182 FF_DEBUG("The dictionary has no value for the key");
183 return "CFDictionaryGetValue() failed";
184 }
185
186 return ffCfNumGetInt(cf, result);
187}
188
189const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) {
190 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
191 if (cf == nullptr) {
192 FF_DEBUG("The dictionary has no value for the key");
193 return "CFDictionaryGetValue() failed";
194 }
195
196 return ffCfNumGetInt64(cf, result);
197}
198
199const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
200 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
201 if (cf == nullptr) {
202 FF_DEBUG("The dictionary has no value for the key");
203 return "CFDictionaryGetValue() failed";
204 }
205
206 return ffCfNumGetDouble(cf, result);
207}
208
209const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
210 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
211 if (cf == nullptr) {
212 FF_DEBUG("The dictionary has no value for the key");
213 return "CFDictionaryGetValue() failed";
214 }
215
216 if (CFGetTypeID(cf) != CFDataGetTypeID()) {
217 FF_DEBUG("Expected a CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
218 return "TypeID is not 'CFData'";
219 }
220
221 CFIndex trueLength = CFDataGetLength((CFDataRef) cf);
222
223 if (trueLength < offset + size) {
224 FF_DEBUG("The CFData holds %ld bytes, the caller asked for [%u, %u)",
225 (long) trueLength, offset, offset + size);
226 return "Data length is less than offset + size";
227 }
228
229 if (length) {
230 *length = (uint32_t) trueLength;
231 }
232
233 CFDataGetBytes((CFDataRef) cf, CFRangeMake(offset, size), result);
234 return nullptr;
235}
236
237const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) {
238 CFDictionaryRef cf = (CFDictionaryRef) CFDictionaryGetValue(dict, key);
239 if (cf == nullptr || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
240 FF_DEBUG("Expected a CFDictionary, got %s",
241 cf == nullptr ? "no value at all" : "a value of another type");
242 return "TypeID is not 'CFDictionary'";
243 }
244
245 *result = cf;
246 return nullptr;
247}
248
249const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
250 CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
251 if (cf == nullptr) {
252 FF_DEBUG("The dictionary has no value for the key");
253 return "CFDictionaryGetValue() failed";
254 }
255
256 return ffCfDateGetEpoch(cf, result);
257}