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