e939f0558af88009781ac27da4207cf589098f4c
[apple/xnu.git] / libkern / c++ / OSSerializeBinary.cpp
1 /*
2 * Copyright (c) 2014 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30 #include <libkern/c++/OSContainers.h>
31 #include <libkern/c++/OSLib.h>
32 #include <libkern/c++/OSDictionary.h>
33 #include <libkern/OSSerializeBinary.h>
34
35 #include <IOKit/IOLib.h>
36
37 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
38
39 #if 0
40 #define DEBG(fmt, args...) { kprintf(fmt, args); }
41 #else
42 #define DEBG(fmt, args...) {}
43 #endif
44
45 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
46
47 OSSerialize *OSSerialize::binaryWithCapacity(unsigned int inCapacity,
48 Editor editor, void * reference)
49 {
50 OSSerialize *me;
51
52 if (inCapacity < sizeof(uint32_t)) return (0);
53 me = OSSerialize::withCapacity(inCapacity);
54 if (!me) return (0);
55
56 me->binary = true;
57 me->endCollection = true;
58 me->editor = editor;
59 me->editRef = reference;
60
61 bcopy(kOSSerializeBinarySignature, &me->data[0], sizeof(kOSSerializeBinarySignature));
62 me->length = sizeof(kOSSerializeBinarySignature);
63
64 return (me);
65 }
66
67 bool OSSerialize::addBinary(const void * bits, size_t size)
68 {
69 unsigned int newCapacity;
70 size_t alignSize;
71
72 alignSize = ((size + 3) & ~3L);
73 newCapacity = length + alignSize;
74 if (newCapacity >= capacity)
75 {
76 newCapacity = (((newCapacity - 1) / capacityIncrement) + 1) * capacityIncrement;
77 if (newCapacity < ensureCapacity(newCapacity)) return (false);
78 }
79
80 bcopy(bits, &data[length], size);
81 length += alignSize;
82
83 return (true);
84 }
85
86 bool OSSerialize::addBinaryObject(const OSMetaClassBase * o, uint32_t key,
87 const void * bits, size_t size)
88 {
89 unsigned int newCapacity;
90 size_t alignSize;
91 OSNumber * tagNum;
92
93 // build a tag
94 tagNum = OSNumber::withNumber(tag, 32);
95 tag++;
96 // add to tag dictionary
97 tags->setObject((const OSSymbol *) o, tagNum);
98 tagNum->release();
99
100 alignSize = ((size + sizeof(key) + 3) & ~3L);
101 newCapacity = length + alignSize;
102 if (newCapacity >= capacity)
103 {
104 newCapacity = (((newCapacity - 1) / capacityIncrement) + 1) * capacityIncrement;
105 if (newCapacity < ensureCapacity(newCapacity)) return (false);
106 }
107
108 if (endCollection)
109 {
110 endCollection = false;
111 key |= kOSSerializeEndCollecton;
112 }
113
114 bcopy(&key, &data[length], sizeof(key));
115 bcopy(bits, &data[length + sizeof(key)], size);
116 length += alignSize;
117
118 return (true);
119 }
120
121 bool OSSerialize::binarySerialize(const OSMetaClassBase *o)
122 {
123 OSDictionary * dict;
124 OSArray * array;
125 OSSet * set;
126 OSNumber * num;
127 OSSymbol * sym;
128 OSString * str;
129 OSData * data;
130 OSBoolean * boo;
131
132 OSNumber * tagNum;
133 uint32_t i, key;
134 size_t len;
135 bool ok;
136
137 tagNum = (OSNumber *)tags->getObject((const OSSymbol *) o);
138 // does it exist?
139 if (tagNum)
140 {
141 key = (kOSSerializeObject | tagNum->unsigned32BitValue());
142 if (endCollection)
143 {
144 endCollection = false;
145 key |= kOSSerializeEndCollecton;
146 }
147 ok = addBinary(&key, sizeof(key));
148 return (ok);
149 }
150
151 if ((dict = OSDynamicCast(OSDictionary, o)))
152 {
153 key = (kOSSerializeDictionary | dict->count);
154 ok = addBinaryObject(o, key, NULL, 0);
155 for (i = 0; ok && (i < dict->count);)
156 {
157 const OSSymbol * dictKey;
158 const OSMetaClassBase * dictValue;
159 const OSMetaClassBase * nvalue = 0;
160
161 i++;
162 dictKey = dict->dictionary[i-1].key;
163 dictValue = dict->dictionary[i-1].value;
164 if (editor)
165 {
166 dictValue = nvalue = (*editor)(editRef, this, dict, dictKey, dictValue);
167 if (!dictValue) dictValue = dict;
168 }
169 ok = binarySerialize(dictKey);
170 if (!ok) break;
171 endCollection = (i == dict->count);
172 ok = binarySerialize(dictValue);
173 if (!ok) ok = dictValue->serialize(this);
174 if (nvalue) nvalue->release();
175 // if (!ok) ok = binarySerialize(kOSBooleanFalse);
176 }
177 }
178 else if ((array = OSDynamicCast(OSArray, o)))
179 {
180 key = (kOSSerializeArray | array->count);
181 ok = addBinaryObject(o, key, NULL, 0);
182 for (i = 0; ok && (i < array->count);)
183 {
184 i++;
185 endCollection = (i == array->count);
186 ok = binarySerialize(array->array[i-1]);
187 if (!ok) ok = array->array[i-1]->serialize(this);
188 // if (!ok) ok = binarySerialize(kOSBooleanFalse);
189 }
190 }
191 else if ((set = OSDynamicCast(OSSet, o)))
192 {
193 key = (kOSSerializeSet | set->members->count);
194 ok = addBinaryObject(o, key, NULL, 0);
195 for (i = 0; ok && (i < set->members->count);)
196 {
197 i++;
198 endCollection = (i == set->members->count);
199 ok = binarySerialize(set->members->array[i-1]);
200 if (!ok) ok = set->members->array[i-1]->serialize(this);
201 // if (!ok) ok = binarySerialize(kOSBooleanFalse);
202 }
203 }
204 else if ((num = OSDynamicCast(OSNumber, o)))
205 {
206 key = (kOSSerializeNumber | num->size);
207 ok = addBinaryObject(o, key, &num->value, sizeof(num->value));
208 }
209 else if ((boo = OSDynamicCast(OSBoolean, o)))
210 {
211 key = (kOSSerializeBoolean | (kOSBooleanTrue == boo));
212 ok = addBinaryObject(o, key, NULL, 0);
213 }
214 else if ((sym = OSDynamicCast(OSSymbol, o)))
215 {
216 len = (sym->getLength() + 1);
217 key = (kOSSerializeSymbol | len);
218 ok = addBinaryObject(o, key, sym->getCStringNoCopy(), len);
219 }
220 else if ((str = OSDynamicCast(OSString, o)))
221 {
222 len = (str->getLength() + 0);
223 key = (kOSSerializeString | len);
224 ok = addBinaryObject(o, key, str->getCStringNoCopy(), len);
225 }
226 else if ((data = OSDynamicCast(OSData, o)))
227 {
228 len = data->getLength();
229 if (data->reserved && data->reserved->disableSerialization) len = 0;
230 key = (kOSSerializeData | len);
231 ok = addBinaryObject(o, key, data->getBytesNoCopy(), len);
232 }
233 else return (false);
234
235 return (ok);
236 }
237
238 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
239
240 #define setAtIndex(v, idx, o) \
241 if (idx >= v##Capacity) \
242 { \
243 uint32_t ncap = v##Capacity + 64; \
244 typeof(v##Array) nbuf = (typeof(v##Array)) kalloc(ncap * sizeof(o)); \
245 if (!nbuf) ok = false; \
246 if (v##Array) \
247 { \
248 bcopy(v##Array, nbuf, v##Capacity * sizeof(o)); \
249 kfree(v##Array, v##Capacity * sizeof(o)); \
250 } \
251 v##Array = nbuf; \
252 v##Capacity = ncap; \
253 } \
254 if (ok) v##Array[idx] = o;
255
256 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
257
258 OSObject *
259 OSUnserializeBinary(const char *buffer, size_t bufferSize, OSString **errorString)
260 {
261 OSObject ** objsArray;
262 uint32_t objsCapacity;
263 uint32_t objsIdx;
264
265 OSObject ** stackArray;
266 uint32_t stackCapacity;
267 uint32_t stackIdx;
268
269 OSObject * result;
270 OSObject * parent;
271 OSDictionary * dict;
272 OSArray * array;
273 OSSet * set;
274 OSDictionary * newDict;
275 OSArray * newArray;
276 OSSet * newSet;
277 OSObject * o;
278 OSSymbol * sym;
279
280 size_t bufferPos;
281 const uint32_t * next;
282 uint32_t key, len, wordLen;
283 bool end, newCollect, isRef;
284 unsigned long long value;
285 bool ok;
286
287 if (errorString) *errorString = 0;
288 if (0 != strcmp(kOSSerializeBinarySignature, buffer)) return (NULL);
289 if (3 & ((uintptr_t) buffer)) return (NULL);
290 if (bufferSize < sizeof(kOSSerializeBinarySignature)) return (NULL);
291 bufferPos = sizeof(kOSSerializeBinarySignature);
292 next = (typeof(next)) (((uintptr_t) buffer) + bufferPos);
293
294 DEBG("---------OSUnserializeBinary(%p)\n", buffer);
295
296 objsArray = stackArray = NULL;
297 objsIdx = objsCapacity = 0;
298 stackIdx = stackCapacity = 0;
299
300 result = 0;
301 parent = 0;
302 dict = 0;
303 array = 0;
304 set = 0;
305 sym = 0;
306
307 ok = true;
308 while (ok)
309 {
310 bufferPos += sizeof(*next);
311 if (!(ok = (bufferPos <= bufferSize))) break;
312 key = *next++;
313
314 len = (key & kOSSerializeDataMask);
315 wordLen = (len + 3) >> 2;
316 end = (0 != (kOSSerializeEndCollecton & key));
317 DEBG("key 0x%08x: 0x%04x, %d\n", key, len, end);
318
319 newCollect = isRef = false;
320 o = 0; newDict = 0; newArray = 0; newSet = 0;
321
322 switch (kOSSerializeTypeMask & key)
323 {
324 case kOSSerializeDictionary:
325 o = newDict = OSDictionary::withCapacity(len);
326 newCollect = (len != 0);
327 break;
328 case kOSSerializeArray:
329 o = newArray = OSArray::withCapacity(len);
330 newCollect = (len != 0);
331 break;
332 case kOSSerializeSet:
333 o = newSet = OSSet::withCapacity(len);
334 newCollect = (len != 0);
335 break;
336
337 case kOSSerializeObject:
338 if (len >= objsIdx) break;
339 o = objsArray[len];
340 o->retain();
341 isRef = true;
342 break;
343
344 case kOSSerializeNumber:
345 bufferPos += sizeof(long long);
346 if (bufferPos > bufferSize) break;
347 value = next[1];
348 value <<= 32;
349 value |= next[0];
350 o = OSNumber::withNumber(value, len);
351 next += 2;
352 break;
353
354 case kOSSerializeSymbol:
355 bufferPos += (wordLen * sizeof(uint32_t));
356 if (bufferPos > bufferSize) break;
357 if (0 != ((const char *)next)[len-1]) break;
358 o = (OSObject *) OSSymbol::withCString((const char *) next);
359 next += wordLen;
360 break;
361
362 case kOSSerializeString:
363 bufferPos += (wordLen * sizeof(uint32_t));
364 if (bufferPos > bufferSize) break;
365 o = OSString::withStringOfLength((const char *) next, len);
366 next += wordLen;
367 break;
368
369 case kOSSerializeData:
370 bufferPos += (wordLen * sizeof(uint32_t));
371 if (bufferPos > bufferSize) break;
372 o = OSData::withBytes(next, len);
373 next += wordLen;
374 break;
375
376 case kOSSerializeBoolean:
377 o = (len ? kOSBooleanTrue : kOSBooleanFalse);
378 break;
379
380 default:
381 break;
382 }
383
384 if (!(ok = (o != 0))) break;
385
386 if (!isRef)
387 {
388 setAtIndex(objs, objsIdx, o);
389 if (!ok) break;
390 objsIdx++;
391 }
392
393 if (dict)
394 {
395 if (sym)
396 {
397 DEBG("%s = %s\n", sym->getCStringNoCopy(), o->getMetaClass()->getClassName());
398 if (o != dict) ok = dict->setObject(sym, o);
399 o->release();
400 sym->release();
401 sym = 0;
402 }
403 else
404 {
405 sym = OSDynamicCast(OSSymbol, o);
406 ok = (sym != 0);
407 }
408 }
409 else if (array)
410 {
411 ok = array->setObject(o);
412 o->release();
413 }
414 else if (set)
415 {
416 ok = set->setObject(o);
417 o->release();
418 }
419 else
420 {
421 assert(!parent);
422 result = o;
423 }
424
425 if (!ok) break;
426
427 if (newCollect)
428 {
429 if (!end)
430 {
431 stackIdx++;
432 setAtIndex(stack, stackIdx, parent);
433 if (!ok) break;
434 }
435 DEBG("++stack[%d] %p\n", stackIdx, parent);
436 parent = o;
437 dict = newDict;
438 array = newArray;
439 set = newSet;
440 end = false;
441 }
442
443 if (end)
444 {
445 if (!stackIdx) break;
446 parent = stackArray[stackIdx];
447 DEBG("--stack[%d] %p\n", stackIdx, parent);
448 stackIdx--;
449 set = 0;
450 dict = 0;
451 array = 0;
452 if (!(dict = OSDynamicCast(OSDictionary, parent)))
453 {
454 if (!(array = OSDynamicCast(OSArray, parent))) ok = (0 != (set = OSDynamicCast(OSSet, parent)));
455 }
456 }
457 }
458 DEBG("ret %p\n", result);
459
460 if (objsCapacity) kfree(objsArray, objsCapacity * sizeof(*objsArray));
461 if (stackCapacity) kfree(stackArray, stackCapacity * sizeof(*stackArray));
462
463 if (!ok && result)
464 {
465 result->release();
466 result = 0;
467 }
468 return (result);
469 }