2 * MessagePack for C unpacking routine
4 * Copyright (C) 2008-2009 FURUHASHI Sadayuki
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)
10 #include "vendor/msgpack/unpack.h"
11 #include "vendor/msgpack/unpack_define.h"
12 #include "vendor/msgpack/util.h"
15 #ifdef _msgpack_atomic_counter_header
16 #include _msgpack_atomic_counter_header
26 #define msgpack_unpack_struct(name) \
27 struct template ## name
29 #define msgpack_unpack_func(ret, name) \
32 #define msgpack_unpack_callback(name) \
33 template_callback ## name
35 #define msgpack_unpack_object msgpack_object
37 #define msgpack_unpack_user unpack_user
40 struct template_context
;
41 typedef struct template_context template_context
;
43 static void template_init(template_context
* ctx
);
45 static msgpack_object
template_data(template_context
* ctx
);
47 static int template_execute(
48 template_context
* ctx
, const char* data
, size_t len
, size_t* off
);
51 static inline msgpack_object
template_callback_root(unpack_user
* u
)
53 msgpack_object o
= {};
55 o
.type
= MSGPACK_OBJECT_NIL
;
59 static inline int template_callback_uint8(unpack_user
* u
, uint8_t d
, msgpack_object
* o
)
62 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
67 static inline int template_callback_uint16(unpack_user
* u
, uint16_t d
, msgpack_object
* o
)
70 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
75 static inline int template_callback_uint32(unpack_user
* u
, uint32_t d
, msgpack_object
* o
)
78 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
83 static inline int template_callback_uint64(unpack_user
* u
, uint64_t d
, msgpack_object
* o
)
86 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
91 static inline int template_callback_int8(unpack_user
* u
, int8_t d
, msgpack_object
* o
)
95 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
96 o
->via
.u64
= (uint64_t)d
;
100 o
->type
= MSGPACK_OBJECT_NEGATIVE_INTEGER
;
106 static inline int template_callback_int16(unpack_user
* u
, int16_t d
, msgpack_object
* o
)
110 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
111 o
->via
.u64
= (uint64_t)d
;
115 o
->type
= MSGPACK_OBJECT_NEGATIVE_INTEGER
;
121 static inline int template_callback_int32(unpack_user
* u
, int32_t d
, msgpack_object
* o
)
125 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
126 o
->via
.u64
= (uint64_t)d
;
130 o
->type
= MSGPACK_OBJECT_NEGATIVE_INTEGER
;
136 static inline int template_callback_int64(unpack_user
* u
, int64_t d
, msgpack_object
* o
)
140 o
->type
= MSGPACK_OBJECT_POSITIVE_INTEGER
;
141 o
->via
.u64
= (uint64_t)d
;
145 o
->type
= MSGPACK_OBJECT_NEGATIVE_INTEGER
;
151 static inline int template_callback_float(unpack_user
* u
, float d
, msgpack_object
* o
)
154 o
->type
= MSGPACK_OBJECT_FLOAT32
;
159 static inline int template_callback_double(unpack_user
* u
, double d
, msgpack_object
* o
)
162 o
->type
= MSGPACK_OBJECT_FLOAT64
;
167 static inline int template_callback_nil(unpack_user
* u
, msgpack_object
* o
)
170 o
->type
= MSGPACK_OBJECT_NIL
;
174 static inline int template_callback_true(unpack_user
* u
, msgpack_object
* o
)
177 o
->type
= MSGPACK_OBJECT_BOOLEAN
;
178 o
->via
.boolean
= true;
182 static inline int template_callback_false(unpack_user
* u
, msgpack_object
* o
)
185 o
->type
= MSGPACK_OBJECT_BOOLEAN
;
186 o
->via
.boolean
= false;
190 static inline int template_callback_array(unpack_user
* u
, unsigned int n
, msgpack_object
* o
)
193 // Let's leverage the fact that sizeof(msgpack_object) is a compile time constant
194 // to check for int overflows.
195 // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object)
196 // might not be constrained to 4GB on 64-bit systems
197 #if SIZE_MAX == UINT_MAX
198 if (n
> SIZE_MAX
/sizeof(msgpack_object
))
199 return MSGPACK_UNPACK_NOMEM_ERROR
;
202 o
->type
= MSGPACK_OBJECT_ARRAY
;
203 o
->via
.array
.size
= 0;
205 size
= n
* sizeof(msgpack_object
);
208 *u
->z
= msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE
);
210 return MSGPACK_UNPACK_NOMEM_ERROR
;
214 // Unsure whether size = 0 should be an error, and if so, what to return
215 o
->via
.array
.ptr
= (msgpack_object
*)msgpack_zone_malloc(*u
->z
, size
);
216 if(o
->via
.array
.ptr
== NULL
) { return MSGPACK_UNPACK_NOMEM_ERROR
; }
220 static inline int template_callback_array_item(unpack_user
* u
, msgpack_object
* c
, msgpack_object o
)
223 #if defined(__GNUC__) && !defined(__clang__)
224 memcpy(&c
->via
.array
.ptr
[c
->via
.array
.size
], &o
, sizeof(msgpack_object
));
225 #else /* __GNUC__ && !__clang__ */
226 c
->via
.array
.ptr
[c
->via
.array
.size
] = o
;
227 #endif /* __GNUC__ && !__clang__ */
232 static inline int template_callback_map(unpack_user
* u
, unsigned int n
, msgpack_object
* o
)
235 // Let's leverage the fact that sizeof(msgpack_object_kv) is a compile time constant
236 // to check for int overflows
237 // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object)
238 // might not be constrained to 4GB on 64-bit systems
240 // Note - this will always be false on 64-bit systems
241 #if SIZE_MAX == UINT_MAX
242 if (n
> SIZE_MAX
/sizeof(msgpack_object_kv
))
243 return MSGPACK_UNPACK_NOMEM_ERROR
;
246 o
->type
= MSGPACK_OBJECT_MAP
;
249 size
= n
* sizeof(msgpack_object_kv
);
252 *u
->z
= msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE
);
254 return MSGPACK_UNPACK_NOMEM_ERROR
;
258 // Should size = 0 be an error? If so, what error to return?
259 o
->via
.map
.ptr
= (msgpack_object_kv
*)msgpack_zone_malloc(*u
->z
, size
);
260 if(o
->via
.map
.ptr
== NULL
) { return MSGPACK_UNPACK_NOMEM_ERROR
; }
264 static inline int template_callback_map_item(unpack_user
* u
, msgpack_object
* c
, msgpack_object k
, msgpack_object v
)
267 #if defined(__GNUC__) && !defined(__clang__)
268 memcpy(&c
->via
.map
.ptr
[c
->via
.map
.size
].key
, &k
, sizeof(msgpack_object
));
269 memcpy(&c
->via
.map
.ptr
[c
->via
.map
.size
].val
, &v
, sizeof(msgpack_object
));
270 #else /* __GNUC__ && !__clang__ */
271 c
->via
.map
.ptr
[c
->via
.map
.size
].key
= k
;
272 c
->via
.map
.ptr
[c
->via
.map
.size
].val
= v
;
273 #endif /* __GNUC__ && !__clang__ */
278 static inline int template_callback_str(unpack_user
* u
, const char* b
, const char* p
, unsigned int l
, msgpack_object
* o
)
282 *u
->z
= msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE
);
284 return MSGPACK_UNPACK_NOMEM_ERROR
;
287 o
->type
= MSGPACK_OBJECT_STR
;
290 u
->referenced
= true;
294 static inline int template_callback_bin(unpack_user
* u
, const char* b
, const char* p
, unsigned int l
, msgpack_object
* o
)
298 *u
->z
= msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE
);
300 return MSGPACK_UNPACK_NOMEM_ERROR
;
303 o
->type
= MSGPACK_OBJECT_BIN
;
306 u
->referenced
= true;
310 static inline int template_callback_ext(unpack_user
* u
, const char* b
, const char* p
, unsigned int l
, msgpack_object
* o
)
314 return MSGPACK_UNPACK_PARSE_ERROR
;
317 *u
->z
= msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE
);
319 return MSGPACK_UNPACK_NOMEM_ERROR
;
322 o
->type
= MSGPACK_OBJECT_EXT
;
323 o
->via
.ext
.type
= *p
;
324 o
->via
.ext
.ptr
= p
+ 1;
325 o
->via
.ext
.size
= l
- 1;
326 u
->referenced
= true;
330 #include "vendor/msgpack/unpack_template.h"
333 #define CTX_CAST(m) ((template_context*)(m))
334 #define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced
336 #define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t))
339 static inline void init_count(void* buffer
)
341 *(volatile _msgpack_atomic_counter_t
*)buffer
= 1;
344 static inline void decr_count(void* buffer
)
346 // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); }
347 if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t
*)buffer
) == 0) {
352 static inline void incr_count(void* buffer
)
354 // atomic ++*(_msgpack_atomic_counter_t*)buffer;
355 _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t
*)buffer
);
358 static inline _msgpack_atomic_counter_t
get_count(void* buffer
)
360 return *(volatile _msgpack_atomic_counter_t
*)buffer
;
363 bool msgpack_unpacker_init(msgpack_unpacker
* mpac
, size_t initial_buffer_size
)
368 if(initial_buffer_size
< COUNTER_SIZE
) {
369 initial_buffer_size
= COUNTER_SIZE
;
372 buffer
= (char*)malloc(initial_buffer_size
);
377 ctx
= malloc(sizeof(template_context
));
383 mpac
->buffer
= buffer
;
384 mpac
->used
= COUNTER_SIZE
;
385 mpac
->free
= initial_buffer_size
- mpac
->used
;
386 mpac
->off
= COUNTER_SIZE
;
388 mpac
->initial_buffer_size
= initial_buffer_size
;
392 init_count(mpac
->buffer
);
394 template_init(CTX_CAST(mpac
->ctx
));
395 CTX_CAST(mpac
->ctx
)->user
.z
= &mpac
->z
;
396 CTX_CAST(mpac
->ctx
)->user
.referenced
= false;
401 void msgpack_unpacker_destroy(msgpack_unpacker
* mpac
)
403 msgpack_zone_free(mpac
->z
);
405 decr_count(mpac
->buffer
);
408 msgpack_unpacker
* msgpack_unpacker_new(size_t initial_buffer_size
)
410 msgpack_unpacker
* mpac
= (msgpack_unpacker
*)malloc(sizeof(msgpack_unpacker
));
415 if(!msgpack_unpacker_init(mpac
, initial_buffer_size
)) {
423 void msgpack_unpacker_free(msgpack_unpacker
* mpac
)
425 msgpack_unpacker_destroy(mpac
);
429 bool msgpack_unpacker_expand_buffer(msgpack_unpacker
* mpac
, size_t size
)
431 if(mpac
->used
== mpac
->off
&& get_count(mpac
->buffer
) == 1
432 && !CTX_REFERENCED(mpac
)) {
434 mpac
->free
+= mpac
->used
- COUNTER_SIZE
;
435 mpac
->used
= COUNTER_SIZE
;
436 mpac
->off
= COUNTER_SIZE
;
438 if(mpac
->free
>= size
) {
443 if(mpac
->off
== COUNTER_SIZE
) {
445 size_t next_size
= (mpac
->used
+ mpac
->free
) * 2; // include COUNTER_SIZE
446 while(next_size
< size
+ mpac
->used
) {
447 size_t tmp_next_size
= next_size
* 2;
448 if (tmp_next_size
<= next_size
) {
449 next_size
= size
+ mpac
->used
;
452 next_size
= tmp_next_size
;
455 tmp
= (char*)realloc(mpac
->buffer
, next_size
);
461 mpac
->free
= next_size
- mpac
->used
;
465 size_t next_size
= mpac
->initial_buffer_size
; // include COUNTER_SIZE
466 size_t not_parsed
= mpac
->used
- mpac
->off
;
467 while(next_size
< size
+ not_parsed
+ COUNTER_SIZE
) {
468 size_t tmp_next_size
= next_size
* 2;
469 if (tmp_next_size
<= next_size
) {
470 next_size
= size
+ not_parsed
+ COUNTER_SIZE
;
473 next_size
= tmp_next_size
;
476 tmp
= (char*)malloc(next_size
);
483 memcpy(tmp
+COUNTER_SIZE
, mpac
->buffer
+mpac
->off
, not_parsed
);
485 if(CTX_REFERENCED(mpac
)) {
486 if(!msgpack_zone_push_finalizer(mpac
->z
, decr_count
, mpac
->buffer
)) {
490 CTX_REFERENCED(mpac
) = false;
492 decr_count(mpac
->buffer
);
496 mpac
->used
= not_parsed
+ COUNTER_SIZE
;
497 mpac
->free
= next_size
- mpac
->used
;
498 mpac
->off
= COUNTER_SIZE
;
504 int msgpack_unpacker_execute(msgpack_unpacker
* mpac
)
506 size_t off
= mpac
->off
;
507 int ret
= template_execute(CTX_CAST(mpac
->ctx
),
508 mpac
->buffer
, mpac
->used
, &mpac
->off
);
509 if(mpac
->off
> off
) {
510 mpac
->parsed
+= mpac
->off
- off
;
515 msgpack_object
msgpack_unpacker_data(msgpack_unpacker
* mpac
)
517 return template_data(CTX_CAST(mpac
->ctx
));
520 msgpack_zone
* msgpack_unpacker_release_zone(msgpack_unpacker
* mpac
)
522 msgpack_zone
* old
= mpac
->z
;
524 if (old
== NULL
) return NULL
;
525 if(!msgpack_unpacker_flush_zone(mpac
)) {
530 CTX_CAST(mpac
->ctx
)->user
.z
= &mpac
->z
;
535 void msgpack_unpacker_reset_zone(msgpack_unpacker
* mpac
)
537 msgpack_zone_clear(mpac
->z
);
540 bool msgpack_unpacker_flush_zone(msgpack_unpacker
* mpac
)
542 if(CTX_REFERENCED(mpac
)) {
543 if(!msgpack_zone_push_finalizer(mpac
->z
, decr_count
, mpac
->buffer
)) {
546 CTX_REFERENCED(mpac
) = false;
548 incr_count(mpac
->buffer
);
554 void msgpack_unpacker_reset(msgpack_unpacker
* mpac
)
556 template_init(CTX_CAST(mpac
->ctx
));
557 // don't reset referenced flag
561 static inline msgpack_unpack_return
unpacker_next(msgpack_unpacker
* mpac
,
562 msgpack_unpacked
* result
)
566 msgpack_unpacked_destroy(result
);
568 ret
= msgpack_unpacker_execute(mpac
);
572 memset(&result
->data
, 0, sizeof(msgpack_object
));
573 return (msgpack_unpack_return
)ret
;
577 return MSGPACK_UNPACK_CONTINUE
;
579 result
->zone
= msgpack_unpacker_release_zone(mpac
);
580 result
->data
= msgpack_unpacker_data(mpac
);
582 return MSGPACK_UNPACK_SUCCESS
;
585 msgpack_unpack_return
msgpack_unpacker_next(msgpack_unpacker
* mpac
,
586 msgpack_unpacked
* result
)
588 msgpack_unpack_return ret
;
590 ret
= unpacker_next(mpac
, result
);
591 if (ret
== MSGPACK_UNPACK_SUCCESS
) {
592 msgpack_unpacker_reset(mpac
);
598 msgpack_unpack_return
599 msgpack_unpacker_next_with_size(msgpack_unpacker
* mpac
,
600 msgpack_unpacked
* result
, size_t *p_bytes
)
602 msgpack_unpack_return ret
;
604 ret
= unpacker_next(mpac
, result
);
605 if (ret
== MSGPACK_UNPACK_SUCCESS
|| ret
== MSGPACK_UNPACK_CONTINUE
) {
606 *p_bytes
= mpac
->parsed
;
609 if (ret
== MSGPACK_UNPACK_SUCCESS
) {
610 msgpack_unpacker_reset(mpac
);
616 msgpack_unpack_return
617 msgpack_unpack(const char* data
, size_t len
, size_t* off
,
618 msgpack_zone
* result_zone
, msgpack_object
* result
)
621 if(off
!= NULL
) { noff
= *off
; }
625 return MSGPACK_UNPACK_CONTINUE
;
629 template_context ctx
;
632 ctx
.user
.z
= &result_zone
;
633 ctx
.user
.referenced
= false;
635 e
= template_execute(&ctx
, data
, len
, &noff
);
637 return (msgpack_unpack_return
)e
;
640 if(off
!= NULL
) { *off
= noff
; }
643 return MSGPACK_UNPACK_CONTINUE
;
646 *result
= template_data(&ctx
);
649 return MSGPACK_UNPACK_EXTRA_BYTES
;
652 return MSGPACK_UNPACK_SUCCESS
;
656 msgpack_unpack_return
657 msgpack_unpack_next(msgpack_unpacked
* result
,
658 const char* data
, size_t len
, size_t* off
)
661 msgpack_unpacked_destroy(result
);
663 if(off
!= NULL
) { noff
= *off
; }
666 return MSGPACK_UNPACK_CONTINUE
;
671 template_context ctx
;
674 ctx
.user
.z
= &result
->zone
;
675 ctx
.user
.referenced
= false;
677 e
= template_execute(&ctx
, data
, len
, &noff
);
679 if(off
!= NULL
) { *off
= noff
; }
682 msgpack_zone_free(result
->zone
);
684 return (msgpack_unpack_return
)e
;
688 return MSGPACK_UNPACK_CONTINUE
;
691 result
->data
= template_data(&ctx
);
693 return MSGPACK_UNPACK_SUCCESS
;
697 #if defined(MSGPACK_OLD_COMPILER_BUS_ERROR_WORKAROUND)
698 // FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc.
699 static void dummy_function_to_avoid_bus_error()