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