blob: 4dca5c853eb05e14fe0188941ef522d79e4f7a98 [file] [log] [blame]
/*
* 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<Node*>(&(*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<Node*>(&(*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<ssize_t>(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<std::string, size_t>& 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