aboutsummaryrefslogtreecommitdiff
path: root/msgpack.c
blob: 07433b13281bd052472312c6c1723070b769b05a (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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#include "msgpack.h"
#include <endian.h>  // endianness
#include <string.h>  // memcpy

// TODO: it works pretty good, but some parts
//       are needlessly complicated, not good

// handy macros for extra expansion
#define _(...) __VA_ARGS__
#define _CALL(f, ...) f(__VA_ARGS__)
#define CAR(a, b) a
#define CDR(a, b) b

// converions to and from big endian, including for floats
#define INT  int
#define UINT uint

#define htobe8(i) (i)
#define be8toh(i) (i)
#define _HTOI(bits, i) htobe##bits(i)
#define _ITOH(bits, i) be##bits##toh(i)

#define CONV_ALL(...) __VA_ARGS__
#define CONV_TYPE(t, b) t##b##_t
#define CONV_BITS(t, b) b

#define ITOH(c, i)       ((CONV_TYPE c)_CALL(_ITOH, CONV_BITS c, i))
#define HTOI(c, h)       ((CONV_TYPE c)_CALL(_HTOI, CONV_BITS c, i))
#define PTOH(c, p)       ((CONV_TYPE c)_CALL(_ITOH, CONV_BITS c, (*(CONV_TYPE c*)(p))))
#define HTOP2(c, h, p)   (((*(CONV_TYPE c *)(p)) = _CALL(_HTOI, CONV_BITS c, h)))

#define ITOF(bits, v) (((union {uint32_t u32; float f32; uint64_t u64; double f64;}){.u##bits = v}).f##bits)
#define FTOI(bits, v) (((union {uint32_t u32; float f32; uint64_t u64; double f64;}){.f##bits = v}).u##bits)

#define PTOH_F(c, p)     _CALL(ITOF, CONV_ALL c, PTOH((UINT, CONV_ALL c), p))     // possibly remove
#define HTOP2_F(c, f, p) HTOP2((UINT, CONV_ALL c), _CALL(FTOI, CONV_ALL c, f), p) // possibly remove

// range low, range high
#define RANGES(X) \
    X(0x00, 0x7F) \
    X(0x80, 0x8F) \
    X(0x90, 0x9F) \
    X(0xA0, 0xBF) \
    X(0xE0, 0xFF)

// byte, format, type
#define FORMAT_NIL(X) \
    X(0xC0, FMT_NIL,     MSGPACK_NIL)
#define FORMAT_UNKNOWN(X) \
    X(0xC1, FMT_UNKNOWN, MSGPACK_UNKNOWN)

// byte, format, type, data
#define FORMAT_BOOL(X)                           \
    X(0xC2, FMT_BOOL_TRUE,  MSGPACK_BOOL, true)  \
    X(0xC3, FMT_BOOL_FALSE, MSGPACK_BOOL, false)

// byte, format, type, subtype, offset, lengh, field, conv, range
#define FORMAT_INT(X) \
    X(0x00, FMT_FIX_UINT, MSGPACK_INT, MSGPACK_INT_UNSIGNED, 0, 1, u, (UINT, 8),  (0,         127))      \
    X(0XE0, FMT_FIX_INT,  MSGPACK_INT, MSGPACK_INT_SIGNED,   0, 1, i, (INT,  8),  (-32,       -1))       \
    X(0xCC, FMT_U8,       MSGPACK_INT, MSGPACK_INT_UNSIGNED, 1, 1, u, (UINT, 8),  (0,         UINT8_MAX)) \
    X(0xCD, FMT_U16,      MSGPACK_INT, MSGPACK_INT_UNSIGNED, 1, 2, u, (UINT, 16), (0,         UINT16_MAX)) \
    X(0xCE, FMT_U32,      MSGPACK_INT, MSGPACK_INT_UNSIGNED, 1, 4, u, (UINT, 32), (0,         UINT32_MAX)) \
    X(0xCF, FMT_U64,      MSGPACK_INT, MSGPACK_INT_UNSIGNED, 1, 8, u, (UINT, 64), (0,         UINT64_MAX)) \
    X(0xD0, FMT_I8,       MSGPACK_INT, MSGPACK_INT_SIGNED,   1, 1, i, (INT,  8),  (INT8_MIN,  INT8_MAX)) \
    X(0xD1, FMT_I16,      MSGPACK_INT, MSGPACK_INT_SIGNED,   1, 2, i, (INT,  16), (INT16_MIN, INT16_MAX)) \
    X(0xD2, FMT_I32,      MSGPACK_INT, MSGPACK_INT_SIGNED,   1, 4, i, (INT,  32), (INT32_MIN, INT32_MAX)) \
    X(0xD3, FMT_I64,      MSGPACK_INT, MSGPACK_INT_SIGNED,   1, 8, i, (INT,  64), (INT64_MIN, INT64_MAX))

// byte, format, type, subtype, offset, length, field, conv
#define FORMAT_FLOAT(X)                                                 \
    X(0xCA, FMT_FLOAT,  MSGPACK_FLOAT, MSGPACK_FLOAT_32, 1, 4, f, (32)) \
    X(0xCB, FMT_DOUBLE, MSGPACK_FLOAT, MSGPACK_FLOAT_64, 1, 8, d, (64))

// byte, fmt, type, subtype, offset, conv, max
#define FORMAT_RAW(X)                                                                  \
    X(0xA0, FMT_FIX_STR,   MSGPACK_RAW, MSGPACK_RAW_STRING, 1, (UINT, 8),  31)         \
    X(0xC4, FMT_BIN8,      MSGPACK_RAW, MSGPACK_RAW_BIN,    2, (UINT, 8),  UINT8_MAX)  \
    X(0xC5, FMT_BIN16,     MSGPACK_RAW, MSGPACK_RAW_BIN,    3, (UINT, 16), UINT16_MAX) \
    X(0xC6, FMT_BIN32,     MSGPACK_RAW, MSGPACK_RAW_BIN,    5, (UINT, 32), UINT32_MAX) \
    X(0xD9, FMT_STR8,      MSGPACK_RAW, MSGPACK_RAW_STRING, 2, (UINT, 8),  UINT8_MAX)  \
    X(0xDA, FMT_STR16,     MSGPACK_RAW, MSGPACK_RAW_STRING, 3, (UINT, 16), UINT16_MAX) \
    X(0xDB, FMT_STR32,     MSGPACK_RAW, MSGPACK_RAW_STRING, 5, (UINT, 32), UINT32_MAX)

// byte, fmt, type, offset, conv, lenght
#define FORMAT_ARRAY(X) \
    X(0x90, FMT_FIX_ARRAY, MSGPACK_ARRAY, 1, (UINT, 8),  15) \
    X(0xDC, FMT_ARRAY16,   MSGPACK_ARRAY, 3, (UINT, 16), UINT16_MAX) \
    X(0xDD, FMT_ARRAY32,   MSGPACK_ARRAY, 5, (UINT, 32), UINT32_MAX)
#define FORMAT_MAP(X) \
    X(0x80, FMT_FIX_MAP, MSGPACK_MAP, 1, (UINT, 8),  15) \
    X(0xDE, FMT_MAP16,   MSGPACK_MAP, 3, (UINT, 16), UINT16_MAX) \
    X(0xDF, FMT_MAP32,   MSGPACK_MAP, 5, (UINT, 32), UINT32_MAX)

// #define FORMAT_EXT(X)

#define FORMATS(X)    \
    FORMAT_NIL    (X) \
    FORMAT_UNKNOWN(X) \
    FORMAT_BOOL   (X) \
    FORMAT_INT    (X) \
    FORMAT_FLOAT  (X) \
    FORMAT_RAW    (X) \
    FORMAT_ARRAY  (X) \
    FORMAT_MAP    (X) \
    // FORMAT_EXT    (X)

#define X_FMT_ENUM(_byte, fmt, ...)       fmt,
#define X_FMT_TYPE(_byte, fmt, type, ...) [fmt] = type,
#define X_BYTE_FMT( byte, fmt, ...)       [byte] = fmt,

enum msgpack_fmt {
    FORMATS(X_FMT_ENUM)
};

static const enum msgpack_type fmt_to_type[] = {
    FORMATS(X_FMT_TYPE)
};

static const enum msgpack_fmt byte_to_fmt[] = {
    FORMATS(X_BYTE_FMT)
};

// --- helper macros --- //

#define SUCCESS(type) SUCCESS2(type, 0)
#define SUCCESS2(type, rest) \
    (((rest) << 6) | (type))

#define ERROR(type, err) ERROR2(type, err, 0)
#define ERROR2(type, err, rest) \
    ((1 << (sizeof(int)*8-1))| ((rest) << 6) | ((err) << 3) | (type))

#define ENOUGH_BYTES(pack, n, on_fail) \
    if((pack)->size < (n)) { on_fail; }
#define MOVE_PACK(pack, sz)   \
    do {                      \
        (pack)->bin  += (sz); \
        (pack)->size -= (sz); \
    } while(0)

static enum msgpack_fmt pack_fmt(msgpack_t *pack);
static enum msgpack_fmt data_fmt(enum msgpack_type type, enum msgpack_type subtype, const void *m);

#define FMT_FOR_READ(pack, type, subtype, m)  pack_fmt(pack)
#define FMT_FOR_WRITE(pack, type, subtype, m) data_fmt(type, subtype, m)

// --- function composition --- //

// these will be shadowed unless missing in parameters
static const void * const m = (void *)1;
static const enum msgpack_type subtype;

#define COMPOSE_FUNCTION(op, type)                                        \
    {                                                                     \
        if(!pack || !pack->bin || pack->size == 0)                        \
            return ERROR(MSGPACK_##type, MSGPACK_ERROR_INVALID_PACK);     \
        if(!m)                                                            \
            return ERROR(MSGPACK_##type, MSGPACK_ERROR_INVALID_ARGUMENT); \
                                                                          \
        const enum msgpack_fmt fmt =                                      \
            FMT_FOR_##op(pack, MSGPACK_##type, subtype, m);               \
                                                                          \
        switch(fmt) {                                                     \
            FORMAT_##type(X_##type##_##op)                                \
        default:                                                          \
            return ERROR2(MSGPACK_##type, MSGPACK_ERROR_WRONG_TYPE,       \
                          fmt_to_type[fmt]);                              \
        }                                                                 \
    }

// TODO: refactor these, they are hard to read

#define OP_BOOL_READ(x_byte, pack, m, x_data) \
    *m = x_data
#define OP_BOOL_WRITE(x_byte, pack, m, x_data) \
    pack->bin[0] = x_byte

#define MAKE_X_BOOL(op, x_byte, x_fmt,          \
                    x_type, x_data)             \
    case x_fmt:                                 \
        OP_BOOL_##op(x_byte, pack, m, x_data);  \
        MOVE_PACK(pack, 1);                     \
        return SUCCESS(x_type);


#define OP_INT_READ(_byte, ptr, value, x_conv) \
    value = PTOH(x_conv, ptr)
#define OP_INT_WRITE(x_byte, ptr, value, x_conv) \
    (ptr-1)[0] = x_byte;      /* temp fix*/      \
    HTOP2(x_conv, value, ptr)

#define OP_FLOAT_READ(_byte, ptr, value, x_conv) \
    value = PTOH_F(x_conv, ptr)
#define OP_FLOAT_WRITE(x_byte, ptr, value, x_conv) \
    (ptr-1)[0] = x_byte;      /* temp fix*/        \
    HTOP2_F(x_conv, value, ptr)

#define MAKE_X_INTORFLOAT(type, op, x_byte, x_fmt, x_type, x_subtype,         \
                          x_offset, x_length, x_field, x_conv, ...)           \
    case x_fmt:                                                               \
        ENOUGH_BYTES(pack, (x_offset) + (x_length),                           \
            return ERROR2(x_type, MSGPACK_ERROR_UNEXPECTED_END, x_subtype));  \
        OP_##type##_##op(x_byte, pack->bin + (x_offset), m->x_field, x_conv); \
                                                                              \
        MOVE_PACK(pack, (x_offset) + (x_length));                             \
        return SUCCESS2(x_type, x_subtype);


#define OP_RAW_LENGTH_READ(x_byte, x_offset, x_conv) \
    (x_offset == 1)                                  \
        ? PTOH(x_conv, pack->bin) & ~(uint8_t)x_byte \
        : PTOH(x_conv, pack->bin + 1);
#define OP_RAW_LENGTH_WRITE(x_byte, x_offset, x_conv) \
    m->size

#define OP_RAW_READ(x_byte, x_offset, x_conv) \
    m->size = length;                         \
    m->bin = pack->bin + (x_offset);
#define OP_RAW_WRITE(x_byte, x_offset, x_conv)            \
    pack->bin[0] = x_byte;                                \
    if((x_offset) == 1) pack->bin[0] |= (uint8_t)length;  \
    else HTOP2(x_conv, length, pack->bin+1);              \
    memcpy(pack->bin+(x_offset), m->bin, m->size);

#define MAKE_X_RAW(op, x_byte, x_fmt, x_type, x_subtype, x_offset, x_conv, ...) \
    case x_fmt: {                                                               \
        ENOUGH_BYTES(pack, (x_offset),                                          \
            return ERROR2(x_type, MSGPACK_ERROR_UNEXPECTED_END, x_subtype));    \
        size_t length = OP_RAW_LENGTH_##op(x_byte, x_offset, x_conv);           \
                                                                                \
        ENOUGH_BYTES(pack, (x_offset) + length,                                 \
            return ERROR2(x_type, MSGPACK_ERROR_UNEXPECTED_END, x_subtype));    \
        OP_RAW_##op(x_byte, x_offset, x_conv);                                  \
                                                                                \
        MOVE_PACK(pack, (x_offset) + length);                                   \
        return SUCCESS2(x_type, x_subtype);                                     \
    }


#define OP_ARRAYORMAP_READ(x_byte, x_offset, x_conv) \
    *m = OP_RAW_LENGTH_READ(x_byte, x_offset, x_conv)
#define OP_ARRAYORMAP_WRITE(x_byte, x_offset, x_conv) \
    pack->bin[0] = x_byte;                            \
    if((x_offset) == 1) pack->bin[0] |= (uint8_t)*m;  \
    else HTOP2(x_conv, *m, pack->bin+1);              \

#define MAKE_X_ARRAYORMAP(op, x_byte, x_fmt, x_type,              \
                          x_offset, x_conv, ...)                  \
    case x_fmt:                                                   \
        ENOUGH_BYTES(pack, (x_offset),                            \
            return ERROR(x_type, MSGPACK_ERROR_UNEXPECTED_END));  \
        OP_ARRAYORMAP_##op(x_byte, x_offset, x_conv);             \
                                                                  \
        MOVE_PACK(pack, (x_offset));                              \
        return SUCCESS(x_type);

