• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2019 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 #include "tensorflow/lite/toco/args.h"
17 
18 #include <string>
19 
20 #include "absl/strings/str_split.h"
21 
22 namespace toco {
23 namespace {
24 
25 // Helper class for SplitStructuredLine parsing.
26 class ClosingSymbolLookup {
27  public:
ClosingSymbolLookup(const char * symbol_pairs)28   explicit ClosingSymbolLookup(const char* symbol_pairs)
29       : closing_(), valid_closing_() {
30     // Initialize the opening/closing arrays.
31     for (const char* symbol = symbol_pairs; *symbol != 0; ++symbol) {
32       unsigned char opening = *symbol;
33       ++symbol;
34       // If the string ends before the closing character has been found,
35       // use the opening character as the closing character.
36       unsigned char closing = *symbol != 0 ? *symbol : opening;
37       closing_[opening] = closing;
38       valid_closing_[closing] = true;
39       if (*symbol == 0) break;
40     }
41   }
42 
43   ClosingSymbolLookup(const ClosingSymbolLookup&) = delete;
44   ClosingSymbolLookup& operator=(const ClosingSymbolLookup&) = delete;
45 
46   // Returns the closing character corresponding to an opening one,
47   // or 0 if the argument is not an opening character.
GetClosingChar(char opening) const48   char GetClosingChar(char opening) const {
49     return closing_[static_cast<unsigned char>(opening)];
50   }
51 
52   // Returns true if the argument is a closing character.
IsClosing(char c) const53   bool IsClosing(char c) const {
54     return valid_closing_[static_cast<unsigned char>(c)];
55   }
56 
57  private:
58   // Maps an opening character to its closing. If the entry contains 0,
59   // the character is not in the opening set.
60   char closing_[256];
61   // Valid closing characters.
62   bool valid_closing_[256];
63 };
64 
SplitStructuredLine(absl::string_view line,char delimiter,const char * symbol_pairs,std::vector<absl::string_view> * cols)65 bool SplitStructuredLine(absl::string_view line, char delimiter,
66                          const char* symbol_pairs,
67                          std::vector<absl::string_view>* cols) {
68   ClosingSymbolLookup lookup(symbol_pairs);
69 
70   // Stack of symbols expected to close the current opened expressions.
71   std::vector<char> expected_to_close;
72 
73   ABSL_RAW_CHECK(cols != nullptr, "");
74   cols->push_back(line);
75   for (size_t i = 0; i < line.size(); ++i) {
76     char c = line[i];
77     if (expected_to_close.empty() && c == delimiter) {
78       // We don't have any open expression, this is a valid separator.
79       cols->back().remove_suffix(line.size() - i);
80       cols->push_back(line.substr(i + 1));
81     } else if (!expected_to_close.empty() && c == expected_to_close.back()) {
82       // Can we close the currently open expression?
83       expected_to_close.pop_back();
84     } else if (lookup.GetClosingChar(c)) {
85       // If this is an opening symbol, we open a new expression and push
86       // the expected closing symbol on the stack.
87       expected_to_close.push_back(lookup.GetClosingChar(c));
88     } else if (lookup.IsClosing(c)) {
89       // Error: mismatched closing symbol.
90       return false;
91     }
92   }
93   if (!expected_to_close.empty()) {
94     return false;  // Missing closing symbol(s)
95   }
96   return true;  // Success
97 }
98 
TryStripPrefixString(absl::string_view str,absl::string_view prefix,std::string * result)99 inline bool TryStripPrefixString(absl::string_view str,
100                                  absl::string_view prefix,
101                                  std::string* result) {
102   bool res = absl::ConsumePrefix(&str, prefix);
103   result->assign(str.begin(), str.end());
104   return res;
105 }
106 
TryStripSuffixString(absl::string_view str,absl::string_view suffix,std::string * result)107 inline bool TryStripSuffixString(absl::string_view str,
108                                  absl::string_view suffix,
109                                  std::string* result) {
110   bool res = absl::ConsumeSuffix(&str, suffix);
111   result->assign(str.begin(), str.end());
112   return res;
113 }
114 
115 }  // namespace
116 
Parse(std::string text)117 bool Arg<toco::IntList>::Parse(std::string text) {
118   parsed_value_.elements.clear();
119   specified_ = true;
120   // absl::StrSplit("") produces {""}, but we need {} on empty input.
121   // TODO(aselle): Moved this from elsewhere, but ahentz recommends we could
122   // use absl::SplitLeadingDec32Values(text.c_str(), &parsed_values_.elements)
123   if (!text.empty()) {
124     int32_t element;
125     for (absl::string_view part : absl::StrSplit(text, ',')) {
126       if (!absl::SimpleAtoi(part, &element)) return false;
127       parsed_value_.elements.push_back(element);
128     }
129   }
130   return true;
131 }
132 
Parse(std::string text)133 bool Arg<toco::StringMapList>::Parse(std::string text) {
134   parsed_value_.elements.clear();
135   specified_ = true;
136 
137   if (text.empty()) {
138     return true;
139   }
140 
141   std::vector<absl::string_view> outer_vector;
142   absl::string_view text_disposable_copy = text;
143   // TODO(aselle): Change argument parsing when absl supports structuredline.
144   SplitStructuredLine(text_disposable_copy, ',', "{}", &outer_vector);
145   for (const absl::string_view& outer_member_stringpiece : outer_vector) {
146     std::string outer_member(outer_member_stringpiece);
147     if (outer_member.empty()) {
148       continue;
149     }
150     std::string outer_member_copy = outer_member;
151     absl::StripAsciiWhitespace(&outer_member);
152     if (!TryStripPrefixString(outer_member, "{", &outer_member)) return false;
153     if (!TryStripSuffixString(outer_member, "}", &outer_member)) return false;
154     const std::vector<std::string> inner_fields_vector =
155         absl::StrSplit(outer_member, ',');
156 
157     std::unordered_map<std::string, std::string> element;
158     for (const std::string& member_field : inner_fields_vector) {
159       std::vector<std::string> outer_member_key_value =
160           absl::StrSplit(member_field, ':');
161       if (outer_member_key_value.size() != 2) return false;
162       std::string& key = outer_member_key_value[0];
163       std::string& value = outer_member_key_value[1];
164       absl::StripAsciiWhitespace(&key);
165       absl::StripAsciiWhitespace(&value);
166       if (element.count(key) != 0) return false;
167       element[key] = value;
168     }
169     parsed_value_.elements.push_back(element);
170   }
171   return true;
172 }
173 
174 }  // namespace toco
175