//------------------------------------------------------------------------------
// File: ContainerUtils.hh
// Author: Abhishek Lekshmanan - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2022 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#pragma once
#include "common/utils/TypeTraits.hh"
#include
#include
namespace eos::common
{
//----------------------------------------------------------------------------
//! erase_if that erases elements in place for elements matching a predicate
//! This is useful in erase remove idiom useful for assoc. containers where
//! std::remove_if will not compile, almost borrowed from erase_if C++ ref page
//!
//! @param C the associative container, elements will be removed in place
//! @param pred the predicate to evaluate, please note the container
//! value_type aka the pair of values will be the input for the
//! predicate
//! @return the no of elements removed
//! Usage eg:
//! eos::common::erase_if(m, [](const auto& p){ return p.first % 2 == 0;})
//----------------------------------------------------------------------------
template
typename C::size_type
erase_if(C& c, Pred pred)
{
// TODO: (abhi) sfinae for normal overloads where you can just erase
// (remove_if) for container types like vector/lists
static_assert(detail::is_assoc_container_v,
"This method is only implemented for assoc. containers just "
"use std::erase(std::remove_if(C,pred)) instead");
auto init_sz = c.size();
for (auto it = c.begin(), last = c.end(); it != last;) {
if (pred(*it)) {
it = c.erase(it);
} else {
++it;
}
}
return init_sz - c.size();
}
// Both the functions below assume 0 is not supplied as an argument!
inline uint8_t get_msb(uint64_t val)
{
#if defined(__GNUC__) || defined(__clang__)
return 63 - __builtin_clzll(val);
#else
uint8_t msb = 0;
while (val >>= 1) {
msb++;
}
return msb;
#endif
}
inline uint64_t
clamp_index(uint64_t index, uint64_t size)
{
if (index < size) {
return index;
}
// For powers of 2 sizes, you just need to find the number within
// the power of 2 range which is cheaper than a modulo.
if ((size & (size - 1)) == 0) {
auto bit = get_msb(size);
return index & ((1ULL << bit) - 1);
}
return index % size;
}
//----------------------------------------------------------------------------
//! A simple Round Robin like picker for a container, for indices past the size
//! we simply wrap around giving a feel of circular iterator
//! NOTE: In case of an empty container this function raises out_of_range
//! exception, so please ensure container is not empty!
//!
//! @param C Container to pick from
//! @param index
//! @return item at index
//! Usage eg:
//! vector v {1,2,3};
//! pickIndexRR(v,3) -> 1 (v,4)->2 (v,5) -> 3 ...
//----------------------------------------------------------------------------
template
typename C::value_type
pickIndexRR(const C& c, uint64_t index)
{
if (!c.size()) {
throw std::out_of_range("Empty Container!");
}
auto iter = c.begin();
std::advance(iter, clamp_index(index, c.size()));
return *iter;
}
//----------------------------------------------------------------------------
//! Transfer a container onto another, this variants destructively move values
//! from other container onto source container at a given pos.
//! \tparam C container type - will be inferred
//! \param c container where other container will be spliced onto
//! \param other container whose elements will be consumed
//! \param pos position where we need to splice
//----------------------------------------------------------------------------
template
void
splice(C& c, C&& other,
typename C::const_iterator pos)
{
c.insert(pos,
std::make_move_iterator(other.begin()),
std::make_move_iterator(other.end()));
}
//----------------------------------------------------------------------------
//! Transfer a container onto another at the end, this variants destructively move values
//! from other container onto source container at a given pos.
//! \tparam C container type - will be inferred
//! \param c container where other container will be spliced onto
//! \param other container whose elements will be consumed
//----------------------------------------------------------------------------
template
void splice(C& c, C&& other)
{
splice(c, std::move(other), c.end());
}
uint64_t inline next_power2(uint64_t x) {
#if defined(__GNUC__) || defined(__clang__)
x = x ? x : 1;
return x == 1 ? 1 : 1ULL<<(64 - __builtin_clzll(x-1));
#else
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
++x;
x += (x == 0);
return x;
#endif
}
} // eos::common