flatbuffer 实例之 verifier
example_verifier.h
#ifndef EXAMPLE_VERIFIER_H
#define EXAMPLE_VERIFIER_H
/* Generated by flatcc 0.6.1-dev FlatBuffers schema compiler for C by dvide.com */
#ifndef EXAMPLE_READER_H
#include "example_reader.h"
#endif
#include "flatcc/flatcc_verifier.h"
#include "flatcc/flatcc_prologue.h"
static int wii_Packet_verify_table(flatcc_table_verifier_descriptor_t *td);
static int wii_Packet_verify_table(flatcc_table_verifier_descriptor_t *td)
{
int ret;
if ((ret = flatcc_verify_field(td, 0, 4, 4) /* a */)) return ret;
if ((ret = flatcc_verify_field(td, 1, 1, 1) /* b */)) return ret;
if ((ret = flatcc_verify_field(td, 2, 1, 1) /* b1 */)) return ret;
if ((ret = flatcc_verify_string_field(td, 3, 0) /* c */)) return ret;
return flatcc_verify_ok;
}
static inline int wii_Packet_verify_as_root(const void *buf, size_t bufsiz)
{
return flatcc_verify_table_as_root(buf, bufsiz, wii_Packet_identifier, &wii_Packet_verify_table);
}
static inline int wii_Packet_verify_as_typed_root(const void *buf, size_t bufsiz)
{
return flatcc_verify_table_as_root(buf, bufsiz, wii_Packet_type_identifier, &wii_Packet_verify_table);
}
static inline int wii_Packet_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
{
return flatcc_verify_table_as_root(buf, bufsiz, fid, &wii_Packet_verify_table);
}
static inline int wii_Packet_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
{
return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &wii_Packet_verify_table);
}
#include "flatcc/flatcc_epilogue.h"
#endif /* EXAMPLE_VERIFIER_H */
这里一共提供了4个校验函数,下面以实例来讲解。
接example_builder.h中的例子。
#include <stdio.h>
#include <stdlib.h>
#include "hexdump.h"
#include "example_builder.h"
#include "example_verifier.h"
int main(int argc, char* argv[])
{
int ret = 0;
char *dbuf = NULL;
size_t dsize;
flatcc_builder_t builder;
flatcc_builder_init(&builder);
int a = 77;
flatbuffers_bool_t b = flatbuffers_true;
flatbuffers_string_ref_t c = flatbuffers_string_create_str(&builder, "hello!!");
printf("a=0x%x b=%d c=%d\n", a, b, c);
// wii_Packet_create(&builder, a, b, c); /* 此创建函数不能通过下面的任何方式校验,因为没有header. */
// wii_Packet_create_as_root(&builder, a, b, c); /* 对应校验函数wii_Packet_verify_as_root OK */
wii_Packet_create_as_typed_root(&builder, a, b, c); /* 对应校验函数wii_Packet_verify_as_typed_root OK*/
dbuf = flatcc_builder_finalize_buffer(&builder, &dsize);
printf("dbuf=%p dsize=%zd\n", dbuf, dsize);
hexdump("Packet", dbuf, dsize, stderr);
ret = wii_Packet_verify_as_root(dbuf, dsize);
printf("Verifier:\nwii_Packet_verify_as_root=%d\n", ret);
ret = wii_Packet_verify_as_typed_root(dbuf, dsize);
printf("wii_Packet_verify_as_typed_root=%d\n", ret);
ret = wii_Packet_verify_as_root_with_identifier(dbuf, dsize, wii_Packet_type_identifier); /* 同wii_Packet_verify_as_typed_root */
printf("wii_Packet_verify_as_root_with_identifier=%d\n", ret);
ret = wii_Packet_verify_as_root_with_type_hash(dbuf, dsize, wii_Packet_type_hash);
printf("wii_Packet_verify_as_root_with_type_hash=%d\n", ret);
flatcc_builder_free(dbuf);
flatcc_builder_clear(&builder);
return 0;
}
$ ./build/fbproject
a=0x4d b=1 c=-12
dbuf=0x563acb530620 dsize=46
Packet:
00000000 08 00 00 00 ae 00 2e 04 e4 ff ff ff 4d 00 00 00 |............M...|
00000010 08 00 00 00 01 00 00 00 07 00 00 00 68 65 6c 6c |............hell|
00000020 6f 21 21 00 0a 00 0d 00 04 00 0c 00 08 00 |o!!...........|
Verifier:
wii_Packet_verify_as_root=0
wii_Packet_verify_as_typed_root=2 ? 因bug造成的校验失败,下面有阐述。
wii_Packet_verify_as_root_with_identifier=2 ? 因bug造成的校验失败,下面有阐述。
wii_Packet_verify_as_root_with_type_hash=0
这四个函数
- wii_Packet_verify_as_root fid=wii_Packet_identifier
- wii_Packet_verify_as_typed_root fid=wii_Packet_type_identifier
- wii_Packet_verify_as_root_with_identifier fid=自定义
内部调用的都是一个函数flatcc_verify_table_as_root,只是第三个参数fid不同。
int flatcc_verify_table_as_root(const void *buf, size_t bufsiz, const char *fid, flatcc_table_verifier_f *tvf) { check_result(flatcc_verify_buffer_header(buf, (uoffset_t)bufsiz, fid)); /* check_result扩展为 if ( flatcc_verify_buffer_header(buf, (uoffset_t)bufsiz, fid)) return flatcc_verify_buffer_header(buf, (uoffset_t)bufsiz, fid); */ return verify_table(buf, (uoffset_t)bufsiz, 0, read_uoffset(buf, 0), FLATCC_VERIFIER_MAX_LEVELS, tvf); }
校验包头header
int flatcc_verify_buffer_header(const void *buf, size_t bufsiz, const char *fid) { thash_t id, id2; verify_runtime(!(((size_t)buf) & (offset_size - 1)), flatcc_verify_error_runtime_buffer_header_not_aligned); /* 校验内存地址是否对齐 */ /* -8 ensures no scalar or offset field size can overflow. */ verify_runtime(bufsiz <= FLATBUFFERS_UOFFSET_MAX - 8, flatcc_verify_error_runtime_buffer_size_too_large); /* 校验包长度是否合法(最大值) -8应该是header部分大小 */ /* * Even if we specify no fid, the user might later. Therefore * require space for it. Not all buffer generators will take this * into account, so it is possible to fail an otherwise valid buffer * - but such buffers aren't safe. */ verify(bufsiz >= offset_size + FLATBUFFERS_IDENTIFIER_SIZE, flatcc_verify_error_buffer_header_too_small); /* 校验包长度是否合法(最小值) */ if (fid != 0) { id2 = read_thash_identifier(fid); id = read_thash(buf, offset_size); verify(id2 == 0 || id == id2, flatcc_verify_error_identifier_mismatch); } return flatcc_verify_ok; // 校验成功返回0 }
那这个fid是什么
fid可以理解为object的4字节长度的id值,flatcc在生成代码时会根据object的名字通过hash算法计算出这个id值。这里发现一个bug,如果计算出来的id值中间有0x00存在,后面的校验会失败 原因出自这段代码 read_thash_identifier -> flatbuffers_type_hash_from_string
static inline flatbuffers_thash_t flatbuffers_type_hash_from_string(const char *identifier) { flatbuffers_thash_t h = 0; const uint8_t *p = (const uint8_t *)identifier; if (!p[0]) return h; // 中间遇到0就退出了 h += ((flatbuffers_thash_t)p[0]); if (!p[1]) return h; // 中间遇到0就退出了 h += ((flatbuffers_thash_t)p[1]) << 8; if (!p[2]) return h; // 中间遇到0就退出了 h += ((flatbuffers_thash_t)p[2]) << 16; /* No need to test for termination here. */ h += ((flatbuffers_thash_t)p[3]) << 24; return h; }
例如以下的id就不行
#define wii_Packet_type_hash ((flatbuffers_thash_t)0x42e00ae) #define wii_Packet_type_identifier "\xae\x00\x2e\x04"
这是依据object名计算hash_id的函数
/* * Returns the type hash of a given name in native endian format. * Generated code already provides these, but if a name was changed * in the schema it may be relevant to recompute the hash manually. * * The wire-format of this value should always be little endian. * * Note: this must be the fully qualified name, e.g. in the namespace * "MyGame.Example": * * flatbuffers_type_hash_from_name("MyGame.Example.Monster"); * * or, in the global namespace just: * * flatbuffers_type_hash_from_name("MyTable"); * * This assumes 32 bit hash type. For other sizes, other FNV-1a * constants would be required. * * Note that we reserve hash value 0 for missing or ignored value. */ static inline flatbuffers_thash_t flatbuffers_type_hash_from_name(const char *name) { uint32_t hash = UINT32_C(2166136261); while (*name) { hash ^= (unsigned char)*name; hash = hash * UINT32_C(16777619); ++name; } if (hash == 0) { hash = UINT32_C(2166136261); } return hash; }
- wii2_Packet_verify_as_root_with_type_hash
它调用的函数是flatcc_verify_table_as_typed_root
int flatcc_verify_table_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash, flatcc_table_verifier_f *tvf) { check_result(flatcc_verify_typed_buffer_header(buf, (uoffset_t)bufsiz, thash)); return verify_table(buf, (uoffset_t)bufsiz, 0, read_uoffset(buf, 0), FLATCC_VERIFIER_MAX_LEVELS, tvf); }
int flatcc_verify_typed_buffer_header(const void *buf, size_t bufsiz, flatbuffers_thash_t thash) { thash_t id, id2; verify_runtime(!(((size_t)buf) & (offset_size - 1)), flatcc_verify_error_runtime_buffer_header_not_aligned); /* -8 ensures no scalar or offset field size can overflow. */ verify_runtime(bufsiz <= FLATBUFFERS_UOFFSET_MAX - 8, flatcc_verify_error_runtime_buffer_size_too_large); /* * Even if we specify no fid, the user might later. Therefore * require space for it. Not all buffer generators will take this * into account, so it is possible to fail an otherwise valid buffer * - but such buffers aren't safe. */ verify(bufsiz >= offset_size + FLATBUFFERS_IDENTIFIER_SIZE, flatcc_verify_error_buffer_header_too_small); if (thash != 0) { id2 = thash; id = read_thash(buf, offset_size); verify(id2 == 0 || id == id2, flatcc_verify_error_identifier_mismatch); } return flatcc_verify_ok; }
这个校验函数就绕过了上面的bug,因为它不需要调用flatbuffers_type_hash_from_string函数。
当你需要同时处理多种object的编码数据时,fid可用于区分。