"Fossies" - the Fresh Open Source Software Archive

Member "flatbuffers-23.1.21/src/annotated_binary_text_gen.cpp" (21 Jan 2023, 15592 Bytes) of package /linux/misc/flatbuffers-23.1.21.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "annotated_binary_text_gen.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 23.1.4_vs_23.1.20.

    1 #include "annotated_binary_text_gen.h"
    2 
    3 #include <algorithm>
    4 #include <sstream>
    5 #include <string>
    6 
    7 #include "binary_annotator.h"
    8 #include "flatbuffers/base.h"
    9 #include "flatbuffers/util.h"
   10 
   11 namespace flatbuffers {
   12 namespace {
   13 
   14 struct OutputConfig {
   15   size_t largest_type_string = 10;
   16 
   17   size_t largest_value_string = 20;
   18 
   19   size_t max_bytes_per_line = 8;
   20 
   21   size_t offset_max_char = 4;
   22 
   23   char delimiter = '|';
   24 };
   25 
   26 static std::string ToString(const BinarySectionType type) {
   27   switch (type) {
   28     case BinarySectionType::Header: return "header";
   29     case BinarySectionType::Table: return "table";
   30     case BinarySectionType::RootTable: return "root_table";
   31     case BinarySectionType::VTable: return "vtable";
   32     case BinarySectionType::Struct: return "struct";
   33     case BinarySectionType::String: return "string";
   34     case BinarySectionType::Vector: return "vector";
   35     case BinarySectionType::Unknown: return "unknown";
   36     case BinarySectionType::Union: return "union";
   37     case BinarySectionType::Padding: return "padding";
   38     default: return "todo";
   39   }
   40 }
   41 
   42 static bool IsOffset(const BinaryRegionType type) {
   43   return type == BinaryRegionType::UOffset || type == BinaryRegionType::SOffset;
   44 }
   45 
   46 template<typename T> std::string ToString(T value) {
   47   if (std::is_floating_point<T>::value) {
   48     std::stringstream ss;
   49     ss << value;
   50     return ss.str();
   51   } else {
   52     return std::to_string(value);
   53   }
   54 }
   55 
   56 template<typename T>
   57 std::string ToValueString(const BinaryRegion &region, const uint8_t *binary) {
   58   std::string s;
   59   s += "0x";
   60   const T val = ReadScalar<T>(binary + region.offset);
   61   const uint64_t start_index = region.offset + region.length - 1;
   62   for (uint64_t i = 0; i < region.length; ++i) {
   63     s += ToHex(binary[start_index - i]);
   64   }
   65   s += " (";
   66   s += ToString(val);
   67   s += ")";
   68   return s;
   69 }
   70 
   71 template<>
   72 std::string ToValueString<std::string>(const BinaryRegion &region,
   73                                        const uint8_t *binary) {
   74   return std::string(reinterpret_cast<const char *>(binary + region.offset),
   75                      static_cast<size_t>(region.array_length));
   76 }
   77 
   78 static std::string ToValueString(const BinaryRegion &region,
   79                                  const uint8_t *binary,
   80                                  const OutputConfig &output_config) {
   81   std::string s;
   82 
   83   if (region.array_length) {
   84     if (region.type == BinaryRegionType::Uint8 ||
   85         region.type == BinaryRegionType::Unknown) {
   86       // Interpet each value as a ASCII to aid debugging
   87       for (uint64_t i = 0; i < region.array_length; ++i) {
   88         const uint8_t c = *(binary + region.offset + i);
   89         s += isprint(c) ? static_cast<char>(c & 0x7F) : '.';
   90       }
   91       return s;
   92     } else if (region.type == BinaryRegionType::Char) {
   93       // string value
   94       return ToValueString<std::string>(region, binary);
   95     }
   96   }
   97 
   98   switch (region.type) {
   99     case BinaryRegionType::Uint32:
  100       return ToValueString<uint32_t>(region, binary);
  101     case BinaryRegionType::Int32: return ToValueString<int32_t>(region, binary);
  102     case BinaryRegionType::Uint16:
  103       return ToValueString<uint16_t>(region, binary);
  104     case BinaryRegionType::Int16: return ToValueString<int16_t>(region, binary);
  105     case BinaryRegionType::Bool: return ToValueString<bool>(region, binary);
  106     case BinaryRegionType::Uint8: return ToValueString<uint8_t>(region, binary);
  107     case BinaryRegionType::Char: return ToValueString<char>(region, binary);
  108     case BinaryRegionType::Byte:
  109     case BinaryRegionType::Int8: return ToValueString<int8_t>(region, binary);
  110     case BinaryRegionType::Int64: return ToValueString<int64_t>(region, binary);
  111     case BinaryRegionType::Uint64:
  112       return ToValueString<uint64_t>(region, binary);
  113     case BinaryRegionType::Double: return ToValueString<double>(region, binary);
  114     case BinaryRegionType::Float: return ToValueString<float>(region, binary);
  115     case BinaryRegionType::UType: return ToValueString<uint8_t>(region, binary);
  116 
  117     // Handle Offsets separately, incase they add additional details.
  118     case BinaryRegionType::UOffset:
  119       s += ToValueString<uint32_t>(region, binary);
  120       break;
  121     case BinaryRegionType::SOffset:
  122       s += ToValueString<int32_t>(region, binary);
  123       break;
  124     case BinaryRegionType::VOffset:
  125       s += ToValueString<uint16_t>(region, binary);
  126       break;
  127 
  128     default: break;
  129   }
  130   // If this is an offset type, include the calculated offset location in the
  131   // value.
  132   // TODO(dbaileychess): It might be nicer to put this in the comment field.
  133   if (IsOffset(region.type)) {
  134     s += " Loc: +0x";
  135     s += ToHex(region.points_to_offset, output_config.offset_max_char);
  136   }
  137   return s;
  138 }
  139 
  140 struct DocContinuation {
  141   // The start column where the value text first starts
  142   size_t value_start_column = 0;
  143 
  144   // The remaining part of the doc to print.
  145   std::string value;
  146 };
  147 
  148 static std::string GenerateTypeString(const BinaryRegion &region) {
  149   return ToString(region.type) +
  150          ((region.array_length)
  151               ? "[" + std::to_string(region.array_length) + "]"
  152               : "");
  153 }
  154 
  155 static std::string GenerateComment(const BinaryRegionComment &comment,
  156                                    const BinarySection &) {
  157   std::string s;
  158   switch (comment.type) {
  159     case BinaryRegionCommentType::Unknown: s = "unknown"; break;
  160     case BinaryRegionCommentType::SizePrefix: s = "size prefix"; break;
  161     case BinaryRegionCommentType::RootTableOffset:
  162       s = "offset to root table `" + comment.name + "`";
  163       break;
  164     // TODO(dbaileychess): make this lowercase to follow the convention.
  165     case BinaryRegionCommentType::FileIdentifier: s = "File Identifier"; break;
  166     case BinaryRegionCommentType::Padding: s = "padding"; break;
  167     case BinaryRegionCommentType::VTableSize: s = "size of this vtable"; break;
  168     case BinaryRegionCommentType::VTableRefferingTableLength:
  169       s = "size of referring table";
  170       break;
  171     case BinaryRegionCommentType::VTableFieldOffset:
  172       s = "offset to field `" + comment.name;
  173       break;
  174     case BinaryRegionCommentType::VTableUnknownFieldOffset:
  175       s = "offset to unknown field (id: " + std::to_string(comment.index) + ")";
  176       break;
  177 
  178     case BinaryRegionCommentType::TableVTableOffset:
  179       s = "offset to vtable";
  180       break;
  181     case BinaryRegionCommentType::TableField:
  182       s = "table field `" + comment.name;
  183       break;
  184     case BinaryRegionCommentType::TableUnknownField: s = "unknown field"; break;
  185     case BinaryRegionCommentType::TableOffsetField:
  186       s = "offset to field `" + comment.name + "`";
  187       break;
  188     case BinaryRegionCommentType::StructField:
  189       s = "struct field `" + comment.name + "`";
  190       break;
  191     case BinaryRegionCommentType::ArrayField:
  192       s = "array field `" + comment.name + "`[" +
  193           std::to_string(comment.index) + "]";
  194       break;
  195     case BinaryRegionCommentType::StringLength: s = "length of string"; break;
  196     case BinaryRegionCommentType::StringValue: s = "string literal"; break;
  197     case BinaryRegionCommentType::StringTerminator:
  198       s = "string terminator";
  199       break;
  200     case BinaryRegionCommentType::VectorLength:
  201       s = "length of vector (# items)";
  202       break;
  203     case BinaryRegionCommentType::VectorValue:
  204       s = "value[" + std::to_string(comment.index) + "]";
  205       break;
  206     case BinaryRegionCommentType::VectorTableValue:
  207       s = "offset to table[" + std::to_string(comment.index) + "]";
  208       break;
  209     case BinaryRegionCommentType::VectorStringValue:
  210       s = "offset to string[" + std::to_string(comment.index) + "]";
  211       break;
  212     case BinaryRegionCommentType::VectorUnionValue:
  213       s = "offset to union[" + std::to_string(comment.index) + "]";
  214       break;
  215 
  216     default: break;
  217   }
  218   if (!comment.default_value.empty()) { s += " " + comment.default_value; }
  219 
  220   switch (comment.status) {
  221     case BinaryRegionStatus::OK: break;  // no-op
  222     case BinaryRegionStatus::WARN: s = "WARN: " + s; break;
  223     case BinaryRegionStatus::WARN_NO_REFERENCES:
  224       s = "WARN: nothing refers to this section.";
  225       break;
  226     case BinaryRegionStatus::WARN_CORRUPTED_PADDING:
  227       s = "WARN: could be corrupted padding region.";
  228       break;
  229     case BinaryRegionStatus::WARN_PADDING_LENGTH:
  230       s = "WARN: padding is longer than expected.";
  231       break;
  232     case BinaryRegionStatus::ERROR: s = "ERROR: " + s; break;
  233     case BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY:
  234       s = "ERROR: " + s + ". Invalid offset, points outside the binary.";
  235       break;
  236     case BinaryRegionStatus::ERROR_INCOMPLETE_BINARY:
  237       s = "ERROR: " + s + ". Incomplete binary, expected to read " +
  238           comment.status_message + " bytes.";
  239       break;
  240     case BinaryRegionStatus::ERROR_LENGTH_TOO_LONG:
  241       s = "ERROR: " + s + ". Longer than the binary.";
  242       break;
  243     case BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT:
  244       s = "ERROR: " + s + ". Shorter than the minimum length: ";
  245       break;
  246     case BinaryRegionStatus::ERROR_REQUIRED_FIELD_NOT_PRESENT:
  247       s = "ERROR: " + s + ". Required field is not present.";
  248       break;
  249     case BinaryRegionStatus::ERROR_INVALID_UNION_TYPE:
  250       s = "ERROR: " + s + ". Invalid union type value.";
  251       break;
  252     case BinaryRegionStatus::ERROR_CYCLE_DETECTED:
  253       s = "ERROR: " + s + ". Invalid offset, cycle detected.";
  254       break;
  255   }
  256 
  257   return s;
  258 }
  259 
  260 static std::string GenerateDocumentation(const BinaryRegion &region,
  261                                          const BinarySection &section,
  262                                          const uint8_t *binary,
  263                                          DocContinuation &continuation,
  264                                          const OutputConfig &output_config) {
  265   std::string s;
  266 
  267   // Check if there is a doc continuation that should be prioritized.
  268   if (continuation.value_start_column) {
  269     s += std::string(continuation.value_start_column - 2, ' ');
  270     s += output_config.delimiter;
  271     s += " ";
  272 
  273     s += continuation.value.substr(0, output_config.max_bytes_per_line);
  274     continuation.value = continuation.value.substr(
  275         std::min(output_config.max_bytes_per_line, continuation.value.size()));
  276     return s;
  277   }
  278 
  279   {
  280     std::stringstream ss;
  281     ss << std::setw(static_cast<int>(output_config.largest_type_string)) << std::left;
  282     ss << GenerateTypeString(region);
  283     s += ss.str();
  284   }
  285   s += " ";
  286   s += output_config.delimiter;
  287   s += " ";
  288   if (region.array_length) {
  289     // Record where the value is first being outputted.
  290     continuation.value_start_column = s.size();
  291 
  292     // Get the full-length value, which we will chunk below.
  293     const std::string value = ToValueString(region, binary, output_config);
  294 
  295     std::stringstream ss;
  296     ss << std::setw(static_cast<int>(output_config.largest_value_string)) << std::left;
  297     ss << value.substr(0, output_config.max_bytes_per_line);
  298     s += ss.str();
  299 
  300     continuation.value =
  301         value.substr(std::min(output_config.max_bytes_per_line, value.size()));
  302   } else {
  303     std::stringstream ss;
  304     ss << std::setw(static_cast<int>(output_config.largest_value_string)) << std::left;
  305     ss << ToValueString(region, binary, output_config);
  306     s += ss.str();
  307   }
  308 
  309   s += " ";
  310   s += output_config.delimiter;
  311   s += " ";
  312   s += GenerateComment(region.comment, section);
  313 
  314   return s;
  315 }
  316 
  317 static std::string GenerateRegion(const BinaryRegion &region,
  318                                   const BinarySection &section,
  319                                   const uint8_t *binary,
  320                                   const OutputConfig &output_config) {
  321   std::string s;
  322   bool doc_generated = false;
  323   DocContinuation doc_continuation;
  324   for (uint64_t i = 0; i < region.length; ++i) {
  325     if ((i % output_config.max_bytes_per_line) == 0) {
  326       // Start a new line of output
  327       s += '\n';
  328       s += "  ";
  329       s += "+0x";
  330       s += ToHex(region.offset + i, output_config.offset_max_char);
  331       s += " ";
  332       s += output_config.delimiter;
  333     }
  334 
  335     // Add each byte
  336     s += " ";
  337     s += ToHex(binary[region.offset + i]);
  338 
  339     // Check for end of line or end of region conditions.
  340     if (((i + 1) % output_config.max_bytes_per_line == 0) ||
  341         i + 1 == region.length) {
  342       if (i + 1 == region.length) {
  343         // We are out of bytes but haven't the kMaxBytesPerLine, so we need to
  344         // zero those out to align everything globally.
  345         for (uint64_t j = i + 1; (j % output_config.max_bytes_per_line) != 0;
  346              ++j) {
  347           s += "   ";
  348         }
  349       }
  350       s += " ";
  351       s += output_config.delimiter;
  352       // This is the end of the first line or its the last byte of the region,
  353       // generate the end-of-line documentation.
  354       if (!doc_generated) {
  355         s += " ";
  356         s += GenerateDocumentation(region, section, binary, doc_continuation,
  357                                    output_config);
  358 
  359         // If we have a value in the doc continuation, that means the doc is
  360         // being printed on multiple lines.
  361         doc_generated = doc_continuation.value.empty();
  362       }
  363     }
  364   }
  365 
  366   return s;
  367 }
  368 
  369 static std::string GenerateSection(const BinarySection &section,
  370                                    const uint8_t *binary,
  371                                    const OutputConfig &output_config) {
  372   std::string s;
  373   s += "\n";
  374   s += ToString(section.type);
  375   if (!section.name.empty()) { s += " (" + section.name + ")"; }
  376   s += ":";
  377   for (const BinaryRegion &region : section.regions) {
  378     s += GenerateRegion(region, section, binary, output_config);
  379   }
  380   return s;
  381 }
  382 }  // namespace
  383 
  384 bool AnnotatedBinaryTextGenerator::Generate(
  385     const std::string &filename, const std::string &schema_filename) {
  386   OutputConfig output_config;
  387   output_config.max_bytes_per_line = options_.max_bytes_per_line;
  388 
  389   // Given the length of the binary, we can calculate the maximum number of
  390   // characters to display in the offset hex: (i.e. 2 would lead to 0XFF being
  391   // the max output).
  392   output_config.offset_max_char =
  393       binary_length_ > 0xFFFFFF
  394           ? 8
  395           : (binary_length_ > 0xFFFF ? 6 : (binary_length_ > 0xFF ? 4 : 2));
  396 
  397   // Find the largest type string of all the regions in this file, so we can
  398   // align the output nicely.
  399   output_config.largest_type_string = 0;
  400   for (const auto &section : annotations_) {
  401     for (const auto &region : section.second.regions) {
  402       std::string s = GenerateTypeString(region);
  403       if (s.size() > output_config.largest_type_string) {
  404         output_config.largest_type_string = s.size();
  405       }
  406 
  407       // Don't consider array regions, as they will be split to multiple lines.
  408       if (!region.array_length) {
  409         s = ToValueString(region, binary_, output_config);
  410         if (s.size() > output_config.largest_value_string) {
  411           output_config.largest_value_string = s.size();
  412         }
  413       }
  414     }
  415   }
  416 
  417   // Generate each of the binary sections
  418   std::string s;
  419 
  420   s += "// Annotated Flatbuffer Binary\n";
  421   s += "//\n";
  422   s += "// Schema file: " + schema_filename + "\n";
  423   s += "// Binary file: " + filename + "\n";
  424 
  425   for (const auto &section : annotations_) {
  426     s += GenerateSection(section.second, binary_, output_config);
  427     s += "\n";
  428   }
  429 
  430   // Modify the output filename.
  431   std::string output_filename = StripExtension(filename);
  432   output_filename += options_.output_postfix;
  433   output_filename +=
  434       "." + (options_.output_extension.empty() ? GetExtension(filename)
  435                                                : options_.output_extension);
  436 
  437   return SaveFile(output_filename.c_str(), s, false);
  438 }
  439 
  440 }  // namespace flatbuffers