Files
rippled/include/xrpl/basics/RangeSet.h
2025-10-23 11:04:30 -04:00

178 lines
4.8 KiB
C++

#ifndef XRPL_BASICS_RANGESET_H_INCLUDED
#define XRPL_BASICS_RANGESET_H_INCLUDED
#include <xrpl/beast/core/LexicalCast.h>
#include <boost/algorithm/string.hpp>
#include <boost/icl/closed_interval.hpp>
#include <boost/icl/interval_set.hpp>
#include <optional>
#include <string>
#include <vector>
namespace ripple {
/** A closed interval over the domain T.
For an instance ClosedInterval c, this represents the closed interval
(c.first(), c.last()). A single element interval has c.first() == c.last().
This is simply a type-alias for boost interval container library interval
set, so users should consult that documentation for available supporting
member and free functions.
*/
template <class T>
using ClosedInterval = boost::icl::closed_interval<T>;
/** Create a closed range interval
Helper function to create a closed range interval without having to qualify
the template argument.
*/
template <class T>
ClosedInterval<T>
range(T low, T high)
{
return ClosedInterval<T>(low, high);
}
/** A set of closed intervals over the domain T.
Represents a set of values of the domain T using the minimum number
of disjoint ClosedInterval<T>. This is useful to represent ranges of
T where a few instances are missing, e.g. the set 1-5,8-9,11-14.
This is simply a type-alias for boost interval container library interval
set, so users should consult that documentation for available supporting
member and free functions.
*/
template <class T>
using RangeSet = boost::icl::interval_set<T, std::less, ClosedInterval<T>>;
/** Convert a ClosedInterval to a styled string
The styled string is
"c.first()-c.last()" if c.first() != c.last()
"c.first()" if c.first() == c.last()
@param ci The closed interval to convert
@return The style string
*/
template <class T>
std::string
to_string(ClosedInterval<T> const& ci)
{
if (ci.first() == ci.last())
return std::to_string(ci.first());
return std::to_string(ci.first()) + "-" + std::to_string(ci.last());
}
/** Convert the given RangeSet to a styled string.
The styled string representation is the set of disjoint intervals joined
by commas. The string "empty" is returned if the set is empty.
@param rs The rangeset to convert
@return The styled string
*/
template <class T>
std::string
to_string(RangeSet<T> const& rs)
{
if (rs.empty())
return "empty";
std::string s;
for (auto const& interval : rs)
s += ripple::to_string(interval) + ",";
s.pop_back();
return s;
}
/** Convert the given styled string to a RangeSet.
The styled string representation is the set
of disjoint intervals joined by commas.
@param rs The set to be populated
@param s The styled string to convert
@return True on successfully converting styled string
*/
template <class T>
[[nodiscard]] bool
from_string(RangeSet<T>& rs, std::string const& s)
{
std::vector<std::string> intervals;
std::vector<std::string> tokens;
bool result{true};
rs.clear();
boost::split(tokens, s, boost::algorithm::is_any_of(","));
for (auto const& t : tokens)
{
boost::split(intervals, t, boost::algorithm::is_any_of("-"));
switch (intervals.size())
{
case 1: {
T front;
if (!beast::lexicalCastChecked(front, intervals.front()))
result = false;
else
rs.insert(front);
break;
}
case 2: {
T front;
if (!beast::lexicalCastChecked(front, intervals.front()))
result = false;
else
{
T back;
if (!beast::lexicalCastChecked(back, intervals.back()))
result = false;
else
rs.insert(range(front, back));
}
break;
}
default:
result = false;
}
if (!result)
break;
intervals.clear();
}
if (!result)
rs.clear();
return result;
}
/** Find the largest value not in the set that is less than a given value.
@param rs The set of interest
@param t The value that must be larger than the result
@param minVal (Default is 0) The smallest allowed value
@return The largest v such that minV <= v < t and !contains(rs, v) or
std::nullopt if no such v exists.
*/
template <class T>
std::optional<T>
prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
{
if (rs.empty() || t == minVal)
return std::nullopt;
RangeSet<T> tgt{ClosedInterval<T>{minVal, t - 1}};
tgt -= rs;
if (tgt.empty())
return std::nullopt;
return boost::icl::last(tgt);
}
} // namespace ripple
#endif