-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathproto.h
153 lines (135 loc) · 3.13 KB
/
proto.h
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
#ifndef LIBWATCHMAN_PROTO_H_
#define LIBWATCHMAN_PROTO_H_
#include <jansson.h>
#include "bser.h"
#include "bser_parse.h"
/* A wrapper around json (jansson) or bser */
enum { PROTO_BSER, PROTO_JSON };
typedef struct proto_ptr {
union {
json_t* json;
bser_t* bser;
} u;
int type;
} proto_t;
proto_t
proto_from_json(json_t* json)
{
proto_t proto;
proto.u.json = json;
proto.type = PROTO_JSON;
return proto;
}
proto_t
proto_from_bser(bser_t* bser)
{
proto_t proto;
proto.u.bser = bser;
proto.type = PROTO_BSER;
return proto;
}
proto_t
proto_null()
{
proto_t proto;
proto.u.json = NULL;
return proto;
}
int
proto_is_null(proto_t p)
{
return p.u.json == NULL;
}
#define PROTO_DISPATCH(ret, name) \
ret \
proto_##name(proto_t p) \
{ \
return p.type == PROTO_JSON ? \
json_##name(p.u.json) : \
bser_##name(p.u.bser); \
}
PROTO_DISPATCH(int, is_boolean)
PROTO_DISPATCH(int, is_integer)
PROTO_DISPATCH(int, is_real)
PROTO_DISPATCH(int, is_string)
PROTO_DISPATCH(int, is_array)
PROTO_DISPATCH(int, is_object)
PROTO_DISPATCH(int, is_true)
PROTO_DISPATCH(int64_t, integer_value)
PROTO_DISPATCH(double, real_value)
PROTO_DISPATCH(int, array_size)
#undef PROTO_DISPATCH
/* Returns a potentially non-null-terminated read-only string, with a length */
const char*
proto_string_value(proto_t p, size_t* length)
{
if (p.type == PROTO_JSON) {
const char* v = json_string_value(p.u.json);
*length = strlen(v);
return v;
} else {
return bser_string_value(p.u.bser, length);
}
}
/* Returns a dynamically-allocated null-terminated c-string (caller-owned) */
char*
proto_strdup(proto_t p)
{
size_t length;
const char* v = proto_string_value(p, &length);
if (v[length] == '\0') {
return strdup(v);
} else {
char* res = malloc(length + 1);
memcpy(res, v, length);
res[length] = '\0';
return res;
}
}
proto_t
proto_array_get(proto_t p, int index)
{
return p.type == PROTO_JSON ?
proto_from_json(json_array_get(p.u.json, index)) :
proto_from_bser(bser_array_get(p.u.bser, index));
}
proto_t
proto_object_get(proto_t p, const char* key)
{
return p.type == PROTO_JSON ?
proto_from_json(json_object_get(p.u.json, key)) :
proto_from_bser(bser_object_get(p.u.bser, key));
}
char*
proto_dumps(proto_t p, int flags)
{
json_t* json = p.u.json;
char* result;
if (p.type == PROTO_BSER) {
json_error_t err;
json = bser2json(p.u.bser, &err);
if (json == NULL) {
json = json_string(err.text);
flags |= JSON_ENCODE_ANY;
}
}
result = json_dumps(json, flags);
if (p.type == PROTO_BSER) {
json_decref(json);
}
return result;
}
/* The proto_free command only decrefs the json object in json mode, so
* it's only a true free if the json only has one reference. */
void
proto_free(proto_t p)
{
if (p.type == PROTO_JSON) {
json_decref(p.u.json);
p.u.json = NULL;
} else {
bser_free(p.u.bser);
p.u.bser = NULL;
}
}
#endif /* ndef LIBWATCHMAN_PROTO_H_ */