1 /*
2  * array.c - array functions
3  *
4  * Copyright (C) 2011-2013 Thien-Thi Nguyen
5  * Copyright (C) 2001 Stefan Jahn <stefan@lkcc.org>
6  * Copyright (C) 2001 Raimund Jacob <raimi@lkcc.org>
7  *
8  * This is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3, or (at your option)
11  * any later version.
12  *
13  * This software is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this package.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include "timidity.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "libserveez/alloc.h"
30 #include "libserveez/util.h"
31 #include "libserveez/array.h"
32 
33 struct svz_array
34 {
35   size_t size;             /* Real size of the array.  */
36   size_t capacity;         /* Current capacity.  */
37   svz_free_func_t destroy; /* The destroy callback.  */
38   void **data;             /* Data pointer.  */
39 };
40 
41 /**
42  * Create a new array with the initial capacity @var{capacity} and return
43  * a pointer to it.  If @var{capacity} is zero it defaults to some value.
44  * If @var{destroy} is non-@code{NULL}, @code{svz_array_destroy} calls
45  * that function (typically used to free dynamically allocated memory).
46  * For example, if the array contains data allocated by @code{svz_malloc},
47  * @var{destroy} should be specified as @code{svz_free}.  If the array
48  * contains data which should not be released, @var{destroy} should
49  * be @code{NULL}.
50  */
51 svz_array_t *
svz_array_create(size_t capacity,svz_free_func_t destroy)52 svz_array_create (size_t capacity, svz_free_func_t destroy)
53 {
54   svz_array_t *array;
55 
56   if (!capacity)
57     capacity = 4;
58   array = svz_calloc (sizeof (svz_array_t));
59   array->data = svz_malloc (sizeof (void *) * capacity);
60   array->capacity = capacity;
61   array->destroy = destroy;
62   return array;
63 }
64 
65 /**
66  * Completely destroy the array @var{array}.  The @var{array} handle is
67  * invalid afterwards.  The routine runs the @var{destroy} callback for each
68  * element of the array.
69  */
70 void
svz_array_destroy(svz_array_t * array)71 svz_array_destroy (svz_array_t *array)
72 {
73   if (array)
74     {
75       if (array->data)
76         {
77           if (array->destroy != NULL)
78             {
79               size_t n;
80 
81               for (n = 0; n < array->size; n++)
82                 array->destroy (array->data[n]);
83             }
84           svz_free (array->data);
85           array->data = NULL;
86           array->size = 0;
87           array->capacity = 0;
88         }
89       svz_free (array);
90     }
91 }
92 
93 /**
94  * Return the array element at the position @var{index} of the array
95  * @var{array} if the index is within the array range.  Return @code{NULL}
96  * if not.
97  */
98 void *
svz_array_get(svz_array_t * array,size_t index)99 svz_array_get (svz_array_t *array, size_t index)
100 {
101   if (array == NULL || index >= array->size)
102     return NULL;
103   return array->data[index];
104 }
105 
106 /**
107  * Replace the array element at the position @var{index} of the array
108  * @var{array} with the value @var{value} and return the previous value
109  * at this index.  Return @code{NULL} and do nothing
110  * if @var{array} is @code{NULL} or the @var{index} is out of the array
111  * range.
112  */
113 void *
svz_array_set(svz_array_t * array,size_t index,void * value)114 svz_array_set (svz_array_t *array, size_t index, void *value)
115 {
116   void *prev;
117 
118   if (array == NULL || index >= array->size)
119     return NULL;
120   prev = array->data[index];
121   array->data[index] = value;
122   return prev;
123 }
124 
125 /**
126  * Append the value @var{value} at the end of the array @var{array}.
127  * Do nothing if @var{array} is @code{NULL}.
128  */
129 void
svz_array_add(svz_array_t * array,void * value)130 svz_array_add (svz_array_t *array, void *value)
131 {
132   if (array)
133     {
134       size_t bigger = 1 + array->size;
135 
136       if (bigger > array->capacity)
137         {
138           array->capacity = array->capacity * 3 / 2 + 1;
139           array->data = svz_realloc (array->data, sizeof (void *) *
140                                      array->capacity);
141         }
142       array->data[array->size] = value;
143       array->size = bigger;
144     }
145 }
146 
147 /**
148  * Remove the array element at the position @var{index} of the array
149  * @var{array}.  Return its previous value or @code{NULL} if the index
150  * is out of the array's range.
151  */
152 void *
svz_array_del(svz_array_t * array,size_t index)153 svz_array_del (svz_array_t *array, size_t index)
154 {
155   void *value;
156 
157   if (array == NULL || index >= array->size)
158     return NULL;
159   value = array->data[index];
160   if (index != array->size - 1)
161     memmove (&array->data[index], &array->data[index + 1],
162              (array->size - index - 1) * sizeof (void *));
163   array->size--;
164   return value;
165 }
166 
167 /**
168  * Return the current size of @var{array}.
169  */
170 size_t
svz_array_size(svz_array_t * array)171 svz_array_size (svz_array_t *array)
172 {
173   if (array == NULL)
174     return 0;
175   return array->size;
176 }
177 
178 /*
179  * This function replicates the given array @var{array}.  It returns
180  * @code{NULL} if there is nothing to do and an identical copy if the
181  * array otherwise.
182  */
183 svz_array_t *
svz_array_dup(svz_array_t * array)184 svz_array_dup (svz_array_t *array)
185 {
186   svz_array_t *dup;
187 
188   if (array == NULL)
189     return NULL;
190   dup = svz_array_create (array->size, array->destroy);
191   dup->size = array->size;
192   if (array->size)
193     memcpy (dup->data, array->data, array->size * sizeof (void *));
194   return dup;
195 }
196 
197 /*
198  * This function works something like @code{svz_array_dup} but considers
199  * the values within the array @var{array} to be zero-terminated character
200  * strings and duplicates these via @code{svz_strdup}.
201  */
202 svz_array_t *
svz_array_strdup(svz_array_t * array)203 svz_array_strdup (svz_array_t *array)
204 {
205   svz_array_t *dup;
206   size_t n;
207 
208   if (array == NULL)
209     return NULL;
210   dup = svz_array_create (array->size, svz_free);
211   dup->size = array->size;
212   for (n = 0; n < array->size; n++)
213     dup->data[n] = svz_strdup (array->data[n]);
214   return dup;
215 }
216 
217 /*
218  * This function destroys the given array @var{array} if it holds no
219  * elements and returns @code{NULL} in this case.  Otherwise the
220  * function returns the given array.
221  */
222 svz_array_t *
svz_array_destroy_zero(svz_array_t * array)223 svz_array_destroy_zero (svz_array_t *array)
224 {
225   if (array && array->size == 0)
226     {
227       svz_array_destroy (array);
228       return NULL;
229     }
230   return array;
231 }
232