source: trunk/CORE/arb_misc.cxx

Last change on this file was 19128, checked in by westram, 2 years ago
  • ARB_executable:
    • accept executables specified with full path.
    • document.
    • change variable type.
File size: 11.7 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    /*! Search for full executable.
109     * @param exe_name name of executable (e.g. 'arb_edit4').
110     *                 When specified with full path -> accepted as is.
111     * @param path colon separated list of directories (as contained in environment variable PATH)
112     * @return executable with full directory path
113     */
114
115    char       *buffer = ARB_alloc<char>(strlen(path)+1+strlen(exe_name)+1);
116    const char *start  = path;
117    bool        found  = false;
118
119    while (!found && start) {
120        const char *colon = strchr(start, ':');
121        int         len   = colon ? (colon-start) : (int)strlen(start);
122
123        memcpy(buffer, start, len);
124        buffer[len] = '/';
125        strcpy(buffer+len+1, exe_name);
126
127        found = GB_is_executablefile(buffer);
128        start = colon ? colon+1 : NULp;
129    }
130
131    if (!found && GB_is_executablefile(exe_name)) { // accept 'exe_name' specified with full path
132        strcpy(buffer, exe_name);
133        found = true;
134    }
135
136    char *executable = found ? ARB_strdup(buffer) : NULp;
137    free(buffer);
138    return executable;
139}
140
141// --------------------------------------------------------------------------------
142
143char ARB_path_contains_unwanted_chars(const char *path) {
144    if (strchr(path, ' ') != NULp) {
145        return ' ';
146    }
147    return 0;
148}
149
150void ARB_warn_about_unwanted_chars(const char *path, const char *path_description) {
151    // annoy user with warnings if paths contain unwanted characters.
152    //
153    // motivation: I tried to fix some scripts to correctly handle the case
154    // where arb is installed in a path containing spaces.
155    //
156    // Since that is a bottomless pit, I decided to deny spaces there.
157
158    char unwantedChar = ARB_path_contains_unwanted_chars(path);
159    if (unwantedChar) {
160        GB_warningf(
161            "arb may not work as expected, because\n"
162            "%s\n"
163            "  (='%s')\n"
164            "contains an unwanted character ('%c').",
165            path_description,
166            path,
167            unwantedChar);
168    }
169}
170
171// --------------------------------------------------------------------------------
172
173#ifdef UNIT_TESTS
174#ifndef TEST_UNIT_H
175#include <test_unit.h>
176#endif
177
178#if 0
179// simple test
180#define TEST_EXPECT_FLOAT_2_ASCII(f,a) TEST_EXPECT_EQUAL(ARB_float_2_ascii(f), a)
181#else
182// also test back-conversion (ascii->float->ascii) is stable
183#define TEST_EXPECT_FLOAT_2_ASCII(f,a) do{                          \
184        TEST_EXPECT_EQUAL(ARB_float_2_ascii(f), a);                 \
185        TEST_EXPECT_EQUAL(ARB_float_2_ascii(strtof(a, NULp)), a);   \
186    }while(0)
187#endif
188
189__ATTR__REDUCED_OPTIMIZE void TEST_float_2_ascii() {
190    TEST_EXPECT_FLOAT_2_ASCII(3.141592e+00, "3.141592");
191    TEST_EXPECT_FLOAT_2_ASCII(3.141592,     "3.141592");
192    TEST_EXPECT_FLOAT_2_ASCII(3.14159,      "3.14159");
193
194    TEST_EXPECT_FLOAT_2_ASCII(0.1,           "0.1");
195    TEST_EXPECT_FLOAT_2_ASCII(0.01,          "0.01");
196    TEST_EXPECT_FLOAT_2_ASCII(0.001,         "0.001");
197    TEST_EXPECT_FLOAT_2_ASCII(0.0001,        "0.0001");
198    TEST_EXPECT_FLOAT_2_ASCII(0.00001,       "1e-05");
199    TEST_EXPECT_FLOAT_2_ASCII(0.000001,      "1e-06");
200    TEST_EXPECT_FLOAT_2_ASCII(0.0000001,     "1e-07");
201    TEST_EXPECT_FLOAT_2_ASCII(0.00000001,    "1e-08");
202    TEST_EXPECT_FLOAT_2_ASCII(0.000000001,   "1e-09");
203    TEST_EXPECT_FLOAT_2_ASCII(0.0000000001,  "1e-10");
204    TEST_EXPECT_FLOAT_2_ASCII(0.00000000001, "1e-11");
205
206    TEST_EXPECT_FLOAT_2_ASCII(10,         "10");
207    TEST_EXPECT_FLOAT_2_ASCII(100,        "100");
208    TEST_EXPECT_FLOAT_2_ASCII(1000,       "1000");
209    TEST_EXPECT_FLOAT_2_ASCII(10000,      "10000");
210    TEST_EXPECT_FLOAT_2_ASCII(100000,     "100000");
211    TEST_EXPECT_FLOAT_2_ASCII(1000000,    "1e+06");
212    TEST_EXPECT_FLOAT_2_ASCII(10000000,   "1e+07");
213    TEST_EXPECT_FLOAT_2_ASCII(100000000,  "1e+08");
214    TEST_EXPECT_FLOAT_2_ASCII(1000000000, "1e+09");
215
216    TEST_EXPECT_FLOAT_2_ASCII(3141592,      "3.141592e+06");
217    TEST_EXPECT_FLOAT_2_ASCII(314159.2,     "3.141592e+05");
218    TEST_EXPECT_FLOAT_2_ASCII(31415.92,     "3.141592e+04");
219    TEST_EXPECT_FLOAT_2_ASCII(3141.592,     "3141.592041");
220    TEST_EXPECT_FLOAT_2_ASCII(3141.592041,  "3141.592041");
221    TEST_EXPECT_FLOAT_2_ASCII(314.1592,     "314.159210");
222    TEST_EXPECT_FLOAT_2_ASCII(314.159210,   "314.159210");
223    TEST_EXPECT_FLOAT_2_ASCII(31.41592,     "31.415920");
224    TEST_EXPECT_FLOAT_2_ASCII(3.141592,     "3.141592");
225    TEST_EXPECT_FLOAT_2_ASCII(.3141592,     "3.141592e-01");
226    TEST_EXPECT_FLOAT_2_ASCII(.03141592,    "3.141592e-02");
227    TEST_EXPECT_FLOAT_2_ASCII(.003141592,   "3.141592e-03");
228    TEST_EXPECT_FLOAT_2_ASCII(.0003141592,  "3.141592e-04");
229    TEST_EXPECT_FLOAT_2_ASCII(.00003141592, "3.141592e-05");
230    TEST_EXPECT_FLOAT_2_ASCII(M_PI,         "3.141593");
231
232    TEST_EXPECT_FLOAT_2_ASCII(1/2.0, "0.5");
233    TEST_EXPECT_FLOAT_2_ASCII(1/3.0, "3.333333e-01");
234    TEST_EXPECT_FLOAT_2_ASCII(1/4.0, "0.25");
235    TEST_EXPECT_FLOAT_2_ASCII(1/5.0, "0.2");
236    TEST_EXPECT_FLOAT_2_ASCII(1/6.0, "1.666667e-01");
237
238    TEST_EXPECT_FLOAT_2_ASCII(37550000.0,  "3.755e+07");
239    TEST_EXPECT_FLOAT_2_ASCII(3755000.0,   "3.755e+06");
240    TEST_EXPECT_FLOAT_2_ASCII(375500.0,    "375500");
241    TEST_EXPECT_FLOAT_2_ASCII(37550.0,     "37550");
242    TEST_EXPECT_FLOAT_2_ASCII(3755.0,      "3755");
243    TEST_EXPECT_FLOAT_2_ASCII(375.5,       "375.5");
244    TEST_EXPECT_FLOAT_2_ASCII(37.55,       "37.55");
245    TEST_EXPECT_FLOAT_2_ASCII(3.755,       "3.755");
246    TEST_EXPECT_FLOAT_2_ASCII(0.3755,      "0.3755");
247    TEST_EXPECT_FLOAT_2_ASCII(0.03755,     "0.03755");
248    TEST_EXPECT_FLOAT_2_ASCII(0.003755,    "0.003755");
249    TEST_EXPECT_FLOAT_2_ASCII(0.0003755,   "0.0003755");
250    TEST_EXPECT_FLOAT_2_ASCII(0.00003755,  "3.755e-05");
251    TEST_EXPECT_FLOAT_2_ASCII(0.000003755, "3.755e-06");
252
253    TEST_EXPECT_FLOAT_2_ASCII(1000.0*1000.0*1000.0,    "1e+09");
254    TEST_EXPECT_FLOAT_2_ASCII(25000.0*25000.0*25000.0, "1.5625e+13");
255}
256
257// ------------------------------------------------------------
258// test to ensure sanitizers work as expected
259
260#if 0
261void TEST_fail_address_sanitizer() {
262    static int array[5];
263    array[2] = 1;
264    array[5] = 1; // <- fails with AddressSanitizer
265
266    printf("array[5]=%i\n", array[5]);
267}
268#endif
269
270#if 0
271void TEST_fail_undef_sanitizer() {
272    // error below are not reported if AddressSanitizer bails out (TEST_fail_address_sanitizer)
273    int x  = 7;
274    int y1 = -1;
275
276    int s = x<<y1; // runtime error with ubsan: shift exponent -1 is negative (does not terminate)
277    printf("s=%i\n", s);
278
279    int o = INT_MAX;
280    int u = INT_MIN;
281    o++; // runtime error: signed integer overflow
282    u--; // runtime error: signed integer overflow
283    printf("o=%i u=%i\n", o, u);
284
285#if 0
286    int y2 = 0;
287    int z1 = x/y1;
288    int z2 = x/y2; // runtime error with ubsan: division by zero (terminates with SEGV; also w/o sanitizers)
289    printf("z1=%i z2=%i\n", z1, z2);
290#endif
291}
292#endif
293
294#if 0
295void TEST_fail_leak_sanitizer() {
296    int *p = new int[5]; // <- fails with LeakSanitizer (only reported if AddressSanitizer does not bail out (TEST_fail_address_sanitizer))
297    printf("p[3]=%i\n", p[3]);
298}
299#endif
300
301// ------------------------------------------------------------
302
303#include "StrUniquifier.h"
304
305void TEST_StrUniquifier() {
306    StrUniquifier uniq("->");
307    TEST_EXPECT_EQUAL(uniq.make_unique_key("hey"), "hey");
308    TEST_EXPECT_EQUAL(uniq.make_unique_key("hey"), "hey->2");
309    TEST_EXPECT_EQUAL(uniq.make_unique_key("Hey"), "Hey");
310    TEST_EXPECT_EQUAL(uniq.make_unique_key("Hey"), "Hey->2");
311
312    TEST_EXPECT_EQUAL(uniq.make_unique_key(""), "");
313    TEST_EXPECT_EQUAL(uniq.make_unique_key(""), "->2");
314
315    StrUniquifier fresh(".");
316    TEST_EXPECT_EQUAL(fresh.make_unique_key(""), "");
317    TEST_EXPECT_EQUAL(fresh.make_unique_key(""), ".2");
318}
319
320// ------------------------------------------------------------
321
322#endif // UNIT_TESTS
323
324// --------------------------------------------------------------------------------
325
Note: See TracBrowser for help on using the repository browser.