aboutsummaryrefslogtreecommitdiff
path: root/msgpack.h
blob: fc3ab9ae0b49468fb470d1b57600a2d7b90095ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#ifndef MSGPACK_H
#define MSGPACK_H

#include <stddef.h>
#include <stdint.h>

/* -- Data structures --
 *
 * These structs are used for decoding the MessagePack format.
 * They follow the format specification and their members are as
 * straight-forward as possible.
 *
 * They are meant to be stack allocated, and be passed as pointers
 * to the appropriate function and be filled. This library does no
 * memory allocations by default (except one function that returns
 * a generic and needs a function ptr to an allocator to be passed
 * as an argument, see 'msgpack_read(...)')
 *
 * msgpack_t:
 *   - stores the current substring and its size
 *   - can use a membuf to cache the length of
 *     it's substrings
 *   - created with 'msgpack_init' macro, by pasing
 *     the binary string, size, and optional membuf
 *     (set to null if not using it)
 *
 * struct msgpack_membuf:
 *   - used to store substring length
 *   - passed down other 'msgpack_t's
 *     in arrays and maps
 *   - created with 'membuf_init' macro by
 *     passing the starting address of the
 *     binary string as an 'uintptr_t' and
 *     an already allocated size_t *, with
 *     as many members as bytes in the bin
 *     string
 *     
 * ...
 *
 */

typedef struct mp_msgpack {
    char *bin;
    size_t size;
    
    struct mp_membuf {
        uintptr_t offset;
        size_t *buf;
    } *membuf;
} msgpack_t;

#define msgpack_init(bin, size, membuf) \
    ((msgpack_t){(bin), (size), (membuf)})
#define membuf_init(uintptr_bin, size_t_buf) \
    ((struct mp_membuf){(offset), (buf)})

union mp_int {
    int64_t  i;
    uint64_t u;
};

union mp_float {
    float f;
    double d;
    uint64_t _u;
};

struct mp_array {
    msgpack_t *members;
    size_t length;
};

struct mp_map {
    msgpack_t *keys;
    msgpack_t *members;
    size_t length;
};

struct mp_bin {
    char* bin;
    size_t size;
};

struct mp_timestamp {
    // ...
};


/* -- API for this library --
 *
 * Arguments:
 *   - pack is a ptr to a struct that hold the msgpack binary
 *   - m is a ptr to allocated memory that will be filled by the function
 *
 * Return value:
 *   - The [2-0] bits are the type of the
 *     read value (useful for msgpack_read);
 *   - If an error is encountered, the sign bit
 *     is set, and;
 *   - The [5-3] bits are the error code.
 *
 *   Use the MSGPACK_CHECK functions to easily
 *   manage errors.
 *
 * Example:
 *   0b|1|...|00000000|001|101
 *      |  |      |     |   |
 *     err |  byte info |  type[5]
 *         |            |
 *    not important  error[1]
 *
 */

int msgpack_read_int     (const msgpack_t *pack, union mp_int   *m);
int msgpack_read_bool    (const msgpack_t *pack,       int      *m);
int msgpack_read_float   (const msgpack_t *pack, union mp_float *m);

int msgpack_read_raw     (const msgpack_t *pack, struct mp_bin *m);
int msgpack_read_raw_cpy (const msgpack_t *pack, struct mp_bin *m);
int msgpack_read_ext     (const msgpack_t *pack, struct mp_bin *m);
int msgpack_read_ext_cpy (const msgpack_t *pack, struct mp_bin *m);

int msgpack_read_array   (msgpack_t *pack, size_t *length);
int msgpack_read_map     (msgpack_t *pack, size_t *length);
int msgpack_read_array2  (const msgpack_t *pack, struct mp_array *m);
int msgpack_read_map2    (const msgpack_t *pack, struct mp_map   *m);


int msgpack_write_int     (msgpack_t *pack, const union mp_int   *m, int subtype);
int msgpack_write_bool    (msgpack_t *pack, const       int      *m);
int msgpack_write_float   (msgpack_t *pack, const union mp_float *m, int subtype);

int msgpack_write_raw     (msgpack_t *pack, const struct mp_bin  *m, int subtype);
int msgpack_write_ext     (msgpack_t *pack, const struct mp_bin  *m, int subtype);