// --- api implemention --- //

#define X_BOOL_READ(...)  MAKE_X_BOOL(READ, __VA_ARGS__)
#define X_INT_READ(...)   MAKE_X_INTORFLOAT(INT,   READ, __VA_ARGS__)
#define X_FLOAT_READ(...) MAKE_X_INTORFLOAT(FLOAT, READ, __VA_ARGS__)
#define X_RAW_READ(...)   MAKE_X_RAW(READ, __VA_ARGS__)
#define X_ARRAY_READ(...) MAKE_X_ARRAYORMAP(READ, __VA_ARGS__)
#define X_MAP_READ(...)   MAKE_X_ARRAYORMAP(READ, __VA_ARGS__)

#define X_BOOL_WRITE(...)  MAKE_X_BOOL(WRITE, __VA_ARGS__)
#define X_INT_WRITE(...)   MAKE_X_INTORFLOAT(INT,   WRITE, __VA_ARGS__)
#define X_FLOAT_WRITE(...) MAKE_X_INTORFLOAT(FLOAT, WRITE, __VA_ARGS__)
#define X_RAW_WRITE(...)   MAKE_X_RAW(WRITE, __VA_ARGS__)
#define X_ARRAY_WRITE(...) MAKE_X_ARRAYORMAP(WRITE, __VA_ARGS__)
#define X_MAP_WRITE(...)   MAKE_X_ARRAYORMAP(WRITE, __VA_ARGS__)

