/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "src/traced/probes/filesystem/prefix_finder.h" #include "perfetto/base/logging.h" #include "perfetto/ext/base/string_splitter.h" namespace perfetto { std::string PrefixFinder::Node::ToString() const { if (parent_ != nullptr) return parent_->ToString() + "/" + name_; return name_; } PrefixFinder::Node* PrefixFinder::Node::AddChild(std::string name) { auto it = children_.emplace(std::move(name), this); return const_cast(&(*it.first)); } PrefixFinder::Node* PrefixFinder::Node::MaybeChild(const std::string& name) { // This will be nicer with C++14 transparent comparators. // Then we will be able to look up by just the key using a sutiable // comparator. // // For now we need to allow to construct Node from the key. Node node(name); auto it = children_.find(node); if (it == children_.end()) return nullptr; return const_cast(&(*it)); } PrefixFinder::PrefixFinder(size_t limit) : limit_(limit) {} void PrefixFinder::InsertPrefix(size_t len) { Node* cur = &root_; for (auto it = state_.cbegin() + 1; it != state_.cbegin() + static_cast(len + 1); it++) { Node* next = cur->MaybeChild(it->first); if (!next) next = cur->AddChild(it->first); cur = next; } } void PrefixFinder::Flush(size_t i) { PERFETTO_CHECK(i > 0); for (size_t j = i; j < state_.size(); ++j) { if (state_[j - 1].second > limit_ && state_[j].second <= limit_) { InsertPrefix(i); break; } } } void PrefixFinder::Finalize() { Flush(1); state_.resize(1); #if PERFETTO_DCHECK_IS_ON() PERFETTO_DCHECK(!finalized_); finalized_ = true; #endif } void PrefixFinder::AddPath(std::string path) { perfetto::base::StringSplitter s(std::move(path), '/'); // An artificial element for the root directory. // This simplifies the logic below because we can always assume // there is a parent element. state_[0].second++; for (size_t i = 1; s.Next(); ++i) { char* token = s.cur_token(); if (i < state_.size()) { std::pair& elem = state_[i]; if (elem.first == token) { elem.second++; } else { // Check if we need to write a prefix for any element // in the previous state. Flush(i); elem.first = token; elem.second = 1; state_.resize(i + 1); } } else { state_.emplace_back(token, 1); } } } PrefixFinder::Node* PrefixFinder::GetPrefix(std::string path) { #if PERFETTO_DCHECK_IS_ON() PERFETTO_DCHECK(finalized_); #endif perfetto::base::StringSplitter s(std::move(path), '/'); Node* cur = &root_; for (size_t i = 0; s.Next(); ++i) { char* token = s.cur_token(); Node* next = cur->MaybeChild(token); if (next == nullptr) break; cur = next; PERFETTO_DCHECK(cur->name_ == token); } return cur; } } // namespace perfetto