diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
commit | 5a40295c4cf0af5ea8da9ced04a4ce7d3621a080 (patch) | |
tree | cb21506e7b04d10b45d6066a0ee1655563d5d52b /samples/reflection |
Squashed 'flatcc/' content from commit 473da2a
git-subtree-dir: flatcc
git-subtree-split: 473da2afa5ca435363f8c5e6569167aee6bc31c5
Diffstat (limited to 'samples/reflection')
-rw-r--r-- | samples/reflection/CMakeLists.txt | 31 | ||||
-rw-r--r-- | samples/reflection/bfbs2json.c | 314 | ||||
-rwxr-xr-x | samples/reflection/build.sh | 27 |
3 files changed, 372 insertions, 0 deletions
diff --git a/samples/reflection/CMakeLists.txt b/samples/reflection/CMakeLists.txt new file mode 100644 index 0000000..cfbef1c --- /dev/null +++ b/samples/reflection/CMakeLists.txt @@ -0,0 +1,31 @@ +include(CTest) + +# +# This projects depends headers generated from reflection.fbs but these +# are pre-generated in `include/flatcc/reflection` so we don't need to +# build them here. +# +# What we do build is a binary schema `monster.bfbs` for the monster +# sample, and the actual C source of this project. +# + +set(INC_DIR "${PROJECT_SOURCE_DIR}/include") +set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(FBS_DIR "${PROJECT_SOURCE_DIR}/samples/monster") + +include_directories("${GEN_DIR}" "${INC_DIR}") + +add_custom_target(gen_monster_bfbs ALL) +add_custom_command ( + TARGET gen_monster_bfbs + COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}" + COMMAND flatcc_cli --schema -o "${GEN_DIR}" "${FBS_DIR}/monster.fbs" + DEPENDS flatcc_cli "${FBS_DIR}/monster.fbs" +) +add_executable(bfbs2json bfbs2json.c) +add_dependencies(bfbs2json gen_monster_bfbs) +target_link_libraries(bfbs2json flatccrt) + +if (FLATCC_TEST) + add_test(bfbs2json bfbs2json${CMAKE_EXECUTABLE_SUFFIX} ${GEN_DIR}/monster.bfbs) +endif() diff --git a/samples/reflection/bfbs2json.c b/samples/reflection/bfbs2json.c new file mode 100644 index 0000000..03c31e7 --- /dev/null +++ b/samples/reflection/bfbs2json.c @@ -0,0 +1,314 @@ +#include "flatcc/support/readfile.h" +#include "flatcc/reflection/reflection_reader.h" + +/* -DFLATCC_PORTABLE may help if inttypes.h is missing. */ +#ifndef PRId64 +#include <inttypes.h> +#endif + + +/* + * Reads a binary schema generated by `flatcc` or Googles `flatc` tool, + * then prints the content out in a custom JSON format. + * + * Note: This is completely unrelated to `flatcc's` JSON support - we + * just needed to do something tangible with the data we read from the + * binary schema and opted to print it as JSON. + * + * The JSON can be pretty printed with an external tool, for example: + * + * cat monster_test_schema.json | jq '.' + */ + +void print_type(reflection_Type_table_t T) +{ + int first = 1; + printf("{"); + if (reflection_Type_base_type_is_present(T)) { + if (!first) { + printf(","); + } + printf("\"base_type\":\"%s\"", reflection_BaseType_name(reflection_Type_base_type(T))); + first = 0; + } + if (reflection_Type_element_is_present(T)) { + if (!first) { + printf(","); + } + printf("\"element\":\"%s\"", reflection_BaseType_name(reflection_Type_element(T))); + first = 0; + } + if (reflection_Type_index_is_present(T)) { + if (!first) { + printf(","); + } + printf("\"index\":%d", reflection_Type_index(T)); + first = 0; + } + if (reflection_Type_fixed_length_is_present(T)) { + if (!first) { + printf(","); + } + printf("\"fixed_length\":%d", reflection_Type_fixed_length(T)); + first = 0; + } + printf("}"); +} + +void print_attributes(reflection_KeyValue_vec_t KV) +{ + size_t i; + reflection_KeyValue_table_t attribute; + const char *key, *value; + + printf("["); + for (i = 0; i < reflection_KeyValue_vec_len(KV); ++i) { + attribute = reflection_KeyValue_vec_at(KV, i); + key = reflection_KeyValue_key(attribute); + value = reflection_KeyValue_value(attribute); + if (i > 0) { + printf(","); + } + printf("{\"key\":\"%s\"", key); + if (value) { + /* TODO: we ought to escape '\"' and other non-string-able characters. */ + printf(",\"value\":\"%s\"", value); + } + printf("}"); + } + printf("]"); +} + +void print_object(reflection_Object_table_t O) +{ + reflection_Field_vec_t Flds; + reflection_Field_table_t F; + size_t i; + + Flds = reflection_Object_fields(O); + printf("{\"name\":\"%s\"", reflection_Object_name(O)); + printf(",\"fields\":["); + for (i = 0; i < reflection_Field_vec_len(Flds); ++i) { + if (i > 0) { + printf(","); + } + F = reflection_Field_vec_at(Flds, i); + printf("{\"name\":\"%s\",\"type\":", reflection_Field_name(F)); + print_type(reflection_Field_type(F)); + if (reflection_Field_id_is_present(F)) { + printf(",\"id\":%hu", reflection_Field_id(F)); + } + if (reflection_Field_default_integer_is_present(F)) { + printf(",\"default_integer\":%"PRId64"", (int64_t)reflection_Field_default_integer(F)); + } + if (reflection_Field_default_real_is_present(F)) { + printf(",\"default_integer\":%lf", reflection_Field_default_real(F)); + } + if (reflection_Field_required_is_present(F)) { + printf(",\"required\":%s", reflection_Field_required(F) ? "true" : "false"); + } + if (reflection_Field_key_is_present(F)) { + printf(",\"key\":%s", reflection_Field_key(F) ? "true" : "false"); + } + if (reflection_Field_attributes_is_present(F)) { + printf(",\"attributes\":"); + print_attributes(reflection_Field_attributes(F)); + } + printf("}"); + } + printf("]"); + if (reflection_Object_is_struct_is_present(O)) { + printf(",\"is_struct\":%s", reflection_Object_is_struct(O) ? "true" : "false"); + } + if (reflection_Object_minalign_is_present(O)) { + printf(",\"minalign\":%d", reflection_Object_minalign(O)); + } + if (reflection_Object_bytesize_is_present(O)) { + printf(",\"bytesize\":%d", reflection_Object_bytesize(O)); + } + if (reflection_Object_attributes_is_present(O)) { + printf(",\"attributes\":"); + print_attributes(reflection_Object_attributes(O)); + } + printf("}"); +} + +void print_enum(reflection_Enum_table_t E) +{ + reflection_EnumVal_vec_t EnumVals; + reflection_EnumVal_table_t EV; + size_t i; + + printf("{\"name\":\"%s\"", reflection_Enum_name(E)); + EnumVals = reflection_Enum_values(E); + printf(",\"values\":["); + for (i = 0; i < reflection_Enum_vec_len(EnumVals); ++i) { + EV = reflection_EnumVal_vec_at(EnumVals, i); + if (i > 0) { + printf(","); + } + printf("{\"name\":\"%s\"", reflection_EnumVal_name(EV)); + if (reflection_EnumVal_value_is_present(EV)) { + printf(",\"value\":%"PRId64"", (int64_t)reflection_EnumVal_value(EV)); + } + if (reflection_EnumVal_object_is_present(EV)) { + printf(",\"object\":"); + print_object(reflection_EnumVal_object(EV)); + } + if (reflection_EnumVal_union_type_is_present(EV)) { + printf(",\"union_type\":"); + print_type(reflection_EnumVal_union_type(EV)); + } + printf("}"); + } + printf("]"); + if (reflection_Enum_is_union_is_present(E)) { + printf(",\"is_union\":%s", reflection_Enum_is_union(E) ? "true" : "false"); + } + printf(",\"underlying_type\":"); + print_type(reflection_Enum_underlying_type(E)); + if (reflection_Enum_attributes_is_present(E)) { + printf(",\"attributes\":"); + print_attributes(reflection_Enum_attributes(E)); + } + printf("}"); +} + +void print_call(reflection_RPCCall_table_t C) +{ + printf("{\"name\":\"%s\"", reflection_RPCCall_name(C)); + printf(",\"request\":"); + print_object(reflection_RPCCall_request(C)); + printf(",\"response\":"); + print_object(reflection_RPCCall_response(C)); + + if (reflection_RPCCall_attributes_is_present(C)) { + printf(",\"attributes\":"); + print_attributes(reflection_RPCCall_attributes(C)); + } + printf("}"); +} + +void print_service(reflection_Service_table_t S) +{ + reflection_RPCCall_vec_t calls; + size_t i; + + printf("{\"name\":\"%s\"", reflection_Service_name(S)); + + printf(",\"calls\":["); + calls = reflection_Service_calls(S); + for (i = 0; i < reflection_RPCCall_vec_len(calls); ++i) { + if (i > 0) { + printf(","); + } + print_call(reflection_RPCCall_vec_at(calls, i)); + } + printf("]"); + + if (reflection_Service_attributes_is_present(S)) { + printf(",\"attributes\":"); + print_attributes(reflection_Service_attributes(S)); + } + printf("}"); +} + +void print_schema(reflection_Schema_table_t S) +{ + reflection_Object_vec_t Objs; + reflection_Enum_vec_t Enums; + reflection_Service_vec_t Services; + size_t i; + + Objs = reflection_Schema_objects(S); + printf("{"); + printf("\"objects\":["); + for (i = 0; i < reflection_Object_vec_len(Objs); ++i) { + if (i > 0) { + printf(","); + } + print_object(reflection_Object_vec_at(Objs, i)); + } + printf("]"); + Enums = reflection_Schema_enums(S); + printf(",\"enums\":["); + for (i = 0; i < reflection_Enum_vec_len(Enums); ++i) { + if (i > 0) { + printf(","); + } + print_enum(reflection_Enum_vec_at(Enums, i)); + } + printf("]"); + if (reflection_Schema_file_ident_is_present(S)) { + printf(",\"file_ident\":\"%s\"", reflection_Schema_file_ident(S)); + } + if (reflection_Schema_file_ext_is_present(S)) { + printf(",\"file_ext\":\"%s\"", reflection_Schema_file_ext(S)); + } + if (reflection_Schema_root_table_is_present(S)) { + printf(",\"root_table\":"); + print_object(reflection_Schema_root_table(S)); + } + if (reflection_Schema_services_is_present(S)) { + printf(",\"services\":["); + Services = reflection_Schema_services(S); + for (i = 0; i < reflection_Service_vec_len(Services); ++i) { + if (i > 0) { + printf(","); + } + print_service(reflection_Service_vec_at(Services, i)); + } + printf("]"); + } + printf("}\n"); +} + +int load_and_dump_schema(const char *filename) +{ + void *buffer; + size_t size; + int ret = -1; + reflection_Schema_table_t S; + + buffer = readfile(filename, 100000, &size); + if (!buffer) { + fprintf(stderr, "failed to load binary schema file: '%s'\n", filename); + goto done; + } + if (size < 12) { + fprintf(stderr, "file too small to access: '%s'\n", filename); + goto done; + } + S = reflection_Schema_as_root(buffer); + if (!S) { + S = reflection_Schema_as_root((char*)buffer + 4); + if (S) { + fprintf(stderr, "(skipping length field of input buffer)\n"); + } + } + if (!S) { + fprintf(stderr, "input is not a valid schema"); + goto done; + } + print_schema(S); + ret = 0; + +done: + if (buffer) { + free(buffer); + } + return ret; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + fprintf(stderr, "usage: bfbs2json <filename>\n"); + fprintf(stderr, "reads a binary flatbuffer schema and prints it to compact json on stdout\n\n"); + fprintf(stderr, "pretty print with exernal tool, for example:\n" + " bfbs2json myschema.bfbs | python -m json.tool > myschema.json\n" + "note: also understands binary schema files with a 4 byte length prefix\n"); + exit(-1); + } + return load_and_dump_schema(argv[1]); +} diff --git a/samples/reflection/build.sh b/samples/reflection/build.sh new file mode 100755 index 0000000..84ccf8a --- /dev/null +++ b/samples/reflection/build.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e +cd `dirname $0`/../.. +ROOT=`pwd` +TMP=${ROOT}/build/tmp/samples/reflection + +CC=${CC:-cc} +${ROOT}/scripts/build.sh +mkdir -p ${TMP} +rm -rf ${TMP}/* +#bin/flatcc --schema --schema-length=yes -o ${TMP} test/monster_test/monster_test.fbs +bin/flatcc --schema -o ${TMP} test/monster_test/monster_test.fbs + +cp samples/reflection/*.c ${TMP} +cd ${TMP} +# We don't need debug version, but it is always useful to have if we get +# assertions in the interface code. +$CC -g -I ${ROOT}/include bfbs2json.c -o bfbs2jsond +$CC -O3 -DNDEBUG -I ${ROOT}/include bfbs2json.c -o bfbs2json +cp bfbs2json ${ROOT}/bin/bfbs2json +echo "generating example json output from monster_test.fbs schema ..." +${ROOT}/bin/bfbs2json ${TMP}/monster_test.bfbs > monster_test_schema.json +cat monster_test_schema.json | python -m json.tool > pretty_monster_test_schema.json +echo "test json file located in ${TMP}/monster_test_schema.json" +echo "pretty printed file located in ${TMP}/pretty_monster_test_schema.json" +echo "bfbs2json tool placed in ${ROOT}/bin/bfbs2json" |