接上文

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

这四个函数

  1. wii_Packet_verify_as_root fid=wii_Packet_identifier
  2. wii_Packet_verify_as_typed_root fid=wii_Packet_type_identifier
  3. 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;
     }
    
  4. 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可用于区分。