Home | History | Annotate | Download | only in strings
      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 "tensorflow/core/lib/strings/numbers.h"
     20 #include "tensorflow/core/lib/strings/scanner.h"
     21 #include "tensorflow/core/lib/strings/str_util.h"
     22 #include "tensorflow/core/lib/strings/strcat.h"
     23 #include "tensorflow/core/platform/macros.h"
     24 #include "tensorflow/core/platform/protobuf.h"
     25 
     26 namespace tensorflow {
     27 namespace strings {
     28 
     29 static constexpr char kColonSeparator[] = ": ";
     30 
     31 // Helper functions for writing proto-text output.
     32 // Used by the code generated from tools/proto_text/gen_proto_text_lib.cc.
     33 class ProtoTextOutput {
     34  public:
     35   // Construct a ProtoTextOutput that writes to <output> If short_debug is true,
     36   // outputs text to match proto.ShortDebugString(); else matches
     37   // proto.DebugString().
     38   ProtoTextOutput(string* output, bool short_debug)
     39       : output_(output),
     40         short_debug_(short_debug),
     41         field_separator_(short_debug ? " " : "\n") {}
     42 
     43   // Writes opening of nested message and increases indent level.
     44   void OpenNestedMessage(const char field_name[]) {
     45     StrAppend(output_, level_empty_ ? "" : field_separator_, indent_,
     46               field_name, " {", field_separator_);
     47     if (!short_debug_) StrAppend(&indent_, "  ");
     48     level_empty_ = true;
     49   }
     50 
     51   // Writes close of nested message and decreases indent level.
     52   void CloseNestedMessage() {
     53     if (!short_debug_) indent_.resize(indent_.size() - 2);
     54     StrAppend(output_, level_empty_ ? "" : field_separator_, indent_, "}");
     55     level_empty_ = false;
     56   }
     57 
     58   // Print the close of the top-level message that was printed.
     59   void CloseTopMessage() {
     60     if (!short_debug_ && !level_empty_) StrAppend(output_, "\n");
     61   }
     62 
     63   // Appends a numeric value, like my_field: 123
     64   template <typename T>
     65   void AppendNumeric(const char field_name[], T value) {
     66     AppendFieldAndValue(field_name, StrCat(value));
     67   }
     68 
     69   // Appends a numeric value, like my_field: 123, but only if value != 0.
     70   template <typename T>
     71   void AppendNumericIfNotZero(const char field_name[], T value) {
     72     if (value != 0) AppendNumeric(field_name, value);
     73   }
     74 
     75   // Appends a bool value, either my_field: true or my_field: false.
     76   void AppendBool(const char field_name[], bool value) {
     77     AppendFieldAndValue(field_name, value ? "true" : "false");
     78   }
     79 
     80   // Appends a bool value, as my_field: true, only if value is true.
     81   void AppendBoolIfTrue(const char field_name[], bool value) {
     82     if (value) AppendBool(field_name, value);
     83   }
     84 
     85   // Appends a string value, like my_field: "abc123".
     86   void AppendString(const char field_name[], const string& value) {
     87     AppendFieldAndValue(
     88         field_name, StrCat("\"", ::tensorflow::str_util::CEscape(value), "\""));
     89   }
     90 
     91   // Appends a string value, like my_field: "abc123", but only if value is not
     92   // empty.
     93   void AppendStringIfNotEmpty(const char field_name[], const string& value) {
     94     if (!value.empty()) AppendString(field_name, value);
     95   }
     96 
     97   // Appends the string name of an enum, like my_field: FIRST_ENUM.
     98   void AppendEnumName(const char field_name[], const string& name) {
     99     AppendFieldAndValue(field_name, name);
    100   }
    101 
    102  private:
    103   void AppendFieldAndValue(const char field_name[], StringPiece value_text) {
    104     StrAppend(output_, level_empty_ ? "" : field_separator_, indent_,
    105               field_name, kColonSeparator, value_text);
    106     level_empty_ = false;
    107   }
    108 
    109   string* const output_;
    110   const bool short_debug_;
    111   const string field_separator_;
    112   string indent_;
    113 
    114   // False when at least one field has been output for the message at the
    115   // current deepest level of nesting.
    116   bool level_empty_ = true;
    117 
    118   TF_DISALLOW_COPY_AND_ASSIGN(ProtoTextOutput);
    119 };
    120 
    121 inline void ProtoSpaceAndComments(Scanner* scanner) {
    122   for (;;) {
    123     scanner->AnySpace();
    124     if (scanner->Peek() != '#') return;
    125     // Skip until newline.
    126     while (scanner->Peek('\n') != '\n') scanner->One(Scanner::ALL);
    127   }
    128 }
    129 
    130 // Parse the next numeric value from <scanner>, returning false if parsing
    131 // failed.
    132 template <typename T>
    133 bool ProtoParseNumericFromScanner(Scanner* scanner, T* value) {
    134   StringPiece numeric_str;
    135   scanner->RestartCapture();
    136   if (!scanner->Many(Scanner::LETTER_DIGIT_DOT_PLUS_MINUS)
    137            .GetResult(nullptr, &numeric_str)) {
    138     return false;
    139   }
    140 
    141   // Special case to disallow multiple leading zeroes, to match proto parsing.
    142   int leading_zero = 0;
    143   for (size_t i = 0; i < numeric_str.size(); ++i) {
    144     const char ch = numeric_str[i];
    145     if (ch == '0') {
    146       if (++leading_zero > 1) return false;
    147     } else if (ch != '-') {
    148       break;
    149     }
    150   }
    151 
    152   ProtoSpaceAndComments(scanner);
    153   return SafeStringToNumeric<T>(numeric_str, value);
    154 }
    155 
    156 // Parse the next boolean value from <scanner>, returning false if parsing
    157 // failed.
    158 bool ProtoParseBoolFromScanner(Scanner* scanner, bool* value);
    159 
    160 // Parse the next string literal from <scanner>, returning false if parsing
    161 // failed.
    162 bool ProtoParseStringLiteralFromScanner(Scanner* scanner, string* value);
    163 
    164 }  // namespace strings
    165 }  // namespace tensorflow
    166 
    167 #endif  // TENSORFLOW_CORE_LIB_STRINGS_PROTO_TEXT_UTIL_H_
    168