1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Bacula array list routines
21  *
22  *    alist is a simple malloc'ed array of pointers.  For the moment,
23  *      it simply malloc's a bigger array controlled by num_grow.
24  *      Default is to realloc the pointer array for each new member.
25  *
26  *    Note: the list can have holes (empty items). This is done by
27  *      using get() and put().  If you are using this kind of indexed
28  *      list, you cannot use: prepend() and remove() as they will
29  *      reorder the list. So, in the ilist array, these functions are
30  *      disabled and the put method is defined.
31  *
32  *   Kern Sibbald, June MMIII
33  *
34  */
35 
36 #include "bacula.h"
37 
38 /*
39  * Private grow list function. Used to insure that
40  *   at least one more "slot" is available.
41  */
grow_list()42 void baselist::grow_list()
43 {
44    int i;
45    int new_max_items;
46 
47    /* put() can insert and item anywhere in the list so
48     * it's important to allocate at least last_item+1 items
49     */
50    int min_grow = MAX(10, last_item+1);
51    if (num_grow < min_grow) {
52       num_grow = min_grow;               /* default if not initialized */
53    }
54 
55    if (items == NULL) {
56       items = (void **)malloc(num_grow * sizeof(void *));
57       for (i=0; i<num_grow; i++) {
58          items[i] = NULL;
59       }
60       max_items = num_grow;
61    } else if (last_item >= max_items) {
62       new_max_items = last_item + num_grow;
63       items = (void **)realloc(items, new_max_items * sizeof(void *));
64       for (i=max_items; i<new_max_items; i++) {
65          items[i] = NULL;
66       }
67       max_items = new_max_items;
68    }
69 }
70 
first()71 void *alist::first()
72 {
73    cur_item = 1;
74    if (num_items == 0) {
75       return NULL;
76    } else {
77       return items[0];
78    }
79 }
80 
last()81 void *alist::last()
82 {
83    if (num_items == 0) {
84       return NULL;
85    } else {
86       cur_item = last_item;
87       return items[last_item-1];
88    }
89 }
90 
next()91 void *alist::next()
92 {
93    if (cur_item >= last_item) {
94       return NULL;
95    } else {
96       return items[cur_item++];
97    }
98 }
99 
100 /* Do not mix prev() and next() calls */
prev()101 void *alist::prev()
102 {
103    if (cur_item <= 1) {
104       return NULL;
105    } else {
106       return items[--cur_item];
107    }
108 }
109 
110 /*
111  * prepend an item to the list -- i.e. add to beginning
112  */
prepend(void * item)113 void alist::prepend(void *item)
114 {
115    grow_list();
116    if (num_items == 0) {
117       items[num_items++] = item;
118       if (num_items > last_item) {
119          last_item = num_items;
120       }
121       return;
122    }
123    for (int i=last_item; i > 0; i--) {
124       items[i] = items[i-1];
125    }
126    items[0] = item;
127    num_items++;
128    last_item++;
129 }
130 
131 /*
132  * Append an item to the list
133  */
append(void * item)134 void baselist::append(void *item)
135 {
136    grow_list();
137    items[last_item++] = item;
138    num_items++;
139 }
140 
141 /*
142  * Remove an item from the list
143  * Note: you must free the item when
144  *   you are done with it.
145  */
remove_item(int index)146 void * baselist::remove_item(int index)
147 {
148    void *item;
149    if (index < 0 || index >= last_item) {
150       return NULL;
151    }
152    item = items[index];
153 
154    /* last_item is from 1..n, we work from 0..n-1 */
155    for (int i=index; i < (last_item-1); i++) {
156       items[i] = items[i+1];
157    }
158 
159    items[last_item-1] = NULL;   /* The last item is shifted by one, the last slot is always free */
160 
161    last_item--;                 /* We have shifted all items by 1 */
162    num_items--;                 /* We have 1 item less */
163 
164    return item;
165 }
166 
167 
168 /* Get the index item -- we should probably allow real indexing here */
get(int index)169 void * baselist::get(int index)
170 {
171    if (items == NULL || index < 0 || index >= last_item) {
172       return NULL;
173    }
174    return items[index];
175 }
176 
177 /* Destroy the list and its contents */
destroy()178 void baselist::destroy()
179 {
180    if (items) {
181       if (own_items) {
182          for (int i=0; i<max_items; i++) {
183             if (items[i]) {
184                bfree(items[i]);
185                items[i] = NULL;
186             }
187          }
188       }
189       bfree(items);
190       items = NULL;
191    }
192    num_items = 0;
193    last_item = 0;
194    max_items = 0;
195    num_grow = 0;
196 }
197 
198 #ifdef TEST_PROGRAM
199 #include "unittests.h"
200 
201 #define NUMITEMS        20
202 #define MORENUMITEMS    115
203 
204 struct FILESET {
205    alist mylist;
206 };
207 
check_all_alist_indexes2(alist * mlist)208 void check_all_alist_indexes2(alist *mlist)
209 {
210    bool check_cont = true;
211    char *bp;
212    int i = 0;
213    int nb;
214 
215    foreach_alist_index(i, bp, mlist) {
216       nb = atoi(bp);
217       if (nb != i){
218          Dmsg2(0, "nb=%d != i=%d\n", nb, i);
219          check_cont = false;
220       }
221    }
222    ok(check_cont, "Check all alist indexes 2");
223 };
224 
check_all_alist_contents(alist * mlist)225 void check_all_alist_contents(alist *mlist)
226 {
227    bool check_cont = true;
228    char buf[30];
229    int i;
230 
231    for (i = 0; i < mlist->size(); i++) {
232       sprintf(buf, "This is item %d", i);
233       if (strcmp(buf, (char*)mlist->get(i)) != 0){
234          check_cont = false;
235       }
236    }
237    ok(check_cont, "Checking alist contents");
238 };
239 
check_all_alist_indexes(alist * mlist)240 void check_all_alist_indexes(alist *mlist)
241 {
242    bool check_cont = true;
243    char *bp;
244    int i = 0;
245    int nb;
246 
247    foreach_alist(bp, mlist) {
248       nb = atoi(bp);
249       if (nb != i++){
250          check_cont = false;
251       }
252    }
253    ok(check_cont, "Check all alist indexes");
254 };
255 
check_alist_destroy_and_delete(alist * mlist)256 void check_alist_destroy_and_delete(alist *mlist)
257 {
258    mlist->destroy();
259    ok(mlist->size() == 0, "Check alist size after destroy");
260    ok(mlist->last() == NULL, "Check alist last after destroy");
261    delete mlist;
262 };
263 
main()264 int main()
265 {
266    Unittests alist_test("alist_test");
267    FILESET *fileset;
268    char buf[30];
269    alist *mlist;
270    char *bp;
271    int i;
272    bool check_cont;
273    bool check_indx;
274 
275    log("Initialize tests ...");
276    fileset = (FILESET *)malloc(sizeof(FILESET));
277    bmemzero(fileset, sizeof(FILESET));
278    fileset->mylist.init();
279    ok(fileset && fileset->mylist.empty() && fileset->mylist.max_size() == 0,
280       "Default initialization");
281 
282    log("Automatic allocation/destruction of alist:");
283 
284    for (int i = 0; i < NUMITEMS; i++) {
285       sprintf(buf, "This is item %d", i);
286       fileset->mylist.append(bstrdup(buf));
287    }
288    ok(fileset->mylist.size() == NUMITEMS, "Checking size");
289 
290    check_all_alist_contents(&fileset->mylist);
291 
292    fileset->mylist.destroy();
293    ok(fileset->mylist.size() == 0, "Check size after delete");
294    ok(fileset->mylist.last() == NULL, "Check last after delete");
295    free(fileset);
296 
297    log("Allocation/destruction using new delete");
298 
299    mlist = New(alist(50));
300    ok(mlist && mlist->empty() && mlist->max_size() == 0,
301       "Constructor initialization");
302    for (i = 0; i < NUMITEMS; i++) {
303       sprintf(buf, "This is item %d", i);
304       mlist->append(bstrdup(buf));
305    }
306    ok(mlist->size() == NUMITEMS, "Checking size");
307    check_all_alist_contents(mlist);
308    check_alist_destroy_and_delete(mlist);
309 
310    log("Test alist::remove(0)");
311    mlist = New(alist(10, owned_by_alist));
312    mlist->append(bstrdup("trash"));
313    mlist->append(bstrdup("0"));
314    mlist->append(bstrdup("1"));
315    mlist->append(bstrdup("2"));
316    mlist->append(bstrdup("3"));
317    ok(mlist && mlist->size() == 5, "Checking size");
318    ok(mlist->last_index() == 5, "Check last_index");
319    free(mlist->remove(0));
320    ok(mlist->size() == 4, "Remove test size");
321    ok(mlist->last_index() == 4, "Check last_index");
322    check_all_alist_indexes(mlist);
323    check_alist_destroy_and_delete(mlist);
324 
325    log("Test alist::remove(3)");
326    mlist = New(alist(10, owned_by_alist));
327    mlist->append(bstrdup("0"));
328    mlist->append(bstrdup("1"));
329    mlist->append(bstrdup("2"));
330    mlist->append(bstrdup("trash"));
331    mlist->append(bstrdup("3"));
332    ok(mlist && mlist->size() == 5, "Checking size");
333    ok(mlist->last_index() == 5, "Check last_index");
334    free(mlist->remove(3));
335    ok(mlist->size() == 4, "Remove test size");
336    ok(mlist->last_index() == 4, "Check last_index");
337    check_all_alist_indexes(mlist);
338    check_alist_destroy_and_delete(mlist);
339 
340    log("Test alist::remove(last)");
341    mlist = New(alist(10, owned_by_alist));
342    mlist->append(bstrdup("0"));
343    mlist->append(bstrdup("1"));
344    mlist->append(bstrdup("2"));
345    mlist->append(bstrdup("3"));
346    mlist->append(bstrdup("trash"));
347    ok(mlist && mlist->size() == 5, "Checking size");
348    ok(mlist->last_index() == 5, "Check last_index");
349    free(mlist->remove(4));
350    ok(mlist->size() == 4, "Remove test size");
351    check_all_alist_indexes(mlist);
352    check_alist_destroy_and_delete(mlist);
353 
354    log("Test alist::remove(last+1)");
355    mlist = New(alist(10, owned_by_alist));
356    mlist->append(bstrdup("0"));
357    mlist->append(bstrdup("1"));
358    mlist->append(bstrdup("2"));
359    mlist->append(bstrdup("3"));
360    mlist->append(bstrdup("4"));
361    check_all_alist_indexes2(mlist);
362    ok(mlist && mlist->size() == 5, "Checking size");
363    ok(mlist->last_index() == 5, "Check last_index");
364    ok(mlist->remove(5) == NULL, "Check remove returns null");
365    ok(mlist->size() == 5, "Remove test size");
366    check_all_alist_indexes(mlist);
367    check_all_alist_indexes2(mlist);
368    check_alist_destroy_and_delete(mlist);
369 
370    log("Test alist::pop()");
371    mlist = New(alist(10, owned_by_alist));
372    mlist->append(bstrdup("0"));
373    mlist->append(bstrdup("1"));
374    mlist->append(bstrdup("2"));
375    mlist->append(bstrdup("3"));
376    mlist->append(bstrdup("trash"));
377    ok(mlist && mlist->size() == 5, "Checking size");
378    ok(mlist->last_index() == 5, "Check last_index");
379    free(mlist->pop());
380    ok(mlist->size() == 4, "Check last_index after pop()");
381    check_all_alist_indexes(mlist);
382    check_alist_destroy_and_delete(mlist);
383 
384    log("Test alist::push()");
385    mlist = New(alist(10, owned_by_alist));
386    check_cont = true;
387    check_indx = true;
388    for (i = 0; i < NUMITEMS; i++) {
389       sprintf(buf, "This is item %d", i);
390       mlist->push(bstrdup(buf));
391       if (mlist->size() != i + 1){
392          check_cont = false;
393       }
394       if (mlist->last_index() != i + 1){
395          check_indx = false;
396       }
397    }
398    ok(check_cont, "Check all sizes after push");
399    ok(check_indx, "Check all last_indexes after push");
400    log("Test alist::pop()");
401    check_cont = true;
402    for (i = NUMITEMS-1; (bp = (char *)mlist->pop()); i--) {
403       sprintf(buf, "This is item %d", i);
404       if (strcmp(buf, bp) != 0){
405          check_cont = false;
406       }
407       free(bp);
408    }
409    ok(check_cont, "Check alist content after pop()");
410    ok(mlist->size() == 0, "Check alist size after pop()");
411    ok(mlist->last_index() == 0, "Check alist last_index after pop()");
412    /* check get after pop, it should be NULL */
413    check_cont = true;
414    for (int i=0; i<mlist->max_size(); i++) {
415       bp = (char *) mlist->get(i);
416       if (bp != NULL){
417          check_cont = false;
418       }
419    }
420    ok(check_cont, "Check get() after pop() contents.");
421    check_alist_destroy_and_delete(mlist);
422 
423    log("Test alist::foreach_alist_index()");
424    mlist = New(alist(10, owned_by_alist));
425    mlist->append(bstrdup("0"));
426    mlist->append(bstrdup("1"));
427    mlist->append(bstrdup("2"));
428    mlist->append(bstrdup("3"));
429    mlist->append(bstrdup("4"));
430    mlist->append(bstrdup("5"));
431    mlist->append(bstrdup("6"));
432    mlist->append(bstrdup("7"));
433    mlist->append(bstrdup("8"));
434    mlist->append(bstrdup("9"));
435    check_all_alist_indexes2(mlist);
436    ok(mlist && mlist->size() == 10, "Checking size");
437    ok(mlist->last_index() == 10, "Check last_index");
438    foreach_alist_index(i, bp, mlist) {
439       ok(i < mlist->size(), "check index");
440       ok(bp != NULL, "check element of alist");
441    }
442    check_alist_destroy_and_delete(mlist);
443    return report();
444 }
445 #endif
446