source: branches/stable/CORE/arb_strarray.cxx

Last change on this file was 18665, checked in by westram, 3 years ago
  • change many WARN_TODO triggered warnings into todo markers.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.1 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : arb_strarray.cxx                               //
4//   Purpose   : handle arrays of strings                       //
5//                                                              //
6//   Coded by Ralf Westram (coder@reallysoft.de) in July 2011   //
7//   Institute of Microbiology (Technical University Munich)    //
8//   http://www.arb-home.de/                                    //
9//                                                              //
10// ============================================================ //
11
12#include "arb_strarray.h"
13
14#include <arb_str.h>
15#include <arb_strbuf.h>
16#include <arb_sort.h>
17
18void CharPtrArray::sort(CharPtrArray_compare_fun compare, void *client_data) {
19    /*! sort the array
20     * @see sort_and_uniq for parameters
21     */
22    GB_sort((void**)str, 0, size(), compare, client_data);
23}
24
25void CharPtrArray::uniq(CharPtrArray_compare_fun compare, void *client_data) {
26    /*! remove consecutive equal elements
27     * @see sort_and_uniq for parameters
28     */
29    for (int i = size()-2; i >= 0; --i) {
30        if (compare(str[i], str[i+1], client_data) == 0) {
31            remove(i+1);
32        }
33    }
34}
35
36/* ----------------------------------------
37 * conversion between
38 *
39 * - char ** (heap-allocated array of heap-allocated char*'s)
40 * - one string containing several substrings separated by a separator
41 *   (e.g. "name1,name2,name3")
42 */
43
44// @@@ search for code which is splitting strings and use GBT_split_string there
45// @@@ rename to GBS_split_string and move to string functions
46
47void GBT_splitNdestroy_string(ConstStrArray& names, char*& namelist, const char *separator, bool dropEmptyTokens) {
48    /*! Split 'namelist' into an array of substrings at each member of 'separator'.
49     *
50     * @param names pointers to split parts (into namelist)
51     * @param namelist string containing separator delimited parts
52     * @param separator contains all characters handled as separators
53     * @param dropEmptyTokens if true, empty tokens will be skipped
54     *
55     * Example:
56     * @code
57     * ConstStrArray array;
58     * char *list = ARB_strdup("Peter;Paul;Mary");
59     * GBT_splitNdestroy_string(array, list, ";", false);
60     * // array[1] contains "Paul"
61     * @endcode
62     *
63     * ownership of namelist is transferred to 'names'
64     */
65
66    names.set_memblock(namelist);
67
68    char *sep = namelist;
69    while (sep) {
70        size_t nonsepcount = strcspn(sep, separator);
71        if (nonsepcount || !dropEmptyTokens) {
72            names.put(sep);
73            sep += nonsepcount;
74        }
75        size_t sepcount = strspn(sep, separator);
76        sep[0] = 0;
77        if (sepcount) {
78            if (!dropEmptyTokens) {
79                for (size_t s = 1; s<sepcount; ++s) names.put(sep);
80            }
81            sep += sepcount;
82        }
83        else {
84            sep = NULp;
85        }
86    }
87    namelist = NULp; // own it
88}
89
90void GBT_splitNdestroy_string(ConstStrArray& dest, char*& namelist, char separator) {
91    char separator_string[] = "x";
92    separator_string[0]   = separator;
93    GBT_splitNdestroy_string(dest, namelist, separator_string, false);
94}
95
96char *GBT_join_strings(const CharPtrArray& strings, char separator) {
97    /*! Joins a NULp-terminated array of 'char*' into one string
98     *
99     * @param strings   array of strings to join (maybe generated using GBT_split_string() or GBT_splitNdestroy_string)
100     * @param separator is put between the concatenated strings (pass 0 to join w/o separator)
101     *
102     * @return heap-copy of joined strings
103     */
104
105    if (!strings[0]) return ARB_strdup("");
106
107    GBS_strstruct *out = GBS_stropen(1000);
108    GBS_strcat(out, strings[0]);
109    arb_assert(implicated(separator, !strchr(strings[0], separator))); // otherwise you'll never be able to GBT_split_string
110    for (int n = 1; strings[n]; ++n) {
111        if (separator) GBS_chrcat(out, separator);
112        GBS_strcat(out, strings[n]);
113        arb_assert(implicated(separator, !strchr(strings[n], separator))); // otherwise you'll never be able to GBT_split_string
114    }
115    return GBS_strclose(out);
116}
117
118int CharPtrArray::index_of(const char *search_for) const {
119    // return index of 'search_for' or -1 if not found or given
120    int index = -1;
121    if (search_for && allocated) {
122        for (int i = 0; str[i]; ++i) {
123            if (strcmp(str[i], search_for) == 0) {
124                index = i;
125                break;
126            }
127        }
128    }
129    return index;
130}
131
132void CharPtrArray::move(int oidx, int nidx) {
133    /*! moves an array-entry from 'oidx' to 'nidx'
134     *  (entries between get shifted by one)
135     * -1 means "last entry"
136     * if 'nidx' is out of bounds, it'll be moved to start of array
137     */
138    int siz = size();
139
140    if (oidx == -1)       oidx = siz-1;
141    if (nidx == -1)       nidx = siz-1;
142    else if (nidx >= siz) nidx = 0;
143
144    arb_assert(nidx<siz);
145
146    if (oidx != nidx && oidx<siz) {
147        if (oidx>nidx) for (int i = oidx-1; i>= nidx; --i) swap(i, i+1); // LOOP_VECTORIZED[!<810]
148        else           for (int i = oidx;   i<  nidx; ++i) swap(i, i+1); // LOOP_VECTORIZED[!<810]
149    }
150}
151
152// --------------------------------------------------------------------------------
153
154#ifdef UNIT_TESTS
155#include <test_unit.h>
156
157void TEST_StrArray() {
158    StrArray array;
159
160    TEST_EXPECT(array.empty());
161    TEST_EXPECT_EQUAL(array.index_of("whatever"), -1);
162    TEST_EXPECT_EQUAL(array.size(), 0);
163    TEST_EXPECT_NULL(array[0]);
164
165    array.put(ARB_strdup("first"));
166
167    TEST_REJECT(array.empty());
168    TEST_EXPECT_EQUAL(array.size(), 1);
169    TEST_EXPECT_EQUAL(array[0], "first");
170    TEST_EXPECT_NULL(array[1]);
171
172    array.put(ARB_strdup("second"));
173   
174    TEST_EXPECT_EQUAL(array.size(), 2);
175    TEST_EXPECT_EQUAL(array[0], "first");
176    TEST_EXPECT_EQUAL(array[1], "second");
177    TEST_EXPECT_NULL(array[2]);
178
179    array.remove(0);
180
181    TEST_EXPECT_EQUAL(array.size(), 1);
182    TEST_EXPECT_EQUAL(array[0], "second");
183    TEST_EXPECT_NULL(array[1]);
184   
185    array.remove(0);
186
187    TEST_EXPECT(array.empty());
188    TEST_EXPECT_EQUAL(array.size(), 0);
189    TEST_EXPECT_NULL(array[0]);
190}
191
192void TEST_StrArray_truncate() {
193    ConstStrArray parts;
194    GBT_split_string(parts, "test;word;bla", ';');
195
196    TEST_EXPECT_EQUAL(parts.size(), 3);
197    parts.resize(1000); TEST_EXPECT_EQUAL(parts.size(), 3);
198    parts.resize(2); TEST_EXPECT_EQUAL(parts.size(), 2);
199    parts.resize(1); TEST_EXPECT_EQUAL(parts.size(), 1);
200    parts.resize(0); TEST_EXPECT(parts.empty());
201}
202
203#define TEST_SPLIT_JOIN(str,sep)                        \
204    do {                                                \
205        ConstStrArray cnames;                           \
206        GBT_split_string(cnames, str, sep);             \
207        char *joined = GBT_join_strings(cnames, sep);   \
208        TEST_EXPECT_EQUAL(str, joined);                 \
209        free(joined);                                   \
210    } while(0)
211
212void TEST_GBT_split_join_names() {
213    {                                               // simple split
214        ConstStrArray names;
215        GBT_split_string(names, "a*b*c", '*');
216        size_t   count = names.size();
217
218        TEST_EXPECT_EQUAL(count, 3U);
219        TEST_EXPECT_EQUAL(names[0], "a");
220        TEST_EXPECT_EQUAL(names[1], "b");
221        TEST_EXPECT_EQUAL(names[2], "c");
222    }
223    {                                               // split string containing empty tokens
224        ConstStrArray names;
225        GBT_split_string(names, "**a**b*c*", '*');
226        size_t   count = names.size();
227
228        TEST_EXPECT_EQUAL(count, 7U);
229        TEST_EXPECT_EQUAL(names[0], "");
230        TEST_EXPECT_EQUAL(names[1], "");
231        TEST_EXPECT_EQUAL(names[2], "a");
232        TEST_EXPECT_EQUAL(names[3], "");
233        TEST_EXPECT_EQUAL(names[4], "b");
234        TEST_EXPECT_EQUAL(names[5], "c");
235        TEST_EXPECT_EQUAL(names[6], "");
236        TEST_EXPECT_NULL(names[7]);
237    }
238
239    TEST_SPLIT_JOIN("a.b.c", '.');
240    TEST_SPLIT_JOIN("a.b.c", '*');
241   
242    TEST_SPLIT_JOIN("..a.b.c", '.');
243    TEST_SPLIT_JOIN("a.b.c..", '.');
244    TEST_SPLIT_JOIN("a..b..c", '.');
245    TEST_SPLIT_JOIN(".", '.');
246    TEST_SPLIT_JOIN("....", '.');
247}
248
249void TEST_StrArray_index_of() {
250    ConstStrArray names;
251
252    TEST_EXPECT_EQUAL(names.index_of("a"), -1);
253
254    GBT_split_string(names, "**a**b*c*", '*');
255
256    TEST_EXPECT_EQUAL(names.index_of("a"), 2);
257    TEST_EXPECT_EQUAL(names.index_of("b"), 4);
258    TEST_EXPECT_EQUAL(names.index_of("c"), 5);
259    TEST_EXPECT_EQUAL(names.index_of(""), 0);
260    TEST_EXPECT_EQUAL(names.index_of("no"), -1);
261}
262
263#define TEST_EXPECT_NAMES_JOIN_TO(names, sep, expected) \
264    do {                                                \
265        char *joined = GBT_join_strings(names, sep);    \
266        TEST_EXPECT_EQUAL(joined, expected);            \
267        free(joined);                                   \
268    } while(0)                                          \
269   
270void TEST_StrArray_safe_remove() {
271    ConstStrArray names;
272    GBT_split_string(names, "a*b*c*d*e", '*');
273
274    TEST_EXPECT_EQUAL(names.size(), 5U);
275    TEST_EXPECT_NAMES_JOIN_TO(names, 0, "abcde"); // test GBT_join_strings w/o separator
276
277    names.safe_remove(0);
278    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d*e");
279
280    names.safe_remove(3);
281    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
282
283    names.safe_remove(3);                      // index out of range
284    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
285
286    names.safe_remove(-1);                     // illegal index
287    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
288
289    names.safe_remove(1);
290    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*d");
291}
292
293void TEST_StrArray_move() {
294    ConstStrArray names;
295    GBT_split_string(names, "a*b*c*dee", '*');
296
297    names.move(0, -1); // -1 means last
298    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*dee*a");
299    names.move(-1, 0);
300    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c*dee");
301    names.move(2, 3);
302    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*dee*c");
303    names.move(2, 1);
304    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*dee*b*c");
305
306    // test wrap arounds
307    names.move(0, -1);
308    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "dee*b*c*a");
309    names.move(-1, 99999);
310    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*dee*b*c");
311}
312
313void TEST_StrArray_put_before() { // test after TEST_StrArray_move (cause put_before() depends on move())
314    ConstStrArray names;
315    GBT_split_string(names, "a", '*');
316
317    names.put_before(-1, "b");          // append at end
318    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b");
319
320    names.put_before(2, "c");           // append at end (using non-existing index)
321    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c");
322
323    names.put_before(99, "d");          // append at end (using even bigger non-existing index)
324    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c*d");
325
326    names.put_before(2, "b2");          // insert inside
327    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*b2*c*d");
328
329    names.put_before(0, "a0");          // insert at beginning
330    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a0*a*b*b2*c*d");
331
332    names.put_before(5, "d0");          // insert before last
333    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a0*a*b*b2*c*d0*d");
334}
335TEST_PUBLISH(TEST_StrArray_put_before);
336
337#endif // UNIT_TESTS
338
Note: See TracBrowser for help on using the repository browser.