Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
utils.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "utils.hh"
3#include "platform.hh"
4#include "strings.hh"
5#include "memory.hh"
6#include "unicode.hh"
7#include "internal.hh"
8#include <signal.h>
9#include <sys/time.h>
10#include <poll.h>
11#include <fcntl.h>
12#include <cmath>
13#include <atomic>
14#include <unistd.h>
15
16namespace Ase {
17
18// == Debugging ==
20bool ase_fatal_warnings = false;
21static const char*
22getenv_ase_debug()
23{
24 // cache $ASE_DEBUG and setup debug_any_enabled;
25 static const char *const ase_debug = [] {
26 const char *const d = getenv ("ASE_DEBUG");
27 ase_debugging_enabled = d && d[0];
28 ase_fatal_warnings = string_to_bool (String (string_option_find_value (d, "fatal-warnings", "0", "0", true)));
29 // ase_sigquit_on_abort = string_to_bool (String (string_option_find_value (d, "sigquit-on-abort", "0", "0", true)));
30 // ase_backtrace_on_error = string_to_bool (String (string_option_find_value (d, "backtrace", "0", "0", true)));
31 return d;
32 }();
33 return ase_debug;
34}
35
37bool
38debug_key_enabled (const char *conditional)
39{
40 const std::string_view sv = string_option_find_value (getenv_ase_debug(), conditional, "0", "0", true);
41 return string_to_bool (String (sv));
42}
43
45bool
46debug_key_enabled (const ::std::string &conditional)
47{
48 return debug_key_enabled (conditional.c_str());
49}
50
53debug_key_value (const char *conditional)
54{
55 const std::string_view sv = string_option_find_value (getenv_ase_debug(), conditional, "", "", true);
56 return String (sv);
57}
58
60void
61debug_message (const char *cond, const std::string &message)
62{
63 return_unless (cond && debug_key_enabled (cond));
64 struct timeval tv = { 0, };
65 gettimeofday (&tv, NULL);
66 const char *const newline = !message.empty() && message.data()[message.size() - 1] == '\n' ? "" : "\n";
67 using namespace AnsiColors;
68 const std::string col = color (FG_CYAN, BOLD), reset = color (RESET);
69 const std::string ul = cond ? color (UNDERLINE) : "", nl = cond ? color (UNDERLINE_OFF) : "";
70 std::string sout;
71 sout += string_format ("%s%u.%06u ", col, tv.tv_sec, tv.tv_usec); // cyan timestamp
72 sout += string_format ("%s%s%s:", ul, cond ? cond : executable_name().c_str(), nl); // underlined cond
73 sout += string_format ("%s %s", reset, message); // normal print message
74 printerr ("%s%s", sout, newline);
75}
76
78void
79diag_flush (uint8 code, const String &txt)
80{
81 fflush (stdout); // preserve output ordering
82 fputs (txt.c_str(), code == 'e' ? stderr : stdout);
83 fflush (stderr); // some platforms (_WIN32) don't properly flush on '\n'
84}
85
89{
90 using namespace AnsiColors;
91 String prefix;
92 if (code == 'W')
93 prefix = color (FG_YELLOW) + "warning:" + color (RESET) + " ";
94 else if (code == 'F')
95 prefix = color (BG_RED, FG_WHITE, BOLD) + "error:" + color (RESET) + " ";
96 std::string executable = executable_path();
97 if (!executable.empty())
98 {
99 size_t sep = executable.find (" ");
100 if (sep != std::string::npos)
101 executable = executable.substr (0, sep); // strips electron command line args
102 sep = executable.rfind ("/");
103 if (sep != std::string::npos)
104 executable = executable.substr (sep + 1); // use simple basename
105 if (!executable.empty())
106 prefix = executable + ": " + prefix;
107 }
108 return prefix;
109}
110
111// == i18n & gettext ==
112const char*
113ase_gettext (const String &untranslated)
114{
115 CString translated = untranslated;
116 return translated.c_str(); // relies on global CString storage
117}
118
119// == Date & Time ==
120String
121now_strftime (const String &format)
122{
123 std::time_t t = std::time (nullptr);
124 char buffer[4096] = { 0, };
125 if (std::strftime (buffer, sizeof (buffer), format.c_str(), std::localtime (&t)))
126 return buffer;
127 return "";
128}
129
130// == MakeIcon ==
131namespace MakeIcon {
132
134IconString
135KwIcon (const String &keywords)
136{
137 static const String keyword_charset = string_set_ascii_alnum() + "_-";
138 String s = keywords;
139 return_unless (!s.empty(), {});
140 StringS words = string_split_any (keywords, " ,");
141 Aux::erase_all (words, [] (const String &word) {
142 if (word.empty()) return true;
143 if (!string_is_canonified (word, keyword_charset))
144 {
145 warning ("%s: invalid icon keyword: '%s'", __func__, word);
146 return true;
147 }
148 return false;
149 });
150 IconString is;
151 is.assign (string_join (", ", words));
152 if (!is.empty() && is.find (',') == is.npos)
153 is += ","; // ensure comma in keyword list
154 return is;
155}
156
159operator""_icon (const char *key, size_t)
160{
161 return KwIcon (key);
162}
163
166UcIcon (const String &unicode)
167{
168 std::vector<uint32_t> codepoints;
169 if (utf8_to_unicode (unicode, codepoints) > 3 ||
170 (codepoints.size() >= 1 && !unicode_is_character (codepoints[0])) ||
171 (codepoints.size() >= 2 && !unicode_is_character (codepoints[1])) ||
172 (codepoints.size() >= 3 && !unicode_is_character (codepoints[2])))
173 warning ("%s: invalid icon unicode: '%s'", __func__, unicode);
174 IconString is;
175 is.assign (unicode);
176 return is;
177}
178
181operator""_uc (const char *key, size_t)
182{
183 return UcIcon (key);
184}
185
188SvgIcon (const String &svgdata)
189{
190 return_unless (!svgdata.empty(), {});
191 if (!string_startswith (svgdata, "<svg") || !string_startswith (svgdata, "<SVG"))
192 warning ("%s: invalid svg icon: %s…", __func__, svgdata.substr (0, 40));
193 IconString is;
194 is.assign (svgdata);
195 return is;
196}
197
198} // MakeIcon
199
200// == EventFd ==
201EventFd::EventFd () :
202 fds { -1, -1 }
203{}
204
205int
207{
208 if (opened())
209 return 0;
210 ASE_UNUSED long nflags;
211#ifdef HAVE_SYS_EVENTFD_H
212 do
213 fds[0] = eventfd (0 /*initval*/, EFD_CLOEXEC | EFD_NONBLOCK);
214 while (fds[0] < 0 && (errno == EAGAIN || errno == EINTR));
215#else
216 int err;
217 do
218 err = pipe2 (fds, O_CLOEXEC | O_NONBLOCK);
219 while (err < 0 && (errno == EAGAIN || errno == EINTR));
220 if (fds[1] >= 0)
221 {
222 nflags = fcntl (fds[1], F_GETFL, 0);
223 assert_return (nflags & O_NONBLOCK, -1);
224 nflags = fcntl (fds[1], F_GETFD, 0);
225 assert_return (nflags & FD_CLOEXEC, -1);
226 }
227#endif
228 if (fds[0] >= 0)
229 {
230 nflags = fcntl (fds[0], F_GETFL, 0);
231 assert_return (nflags & O_NONBLOCK, -1);
232 nflags = fcntl (fds[0], F_GETFD, 0);
233 assert_return (nflags & FD_CLOEXEC, -1);
234 return 0;
235 }
236 return -errno;
237}
238
239int
240EventFd::inputfd () // fd for POLLIN
241{
242 return fds[0];
243}
244
245bool
247{
248 return inputfd() >= 0;
249}
250
251bool
253{
254 struct pollfd pfd = { inputfd(), POLLIN, 0 };
255 int presult;
256 do
257 presult = poll (&pfd, 1, -1);
258 while (presult < 0 && (errno == EAGAIN || errno == EINTR));
259 return pfd.revents != 0;
260}
261
262void
264{
265 int err;
266#ifdef HAVE_SYS_EVENTFD_H
267 do
268 err = eventfd_write (fds[0], 1);
269 while (err < 0 && errno == EINTR);
270#else
271 char w = 'w';
272 do
273 err = write (fds[1], &w, 1);
274 while (err < 0 && errno == EINTR);
275#endif
276 // EAGAIN occours if too many wakeups are pending
277}
278
279void
281{
282 int err;
283#ifdef HAVE_SYS_EVENTFD_H
284 eventfd_t bytes8;
285 do
286 err = eventfd_read (fds[0], &bytes8);
287 while (err < 0 && errno == EINTR);
288#else
289 char buffer[512]; // 512 is POSIX pipe atomic read/write size
290 do
291 err = read (fds[0], buffer, sizeof (buffer));
292 while (err == 512 || (err < 0 && errno == EINTR));
293#endif
294 // EAGAIN occours if no wakeups are pending
295}
296
297EventFd::~EventFd ()
298{
299#ifdef HAVE_SYS_EVENTFD_H
300 close (fds[0]);
301#else
302 close (fds[0]);
303 close (fds[1]);
304#endif
305 fds[0] = -1;
306 fds[1] = -1;
307}
308
309// == CustomDataContainer ==
310CustomDataContainer::CustomDataEntry&
311CustomDataContainer::custom_data_entry (VirtualBase *key)
312{
313 if (!custom_data_)
314 custom_data_ = std::make_unique<CustomDataS> (1);
315 assert_return (key != nullptr, *custom_data_->end());
316 for (auto &e : *custom_data_)
317 if (e.key == key)
318 return e;
319 custom_data_->push_back ({ .key = key, });
320 return custom_data_->back();
321}
322
324CustomDataContainer::custom_data_get (VirtualBase *key) const
325{
326 if (custom_data_)
327 for (auto &e : *custom_data_)
328 if (e.key == key)
329 return e;
330 static std::any dummy;
331 return dummy;
332}
333
334bool
335CustomDataContainer::custom_data_del (VirtualBase *key)
336{
337 if (custom_data_)
338 return Aux::erase_first (*custom_data_, [key] (auto &e) { return key == e.key; });
339 return 0;
340 static_assert (sizeof (CustomDataContainer) == sizeof (void*));
341}
342
343void
344CustomDataContainer::custom_data_destroy()
345{
346 return_unless (custom_data_);
347 while (!custom_data_->empty())
348 {
349 std::any old;
350 std::swap (old, custom_data_->back());
351 custom_data_->pop_back();
352 // destroy old *after* removal from vector
353 }
354}
355
356CustomDataContainer::~CustomDataContainer()
357{
358 custom_data_destroy();
359}
360
361} // Ase
362
363#include "testing.hh"
364
365namespace { // Anon
366
367TEST_INTEGRITY (utils_tests);
368static void
369utils_tests()
370{
371 using namespace Ase;
372 TASSERT (uint16_swap_le_be (0x1234) == 0x3412);
373 TASSERT (uint32_swap_le_be (0xe23456f8) == 0xf85634e2);
374 TASSERT (uint64_swap_le_be (0xf2345678a1b2c3d4) == 0xd4c3b2a1785634f2);
375 std::vector<float> fv { 1, -2, 0, -0.5, 2, -1 };
376 bool b;
377 b = Aux::contains (fv, [] (auto v) { return v == 9; }); TASSERT (b == false);
378 b = Aux::contains (fv, [] (auto v) { return v == 2; }); TASSERT (b == true);
379 size_t j;
380 j = Aux::erase_all (fv, [] (auto v) { return fabs (v) == 2; });
381 TASSERT (j == 2 && fv == (std::vector<float> { 1, 0, -0.5, -1 }));
382 j = Aux::erase_first (fv, [] (auto v) { return fabs (v) == 1; });
383 TASSERT (j == 1 && fv == (std::vector<float> { 0, -0.5, -1 }));
384}
385
386} // Anon
#define EAGAIN
T assign(T... args)
T c_str(T... args)
bool opened()
Indicates whether eventfd has been opened.
Definition utils.cc:246
void flush()
Clear pending wakeups.
Definition utils.cc:280
int inputfd()
Returns the file descriptor for POLLIN.
Definition utils.cc:240
bool pollin()
Checks whether events are pending.
Definition utils.cc:252
int open()
Opens the eventfd and returns -errno.
Definition utils.cc:206
void wakeup()
Wakeup polling end.
Definition utils.cc:263
close
T data(T... args)
T empty(T... args)
errno
fabs
fcntl
fflush
T find(T... args)
fputs
getenv
gettimeofday
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
Definition internal.hh:77
T localtime(T... args)
size_t erase_all(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase all elements for which pred() is true in vector or list.
Definition utils.hh:350
size_t erase_first(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase first element for which pred() is true in vector or list.
Definition utils.hh:337
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
bool debug_key_enabled(const char *conditional)
Check if conditional is enabled by $ASE_DEBUG.
Definition utils.cc:38
bool ase_fatal_warnings
Global boolean to cause the program to abort on warnings.
Definition utils.cc:20
String string_join(const String &junctor, const StringS &strvec)
Definition strings.cc:452
String diag_prefix(uint8 code)
Create prefix for warnings and errors.
Definition utils.cc:88
bool string_is_canonified(const String &string, const String &valid_chars)
Check if string_canonify() would modify string.
Definition strings.cc:90
uint8_t uint8
An 8-bit unsigned integer.
Definition cxxaux.hh:22
std::string executable_path()
Retrieve the path to the currently running executable.
Definition platform.cc:734
void diag_flush(uint8 code, const String &txt)
Handle stdout and stderr printing with flushing.
Definition utils.cc:79
size_t utf8_to_unicode(const char *str, uint32_t *codepoints)
Definition unicode.cc:221
constexpr bool unicode_is_character(uint32_t u)
Return whether u is not one of the 66 Unicode noncharacters.
Definition unicode.hh:67
const String & string_set_ascii_alnum()
Returns a string containing all of 0-9, A-Z and a-z.
Definition strings.cc:118
bool ase_debugging_enabled
Global boolean to reduce debugging penalty where possible.
Definition utils.cc:19
StringS string_split_any(const String &string, const String &splitchars, size_t maxn)
Definition strings.cc:365
void debug_message(const char *cond, const std::string &message)
Print a debug message, called from Ase::debug().
Definition utils.cc:61
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
std::string executable_name()
Retrieve the name part of executable_path().
Definition platform.cc:742
bool string_to_bool(const String &string, bool fallback)
Definition strings.cc:467
::std::string debug_key_value(const char *conditional)
Retrieve the value assigned to debug key conditional in $ASE_DEBUG.
Definition utils.cc:53
std::string_view string_option_find_value(const char *string, const char *feature, const char *fallback, const char *denied, bool matchallnone)
Low level option search, avoids dynamic allocations.
Definition strings.cc:1397
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
Definition strings.cc:846
poll
read
write
T rfind(T... args)
T size(T... args)
stdout
T strftime(T... args)
T substr(T... args)
T swap(T... args)
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.
Definition testing.hh:24
T time(T... args)
IconString SvgIcon(const String &svgdata)
Create an IconString consisting of an SVG string.
Definition utils.cc:188
IconString UcIcon(const String &unicode)
Create an IconString consisting of a single/double unicode character.
Definition utils.cc:166
IconString KwIcon(const String &keywords)
Create an IconString consisting of keywords.
Definition utils.cc:135