diff --git a/src/eval_env.cc b/src/eval_env.cc index 796a3264d1..cbc935acc2 100644 --- a/src/eval_env.cc +++ b/src/eval_env.cc @@ -99,6 +99,10 @@ string BindingEnv::LookupWithFallback(const string& var, } string EvalString::Evaluate(Env* env) const { + if (parsed_.empty()) { + return single_token_; + } + string result; for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) { if (i->second == RAW) @@ -110,40 +114,57 @@ string EvalString::Evaluate(Env* env) const { } void EvalString::AddText(StringPiece text) { - // Add it to the end of an existing RAW token if possible. - if (!parsed_.empty() && parsed_.back().second == RAW) { - parsed_.back().first.append(text.str_, text.len_); + if (parsed_.empty()) { + single_token_.append(text.begin(), text.end()); + } else if (!parsed_.empty() && parsed_.back().second == RAW) { + parsed_.back().first.append(text.begin(), text.end()); } else { - parsed_.push_back(make_pair(text.AsString(), RAW)); + parsed_.push_back(std::make_pair(text.AsString(), RAW)); } } + void EvalString::AddSpecial(StringPiece text) { - parsed_.push_back(make_pair(text.AsString(), SPECIAL)); + if (parsed_.empty() && !single_token_.empty()) { + // Going from one to two tokens, so we can no longer apply + // our single_token_ optimization and need to push everything + // onto the vector. + parsed_.push_back(std::make_pair(std::move(single_token_), RAW)); + } + parsed_.push_back(std::make_pair(text.AsString(), SPECIAL)); } string EvalString::Serialize() const { string result; - for (TokenList::const_iterator i = parsed_.begin(); - i != parsed_.end(); ++i) { + if (parsed_.empty() && !single_token_.empty()) { result.append("["); - if (i->second == SPECIAL) - result.append("$"); - result.append(i->first); + result.append(single_token_); result.append("]"); + } else { + for (const auto& pair : parsed_) { + result.append("["); + if (pair.second == SPECIAL) + result.append("$"); + result.append(pair.first.begin(), pair.first.end()); + result.append("]"); + } } return result; } string EvalString::Unparse() const { string result; - for (TokenList::const_iterator i = parsed_.begin(); - i != parsed_.end(); ++i) { - bool special = (i->second == SPECIAL); - if (special) - result.append("${"); - result.append(i->first); - if (special) - result.append("}"); + if (parsed_.empty() && !single_token_.empty()) { + result.append(single_token_.begin(), single_token_.end()); + } else { + for (TokenList::const_iterator i = parsed_.begin(); + i != parsed_.end(); ++i) { + bool special = (i->second == SPECIAL); + if (special) + result.append("${"); + result.append(i->first.begin(), i->first.end()); + if (special) + result.append("}"); + } } return result; } diff --git a/src/eval_env.h b/src/eval_env.h index 677dc217a2..ae6d8bc898 100644 --- a/src/eval_env.h +++ b/src/eval_env.h @@ -39,8 +39,8 @@ struct EvalString { /// @return The string with variables not expanded. std::string Unparse() const; - void Clear() { parsed_.clear(); } - bool empty() const { return parsed_.empty(); } + void Clear() { parsed_.clear(); single_token_.clear(); } + bool empty() const { return parsed_.empty() && single_token_.empty(); } void AddText(StringPiece text); void AddSpecial(StringPiece text); @@ -53,6 +53,12 @@ struct EvalString { enum TokenType { RAW, SPECIAL }; typedef std::vector > TokenList; TokenList parsed_; + + // If we hold only a single RAW token, then we keep it here instead of + // pushing it on TokenList. This saves a bunch of allocations for + // what is a common case. If parsed_ is nonempty, then this value + // must be ignored. + std::string single_token_; }; /// An invocable build command and associated metadata (description, etc.). diff --git a/src/string_piece.h b/src/string_piece.h index 1c0bee6e1c..7e7367c20a 100644 --- a/src/string_piece.h +++ b/src/string_piece.h @@ -63,6 +63,10 @@ struct StringPiece { return len_; } + size_t empty() const { + return len_ == 0; + } + const char* str_; size_t len_; };