// int msgpack_read_nil   (msgpack_t *pack);
int msgpack_read_bool  (msgpack_t *pack,        bool     *m) COMPOSE_FUNCTION(READ, BOOL)
int msgpack_read_int   (msgpack_t *pack, union  mp_int   *m) COMPOSE_FUNCTION(READ, INT)
int msgpack_read_float (msgpack_t *pack, union  mp_float *m) COMPOSE_FUNCTION(READ, FLOAT)

int msgpack_read_raw   (msgpack_t *pack, struct mp_bin  *m)  COMPOSE_FUNCTION(READ, RAW)
// int msgpack_read_raw_cpy (msgpack_t *pack, struct mp_bin *m);
// int msgpack_read_ext   (msgpack_t *pack, struct mp_bin  *m)  COMPOSE_FUNCTION(READ, EXT)
// int msgpack_read_ext_cpy (msgpack_t *pack, struct mp_bin *m);

int msgpack_read_array (msgpack_t *pack, size_t *m)          COMPOSE_FUNCTION(READ, ARRAY)
int msgpack_read_map   (msgpack_t *pack, size_t *m)          COMPOSE_FUNCTION(READ, MAP)
// 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_nil     (msgpack_t *pack);
int msgpack_write_bool    (msgpack_t *pack, const       bool     *m) COMPOSE_FUNCTION(WRITE, BOOL)
int msgpack_write_int     (msgpack_t *pack, const union mp_int   *m, enum msgpack_type subtype) COMPOSE_FUNCTION(WRITE, INT)
int msgpack_write_float   (msgpack_t *pack, const union mp_float *m, enum msgpack_type subtype) COMPOSE_FUNCTION(WRITE, FLOAT)

