26#include <boost/lexical_cast.hpp>
27#include <boost/algorithm/string.hpp>
148template<
typename Value_t>
247template<
typename Target_t,
typename Mutex_t>
329template<
typename Value_t>
331 m_target_or_null(target),
332 m_saved_value(std::move(*m_target_or_null))
337template<
typename Value_t>
339 m_target_or_null(src_moved.m_target_or_null),
340 m_saved_value(std::move(src_moved.m_saved_value))
342 assert(
m_target_or_null &&
"Should not be moving-from a thing that has already been moved-from.");
344 src_moved.m_target_or_null =
nullptr;
348template<
typename Value_t>
351 if (m_target_or_null)
353 *m_target_or_null = std::move(m_saved_value);
360template<
typename Target_t,
typename Mutex_t>
365 assert(target &&
"Locked_proxy ctor: target must be non-null.");
368template<
typename Target_t,
typename Mutex_t>
371template<
typename Target_t,
typename Mutex_t>
377template<
typename Target_t,
typename Mutex_t>
385template<
typename Time_unit,
typename N_items>
402 double(items_per_time) * double(bits_per_item) * double(Time_unit::period::den)
403 / (double(Time_unit::period::num) * double(1000 * 1000));
406template<
typename Integer,
typename Integer2>
407constexpr Integer
ceil_div(Integer dividend, Integer2 divisor)
410 static_assert(std::is_integral_v<Integer>,
"ceil_div<T, T2>: T must be an integer type.");
411 static_assert(std::is_integral_v<Integer2>,
"ceil_div<T, T2>: T2 must be an integer type.");
416 return (dividend +
static_cast<Integer
>(divisor) -
static_cast<Integer
>(1)) /
static_cast<Integer
>(divisor);
421template<
typename Integer,
typename Integer2>
424 return static_cast<Integer
>(unit) *
ceil_div(dividend, unit);
427template<u
int64_t POSITIVE_INT>
430 static_assert(POSITIVE_INT > 0,
"UINT tparam needs to be a positive integer.");
431 return (POSITIVE_INT & (POSITIVE_INT - uint64_t(1))) == uint64_t(0);
436 return alignof(std::max_align_t);
439template<
typename Data,
size_t ALIGN_SZ>
442 static_assert(is_power_of_two<ALIGN_SZ>(),
"ALIGN_SZ must be one of 1, 2, 4, 8, ....");
446template<
typename Prefix,
typename Data,
size_t ALIGN_SZ>
449 const auto data_byte_ptr =
reinterpret_cast<uintptr_t
>(data_ptr);
450 assert(((data_byte_ptr % ALIGN_SZ) == 0)
451 &&
"Input ptr is not aligned; output wouldn't be either.");
453 return reinterpret_cast<Prefix*
>
454 (data_byte_ptr - aligned_sz_of<Prefix, ALIGN_SZ>());
457template<
typename Prefix,
typename Data,
size_t ALIGN_SZ>
460 return aligned_prefix_before<Prefix, Data, ALIGN_SZ>(
const_cast<Data*
>(data_ptr));
463template<
typename Data,
typename Prefix,
size_t ALIGN_SZ>
466 const auto prefix_byte_ptr =
reinterpret_cast<uintptr_t
>(prefix_ptr);
467 assert(((prefix_byte_ptr % ALIGN_SZ) == 0)
468 &&
"Input ptr is not aligned; output wouldn't be either.");
470 return reinterpret_cast<Data*
>
471 (prefix_byte_ptr + aligned_sz_of<Prefix, ALIGN_SZ>());
474template<
typename Data,
typename Prefix,
size_t ALIGN_SZ>
477 return after_aligned_prefix<Data, Prefix, ALIGN_SZ>(
const_cast<Prefix*
>(prefix_ptr));
484 return ((min_val < val) || (!(val < min_val))) &&
485 ((val < max_val) || (!(max_val < val)));
492 return (min_val < val) &&
493 ((val < max_val) || (!(max_val < val)));
500 return (val < max_val) &&
501 ((min_val < val) || (!(val < min_val)));
507 return (min_val < val) && (val < max_val);
510template<
typename Container>
511bool key_exists(
const Container& container,
const typename Container::key_type& key)
513 return container.find(key) != container.end();
516template<
typename Cleanup_func>
523 [func = std::move(func)](
auto) { func(); }};
526template<
typename Minuend,
typename Subtrahend>
535 const Minuend converted_subtrahend = Minuend{subtrahend};
538 if ((*minuend - floor) <= converted_subtrahend)
544 *minuend -= converted_subtrahend;
548template<
typename From,
typename To>
551 return ((num_froms *
sizeof(From)) +
sizeof(To) - 1) /
sizeof(To);
554template<
typename... T>
557 (*os << ... << std::forward<T>(ostream_args));
560template<
typename... T>
574template<
typename... T>
584template<
typename Map,
typename Sequence>
586 (Sequence
const & src_seq, Map* target_map,
587 const Function<
typename Map::mapped_type (
size_t)>& idx_to_map_val_func)
590 for (
const auto& src_element : src_seq)
592 (*target_map)[src_element] = idx_to_map_val_func(idx);
597template<
typename Map,
typename Sequence>
606template<
typename Const_buffer_sequence>
608 const Const_buffer_sequence& data,
609 const std::string& indentation,
610 size_t bytes_per_line)
612 using boost::io::ios_fill_saver;
613 using boost::io::ios_flags_saver;
614 using boost::io::ios_width_saver;
615 using boost::asio::buffers_iterator;
620 using Bufs_iter = buffers_iterator<Const_buffer_sequence, uint8_t>;
622 constexpr size_t BYTES_PER_LINE_DEFAULT = 16;
623 bool single_line_mode =
false;
624 if (bytes_per_line == 0)
626 bytes_per_line = BYTES_PER_LINE_DEFAULT;
628 else if (bytes_per_line ==
size_t(-1))
632 bytes_per_line = buffer_size(data);
636 single_line_mode =
true;
640 ios_flags_saver flags_saver{os};
641 ios_fill_saver fill_saver{os};
642 ios_width_saver width_saver{os};
646 os.setf(std::ios::right | std::ios::hex, std::ios::adjustfield | std::ios::basefield);
647 os << std::setfill(
'0');
649 const Bufs_iter end_byte_it = Bufs_iter::end(data);
650 for (Bufs_iter cur_byte_it = Bufs_iter::begin(data);
651 cur_byte_it != end_byte_it;
655 os << indentation <<
'['
656 << std::setw(2) << int(*cur_byte_it);
659 size_t n_bytes_printed;
660 for ((n_bytes_printed = 1), ++cur_byte_it;
661 (n_bytes_printed != bytes_per_line) && (cur_byte_it != end_byte_it);
662 ++cur_byte_it, ++n_bytes_printed)
664 os <<
' ' << std::setw(2) << int(*cur_byte_it);
668 for (
size_t n_bytes_printed_including_padding = n_bytes_printed;
669 n_bytes_printed_including_padding != bytes_per_line;
670 ++n_bytes_printed_including_padding)
676 cur_byte_it -= n_bytes_printed;
679 for (
size_t n_chars_printed = 0;
680 n_chars_printed != n_bytes_printed;
681 ++cur_byte_it, ++n_chars_printed)
683 char c = *cur_byte_it;
684 os << (isprint(c) ? c :
'.');
687 if (!single_line_mode)
698template<
typename Const_buffer_sequence>
700 size_t bytes_per_line)
715template<
typename Enum>
717 bool accept_num_encoding,
bool case_sensitive,
720 using boost::lexical_cast;
721 using boost::bad_lexical_cast;
722 using boost::algorithm::equals;
723 using boost::algorithm::is_iequal;
728 using Traits = std::char_traits<char>;
729 using enum_t = std::underlying_type_t<Enum>;
733 assert(enum_t(enum_lowest) >= 0);
735 const is_iequal i_equal_func{locale::classic()};
740 while (((ch = is.peek()) != Traits::eof()) && (isalnum(ch) || (ch ==
'_')))
746 Enum val = enum_default;
750 if (accept_num_encoding && isdigit(token.front()))
755 num_enum = lexical_cast<enum_t>(token);
757 if ((num_enum >= enum_t(enum_sentinel) || (num_enum < enum_t(enum_lowest))))
759 num_enum = enum_t(enum_default);
761 val = Enum{num_enum};
763 catch (
const bad_lexical_cast&)
765 assert(val == enum_default);
772 for (idx = enum_t(enum_lowest); idx != enum_t(enum_sentinel); ++idx)
774 const auto candidate = Enum{idx};
778 if (case_sensitive ? equals(token, lexical_cast<string>(candidate))
779 : equals(token, lexical_cast<string>(candidate), i_equal_func))
785 assert((idx != enum_t(enum_sentinel)) || (val == enum_default));
792template<
typename T,
typename... Ctor_args>
798 return ::new (
const_cast<void*
>
799 (
static_cast<void const volatile*
>
801 Value(std::forward<Ctor_args>(ctor_args)...);
813 static_assert(std::is_trivially_default_constructible_v<Value>,
814 "default_init_at() exists to begin an object's lifetime while writing nothing; a type whose "
815 "default-ction (possibly) writes defeats that purpose. "
816 "If you want the ctor to run, use construct_at().");
819 return ::new (
const_cast<void*
>
820 (
static_cast<void const volatile*
>
A simple locking-proxy class template (execute-around-pointer idiom): at construction locks the given...
Mutex_t Mutex
Alias for template parameter.
Locked_proxy & operator=(Locked_proxy &&)=delete
Prohibit modifying existing *this; except that moving-from is enabled via the move ctor.
Target * operator->() const
Returns pointer to the target object, the guarding mutex being locked by *this (do not use if moved-f...
Target_t Target
Alias for template parameter.
Target * m_target
The target object to which operator->() forwards. Meaningless if *this is moved-from.
Lock_guard< Mutex > m_lock
Holds the caller-supplied mutex locked throughout *this lifetime (unless moved-from: then holds nothi...
Locked_proxy(const Locked_proxy &)=delete
Prohibit copying: for each explicit ctor invocation, there shall be exactly 1 unlocking dtor invocati...
Locked_proxy(Target *target, Mutex *mutex)
Locks *mutex – blocking as-needed until that is possible – and makes *this proxy *target.
Target & operator*() const
Returns reference to the target object, the guarding mutex being locked by *this (do not use if moved...
Locked_proxy & operator=(const Locked_proxy &)=delete
Prohibit copying (see deleted copy ctor).
Locked_proxy(Locked_proxy &&src_moved)
Move constructor: *this acts as src_moved would-have (in particular its eventual destruction unlocks ...
An empty interface, consisting of nothing but a default virtual destructor, intended as a boiler-plat...
virtual ~Null_interface()=0
Boring virtual destructor.
A simple RAII-pattern class template that, at construction, sets the specified location in memory to ...
Value_t Value
Alias for template parameter.
Value * m_target_or_null
Target object location; see ctors; if null then this is a moved-from Scoped_setter that intentionally...
Value m_saved_value
If and only if m_target_or_null is non-null, this saves *m_target_or_null. Otherwise meaningless.
Scoped_setter & operator=(Scoped_setter &&)=delete
Prohibit modifying existing *this; except that moving-from is enabled via the move ctor.
Scoped_setter(const Scoped_setter &)=delete
Prohibit copying: for each explicit ctor invocation, there shall be exactly 1 non-no-op dtor invocati...
Scoped_setter & operator=(const Scoped_setter &)=delete
Prohibit copying (see deleted copy ctor).
~Scoped_setter()
Restores *target (from main ctor) to its value at entry to said ctor; or does nothing if *this has be...
Scoped_setter(Value *target, Value &&val_src_moved)
Post-condition: *target contains was val_src_moved contained at ctor entry; and the destructor invoca...
Similar to ostringstream but allows fast read-only access directly into the std::string being written...
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
bool in_closed_open_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a [low,...
constexpr size_t aligned_sz_of()
Compile-time calculation: Given a type Data returns the minimum size, in bytes, of a hypothetical buf...
bool key_exists(const Container &container, const typename Container::key_type &key)
Returns true if and only if the given key is present at least once in the given associative container...
constexpr bool is_power_of_two()
Compile-time check: true if and only if the given positive integer is a power of two: 1,...
void sequence_to_inverted_lookup_map(Sequence const &src_seq, Map *target_map, const Function< typename Map::mapped_type(size_t)> &idx_to_map_val_func)
Similar to the 2-arg overload of sequence_to_inverted_lookup_map() but with the ability to store a va...
T * default_init_at(T *obj)
Superficially similar to construct_at(), this default-initializes the T at the given location; this c...
double to_mbit_per_sec(N_items items_per_time, size_t bits_per_item)
Utility that converts a bandwidth in arbitrary units in both numerator and denominator to the same ba...
std::string buffers_dump_string(const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line)
Identical to buffers_to_ostream() but returns an std::string instead of writing to a given ostream.
Enum istream_to_enum(std::istream *is_ptr, Enum enum_default, Enum enum_sentinel, bool accept_num_encoding, bool case_sensitive, Enum enum_lowest)
Deserializes an enum class value from a standard input stream.
void feed_args_to_ostream(std::ostream *os, T &&... ostream_args)
Function template that simply outputs arguments 2+ via << to the given ostream, in the order given.
bool subtract_with_floor(Minuend *minuend, const Subtrahend &subtrahend, const Minuend &floor)
Performs *minuend -= subtrahend, subject to a floor of floor.
constexpr Integer round_to_multiple(Integer dividend, Integer2 unit)
Returns the smallest integer >= the given integer dividend such that it is a multiple of the given in...
boost::unique_lock< Mutex > Lock_guard
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of tha...
size_t size_unit_convert(From num_froms)
Answers the question what's the smallest integer number of Tos sufficient to verbatim store the given...
bool in_open_open_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
bool in_open_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
constexpr size_t max_align_sz()
Compile-time value: the most stringent alignment in the target architecture.
Prefix * aligned_prefix_before(Data *data_ptr)
Given a pointer to an aligned area, returns the pointer to a prefix area that is equally aligned,...
void ostream_op_to_string(std::string *target_str, T &&... ostream_args)
Writes to the specified string, as if the given arguments were each passed, via << in sequence,...
std::ostream & buffers_to_ostream(std::ostream &os, const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line)
Writes a multi- or single-line string representation of the provided binary data to an output stream,...
Data * after_aligned_prefix(Prefix *prefix_ptr)
The reverse of aligned_prefix_before(): given a prefix pointer, returns the base area following it.
bool in_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, inclusive.
boost::shared_ptr< void > Auto_cleanup
Helper type for setup_auto_cleanup().
Auto_cleanup setup_auto_cleanup(Cleanup_func &&func)
Provides a way to execute arbitrary (cleanup) code at the exit of the current block.
std::string ostream_op_string(T &&... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
T * construct_at(T *obj, Ctor_args &&... ctor_args)
std::construct_at() equivalent; unavailable until C++20, so here it is.
constexpr Integer ceil_div(Integer dividend, Integer2 divisor)
Returns the result of the given non-negative integer divided by a positive integer,...
Useful as a no-unique-address private member to make a type noncopyable while keeping that type an ag...
Noncopyable(const Noncopyable &)=delete
Forbid copying.
void operator=(const Noncopyable &)=delete
Forbid copying.
Noncopyable()=default
Makes it possible to instantiate.