e6d26aa231670fcdd6a7c12ef2193b84619a0363
[citadel.git] / libcitadel / lib / array.c
1 // This is a simple implementation of an elastic array class.  It includes constructor and destructor
2 // methods, append and access methods, and that's about it.  The memory allocation is very naive: whenever
3 // we are about to append beyond the size of the buffer, we double its size.
4 //
5 // Copyright (c) 2021-2022 by Art Cancro
6 //
7 // This program is open source software.  Use, duplication, or disclosure
8 // is subject to the terms of the GNU General Public License, version 3.
9
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include "libcitadel.h"
15
16 // Constructor for elastic array
17 Array *array_new(size_t element_size) {
18         Array *newarr = malloc(sizeof(Array));
19         if (newarr) {
20                 memset(newarr, 0, sizeof(Array));
21         }
22         newarr->element_size = element_size;
23         return newarr;
24 }
25
26
27 // Destructor for elastic array
28 void array_free(Array *arr) {
29         free(arr->the_elements);
30         free(arr);
31 }
32
33
34 // Append an element to an array (we already know the element size because it's in the data type)
35 void array_append(Array *arr, void *new_element) {
36
37         if (!arr) {                                             // silently fail if they gave us a null array
38                 return;
39         }
40
41         if ( (arr->the_elements == NULL) || (arr->num_alloc == 0)) {
42                 arr->num_alloc = 1;
43                 arr->num_elements = 0;
44                 arr->the_elements = malloc(arr->element_size * arr->num_alloc);
45                 if (arr->the_elements == NULL) {
46                         abort();
47                 }
48         }
49
50         ++arr->num_elements;
51         if (arr->num_elements > arr->num_alloc) {
52                 arr->num_alloc = arr->num_alloc * 2;            // whenever we exceed the buffer size, we double it.
53                 arr->the_elements = realloc(arr->the_elements, (arr->element_size * arr->num_alloc));
54                 if (arr->the_elements == NULL) {
55                         abort();
56                 }
57         }
58
59         memcpy((arr->the_elements + ( (arr->num_elements-1) * arr->element_size )), new_element, arr->element_size);
60 }
61
62
63 // Return the element in an array at the specified index
64 void *array_get_element_at(Array *arr, int index) {
65         return (arr->the_elements + ( index * arr->element_size ));
66 }
67
68
69 // Return the number of elements in an array
70 int array_len(Array *arr) {
71         if (!arr) {
72                 return(0);
73         }
74
75         return arr->num_elements;
76 }
77
78
79 // Sort an array.  We already know the element size and number of elements, so all we need is
80 // a sort function, which we will pass along to qsort().
81 void array_sort(Array *arr, int (*compar)(const void *, const void *)) {
82         qsort(arr->the_elements, arr->num_elements, arr->element_size, compar);
83 }
84
85
86 // Delete an element from an array
87 void array_delete_element_at(Array *arr, int index) {
88
89         if (index >= arr->num_elements) {               // If the supplied index is out of bounds, return quietly.
90                 return;
91         }
92
93         if (index < 0) {                                // If the supplied index is invalid, return quietly.
94                 return;
95         }
96
97         --arr->num_elements;
98
99         if (index == arr->num_elements) {               // If we deleted the element at the end, there's no more to be done.
100                 return;
101         }
102
103         memmove(                                        // memmove() is supposed to be safer than memcpy()
104                 (arr->the_elements + (index * arr->element_size)),
105                 arr->the_elements + ((index+1) * arr->element_size),
106                 (arr->num_elements - index) * arr->element_size
107         );
108 }