source: branches/stable/CORE/arb_strarray.cxx

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