大部份內容來自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(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用於二進制對象網絡序列化。嵌套在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用於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[] */ }