libubox-blob/blobmsg

大部份內容來自libubox [3] - BLOB BLOGMSG,推薦閱讀原文。json

blob提供二進制數據處理能力。有幾種支持的數據類型,並能夠建立塊數據在socket上發送。整型數字會在libubox庫內部轉換爲網絡字節序進行處理。segmentfault

二進制塊的處理方法是建立一個TLV(類型-長度-值)鏈表數據,支持嵌套類型數據,並提供設置和獲取數據接口。blob定義在blob.h中。數組

blogmsg位於blob的上層,提供表格和數組等數據類型的處理,定義在blogmsg.h中。緩存

TLV是用於表示可變長度的數據格式,Type表示數據的類型,length表示數據長度,value存儲數據值。類型和長度的佔用空間是固定的,在libubox庫中共佔用4個字節。服務器

Value的長度由length指定。這樣能夠存儲和傳輸任何類型的數據,只需預先定義服務器和客戶端之間的TLV的類型和長度的空間大小便可。cookie

一. blob

blob(binary large object),二進制大對象,用於二進制對象序列化;blob主要在一些系統級組件(ubox/libubox/ubus/netifd)內部使用,通常應用不須要使用blob封裝,blob用數據結構blob_attr表示。網絡

blob_buf用於管理(多個)二進制大對象。數據結構

 1. 數據結構socket

#define BLOB_COOKIE     0x01234567

enum {
    BLOB_ATTR_UNSPEC,
    BLOB_ATTR_NESTED,
    BLOB_ATTR_BINARY,
    BLOB_ATTR_STRING,
    BLOB_ATTR_INT8,
    BLOB_ATTR_INT16,
    BLOB_ATTR_INT32,
    BLOB_ATTR_INT64,
    BLOB_ATTR_LAST
};

#define BLOB_ATTR_ID_MASK  0x7f000000
#define BLOB_ATTR_ID_SHIFT 24
#define BLOB_ATTR_LEN_MASK 0x00ffffff
#define BLOB_ATTR_ALIGN    4       //blob字節對齊
#define BLOB_ATTR_EXTENDED 0x80000000

// blob數據結構 struct blob_attr { uint32_t id_len; //id佔用最高字節,msb用於擴展,len佔用低3個字節 char data[]; } __packed; // 屬性過濾 struct blob_attr_info { unsigned int type; unsigned int minlen; unsigned int maxlen; bool (*validate)(const struct blob_attr_info *, struct blob_attr *); };
// 多個blob管理數據結構
struct blob_buf { struct blob_attr *head; // 指向blob_buf的開頭,分配一個4字節的blob_attr(僅有id_len),記錄已使用的len。最初時等於blob_buf->buf bool (*grow)(struct blob_buf *buf, int minlen); //內存擴展回調函數 int buflen; //buf總長度 void *buf; // 指向buf起始位置(開頭) };

2. 函數函數

 獲取blob屬性

/**
 * 返回指向BLOB屬性數據區指針
 */
static inline void * blob_data(const struct blob_attr *attr)

/**
 * 返回BLOB屬性ID
 */
static inline unsigned int blob_id(const struct blob_attr *attr)

/**
 * 判斷BLOB屬性擴展標誌是否爲真
 */
static inline bool blob_is_extended(const struct blob_attr *attr)

/**
 * 返回BLOB屬性有效存儲空間大小
 */
static inline unsigned int blob_len(const struct blob_attr *attr)

/*
 * 返回BLOB屬性徹底存儲空間大小(包括頭部)
 */
static inline unsigned int blob_raw_len(const struct blob_attr *attr)

/*
 * 返回BLOB屬性填補後存儲空間大小(包括頭部),存儲空間補齊大小
 */
static inline unsigned int blob_pad_len(const struct blob_attr *attr)
{
    unsigned int len = blob_raw_len(attr);
    len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1);
    return len;
}

獲取blob數據

static inline uint8_t blob_get_u8(const struct blob_attr *attr)

static inline uint16_t blob_get_u16(const struct blob_attr *attr)

static inline uint32_t blob_get_u32(const struct blob_attr *attr)

static inline uint64_t blob_get_u64(const struct blob_attr *attr)

static inline int8_t blob_get_int8(const struct blob_attr *attr)

static inline int16_t blob_get_int16(const struct blob_attr *attr)

static inline int32_t blob_get_int32(const struct blob_attr *attr)

static inline int64_t blob_get_int64(const struct blob_attr *attr)

static inline const char * blob_get_string(const struct blob_attr *attr)

設置blob數據

static inline struct blob_attr *
blob_put_string(struct blob_buf *buf, int id, const char *str)

static inline struct blob_attr *
blob_put_u8(struct blob_buf *buf, int id, uint8_t val)

static inline struct blob_attr *
blob_put_u16(struct blob_buf *buf, int id, uint16_t val)

static inline struct blob_attr *
blob_put_u32(struct blob_buf *buf, int id, uint32_t val)

static inline struct blob_attr *
blob_put_u64(struct blob_buf *buf, int id, uint64_t val)

