Flow 3.0.0
Flow project: Full implementation reference.
util.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22// So that this_thread_unique_token() is usable (_fwd.hpp only => incomplete type; but they can #include us at least):
26#include <boost/lexical_cast.hpp>
27#include <boost/algorithm/string.hpp>
28#include <type_traits>
29#include <utility>
30
31namespace flow::util
32{
33
34// Types.
35
36/**
37 * An empty interface, consisting of nothing but a default `virtual` destructor, intended as a boiler-plate-reducing
38 * base for any other (presumably `virtual`-method-having) class that would otherwise require a default `virtual`
39 * destructor.
40 *
41 * Usually, if you have a base class `C` at the top of a `virtual`-method-having hierarchy, then it needs a `virtual`
42 * destructor, even if it is `= default` or `{}`. Otherwise, trying to delete an object of subclass `C2 : public C`
43 * via a `C*` pointer will fail to call destructor `~C2()` -- which may not be empty, causing leaks and so on.
44 * Declaring `~C()` and its empty implementation is surprisingly verbose. So, instead, don't; and `public`ly derive
45 * from Null_interface.
46 *
47 * It is particularly useful for interface classes.
48 */
50{
51public:
52 // Destructor.
53
54 /**
55 * Boring `virtual` destructor.
56 *
57 * Why is it pure? Main reason: Then Null_interface becomes abstract and cannot be itself instantiated, which is good.
58 * Otherwise we'd need a `protected` constructor or something to prevent it.
59 *
60 * I (ygoldfel) actually thought this means a subclass *has* to now define a body (even if merely `= default` or
61 * `{}`): but no: the compiler will (and must) generate an empty (and, because of us, `virtual`) destructor for any
62 * subclass that doesn't explicitly define one. A destructor isn't a regular method, so that's how it works.
63 * There will not be a linker error.
64 */
65 virtual ~Null_interface() = 0;
66};
67
68/**
69 * Useful as a no-unique-address private member to make a type noncopyable while keeping that type an aggregate
70 * (can be direct-initialized).
71 *
72 * So you can do: `[[no_unique_address]] flow::util::Noncopyable m_nc{};`.
73 *
74 * ### Rationale ###
75 * The usual technique of deriving from `boost::noncopyable` disables aggregateness. In C++20 declaring
76 * a `= delete` copy ctor also disables it. This trick still works though.
77 */
79{
80 // Constructors/destructor.
81
82 /// Makes it possible to instantiate.
83 Noncopyable() = default;
84 /// Forbid copying.
85 Noncopyable(const Noncopyable&) = delete;
86
87 // Methods.
88
89 /// Forbid copying.
90 void operator=(const Noncopyable&) = delete;
91};
92
93/**
94 * A simple RAII-pattern class template that, at construction, sets the specified location in memory to a specified
95 * value, memorizing the previous contents; and at destruction restores the value. E.g.:
96 *
97 * ~~~
98 * thread_local int s_this_thread_val;
99 * ...
100 * {
101 * Scoped_setter<int> setter{&s_this_thread_val, 75}; // Set it to 75 and memorize (whatever).
102 * ...
103 * {
104 * Scoped_setter<int> setter{&s_this_thread_val, 125}; // Set it to 125 and memorize 75.
105 * ...
106 * } // Restored from 125 to 75.
107 * ...
108 * } // Restored from (probably) 75 to (whatever).
109 * ~~~
110 *
111 * The object is movable, not copyable (which is similar to `unique_ptr`) to prevent "double-restore." Related:
112 * one can easily return customized auto-setter/restorers:
113 *
114 * ~~~
115 * thread_local Widget s_widget;
116 * auto widget_setter_auto(Widget&& widget_moved)
117 * {
118 * return flow::util::Scoped_setter<Widget>{&s_widget, std::move(widget_moved)};
119 * }
120 * ...
121 * { // Later, some block: Set s_widget. Code here doesn't even know/care a Scoped_setter is involved.
122 * const auto setter_auto = widget_setter_auto({ ...widget-init... });
123 * ...
124 * } // Restore s_widget.
125 * ~~~
126 *
127 * ### Thready safety ###
128 * This is a simple object: it just performs a few assignments without any added
129 * concurrency protection. If the memory location can be accessed simultaneously by other threads, watch out.
130 *
131 * In particular it's a good fit for thread-local locations: `&X`, where `X` is declared `thread_local`, or
132 * `X == *(P.get())` where `P` is a `boost::thread_specific_ptr` or equivalent.
133 *
134 * @tparam Value_t
135 * The stored type, which must be move-assignable and move-constructible.
136 * All `Value` writes are performed using exclusively these operations.
137 * Informally: For best performance when `Value` is a heavy-weight type, these operations should be
138 * be written to be light-weight, such as in terms of swapping a few scalars.
139 * In particular this is already the case for all STL-compliant container types.
140 *
141 * @internal
142 * ### Implementation ###
143 * An alternative implementation, which could even be reduced to just an alias, would have used `unique_ptr`.
144 * However in this case I (ygoldfel) wanted maximum control for perf. The use case originally driving this was
145 * the thread-local verbosity override: log::Config::this_thread_verbosity_override_auto(). flow::log is fairly
146 * paranoid about performance, in general, although admittedly this particular call isn't necessarily ubiquitous.
147 */
148template<typename Value_t>
150{
151public:
152 // Types.
153
154 /// Alias for template parameter.
155 using Value = Value_t;
156
157 // Constructors/destructor.
158
159 /**
160 * Post-condition: `*target` contains was `val_src_moved` contained at ctor entry; and the destructor invocation shall
161 * reverse this, so that `*target` is restored to its value at entry.
162 *
163 * `*this` cannot be copied, but it can be moved. As a result, it is guaranteed that the aforementioned destructor
164 * will execute exactly once; however it can be move-constructed-from-`*this` other Scope_setter's destructor,
165 * while our own dtor therefore is a no-op.
166 *
167 * @param target
168 * The target object that shall be set to `val_src_moved` now and restored in our, or moved-object's, dtor.
169 * The current value of `*target` is saved internally via assignment of `move(*target)`.
170 * Behavior undefined (assertion may trip) if null.
171 * @param val_src_moved
172 * Value to save to `*target` immediately, via assignment of `move(val_src_moved)`.
173 */
174 explicit Scoped_setter(Value* target, Value&& val_src_moved);
175
176 /**
177 * Move constructor: `*this` acts as `src_moved` would-have, while `src_moved` becomes a no-op object permanently.
178 *
179 * @param src_moved
180 * Source object. Its destructor shall do nothing after this returns.
181 */
182 Scoped_setter(Scoped_setter&& src_moved);
183
184 /// Prohibit copying: for each `explicit` ctor invocation, there shall be exactly 1 non-no-op dtor invocation.
185 Scoped_setter(const Scoped_setter&) = delete;
186
187 /**
188 * Restores `*target` (from main ctor) to its value at entry to said ctor; or does nothing if `*this` has been
189 * moved-from via the move ctor.
190 */
192
193 // Methods.
194
195 /// Prohibit copying (see `delete`d copy ctor).
197
198 /// Prohibit modifying existing `*this`; except that moving-from is enabled via the move ctor.
200
201private:
202 // Data.
203
204 /// Target object location; see ctors; if null then this is a moved-from Scoped_setter that intentionally no-ops.
206
207 /// If and only if #m_target_or_null is non-null, this saves `*m_target_or_null`. Otherwise meaningless.
209}; // class Scoped_setter
210
211/**
212 * A simple locking-proxy class template (execute-around-pointer idiom): at construction locks the given mutex and
213 * provides `->` access to the given target object; at destruction unlocks it. In typical use one obtains `*this` as
214 * an unnamed temporary in the middle of an expression, so that the mutex is locked for exactly that expression's
215 * duration. E.g., given `Widget w` and a mutex `m` serializing all access to `w` -- suppose some `w_locked()` returns
216 * `Locked_proxy{&w, &m}`:
217 *
218 * ~~~
219 * const auto n = w_locked()->compute_n(); // Lock m; invoke w.compute_n(); save the result; unlock m.
220 * ~~~
221 *
222 * This is the *execute-around-pointer idiom*. The typical motivation: some class `C` internally owns both a
223 * mutex-protected object and the mutex, its background/concurrent machinery locking the latter as needed; and
224 * wants to let its user safely invoke methods of that object. Then `C` can supply a `w_locked()`-style accessor
225 * as sketched above -- instead of writing a delegating, internally-locking method for every operation of
226 * interest. The set of available operations then maintains itself, as does the correctness of each such call's
227 * locking.
228 *
229 * To grant read-only (`const` methods only) access, simply use a `const`-qualified #Target type: e.g.,
230 * `Locked_proxy<const Widget, ...>`.
231 *
232 * ### Keep it to one expression ###
233 * `*this` existing = the mutex being locked. The intended use is as an unnamed temporary (see above example):
234 * then the lock is held for a few microseconds, and it is impossible to leak. If you *do* save a `*this` to
235 * extend the locked section, keep the scope tight: whatever machinery normally locks that mutex is blocked
236 * in the meantime.
237 *
238 * @see `boost::synchronized_value` implements the same idiom but *owns* both the payload and the mutex
239 * ("wrap your data in me"). `*this`, by contrast, is non-owning: it is for objects whose guarding
240 * mutex exists independently -- e.g., one that guards more state than just the target object.
241 *
242 * @tparam Target_t
243 * Type of the object to which `*this` proxies access; possibly `const`-qualified (see above).
244 * @tparam Mutex_t
245 * A `Lockable`-concept mutex type, e.g. util::Mutex_non_recursive.
246 */
247template<typename Target_t, typename Mutex_t>
249{
250public:
251 // Types.
252
253 /// Alias for template parameter.
254 using Target = Target_t;
255
256 /// Alias for template parameter.
257 using Mutex = Mutex_t;
258
259 // Constructors/destructor.
260
261 /**
262 * Locks `*mutex` -- blocking as-needed until that is possible -- and makes `*this` proxy `*target`.
263 * The destructor invocation shall unlock it.
264 *
265 * `*this` cannot be copied, but it can be moved. As a result, it is guaranteed that the aforementioned
266 * unlocking will occur exactly once; however it can occur via the destructor of another Locked_proxy that was
267 * move-constructed from `*this`, our own dtor therefore being a no-op. (A moved-from `*this` must not be
268 * dereferenced.)
269 *
270 * @param target
271 * The object to which operator->() shall forward. Behavior undefined (assertion may trip) if null.
272 * @param mutex
273 * The mutex to hold for `*this` lifetime: the mutex -- the one and only one -- by which all
274 * access to `*target` is serialized (see class doc header for the typical setup).
275 * Must be non-null; must outlive `*this`.
276 */
277 explicit Locked_proxy(Target* target, Mutex* mutex);
278
279 /**
280 * Move constructor: `*this` acts as `src_moved` would-have (in particular its eventual destruction unlocks the
281 * mutex), while `src_moved` becomes a no-op object permanently.
282 *
283 * @param src_moved
284 * Source object. Its destructor shall do nothing after this returns; it must not be dereferenced.
285 */
287
288 /// Prohibit copying: for each `explicit` ctor invocation, there shall be exactly 1 unlocking dtor invocation.
289 Locked_proxy(const Locked_proxy&) = delete;
290
291 // Methods.
292
293 /// Prohibit copying (see `delete`d copy ctor).
295
296 /// Prohibit modifying existing `*this`; except that moving-from is enabled via the move ctor.
298
299 /**
300 * Returns pointer to the target object, the guarding mutex being locked by `*this` (do not use if moved-from).
301 * @return See above.
302 */
303 Target* operator->() const;
304
305 /**
306 * Returns reference to the target object, the guarding mutex being locked by `*this` (do not use if moved-from).
307 * Useful, e.g., to print the target (`os << *w_locked()`) or to pass it to a function taking a reference --
308 * keeping in mind the mutex is locked only until the end of the full expression (in typical unnamed-temporary
309 * use).
310 *
311 * @return See above.
312 */
313 Target& operator*() const;
314
315private:
316 // Data.
317
318 /// Holds the caller-supplied mutex locked throughout `*this` lifetime (unless moved-from: then holds nothing).
320
321 /// The target object to which operator->() forwards. Meaningless if `*this` is moved-from.
323}; // class Locked_proxy
324
325// Template implementations.
326
327// Scoped_setter template implementations.
328
329template<typename Value_t>
331 m_target_or_null(target),
332 m_saved_value(std::move(*m_target_or_null))
333{
334 *m_target_or_null = std::move(val_src_moved);
335}
336
337template<typename Value_t>
338Scoped_setter<Value_t>::Scoped_setter(Scoped_setter&& src_moved) : // =default might work fine but to be clear/certain:
339 m_target_or_null(src_moved.m_target_or_null),
340 m_saved_value(std::move(src_moved.m_saved_value))
341{
342 assert(m_target_or_null && "Should not be moving-from a thing that has already been moved-from.");
343
344 src_moved.m_target_or_null = nullptr;
345 // As promised: Now src_moved's dtor will no-op.
346}
347
348template<typename Value_t>
350{
351 if (m_target_or_null)
352 {
353 *m_target_or_null = std::move(m_saved_value);
354 }
355 // else { `*this` must have been moved-from. No-op. }
356}
357
358// Locked_proxy template implementations.
359
360template<typename Target_t, typename Mutex_t>
362 m_lock(*mutex), // Locks it (blocking as-needed).
363 m_target(target)
364{
365 assert(target && "Locked_proxy ctor: target must be non-null.");
366}
367
368template<typename Target_t, typename Mutex_t>
370
371template<typename Target_t, typename Mutex_t>
373{
374 return m_target;
375}
376
377template<typename Target_t, typename Mutex_t>
379{
380 return *m_target;
381}
382
383// Free function template (and/or constexpr) implementations.
384
385template<typename Time_unit, typename N_items>
386double to_mbit_per_sec(N_items items_per_time, size_t bits_per_item)
387{
388 /* Let there be U/W seconds per Time_unit. Then the following holds:
389 *
390 * items_per_time items/Time_units * W/U Time_units/second * bits_per_item bits/item
391 * * 1/(1000*1000) megabits/bits
392 * = items_per_time * W / U * bits_per_item / 1000 / 1000 megabits/second.
393 *
394 * Notice the units work out. W and U are conveniently available in Time_unit::period, which is a boost::ratio. */
395
396 return
397 /* This zealously converts everything to double ASAP to avoid overflow. Could probably speed things up a bit
398 * by postponing some of those conversions until after some integer multiplications, but then overflows could
399 * creep in. It's best not to assume too much about the values of den and num, as this function is meant to
400 * be rather adaptable to various situations. I did try to avoid unnecessary divisions though in favor of
401 * multiplications, sort of guessing the latter are faster. Or not... *shrug*. */
402 double(items_per_time) * double(bits_per_item) * double(Time_unit::period::den)
403 / (double(Time_unit::period::num) * double(1000 * 1000));
404}
405
406template<typename Integer, typename Integer2>
407constexpr Integer ceil_div(Integer dividend, Integer2 divisor)
408{
409 // ceil(A : B) = (A + B - 1) / B, where : is floating point division, while / is integer division.
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.");
412
413 // assert(dividend >= 0); // Cannot do that (or throw in C++17, can in C++20; @todo) in constexpr.
414 // assert(divisor > 0); // Ditto.
415
416 return (dividend + static_cast<Integer>(divisor) - static_cast<Integer>(1)) / static_cast<Integer>(divisor);
417 /* (Could one do further bitwise trickery? Perhaps but let optimizer do it. Wouldn't optimizer also just
418 * optimize a literal floating-point `ceil(a / b)`? Well, no. Probably not. So we wrote this function.) */
419}
420
421template<typename Integer, typename Integer2>
422constexpr Integer round_to_multiple(Integer dividend, Integer2 unit)
423{
424 return static_cast<Integer>(unit) * ceil_div(dividend, unit);
425}
426
427template<uint64_t POSITIVE_INT>
428constexpr bool is_power_of_two()
429{
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);
432}
433
434constexpr size_t max_align_sz()
435{
436 return alignof(std::max_align_t);
437}
438
439template<typename Data, size_t ALIGN_SZ>
440constexpr size_t aligned_sz_of()
441{
442 static_assert(is_power_of_two<ALIGN_SZ>(), "ALIGN_SZ must be one of 1, 2, 4, 8, ....");
443 return round_to_multiple(sizeof(Data), ALIGN_SZ);
444}
445
446template<typename Prefix, typename Data, size_t ALIGN_SZ>
447Prefix* aligned_prefix_before(Data* data_ptr)
448{
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.");
452
453 return reinterpret_cast<Prefix*>
454 (data_byte_ptr - aligned_sz_of<Prefix, ALIGN_SZ>());
455}
456
457template<typename Prefix, typename Data, size_t ALIGN_SZ>
458const Prefix* aligned_prefix_before(const Data* data_ptr)
459{
460 return aligned_prefix_before<Prefix, Data, ALIGN_SZ>(const_cast<Data*>(data_ptr));
461}
462
463template<typename Data, typename Prefix, size_t ALIGN_SZ>
464Data* after_aligned_prefix(Prefix* prefix_ptr)
465{
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.");
469
470 return reinterpret_cast<Data*>
471 (prefix_byte_ptr + aligned_sz_of<Prefix, ALIGN_SZ>());
472}
473
474template<typename Data, typename Prefix, size_t ALIGN_SZ>
475const Data* after_aligned_prefix(const Prefix* prefix_ptr)
476{
477 return after_aligned_prefix<Data, Prefix, ALIGN_SZ>(const_cast<Prefix*>(prefix_ptr));
478}
479
480template<typename T>
481bool in_closed_range(T const & min_val, T const & val, T const & max_val)
482{
483 // This writes "(min_val <= val) && (val <= max_val)" by using only <, to support the greatest number of types.
484 return ((min_val < val) || (!(val < min_val))) &&
485 ((val < max_val) || (!(max_val < val)));
486}
487
488template<typename T>
489bool in_open_closed_range(T const & min_val, T const & val, T const & max_val)
490{
491 // This writes "(min_val < val) && (val <= max_val)" by using only <, to support the greatest number of types.
492 return (min_val < val) &&
493 ((val < max_val) || (!(max_val < val)));
494}
495
496template<typename T>
497bool in_closed_open_range(T const & min_val, T const & val, T const & max_val)
498{
499 // This writes "(val < max_val) && (min_val <= val)" by using only <, to support the greatest number of types.
500 return (val < max_val) &&
501 ((min_val < val) || (!(val < min_val)));
502}
503
504template<typename T>
505bool in_open_open_range(T const & min_val, T const & val, T const & max_val)
506{
507 return (min_val < val) && (val < max_val);
508}
509
510template<typename Container>
511bool key_exists(const Container& container, const typename Container::key_type& key)
512{
513 return container.find(key) != container.end();
514}
515
516template<typename Cleanup_func>
518{
519 /* This trick, from shared_ptr or bind Boost docs (if I recall correctly), uses shared_ptr's deleter feature. The
520 * Auto_cleanup gains "ownership" of null pointer, purely for the purpose of running a deleter on it when the object
521 * goes out of scope sometime later. */
522 return Auto_cleanup{nullptr,
523 [func = std::move(func)](auto) { func(); }};
524}
525
526template<typename Minuend, typename Subtrahend>
527bool subtract_with_floor(Minuend* minuend, const Subtrahend& subtrahend, const Minuend& floor)
528{
529 assert(minuend);
530
531 /* Basically just avoid implicit conversions and anything that mind overflow or underflow.
532 * The one underflow we allow is the subtraction of `floor`: doc header says keep `floor` small.
533 * So it's their problem if it's not. */
534
535 const Minuend converted_subtrahend = Minuend{subtrahend};
536
537 // min - sub <= floor <===> min - floor <= sub.
538 if ((*minuend - floor) <= converted_subtrahend)
539 {
540 *minuend = floor;
541 return false;
542 }
543 // else
544 *minuend -= converted_subtrahend;
545 return true;
546}
547
548template<typename From, typename To>
549size_t size_unit_convert(From num_froms)
550{
551 return ((num_froms * sizeof(From)) + sizeof(To) - 1) / sizeof(To);
552}
553
554template<typename... T>
555void feed_args_to_ostream(std::ostream* os, T&&... ostream_args)
556{
557 (*os << ... << std::forward<T>(ostream_args));
558}
559
560template<typename... T>
561void ostream_op_to_string(std::string* target_str, T&&... ostream_args)
562{
563 using std::flush;
564
565 /* Pushes characters directly onto an `std::string`, instead of doing so into an `ostringstream` and then getting it
566 * by copy via `ostringstream::copy()`. This is for performance and may make a large difference
567 * overall, if this is used in logging for example. However, Thread_local_string_appender accomplishes
568 * better performance still and some other features. */
569 String_ostream os{target_str};
570 feed_args_to_ostream(&(os.os()), std::forward<T>(ostream_args)...);
571 os.os() << flush;
572}
573
574template<typename... T>
575std::string ostream_op_string(T&&... ostream_args)
576{
577 using std::string;
578
579 string result;
580 ostream_op_to_string(&result, std::forward<T>(ostream_args)...);
581 return result;
582}
583
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)
588{
589 size_t idx = 0;
590 for (const auto& src_element : src_seq)
591 {
592 (*target_map)[src_element] = idx_to_map_val_func(idx);
593 ++idx;
594 }
595}
596
597template<typename Map, typename Sequence>
598void sequence_to_inverted_lookup_map(Sequence const & src_seq, Map* target_map)
599{
600 sequence_to_inverted_lookup_map(src_seq, target_map, [](size_t idx) -> size_t
601 {
602 return idx;
603 });
604}
605
606template<typename Const_buffer_sequence>
607std::ostream& buffers_to_ostream(std::ostream& os,
608 const Const_buffer_sequence& data,
609 const std::string& indentation,
610 size_t bytes_per_line)
611{
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;
616 using std::isprint;
617
618 /* This sweet type will iterate over the buffer sequence (jumping between contained buffers, if ther are > 1).
619 * If `Bufs_iter it`, then *it is of type uint8_t. */
620 using Bufs_iter = buffers_iterator<Const_buffer_sequence, uint8_t>;
621
622 constexpr size_t BYTES_PER_LINE_DEFAULT = 16;
623 bool single_line_mode = false;
624 if (bytes_per_line == 0)
625 {
626 bytes_per_line = BYTES_PER_LINE_DEFAULT;
627 }
628 else if (bytes_per_line == size_t(-1))
629 {
630 /* Firstly just pretend exactly the bytes in the whole input = "max" bytes per line.
631 * This accomplishes the bulk of what "single-line mode" means. */
632 bytes_per_line = buffer_size(data);
633 // A bit slow potentially to need to enumerate all scattered buffers. Eh. Contract said we should be assumed slow.
634
635 // The rest of what it means is lacking a newline at the end in single-line mode. So just remember that part.
636 single_line_mode = true;
637 }
638
639 // Ensure format settings return to their previous values subsequently.
640 ios_flags_saver flags_saver{os};
641 ios_fill_saver fill_saver{os};
642 ios_width_saver width_saver{os};
643
644 /* Set formatting and output numeric value (hex) of first byte.
645 * @todo Is there a way to write this with manipulators too? */
646 os.setf(std::ios::right | std::ios::hex, std::ios::adjustfield | std::ios::basefield);
647 os << std::setfill('0');
648
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;
652 /* Advancing of cur_byte_it occurs within body of loop. */)
653 {
654 // The for() loop around us guarantees there is at least this first byte. Print the numeric value.
655 os << indentation << '['
656 << std::setw(2) << int(*cur_byte_it); // Numeric value in hex.
657
658 // Repeat for remaining bytes left in this line. Stop at bytes/line limit, or if reached end of buffers.
659 size_t n_bytes_printed;
660 for ((n_bytes_printed = 1), ++cur_byte_it; // Account for printing that first byte above.
661 (n_bytes_printed != bytes_per_line) && (cur_byte_it != end_byte_it);
662 ++cur_byte_it, ++n_bytes_printed)
663 {
664 os << ' ' << std::setw(2) << int(*cur_byte_it); // Numeric value in hex.
665 }
666
667 // Spaces as if rest of line still had a few ghost values to print (space + 2 spaces for the hex digits).
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)
671 {
672 os << " ";
673 }
674
675 // Backtrack and print those same bytes -- this time as printable characters (when printable, else dots).
676 cur_byte_it -= n_bytes_printed;
677
678 os << '|';
679 for (size_t n_chars_printed = 0;
680 n_chars_printed != n_bytes_printed;
681 ++cur_byte_it, ++n_chars_printed)
682 {
683 char c = *cur_byte_it;
684 os << (isprint(c) ? c : '.');
685 }
686 os << ']';
687 if (!single_line_mode)
688 {
689 os << '\n';
690 }
691 } // for (cur_byte_it)
692
693 return os;
694
695 // The formatting changes will be restored here as the savers exit scope.
696} // buffers_to_ostream()
697
698template<typename Const_buffer_sequence>
699std::string buffers_dump_string(const Const_buffer_sequence& data, const std::string& indentation,
700 size_t bytes_per_line)
701{
702 using std::flush;
703 using std::string;
704
705 // See comment in ostream_op_to_string() which applies here too (re. perf).
706
707 string target_str;
708 String_ostream os{&target_str};
709 buffers_to_ostream(os.os(), data, indentation, bytes_per_line);
710 os.os() << flush;
711
712 return target_str;
713}
714
715template<typename Enum>
716Enum istream_to_enum(std::istream* is_ptr, Enum enum_default, Enum enum_sentinel,
717 bool accept_num_encoding, bool case_sensitive,
718 Enum enum_lowest)
719{
720 using boost::lexical_cast;
721 using boost::bad_lexical_cast;
722 using boost::algorithm::equals;
723 using boost::algorithm::is_iequal;
724 using std::locale;
725 using std::string;
726 using std::isdigit;
727 using std::isalnum;
728 using Traits = std::char_traits<char>;
729 using enum_t = std::underlying_type_t<Enum>;
730
731 // Reminder: There are various assumptions about Enum this takes for granted; behavior undefined otherwise.
732
733 assert(enum_t(enum_lowest) >= 0); // Otherwise we'd have to allow '-' (minus sign), and we'd... just rather not.
734 auto& is = *is_ptr;
735 const is_iequal i_equal_func{locale::classic()};
736
737 // Read into `token` until (and not including) the first non-alphanumeric/underscore character or stream end.
738 string token;
739 char ch;
740 while (((ch = is.peek()) != Traits::eof()) && (isalnum(ch) || (ch == '_')))
741 {
742 token += ch;
743 is.get();
744 }
745
746 Enum val = enum_default;
747
748 if (!token.empty())
749 {
750 if (accept_num_encoding && isdigit(token.front())) // Hence ostream<< shouldn't serialize a digit-leading value.
751 {
752 enum_t num_enum;
753 try
754 {
755 num_enum = lexical_cast<enum_t>(token);
756 // This assumes a vanilla enum integer value ordering.
757 if ((num_enum >= enum_t(enum_sentinel) || (num_enum < enum_t(enum_lowest))))
758 {
759 num_enum = enum_t(enum_default);
760 }
761 val = Enum{num_enum};
762 }
763 catch (const bad_lexical_cast&)
764 {
765 assert(val == enum_default);
766 }
767 } // if (accept_num_encoding && isdigit())
768 else // if (!(accept_num_encoding && isdigit()))
769 {
770 enum_t idx;
771 // This assumes a vanilla enum integer value ordering within this [closed range].
772 for (idx = enum_t(enum_lowest); idx != enum_t(enum_sentinel); ++idx)
773 {
774 const auto candidate = Enum{idx};
775 /* Note -- lexical_cast<string>(Enum) == (operator<<(ostringstream&, Enum)).str() -- the symbolic
776 * encoding of Enum (as we promised to accept, case-[in]sensitively), not the numeric encoding. The numeric
777 * encoding is checked-for in the `if (accept_num_encoding...)` branch above using a non-looping technique. */
778 if (case_sensitive ? equals(token, lexical_cast<string>(candidate))
779 : equals(token, lexical_cast<string>(candidate), i_equal_func))
780 {
781 val = candidate;
782 break;
783 }
784 }
785 assert((idx != enum_t(enum_sentinel)) || (val == enum_default));
786 } // else if (!(accept_num_encoding && isdigit()))
787 } // if (!token.empty())
788
789 return val;
790} // istream_to_enum()
791
792template<typename T, typename... Ctor_args>
793T* construct_at(T* obj, Ctor_args&&... ctor_args)
794{
795 using Value = T;
796
797 // Use placement-new expression used by C++20's construct_at() per cppreference.com.
798 return ::new (const_cast<void*>
799 (static_cast<void const volatile*>
800 (obj)))
801 Value(std::forward<Ctor_args>(ctor_args)...);
802 /* Careful -^-... Value{} would be preferred in most non-generic code, but generically as here it can mess things
803 * up by unintentionally invoking an initializer-list ctor form, if one exists, and the types line up just so
804 * (as when Value is, say, vector<size_t>, and one passes-in a single integer). Use Value() to avoid
805 * any such surprises. */
806}
807
808template<typename T>
810{
811 using Value = T;
812
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().");
817
818 // Similar to the above but...
819 return ::new (const_cast<void*>
820 (static_cast<void const volatile*>
821 (obj)))
822 Value; // ...default-initialize: no `()`. E.g., a struct of `int`s won't have them zeroed by this.
823}
824
825} // namespace flow::util
A simple locking-proxy class template (execute-around-pointer idiom): at construction locks the given...
Definition: util.hpp:249
Mutex_t Mutex
Alias for template parameter.
Definition: util.hpp:257
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...
Definition: util.hpp:372
Target_t Target
Alias for template parameter.
Definition: util.hpp:254
Target * m_target
The target object to which operator->() forwards. Meaningless if *this is moved-from.
Definition: util.hpp:322
Lock_guard< Mutex > m_lock
Holds the caller-supplied mutex locked throughout *this lifetime (unless moved-from: then holds nothi...
Definition: util.hpp:319
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.
Definition: util.hpp:361
Target & operator*() const
Returns reference to the target object, the guarding mutex being locked by *this (do not use if moved...
Definition: util.hpp:378
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...
Definition: util.hpp:50
virtual ~Null_interface()=0
Boring virtual destructor.
A simple RAII-pattern class template that, at construction, sets the specified location in memory to ...
Definition: util.hpp:150
Value_t Value
Alias for template parameter.
Definition: util.hpp:155
Value * m_target_or_null
Target object location; see ctors; if null then this is a moved-from Scoped_setter that intentionally...
Definition: util.hpp:205
Value m_saved_value
If and only if m_target_or_null is non-null, this saves *m_target_or_null. Otherwise meaningless.
Definition: util.hpp:208
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...
Definition: util.hpp:349
Scoped_setter(Value *target, Value &&val_src_moved)
Post-condition: *target contains was val_src_moved contained at ctor entry; and the destructor invoca...
Definition: util.hpp:330
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,...
Definition: util.hpp:497
constexpr size_t aligned_sz_of()
Compile-time calculation: Given a type Data returns the minimum size, in bytes, of a hypothetical buf...
Definition: util.hpp:440
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...
Definition: util.hpp:511
constexpr bool is_power_of_two()
Compile-time check: true if and only if the given positive integer is a power of two: 1,...
Definition: util.hpp:428
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...
Definition: util.hpp:586
T * default_init_at(T *obj)
Superficially similar to construct_at(), this default-initializes the T at the given location; this c...
Definition: util.hpp:809
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...
Definition: util.hpp:386
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.
Definition: util.hpp:699
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.
Definition: util.hpp:716
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.
Definition: util.hpp:555
bool subtract_with_floor(Minuend *minuend, const Subtrahend &subtrahend, const Minuend &floor)
Performs *minuend -= subtrahend, subject to a floor of floor.
Definition: util.hpp:527
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...
Definition: util.hpp:422
boost::unique_lock< Mutex > Lock_guard
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of tha...
Definition: util_fwd.hpp:298
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...
Definition: util.hpp:549
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,...
Definition: util.hpp:505
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,...
Definition: util.hpp:489
constexpr size_t max_align_sz()
Compile-time value: the most stringent alignment in the target architecture.
Definition: util.hpp:434
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,...
Definition: util.hpp:447
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,...
Definition: util.hpp:561
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,...
Definition: util.hpp:607
Data * after_aligned_prefix(Prefix *prefix_ptr)
The reverse of aligned_prefix_before(): given a prefix pointer, returns the base area following it.
Definition: util.hpp:464
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.
Definition: util.hpp:481
boost::shared_ptr< void > Auto_cleanup
Helper type for setup_auto_cleanup().
Definition: util_fwd.hpp:238
Auto_cleanup setup_auto_cleanup(Cleanup_func &&func)
Provides a way to execute arbitrary (cleanup) code at the exit of the current block.
Definition: util.hpp:517
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...
Definition: util.hpp:575
T * construct_at(T *obj, Ctor_args &&... ctor_args)
std::construct_at() equivalent; unavailable until C++20, so here it is.
Definition: util.hpp:793
constexpr Integer ceil_div(Integer dividend, Integer2 divisor)
Returns the result of the given non-negative integer divided by a positive integer,...
Definition: util.hpp:407
Useful as a no-unique-address private member to make a type noncopyable while keeping that type an ag...
Definition: util.hpp:79
Noncopyable(const Noncopyable &)=delete
Forbid copying.
void operator=(const Noncopyable &)=delete
Forbid copying.
Noncopyable()=default
Makes it possible to instantiate.