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