#define blob_put_int8   blob_put_u8
#define blob_put_int16  blob_put_u16
#define blob_put_int32  blob_put_u32
#define blob_put_int64  blob_put_u64
/** * ptr - 指向struct blob_attr */
struct blob_attr * blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len)  

struct blob_attr * blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len)

遍歷

static inline struct blob_attr *
blob_next(const struct blob_attr *attr)
{
    return (struct blob_attr *) ((char *) attr + blob_pad_len(attr));
}

#define
__blob_for_each_attr(pos, attr, rem) \ for (pos = (void *) attr; \ rem > 0 && (blob_pad_len(pos) <= rem) && \ (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ rem -= blob_pad_len(pos), pos = blob_next(pos)) #define blob_for_each_attr(pos, attr, rem) \ for (rem = attr ? blob_len(attr) : 0, \ pos = attr ? blob_data(attr) : 0; \ rem > 0 && (blob_pad_len(pos) <= rem) && \ (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ rem -= blob_pad_len(pos), pos = blob_next(pos))

複製

extern struct blob_attr *blob_memdup(struct blob_attr *attr);

嵌套

extern void *blob_nest_start(struct blob_buf *buf, int id);
extern void blob_nest_end(struct blob_buf *buf, void *cookie);

判斷

bool blob_check_type(const void *ptr, unsigned int len, int type);
bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2);

初始化和銷燬

blob_buf通常聲明爲本地靜態變量,id通常使用0(BLOBMSG_TYPE_UNSPEC)來初始化。

/**
 * 初始化BLOB buffer
 */
int blob_buf_init(struct blob_buf *buf, int id)

/**
 * 銷燬BLOB buffer
 */
void blob_buf_free(struct blob_buf *buf)

解析blob

/**
 * 從attr串中根據info策略過濾,獲得的結果存儲在data屬性數組中
 *
 * @param  attr 輸入BLOB屬性串
 * @param  data 輸出BLOB屬性數組
 * @param  info 屬性過濾策略
 * @param  max data數組大小
 */
int blob_parse(struct blob_attr *attr, struct blob_attr **data, 
               const struct blob_attr_info *info, int max)

二. blobmsg

blobmsg用於二進制對象網絡序列化。嵌套在blob數據結構(blob_attr)的data區。所以造成:blob_buff <- blob_attr -< blobmsg,blob_buff可存儲管理多個blob_attr,每一個blob_attr又可存儲管理一個blogmsg。且可存儲在線性數據區,不須要鏈表指針。

blobmsg_policy用於解析和緩存blobmsg列表,通常聲明爲一個靜態數組,用於指導消息解析。

blobmsg默認使用id爲table。array相似C語言的數組,table相似C的結構

1. 數據結構

#define BLOBMSG_ALIGN   2
#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1))

enum blobmsg_type {
    BLOBMSG_TYPE_UNSPEC,
    BLOBMSG_TYPE_ARRAY,
    BLOBMSG_TYPE_TABLE,
    BLOBMSG_TYPE_STRING,
    BLOBMSG_TYPE_INT64,
    BLOBMSG_TYPE_INT32,
    BLOBMSG_TYPE_INT16,
    BLOBMSG_TYPE_INT8,
    __BLOBMSG_TYPE_LAST,
    BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1,
    BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8,
};

struct blobmsg_hdr {
    uint16_t namelen;
    uint8_t name[];
} __packed;

// 解析blobmsg列表
struct blobmsg_policy { const char *name; // 與blobmsg_hdr->name對應 enum blobmsg_type type; // 策略值的類型,數據打包解包時做爲blob_attr id };

2. 獲取blogmsg屬性

/**
 * 根據BLOB消息名字長度計算出blobmsg頭部大小
 */
static inline int blobmsg_hdrlen(unsigned int namelen)

/**
 * 獲取BLOB消息名字
 */
static inline const char *blobmsg_name(const struct blob_attr *attr)

/**
 * 獲取BLOB消息類型
 */
static inline int blobmsg_type(const struct blob_attr *attr)

/**
 * 獲取BLOB消息數據內容
 */
static inline void *blobmsg_data(const struct blob_attr *attr)

/**
 * 獲取BLOB消息數據內容大小
 */
static inline int blobmsg_data_len(const struct blob_attr *attr)
{
    uint8_t *start, *end;

    start = (uint8_t *) blob_data(attr);
    end = (uint8_t *) blobmsg_data(attr);

    return blob_len(attr) - (end - start);
}
static inline int blobmsg_len(const struct blob_attr *attr)
{
    return blobmsg_data_len(attr);
}

3. 數據類型判斷

/**
 * 判斷BLOBMSG屬性類型是否合法
 */
bool blobmsg_check_attr(const struct blob_attr *attr, bool name)

4. 設置

int blobmsg_add_field(struct blob_buf *buf, int type, const char *name,
                      const void *data, unsigned int len)

static inline int
blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val)

static inline int
blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val)

static inline int
blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val)

