object-introspection/include/oi/result/SizedResult.h
Jake Hillion 71e734b120 tbv2: calculate total memory footprint
Add the option to calculate total size (inclusive size) by wrapping the
existing iterator. This change provides a new iterator, `SizedIterator`, which
wraps an existing iterator and adds a new field `size` to the output element.

This is achieved with a two pass algorithm on the existing iterator:
1. Gather metadata for each element. This includes the total size up until that
   element and the range of elements that should be included in the size.
2. Return the result from the underlying iterator with the additional
   field.

This algorithm is `O(N)` time on the number of elements in the iterator and
`O(N)` time, storing 16 bytes per element. This isn't super expensive but is a
lot more than the current algorithm which requires close to constant space.
Because of this I've implemented it as a wrapper on the iterator rather than on
by default, though it is now on in every one of our integration test cases.

Test plan:
- Added to the integration tests for full coverage.
2024-01-04 09:21:35 +00:00

85 lines
2.0 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.
*/
#ifndef INCLUDED_OI_RESULT_SIZED_ELEMENT_H
#define INCLUDED_OI_RESULT_SIZED_ELEMENT_H 1
#include <cstddef>
#include <optional>
#include <type_traits>
#include <vector>
namespace oi::result {
template <typename El>
struct SizedElement : public El {
SizedElement(const El& el, size_t size);
const El& inner() const;
size_t size;
};
template <typename Res>
class SizedResult {
private:
using It = std::decay_t<decltype(std::declval<Res&>().begin())>;
using ParentEl = std::decay_t<decltype(std::declval<It&>().operator*())>;
public:
using Element = SizedElement<ParentEl>;
private:
struct SizeHelper {
size_t size = -1;
size_t last_child = -1;
};
public:
class const_iterator {
friend SizedResult;
public:
bool operator==(const const_iterator& that) const;
bool operator!=(const const_iterator& that) const;
const Element& operator*() const;
const Element* operator->() const;
const_iterator& operator++();
const_iterator operator++(int);
private:
const_iterator(It start, const It& end);
const_iterator(It end);
It data_;
std::vector<SizeHelper> helpers_;
size_t count_ = 0;
std::optional<Element> next_;
};
SizedResult(Res res);
const_iterator begin() const;
const_iterator end() const;
private:
Res res_;
};
} // namespace oi::result
#include "SizedResult-inl.h"
#endif