Browse code

- import the libbinrpc library; this library is a evolution of the initial protocol described into and implemented by the 'ctl' module; - compared to 'binrpc' library, it adds (rather small) improvements to the original protocol, but offers a much more powerful API and incipient (but, in most of the cases enough) networking functionality; - compatibility with the initial protocol (and adjoined 'ctl' module) is planned (until then, will live as a parallel alternative); - original library's developed under http://developer.berlios.de/projects/libbinrpc/ ; - license changed from GPL2 to simplified BSD.

bpi authored on 14/01/2010 21:37:39
Showing 40 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,30 @@
1
+/*
2
+ * Copyright (c) 2010 IPTEGO GmbH.
3
+ * All rights reserved.
4
+ *
5
+ * This file is part of the BinRPC Library (libbinrpc).
6
+ * 
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ * 
10
+ *    1. Redistributions of source code must retain the above copyright
11
+ *       notice, this list of conditions and the following disclaimer.
12
+ * 
13
+ *    2. Redistributions in binary form must reproduce the above copyright
14
+ *       notice, this list of conditions and the following disclaimer in the
15
+ *       documentation and/or other materials provided with the distribution.
16
+ * 
17
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
18
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27
+ * DAMAGE.
28
+ *
29
+ * Author: Bogdan Pintea.
30
+ */
0 31
new file mode 100644
... ...
@@ -0,0 +1,11 @@
1
+
2
+include ../../Makefile.defs
3
+
4
+NAME:=binrpc2
5
+
6
+MAJOR_VER=2
7
+MINOR_VER=0
8
+BUGFIX_VER=0
9
+
10
+include Makefile.defs
11
+include ../../Makefile.libs
0 12
new file mode 100644
... ...
@@ -0,0 +1,77 @@
1
+
2
+MY_OS=$(shell uname -s)
3
+
4
+DEFS+=-Iinclude/
5
+DEFS+=-D_LIBBINRPC_BUILD
6
+
7
+###
8
+# Used flags
9
+#
10
+# NDEBUG				:	release build
11
+# INT_AS_ID				:	allow integer records as attribute identifes 
12
+# 							(besides str)
13
+# FIX_FALSE_GCC_WARNS	:	initialize some vars falsely signaled by GCC
14
+# 							(4.1.1) as potentially used without.
15
+# 							(some may be triggered by pointer usage without
16
+# 							'const' or 'restrict' keyword)
17
+# _BINRPC_REENTRANT		:	enable thread local storage variables (set them 
18
+# 							global)
19
+# LOCKS_INIT_NOP				:	hollow locking; if not set and reentrant,
20
+# 							realtime's locking system will be used
21
+# BINRPC_VER1_COMPAT	:	compatibility with version 1 (TODO: :) )
22
+# USE_DEFAULT_SYSLOG	:	use syslog as default logger, instead of black
23
+# 							hole
24
+#
25
+
26
+ifneq (,$(findstring release,$(mode)))
27
+	DEFS+=-O3 -DNDEBUG
28
+endif
29
+ifneq (,$(findstring strict,$(mode)))
30
+	DEFS+=-Werror
31
+endif
32
+
33
+DEFS+=-DBINRPC_LIB_VER=\"$(MAJOR_VER).$(MINOR_VER)-$(BUGFIX_VER)\"
34
+
35
+DEFS += \
36
+	-DINT_AS_ID \
37
+	#-D_BINRPC_REENTRANT \
38
+	#-DFIX_FALSE_GCC_WARNS \
39
+
40
+ifeq (,$(findstring _BINRPC_REENTRANT,$(DEFS)))
41
+	DEFS+=-DLOCKS_INIT_NOP
42
+else # _BINRPC_REENTRANT
43
+ifeq (,$(findstring LOCKS_INIT_NOP,$(DEFS)))
44
+	LIBS+=-lrt
45
+endif # LOCKS_INIT_NOP
46
+endif # _BINRPC_REENTRANT
47
+
48
+
49
+ifeq ($(MY_OS),Linux)
50
+DEFS+=-DHAVE_IPV4_MAPPED
51
+DEFS+=-DHAVE_GAI_ADDRCONFIG -DWITH_GAI_ADDRCONFIG
52
+DEFS+=-DHAVE_MSG_NOSIGNAL
53
+endif # MY_OS:linux
54
+
55
+ifeq ($(MY_OS),SunOS)
56
+DEFS+=-std=gnu99
57
+DEFS+=-DHAVE_IPV4_MAPPED
58
+DEFS+=-DHAVE_GAI_ADDRCONFIG -DWITH_GAI_ADDRCONFIG
59
+endif # MY_OS:SunOS
60
+
61
+ifeq ($(MY_OS),FreeBSD)
62
+DEFS+=-DHAVE_IN_SYSTM_H -DHAVE_IPV4_MAPPED
63
+DEFS+=-DHAVE_GAI_ADDRCONFIG -DWITH_GAI_ADDRCONFIG
64
+DEFS+=-DHAVE_MSG_NOSIGNAL
65
+endif # MY_OS:FreeBSD
66
+
67
+ifeq ($(MY_OS),NetBSD)
68
+DEFS+=-DHAVE_IN_SYSTM_H
69
+DEFS+=-DHAVE_MSG_NOSIGNAL
70
+endif # MY_OS:NetBSD
71
+
72
+ifeq ($(MY_OS),OpenBSD)
73
+DEFS+=-DHAVE_IN_SYSTM_H
74
+endif # MY_OS:OpenBSD
75
+
76
+.PHONY: distclean tags
77
+
0 78
new file mode 100644
... ...
@@ -0,0 +1,27 @@
1
+[ ] add v1 compatibility
2
+[*] optimize int repr (negatives).
3
+[ ] add speedup code (vs minimal size) (regarding packing to wire)
4
+[ ] add (TLS) object cache for call contexts, call values, cb's req/rpl_cb_t,
5
+	dissectors
6
+[ ] deal with (.,*) for req cb registrations in case of name colision
7
+[ ] increase req:rpl metching reliabilty (increase CL field (cookie len) by 1
8
+    bits from flags (FLAGS fld))
9
+[ ] parse only header|parse values
10
+[ ] fix hdr definition: lenght (&cookie?) always 4 bytes (save 1 syscall)
11
+[ ] floating point support
12
+[ ] reentrant call disassambling|scanning (scanning context, value scanning)
13
+[ ] lazier parsing (if needed) [flags for mname|body parse status]
14
+[ ] don't deallocate raw buffer
15
+[ ] check protocol version when receving
16
+[ ] if not reentrant, use static recv buffers
17
+[ ] seq val add with same sign. as bprc_asm
18
+
19
+Proto notes:
20
+- no struct terminator
21
+- AVPs can be identified by integers
22
+- flags used to discriminate among call type
23
+- request error response (code/reason)
24
+- only accept power of 2 as int size representation, with max exp 3 (but:)
25
+- integer means signed 32 bits
26
+- requests have a STR first record
27
+- replies can contain multiple (top level) values.
0 28
new file mode 100644
... ...
@@ -0,0 +1,56 @@
1
+/*
2
+ * Copyright (c) 2010 IPTEGO GmbH.
3
+ * All rights reserved.
4
+ *
5
+ * This file is part of the BinRPC Library (libbinrpc).
6
+ * 
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ * 
10
+ *    1. Redistributions of source code must retain the above copyright
11
+ *       notice, this list of conditions and the following disclaimer.
12
+ * 
13
+ *    2. Redistributions in binary form must reproduce the above copyright
14
+ *       notice, this list of conditions and the following disclaimer in the
15
+ *       documentation and/or other materials provided with the distribution.
16
+ * 
17
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
18
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27
+ * DAMAGE.
28
+ *
29
+ * Author: Bogdan Pintea.
30
+ */
31
+
32
+#ifdef __cplusplus
33
+extern "C" 
34
+{
35
+#endif
36
+
37
+#include "tls.h"
38
+#include "errnr.h"
39
+#include "mem.h"
40
+#include "utime.h"
41
+#include "list.h"
42
+#include "config.h"
43
+#include "errcode.h"
44
+#include "lock.h"
45
+#include "log.h"
46
+#include "value.h"
47
+#include "call.h"
48
+#include "cb.h"
49
+#include "dissector.h"
50
+#include "net.h"
51
+#include "print.h"
52
+
53
+#ifdef __cplusplus
54
+}
55
+/* extern "C" linkage closes */
56
+#endif
0 57
new file mode 100644
... ...
@@ -0,0 +1,1232 @@
1
+/*
2
+ * Copyright (c) 2010 IPTEGO GmbH.
3
+ * All rights reserved.
4
+ *
5
+ * This file is part of the BinRPC Library (libbinrpc).
6
+ * 
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ * 
10
+ *    1. Redistributions of source code must retain the above copyright
11
+ *       notice, this list of conditions and the following disclaimer.
12
+ * 
13
+ *    2. Redistributions in binary form must reproduce the above copyright
14
+ *       notice, this list of conditions and the following disclaimer in the
15
+ *       documentation and/or other materials provided with the distribution.
16
+ * 
17
+ * THIS SOFTWARE IS PROVIDED BY IPTEGO GmbH ``AS IS'' AND ANY EXPRESS OR
18
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20
+ * NO EVENT SHALL IPTEGO GmbH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27
+ * DAMAGE.
28
+ *
29
+ * Author: Bogdan Pintea.
30
+ */
31
+
32
+
33
+#include <assert.h>
34
+#include <time.h>
35
+#include <unistd.h>
36
+#include <stdarg.h>
37
+#include <stdbool.h>
38
+
39
+#include "call.h"
40
+#include "mem.h"
41
+#include "errnr.h"
42
+#include "list.h"
43
+#include "log.h"
44
+#include "misc.h"
45
+
46
+
47
+#define PLOAD_LEN_LEN(byte2) \
48
+	(((byte2 >> PKT_LL_OFF) & PKT_LL_MASK) + 1)
49
+#define COOKIE_LEN(byte2) \
50
+	((byte2 & PKT_CL_MASK) + 1)
51
+#define list_add_val(_head_, _val_) \
52
+		list_add_tail(&(_val_)->list, _head_)
53
+#define _brpc_add_val(_call_, _val_) \
54
+	do { \
55
+		list_add_val(&(_call_)->vals.list, _val_); \
56
+		(_call_)->vals.cnt ++; \
57
+	} while (0)
58
+
59
+#define _brpc_init(_call_) \
60
+	do { \
61
+		memset(_call_, 0, sizeof(brpc_t)); \
62
+		INIT_LIST_HEAD(&(_call_)->vals.list); \
63
+	} while (0)
64
+#define val_list_next(_elem_)	(_elem_)->next
65
+
66
+
67
+__LOCAL ssize_t header_len(const uint8_t *buff, size_t len)
68
+{
69
+	ssize_t ll, cl;
70
+
71
+	if (len < HDR_START_BYTES)
72
+		return -(ssize_t)(HDR_START_BYTES - len); /* read (-) this much more */
73
+
74
+	ll = PLOAD_LEN_LEN(buff[HDR_LENS_OFF]);
75
+	cl = COOKIE_LEN(buff[HDR_LENS_OFF]);
76
+
77
+	return HDR_START_BYTES + ll + cl;
78
+}
79
+
80
+ssize_t brpc_pkt_len(const uint8_t *buff, size_t len)
81
+{
82
+	ssize_t hdr_len;
83
+	size_t pload_len, ll;
84
+
85
+	hdr_len = header_len(buff, len);
86
+	if (hdr_len < 0)
87
+		return hdr_len;
88
+	if (len < hdr_len)
89
+		return len - hdr_len; /* need (-) this much more, to tell */
90
+
91
+	ll = PLOAD_LEN_LEN(buff[HDR_LENS_OFF]);
92
+	pload_len = ntohz(buff + HDR_PLLEN_OFF, ll);
93
+	return pload_len + hdr_len;
94
+}
95
+
96
+bool brpc_pkg_supported(uint8_t first_byte)
97
+{
98
+	unsigned int ver = first_byte & ((1 << VER_BITS) - 1);
99
+	return (ver == BINRPC_PROTO_VER);
100
+}
101
+
102
+__LOCAL brpc_t *brpc_new(brpc_ctype_t type)
103
+{
104
+	brpc_t *ctx;
105
+	ctx = (brpc_t *)brpc_calloc(1, sizeof(brpc_t));
106
+	if (! ctx) {
107
+		WERRNO(ENOMEM);
108
+		return NULL;
109
+	}
110
+	ctx->type = type;
111
+	INIT_LIST_HEAD(&ctx->vals.list);
112
+	return ctx;
113
+}
114
+
115
+brpc_t *brpc_req(brpc_str_t _method, brpc_id_t id)
116
+{
117
+	brpc_val_t *method;
118
+	brpc_t *req;
119
+
120
+	if (! _method.len) {
121
+		ERR("request's method name can not be NULL (id:%u).\n", id);
122
+		WERRNO(EINVAL);
123
+		return NULL;
124
+	}
125
+	req = brpc_new(BRPC_CALL_REQUEST);
126
+	if (! req)
127
+		return NULL;
128
+	method = brpc_str(_method.val, _method.len);
129
+	if (! method)
130
+		return NULL;
131
+	brpc_add_val(req, method);
132
+	req->id = id;
133
+	DBG("new BINRPC request %.*s(), #%u\n.", 
134
+		BRPC_STR_FMT(&brpc_str_val(method)), id);
135
+	return req;
136
+}
137
+
138
+bool brpc_fault(brpc_t *rpl, const brpc_int_t *_code, 
139
+		const brpc_str_t *_reason)
140
+{
141
+	brpc_val_t *code, *reason;
142
+
143
+	assert(rpl);
144
+	if (rpl->type != BRPC_CALL_REPLY) {
145
+		WERRNO(EINVAL);
146
+		ERR("only replies can be marked as fault.\n");
147
+		return false;
148
+	}
149
+	if (rpl->error) {
150
+		WERRNO(EINVAL);
151
+		ERR("reply alreay marked as fault indicator.\n");
152
+		return false;
153
+	}
154
+	rpl->error = true;
155
+
156
+	if (_code)
157
+		code = brpc_int(*_code);
158
+	else
159
+		code = brpc_null(BRPC_VAL_INT);
160
+	if (! code)
161
+		goto error;
162
+	brpc_add_val(rpl, code);
163
+	
164
+	if (_reason)
165
+		reason = brpc_str(_reason->val, _reason->len);
166
+	else
167
+		reason = brpc_null(BRPC_VAL_STR);
168
+	if (! reason)
169
+		goto error;
170
+	brpc_add_val(rpl, reason);
171
+
172
+	return true;
173
+error:
174
+	brpc_finish(rpl);
175
+	return false;
176
+}
177
+
178
+brpc_t *brpc_rpl(const brpc_t *req)
179
+{
180
+	brpc_t *rpl;
181
+	rpl = brpc_new(BRPC_CALL_REPLY);
182
+	if (! rpl)
183
+		return NULL;
184
+	rpl->id = req->id;
185
+	return rpl;
186
+}
187
+
188
+void brpc_finish(brpc_t *call)
189
+{
190
+	struct brpc_list_head *k, *tmp;
191
+
192
+	list_for_each_safe(k, tmp, &call->vals.list) {
193
+		list_del(k);
194
+		brpc_val_free(_BRPC_VAL4LIST(k));
195
+	}
196
+
197
+	if (call->nbuf.val)
198
+		brpc_free(call->nbuf.val);
199
+
200
+	brpc_free(call);
201
+}
202
+
203
+bool brpc_add_val(brpc_t *call, brpc_val_t *val)
204
+{
205
+	if (! list_empty(&val->list)) {
206
+		WERRNO(EINVAL);
207
+		ERR("can not add already added call value.\n");
208
+		return false;
209
+	}
210
+	_brpc_add_val(call, val);
211
+	return true;
212
+}
213
+
214
+brpc_val_t *brpc_fetch_val(brpc_t *call, size_t index)
215
+{
216
+	struct brpc_list_head *k;
217
+	size_t idx = 0;
218
+
219
+	if (index < 0) {
220
+		WERRNO(EINVAL);
221
+		return NULL;
222
+	} else if (brpc_type(call) == BRPC_CALL_REQUEST)
223
+		index ++; /* first is the method name */
224
+	list_for_each(k, &call->vals.list) {
225
+		if (idx == index) 
226
+			return _BRPC_VAL4LIST(k);
227
+		idx ++;
228
+	}
229
+	return NULL;
230
+}
231
+
232
+/**
233
+ * Serializes the BINRPC
234
+ * @param call The BINRPC to serialize
235
+ * @return reference to container buffer; this buffer is handled by ??? (must
236
+ * not be free'd!).
237
+ */
238
+const brpc_bin_t *brpc_serialize(brpc_t *call)
239
+{
240
+	uint8_t buff[BINRPC_MAX_PKT_LEN], *pos, *end;
241
+	struct brpc_list_head *k;
242
+	size_t blenlen, cklen, hdrlen, bodylen, tlen;
243
+	unsigned int flags;
244
+
245
+	/* some sanity checks: rpc origin and call consistency */
246
+	if (call->locked) {
247
+		WERRNO(EINVAL);
248
+		ERR("trying to serialize locked call.\n");
249
+		return NULL;
250
+	}
251
+
252
+	if (call->nbuf.val)
253
+		return &call->nbuf;
254
+	assert(call->nbuf.len == 0);
255
+
256
+	pos = buff;
257
+	end = buff + sizeof(buff);
258
+	list_for_each(k, &call->vals.list) {
259
+		pos = brpc_val_ser(_BRPC_VAL4LIST(k), pos, end);
260
+		if (! pos)
261
+			return NULL;
262
+	}
263
+
264
+	bodylen = pos - buff;
265
+	if (! bodylen) {
266
+		if (call->type == BRPC_CALL_REQUEST) {
267
+			WERRNO(EINVAL);
268
+			ERR("request #%u has no method name set.\n", call->id);
269
+		}
270
+	}
271
+	blenlen = sizeofz(bodylen);
272
+	cklen = sizeofz((size_t)call->id);
273
+	/* is there space left for the header? */
274
+	hdrlen = HDR_PLLEN_OFF + blenlen + cklen;
275
+	if ((end - pos) < hdrlen) {
276
+		WERRNO(ENOBUFS);
277
+		return NULL;
278
+	}
279
+	DBG("pkg header len: %zd.\n", hdrlen);
280
+	DBG("pkg body len: %zd.\n", bodylen);
281
+	
282
+	tlen = hdrlen + bodylen;
283
+	call->nbuf.val = (uint8_t *)brpc_malloc(tlen * sizeof(uint8_t));
284
+	if (! call->nbuf.val) {
285
+		WERRNO(ENOMEM);
286
+		return NULL;
287
+	}
288
+	call->nbuf.len = tlen;
289
+
290
+	/* serialize header */
291
+	/* first [| MAGIC  | VERS  |] byte */
292
+	pos = (uint8_t *)call->nbuf.val;
293
+	*pos = (BINRPC_PROTO_MAGIC << VER_BITS) | 
294
+			(BINRPC_PROTO_VER & ((1 << VER_BITS) - 1));
295
+	pos ++;
296
+	/* second [| FLAGS  | LL | CL |] byte */
297
+	if (call->type == BRPC_CALL_REQUEST)
298
+		flags = BRPC_FLG_REQUEST;
299
+	else if (call->error)
300
+		flags = BRPC_FLG_ERROR;
301
+	else
302
+		flags = 0;
303
+	*pos = flags << PKT_FLAGS_OFF;
304
+
305
+	assert(blenlen < (1 << (PKT_FLAGS_OFF - PKT_LL_OFF)));
306
+	*pos |= (blenlen - 1) << PKT_LL_OFF;
307
+	assert((cklen - 1) < (1 << PKT_LL_OFF));
308
+	*pos |= cklen - 1;
309
+	pos ++;
310
+	/* lenght and cookie fields */
311
+	pos += htonz(pos, bodylen);
312
+	pos += htonz(pos, (size_t)call->id);
313
+
314
+	/* copy serialized body */
315
+	memcpy(pos, buff, bodylen);
316
+	
317
+	return &call->nbuf;
318
+}
319
+
320
+brpc_t *brpc_raw(uint8_t *buff, size_t len)
321
+{
322
+	ssize_t hdrlen;
323
+	size_t bodylen, ll, cl;
324
+	uint8_t *pos;
325
+	unsigned int flags;
326
+	brpc_t *call;
327
+
328
+	assert(buff);
329
+
330
+	hdrlen = header_len(buff, len);
331
+	if ((hdrlen < 0) || (len < hdrlen)) {
332
+		WERRNO(EMSGSIZE);
333
+		return false;
334
+	}
335
+	DBG("new raw packet (len :%zd).\n", len);
336
+
337
+	pos = buff;
338
+	assert((*pos & ((1 << VER_BITS) - 1)) == BINRPC_PROTO_VER);
339
+	pos ++;
340
+
341
+	flags = *pos >> PKT_FLAGS_OFF;
342
+	call = brpc_new((flags & BRPC_FLG_REQUEST) ? BRPC_CALL_REQUEST : 
343
+			BRPC_CALL_REPLY);
344
+	if (! call)
345
+		return NULL;
346
+	call->locked = true;
347
+	call->nbuf.val = buff;
348
+	call->nbuf.len = len;
349
+	
350
+	if (flags & BRPC_FLG_ERROR) {
351
+		if (call->type == BRPC_FLG_REQUEST) {
352
+			WERRNO(EBADMSG);
353
+			ERR("error flag can only be set for replies.\n");
354
+			goto error;
355
+		}
356
+		call->error = true;
357
+	}
358
+
359
+	if (flags & (BRPC_FLG_2 | BRPC_FLG_3))
360
+		WARN("message using reserved flags #2&#3.\n");
361
+		
362
+	ll = PLOAD_LEN_LEN(*pos);
363
+	cl = COOKIE_LEN(*pos);
364
+	pos ++;
365
+
366
+	bodylen = ntohz(pos, ll);
367
+	pos += ll;
368
+
369
+	DBG("pkg header len: %zd.\n", hdrlen);
370
+	DBG("pkg body len: %zd.\n", bodylen);
371
+
372
+	if (len < hdrlen + bodylen) {
373
+		WERRNO(EMSGSIZE);
374
+		goto error;
375
+	}
376
+
377
+	call->id = (uint32_t)ntohz(pos, cl);
378
+	pos += cl;
379
+	call->pos = pos;
380
+
381
+	return call;
382
+error:
383
+	brpc_finish(call);
384
+	return NULL;
385
+}
386
+
387
+
388
+brpc_t *brpc_deserialize(uint8_t *buff, size_t len)
389
+{
390
+	uint8_t *own;
391
+
392
+	if (! (own = brpc_malloc(len * sizeof(uint8_t)))) {
393
+		brpc_errno = ENOMEM;
394
+		return NULL;
395
+	}
396
+	memcpy(own, buff, len);
397
+	return brpc_raw(own, len);
398
+}
399
+
400
+/* some sanity checks */
401
+__LOCAL bool check_fault(brpc_t *call, unsigned cnt)
402
+{
403
+	brpc_val_t *new_val, *tmp1, *tmp2;
404
+	brpc_vtype_t type;
405
+
406
+	switch (cnt) {
407
+		default:
408
+			/* let's try to ignore the rest, but expect first 2 to be corr. */
409
+			WARN("multiple records (%u) in error message: considering "
410
+					"only first two.\n", cnt);
411
+			/* no break; */
412
+		case 2:
413
+			tmp1 = _BRPC_VAL4LIST(call->vals.list.next);
414
+			tmp2 = _BRPC_VAL4LIST(call->vals.list.next->next);
415
+			switch (tmp1->type) {
416
+				case BRPC_VAL_INT:
417
+					/* I expect the next to be string */
418
+					switch (tmp2->type) {
419
+						case BRPC_VAL_STR: 
420
+							return true;;
421
+						default:
422
+							WERRNO(EBADMSG);
423
+							ERR("unexpected type (%u) as fault reason "
424
+									"value.\n", tmp2->type);
425
+							return false;
426
+					}
427
+					break;
428
+				case BRPC_VAL_STR:
429
+					switch (tmp2->type) {
430
+						case BRPC_VAL_INT:
431
+							/* values present, but wrong order: put int 1st */
432
+							list_del(&tmp2->list); /* del int */
433
+							list_add(&tmp2->list, &call->vals.list); /* 1st */
434
+							INFO("reordered fault values (int, str).\n");
435
+							return true;
436
+						default:
437
+							WERRNO(EBADMSG);
438
+							ERR("unexpected type (%u) as fault code "
439
+									"value.\n", tmp2->type);
440
+							return false;
441
+							
442
+					}
443
+					break;
444
+				default:
445
+					type = tmp1->type;
446
+			}
447
+			break;
448
+		
449
+		case 1:
450
+			tmp1 = _BRPC_VAL4LIST(call->vals.list.next);
451
+			switch (tmp1->type) {
452
+				case BRPC_VAL_INT:
453
+					new_val = brpc_null(BRPC_VAL_STR);
454
+					if (! new_val)
455
+						return false;
456
+					_brpc_add_val(call, new_val);
457
+					INFO("added null value as fault reason.\n");
458
+					return true;
459
+				case BRPC_VAL_STR:
460
+					new_val = brpc_null(BRPC_VAL_INT);
461
+					if (! new_val)
462
+						return false;
463
+					/* insert 1st */
464
+					list_add(&new_val->list, &call->vals.list); 
465
+					INFO("added null value as fault code.\n");
466
+					return true;
467
+				default:
468
+					type = tmp1->type;
469
+			}
470
+			break;
471
+
472
+		case 0:
473
+			WARN("no fault explanation.\n");
474
+			tmp1 = brpc_null(BRPC_VAL_INT);
475
+			tmp2 = brpc_null(BRPC_VAL_STR);
476
+			if ((! tmp1) || (! tmp2))
477
+				return false;
478
+			_brpc_add_val(call, tmp1);
479
+			_brpc_add_val(call, tmp2);
480
+			return true;
481
+	}
482
+
483
+	WERRNO(EBADMSG);
484
+	ERR("unexpected type (%u) as fault value.\n", type);
485
+	return false;
486
+}
487
+
488
+bool brpc_unpack(brpc_t *call)
489
+{
490
+	uint8_t *end;
491
+	brpc_val_t *new_val;
492
+
493
+	if (! call->locked) {
494
+		WERRNO(EINVAL);
495
+		ERR("can not unpack not locked call #%u.\n", call->id);
496
+	}
497
+
498
+	end = call->nbuf.val + call->nbuf.len;
499
+	if (end <= call->pos) {
500
+		DBG("call #%u already unpacked.\n", call->id);
501
+		return true;
502
+	}
503
+
504
+	if (call->type == BRPC_CALL_REQUEST)
505
+		if (! brpc_unpack_method(call))
506
+			return false;
507
+
508
+	while (call->pos < end) {
509
+		new_val = brpc_val_deser(&call->pos, end);
510
+		if (! new_val)
511
+			goto error;
512
+		_brpc_add_val(call, new_val);
513
+	}
514
+
515
+	if (call->error && (! check_fault(call, call->vals.cnt))) {
516
+		ERR("invalid fault reply.\n");
517
+		goto error;
518
+	}
519
+
520
+	return true;
521
+error:
522
+	return false;
523
+}
524
+
525
+bool brpc_unpack_method(brpc_t *req)
526
+{
527
+	brpc_val_t *meth;
528
+
529
+	if (req->type != BRPC_CALL_REQUEST) {
530
+		WERRNO(EINVAL);
531
+		ERR("can not parse method name for reply (#%u).\n", req->id);
532
+		return false;
533
+	}
534
+
535
+	if (! list_empty(&req->vals.list)) {
536
+		DBG("request's #%u method name already unpacked.\n", req->id);
537
+		return true;
538
+	}
539
+
540
+	meth = brpc_val_deser(&req->pos, req->nbuf.val + req->nbuf.len);
541
+	if (! meth) {
542
+		WERRNO(EBADMSG);
543
+		ERR("request #%u lacks method name.\n", req->id);
544
+		return false;
545
+	}
546
+	_brpc_add_val(req, meth);
547
+	if (meth->type != BRPC_VAL_STR) {
548
+		WERRNO(EBADMSG);
549
+		ERR("req flaged as request but first value's type (%u) isn't "
550
+				"string.\n", meth->type);
551
+		return false;
552
+	}
553
+
554
+	return true;
555
+}
556
+
557
+const brpc_str_t *brpc_method(brpc_t *req)
558
+{
559
+	if (req->type != BRPC_CALL_REQUEST) {
560
+		WERRNO(EINVAL);
561
+		ERR("req not a request (%d).\n", req->type);
562
+		return NULL;
563
+	}
564
+	if (req->locked)
565
+		if (! brpc_unpack_method(req))
566
+			return false;
567
+	return &brpc_str_val(_BRPC_VAL4LIST(req->vals.list.next));
568
+}
569
+
570
+bool brpc_fault_status(brpc_t *rpl, brpc_int_t **_code, 
571
+		brpc_str_t **_reason)
572
+{
573
+	brpc_val_t *c, *r;
574
+	if (! rpl->error) {
575
+		WERRNO(EINVAL);
576
+		ERR("rpl not a fault.\n");
577
+		return false;
578
+	}
579
+
580
+	if (rpl->locked)
581
+		if (! brpc_unpack(rpl))
582
+			return false;
583
+
584
+	c = _BRPC_VAL4LIST(rpl->vals.list.next);
585
+	r = _BRPC_VAL4LIST(rpl->vals.list.next->next);
586
+
587
+	if (c->null)
588
+		*_code = NULL;
589
+	else
590
+		*_code = &brpc_int_val(c);
591
+
592
+	if (r->null)
593
+		*_reason = NULL;
594
+	else
595
+		*_reason = &brpc_str_val(r);
596
+
597
+	return true;
598
+}
599
+
600
+bool brpc_asm(brpc_t *call, const char *fmt, ...)
601
+{
602
+	va_list ap;
603
+	ssize_t sp, i;
604
+	brpc_val_t **recstack, *rec;
605
+	brpc_val_t lst;
606
+	char spec;
607
+	brpc_vtype_t closed;
608
+	bool ret;
609
+	brpc_vtype_t type;
610
+	char *c_arg;
611
+	brpc_int_t *i_arg;
612
+	brpc_str_t *s_arg;
613
+	brpc_bin_t *b_arg;
614
+	void **v_args; /* arguments packed into array */
615
+	ssize_t v_pos;
616
+	bool use_va; /* use va_arg; if !, use v_args */
617
+
618
+
619
+	if (call->locked) {
620
+		WERRNO(EINVAL);
621
+		ERR("can not pack into locked BINRPC.\n");
622
+		return false;
623
+	}
624
+
625
+	/* normally, strlen/2 should do, but guard agaisnt "<<<<<" bugs */
626
+	recstack = (brpc_val_t **)brpc_malloc(strlen(fmt) * sizeof(brpc_val_t *));
627
+	if (! recstack) {
628
+		WERRNO(ENOMEM);
629
+		return false;
630
+	}
631
+	sp = 0;
632
+
633
+	/* fake call into a list of values */
634
+	memset(&lst, 0, sizeof(brpc_val_t));
635
+	INIT_LIST_HEAD(&lst.val.seq.list);
636
+	lst.type = BRPC_VAL_LIST;
637
+
638
+	ret = false;
639
+
640
+#define PUSH(lst) \
641
+	do { \
642
+		assert(sp < strlen(fmt)); \
643
+		recstack[sp++] = lst; \
644
+	} while (0)
645
+#define POP(expect) \
646
+	do { \
647
+		if (! sp --) { \
648
+			WERRNO(EINVAL); \
649
+			ERR("invalid format descriptor; understack before `%s'.\n",fmt); \
650
+			goto error; \
651
+		} \
652
+		if (recstack[sp]->type != expect) { \
653
+			WERRNO(EFMT); \
654
+			ERR("invalid format specified (expected to close %d instead of" \
655
+					" %d); jammed at `%c%s'.\n", recstack[sp]->type, \
656
+					expect, spec, fmt); \
657
+			goto error; \
658
+		} \
659
+	} while(0)
660
+#ifndef NDEBUG
661
+#	define TOP	(sp ? recstack[sp-1] : NULL)
662
+#else
663
+#	define TOP	recstack[sp - 1]
664
+#endif
665
+#define OVER	recstack[sp] /* only used after POP */
666
+
667
+#define ADD2SEQ(rec) \
668
+	do { \
669
+		bool (*add)(brpc_val_t *, brpc_val_t *); \
670
+		switch (TOP->type) { \
671
+			case BRPC_VAL_AVP: add = brpc_avp_add; break;\
672
+			case BRPC_VAL_MAP: add = brpc_map_add; break;\
673
+			case BRPC_VAL_LIST: add = brpc_list_add; break;\
674
+			default: \
675
+				BUG("illegal type (%u) as sequence.\n", TOP->type); \
676
+				goto error; \
677
+		} \
678
+		if (! add(TOP, rec)) \
679
+			goto error; \
680
+	} while (0)
681
+/* argument fetch */
682
+#define AFETCH(type) \
683
+	((use_va) ? va_arg(ap, type) : (type)v_args[v_pos ++])
684
+/* integer fetch */
685
+#define IAFETCH(type) \
686
+	((use_va) ? va_arg(ap, type) : (type)(intptr_t)v_args[v_pos ++])
687
+
688
+#ifdef FIX_FALSE_GCC_WARNS
689
+	v_pos = 0;
690
+	v_args = NULL;
691
+#endif
692
+	spec = 0;
693
+	PUSH(&lst);
694
+	use_va = true;
695
+	va_start(ap, fmt);
696
+	while (*fmt) {
697
+		switch ((spec = *fmt++)) {
698
+			case '!':
699
+				if (! use_va) {
700
+					WERRNO(EINVAL);
701
+					ERR("`!' used twice in format descriptor; jammed at "
702
+							"`%c%s'.\n", spec, fmt);
703
+				}
704
+				use_va = false;
705
+				v_args = va_arg(ap, void **);
706
+				v_pos = 0;
707
+				DBG("switching to array arguments.\n");
708
+				break;
709
+
710
+			do {
711
+			case '<': rec = brpc_empty_avp(); break;
712
+			case '[': rec = brpc_list(NULL); break;
713
+			case '{': rec = brpc_map(NULL); break;
714
+			} while (0); 
715
+				if (! rec)
716
+					goto error;
717
+				PUSH(rec);
718
+				break;
719
+			
720
+			do {
721
+			case '>': closed = BRPC_VAL_AVP; break;
722
+			case ']': closed = BRPC_VAL_LIST; break;
723
+			case '}': closed = BRPC_VAL_MAP; break;
724
+			} while (0);
725
+				POP(closed);
726
+				if (list_empty(&OVER->val.seq.list)) {
727
+					WARN("empty %d set at`%c%s'\n", closed, spec, fmt);
728
+					brpc_val_free(TOP);
729
+				} else {
730
+					ADD2SEQ(OVER);
731
+				}
732
+				break;
733
+
734
+			do {
735
+			case 'c':
736
+				c_arg = AFETCH(char *);
737
+				rec = (c_arg) ? brpc_cstr(c_arg) : brpc_null(BRPC_VAL_STR);
738
+				break;
739
+			case 'd':
740
+				rec = brpc_int(IAFETCH(int));
741
+				break;
742
+			case 'i':
743
+				i_arg = AFETCH(brpc_int_t *);
744
+				rec = (i_arg) ? brpc_int(*i_arg) : brpc_null(BRPC_VAL_INT);
745
+				break;
746
+			case 's': 
747
+				s_arg = AFETCH(brpc_str_t *);
748
+				rec = (s_arg) ? brpc_str(s_arg->val, s_arg->len) : 
749
+						brpc_null(BRPC_VAL_STR);
750
+				break;
751
+			case 'b':
752
+				b_arg = AFETCH(brpc_bin_t *);
753
+				rec = (b_arg) ? brpc_bin(b_arg->val, b_arg->len) : 
754
+						brpc_null(BRPC_VAL_BIN);
755
+				break;
756
+			} while (0);
757
+				if (! rec)
758
+					goto error;
759
+				ADD2SEQ(rec);
760
+				break;
761
+
762
+			do {
763
+			case 'I': type = BRPC_VAL_INT; break;
764
+			case 'S': type = BRPC_VAL_STR; break;
765
+			case 'B': type = BRPC_VAL_BIN; break;
766
+			case 'A': type = BRPC_VAL_AVP; break;
767
+			case 'M': type = BRPC_VAL_MAP; break;
768
+			case 'L': type = BRPC_VAL_LIST; break;
769
+			} while (0);
770
+				rec = AFETCH(brpc_val_t *);
771
+				if (rec) {
772
+					if (rec->type != type) {
773
+						WERRNO(EINVAL);
774
+						ERR("expected value of type '%d'; got type '%d'.\n",
775
+								type, rec->type);
776
+						goto error;
777
+					}
778
+					if (rec->locked) {
779
+						rec = brpc_val_clone(rec);
780
+						if (! rec)
781
+							goto error;
782
+					}
783
+				} else {
784
+					rec = brpc_null(type);
785
+					if (! rec)
786
+						goto error;
787
+				}
788
+				ADD2SEQ(rec);
789
+				break;
790
+
791
+			case '\t':
792
+			case ':':
793
+			case ',':
794
+			case ' ': break; /* better visibility */
795
+			default:
796
+				WERRNO(EINVAL);
797
+				ERR("unsupported format specifier `%c'; jammed at "
798
+						"`%c%s'.\n", spec, spec, fmt);
799
+				goto error;
800
+		}
801
+	}
802
+	va_end(ap);
803
+	POP(BRPC_VAL_LIST);
804
+
805
+	if (sp) {
806
+		WERRNO(EINVAL);
807
+		ERR("invalid format specified: unclosed groups.\n");
808
+		goto error;
809
+	}
810
+
811
+	list_splice_tail(&lst.val.seq.list, &call->vals.list);
812
+	
813
+	ret = true;
814
+error:
815
+	for (i = /*@0: lst*/1; i < sp; i ++)
816
+		brpc_val_free(recstack[i]);
817
+	brpc_free(recstack);
818
+	return ret;
819
+
820
+#undef PUSH
821
+#undef POP
822
+#undef TOP
823
+#undef ADD2SEQ
824
+#undef AFETCH
825
+}
826
+
827
+
828
+bool brpc_dsm(brpc_t *call, const char *fmt, ...)
829
+{
830
+	va_list ap;
831
+	char spec;
832
+	struct brpc_list_head **headstack, **posstack, *crrhead, *crrpos;
833
+	size_t sksz;
834
+	ssize_t sp;
835
+	bool ret, skip;
836
+	brpc_val_t *rec;
837
+	brpc_vtype_t type;
838
+	void **v_args;
839
+	ssize_t v_pos;
840
+	register enum {USE_VAARG, VOID_LEVEL2, VOID_LEVEL1} use_vargs;
841
+
842
+	if (brpc_is_fault(call)) {
843
+		ERR("will not disassamble faulted call.\n");
844
+		WERRNO(EINVAL);
845
+		return false;
846
+	}
847
+
848
+	if (call->locked)
849
+		if (! brpc_unpack(call))
850
+			return false;
851
+
852
+	/*
853
+	 * How deep the stack can be.
854
+	 * The parser ensures a correct packet, so it's safe to assume the worst
855
+	 * case as `[[... [x] ...]]'.
856
+	 */
857
+	sksz = strlen(fmt) / 2 + /* round up */1;
858
+	DBG("unpack stack size: %zd.\n", sksz);
859
+
860
+	headstack = (struct brpc_list_head **)brpc_malloc(sksz * 
861
+			sizeof(struct brpc_list_head *));
862
+	posstack = (struct brpc_list_head **)brpc_malloc(sksz * 
863
+			sizeof(struct brpc_list_head *));
864
+	if ((! headstack) || (! posstack)) {
865
+		WERRNO(ENOMEM);
866
+		return false;
867
+	}
868
+	sp = 0;
869
+
870
+	crrhead = &call->vals.list;
871
+	crrpos = crrhead;
872
+
873
+#define LPUSH(newhead) \
874
+	do { \
875
+		assert(sp < sksz); \
876
+		headstack[sp] = crrhead; \
877
+		posstack[sp] = crrpos; \
878
+		sp ++; \
879
+		crrhead = newhead; \
880
+		crrpos = newhead; \
881
+	} while (0)
882
+#define LPOP \
883
+	do { \
884
+		if (! sp) { \
885
+			WERRNO(EINVAL); \
886
+			ERR("illegal format specified (group closing without beginning);"\
887
+					" jammed at `%c%s'.\n", spec, fmt); \
888
+			goto error; \
889
+		} \
890
+		sp --; \
891
+		crrhead = headstack[sp]; \
892
+		crrpos = posstack[sp]; \
893
+	} while (0)
894
+#define LNEXT	crrpos = crrpos->next
895
+#define LFETCH(expect, rec) \
896
+	do { \
897
+		LNEXT; \
898
+		if (crrpos == crrhead) { /* did the list cycle? */ \
899
+			WERRNO(EFMT); \
900
+			ERR("list value ended; unpacking failed at `%c%s'.\n", \
901
+					spec, fmt); \
902
+			goto error; \
903
+		} \
904
+		rec = _BRPC_VAL4LIST(crrpos); \
905
+		if (rec->type != expect) { \
906
+			WERRNO(EFMT); \
907
+			ERR("format-type mismatch; expected: %d; encountered: %d; " \
908
+					"jammed at `%c%s'.\n", expect, rec->type, spec, fmt); \
909
+			goto error; \
910
+		} \
911
+	} while (0)
912
+#define AASIGN(basic_type, val) \
913
+	do { \
914
+		switch (use_vargs) { \
915
+			case USE_VAARG: *va_arg(ap, basic_type **) = val; break; \
916
+			case VOID_LEVEL1: v_args[v_pos++] = (void *)val; break; \
917
+			case VOID_LEVEL2: *(basic_type **)v_args[v_pos++] = val; break; \
918
+		} \
919
+	} while (0)
920
+
921
+#ifdef FIX_FALSE_GCC_WARNS
922
+	v_pos = 0;
923
+#endif
924
+
925
+	if (call->type == BRPC_CALL_REQUEST)
926
+		/* go past method name */
927
+		LNEXT;
928
+	ret = false;
929
+	use_vargs = USE_VAARG;
930
+	v_args = NULL;
931
+	va_start(ap, fmt);
932
+	while ((spec = *fmt++)) {
933
+		DBG("decoding a `%c'.\n", spec);
934
+		switch (spec) {
935
+			case '!': 
936
+				/* LEV1: void[x] will contain reference to value */
937
+				switch (use_vargs) {
938
+					case VOID_LEVEL2:
939
+						DBG("switching void levels: 2 -> 1\n");
940
+					case USE_VAARG:
941
+						use_vargs = VOID_LEVEL1;
942
+						v_args = va_arg(ap, void **);
943
+						v_pos = 0;
944
+						break;
945
+					case VOID_LEVEL1:
946
+						WARN("`!' reused in expression: ignoring instance "
947
+								"at `%c%s'.\n", spec, fmt);
948
+						break;
949
+				}
950
+				DBG("switching to array arguments; void level 1.\n");
951
+				break;
952
+
953
+			case '&': 
954
+				/* LEV2: the value in void[x] will contain ref to value */
955
+				switch (use_vargs) {
956
+					case VOID_LEVEL1:
957
+						DBG("switching void levels: 1 -> 2\n");
958
+					case USE_VAARG:
959
+						use_vargs = VOID_LEVEL2;
960
+						v_args = va_arg(ap, void **);
961
+						v_pos = 0;
962
+						break;
963
+					case VOID_LEVEL2:
964
+						WARN("`&' reused in expression: ignoring instance "
965
+								"at `%c%s'.\n", spec, fmt);
966
+						break;
967
+				}
968
+				DBG("switching to array arguments; void level 2.\n");
969
+				break;
970
+
971
+
972
+			do {
973
+			case '<': type = BRPC_VAL_AVP; break;
974
+			case '[': type = BRPC_VAL_LIST; break;
975
+			case '{': type = BRPC_VAL_MAP; break;
976
+			} while (0);
977
+				LFETCH(type, rec);
978
+				LPUSH(&rec->val.seq.list);
979
+				break;
980
+
981
+			do {
982
+			case '>': type = BRPC_VAL_AVP; break;
983
+			case ']': type = BRPC_VAL_LIST; break;
984
+			case '}': type = BRPC_VAL_MAP; break;
985
+			} while (0);
986
+				rec = _BRPC_VAL4SEQ(crrhead);
987
+				if (rec->type != type) {
988
+					WERRNO(EINVAL);
989
+					ERR("invalid format: expected: %d; received: %d; jammed"
990
+							" at `%c%s'.\n", rec->type, type, spec, fmt);
991
+					goto error;
992
+				}
993
+				LPOP;
994
+				break;
995
+			
996
+			case '*':
997
+				skip = true;
998
+				do {
999
+					switch ((spec = *fmt++)) {
1000
+						case ' ': break;
1001
+						case ')':
1002
+						case ']':
1003
+						case '}':
1004
+						case 0:
1005
+							fmt --; /* re-read it in main loop */
1006
+							skip = false;
1007
+							break;
1008
+						default:
1009
+							WERRNO(EINVAL);
1010
+							ERR("invalid format: unsupported specifier `%c'"
1011
+									" after `*'", spec);
1012
+							goto error;
1013
+					}
1014
+				} while (skip);
1015
+				break;
1016
+
1017
+			case 'c': 
1018
+				LFETCH(BRPC_VAL_STR, rec);
1019
+				AASIGN(char, (rec->null) ? NULL : rec->val.str.val);
1020
+				break;
1021
+			case 'd':
1022
+				LFETCH(BRPC_VAL_INT, rec);
1023
+				AASIGN(int, (rec->null) ? NULL : (int *)&rec->val.int32);
1024
+				break;
1025
+			case 'i':
1026
+				LFETCH(BRPC_VAL_INT, rec);
1027
+				AASIGN(brpc_int_t, (rec->null) ? NULL : &rec->val.int32);
1028
+				break;
1029
+			case 's':
1030
+				LFETCH(BRPC_VAL_STR, rec);
1031
+				AASIGN(brpc_str_t, (rec->null) ? NULL : &rec->val.str);
1032
+				break;
1033
+			case 'b':
1034
+				LFETCH(BRPC_VAL_BIN, rec);
1035
+				AASIGN(brpc_bin_t, (rec->null) ? NULL : &rec->val.bin);
1036
+				break;
1037
+
1038
+			do {
1039
+			case 'I': type = BRPC_VAL_INT; break;
1040
+			case 'S': type = BRPC_VAL_STR; break;
1041
+			case 'B': type = BRPC_VAL_BIN; break;
1042
+			case 'A': type = BRPC_VAL_AVP; break;
1043
+			case 'L': type = BRPC_VAL_LIST; break;
1044
+			case 'M': type = BRPC_VAL_MAP; break;
1045
+			} while (0);
1046
+				LFETCH(type, rec);
1047
+				AASIGN(brpc_val_t, rec);
1048
+				break;
1049
+
1050
+			case '.': LNEXT; break;
1051
+			case '\t':
1052
+			case ':':
1053
+			case ',':
1054
+			case ' ': break;
1055
+			default:
1056
+				WERRNO(EINVAL);
1057
+				ERR("illegal descriptor '%c'(0x%x); jammed at `%c%s'.\n", 
1058
+						spec, spec, spec, fmt);
1059
+				goto error;
1060
+		}
1061
+	}
1062
+	va_end(ap);
1063
+
1064
+	if (sp) {
1065
+		WERRNO(EINVAL);
1066
+		ERR("invalid specifier: group(s) not closed; last opened: %u.\n", 
1067
+				_BRPC_VAL4SEQ(crrhead)->type);
1068
+		goto error;
1069
+	}
1070
+	ret = true;
1071
+error:
1072
+	brpc_free(headstack);
1073
+	brpc_free(posstack);
1074
+	return ret;
1075
+
1076
+#undef LPUSH
1077
+#undef LPOP
1078
+#undef LFETCH
1079
+#undef LNEXT
1080
+#undef AASIGN
1081
+}
1082
+
1083
+char *brpc_repr(brpc_t *call, size_t *len)
1084
+{
1085
+	brpc_str_t repr;
1086
+	ssize_t pos;
1087
+	struct brpc_list_head *head;
1088
+
1089
+	if (call->error) {
1090
+		WERRNO(EINVAL);
1091
+		ERR("can not build representation for errornous reply.\n");
1092
+		return NULL;
1093
+	}
1094
+
1095
+	if (call->locked)
1096
+		if (! brpc_unpack(call))
1097
+			return false;
1098
+
1099
+	pos = 0;
1100
+	memset(&repr, 0, sizeof(brpc_str_t));
1101
+	head = &call->vals.list;
1102
+	if (! brpc_vals_repr(head, &repr, &pos))
1103
+		return NULL;
1104
+
1105
+	if (repr.val) {
1106
+		repr.val[pos] = 0; /* always place for 0-term */
1107
+		DBG("repr[%u]: <%s>\n", pos, repr.val);
1108
+		/* first char will always be 's', for requests (due to method name) */
1109
+		if (call->type == BRPC_CALL_REQUEST)
1110
+			memcpy(repr.val, repr.val + 1, repr.len - 1);
1111
+	} else {
1112
+		DBG("empty representation");
1113
+	}
1114
+
1115
+	if (len)
1116
+		*len = pos;
1117
+
1118
+	return repr.val;
1119
+}
1120
+
1121
+
1122
+bool brpc_repr_check(const char *repr, bool strict)
1123
+{
1124
+	/* TODO: implement a generic stack (possible?) */
1125
+	brpc_vtype_t *stack;
1126
+	unsigned int sp;
1127
+	bool passed;
1128
+	bool run;
1129
+	size_t repr_len;
1130
+
1131
+	DBG("checking signature `%s'.\n", repr);
1132
+	if (! repr)
1133
+		return false;
1134
+	repr_len = strlen(repr);
1135
+	if (! repr_len)
1136
+		/* empty repr. are well formated */
1137
+		return true;
1138
+
1139
+	/* normally, strlen/2 should do, but guard agaisnt "<<<<<" bugs */
1140
+	stack = (brpc_vtype_t *)brpc_malloc(repr_len * sizeof(brpc_vtype_t));
1141
+	if (! stack) {
1142
+		WERRNO(ENOMEM);
1143
+		return false; /* actually, a false indicator (the repr might be OK) */
1144
+	}
1145
+	stack[0] = 0; /* prevent ")" from accidentally working */
1146
+	sp = 0;
1147
+
1148
+#define PUSH(sym) \
1149
+	do { \
1150
+		assert(sp < repr_len); \
1151
+		stack[sp++] = sym; \
1152
+	} while (0)
1153
+#define POP(sym) \
1154
+	do { \
1155
+		if ((! sp) || (stack[--sp] != sym)) { \
1156
+			passed = false; \
1157
+			goto clean; \
1158
+		} \
1159
+	} while (0)
1160
+
1161
+	passed = true;
1162
+	run = true;
1163
+	while (run)
1164
+		switch (*repr ++) {
1165
+			case 'c':
1166
+			case 'd':
1167
+			case 'i':
1168
+			case 's':
1169
+			case 'b':
1170
+			case 'I':
1171
+			case 'S':
1172
+			case 'B':
1173
+				/* allowed */
1174
+				break;
1175
+
1176
+			case 'A':
1177
+			case 'M':
1178
+			case 'L':
1179
+			case '.':
1180
+			case '*':
1181
+			case ' ':
1182
+				if (strict) {
1183
+					DBG("illegal char `%c' for strict RPC representation.\n", 
1184
+							*(repr - 1));
1185
+					passed = false;
1186
+					goto clean;
1187
+				}
1188
+				/* allowed if not strict */
1189
+				break;
1190
+
1191
+			case '[': PUSH(BRPC_VAL_LIST); break;
1192
+			case ']': POP(BRPC_VAL_LIST); break;
1193
+			case '<': PUSH(BRPC_VAL_AVP); break;
1194
+			case '>': POP(BRPC_VAL_AVP); break;
1195
+			case '{': PUSH(BRPC_VAL_MAP); break;
1196
+			case '}': POP(BRPC_VAL_MAP); break;
1197
+