xref: /aosp_15_r20/external/tensorflow/tensorflow/core/lib/strings/proto_text_util.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #ifndef TENSORFLOW_CORE_LIB_STRINGS_PROTO_TEXT_UTIL_H_
17 #define TENSORFLOW_CORE_LIB_STRINGS_PROTO_TEXT_UTIL_H_
18 
19 #include "absl/strings/str_cat.h"
20 #include "tensorflow/core/platform/macros.h"
21 #include "tensorflow/core/platform/numbers.h"
22 #include "tensorflow/core/platform/protobuf.h"
23 #include "tensorflow/core/platform/scanner.h"
24 #include "tensorflow/core/platform/str_util.h"
25 #include "tensorflow/core/platform/strcat.h"
26 
27 namespace tensorflow {
28 namespace strings {
29 
30 static constexpr char kColonSeparator[] = ": ";
31 
32 // Helper functions for writing proto-text output.
33 // Used by the code generated from tools/proto_text/gen_proto_text_lib.cc.
34 class ProtoTextOutput {
35  public:
36   // Construct a ProtoTextOutput that writes to <output> If short_debug is true,
37   // outputs text to match proto.ShortDebugString(); else matches
38   // proto.DebugString().
ProtoTextOutput(string * output,bool short_debug)39   ProtoTextOutput(string* output, bool short_debug)
40       : output_(output),
41         short_debug_(short_debug),
42         field_separator_(short_debug ? " " : "\n") {}
43 
44   // Writes opening of nested message and increases indent level.
OpenNestedMessage(const char field_name[])45   void OpenNestedMessage(const char field_name[]) {
46     StrAppend(output_, level_empty_ ? "" : field_separator_, indent_,
47               field_name, " {", field_separator_);
48     if (!short_debug_) StrAppend(&indent_, "  ");
49     level_empty_ = true;
50   }
51 
52   // Writes close of nested message and decreases indent level.
CloseNestedMessage()53   void CloseNestedMessage() {
54     if (!short_debug_) indent_.resize(indent_.size() - 2);
55     StrAppend(output_, level_empty_ ? "" : field_separator_, indent_, "}");
56     level_empty_ = false;
57   }
58 
59   // Print the close of the top-level message that was printed.
CloseTopMessage()60   void CloseTopMessage() {
61     if (!short_debug_ && !level_empty_) StrAppend(output_, "\n");
62   }
63 
64   // Appends a numeric value, like my_field: 123
65   template <typename T>
AppendNumeric(const char field_name[],T value)66   void AppendNumeric(const char field_name[], T value) {
67     AppendFieldAndValue(field_name, StrCat(value));
68   }
69 
70   // Appends a numeric value, like my_field: 123, but only if value != 0.
71   template <typename T>
AppendNumericIfNotZero(const char field_name[],T value)72   void AppendNumericIfNotZero(const char field_name[], T value) {
73     if (value != 0) AppendNumeric(field_name, value);
74   }
75 
76   // Appends a bool value, either my_field: true or my_field: false.
AppendBool(const char field_name[],bool value)77   void AppendBool(const char field_name[], bool value) {
78     AppendFieldAndValue(field_name, value ? "true" : "false");
79   }
80 
81   // Appends a bool value, as my_field: true, only if value is true.
AppendBoolIfTrue(const char field_name[],bool value)82   void AppendBoolIfTrue(const char field_name[], bool value) {
83     if (value) AppendBool(field_name, value);
84   }
85 
86   // Appends a string value, like my_field: "abc123".
AppendString(const char field_name[],const string & value)87   void AppendString(const char field_name[], const string& value) {
88     AppendFieldAndValue(
89         field_name, StrCat("\"", ::tensorflow::str_util::CEscape(value), "\""));
90   }
91 
92   // Appends a string value, like my_field: "abc123", but only if value is not
93   // empty.
AppendStringIfNotEmpty(const char field_name[],const string & value)94   void AppendStringIfNotEmpty(const char field_name[], const string& value) {
95     if (!value.empty()) AppendString(field_name, value);
96   }
97 
98   // Appends the string name of an enum, like my_field: FIRST_ENUM.
AppendEnumName(const char field_name[],const string & name)99   void AppendEnumName(const char field_name[], const string& name) {
100     AppendFieldAndValue(field_name, name);
101   }
102 
103  private:
AppendFieldAndValue(const char field_name[],StringPiece value_text)104   void AppendFieldAndValue(const char field_name[], StringPiece value_text) {
105     absl::StrAppend(output_, level_empty_ ? "" : field_separator_, indent_,
106                     field_name, kColonSeparator, value_text);
107     level_empty_ = false;
108   }
109 
110   string* const output_;
111   const bool short_debug_;
112   const string field_separator_;
113   string indent_;
114 
115   // False when at least one field has been output for the message at the
116   // current deepest level of nesting.
117   bool level_empty_ = true;
118 
119   TF_DISALLOW_COPY_AND_ASSIGN(ProtoTextOutput);
120 };
121 
ProtoSpaceAndComments(Scanner * scanner)122 inline void ProtoSpaceAndComments(Scanner* scanner) {
123   for (;;) {
124     scanner->AnySpace();
125     if (scanner->Peek() != '#') return;
126     // Skip until newline.
127     while (scanner->Peek('\n') != '\n') scanner->One(Scanner::ALL);
128   }
129 }
130 
131 // Parse the next numeric value from <scanner>, returning false if parsing
132 // failed.
133 template <typename T>
ProtoParseNumericFromScanner(Scanner * scanner,T * value)134 bool ProtoParseNumericFromScanner(Scanner* scanner, T* value) {
135   StringPiece numeric_str;
136   scanner->RestartCapture();
137   if (!scanner->Many(Scanner::LETTER_DIGIT_DOT_PLUS_MINUS)
138            .GetResult(nullptr, &numeric_str)) {
139     return false;
140   }
141 
142   // Special case to disallow multiple leading zeroes, to match proto parsing.
143   int leading_zero = 0;
144   for (size_t i = 0; i < numeric_str.size(); ++i) {
145     const char ch = numeric_str[i];
146     if (ch == '0') {
147       if (++leading_zero > 1) return false;
148     } else if (ch != '-') {
149       break;
150     }
151   }
152 
153   ProtoSpaceAndComments(scanner);
154   return SafeStringToNumeric<T>(numeric_str, value);
155 }
156 
157 // Parse the next boolean value from <scanner>, returning false if parsing
158 // failed.
159 bool ProtoParseBoolFromScanner(Scanner* scanner, bool* value);
160 
161 // Parse the next string literal from <scanner>, returning false if parsing
162 // failed.
163 bool ProtoParseStringLiteralFromScanner(Scanner* scanner, string* value);
164 
165 }  // namespace strings
166 }  // namespace tensorflow
167 
168 #endif  // TENSORFLOW_CORE_LIB_STRINGS_PROTO_TEXT_UTIL_H_
169