You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/core/plug-in/binrpcctrl/libbinrpc/src/value.c

908 lines
20 KiB

/**
* Copyright (C) 2007 iptego GmbH
*
* This file is part of libbinrpc.
*
* libbinrpc is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* libbinrpc is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include "mem.h"
#include "log.h"
#include "errno.h"
#include "misc.h"
#include "config.h"
#include "value.h"
__LOCAL brpc_val_t *decode_by_type(uint8_t **, const uint8_t *, brpc_vtype_t);
__LOCAL brpc_val_t *decode_int(uint8_t **, const uint8_t *);
__LOCAL brpc_val_t *decode_str(uint8_t **, const uint8_t *);
__LOCAL brpc_val_t *decode_bin(uint8_t **, const uint8_t *);
__LOCAL brpc_val_t *decode_avp(uint8_t **, const uint8_t *);
__LOCAL brpc_val_t *decode_seq(uint8_t **, const uint8_t *, brpc_vtype_t);
__LOCAL brpc_val_t *_brpc_bin(uint8_t *val, size_t len);
__LOCAL brpc_val_t *_brpc_str(char *val, size_t len);
__LOCAL bool _brpc_map_add(brpc_val_t *map, brpc_val_t *avp);
__LOCAL bool _brpc_list_add(brpc_val_t *list, brpc_val_t *avp);
__LOCAL uint8_t *serialize_null(brpc_val_t *, uint8_t *, const uint8_t *);
__LOCAL uint8_t *serialize_int(brpc_val_t *, uint8_t *, const uint8_t *);
__LOCAL uint8_t *serialize_bin(brpc_val_t *, uint8_t *, const uint8_t *);
__LOCAL uint8_t *serialize_content(brpc_val_t *, uint8_t *, const uint8_t *);
__LOCAL uint8_t *serialize_complex(brpc_val_t *, uint8_t *, const uint8_t *);
#define IALLOC(_rec_, _type_) \
do { \
_rec_ = (brpc_val_t *)brpc_calloc(1, sizeof(brpc_val_t)); \
if (! _rec_) { \
WERRNO(ENOMEM); \
return NULL; \
} \
(_rec_)->type = _type_; \
INIT_LIST_HEAD(&(_rec_)->list); \
} while (0)
#define brpc_seq_add(_head_, _new_) \
do { \
assert( ((_head_)->type == BRPC_VAL_AVP) || \
((_head_)->type == BRPC_VAL_MAP) || \
((_head_)->type == BRPC_VAL_LIST) ); \
list_add_tail(&(_new_)->list, &(_head_)->val.seq.list); \
(_head_)->val.seq.cnt ++; \
} while (0)
void brpc_val_free(brpc_val_t *rec)
{
struct brpc_list_head *k, *tmp;
if (! rec) {
WARN("trying to free NULL reference as record.\n");
return;
}
#if 0
DBG("freeing BINRPC value of type %d.\n", rec->type);
#endif
if (! rec->null)
switch (rec->type) {
case BRPC_VAL_INT: break;
case BRPC_VAL_STR:
case BRPC_VAL_BIN:
if (! rec->locked)
brpc_free(brpc_bin_val(rec).val);
break;
case BRPC_VAL_AVP:
case BRPC_VAL_MAP:
case BRPC_VAL_LIST:
list_for_each_safe(k, tmp, &rec->val.seq.list) {
list_del(k);
brpc_val_free(_BRPC_VAL4LIST(k));
}
break;
default:
BUG("unknown record type 0x%x.\n", rec->type);
}
brpc_free(rec);
}
brpc_val_t *brpc_null(brpc_vtype_t type)
{
brpc_val_t *rec;
IALLOC(rec, type);
if (rec)
rec->null = true;
return rec;
}
brpc_val_t *brpc_int(brpc_int_t val)
{
brpc_val_t *rec;
IALLOC(rec, BRPC_VAL_INT);
brpc_int_val(rec) = val;
return rec;
}
__LOCAL brpc_val_t *_brpc_bin(uint8_t *val, size_t len)
{
brpc_val_t *rec;
#ifdef PARANOID
if (INT_MAX < len) {
ERR("string too large");
WERRNO(EINVAL);
}
#endif
IALLOC(rec, BRPC_VAL_BIN);
brpc_bin_val(rec).val = val;
brpc_bin_val(rec).len = len;
if (! len)
rec->null = true;
return rec;
}
__LOCAL brpc_val_t *_brpc_str(char *val, size_t len)
{
brpc_val_t *rec;
#ifdef PARANOID
if (INT_MAX < len) {
ERR("string too large");
WERRNO(EINVAL);
}
#endif
rec = _brpc_bin((uint8_t *)val, len); /* works safe due to the union. */
if (rec)
rec->type = BRPC_VAL_STR;
return rec;
}
brpc_val_t *brpc_bin(uint8_t *val, size_t len)
{
brpc_val_t *rec;
uint8_t *dup;
if (! len)
return brpc_null(BRPC_VAL_BIN);
dup = (uint8_t *)brpc_malloc(len * sizeof(uint8_t));
if (! dup) {
WERRNO(ENOMEM);
return NULL;
}
memcpy(dup, val, len);
rec = _brpc_bin(dup, len);
if (! rec)
brpc_free(dup);
return rec;
}
brpc_val_t *brpc_str(char *val, size_t len)
{
brpc_val_t *rec;
char *dup;
bool zterm; /* 0-term present ? */
zterm = ((! len) || val[len - 1]) ? false : true;
dup = (char *)brpc_malloc((len + (zterm ? 0 : 1)) * sizeof(char));
if (! dup) {
WERRNO(ENOMEM);
return NULL;
}
memcpy(dup, val, len);
if (! zterm)
dup[len] = 0;
rec = _brpc_str(dup, len + (zterm ? 0 : 1));
if (! rec)
brpc_free(dup);
return rec;
}
brpc_val_t *brpc_avp(brpc_val_t *name, brpc_val_t *value)
{
brpc_val_t *rec;
if ((name->type != BRPC_VAL_STR)
#ifdef INT_AS_ID
&& (name->type != BRPC_VAL_INT)
#endif
) {
WERRNO(EINVAL);
ERR("invalid type (%d) as AVP identifier.\n", name->type);
return NULL;
}
IALLOC(rec, BRPC_VAL_AVP);
INIT_LIST_HEAD(&rec->val.seq.list);
brpc_seq_add(rec, name);
brpc_seq_add(rec, value);
return rec;
}
brpc_val_t *brpc_seq(brpc_vtype_t type, ...)
{
brpc_val_t *rec, *val;
va_list ap;
bool (*add)(brpc_val_t *, brpc_val_t *);
switch (type) {
case BRPC_VAL_MAP: add = _brpc_map_add; break;
case BRPC_VAL_LIST: add = _brpc_list_add; break;
case BRPC_VAL_AVP: add = NULL; break; /* force sigsegv */
default:
BUG("illegal or unsupported type as sequence (%d).\n", type);
return NULL;
}
IALLOC(rec, type);
INIT_LIST_HEAD(&rec->val.seq.list);
va_start(ap, type);
while ((val = va_arg(ap, brpc_val_t *)))
if (! add(rec, val))
goto error;
va_end(ap);
return rec;
error:
INIT_LIST_HEAD(&rec->val.seq.list); /* avoid freeing the rcvd arguments */
brpc_val_free(rec);
return NULL;
}
brpc_val_t *brpc_val_deserialize(uint8_t **_pos, const uint8_t *end)
{
unsigned char hdr;
uint8_t *rec_limit, *pos;
int have_len_fld;
size_t size, val_len;
unsigned int type;
brpc_val_t *new_rec;
pos = *_pos;
if (end <= pos)
return NULL;
hdr = *pos; pos ++;
have_len_fld = hdr & REC_HAVE_SFLD;
size = (hdr >> REC_SIZE_OFF) & REC_SSIZE_MASK;
type = hdr & ((1 << REC_TYPE_BITS) - 1);
DBG("decoding type %d.\n", type);
if (! have_len_fld)
val_len = size;
else {
if (end <= pos + size) {
WERRNO(EBADMSG);
ERR("record value size field out of bounds.\n");
return NULL;
}
val_len = ntohz(pos, size); pos += size;
}
if (end < pos + val_len) {
WERRNO(EBADMSG);
ERR("record value field out of bounds.\n");
return NULL;
}
if (val_len) {
rec_limit = pos + val_len;
new_rec = decode_by_type(&pos, rec_limit, type);
if (new_rec && (pos != rec_limit)) {
WERRNO(EBADMSG);
ERR("unconsumed buffer segment [%zd] after decoding record.\n",
rec_limit - pos);
brpc_val_free(new_rec);
return NULL;
}
} else {
new_rec = brpc_null(type);
new_rec->locked = true;
}
*_pos = pos;
return new_rec;
}
__LOCAL brpc_val_t *decode_by_type(uint8_t **start, const uint8_t *end,
brpc_vtype_t type)
{
switch (type) {
case BRPC_VAL_INT: return decode_int(start, end);
case BRPC_VAL_BIN: return decode_bin(start, end);
case BRPC_VAL_STR: return decode_str(start, end);
case BRPC_VAL_AVP: return decode_avp(start, end);
case BRPC_VAL_LIST: return decode_seq(start, end, BRPC_VAL_LIST);
case BRPC_VAL_MAP: return decode_seq(start, end, BRPC_VAL_MAP);
default:
WERRNO(EBADMSG);
ERR("unknown record type 0x%x.\n", type);
}
return NULL;
}
__LOCAL brpc_val_t *decode_int(uint8_t **_start, const uint8_t *end)
{
brpc_val_t *new_rec;
size_t len;
long integer;
len = end - *_start;
if (sizeof(brpc_int_t) < len) {
WERRNO(EBADMSG);
ERR("message's int representation (%zd) larger than local (%zd).\n",
len, sizeof(brpc_int_t));
return NULL;
}
if (! lntoh(*_start, len, &integer)) {
WERRNO(EBADMSG);
return NULL;
}
DBG("read [%zd]: %d.\n", len, integer);
new_rec = brpc_int(integer);
if (new_rec)
new_rec->locked = true;
*_start += len;
return new_rec;
}
__LOCAL brpc_val_t *decode_bin(uint8_t **_start, const uint8_t *end)
{
brpc_val_t *new_rec;
new_rec = _brpc_bin(*_start, end - *_start);
if (new_rec)
new_rec->locked = true;
*_start += new_rec->val.bin.len;
DBG("read [%zd]: `%.*s.\n'", end - *_start,
new_rec->val.bin.len, new_rec->val.bin.val);
return new_rec;
}
__LOCAL brpc_val_t *decode_str(uint8_t **_start, const uint8_t *end)
{
brpc_val_t *new_rec;
if (! (end - *_start)) {
WERRNO(EBADMSG);
ERR("string record can not be empty (need at least 0-term).\n");
return NULL;
}
if (end[-1]) {
WERRNO(EBADMSG);
ERR("string record misses 0-terminator (got 0x%x).\n", end[-1]);
return NULL;
}
new_rec = _brpc_str((char *)*_start, end - *_start);
if (new_rec)
new_rec->locked = true;
*_start += new_rec->val.bin.len;
DBG("read [%zd]: `%.*s.\n'", end - *_start,
new_rec->val.str.len, new_rec->val.str.val);
return new_rec;
}
__LOCAL brpc_val_t *decode_avp(uint8_t **_start, const uint8_t *end)
{
brpc_val_t *name, *val, *new_rec;
name = brpc_val_deserialize(_start, end);
if (! name)
return NULL;
val = brpc_val_deserialize(_start, end);
if (! val) {
brpc_val_free(name);
return NULL;
}
new_rec = brpc_avp(name, val);
if (! new_rec) {
brpc_val_free(name);
brpc_val_free(val);
return NULL;
} else {
new_rec->locked = true;
}
return new_rec;
}
__LOCAL brpc_val_t *decode_seq(uint8_t **_start, const uint8_t *end,
brpc_vtype_t type)
{
brpc_val_t *seq_val, *sub_val, *name;
switch (type) {
case BRPC_VAL_LIST: seq_val = brpc_list(NULL); break;
case BRPC_VAL_MAP: seq_val = brpc_map(NULL); break;
default:
WERRNO(EINVAL);
BUG("illegal or unsupported type (%u) as sequence.\n", type);
return NULL;
}
if (! seq_val)
return NULL;
seq_val->locked = true;
while (*_start < end) {
sub_val = brpc_val_deserialize(_start, end);
if (! sub_val)
/* TODO: support for struct terminator (0x83) */
goto error;
switch (type) {
case BRPC_VAL_MAP:
if (sub_val->type != BRPC_VAL_AVP) {
WERRNO(EPROTO);
ERR("map type only accepts AVP as subvalue "
"(received: 0x%x).\n", sub_val->type);
goto err_subval;
}
name = brpc_avp_name(sub_val);
if ((name->type != BRPC_VAL_STR)
#ifdef INT_AS_ID
&& (name->type != BRPC_VAL_INT)
#endif
) {
WERRNO(EPROTO);
ERR("unsupported record type (0x%x) as AVP identifier.\n",
name->type);
goto err_subval;
}
break;
default: break;
}
brpc_seq_add(seq_val, sub_val);
}
return seq_val;
err_subval:
brpc_val_free(sub_val);
error:
brpc_val_free(seq_val);
return NULL;
}
bool brpc_list_add(brpc_val_t *list, brpc_val_t *val)
{
return _brpc_list_add(list, val);
}
__LOCAL bool _brpc_list_add(brpc_val_t *list, brpc_val_t *_val)
{
brpc_val_t *val;
if (list->type != BRPC_VAL_LIST) {
WERRNO(EINVAL);
ERR("type (%d) does support enlisting values.\n", list->type);
return false;
}
if (list->locked) {
WERRNO(EINVAL);
ERR("can not enlist into locked list.\n");
return false;
}
if (! list_empty(&_val->list)) {
WERRNO(EINVAL);
ERR("can not enlist already enlisted value (type: %d).\n",
_val->type);
return false;
}
if (_val->locked) {
val = brpc_val_clone(_val);
if (! val)
return false;
} else {
val = _val;
}
brpc_seq_add(list, val);
return true;
}
bool brpc_map_add(brpc_val_t *map, brpc_val_t *avp)
{
return _brpc_map_add(map, avp);
}
__LOCAL bool _brpc_map_add(brpc_val_t *map, brpc_val_t *_avp)
{
brpc_val_t *name, *avp;
if (map->type != BRPC_VAL_MAP) {
WERRNO(EINVAL);
ERR("type (%d) does not mapping values.\n", map->type);
return false;
}
if (map->locked) {
WERRNO(EINVAL);
ERR("can not do mapping into locked map.\n");
return false;
}
if (_avp->type != BRPC_VAL_AVP) {
WERRNO(EINVAL);
ERR("map type only accepts AVPs as subvals (tried: %d).\n",
_avp->type);
return false;
}
if (! list_empty(&_avp->list)) {
WERRNO(EINVAL);
ERR("can not do mapping with already mapped AVP.\n");
return false;
}
if (_avp->locked) {
avp = brpc_val_clone(_avp);
if (! avp)
return false;
} else {
avp = _avp;
}
name = brpc_avp_name(avp);
if ((name->type != BRPC_VAL_STR)
#ifdef INT_AS_ID
&& (name->type != BRPC_VAL_INT)
#endif /* INT_AS_ID */
) {
WERRNO(EINVAL);
ERR("unsupported record type (%d) as AVP identifier.\n", name->type);
return false;
}
brpc_seq_add(map, avp);
return true;
}
bool brpc_avp_add(brpc_val_t *avp, brpc_val_t *_member)
{
brpc_val_t *member;
if (avp->type != BRPC_VAL_AVP) {
WERRNO(EINVAL);
ERR("type (%d) is not of type attribute.\n", avp->type);
return false;
}
if (avp->locked) {
WERRNO(EINVAL);
ERR("attribute locked: can not add value");
return false;
}
if (! list_empty(&_member->list)) {
WERRNO(EINVAL);
ERR("can not attribute already used value.\n");
return false;
}
if (list_empty(&avp->val.seq.list)) {
if ((_member->type != BRPC_VAL_STR)
#ifdef INT_AS_ID
&& (_member->type != BRPC_VAL_INT)
#endif
) {
WERRNO(EINVAL);
ERR("type (%d) can not be used as attribute identifier.\n",
_member->type);
return false;
}
} else if (&avp->val.seq.list != avp->val.seq.list.next->next) {
WERRNO(EEXIST);
ERR("attribute already has a value.\n");
return false;
}
if (_member->locked) {
member = brpc_val_clone(_member);
if (! member)
return false;
} else {
member = _member;
}
brpc_seq_add(avp, member);
return true;
}
uint8_t* brpc_val_serialize(brpc_val_t *val, uint8_t *start,
const uint8_t *end)
{
DBG("serializing BINRPC value of type %d.\n", val->type);
if (val->null)
return serialize_null(val, start, end);
switch (val->type) {
case BRPC_VAL_INT: return serialize_int(val, start, end);
case BRPC_VAL_STR:
case BRPC_VAL_BIN: return serialize_bin(val, start, end);
case BRPC_VAL_AVP:
case BRPC_VAL_MAP:
case BRPC_VAL_LIST: return serialize_complex(val, start, end);
default:
WERRNO(EINVAL);
BUG("unknow val type %d to serialize.\n", val->type);
return NULL;
}
}
__LOCAL uint8_t *serialize_null(brpc_val_t *val, uint8_t *pos,
const uint8_t *end)
{
if ((end - pos) < REC_HDR_SIZE) {
WERRNO(ENOBUFS);
return NULL;
}
*pos = val->type;
return pos + REC_HDR_SIZE;
}
__LOCAL uint8_t *serialize_int(brpc_val_t *val, uint8_t *pos,
const uint8_t *end)
{
size_t ilen;
bool needs; /* does the len need a field of its own? */
ilen = sizeofl(val->val.int32);
ilen = clss(ilen);
needs = (MAX_REC_SSIZE < ilen) ? true : false;
if (end - pos < REC_HDR_SIZE + ilen + (needs ? 1 : 0)) {
WERRNO(ENOBUFS);
return NULL;
}
*pos = ilen << REC_TYPE_BITS;
*pos |= BRPC_VAL_INT;
pos ++;
if (needs) {
assert(ilen < 0xFF);
pos += lhton(pos, ilen);
}
pos += lhton(pos, val->val.int32);
DBG("written (%zd:%zd): %d.\n", REC_HDR_SIZE + (needs?1:0), ilen,
val->val.int32);
return pos;
}
__LOCAL uint8_t *serialize_bin(brpc_val_t *val, uint8_t *pos,
const uint8_t *end)
{
size_t lensize;
if (val->val.bin.len <= MAX_REC_SSIZE) {
/* do I have enough room? */
if ((end-pos) < REC_HDR_SIZE + val->val.bin.len) {
WERRNO(ENOBUFS);
return NULL;
}
*pos = val->val.bin.len << REC_TYPE_BITS;
*pos |= val->type;
pos ++;
DBG("written bin/str (%zd:%zd): `%.*s'.\n", REC_HDR_SIZE,
val->val.bin.len, val->val.bin.len, val->val.bin.val);
} else {
/* need to use 'opt value len' header */
lensize = sizeofz(val->val.bin.len);
assert(lensize < MAX_REC_SSIZE); /* not a string with len >= 1<<32 */
/* do I have enough room? */
if ((end - pos) < REC_HDR_SIZE + lensize + val->val.bin.len) {
WERRNO(ENOBUFS);
return NULL;
}
/* looks good: do copy */
*pos = lensize << REC_TYPE_BITS;
*pos |= REC_HAVE_SFLD; /* record uses optional lenth */
*pos |= val->type;
pos ++;
htonz(pos, val->val.bin.len);
pos += lensize;
DBG("written bin/str (%zd:%zd): `%.*s'.\n", REC_HDR_SIZE +
sizeofz(val->val.bin.len), val->val.bin.len,
val->val.bin.len, val->val.bin.val);
}
memcpy(pos, val->val.bin.val, val->val.bin.len);
pos += val->val.bin.len;
return pos;
}
__LOCAL uint8_t *serialize_complex(brpc_val_t *val, uint8_t *pos,
const uint8_t *end)
{
uint8_t *start;
size_t clen, avail, lensize;
start = pos;
pos = serialize_content(val, pos, end);
clen = pos - start;
avail = end - pos;
if (clen <= (1 << REC_SIZE_BITS) - 1) {
/* enough buffer room? */
if (avail < REC_HDR_SIZE) {
WERRNO(ENOBUFS);
return NULL;
}
/* move all buffer up REC_HDR_SIZE bytes */
memmove(start + REC_HDR_SIZE, start, clen);
*start = clen << REC_TYPE_BITS;
*start |= val->type;
pos += REC_HDR_SIZE;
} else {
/* need to use 'opt value len' header */
lensize = sizeofz(clen);
if (avail < REC_HDR_SIZE + lensize) {
WERRNO(ENOBUFS);
return NULL;
}
memmove(start + REC_HDR_SIZE + lensize, start, clen);
*start = lensize << REC_TYPE_BITS;
*start |= REC_HAVE_SFLD;
*start |= val->type;
start ++;
htonz(start, clen);
pos += REC_HDR_SIZE + lensize;
}
return pos;
}
__LOCAL uint8_t *serialize_content(brpc_val_t *val, uint8_t *pos,
const uint8_t *end)
{
struct brpc_list_head *k;
switch (val->type) {
case BRPC_VAL_AVP:
case BRPC_VAL_MAP:
case BRPC_VAL_LIST:
list_for_each(k, &val->val.seq.list) {
pos = brpc_val_serialize(_BRPC_VAL4LIST(k), pos, end);
if (! pos)
return NULL;
}
break;
default:
WERRNO(EINVAL);
BUG("value type 0x%x not a complex type.\n", val->type);
return NULL;
}
return pos;
}
brpc_val_t *brpc_val_clone(const brpc_val_t *orig)
{
brpc_val_t *clo, *dup;
bool (*add)(brpc_val_t *, brpc_val_t *);
struct brpc_list_head *pos;
if (orig->null)
return brpc_null(orig->type);
switch (orig->type) {
case BRPC_VAL_INT:
return brpc_int(orig->val.int32);
case BRPC_VAL_BIN:
return brpc_bin(orig->val.bin.val, orig->val.bin.len);
case BRPC_VAL_STR:
return brpc_str(orig->val.str.val, orig->val.str.len);
do {
case BRPC_VAL_AVP:
dup = brpc_empty_avp();
add = brpc_avp_add;
break;
case BRPC_VAL_MAP:
dup = brpc_map(NULL);
add = brpc_map_add;
break;
case BRPC_VAL_LIST:
dup = brpc_list(NULL);
add = brpc_list_add;
} while (0);
if (! dup)
return NULL;
list_for_each(pos, &orig->val.seq.list) {
clo = brpc_val_clone(_BRPC_VAL4LIST(pos));
if (! add(dup, clo))
brpc_val_free(dup);
return NULL;
}
return dup;
default:
BUG("illegal val type (%d).\n", orig->type);
return NULL;
}
}
__LOCAL bool write_repr(brpc_str_t *repr, ssize_t *pos, char val)
{
char *ptr;
size_t nsz;
if (repr->len < *pos + /*0-term*/1) {
switch (repr->len) {
case 0: nsz = BINRPC_MAX_REPR_LEN; break;
default: nsz = 2 * (repr->len - /*0-term*/1);
}
nsz ++; /*0-term*/
ptr = brpc_realloc(repr->val, nsz * sizeof(char));
if (! ptr) {
WERRNO(ENOMEM);
return false;
}
repr->len = nsz;
repr->val = ptr;
}
repr->val[(*pos)++] = val;
return true;
}
__LOCAL bool repr_val(brpc_val_t *rec, brpc_str_t *repr, ssize_t *pos)
{
char val, clo;
switch (rec->type) {
do {
case BRPC_VAL_INT: val = 'i'; break;
case BRPC_VAL_STR: val = 's'; break;
case BRPC_VAL_BIN: val = 'b'; break;
} while (0);
return write_repr(repr, pos, val);
do {
case BRPC_VAL_AVP: val = '<'; clo = '>'; break;
case BRPC_VAL_MAP: val = '{'; clo = '}'; break;
case BRPC_VAL_LIST: val = '['; clo = ']'; break;
} while (0);
if (! write_repr(repr, pos, val))
return false;
if (! brpc_vals_repr(&rec->val.seq.list, repr, pos))
return false;
return write_repr(repr, pos, clo);
default:
BUG("illegal value type (%d); unknown reprriptor.\n", rec->type);
}
return false;
}
bool brpc_vals_repr(struct brpc_list_head *head, brpc_str_t *repr, ssize_t *pos)
{
struct brpc_list_head *k;
brpc_val_t *val;
list_for_each(k, head) {
val = _BRPC_VAL4LIST(k);
if (! repr_val(val, repr, pos))
return false;
}
return true;
}