mirror of
https://github.com/tstack/lnav
synced 2024-11-15 18:13:10 +00:00
667 lines
14 KiB
C++
667 lines
14 KiB
C++
/**
|
|
* Copyright (c) 2022, Timothy Stack
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lnav_itertools_hh
|
|
#define lnav_itertools_hh
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "func_util.hh"
|
|
#include "optional.hpp"
|
|
|
|
namespace lnav {
|
|
namespace itertools {
|
|
|
|
struct empty {};
|
|
|
|
struct not_empty {};
|
|
|
|
struct full {
|
|
size_t f_max_size;
|
|
};
|
|
|
|
namespace details {
|
|
|
|
template<typename T>
|
|
struct unwrap_or {
|
|
T uo_value;
|
|
};
|
|
|
|
template<typename P>
|
|
struct find_if {
|
|
P fi_predicate;
|
|
};
|
|
|
|
template<typename T>
|
|
struct find {
|
|
T f_value;
|
|
};
|
|
|
|
struct second {};
|
|
|
|
template<typename F>
|
|
struct filter_in {
|
|
F f_func;
|
|
};
|
|
|
|
template<typename F>
|
|
struct filter_out {
|
|
F f_func;
|
|
};
|
|
|
|
template<typename C>
|
|
struct sort_by {
|
|
C sb_cmp;
|
|
};
|
|
|
|
struct sorted {};
|
|
|
|
template<typename F>
|
|
struct mapper {
|
|
F m_func;
|
|
};
|
|
|
|
template<typename R, typename T>
|
|
struct folder {
|
|
R f_func;
|
|
T f_init;
|
|
};
|
|
|
|
template<typename T>
|
|
struct prepend {
|
|
T p_value;
|
|
};
|
|
|
|
template<typename T>
|
|
struct append {
|
|
T p_value;
|
|
};
|
|
|
|
struct nth {
|
|
nonstd::optional<size_t> a_index;
|
|
};
|
|
|
|
struct skip {
|
|
size_t a_count;
|
|
};
|
|
|
|
struct unique {};
|
|
|
|
struct max_value {};
|
|
|
|
template<typename T>
|
|
struct max_with_init {
|
|
T m_init;
|
|
};
|
|
|
|
struct sum {};
|
|
|
|
} // namespace details
|
|
|
|
template<typename T>
|
|
inline details::unwrap_or<T>
|
|
unwrap_or(T value)
|
|
{
|
|
return details::unwrap_or<T>{
|
|
value,
|
|
};
|
|
}
|
|
|
|
template<typename P>
|
|
inline details::find_if<P>
|
|
find_if(P predicate)
|
|
{
|
|
return details::find_if<P>{
|
|
predicate,
|
|
};
|
|
}
|
|
|
|
template<typename T>
|
|
inline details::find<T>
|
|
find(T value)
|
|
{
|
|
return details::find<T>{
|
|
value,
|
|
};
|
|
}
|
|
|
|
inline details::second
|
|
second()
|
|
{
|
|
return details::second{};
|
|
}
|
|
|
|
inline details::nth
|
|
nth(nonstd::optional<size_t> index)
|
|
{
|
|
return details::nth{
|
|
index,
|
|
};
|
|
}
|
|
|
|
inline details::skip
|
|
skip(size_t count)
|
|
{
|
|
return details::skip{
|
|
count,
|
|
};
|
|
}
|
|
|
|
template<typename F>
|
|
inline details::filter_in<F>
|
|
filter_in(F func)
|
|
{
|
|
return details::filter_in<F>{
|
|
func,
|
|
};
|
|
}
|
|
|
|
template<typename F>
|
|
inline details::filter_out<F>
|
|
filter_out(F func)
|
|
{
|
|
return details::filter_out<F>{
|
|
func,
|
|
};
|
|
}
|
|
|
|
template<typename T>
|
|
inline details::prepend<T>
|
|
prepend(T value)
|
|
{
|
|
return details::prepend<T>{
|
|
std::move(value),
|
|
};
|
|
}
|
|
|
|
template<typename T>
|
|
inline details::append<T>
|
|
append(T value)
|
|
{
|
|
return details::append<T>{
|
|
std::move(value),
|
|
};
|
|
}
|
|
|
|
template<typename C>
|
|
inline details::sort_by<C>
|
|
sort_with(C cmp)
|
|
{
|
|
return details::sort_by<C>{cmp};
|
|
}
|
|
|
|
template<typename C, typename T>
|
|
inline auto
|
|
sort_by(T C::*m)
|
|
{
|
|
return sort_with(
|
|
[m](const C& lhs, const C& rhs) { return lhs.*m < rhs.*m; });
|
|
}
|
|
|
|
template<typename F>
|
|
inline details::mapper<F>
|
|
map(F func)
|
|
{
|
|
return details::mapper<F>{func};
|
|
}
|
|
|
|
inline auto
|
|
deref()
|
|
{
|
|
return map([](auto iter) { return *iter; });
|
|
}
|
|
|
|
template<typename R, typename T>
|
|
inline details::folder<R, T>
|
|
fold(R func, T init)
|
|
{
|
|
return details::folder<R, T>{func, init};
|
|
}
|
|
|
|
inline details::unique
|
|
unique()
|
|
{
|
|
return details::unique{};
|
|
}
|
|
|
|
inline details::sorted
|
|
sorted()
|
|
{
|
|
return details::sorted{};
|
|
}
|
|
|
|
template<typename T, typename... Args>
|
|
T
|
|
chain(const T& value1, const Args&... args)
|
|
{
|
|
T retval;
|
|
|
|
for (const auto& arg : {value1, args...}) {
|
|
for (const auto& elem : arg) {
|
|
retval.emplace_back(elem);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
inline details::max_value
|
|
max()
|
|
{
|
|
return details::max_value{};
|
|
}
|
|
|
|
template<typename T>
|
|
inline details::max_with_init<T>
|
|
max(T init)
|
|
{
|
|
return details::max_with_init<T>{init};
|
|
}
|
|
|
|
inline details::sum
|
|
sum()
|
|
{
|
|
return details::sum{};
|
|
}
|
|
|
|
} // namespace itertools
|
|
} // namespace lnav
|
|
|
|
template<typename C, typename P>
|
|
nonstd::optional<std::conditional_t<
|
|
std::is_const<typename std::remove_reference_t<C>>::value,
|
|
typename std::remove_reference_t<C>::const_iterator,
|
|
typename std::remove_reference_t<C>::iterator>>
|
|
operator|(C&& in, const lnav::itertools::details::find_if<P>& finder)
|
|
{
|
|
for (auto iter = in.begin(); iter != in.end(); ++iter) {
|
|
if (lnav::func::invoke(finder.fi_predicate, *iter)) {
|
|
return nonstd::make_optional(iter);
|
|
}
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
template<typename C, typename T>
|
|
nonstd::optional<size_t>
|
|
operator|(const C& in, const lnav::itertools::details::find<T>& finder)
|
|
{
|
|
size_t retval = 0;
|
|
for (const auto& elem : in) {
|
|
if (elem == finder.f_value) {
|
|
return nonstd::make_optional(retval);
|
|
}
|
|
retval += 1;
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
template<typename C>
|
|
nonstd::optional<typename C::const_iterator>
|
|
operator|(const C& in, const lnav::itertools::details::nth indexer)
|
|
{
|
|
if (!indexer.a_index.has_value()) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
if (indexer.a_index.value() < in.size()) {
|
|
auto iter = in.begin();
|
|
|
|
std::advance(iter, indexer.a_index.value());
|
|
return nonstd::make_optional(iter);
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
template<typename C>
|
|
nonstd::optional<typename C::value_type>
|
|
operator|(const C& in, const lnav::itertools::details::max_value maxer)
|
|
{
|
|
nonstd::optional<typename C::value_type> retval;
|
|
|
|
for (const auto& elem : in) {
|
|
if (!retval) {
|
|
retval = elem;
|
|
continue;
|
|
}
|
|
|
|
if (elem > retval.value()) {
|
|
retval = elem;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C, typename T>
|
|
typename C::value_type
|
|
operator|(const C& in, const lnav::itertools::details::max_with_init<T> maxer)
|
|
{
|
|
typename C::value_type retval = (typename C::value_type) maxer.m_init;
|
|
|
|
for (const auto& elem : in) {
|
|
if (elem > retval) {
|
|
retval = elem;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C>
|
|
typename C::value_type
|
|
operator|(const C& in, const lnav::itertools::details::sum summer)
|
|
{
|
|
typename C::value_type retval{0};
|
|
|
|
for (const auto& elem : in) {
|
|
retval += elem;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C>
|
|
C
|
|
operator|(const C& in, const lnav::itertools::details::skip& skipper)
|
|
{
|
|
C retval;
|
|
|
|
if (skipper.a_count < in.size()) {
|
|
auto iter = in.begin();
|
|
std::advance(iter, skipper.a_count);
|
|
for (; iter != in.end(); ++iter) {
|
|
retval.emplace_back(*iter);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
std::vector<T*>
|
|
operator|(const std::vector<std::unique_ptr<T>>& in,
|
|
const lnav::itertools::details::filter_in<F>& filterer)
|
|
{
|
|
std::vector<T*> retval;
|
|
|
|
for (const auto& elem : in) {
|
|
if (lnav::func::invoke(filterer.f_func, elem)) {
|
|
retval.emplace_back(elem.get());
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C, typename F>
|
|
C
|
|
operator|(const C& in, const lnav::itertools::details::filter_in<F>& filterer)
|
|
{
|
|
C retval;
|
|
|
|
for (const auto& elem : in) {
|
|
if (lnav::func::invoke(filterer.f_func, elem)) {
|
|
retval.emplace_back(elem);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C, typename F>
|
|
C
|
|
operator|(const C& in, const lnav::itertools::details::filter_out<F>& filterer)
|
|
{
|
|
C retval;
|
|
|
|
for (const auto& elem : in) {
|
|
if (!lnav::func::invoke(filterer.f_func, elem)) {
|
|
retval.emplace_back(elem);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename C, typename T>
|
|
C
|
|
operator|(C in, const lnav::itertools::details::prepend<T>& prepender)
|
|
{
|
|
in.emplace(in.begin(), prepender.p_value);
|
|
|
|
return in;
|
|
}
|
|
|
|
template<typename C, typename T>
|
|
C
|
|
operator|(C in, const lnav::itertools::details::append<T>& appender)
|
|
{
|
|
in.emplace_back(appender.p_value);
|
|
|
|
return in;
|
|
}
|
|
|
|
template<typename C, typename R, typename T>
|
|
T
|
|
operator|(const C& in, const lnav::itertools::details::folder<R, T>& folder)
|
|
{
|
|
auto accum = folder.f_init;
|
|
|
|
for (const auto& elem : in) {
|
|
accum = folder.f_func(elem, accum);
|
|
}
|
|
|
|
return accum;
|
|
}
|
|
|
|
template<typename C>
|
|
std::set<typename C::value_type>
|
|
operator|(C&& in, const lnav::itertools::details::unique& sorter)
|
|
{
|
|
return {in.begin(), in.end()};
|
|
}
|
|
|
|
template<typename T, typename C>
|
|
T
|
|
operator|(T in, const lnav::itertools::details::sort_by<C>& sorter)
|
|
{
|
|
std::sort(in.begin(), in.end(), sorter.sb_cmp);
|
|
|
|
return in;
|
|
}
|
|
|
|
template<typename T>
|
|
T
|
|
operator|(T in, const lnav::itertools::details::sorted& sorter)
|
|
{
|
|
std::sort(in.begin(), in.end());
|
|
|
|
return in;
|
|
}
|
|
|
|
template<typename T,
|
|
typename F,
|
|
std::enable_if_t<lnav::func::is_invocable<F, T>::value, int> = 0>
|
|
auto
|
|
operator|(nonstd::optional<T> in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> nonstd::optional<
|
|
typename std::remove_reference_t<typename std::remove_const_t<
|
|
decltype(lnav::func::invoke(mapper.m_func, in.value()))>>>
|
|
{
|
|
if (!in) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
return nonstd::make_optional(lnav::func::invoke(mapper.m_func, in.value()));
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
|
|
-> std::vector<decltype(mapper.m_func(typename T::value_type{}))>
|
|
{
|
|
using return_type
|
|
= std::vector<decltype(mapper.m_func(typename T::value_type{}))>;
|
|
return_type retval;
|
|
|
|
retval.reserve(in.size());
|
|
std::transform(
|
|
in.begin(), in.end(), std::back_inserter(retval), mapper.m_func);
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(const std::vector<T>& in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> std::vector<typename std::remove_const_t<decltype((*(*in.begin())
|
|
.*mapper.m_func)())>>
|
|
{
|
|
using return_type = std::vector<typename std::remove_const_t<decltype((
|
|
*(*in.begin()).*mapper.m_func)())>>;
|
|
return_type retval;
|
|
|
|
retval.reserve(in.size());
|
|
std::transform(
|
|
in.begin(),
|
|
in.end(),
|
|
std::back_inserter(retval),
|
|
[&mapper](const auto& elem) { return ((*elem).*mapper.m_func)(); });
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(const std::vector<std::shared_ptr<T>>& in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> std::vector<typename std::remove_reference_t<
|
|
typename std::remove_const_t<decltype(((*in.front()).*mapper.m_func))>>>
|
|
{
|
|
using return_type = std::vector<
|
|
typename std::remove_reference_t<typename std::remove_const_t<decltype((
|
|
(*in.front()).*mapper.m_func))>>>;
|
|
return_type retval;
|
|
|
|
retval.reserve(in.size());
|
|
for (const auto& elem : in) {
|
|
retval.template emplace_back(((*elem).*mapper.m_func));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(const std::vector<T>& in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> std::vector<
|
|
typename std::remove_const_t<typename std::remove_reference_t<
|
|
decltype(((in.front()).*mapper.m_func))>>>
|
|
{
|
|
using return_type = std::vector<
|
|
typename std::remove_const_t<typename std::remove_reference_t<decltype((
|
|
(in.front()).*mapper.m_func))>>>;
|
|
return_type retval;
|
|
|
|
retval.reserve(in.size());
|
|
for (const auto& elem : in) {
|
|
retval.template emplace_back(elem.*mapper.m_func);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(nonstd::optional<T> in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> nonstd::optional<typename std::remove_reference_t<
|
|
typename std::remove_const_t<decltype(((in.value()).*mapper.m_func))>>>
|
|
{
|
|
if (!in) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
return nonstd::make_optional((in.value()).*mapper.m_func);
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(nonstd::optional<T> in,
|
|
const lnav::itertools::details::mapper<F>& mapper)
|
|
-> nonstd::optional<
|
|
typename std::remove_const_t<typename std::remove_reference_t<
|
|
decltype(((*in.value()).*mapper.m_func))>>>
|
|
{
|
|
if (!in) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
return nonstd::make_optional((*in.value()).*mapper.m_func);
|
|
}
|
|
|
|
template<typename T>
|
|
T
|
|
operator|(nonstd::optional<T> in,
|
|
const lnav::itertools::details::unwrap_or<T>& unwrapper)
|
|
{
|
|
return in.value_or(unwrapper.uo_value);
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
auto
|
|
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
|
|
-> std::vector<
|
|
std::remove_const_t<decltype(((*in.begin()).*mapper.m_func)())>>
|
|
{
|
|
using return_type = std::vector<
|
|
std::remove_const_t<decltype(((*in.begin()).*mapper.m_func)())>>;
|
|
return_type retval;
|
|
|
|
retval.reserve(in.size());
|
|
for (const auto& elem : in) {
|
|
retval.template emplace_back((elem.*mapper.m_func)());
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
#endif
|