int msgpack_write_raw     (msgpack_t *pack, const struct mp_bin  *m, enum msgpack_type subtype) COMPOSE_FUNCTION(WRITE, RAW)
// int msgpack_write_ext     (msgpack_t *pack, const struct mp_bin  *m, enum msgpack_type subtype);

int msgpack_write_array   (msgpack_t *pack, const size_t *m) COMPOSE_FUNCTION(WRITE, ARRAY)
int msgpack_write_map     (msgpack_t *pack, const size_t *m) COMPOSE_FUNCTION(WRITE, MAP)

// --- helpers --- //

#define X_MASK_RANGE(range_low, range_high)         \
    if(byte >= range_low && byte <= range_high)     \
        byte &= ~(uint8_t)(range_high-range_low);   \
    else                                            \

static enum msgpack_fmt pack_fmt(msgpack_t *pack)
{
    uint8_t byte = pack->bin[0];

    RANGES(X_MASK_RANGE);
    return byte_to_fmt[byte];
}

#define WRAP_SWTICH(type, cond, e) case type: switch(cond) { e; default: break; } break;
#define WRAP_IFELSE(type, e)       case type: e; break;

#define WRAP_FMT_BOOL(e) WRAP_SWTICH(MSGPACK_BOOL, (int)*(bool *)m, e);
#define X_FMT_BOOL(_byte, x_fmt, x_type, x_data) \
    case x_data: return x_fmt;

