1 /*
2 * MessagePack for C unpacking routine
3 *
4 * Copyright (C) 2008-2009 FURUHASHI Sadayuki
5 *
6 * Distributed under the Boost Software License, Version 1.0.
7 * (See accompanying file LICENSE_1_0.txt or copy at
8 * http://www.boost.org/LICENSE_1_0.txt)
9 */
10 #include "msgpack/unpack.h"
11 #include "msgpack/unpack_define.h"
12 #include "msgpack/util.h"
13 #include <stdlib.h>
14
15 #ifdef _msgpack_atomic_counter_header
16 #include _msgpack_atomic_counter_header
17 #endif
18
19
20 typedef struct {
21 msgpack_zone* z;
22 bool referenced;
23 } unpack_user;
24
25
26 #define msgpack_unpack_struct(name) \
27 struct template ## name
28
29 #define msgpack_unpack_func(ret, name) \
30 ret template ## name
31
32 #define msgpack_unpack_callback(name) \
33 template_callback ## name
34
35 #define msgpack_unpack_object msgpack_object
36
37 #define msgpack_unpack_user unpack_user
38
39
40 struct template_context;
41 typedef struct template_context template_context;
42
43 static void template_init(template_context* ctx);
44
45 static msgpack_object template_data(template_context* ctx);
46
47 static int template_execute(
48 template_context* ctx, const char* data, size_t len, size_t* off);
49
50
template_callback_root(unpack_user * u)51 static inline msgpack_object template_callback_root(unpack_user* u)
52 {
53 msgpack_object o;
54 MSGPACK_UNUSED(u);
55 o.type = MSGPACK_OBJECT_NIL;
56 return o;
57 }
58
template_callback_uint8(unpack_user * u,uint8_t d,msgpack_object * o)59 static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_object* o)
60 {
61 MSGPACK_UNUSED(u);
62 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
63 o->via.u64 = d;
64 return 0;
65 }
66
template_callback_uint16(unpack_user * u,uint16_t d,msgpack_object * o)67 static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_object* o)
68 {
69 MSGPACK_UNUSED(u);
70 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
71 o->via.u64 = d;
72 return 0;
73 }
74
template_callback_uint32(unpack_user * u,uint32_t d,msgpack_object * o)75 static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_object* o)
76 {
77 MSGPACK_UNUSED(u);
78 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
79 o->via.u64 = d;
80 return 0;
81 }
82
template_callback_uint64(unpack_user * u,uint64_t d,msgpack_object * o)83 static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_object* o)
84 {
85 MSGPACK_UNUSED(u);
86 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
87 o->via.u64 = d;
88 return 0;
89 }
90
template_callback_int8(unpack_user * u,int8_t d,msgpack_object * o)91 static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_object* o)
92 {
93 MSGPACK_UNUSED(u);
94 if(d >= 0) {
95 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
96 o->via.u64 = (uint64_t)d;
97 return 0;
98 }
99 else {
100 o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
101 o->via.i64 = d;
102 return 0;
103 }
104 }
105
template_callback_int16(unpack_user * u,int16_t d,msgpack_object * o)106 static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_object* o)
107 {
108 MSGPACK_UNUSED(u);
109 if(d >= 0) {
110 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
111 o->via.u64 = (uint64_t)d;
112 return 0;
113 }
114 else {
115 o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
116 o->via.i64 = d;
117 return 0;
118 }
119 }
120
template_callback_int32(unpack_user * u,int32_t d,msgpack_object * o)121 static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_object* o)
122 {
123 MSGPACK_UNUSED(u);
124 if(d >= 0) {
125 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
126 o->via.u64 = (uint64_t)d;
127 return 0;
128 }
129 else {
130 o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
131 o->via.i64 = d;
132 return 0;
133 }
134 }
135
template_callback_int64(unpack_user * u,int64_t d,msgpack_object * o)136 static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_object* o)
137 {
138 MSGPACK_UNUSED(u);
139 if(d >= 0) {
140 o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
141 o->via.u64 = (uint64_t)d;
142 return 0;
143 }
144 else {
145 o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
146 o->via.i64 = d;
147 return 0;
148 }
149 }
150
template_callback_float(unpack_user * u,float d,msgpack_object * o)151 static inline int template_callback_float(unpack_user* u, float d, msgpack_object* o)
152 {
153 MSGPACK_UNUSED(u);
154 o->type = MSGPACK_OBJECT_FLOAT32;
155 o->via.f64 = d;
156 return 0;
157 }
158
template_callback_double(unpack_user * u,double d,msgpack_object * o)159 static inline int template_callback_double(unpack_user* u, double d, msgpack_object* o)
160 {
161 MSGPACK_UNUSED(u);
162 o->type = MSGPACK_OBJECT_FLOAT64;
163 o->via.f64 = d;
164 return 0;
165 }
166
template_callback_nil(unpack_user * u,msgpack_object * o)167 static inline int template_callback_nil(unpack_user* u, msgpack_object* o)
168 {
169 MSGPACK_UNUSED(u);
170 o->type = MSGPACK_OBJECT_NIL;
171 return 0;
172 }
173
template_callback_true(unpack_user * u,msgpack_object * o)174 static inline int template_callback_true(unpack_user* u, msgpack_object* o)
175 {
176 MSGPACK_UNUSED(u);
177 o->type = MSGPACK_OBJECT_BOOLEAN;
178 o->via.boolean = true;
179 return 0;
180 }
181
template_callback_false(unpack_user * u,msgpack_object * o)182 static inline int template_callback_false(unpack_user* u, msgpack_object* o)
183 {
184 MSGPACK_UNUSED(u);
185 o->type = MSGPACK_OBJECT_BOOLEAN;
186 o->via.boolean = false;
187 return 0;
188 }
189
template_callback_array(unpack_user * u,unsigned int n,msgpack_object * o)190 static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_object* o)
191 {
192 unsigned int size;
193 o->type = MSGPACK_OBJECT_ARRAY;
194 o->via.array.size = 0;
195 size = n*sizeof(msgpack_object);
196 if (size / sizeof(msgpack_object) != n) {
197 // integer overflow
198 return MSGPACK_UNPACK_NOMEM_ERROR;
199 }
200 o->via.array.ptr = (msgpack_object*)msgpack_zone_malloc(u->z, size);
201 if(o->via.array.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; }
202 return 0;
203 }
204
template_callback_array_item(unpack_user * u,msgpack_object * c,msgpack_object o)205 static inline int template_callback_array_item(unpack_user* u, msgpack_object* c, msgpack_object o)
206 {
207 MSGPACK_UNUSED(u);
208 #if defined(__GNUC__) && !defined(__clang__)
209 memcpy(&c->via.array.ptr[c->via.array.size], &o, sizeof(msgpack_object));
210 #else /* __GNUC__ && !__clang__ */
211 c->via.array.ptr[c->via.array.size] = o;
212 #endif /* __GNUC__ && !__clang__ */
213 ++c->via.array.size;
214 return 0;
215 }
216
template_callback_map(unpack_user * u,unsigned int n,msgpack_object * o)217 static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_object* o)
218 {
219 unsigned int size;
220 o->type = MSGPACK_OBJECT_MAP;
221 o->via.map.size = 0;
222 size = n*sizeof(msgpack_object_kv);
223 if (size / sizeof(msgpack_object_kv) != n) {
224 // integer overflow
225 return MSGPACK_UNPACK_NOMEM_ERROR;
226 }
227 o->via.map.ptr = (msgpack_object_kv*)msgpack_zone_malloc(u->z, size);
228 if(o->via.map.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; }
229 return 0;
230 }
231
template_callback_map_item(unpack_user * u,msgpack_object * c,msgpack_object k,msgpack_object v)232 static inline int template_callback_map_item(unpack_user* u, msgpack_object* c, msgpack_object k, msgpack_object v)
233 {
234 MSGPACK_UNUSED(u);
235 #if defined(__GNUC__) && !defined(__clang__)
236 memcpy(&c->via.map.ptr[c->via.map.size].key, &k, sizeof(msgpack_object));
237 memcpy(&c->via.map.ptr[c->via.map.size].val, &v, sizeof(msgpack_object));
238 #else /* __GNUC__ && !__clang__ */
239 c->via.map.ptr[c->via.map.size].key = k;
240 c->via.map.ptr[c->via.map.size].val = v;
241 #endif /* __GNUC__ && !__clang__ */
242 ++c->via.map.size;
243 return 0;
244 }
245
template_callback_str(unpack_user * u,const char * b,const char * p,unsigned int l,msgpack_object * o)246 static inline int template_callback_str(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
247 {
248 MSGPACK_UNUSED(u);
249 MSGPACK_UNUSED(b);
250 o->type = MSGPACK_OBJECT_STR;
251 o->via.str.ptr = p;
252 o->via.str.size = l;
253 u->referenced = true;
254 return 0;
255 }
256
template_callback_bin(unpack_user * u,const char * b,const char * p,unsigned int l,msgpack_object * o)257 static inline int template_callback_bin(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
258 {
259 MSGPACK_UNUSED(u);
260 MSGPACK_UNUSED(b);
261 o->type = MSGPACK_OBJECT_BIN;
262 o->via.bin.ptr = p;
263 o->via.bin.size = l;
264 u->referenced = true;
265 return 0;
266 }
267
template_callback_ext(unpack_user * u,const char * b,const char * p,unsigned int l,msgpack_object * o)268 static inline int template_callback_ext(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
269 {
270 if (l == 0) {
271 return MSGPACK_UNPACK_PARSE_ERROR;
272 }
273 MSGPACK_UNUSED(u);
274 MSGPACK_UNUSED(b);
275 o->type = MSGPACK_OBJECT_EXT;
276 o->via.ext.type = *p;
277 o->via.ext.ptr = p + 1;
278 o->via.ext.size = l - 1;
279 u->referenced = true;
280 return 0;
281 }
282
283 #include "msgpack/unpack_template.h"
284
285
286 #define CTX_CAST(m) ((template_context*)(m))
287 #define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced
288
289 #define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t))
290
291
init_count(void * buffer)292 static inline void init_count(void* buffer)
293 {
294 *(volatile _msgpack_atomic_counter_t*)buffer = 1;
295 }
296
decr_count(void * buffer)297 static inline void decr_count(void* buffer)
298 {
299 // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); }
300 if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) {
301 free(buffer);
302 }
303 }
304
incr_count(void * buffer)305 static inline void incr_count(void* buffer)
306 {
307 // atomic ++*(_msgpack_atomic_counter_t*)buffer;
308 _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer);
309 }
310
get_count(void * buffer)311 static inline _msgpack_atomic_counter_t get_count(void* buffer)
312 {
313 return *(volatile _msgpack_atomic_counter_t*)buffer;
314 }
315
msgpack_unpacker_init(msgpack_unpacker * mpac,size_t initial_buffer_size)316 bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size)
317 {
318 char* buffer;
319 void* ctx;
320 msgpack_zone* z;
321
322 if(initial_buffer_size < COUNTER_SIZE) {
323 initial_buffer_size = COUNTER_SIZE;
324 }
325
326 buffer = (char*)malloc(initial_buffer_size);
327 if(buffer == NULL) {
328 return false;
329 }
330
331 ctx = malloc(sizeof(template_context));
332 if(ctx == NULL) {
333 free(buffer);
334 return false;
335 }
336
337 z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
338 if(z == NULL) {
339 free(ctx);
340 free(buffer);
341 return false;
342 }
343
344 mpac->buffer = buffer;
345 mpac->used = COUNTER_SIZE;
346 mpac->free = initial_buffer_size - mpac->used;
347 mpac->off = COUNTER_SIZE;
348 mpac->parsed = 0;
349 mpac->initial_buffer_size = initial_buffer_size;
350 mpac->z = z;
351 mpac->ctx = ctx;
352
353 init_count(mpac->buffer);
354
355 template_init(CTX_CAST(mpac->ctx));
356 CTX_CAST(mpac->ctx)->user.z = mpac->z;
357 CTX_CAST(mpac->ctx)->user.referenced = false;
358
359 return true;
360 }
361
msgpack_unpacker_destroy(msgpack_unpacker * mpac)362 void msgpack_unpacker_destroy(msgpack_unpacker* mpac)
363 {
364 msgpack_zone_free(mpac->z);
365 free(mpac->ctx);
366 decr_count(mpac->buffer);
367 }
368
msgpack_unpacker_new(size_t initial_buffer_size)369 msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size)
370 {
371 msgpack_unpacker* mpac = (msgpack_unpacker*)malloc(sizeof(msgpack_unpacker));
372 if(mpac == NULL) {
373 return NULL;
374 }
375
376 if(!msgpack_unpacker_init(mpac, initial_buffer_size)) {
377 free(mpac);
378 return NULL;
379 }
380
381 return mpac;
382 }
383
msgpack_unpacker_free(msgpack_unpacker * mpac)384 void msgpack_unpacker_free(msgpack_unpacker* mpac)
385 {
386 msgpack_unpacker_destroy(mpac);
387 free(mpac);
388 }
389
msgpack_unpacker_expand_buffer(msgpack_unpacker * mpac,size_t size)390 bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size)
391 {
392 if(mpac->used == mpac->off && get_count(mpac->buffer) == 1
393 && !CTX_REFERENCED(mpac)) {
394 // rewind buffer
395 mpac->free += mpac->used - COUNTER_SIZE;
396 mpac->used = COUNTER_SIZE;
397 mpac->off = COUNTER_SIZE;
398
399 if(mpac->free >= size) {
400 return true;
401 }
402 }
403
404 if(mpac->off == COUNTER_SIZE) {
405 char* tmp;
406 size_t next_size = (mpac->used + mpac->free) * 2; // include COUNTER_SIZE
407 while(next_size < size + mpac->used) {
408 size_t tmp_next_size = next_size * 2;
409 if (tmp_next_size <= next_size) {
410 next_size = size + mpac->used;
411 break;
412 }
413 next_size = tmp_next_size;
414 }
415
416 tmp = (char*)realloc(mpac->buffer, next_size);
417 if(tmp == NULL) {
418 return false;
419 }
420
421 mpac->buffer = tmp;
422 mpac->free = next_size - mpac->used;
423
424 } else {
425 char* tmp;
426 size_t next_size = mpac->initial_buffer_size; // include COUNTER_SIZE
427 size_t not_parsed = mpac->used - mpac->off;
428 while(next_size < size + not_parsed + COUNTER_SIZE) {
429 size_t tmp_next_size = next_size * 2;
430 if (tmp_next_size <= next_size) {
431 next_size = size + not_parsed + COUNTER_SIZE;
432 break;
433 }
434 next_size = tmp_next_size;
435 }
436
437 tmp = (char*)malloc(next_size);
438 if(tmp == NULL) {
439 return false;
440 }
441
442 init_count(tmp);
443
444 memcpy(tmp+COUNTER_SIZE, mpac->buffer+mpac->off, not_parsed);
445
446 if(CTX_REFERENCED(mpac)) {
447 if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) {
448 free(tmp);
449 return false;
450 }
451 CTX_REFERENCED(mpac) = false;
452 } else {
453 decr_count(mpac->buffer);
454 }
455
456 mpac->buffer = tmp;
457 mpac->used = not_parsed + COUNTER_SIZE;
458 mpac->free = next_size - mpac->used;
459 mpac->off = COUNTER_SIZE;
460 }
461
462 return true;
463 }
464
msgpack_unpacker_execute(msgpack_unpacker * mpac)465 int msgpack_unpacker_execute(msgpack_unpacker* mpac)
466 {
467 size_t off = mpac->off;
468 int ret = template_execute(CTX_CAST(mpac->ctx),
469 mpac->buffer, mpac->used, &mpac->off);
470 if(mpac->off > off) {
471 mpac->parsed += mpac->off - off;
472 }
473 return ret;
474 }
475
msgpack_unpacker_data(msgpack_unpacker * mpac)476 msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac)
477 {
478 return template_data(CTX_CAST(mpac->ctx));
479 }
480
msgpack_unpacker_release_zone(msgpack_unpacker * mpac)481 msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac)
482 {
483 msgpack_zone* r;
484 msgpack_zone* old;
485
486 if(!msgpack_unpacker_flush_zone(mpac)) {
487 return NULL;
488 }
489
490 r = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
491 if(r == NULL) {
492 return NULL;
493 }
494
495 old = mpac->z;
496 mpac->z = r;
497 CTX_CAST(mpac->ctx)->user.z = mpac->z;
498
499 return old;
500 }
501
msgpack_unpacker_reset_zone(msgpack_unpacker * mpac)502 void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac)
503 {
504 msgpack_zone_clear(mpac->z);
505 }
506
msgpack_unpacker_flush_zone(msgpack_unpacker * mpac)507 bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac)
508 {
509 if(CTX_REFERENCED(mpac)) {
510 if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) {
511 return false;
512 }
513 CTX_REFERENCED(mpac) = false;
514
515 incr_count(mpac->buffer);
516 }
517
518 return true;
519 }
520
msgpack_unpacker_reset(msgpack_unpacker * mpac)521 void msgpack_unpacker_reset(msgpack_unpacker* mpac)
522 {
523 template_init(CTX_CAST(mpac->ctx));
524 // don't reset referenced flag
525 mpac->parsed = 0;
526 }
527
unpacker_next(msgpack_unpacker * mpac,msgpack_unpacked * result)528 static inline msgpack_unpack_return unpacker_next(msgpack_unpacker* mpac,
529 msgpack_unpacked* result)
530 {
531 int ret;
532
533 msgpack_unpacked_destroy(result);
534
535 ret = msgpack_unpacker_execute(mpac);
536
537 if(ret < 0) {
538 result->zone = NULL;
539 memset(&result->data, 0, sizeof(msgpack_object));
540 return ret;
541 }
542
543 if(ret == 0) {
544 return MSGPACK_UNPACK_CONTINUE;
545 }
546 result->zone = msgpack_unpacker_release_zone(mpac);
547 result->data = msgpack_unpacker_data(mpac);
548
549 return MSGPACK_UNPACK_SUCCESS;
550 }
551
msgpack_unpacker_next(msgpack_unpacker * mpac,msgpack_unpacked * result)552 msgpack_unpack_return msgpack_unpacker_next(msgpack_unpacker* mpac,
553 msgpack_unpacked* result)
554 {
555 int ret;
556
557 ret = unpacker_next(mpac, result);
558 if (ret == MSGPACK_UNPACK_SUCCESS) {
559 msgpack_unpacker_reset(mpac);
560 }
561
562 return ret;
563 }
564
565 msgpack_unpack_return
msgpack_unpacker_next_with_size(msgpack_unpacker * mpac,msgpack_unpacked * result,size_t * p_bytes)566 msgpack_unpacker_next_with_size(msgpack_unpacker* mpac,
567 msgpack_unpacked* result, size_t *p_bytes)
568 {
569 int ret;
570
571 ret = unpacker_next(mpac, result);
572 if (ret == MSGPACK_UNPACK_SUCCESS || ret == MSGPACK_UNPACK_CONTINUE) {
573 *p_bytes = mpac->parsed;
574 }
575
576 if (ret == MSGPACK_UNPACK_SUCCESS) {
577 msgpack_unpacker_reset(mpac);
578 }
579
580 return ret;
581 }
582
583 msgpack_unpack_return
msgpack_unpack(const char * data,size_t len,size_t * off,msgpack_zone * result_zone,msgpack_object * result)584 msgpack_unpack(const char* data, size_t len, size_t* off,
585 msgpack_zone* result_zone, msgpack_object* result)
586 {
587 size_t noff = 0;
588 if(off != NULL) { noff = *off; }
589
590 if(len <= noff) {
591 // FIXME
592 return MSGPACK_UNPACK_CONTINUE;
593 }
594 else {
595 int e;
596 template_context ctx;
597 template_init(&ctx);
598
599 ctx.user.z = result_zone;
600 ctx.user.referenced = false;
601
602 e = template_execute(&ctx, data, len, &noff);
603 if(e < 0) {
604 return e;
605 }
606
607 if(off != NULL) { *off = noff; }
608
609 if(e == 0) {
610 return MSGPACK_UNPACK_CONTINUE;
611 }
612
613 *result = template_data(&ctx);
614
615 if(noff < len) {
616 return MSGPACK_UNPACK_EXTRA_BYTES;
617 }
618
619 return MSGPACK_UNPACK_SUCCESS;
620 }
621 }
622
623 msgpack_unpack_return
msgpack_unpack_next(msgpack_unpacked * result,const char * data,size_t len,size_t * off)624 msgpack_unpack_next(msgpack_unpacked* result,
625 const char* data, size_t len, size_t* off)
626 {
627 size_t noff = 0;
628 msgpack_unpacked_destroy(result);
629
630 if(off != NULL) { noff = *off; }
631
632 if(len <= noff) {
633 return MSGPACK_UNPACK_CONTINUE;
634 }
635
636 if (!result->zone) {
637 result->zone = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
638 }
639
640 if (!result->zone) {
641 return MSGPACK_UNPACK_NOMEM_ERROR;
642 }
643 else {
644 int e;
645 template_context ctx;
646 template_init(&ctx);
647
648 ctx.user.z = result->zone;
649 ctx.user.referenced = false;
650
651 e = template_execute(&ctx, data, len, &noff);
652
653 if(off != NULL) { *off = noff; }
654
655 if(e < 0) {
656 msgpack_zone_free(result->zone);
657 result->zone = NULL;
658 return e;
659 }
660
661 if(e == 0) {
662 return MSGPACK_UNPACK_CONTINUE;
663 }
664
665 result->data = template_data(&ctx);
666
667 return MSGPACK_UNPACK_SUCCESS;
668 }
669 }
670
671 #if defined(MSGPACK_OLD_COMPILER_BUS_ERROR_WORKAROUND)
672 // FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc.
dummy_function_to_avoid_bus_error()673 static void dummy_function_to_avoid_bus_error()
674 {
675 }
676 #endif
677