text: #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_schema(reflection_Schema_table_t S)
  {
      reflection_Object_vec_t Objs;
      reflection_Enum_vec_t Enums;
      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));
      }
      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]);
  }
  