source: branches/stable/CORE/arb_misc.cxx

Last change on this file was 18634, checked in by westram, 3 years ago
File size: 15.4 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
315static void drop_unused_ARB_ERROR() {
316    ARB_ERROR e0;
317}
318static void drop_ARB_ERROR() {
319    ARB_ERROR e1 = SOME_ERROR;
320}
321static void overwrite_ARB_ERROR() {
322    ARB_ERROR e1 = SOME_ERROR;
323    ARB_ERROR e2 = SOME_ERROR;
324
325    e1 = e2;           // expect this command to trigger assertion
326    ARB_SIGSEGV(true); // -> this is never reached
327}
328
329static ARB_ERROR forwardError(ARB_ERROR err) {
330    return err;
331}
332static ARB_ERROR annotateError(ARB_ERROR err) {
333    if (err) {
334        ARB_ERROR ann_err = GBS_global_string("annotated '%s'", err.deliver());
335        err               = ann_err;
336    }
337    return err;
338}
339
340void TEST_misuse_ARB_ERROR_crashtest() {
341    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_unused_ARB_ERROR);
342    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ARB_ERROR);
343    TEST_EXPECT_CODE_ASSERTION_FAILS(overwrite_ARB_ERROR);
344}
345void TEST_ARB_ERROR() {
346    // unused error:
347    {
348        ARB_ERROR e0;
349        TEST_EXPECT_NULL(e0.deliver());
350    }
351
352    // simple error delivery:
353    {
354        ARB_ERROR e1 = SOME_ERROR;
355        TEST_EXPECT_EQUAL(e1.deliver(), SOME_ERROR);
356    }
357
358    // deliver reassigned error (copy ctor):
359    {
360        ARB_ERROR e1 = SOME_ERROR;
361        ARB_ERROR e2(e1);
362        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
363    }
364    // deliver reassigned error (also copy ctor):
365    {
366        ARB_ERROR e1 = SOME_ERROR;
367        ARB_ERROR e2 = e1; // assign in definition == copy-ctor
368        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
369    }
370    // deliver reassigned error (op=):
371    {
372        ARB_ERROR e1 = SOME_ERROR;
373        ARB_ERROR e2;
374        e2 = e1; // real assignment
375        TEST_EXPECT_EQUAL(e2.deliver(), SOME_ERROR);
376    }
377
378    // deliver error forwarded through function:
379    {
380        ARB_ERROR e0;
381        ARB_ERROR e1 = SOME_ERROR;
382        ARB_ERROR f0 = forwardError(e0);
383        ARB_ERROR f1 = forwardError(e1);
384        TEST_EXPECT_NULL(f0.deliver());
385        TEST_EXPECT_EQUAL(f1.deliver(), SOME_ERROR);
386    }
387    // forward and annotate error:
388    {
389        ARB_ERROR e0;
390        ARB_ERROR e1 = SOME_ERROR;
391        ARB_ERROR a0 = annotateError(e0);
392        ARB_ERROR a1 = annotateError(e1);
393        TEST_EXPECT_NULL(a0.deliver());
394        TEST_EXPECT_EQUAL(a1.deliver(), "annotated 'whatever'");
395    }
396}
397
398typedef ErrorOr<int> ErrorOrInt;
399
400static void drop_ErrorOr0() {
401    ErrorOrInt e(NULp, 42);
402}
403static void drop_ErrorOr1() {
404    ErrorOrInt e(SOME_ERROR, 0);
405}
406static void retrieve_unchecked_ErrorOr0() {
407    ErrorOrInt e(NULp, 21);
408    int v = e.getValue(); // getValue w/o previous mandatory hasError
409    TEST_EXPECT_EQUAL(v, 21);
410}
411static void retrieve_unchecked_ErrorOr1() {
412    ErrorOrInt e(SOME_ERROR, 0);
413    ARB_ERROR  ae = e.getError(); // getError w/o previous mandatory hasError
414    TEST_REJECT_NULL(ae.deliver());
415}
416
417static ErrorOrInt generateErrorOr(int i) {
418    return ErrorOrInt(NULp, i);
419}
420static ErrorOrInt generateErrorOr(const char *msg) {
421    return ErrorOrInt(msg, -1);
422}
423
424void TEST_misuse_ErrorOr_crashtest() {
425    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ErrorOr0);
426    TEST_EXPECT_CODE_ASSERTION_FAILS(drop_ErrorOr1);
427    TEST_EXPECT_CODE_ASSERTION_FAILS(retrieve_unchecked_ErrorOr0);
428    TEST_EXPECT_CODE_ASSERTION_FAILS(retrieve_unchecked_ErrorOr1);
429}
430void TEST_ErrorOr() {
431    // simple uses (create, check, use + destroy):
432    {
433        ErrorOrInt e(SOME_ERROR, 0);
434        TEST_EXPECT(e.hasError());
435        TEST_EXPECT_EQUAL(e.getError().deliver(), SOME_ERROR);
436    }
437    {
438        ErrorOrInt v(NULp, 7);
439        TEST_EXPECT(v.hasValue());
440        TEST_EXPECT_EQUAL(v.getValue(), 7);
441    }
442
443    // test copy-construction:
444    {
445        ErrorOrInt e(SOME_ERROR, 0);
446        ErrorOrInt c = e;
447        TEST_EXPECT(c.hasError());
448        TEST_EXPECT_EQUAL(c.getError().deliver(), SOME_ERROR);
449    }
450    {
451        ErrorOrInt v(NULp, 17);
452        ErrorOrInt c = v;
453        TEST_EXPECT(c.hasValue());
454        TEST_EXPECT_EQUAL(c.getValue(), 17);
455    }
456    {
457        ErrorOrInt v = generateErrorOr(17);
458        TEST_EXPECT(v.hasValue());
459        TEST_EXPECT_EQUAL(v.getValue(), 17);
460    }
461    {
462        ErrorOrInt e = generateErrorOr(SOME_ERROR);
463        TEST_EXPECT(e.hasError());
464        TEST_EXPECT_EQUAL(e.getError().deliver(), SOME_ERROR);
465    }
466}
467
468#endif // UNIT_TESTS
469
470// --------------------------------------------------------------------------------
471
Note: See TracBrowser for help on using the repository browser.