object-introspection/include/oi/result/SizedResult-inl.h
Jake Hillion 89b230395f tbv2: remove unnecessary copy in Element (#457)
Summary:
tbv2: remove unnecessary copy in Element

`IntrospectionResult::const_iterator` iterates through the `Element`s in an
`IntrospectionResult`. `Element` currently copies the `type_path` which is a
`std::vector<string_view>` every time the iterator is incremented. This is
unnecessary as the data in the vector only changes slightly between iterations.

This change changes the `type_path` field in `Element` to a
`std::span<const std::string_view>`. Doing this previously caused SEGVs because
of the iterator's potential to be copied. To make it possible we do two things:
1. Make all copies explicit using a clone interface as in `ContainerInfo`. This
   prevents accidental copies of an expensive structure.
2. After calling the copy constructor in `clone()` update the `span` in `next_`
   to point at the newly copied structure.

Moves are fine because the `span` points at the allocation of the `vector`, not
the vector itself.


Test Plan:
- CI
- `FILTER='OilIntegration.*' make test`
- Ran `OilgenIntegration.std_vector_vector_int_some` which SEGVd with the
  `span` change before and now doesn't. This now passes cleanly with ASAN
  enabled on the target, though isn't available in `main` (only works on my
  machine).

Differential Revision: D53472595

Pulled By: JakeHillion
2024-02-07 07:51:22 -08:00

148 lines
3.8 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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.
*/
#if defined(INCLUDED_OI_RESULT_SIZED_ELEMENT_INL_H) || \
!defined(INCLUDED_OI_RESULT_SIZED_ELEMENT_H)
static_assert(false,
"SizedResult-inl.h provides inline declarations for "
"SizedResult.h and should only "
"be included by SizedResult.h");
#endif
#define INCLUDED_OI_RESULT_SIZED_ELEMENT_INL_H 1
#include <utility>
#include <vector>
#include "SizedResult.h"
namespace oi::result {
template <typename Res>
SizedResult<Res>::SizedResult(Res res) : res_{std::move(res)} {
}
template <typename Res>
typename SizedResult<Res>::const_iterator SizedResult<Res>::begin() const {
const_iterator it{res_.begin(), res_.end()};
++it;
return it;
}
template <typename Res>
typename SizedResult<Res>::const_iterator SizedResult<Res>::end() const {
return res_.end();
}
template <typename Res>
SizedResult<Res>::const_iterator::const_iterator(It it, const It& end)
: data_{it.clone()} {
struct StackEntry {
size_t index;
size_t depth;
};
std::vector<StackEntry> stack;
size_t size = 0;
size_t count = -1;
for (; it != end; ++it) {
++count;
auto depth = it->type_path.size();
while (!stack.empty() && stack.back().depth >= depth) {
auto i = stack.back().index;
stack.pop_back();
helpers_[i].last_child = count - 1;
}
size += it->exclusive_size;
helpers_.emplace_back(SizeHelper{.size = size});
stack.emplace_back(StackEntry{.index = count, .depth = depth});
}
while (!stack.empty()) {
auto i = stack.back().index;
stack.pop_back();
helpers_[i].last_child = count;
}
}
template <typename Res>
SizedResult<Res>::const_iterator::const_iterator(It end)
: data_{std::move(end)} {
}
template <typename Res>
typename SizedResult<Res>::const_iterator
SizedResult<Res>::const_iterator::operator++(int) {
auto old = *this;
operator++();
return old;
}
template <typename Res>
typename SizedResult<Res>::const_iterator&
SizedResult<Res>::const_iterator::operator++() {
// The below iterator is already pointing at the first element while this
// iterator is not. Skip incrementing it the first time around.
if (count_ != 0)
++data_;
if (count_ == helpers_.size()) {
next_.reset();
return *this;
}
size_t size = helpers_[helpers_[count_].last_child].size;
if (count_ != 0)
size -= helpers_[count_ - 1].size;
next_.emplace(*data_, size);
++count_;
return *this;
}
template <typename Res>
const typename SizedResult<Res>::Element&
SizedResult<Res>::const_iterator::operator*() const {
return *next_;
}
template <typename Res>
const typename SizedResult<Res>::Element*
SizedResult<Res>::const_iterator::operator->() const {
return &*next_;
}
template <typename Res>
bool SizedResult<Res>::const_iterator::operator==(
const SizedResult<Res>::const_iterator& that) const {
return this->data_ == that.data_;
}
template <typename Res>
bool SizedResult<Res>::const_iterator::operator!=(
const SizedResult<Res>::const_iterator& that) const {
return !(*this == that);
}
template <typename El>
SizedElement<El>::SizedElement(const El& el, size_t size_)
: El{el}, size{size_} {
}
template <typename El>
const El& SizedElement<El>::inner() const {
return static_cast<const El&>(*this);
}
} // namespace oi::result