source: branches/help/CORE/arb_misc.cxx

Last change on this file was 18734, checked in by westram, 3 years ago
  • fix NDEBUG warnings:
    • conditionally compile functions used only with assertions.
    • NodeTextBuilder is Noncopyable!
    • declare NDS_Labeler final.
File size: 15.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : arb_misc.cxx                                      //
4//   Purpose   : misc that doesnt fit elsewhere                    //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2012   //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include "arb_misc.h"
13#include "arb_msg.h"
14#include "arb_file.h"
15#include "arb_string.h"
16
17#include <cmath>
18
19// AISC_MKPT_PROMOTE:#ifndef _GLIBCXX_CSTDLIB
20// AISC_MKPT_PROMOTE:#include <cstdlib>
21// AISC_MKPT_PROMOTE:#endif
22
23const char *GBS_readable_size(unsigned long long size, const char *unit_suffix) {
24    // return human readable size information
25    // returned string is maximal 6+strlen(unit_suffix) characters long
26    // (using "b" as 'unit_suffix' produces '### b', '### Mb' etc)
27
28    if (size<1000) return GBS_global_string("%llu %s", size, unit_suffix);
29
30    const char *units = "kMGTPEZY"; // kilo, Mega, Giga, Tera, ... should be enough forever
31    int i;
32
33    for (i = 0; units[i]; ++i) {
34        char unit = units[i];
35        if (size<1000*1024) {
36            double amount = size/(double)1024;
37            if (amount<10.0)  return GBS_global_string("%4.2f %c%s", amount+0.005, unit, unit_suffix);
38            if (amount<100.0) return GBS_global_string("%4.1f %c%s", amount+0.05, unit, unit_suffix);
39            return GBS_global_string("%i %c%s", (int)(amount+0.5), unit, unit_suffix);
40        }
41        size /= 1024; // next unit
42    }
43    return GBS_global_string("MUCH %s", unit_suffix);
44}
45
46const char *GBS_readable_timediff(size_t seconds) {
47    size_t mins  = seconds/60; seconds -= mins  * 60;
48    size_t hours = mins/60;    mins    -= hours * 60;
49    size_t days  = hours/24;   hours   -= days  * 24;
50
51    const int   MAXPRINT = 40;
52    int         printed  = 0;
53    static char buffer[MAXPRINT+1];
54
55    if (days>0)               printed += sprintf(buffer+printed, "%zud", days);
56    if (printed || hours>0)   printed += sprintf(buffer+printed, "%zuh", hours);
57    if (printed || mins>0)    printed += sprintf(buffer+printed, "%zum", mins);
58
59    printed += sprintf(buffer+printed, "%zus", seconds);
60
61    arb_assert(printed>0 && printed<MAXPRINT);
62
63    return buffer;
64}
65
66const char *ARB_float_2_ascii(const float f) {
67    /*! calculate the "best" ascii representation for float 'f'
68     *  - smaller conversion error is better
69     *  - shorter representation is better (for equal conversion errors)
70     */
71
72    const int   MAXSIZE = 50;
73    static char result[MAXSIZE];
74    char        buffer[MAXSIZE];
75
76    int   printed_e = snprintf(result, MAXSIZE, "%e", f); arb_assert(printed_e<MAXSIZE);
77    float back_e    = strtof(result, NULp);
78    float diff_e    = fabsf(f-back_e);
79
80    int   printed_g = snprintf(buffer, MAXSIZE, "%g", f); arb_assert(printed_g<MAXSIZE);
81    float back_g    = strtof(buffer, NULp);
82    float diff_g    = fabsf(f-back_g);
83
84    if (diff_g<diff_e || (diff_g == diff_e && printed_g<printed_e)) {
85        printed_e = printed_g;
86        back_e    = back_g;
87        diff_e    = diff_g;
88        memcpy(result, buffer, printed_g+1);
89    }
90
91    int   printed_f = snprintf(buffer, MAXSIZE, "%f", f); arb_assert(printed_f<MAXSIZE);
92    float back_f    = strtof(buffer, NULp);
93    float diff_f    = fabsf(f-back_f);
94
95    if (diff_f<diff_e || (diff_f == diff_e && printed_f<printed_e)) {
96        memcpy(result, buffer, printed_f+1);
97    }
98
99    return result;
100}
101
102const char *ARB_getenv_ignore_empty(const char *envvar) {
103    const char *result = getenv(envvar);
104    return (result && result[0]) ? result : NULp;
105}
106
107char *ARB_executable(const char *exe_name, const char *path) {
108    char       *buffer = ARB_alloc<char>(strlen(path)+1+strlen(exe_name)+1);
109    const char *start  = path;
110    int         found  = 0;
111
112    while (!found && start) {
113        const char *colon = strchr(start, ':');
114        int         len   = colon ? (colon-start) : (int)strlen(start);
115
116        memcpy(buffer, start, len);
117        buffer[len] = '/';
118        strcpy(buffer+len+1, exe_name);
119
120        found = GB_is_executablefile(buffer);
121        start = colon ? colon+1 : NULp;
122    }
123
124    char *executable = found ? ARB_strdup(buffer) : NULp;
125    free(buffer);
126    return executable;
127}
128
129// --------------------------------------------------------------------------------
130
131char ARB_path_contains_unwanted_chars(const char *path) {
132    if (strchr(path, ' ') != NULp) {
133        return ' ';
134    }
135    return 0;
136}
137
138void ARB_warn_about_unwanted_chars(const char *path, const char *path_description) {
139    // annoy user with warnings if paths contain unwanted characters.
140    //
141    // motivation: I tried to fix some scripts to correctly handle the case
142    // where arb is installed in a path containing spaces.
143    //
144    // Since that is a bottomless pit, I decided to deny spaces there.
145
146    char unwantedChar = ARB_path_contains_unwanted_chars(path);
147    if (unwantedChar) {
148        GB_warningf(
149            "arb may not work as expected, because\n"
150            "%s\n"
151            "  (='%s')\n"
152            "contains an unwanted character ('%c').",
153            path_description,
154            path,
155            unwantedChar);
156    }
157}
158
159// --------------------------------------------------------------------------------
160
161#ifdef UNIT_TESTS
162#ifndef TEST_UNIT_H
163#include <test_unit.h>
164#endif
165
166#if 0
167// simple test
168#define TEST_EXPECT_FLOAT_2_ASCII(f,a) TEST_EXPECT_EQUAL(ARB_float_2_ascii(f), a)
169#else
170// also test back-conversion (ascii->float->ascii) is stable
171#define TEST_EXPECT_FLOAT_2_ASCII(f,a) do{                          \
172        TEST_EXPECT_EQUAL(ARB_float_2_ascii(f), a);                 \
173        TEST_EXPECT_EQUAL(ARB_float_2_ascii(strtof(a, NULp)), a);   \
174    }while(0)
175#endif
176
177__ATTR__REDUCED_OPTIMIZE void TEST_float_2_ascii() {
178    TEST_EXPECT_FLOAT_2_ASCII(3.141592e+00, "3.141592");
179    TEST_EXPECT_FLOAT_2_ASCII(3.141592,     "3.141592");
180    TEST_EXPECT_FLOAT_2_ASCII(3.14159,      "3.14159");
181
182    TEST_EXPECT_FLOAT_2_ASCII(0.1,           "0.1");
183    TEST_EXPECT_FLOAT_2_ASCII(0.01,          "0.01");
184    TEST_EXPECT_FLOAT_2_ASCII(0.001,         "0.001");
185    TEST_EXPECT_FLOAT_2_ASCII(0.0001,        "0.0001");
186    TEST_EXPECT_FLOAT_2_ASCII(0.00001,       "1e-05");
187    TEST_EXPECT_FLOAT_2_ASCII(0.000001,      "1e-06");
188    TEST_EXPECT_FLOAT_2_ASCII(0.0000001,     "1e-07");
189    TEST_EXPECT_FLOAT_2_ASCII(0.00000001,    "1e-08");
190    TEST_EXPECT_FLOAT_2_ASCII(0.000000001,   "1e-09");
191    TEST_EXPECT_FLOAT_2_ASCII(0.0000000001,  "1e-10");
192    TEST_EXPECT_FLOAT_2_ASCII(0.00000000001, "1e-11");
193
194    TEST_EXPECT_FLOAT_2_ASCII(10,         "10");
195    TEST_EXPECT_FLOAT_2_ASCII(100,        "100");
196    TEST_EXPECT_FLOAT_2_ASCII(1000,       "1000");
197    TEST_EXPECT_FLOAT_2_ASCII(10000,      "10000");
198    TEST_EXPECT_FLOAT_2_ASCII(100000,     "100000");
199    TEST_EXPECT_FLOAT_2_ASCII(1000000,    "1e+06");
200    TEST_EXPECT_FLOAT_2_ASCII(10000000,   "1e+07");
201    TEST_EXPECT_FLOAT_2_ASCII(100000000,  "1e+08");
202    TEST_EXPECT_FLOAT_2_ASCII(1000000000, "1e+09");
203
204    TEST_EXPECT_FLOAT_2_ASCII(3141592,      "3.141592e+06");
205    TEST_EXPECT_FLOAT_2_ASCII(314159.2,     "3.141592e+05");
206    TEST_EXPECT_FLOAT_2_ASCII(31415.92,     "3.141592e+04");
207    TEST_EXPECT_FLOAT_2_ASCII(3141.592,     "3141.592041");
208    TEST_EXPECT_FLOAT_2_ASCII(3141.592041,  "3141.592041");
209    TEST_EXPECT_FLOAT_2_ASCII(314.1592,     "314.159210");
210    TEST_EXPECT_FLOAT_2_ASCII(314.159210,   "314.159210");
211    TEST_EXPECT_FLOAT_2_ASCII(31.41592,     "31.415920");
212    TEST_EXPECT_FLOAT_2_ASCII(3.141592,     "3.141592");
213    TEST_EXPECT_FLOAT_2_ASCII(.3141592,     "3.141592e-01");
214    TEST_EXPECT_FLOAT_2_ASCII(.03141592,    "3.141592e-02");
215    TEST_EXPECT_FLOAT_2_ASCII(.003141592,   "3.141592e-03");
216    TEST_EXPECT_FLOAT_2_ASCII(.0003141592,  "3.141592e-04");
217    TEST_EXPECT_FLOAT_2_ASCII(.00003141592, "3.141592e-05");
218    TEST_EXPECT_FLOAT_2_ASCII(M_PI,         "3.141593");
219
220    TEST_EXPECT_FLOAT_2_ASCII(1/2.0, "0.5");
221    TEST_EXPECT_FLOAT_2_ASCII(1/3.0, "3.333333e-01");
222    TEST_EXPECT_FLOAT_2_ASCII(1/4.0, "0.25");
223    TEST_EXPECT_FLOAT_2_ASCII(1/5.0, "0.2");
224    TEST_EXPECT_FLOAT_2_ASCII(1/6.0, "1.666667e-01");
225
226    TEST_EXPECT_FLOAT_2_ASCII(37550000.0,  "3.755e+07");
227    TEST_EXPECT_FLOAT_2_ASCII(3755000.0,   "3.755e+06");
228    TEST_EXPECT_FLOAT_2_ASCII(375500.0,    "375500");
229    TEST_EXPECT_FLOAT_2_ASCII(37550.0,     "37550");
230    TEST_EXPECT_FLOAT_2_ASCII(3755.0,      "3755");
231    TEST_EXPECT_FLOAT_2_ASCII(375.5,       "375.5");
232    TEST_EXPECT_FLOAT_2_ASCII(37.55,       "37.55");
233    TEST_EXPECT_FLOAT_2_ASCII(3.755,       "3.755");
234    TEST_EXPECT_FLOAT_2_ASCII(0.3755,      "0.3755");
235    TEST_EXPECT_FLOAT_2_ASCII(0.03755,     "0.03755");
236    TEST_EXPECT_FLOAT_2_ASCII(0.003755,    "0.003755");
237    TEST_EXPECT_FLOAT_2_ASCII(0.0003755,   "0.0003755");
238    TEST_EXPECT_FLOAT_2_ASCII(0.00003755,  "3.755e-05");
239    TEST_EXPECT_FLOAT_2_ASCII(0.000003755, "3.755e-06");
240
241    TEST_EXPECT_FLOAT_2_ASCII(1000.0*1000.0*1000.0,    "1e+09");
242    TEST_EXPECT_FLOAT_2_ASCII(25000.0*25000.0*25000.0, "1.5625e+13");
243}
244
245// ------------------------------------------------------------
246// test to ensure sanitizers work as expected
247
248#if 0
249void TEST_fail_address_sanitizer() {
250    static int array[5];
251    array[2] = 1;
252    array[5] = 1; // <- fails with AddressSanitizer
253
254    printf("array[5]=%i\n", array[5]);
255}
256#endif
257
258#if 0
259void TEST_fail_undef_sanitizer() {
260    // error below are not reported if AddressSanitizer bails out (TEST_fail_address_sanitizer)
261    int x  = 7;
262    int y1 = -1;
263
264    int s = x<<y1; // runtime error with ubsan: shift exponent -1 is negative (does not terminate)
265    printf("s=%i\n", s);
266
267    int o = INT_MAX;
268    int u = INT_MIN;
269    o++; // runtime error: signed integer overflow
270    u--; // runtime error: signed integer overflow
271    printf("o=%i u=%i\n", o, u);
272
273#if 0
274    int y2 = 0;
275    int z1 = x/y1;
276    int z2 = x/y2; // runtime error with ubsan: division by zero (terminates with SEGV; also w/o sanitizers)
277    printf("z1=%i z2=%i\n", z1, z2);
278#endif
279}
280#endif
281
282#if 0
283void TEST_fail_leak_sanitizer() {
284    int *p = new int[5]; // <- fails with LeakSanitizer (only reported if AddressSanitizer does not bail out (TEST_fail_address_sanitizer))
285    printf("p[3]=%i\n", p[3]);
286}
287#endif
288
289// ------------------------------------------------------------
290
291#include "StrUniquifier.h"
292
293void TEST_StrUniquifier() {
294    StrUniquifier uniq("->");
295    TEST_EXPECT_EQUAL(uniq.make_unique_key("hey"), "hey");
296    TEST_EXPECT_EQUAL(uniq.make_unique_key("hey"), "hey->2");
297    TEST_EXPECT_EQUAL(uniq.make_unique_key("Hey"), "Hey");
298    TEST_EXPECT_EQUAL(uniq.make_unique_key("Hey"), "Hey->2");
299
300    TEST_EXPECT_EQUAL(uniq.make_unique_key(""), "");
301    TEST_EXPECT_EQUAL(uniq.make_unique_key(""), "->2");
302
303    StrUniquifier fresh(".");
304    TEST_EXPECT_EQUAL(fresh.make_unique_key(""), "");
305    TEST_EXPECT_EQUAL(fresh.make_unique_key(""), ".2");
306}
307
308// ------------------------------------------------------------
309
310#include <arb_error.h>
311#include <ErrorOrType.h>
312
313static const char *SOME_ERROR = "whatever";
314
315#if defined(ASSERTION_USED)
316
317static void drop_unused_ARB_ERROR() {
318    ARB_ERROR e0;
319}
320static void drop_ARB_ERROR() {
321    ARB_ERROR e1 = SOME_ERROR;
322}
323static void overwrite_ARB_ERROR() {
324    ARB_ERROR e1 = SOME_ERROR;
325    ARB_ERROR e2 = SOME_ERROR;
326
327    e1 = e2;           // expect this command to trigger assertion
328    ARB_SIGSEGV(true); // -> this is never reached
329}
330
331#endif
332
333static ARB_ERROR forwardError(ARB_ERROR err) {
334    return err;
335}
336static ARB_ERROR annotateError(ARB_ERROR err) {
337    if (err) {
338        ARB_ERROR ann_err = GBS_global_string("annotated '%s'", err.deliver());
339        err               = ann_err;
340    }
341    return err;
342}
343
344void TEST_misuse_ARB_ERROR_crashtest() {
345    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_unused_ARB_ERROR);
346    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ARB_ERROR);
347    TEST_EXPECT_CODE_ASSERTION_FAILS(overwrite_ARB_ERROR);
348}
349void TEST_ARB_ERROR() {
350    // unused error:
351    {
352        ARB_ERROR e0;
353        TEST_EXPECT_NULL(e0.deliver());
354    }
355
356    // simple error delivery:
357    {
358        ARB_ERROR e1 = SOME_ERROR;
359        TEST_EXPECT_EQUAL(e1.deliver(), SOME_ERROR);
360    }
361
362    // deliver reassigned error (copy ctor):
363    {
364        ARB_ERROR e1 = SOME_ERROR;
365        ARB_ERROR e2(e1);
366        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
367    }
368    // deliver reassigned error (also copy ctor):
369    {
370        ARB_ERROR e1 = SOME_ERROR;
371        ARB_ERROR e2 = e1; // assign in definition == copy-ctor
372        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
373    }
374    // deliver reassigned error (op=):
375    {
376        ARB_ERROR e1 = SOME_ERROR;
377        ARB_ERROR e2;
378        e2 = e1; // real assignment
379        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
380    }
381
382    // deliver error forwarded through function:
383    {
384        ARB_ERROR e0;
385        ARB_ERROR e1 = SOME_ERROR;
386        ARB_ERROR f0 = forwardError(e0);
387        ARB_ERROR f1 = forwardError(e1);
388        TEST_EXPECT_NULL(f0.deliver());
389        TEST_EXPECT_EQUAL(f1.deliver(), SOME_ERROR);
390    }
391    // forward and annotate error:
392    {
393        ARB_ERROR e0;
394        ARB_ERROR e1 = SOME_ERROR;
395        ARB_ERROR a0 = annotateError(e0);
396        ARB_ERROR a1 = annotateError(e1);
397        TEST_EXPECT_NULL(a0.deliver());
398        TEST_EXPECT_EQUAL(a1.deliver(), "annotated 'whatever'");
399    }
400}
401
402typedef ErrorOr<int> ErrorOrInt;
403
404#if defined(ASSERTION_USED)
405
406static void drop_ErrorOr0() {
407    ErrorOrInt e(NULp, 42);
408}
409static void drop_ErrorOr1() {
410    ErrorOrInt e(SOME_ERROR, 0);
411}
412static void retrieve_unchecked_ErrorOr0() {
413    ErrorOrInt e(NULp, 21);
414    int v = e.getValue(); // getValue w/o previous mandatory hasError
415    TEST_EXPECT_EQUAL(v, 21);
416}
417static void retrieve_unchecked_ErrorOr1() {
418    ErrorOrInt e(SOME_ERROR, 0);
419    ARB_ERROR  ae = e.getError(); // getError w/o previous mandatory hasError
420    TEST_REJECT_NULL(ae.deliver());
421}
422
423#endif
424
425static ErrorOrInt generateErrorOr(int i) {
426    return ErrorOrInt(NULp, i);
427}
428static ErrorOrInt generateErrorOr(const char *msg) {
429    return ErrorOrInt(msg, -1);
430}
431
432void TEST_misuse_ErrorOr_crashtest() {
433    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ErrorOr0);
434    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ErrorOr1);
435    TEST_EXPECT_CODE_ASSERTION_FAILS(retrieve_unchecked_ErrorOr0);
436    TEST_EXPECT_CODE_ASSERTION_FAILS(retrieve_unchecked_ErrorOr1);
437}
438void TEST_ErrorOr() {
439    // simple uses (create, check, use + destroy):
440    {
441        ErrorOrInt e(SOME_ERROR, 0);
442        TEST_EXPECT(e.hasError());
443        TEST_EXPECT_EQUAL(e.getError().deliver(), SOME_ERROR);
444    }
445    {
446        ErrorOrInt v(NULp, 7);
447        TEST_EXPECT(v.hasValue());
448        TEST_EXPECT_EQUAL(v.getValue(), 7);
449    }
450
451    // test copy-construction:
452    {
453        ErrorOrInt e(SOME_ERROR, 0);
454        ErrorOrInt c = e;
455        TEST_EXPECT(c.hasError());
456        TEST_EXPECT_EQUAL(c.getError().deliver(), SOME_ERROR);
457    }
458    {
459        ErrorOrInt v(NULp, 17);
460        ErrorOrInt c = v;
461        TEST_EXPECT(c.hasValue());
462        TEST_EXPECT_EQUAL(c.getValue(), 17);
463    }
464    {
465        ErrorOrInt v = generateErrorOr(17);
466        TEST_EXPECT(v.hasValue());
467        TEST_EXPECT_EQUAL(v.getValue(), 17);
468    }
469    {
470        ErrorOrInt e = generateErrorOr(SOME_ERROR);
471        TEST_EXPECT(e.hasError());
472        TEST_EXPECT_EQUAL(e.getError().deliver(), SOME_ERROR);
473    }
474}
475
476#endif // UNIT_TESTS
477
478// --------------------------------------------------------------------------------
479
Note: See TracBrowser for help on using the repository browser.