static inline int
blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val)

static inline int
blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string)

static inline int
blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr)

/**
 * 格式化設備BLOGMSG
 */
void blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...)

5. 獲取

static inline uint8_t blobmsg_get_u8(struct blob_attr *attr)
static inline bool blobmsg_get_bool(struct blob_attr *attr)
static inline uint16_t blobmsg_get_u16(struct blob_attr *attr)
static inline uint32_t blobmsg_get_u32(struct blob_attr *attr)
static inline uint64_t blobmsg_get_u64(struct blob_attr *attr)
static inline char *blobmsg_get_string(struct blob_attr *attr)

6. 建立

/**
 * 建立BLOBMSG,返回數據區開始地址
 */
void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen)

/**
 * 擴大BLOGMSG,返回數據區開始地址
 */
void *blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen)

void blobmsg_add_string_buffer(struct blob_buf *buf)

7. 遍歷

#define blobmsg_for_each_attr(pos, attr, rem) \
    for (rem = attr ? blobmsg_data_len(attr) : 0, \
         pos = attr ? blobmsg_data(attr) : 0; \
         rem > 0 && (blob_pad_len(pos) <= rem) && \
         (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
         rem -= blob_pad_len(pos), pos = blob_next(pos))

8. 嵌套

static inline void * blobmsg_open_array(struct blob_buf *buf, const char *name)
static inline void blobmsg_close_array(struct blob_buf *buf, void *cookie)

static inline void *blobmsg_open_table(struct blob_buf *buf, const char *name)
static inline void blobmsg_close_table(struct blob_buf *buf, void *cookie)

9. 解析blogmsg

/**
 * 從data BLOGMSG串中根據policy策略過濾,獲得的結果存儲在tb BLOGATTR數組中
 *
 * @param  policy 過濾策略
 * @param  policy_len 策略個數
 * @param  tb 返回屬性數據
 * @param  len data屬性個數
 */
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
                  struct blob_attr **tb, void *data, unsigned int len)

 blobmsg根節點是一個純粹的blob,因此blobmsg解析時須要注意:
(1)第一層解析,data必須取值爲blob_data(root_blob),len必須取值爲blob_len(root_blob)
(2)第二層及以上解析,data必須取值爲blobmsg_data(sub_blob),len必須取值爲blobmsg_data_len(sub_blob)
因此,應避免混合使用blob和blobmsg語義,好比第一層使用blob語義,第二層使用blobmsg語義。

三. blobmsg_json

blobmsg_json用於json對象的序列化,json提供腳本級消息發送機制,若是應用須要腳本配合,則須要使用json。

四. 示例

UCI配置文件: /etc/config/test

config policy test
    option name 'test'
    option enable '1'
    option dns '1.1.1.1 2.2.2.2'

定義參數列表

enum {
    POLICY_ATTR_NAME,       /** name */
    POLICY_ATTR_ENABLE,     /** enable */
    POLICY_ATTR_DNS,        /** dns */
    __POLICY_ATTR_MAX
};

static const struct blobmsg_policy policy_attrs[__POLICY_ATTR_MAX] = {
    [POLICY_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
    [POLICY_ATTR_ENABLE] = { .name = "enable", .type = BLOBMSG_TYPE_BOOL },
    [POLICY_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
};

/** 定義BLOBMSG_TYPE_ARRAY類型參數的實際數據類型 */
static const struct uci_blob_param_info policy_attr_info[__POLICY_ATTR_MAX] = {
    [POLICY_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING },
};

static const struct uci_blob_param_list policy_attr_list = {
    .n_params = __POLICY_ATTR_MAX,
    .params = policy_attrs,
    .info = policy_attr_info,
};

轉化爲blob

static struct uci_context *g_uci_ctx;
static struct blob_buf *b;

void
transform(const char *config)
{
    struct uci_context *ctx = g_uci_ctx;
    struct uci_package *p = NULL;

    if (!ctx) {
        ctx = uci_alloc_context();
        g_uci_ctx = ctx;
        uci_set_confdir(ctx, NULL);
    } else {
        p = uci_lookup_package(ctx, config);
        if (p)
            uci_unload(ctx, p);
    }

    if (uci_load(ctx, config, &p))
        return;    

    struct uci_element *e;
    struct blob_attr *config = NULL;
    uci_foreach_element(&p->sectons, e) {
        struct uci_section *s = uci_to_section(e);

        blob_buf_init(&b, 0);
        uci_to_blob(&b, s, &policy_attr_list);
        config = blob_memdup(b.head);

        /**
         * do something with `config` 
         * free(config), when not use it
         */
    }
}

使用轉換後的blob

void
foo(blob_attr *confg)
{
    struct blob_attr *tb[__POLICY_ATTR_MAX];

    blobmsg_parse(policy_attrs, __POLICY_ATTR_MAX, tb,
            blob_data(config), blob_len(config));

    /**
     * do something with *tb[] 
     */
}
本站公眾號
   歡迎關注本站公眾號,獲取更多信息