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