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