blob: fda57f516a394c6b1ef2c2872eb3c11ecb1b6ef9 [file] [log] [blame]
/*
* Copyright (C) 2019 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/trace_processor/string_pool.h"
#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
StringPool::StringPool() {
blocks_.emplace_back();
// Reserve a slot for the null string.
PERFETTO_CHECK(blocks_.back().TryInsert(NullTermStringView()));
}
StringPool::~StringPool() = default;
StringPool::StringPool(StringPool&&) noexcept = default;
StringPool& StringPool::operator=(StringPool&&) = default;
StringPool::Id StringPool::InsertString(base::StringView str, uint64_t hash) {
// We shouldn't be writing string with more than 2^16 characters to the pool.
PERFETTO_CHECK(str.size() < std::numeric_limits<uint16_t>::max());
// Try and find enough space in the current block for the string and the
// metadata (the size of the string + the null terminator + 2 bytes to encode
// the size itself).
auto* ptr = blocks_.back().TryInsert(str);
if (PERFETTO_UNLIKELY(!ptr)) {
// This means the block did not have enough space. This should only happen
// on 32-bit platforms as we allocate a 4GB mmap on 64 bit.
PERFETTO_CHECK(sizeof(uint8_t*) == 4);
// Add a new block to store the data.
blocks_.emplace_back();
// Try and reserve space again - this time we should definitely succeed.
ptr = blocks_.back().TryInsert(str);
PERFETTO_CHECK(ptr);
}
// Finish by computing the id of the pointer and adding a mapping from the
// hash to the string_id.
Id string_id = PtrToId(ptr);
string_index_.emplace(hash, string_id);
return string_id;
}
uint8_t* StringPool::Block::TryInsert(base::StringView str) {
auto str_size = str.size();
auto size = str_size + kMetadataSize;
if (static_cast<uint64_t>(pos_) + size >= kBlockSize)
return nullptr;
// Get where we should start writing this string.
uint8_t* ptr = Get(pos_);
// First memcpy the size of the string into the buffer.
memcpy(ptr, &str_size, sizeof(str_size));
// Next the string itself which starts at offset 2.
if (PERFETTO_LIKELY(str_size > 0))
memcpy(&ptr[2], str.data(), str_size);
// Finally add a null terminator.
ptr[2 + str_size] = '\0';
// Update the end of the block and return the pointer to the string.
pos_ += size;
return ptr;
}
StringPool::Iterator::Iterator(const StringPool* pool) : pool_(pool) {}
StringPool::Iterator& StringPool::Iterator::operator++() {
PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
// Try and go to the next string in the current block.
const auto& block = pool_->blocks_[block_id_];
// Find the size of the string at the current offset in the block
// and increment the offset by that size.
auto str_size = GetSize(block.Get(block_offset_));
block_offset_ += kMetadataSize + str_size;
// If we're out of bounds for this block, go the the start of the next block.
if (block.pos() <= block_offset_) {
block_id_++;
block_offset_ = 0;
}
return *this;
}
StringPool::Iterator::operator bool() const {
return block_id_ < pool_->blocks_.size();
}
NullTermStringView StringPool::Iterator::StringView() {
PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
PERFETTO_DCHECK(block_offset_ < pool_->blocks_[block_id_].pos());
// If we're at (0, 0), we have the null string.
if (block_id_ == 0 && block_offset_ == 0)
return NullTermStringView();
return GetFromPtr(pool_->blocks_[block_id_].Get(block_offset_));
}
StringPool::Id StringPool::Iterator::StringId() {
PERFETTO_DCHECK(block_id_ < pool_->blocks_.size());
PERFETTO_DCHECK(block_offset_ < pool_->blocks_[block_id_].pos());
// If we're at (0, 0), we have the null string which has id 0.
if (block_id_ == 0 && block_offset_ == 0)
return 0;
return pool_->PtrToId(pool_->blocks_[block_id_].Get(block_offset_));
}
} // namespace trace_processor
} // namespace perfetto