#define WRAP_FMT_INT(e) WRAP_IFELSE(MSGPACK_INT, e)
#define X_FMT_INT(_byte, x_fmt, x_type, x_subtype, _offset, _length, x_field, _conv, x_range) \
    if((x_subtype == subtype) &&                                        \
        (((union mp_int *)m)->x_field >= CAR x_range) &&                \
       (((union mp_int *)m)->x_field <= CDR x_range))                   \
        return x_fmt;                                                   \
    else

#define WRAP_FMT_FLOAT(e) WRAP_SWTICH(MSGPACK_FLOAT, subtype, e)
#define X_FMT_FLOAT(_byte, x_fmt, _type, x_subtype, _offset, _length, _field, _conv) \
    case x_subtype: return x_fmt;

#define WRAP_FMT_RAW(e) WRAP_IFELSE(MSGPACK_RAW, e)
#define X_FMT_RAW(_byte, x_fmt, x_type, x_subtype, _offset, _conv, x_range_max) \
    if((x_subtype == subtype) &&                                        \
       ((struct mp_bin *)m)->size <= x_range_max) return x_fmt;         \
    else

#define WRAP_FMT_ARRAY(e) WRAP_IFELSE(MSGPACK_ARRAY, e)
#define X_FMT_ARRAY(_byte, x_fmt, _type, _offset, _conv, x_range_max) \
    if(*(size_t *)m <= x_range_max) return x_fmt; else

#define WRAP_FMT_MAP(e) WRAP_IFELSE(MSGPACK_MAP, e)
#define X_FMT_MAP(...)  X_FMT_ARRAY(__VA_ARGS__)

#define MAIN_TYPES(X) X(BOOL) X(INT) X(FLOAT) X(RAW) X(ARRAY) X(MAP)
#define X_MAIN_TYPES(type) WRAP_FMT_##type(FORMAT_##type(X_FMT_##type))

static enum msgpack_fmt data_fmt(enum msgpack_type type, enum msgpack_type subtype, const void *m)
{
    switch(type) {
        MAIN_TYPES(X_MAIN_TYPES)
    default: return FMT_UNKNOWN;
    }

    return FMT_UNKNOWN;
}