int msgpack_write_array   (msgpack_t *pack, const size_t *length);
int msgpack_write_map     (msgpack_t *pack, const size_t *length);

/* -- MessagePack Types --
 *
 * These types are defined by the messagepack specification
 * and X-Marcros are used to generate enums and arrays with
 * their string counterparts.
 *
 * There are subtypes for some of the main types,
 * which are not present in the specificiation, but
 * are can be useful for the client
 *
 */

#define MSGPACK_TYPES(X) \
    X(MSGPACK_NIL)       \
    X(MSGPACK_INT)       \
    X(MSGPACK_BOOL)      \
    X(MSGPACK_FLOAT)     \
    X(MSGPACK_RAW)       \
    X(MSGPACK_ARRAY)     \
    X(MSGPACK_MAP)       \
    X(MSGPACK_EXT)       \
    X(MSGPACK_UNKNOWN)   \

#define MSGPACK_SUBTYPES(X)  \
    X(MSGPACK_INT_SIGNED)    \
    X(MSGPACK_INT_UNSIGNED)  \
    X(MSGPACK_FLOAT_32)      \
    X(MSGPACK_FLOAT_64)      \
    X(MSGPACK_RAW_STRING)    \
    X(MSGPACK_RAW_BIN)       \
    X(MSGPACK_EXT_TIMESTAMP)

#define MSGPACK_ERRORS(X)                  \
    X(MSGPACK_ERROR_UNKNOWN)               \
    X(MSGPACK_ERROR_WRONG_TYPE)            \
    X(MSGPACK_ERROR_UNSUFFICIENT_CAPACITY) \
    X(MSGPACK_ERROR_UNEXPECTED_END)        \
    X(MSGPACK_ERROR_INVALID_PACK)          \
    X(MSGPACK_ERROR_INVALID_ARGUMENT)

#define X_TO_ENUM(e) e, 
#define X_TO_STRING(e) [e] = #e,

enum msgpack_type {
    MSGPACK_TYPES(X_TO_ENUM)
    MSGPACK_SUBTYPES(X_TO_ENUM)
};

enum msgpack_error {
    MSGPACK_ERRORS(X_TO_ENUM)
};

static const char * const msgpack_type_string[] = {
    MSGPACK_TYPES(X_TO_STRING)
    MSGPACK_SUBTYPES(X_TO_STRING)
};
static const char * const msgpack_error_string[] = {
    MSGPACK_ERRORS(X_TO_STRING)
};

/* -- Macros for error handling --
 *
 * Argumnts:
 *   - call is the function call to a msgpack_*** function;
 *   - body_*** is a scope that is invoked upon error or success,
 *     it uses the variable names from the 'arg' varible;
 *   - arg is the name of the variables that hold the:
 *       - msgpack type;
 *       - msgpack error code;
 *       - extra byte data,
 *     which are extracted from the return value of the function call.
 *
 * Example:
 *   MSGPACK_CHECK(msgpack_read_bool(&pack, &b), (t, e, a), {
 *       if(e == MSGPACK_ERROR_WRONG_TYPE) {
 *           err("msgpack: %s, expected type %s, but got type %s",
 *               msgpack_error_string[e],
 *               msgpack_type_string[a],
 *               msgpack_type_string[t]);
 *       }
 *   });
 *
 */



#define __EXPAND(...) __VA_ARGS__
#define __MSGPACK_CHECK_DEFER(...) __MSGPACK_CHECK(__VA_ARGS__)
#define __MSGPACK_CHECK(call, body_suc, body_err, type_var, err_var, rest_var) \
    do {                                     \
        int __r = (call);                    \
        int (type_var) =  __r       & 0x7;   \
        int (err_var)  = (__r >> 3) & 0x7;   \
        int (rest_var) = (__r >> 6) & 0xFF;  \
        if(__r >= 0) { body_suc; }           \
        else         { body_err; }           \
    } while(0)

#define MSGPACK_CHECK(call, arg, body_err) \
    __MSGPACK_CHECK_DEFER(call, ;, body_err, __EXPAND arg)
#define MSGPACK_CHECK2(call, arg, body_suc, body_err) \
    __MSGPACK_CHECK_DEFER(call, body_suc, body_err, __EXPAND arg)

#endif