xref: /aosp_15_r20/art/cmdline/token_range.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #ifndef ART_CMDLINE_TOKEN_RANGE_H_
18*795d594fSAndroid Build Coastguard Worker #define ART_CMDLINE_TOKEN_RANGE_H_
19*795d594fSAndroid Build Coastguard Worker 
20*795d594fSAndroid Build Coastguard Worker #include <assert.h>
21*795d594fSAndroid Build Coastguard Worker #include <algorithm>
22*795d594fSAndroid Build Coastguard Worker #include <memory>
23*795d594fSAndroid Build Coastguard Worker #include <string>
24*795d594fSAndroid Build Coastguard Worker #include <vector>
25*795d594fSAndroid Build Coastguard Worker 
26*795d594fSAndroid Build Coastguard Worker #include "android-base/strings.h"
27*795d594fSAndroid Build Coastguard Worker 
28*795d594fSAndroid Build Coastguard Worker namespace art {
29*795d594fSAndroid Build Coastguard Worker // A range of tokens to make token matching algorithms easier.
30*795d594fSAndroid Build Coastguard Worker //
31*795d594fSAndroid Build Coastguard Worker // We try really hard to avoid copying and store only a pointer and iterators to the
32*795d594fSAndroid Build Coastguard Worker // interiors of the vector, so a typical copy constructor never ends up doing a deep copy.
33*795d594fSAndroid Build Coastguard Worker // It is up to the user to play nice and not to mutate the strings in-place.
34*795d594fSAndroid Build Coastguard Worker //
35*795d594fSAndroid Build Coastguard Worker // Tokens are only copied if a mutating operation is performed (and even then only
36*795d594fSAndroid Build Coastguard Worker // if it *actually* mutates the token).
37*795d594fSAndroid Build Coastguard Worker struct TokenRange {
38*795d594fSAndroid Build Coastguard Worker   // Short-hand for a vector of strings. A single string and a token is synonymous.
39*795d594fSAndroid Build Coastguard Worker   using TokenList = std::vector<std::string>;
40*795d594fSAndroid Build Coastguard Worker 
41*795d594fSAndroid Build Coastguard Worker   // Copying-from-vector constructor.
TokenRangeTokenRange42*795d594fSAndroid Build Coastguard Worker   explicit TokenRange(const TokenList& token_list)
43*795d594fSAndroid Build Coastguard Worker     : token_list_(new TokenList(token_list)),
44*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
45*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end())
46*795d594fSAndroid Build Coastguard Worker   {}
47*795d594fSAndroid Build Coastguard Worker 
48*795d594fSAndroid Build Coastguard Worker   // Copying-from-iterator constructor
49*795d594fSAndroid Build Coastguard Worker   template <typename ForwardIterator>
TokenRangeTokenRange50*795d594fSAndroid Build Coastguard Worker   TokenRange(ForwardIterator it_begin, ForwardIterator it_end)
51*795d594fSAndroid Build Coastguard Worker     : token_list_(new TokenList(it_begin, it_end)),
52*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
53*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end())
54*795d594fSAndroid Build Coastguard Worker   {}
55*795d594fSAndroid Build Coastguard Worker 
56*795d594fSAndroid Build Coastguard Worker #if 0
57*795d594fSAndroid Build Coastguard Worker   // Copying-from-vector constructor.
58*795d594fSAndroid Build Coastguard Worker   TokenRange([[maybe_unused]] const TokenList& token_list,
59*795d594fSAndroid Build Coastguard Worker              TokenList::const_iterator it_begin,
60*795d594fSAndroid Build Coastguard Worker              TokenList::const_iterator it_end)
61*795d594fSAndroid Build Coastguard Worker     : token_list_(new TokenList(it_begin, it_end)),
62*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
63*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end()) {
64*795d594fSAndroid Build Coastguard Worker     assert(it_begin >= token_list.begin());
65*795d594fSAndroid Build Coastguard Worker     assert(it_end <= token_list.end());
66*795d594fSAndroid Build Coastguard Worker   }
67*795d594fSAndroid Build Coastguard Worker #endif
68*795d594fSAndroid Build Coastguard Worker 
69*795d594fSAndroid Build Coastguard Worker   // Copying from char array constructor, convertings into tokens (strings) along the way.
TokenRangeTokenRange70*795d594fSAndroid Build Coastguard Worker   TokenRange(const char* token_list[], size_t length)
71*795d594fSAndroid Build Coastguard Worker     : token_list_(new TokenList(&token_list[0], &token_list[length])),
72*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
73*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end())
74*795d594fSAndroid Build Coastguard Worker   {}
75*795d594fSAndroid Build Coastguard Worker 
76*795d594fSAndroid Build Coastguard Worker   // Non-copying move-from-vector constructor. Takes over the token vector.
TokenRangeTokenRange77*795d594fSAndroid Build Coastguard Worker   explicit TokenRange(TokenList&& token_list)
78*795d594fSAndroid Build Coastguard Worker     : token_list_(new TokenList(std::forward<TokenList>(token_list))),
79*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
80*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end())
81*795d594fSAndroid Build Coastguard Worker   {}
82*795d594fSAndroid Build Coastguard Worker 
83*795d594fSAndroid Build Coastguard Worker   // Non-copying constructor. Retain reference to existing list of tokens.
TokenRangeTokenRange84*795d594fSAndroid Build Coastguard Worker   TokenRange(const std::shared_ptr<TokenList>& token_list,
85*795d594fSAndroid Build Coastguard Worker              TokenList::const_iterator it_begin,
86*795d594fSAndroid Build Coastguard Worker              TokenList::const_iterator it_end)
87*795d594fSAndroid Build Coastguard Worker     : token_list_(token_list),
88*795d594fSAndroid Build Coastguard Worker       begin_(it_begin),
89*795d594fSAndroid Build Coastguard Worker       end_(it_end) {
90*795d594fSAndroid Build Coastguard Worker     assert(it_begin >= token_list->begin());
91*795d594fSAndroid Build Coastguard Worker     assert(it_end <= token_list->end());
92*795d594fSAndroid Build Coastguard Worker   }
93*795d594fSAndroid Build Coastguard Worker 
94*795d594fSAndroid Build Coastguard Worker   // Non-copying copy constructor.
95*795d594fSAndroid Build Coastguard Worker   TokenRange(const TokenRange&) = default;
96*795d594fSAndroid Build Coastguard Worker 
97*795d594fSAndroid Build Coastguard Worker   // Non-copying move constructor.
98*795d594fSAndroid Build Coastguard Worker   TokenRange(TokenRange&&) = default;
99*795d594fSAndroid Build Coastguard Worker 
100*795d594fSAndroid Build Coastguard Worker   // Non-copying constructor. Retains reference to an existing list of tokens, with offset.
TokenRangeTokenRange101*795d594fSAndroid Build Coastguard Worker   explicit TokenRange(const std::shared_ptr<TokenList>& token_list)
102*795d594fSAndroid Build Coastguard Worker     : token_list_(token_list),
103*795d594fSAndroid Build Coastguard Worker       begin_(token_list_->begin()),
104*795d594fSAndroid Build Coastguard Worker       end_(token_list_->end())
105*795d594fSAndroid Build Coastguard Worker   {}
106*795d594fSAndroid Build Coastguard Worker 
107*795d594fSAndroid Build Coastguard Worker   // Iterator type for begin() and end(). Guaranteed to be a RandomAccessIterator.
108*795d594fSAndroid Build Coastguard Worker   using iterator = TokenList::const_iterator;
109*795d594fSAndroid Build Coastguard Worker 
110*795d594fSAndroid Build Coastguard Worker   // Iterator type for const begin() and const end(). Guaranteed to be a RandomAccessIterator.
111*795d594fSAndroid Build Coastguard Worker   using const_iterator = iterator;
112*795d594fSAndroid Build Coastguard Worker 
113*795d594fSAndroid Build Coastguard Worker   // Create a token range by splitting a string. Each separator gets their own token.
114*795d594fSAndroid Build Coastguard Worker   // Since the separator are retained as tokens, it might be useful to call
115*795d594fSAndroid Build Coastguard Worker   // RemoveToken afterwards.
SplitTokenRange116*795d594fSAndroid Build Coastguard Worker   static TokenRange Split(const std::string& string, std::initializer_list<char> separators) {
117*795d594fSAndroid Build Coastguard Worker     TokenList new_token_list;
118*795d594fSAndroid Build Coastguard Worker 
119*795d594fSAndroid Build Coastguard Worker     std::string tok;
120*795d594fSAndroid Build Coastguard Worker     for (auto&& c : string) {
121*795d594fSAndroid Build Coastguard Worker       for (char sep : separators) {
122*795d594fSAndroid Build Coastguard Worker         if (c == sep) {
123*795d594fSAndroid Build Coastguard Worker           // We spotted a separator character.
124*795d594fSAndroid Build Coastguard Worker           // Push back everything before the last separator as a new token.
125*795d594fSAndroid Build Coastguard Worker           // Push back the separator as a token.
126*795d594fSAndroid Build Coastguard Worker           if (!tok.empty()) {
127*795d594fSAndroid Build Coastguard Worker             new_token_list.push_back(tok);
128*795d594fSAndroid Build Coastguard Worker             tok = "";
129*795d594fSAndroid Build Coastguard Worker           }
130*795d594fSAndroid Build Coastguard Worker           new_token_list.push_back(std::string() + sep);
131*795d594fSAndroid Build Coastguard Worker         } else {
132*795d594fSAndroid Build Coastguard Worker           // Build up the token with another character.
133*795d594fSAndroid Build Coastguard Worker           tok += c;
134*795d594fSAndroid Build Coastguard Worker         }
135*795d594fSAndroid Build Coastguard Worker       }
136*795d594fSAndroid Build Coastguard Worker     }
137*795d594fSAndroid Build Coastguard Worker 
138*795d594fSAndroid Build Coastguard Worker     if (!tok.empty()) {
139*795d594fSAndroid Build Coastguard Worker       new_token_list.push_back(tok);
140*795d594fSAndroid Build Coastguard Worker     }
141*795d594fSAndroid Build Coastguard Worker 
142*795d594fSAndroid Build Coastguard Worker     return TokenRange(std::move(new_token_list));
143*795d594fSAndroid Build Coastguard Worker   }
144*795d594fSAndroid Build Coastguard Worker 
145*795d594fSAndroid Build Coastguard Worker   // A RandomAccessIterator to the first element in this range.
beginTokenRange146*795d594fSAndroid Build Coastguard Worker   iterator begin() const {
147*795d594fSAndroid Build Coastguard Worker     return begin_;
148*795d594fSAndroid Build Coastguard Worker   }
149*795d594fSAndroid Build Coastguard Worker 
150*795d594fSAndroid Build Coastguard Worker   // A RandomAccessIterator to one past the last element in this range.
endTokenRange151*795d594fSAndroid Build Coastguard Worker   iterator end() const {
152*795d594fSAndroid Build Coastguard Worker     return end_;
153*795d594fSAndroid Build Coastguard Worker   }
154*795d594fSAndroid Build Coastguard Worker 
155*795d594fSAndroid Build Coastguard Worker   // The size of the range, i.e. how many tokens are in it.
SizeTokenRange156*795d594fSAndroid Build Coastguard Worker   size_t Size() const {
157*795d594fSAndroid Build Coastguard Worker     return std::distance(begin_, end_);
158*795d594fSAndroid Build Coastguard Worker   }
159*795d594fSAndroid Build Coastguard Worker 
160*795d594fSAndroid Build Coastguard Worker   // Are there 0 tokens in this range?
IsEmptyTokenRange161*795d594fSAndroid Build Coastguard Worker   bool IsEmpty() const {
162*795d594fSAndroid Build Coastguard Worker     return Size() > 0;
163*795d594fSAndroid Build Coastguard Worker   }
164*795d594fSAndroid Build Coastguard Worker 
165*795d594fSAndroid Build Coastguard Worker   // Look up a token by it's offset.
GetTokenTokenRange166*795d594fSAndroid Build Coastguard Worker   const std::string& GetToken(size_t offset) const {
167*795d594fSAndroid Build Coastguard Worker     assert(offset < Size());
168*795d594fSAndroid Build Coastguard Worker     return *(begin_ + offset);
169*795d594fSAndroid Build Coastguard Worker   }
170*795d594fSAndroid Build Coastguard Worker 
171*795d594fSAndroid Build Coastguard Worker   // Does this token range equal the other range?
172*795d594fSAndroid Build Coastguard Worker   // Equality is defined as having both the same size, and
173*795d594fSAndroid Build Coastguard Worker   // each corresponding token being equal.
174*795d594fSAndroid Build Coastguard Worker   bool operator==(const TokenRange& other) const {
175*795d594fSAndroid Build Coastguard Worker     if (this == &other) {
176*795d594fSAndroid Build Coastguard Worker       return true;
177*795d594fSAndroid Build Coastguard Worker     }
178*795d594fSAndroid Build Coastguard Worker 
179*795d594fSAndroid Build Coastguard Worker     if (Size() != other.Size()) {
180*795d594fSAndroid Build Coastguard Worker       return false;
181*795d594fSAndroid Build Coastguard Worker     }
182*795d594fSAndroid Build Coastguard Worker 
183*795d594fSAndroid Build Coastguard Worker     return std::equal(begin(), end(), other.begin());
184*795d594fSAndroid Build Coastguard Worker   }
185*795d594fSAndroid Build Coastguard Worker 
186*795d594fSAndroid Build Coastguard Worker   // Look up the token at the requested index.
187*795d594fSAndroid Build Coastguard Worker   const std::string& operator[](int index) const {
188*795d594fSAndroid Build Coastguard Worker     assert(index >= 0 && static_cast<size_t>(index) < Size());
189*795d594fSAndroid Build Coastguard Worker     return *(begin() + index);
190*795d594fSAndroid Build Coastguard Worker   }
191*795d594fSAndroid Build Coastguard Worker 
192*795d594fSAndroid Build Coastguard Worker   // Does this current range start with the other range?
StartsWithTokenRange193*795d594fSAndroid Build Coastguard Worker   bool StartsWith(const TokenRange& other) const {
194*795d594fSAndroid Build Coastguard Worker     if (this == &other) {
195*795d594fSAndroid Build Coastguard Worker       return true;
196*795d594fSAndroid Build Coastguard Worker     }
197*795d594fSAndroid Build Coastguard Worker 
198*795d594fSAndroid Build Coastguard Worker     if (Size() < other.Size()) {
199*795d594fSAndroid Build Coastguard Worker       return false;
200*795d594fSAndroid Build Coastguard Worker     }
201*795d594fSAndroid Build Coastguard Worker 
202*795d594fSAndroid Build Coastguard Worker     auto& smaller = Size() < other.Size() ? *this : other;
203*795d594fSAndroid Build Coastguard Worker     auto& greater = Size() < other.Size() ? other : *this;
204*795d594fSAndroid Build Coastguard Worker 
205*795d594fSAndroid Build Coastguard Worker     return std::equal(smaller.begin(), smaller.end(), greater.begin());
206*795d594fSAndroid Build Coastguard Worker   }
207*795d594fSAndroid Build Coastguard Worker 
208*795d594fSAndroid Build Coastguard Worker   // Remove all characters 'c' from each token, potentially copying the underlying tokens.
RemoveCharacterTokenRange209*795d594fSAndroid Build Coastguard Worker   TokenRange RemoveCharacter(char c) const {
210*795d594fSAndroid Build Coastguard Worker     TokenList new_token_list(begin(), end());
211*795d594fSAndroid Build Coastguard Worker 
212*795d594fSAndroid Build Coastguard Worker     bool changed = false;
213*795d594fSAndroid Build Coastguard Worker     for (auto&& token : new_token_list) {
214*795d594fSAndroid Build Coastguard Worker       auto it = std::remove_if(token.begin(), token.end(), [&](char ch) {
215*795d594fSAndroid Build Coastguard Worker         if (ch == c) {
216*795d594fSAndroid Build Coastguard Worker           changed = true;
217*795d594fSAndroid Build Coastguard Worker           return true;
218*795d594fSAndroid Build Coastguard Worker         }
219*795d594fSAndroid Build Coastguard Worker         return false;
220*795d594fSAndroid Build Coastguard Worker       });
221*795d594fSAndroid Build Coastguard Worker       token.erase(it, token.end());
222*795d594fSAndroid Build Coastguard Worker     }
223*795d594fSAndroid Build Coastguard Worker 
224*795d594fSAndroid Build Coastguard Worker     if (!changed) {
225*795d594fSAndroid Build Coastguard Worker       return *this;
226*795d594fSAndroid Build Coastguard Worker     }
227*795d594fSAndroid Build Coastguard Worker 
228*795d594fSAndroid Build Coastguard Worker     return TokenRange(std::move(new_token_list));
229*795d594fSAndroid Build Coastguard Worker   }
230*795d594fSAndroid Build Coastguard Worker 
231*795d594fSAndroid Build Coastguard Worker   // Remove all tokens matching this one, potentially copying the underlying tokens.
RemoveTokenTokenRange232*795d594fSAndroid Build Coastguard Worker   TokenRange RemoveToken(const std::string& token) {
233*795d594fSAndroid Build Coastguard Worker     return RemoveIf([&](const std::string& tok) { return tok == token; });
234*795d594fSAndroid Build Coastguard Worker   }
235*795d594fSAndroid Build Coastguard Worker 
236*795d594fSAndroid Build Coastguard Worker   // Discard all empty tokens, potentially copying the underlying tokens.
DiscardEmptyTokenRange237*795d594fSAndroid Build Coastguard Worker   TokenRange DiscardEmpty() const {
238*795d594fSAndroid Build Coastguard Worker     return RemoveIf([](const std::string& token) { return token.empty(); });
239*795d594fSAndroid Build Coastguard Worker   }
240*795d594fSAndroid Build Coastguard Worker 
241*795d594fSAndroid Build Coastguard Worker   // Create a non-copying subset of this range.
242*795d594fSAndroid Build Coastguard Worker   // Length is trimmed so that the Slice does not go out of range.
243*795d594fSAndroid Build Coastguard Worker   TokenRange Slice(size_t offset, size_t length = std::string::npos) const {
244*795d594fSAndroid Build Coastguard Worker     assert(offset < Size());
245*795d594fSAndroid Build Coastguard Worker 
246*795d594fSAndroid Build Coastguard Worker     if (length != std::string::npos && offset + length > Size()) {
247*795d594fSAndroid Build Coastguard Worker       length = Size() - offset;
248*795d594fSAndroid Build Coastguard Worker     }
249*795d594fSAndroid Build Coastguard Worker 
250*795d594fSAndroid Build Coastguard Worker     iterator it_end;
251*795d594fSAndroid Build Coastguard Worker     if (length == std::string::npos) {
252*795d594fSAndroid Build Coastguard Worker       it_end = end();
253*795d594fSAndroid Build Coastguard Worker     } else {
254*795d594fSAndroid Build Coastguard Worker       it_end = begin() + offset + length;
255*795d594fSAndroid Build Coastguard Worker     }
256*795d594fSAndroid Build Coastguard Worker 
257*795d594fSAndroid Build Coastguard Worker     return TokenRange(token_list_, begin() + offset, it_end);
258*795d594fSAndroid Build Coastguard Worker   }
259*795d594fSAndroid Build Coastguard Worker 
260*795d594fSAndroid Build Coastguard Worker   // Try to match the string with tokens from this range.
261*795d594fSAndroid Build Coastguard Worker   // Each token is used to match exactly once (after which the next token is used, and so on).
262*795d594fSAndroid Build Coastguard Worker   // The matching happens from left-to-right in a non-greedy fashion.
263*795d594fSAndroid Build Coastguard Worker   // If the currently-matched token is the wildcard, then the new outputted token will
264*795d594fSAndroid Build Coastguard Worker   // contain as much as possible until the next token is matched.
265*795d594fSAndroid Build Coastguard Worker   //
266*795d594fSAndroid Build Coastguard Worker   // For example, if this == ["a:", "_", "b:] and "_" is the match string, then
267*795d594fSAndroid Build Coastguard Worker   // MatchSubstrings on "a:foob:" will yield: ["a:", "foo", "b:"]
268*795d594fSAndroid Build Coastguard Worker   //
269*795d594fSAndroid Build Coastguard Worker   // Since the string matching can fail (e.g. ["foo"] against "bar"), then this
270*795d594fSAndroid Build Coastguard Worker   // function can fail, in which cause it will return null.
MatchSubstringsTokenRange271*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<TokenRange> MatchSubstrings(const std::string& string,
272*795d594fSAndroid Build Coastguard Worker                                               const std::string& wildcard) const {
273*795d594fSAndroid Build Coastguard Worker     TokenList new_token_list;
274*795d594fSAndroid Build Coastguard Worker 
275*795d594fSAndroid Build Coastguard Worker     size_t wildcard_idx = std::string::npos;
276*795d594fSAndroid Build Coastguard Worker     size_t string_idx = 0;
277*795d594fSAndroid Build Coastguard Worker 
278*795d594fSAndroid Build Coastguard Worker     // Function to push all the characters matched as a wildcard so far
279*795d594fSAndroid Build Coastguard Worker     // as a brand new token. It resets the wildcard matching.
280*795d594fSAndroid Build Coastguard Worker     // Empty wildcards are possible and ok, but only if wildcard matching was on.
281*795d594fSAndroid Build Coastguard Worker     auto maybe_push_wildcard_token = [&]() {
282*795d594fSAndroid Build Coastguard Worker       if (wildcard_idx != std::string::npos) {
283*795d594fSAndroid Build Coastguard Worker         size_t wildcard_length = string_idx - wildcard_idx;
284*795d594fSAndroid Build Coastguard Worker         std::string wildcard_substr = string.substr(wildcard_idx, wildcard_length);
285*795d594fSAndroid Build Coastguard Worker         new_token_list.push_back(std::move(wildcard_substr));
286*795d594fSAndroid Build Coastguard Worker 
287*795d594fSAndroid Build Coastguard Worker         wildcard_idx = std::string::npos;
288*795d594fSAndroid Build Coastguard Worker       }
289*795d594fSAndroid Build Coastguard Worker     };
290*795d594fSAndroid Build Coastguard Worker 
291*795d594fSAndroid Build Coastguard Worker     for (iterator it = begin(); it != end(); ++it) {
292*795d594fSAndroid Build Coastguard Worker       const std::string& tok = *it;
293*795d594fSAndroid Build Coastguard Worker 
294*795d594fSAndroid Build Coastguard Worker       if (tok == wildcard) {
295*795d594fSAndroid Build Coastguard Worker         maybe_push_wildcard_token();
296*795d594fSAndroid Build Coastguard Worker         wildcard_idx = string_idx;
297*795d594fSAndroid Build Coastguard Worker         continue;
298*795d594fSAndroid Build Coastguard Worker       }
299*795d594fSAndroid Build Coastguard Worker 
300*795d594fSAndroid Build Coastguard Worker       size_t next_token_idx = string.find(tok);
301*795d594fSAndroid Build Coastguard Worker       if (next_token_idx == std::string::npos) {
302*795d594fSAndroid Build Coastguard Worker         // Could not find token at all
303*795d594fSAndroid Build Coastguard Worker         return nullptr;
304*795d594fSAndroid Build Coastguard Worker       } else if (next_token_idx != string_idx && wildcard_idx == std::string::npos) {
305*795d594fSAndroid Build Coastguard Worker         // Found the token at a non-starting location, and we weren't
306*795d594fSAndroid Build Coastguard Worker         // trying to parse the wildcard.
307*795d594fSAndroid Build Coastguard Worker         return nullptr;
308*795d594fSAndroid Build Coastguard Worker       }
309*795d594fSAndroid Build Coastguard Worker 
310*795d594fSAndroid Build Coastguard Worker       new_token_list.push_back(string.substr(next_token_idx, tok.size()));
311*795d594fSAndroid Build Coastguard Worker       maybe_push_wildcard_token();
312*795d594fSAndroid Build Coastguard Worker       string_idx += tok.size();
313*795d594fSAndroid Build Coastguard Worker     }
314*795d594fSAndroid Build Coastguard Worker 
315*795d594fSAndroid Build Coastguard Worker     size_t remaining = string.size() - string_idx;
316*795d594fSAndroid Build Coastguard Worker     if (remaining > 0) {
317*795d594fSAndroid Build Coastguard Worker       if (wildcard_idx == std::string::npos) {
318*795d594fSAndroid Build Coastguard Worker         // Some characters were still remaining in the string,
319*795d594fSAndroid Build Coastguard Worker         // but it wasn't trying to match a wildcard.
320*795d594fSAndroid Build Coastguard Worker         return nullptr;
321*795d594fSAndroid Build Coastguard Worker       }
322*795d594fSAndroid Build Coastguard Worker     }
323*795d594fSAndroid Build Coastguard Worker 
324*795d594fSAndroid Build Coastguard Worker     // If some characters are remaining, the rest must be a wildcard.
325*795d594fSAndroid Build Coastguard Worker     string_idx += remaining;
326*795d594fSAndroid Build Coastguard Worker     maybe_push_wildcard_token();
327*795d594fSAndroid Build Coastguard Worker 
328*795d594fSAndroid Build Coastguard Worker     return std::make_unique<TokenRange>(std::move(new_token_list));
329*795d594fSAndroid Build Coastguard Worker   }
330*795d594fSAndroid Build Coastguard Worker 
331*795d594fSAndroid Build Coastguard Worker   // Do a quick match token-by-token, and see if they match.
332*795d594fSAndroid Build Coastguard Worker   // Any tokens with a wildcard in them are only matched up until the wildcard.
333*795d594fSAndroid Build Coastguard Worker   // If this is true, then the wildcard matching later on can still fail, so this is not
334*795d594fSAndroid Build Coastguard Worker   // a guarantee that the argument is correct, it's more of a strong hint that the
335*795d594fSAndroid Build Coastguard Worker   // user-provided input *probably* was trying to match this argument.
336*795d594fSAndroid Build Coastguard Worker   //
337*795d594fSAndroid Build Coastguard Worker   // Returns how many tokens were either matched (or ignored because there was a
338*795d594fSAndroid Build Coastguard Worker   // wildcard present). 0 means no match. If the size() tokens are returned.
MaybeMatchesTokenRange339*795d594fSAndroid Build Coastguard Worker   size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
340*795d594fSAndroid Build Coastguard Worker     auto token_it = token_list.begin();
341*795d594fSAndroid Build Coastguard Worker     auto token_end = token_list.end();
342*795d594fSAndroid Build Coastguard Worker     auto name_it = begin();
343*795d594fSAndroid Build Coastguard Worker     auto name_end = end();
344*795d594fSAndroid Build Coastguard Worker 
345*795d594fSAndroid Build Coastguard Worker     size_t matched_tokens = 0;
346*795d594fSAndroid Build Coastguard Worker 
347*795d594fSAndroid Build Coastguard Worker     while (token_it != token_end && name_it != name_end) {
348*795d594fSAndroid Build Coastguard Worker       // Skip token matching when the corresponding name has a wildcard in it.
349*795d594fSAndroid Build Coastguard Worker       const std::string& name = *name_it;
350*795d594fSAndroid Build Coastguard Worker 
351*795d594fSAndroid Build Coastguard Worker       size_t wildcard_idx = name.find(wildcard);
352*795d594fSAndroid Build Coastguard Worker       if (wildcard_idx == std::string::npos) {  // No wildcard present
353*795d594fSAndroid Build Coastguard Worker         // Did the definition token match the user token?
354*795d594fSAndroid Build Coastguard Worker         if (name != *token_it) {
355*795d594fSAndroid Build Coastguard Worker           return matched_tokens;
356*795d594fSAndroid Build Coastguard Worker         }
357*795d594fSAndroid Build Coastguard Worker       } else {
358*795d594fSAndroid Build Coastguard Worker         std::string name_prefix = name.substr(0, wildcard_idx);
359*795d594fSAndroid Build Coastguard Worker 
360*795d594fSAndroid Build Coastguard Worker         // Did the user token start with the up-to-the-wildcard prefix?
361*795d594fSAndroid Build Coastguard Worker         if (!StartsWith(*token_it, name_prefix)) {
362*795d594fSAndroid Build Coastguard Worker           return matched_tokens;
363*795d594fSAndroid Build Coastguard Worker         }
364*795d594fSAndroid Build Coastguard Worker       }
365*795d594fSAndroid Build Coastguard Worker 
366*795d594fSAndroid Build Coastguard Worker       ++token_it;
367*795d594fSAndroid Build Coastguard Worker       ++name_it;
368*795d594fSAndroid Build Coastguard Worker       ++matched_tokens;
369*795d594fSAndroid Build Coastguard Worker     }
370*795d594fSAndroid Build Coastguard Worker 
371*795d594fSAndroid Build Coastguard Worker     // If we got this far, it's either a full match or the token list was too short.
372*795d594fSAndroid Build Coastguard Worker     return matched_tokens;
373*795d594fSAndroid Build Coastguard Worker   }
374*795d594fSAndroid Build Coastguard Worker 
375*795d594fSAndroid Build Coastguard Worker   // Flatten the token range by joining every adjacent token with the separator character.
376*795d594fSAndroid Build Coastguard Worker   // e.g. ["hello", "world"].join('$') == "hello$world"
JoinTokenRange377*795d594fSAndroid Build Coastguard Worker   std::string Join(char separator) const {
378*795d594fSAndroid Build Coastguard Worker     TokenList tmp(begin(), end());
379*795d594fSAndroid Build Coastguard Worker     return android::base::Join(tmp, separator);
380*795d594fSAndroid Build Coastguard Worker     // TODO: Join should probably take an offset or iterators
381*795d594fSAndroid Build Coastguard Worker   }
382*795d594fSAndroid Build Coastguard Worker 
383*795d594fSAndroid Build Coastguard Worker  private:
StartsWithTokenRange384*795d594fSAndroid Build Coastguard Worker   static bool StartsWith(const std::string& larger, const std::string& smaller) {
385*795d594fSAndroid Build Coastguard Worker     if (larger.size() >= smaller.size()) {
386*795d594fSAndroid Build Coastguard Worker       return std::equal(smaller.begin(), smaller.end(), larger.begin());
387*795d594fSAndroid Build Coastguard Worker     }
388*795d594fSAndroid Build Coastguard Worker 
389*795d594fSAndroid Build Coastguard Worker     return false;
390*795d594fSAndroid Build Coastguard Worker   }
391*795d594fSAndroid Build Coastguard Worker 
392*795d594fSAndroid Build Coastguard Worker   template <typename TPredicate>
RemoveIfTokenRange393*795d594fSAndroid Build Coastguard Worker   TokenRange RemoveIf(const TPredicate& predicate) const {
394*795d594fSAndroid Build Coastguard Worker     // If any of the tokens in the token lists are empty, then
395*795d594fSAndroid Build Coastguard Worker     // we need to remove them and compress the token list into a smaller one.
396*795d594fSAndroid Build Coastguard Worker     bool remove = false;
397*795d594fSAndroid Build Coastguard Worker     for (auto it = begin_; it != end_; ++it) {
398*795d594fSAndroid Build Coastguard Worker       auto&& token = *it;
399*795d594fSAndroid Build Coastguard Worker 
400*795d594fSAndroid Build Coastguard Worker       if (predicate(token)) {
401*795d594fSAndroid Build Coastguard Worker         remove = true;
402*795d594fSAndroid Build Coastguard Worker         break;
403*795d594fSAndroid Build Coastguard Worker       }
404*795d594fSAndroid Build Coastguard Worker     }
405*795d594fSAndroid Build Coastguard Worker 
406*795d594fSAndroid Build Coastguard Worker     // Actually copy the token list and remove the tokens that don't match our predicate.
407*795d594fSAndroid Build Coastguard Worker     if (remove) {
408*795d594fSAndroid Build Coastguard Worker       auto token_list = std::make_shared<TokenList>(begin(), end());
409*795d594fSAndroid Build Coastguard Worker       TokenList::iterator new_end =
410*795d594fSAndroid Build Coastguard Worker           std::remove_if(token_list->begin(), token_list->end(), predicate);
411*795d594fSAndroid Build Coastguard Worker       token_list->erase(new_end, token_list->end());
412*795d594fSAndroid Build Coastguard Worker 
413*795d594fSAndroid Build Coastguard Worker       assert(token_list_->size() > token_list->size() && "Nothing was actually removed!");
414*795d594fSAndroid Build Coastguard Worker 
415*795d594fSAndroid Build Coastguard Worker       return TokenRange(token_list);
416*795d594fSAndroid Build Coastguard Worker     }
417*795d594fSAndroid Build Coastguard Worker 
418*795d594fSAndroid Build Coastguard Worker     return *this;
419*795d594fSAndroid Build Coastguard Worker   }
420*795d594fSAndroid Build Coastguard Worker 
421*795d594fSAndroid Build Coastguard Worker   const std::shared_ptr<std::vector<std::string>> token_list_;
422*795d594fSAndroid Build Coastguard Worker   const iterator begin_;
423*795d594fSAndroid Build Coastguard Worker   const iterator end_;
424*795d594fSAndroid Build Coastguard Worker };
425*795d594fSAndroid Build Coastguard Worker }  // namespace art
426*795d594fSAndroid Build Coastguard Worker 
427*795d594fSAndroid Build Coastguard Worker #endif  // ART_CMDLINE_TOKEN_RANGE_H_
428