Flow 1.0.1
Flow project: Full implementation reference.
util.cpp
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#include "flow/util/util.hpp"
20
21namespace flow::util
22{
23
24// Types.
25
26/**
27 * Internally used class that enables some of the activities of beautify_chrono_ostream() API. It's a locale facet,
28 * whose `virtual` API can be used to imbue onto an `ostream`;
29 * but even within our own internal code the only public API used is beautified_locale(), which
30 * returns a reference to a ready-made such `std::locale`.
31 *
32 * This facet enables the default `chrono` output behavior, except the "micro" short-form prefix
33 * is just "u" instead of the default Unicode Greek letter mu.
34 *
35 * ### Implementation notes ###
36 * How it does this is fairly self-explanatory, but finding out how to do it was annoyingly tricky due to the
37 * inconsistent/outdated boost.chrono docs which only partially explain boost.chrono I/O v2. Ultimately I had to
38 * find the actual example `french.cpp` (not its outdated v1 version directly inside the main doc text, in Boost 1.76);
39 * then cross-reference it with the Reference section that described the `duration_units_default` class.
40 */
42{
43public:
44 // Methods.
45
46 /**
47 * Returns a reference to a locale that -- if imbued onto an `ostream` --
48 * equals `std::locale::classic` except beautified by a Duration_units_beautified facet (as described in
49 * the class doc header).
50 *
51 * ### Thread safety and perf ###
52 * This (`static`) method is safe to call concurrently with itself. Internally it achieves this possibly by using a
53 * simple mutex. Since it is not expected one would call this frequently, let alone frequently enough for any
54 * contention to occur, we consider this acceptable.
55 *
56 * @return See above.
57 */
58 static const std::locale& beautified_locale();
59
60 /**
61 * Returns the prefix string for micro-units; namely the default thing for long-form format ("micro") but
62 * the non-default "u" for the short-form. Hence microseconds are either "microsecond[s]" or "us."
63 *
64 * ### Rationale ###
65 * This is `public` only so as to work as a `virtual` facet interface element invoked by STL `ostream` machinery upon
66 * imbuing `*this` onto a `locale`. Since Duration_units_beautified is not exposed as a flow::util public API,
67 * leaving this method `public` (to our internal code in this .cpp file only) is acceptable.
68 *
69 * @param style
70 * Either `name_format` or `symbol_format` (the latter being the short-form choice).
71 * @param period_tag
72 * Tag indicating this method is for the micro prefix.
73 * @return The prefix string.
74 */
75 typename boost::chrono::duration_units<char_type>::string_type
76 do_get_ratio_prefix(boost::chrono::duration_style style, boost::micro period_tag) const override;
77
78private:
79 // Types.
80
81 /// Short-hand for superclass.
83
84 // Constructors/destructor.
85
86 /// Constructs.
88
89 // Data.
90
91 /**
92 * Pointer returned by beautified_locale(), pointing to an object allocated exactly once; or null if that has not yet
93 * been called. The allocated object lives until the process exits.
94 */
95 static std::locale const * s_beautified_locale_or_null;
96
97 /// Helper to initialize #s_beautified_locale_or_null at most once.
98 static boost::once_flag s_beautified_locale_init_flag;
99}; // class Duration_units_beautified
100
101// Static initializers.
102
105
106// Implementations.
107
108// Local class implementations.
109
111 Duration_units_default(0) // Just use `refs == 0`. See boost::chrono docs, if you care to know more re. this detail.
112{
113 // That's all.
114}
115
116const std::locale& Duration_units_beautified::beautified_locale() // Static.
117{
118 using boost::call_once;
119 using std::locale;
120
121 // As advertised create (thread-safely) a locale that survives until the process exits.
123 [&]() { s_beautified_locale_or_null = new locale(locale::classic(), new Duration_units_beautified); });
124
127}
128
129boost::chrono::duration_units<char>::string_type Duration_units_beautified::do_get_ratio_prefix
130 (boost::chrono::duration_style style,
131 boost::micro period_tag) const // Virtual.
132{
133 // See Implementation in our class doc header for background.
134
135 if (style == boost::chrono::duration_style::symbol)
136 {
137 return "u"; // Normally this would be the Unicode string for the Greek letter mu, but we hate that stuff.
138 }
139 // else
140 return Duration_units_default::do_get_ratio_prefix(style, period_tag);
141}
142
143// Free function implementations.
144
146
147boost::chrono::microseconds time_since_posix_epoch()
148{
149 /* Get the # of microseconds since the advertised Epoch time point, which is explicitly what microsec_clock
150 * gives us, the latter being officially the most precise documented clock in boost.date_time. Then just
151 * make a boost.chrono duration<> out of that. */
152 using boost::posix_time::microsec_clock;
153 using boost::posix_time::ptime;
154 using boost::chrono::microseconds;
155 using boost::gregorian::date;
156 return microseconds((microsec_clock::universal_time() - ptime(date(1970, 1, 1)))
157 .total_microseconds());
158
159 /* I am using boost.date_time machinery to get the UTC time and then the # of usec since the
160 * exact epoch time point. Beyond dealing with calendars and the like, I find boost.date_time
161 * suspect and would always prefer boost.chrono, but my attempt to entirely use that for this
162 * failed. chrono::system_clock::now() actually returns the current time point from the correct
163 * clock, with the best possible resolution. That's great, but then the docs say that the epoch
164 * against which this is computed is not defined and to use system_clock::to_time_t() to
165 * convert now() to a count since the actual POSIX Epoch. Again, that's great, except that
166 * time_t is in seconds, which means the juicy as-high-res-as-possible time point is rounded
167 * to seconds, destroying information irretrievably. We need more resolution than seconds, so
168 * we've no choice but to rely on the crusty boost.date_time wall clock time.
169 *
170 * It doesn't matter that much... it's just style in this one little function. */
171}
172
173void beautify_chrono_ostream(std::ostream* os_ptr)
174{
175 using boost::chrono::symbol_format;
176
177 auto& os = *os_ptr;
178
179 /* We prefer all output of durations to be like "ms" and not "milliseconds."
180 * Also we hate the Unicode Greek mu prefix for micro, so we use custom facet to have something nicer. */
181
183 os << symbol_format;
184}
185
186size_t deep_size(const std::string& val)
187{
188 // We're following the loose pattern explained at the end of Async_file_logger::mem_cost().
189
190#if (!defined(__GNUC__)) || (!defined(__x86_64__))
191# error "An estimation trick below has only been checked with x64 gcc and clang. Revisit code for other envs."
192#endif
193
194 /* If it is long enough to not fit inside the std::string object itself
195 * (common optimization in STL: Small String Optimization), then it'll allocate a buffer in heap.
196 * We could even determine whether it actually happened here at runtime, but that wastes cycles
197 * (original use case is in log::Async_file_logger specifically where every cycle counts).
198 * Instead we've established experimentally that with default STL and clangs 4-17 and gccs 5-13
199 * SSO is active for .capacity() <= 15. @todo Check LLVM libc++ too. Prob same thing... SSO is well established. */
200 const auto sz = val.capacity();
201 return (sz <= 15) ? 0 : sz;
202}
203
204} // namespace flow::util
Internally used class that enables some of the activities of beautify_chrono_ostream() API.
Definition: util.cpp:42
boost::chrono::duration_units< char_type >::string_type do_get_ratio_prefix(boost::chrono::duration_style style, boost::micro period_tag) const override
Returns the prefix string for micro-units; namely the default thing for long-form format ("micro") bu...
Definition: util.cpp:130
static std::locale const * s_beautified_locale_or_null
Pointer returned by beautified_locale(), pointing to an object allocated exactly once; or null if tha...
Definition: util.cpp:95
static const std::locale & beautified_locale()
Returns a reference to a locale that – if imbued onto an ostream – equals std::locale::classic except...
Definition: util.cpp:116
static boost::once_flag s_beautified_locale_init_flag
Helper to initialize s_beautified_locale_or_null at most once.
Definition: util.cpp:98
virtual ~Null_interface()=0
Boring virtual destructor.
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
Definition: basic_blob.hpp:29
void beautify_chrono_ostream(std::ostream *os_ptr)
Sets certain chrono-related formatting on the given ostream that results in a consistent,...
Definition: util.cpp:173
size_t deep_size(const std::string &val)
Estimate of memory footprint of the given value, including memory allocated on its behalf – but exclu...
Definition: util.cpp:186
boost::chrono::microseconds time_since_posix_epoch()
Get the current POSIX (Unix) time as a duration from the Epoch time point.
Definition: util.cpp:147