/src/server/mysys/array.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License as published by |
5 | | the Free Software Foundation; version 2 of the License. |
6 | | |
7 | | This program is distributed in the hope that it will be useful, |
8 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | GNU General Public License for more details. |
11 | | |
12 | | You should have received a copy of the GNU General Public License |
13 | | along with this program; if not, write to the Free Software |
14 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ |
15 | | |
16 | | /* Handling of arrays that can grow dynamicly. */ |
17 | | |
18 | | #include "mysys_priv.h" |
19 | | #include "m_string.h" |
20 | | |
21 | | /* |
22 | | Initiate dynamic array |
23 | | |
24 | | SYNOPSIS |
25 | | init_dynamic_array2() |
26 | | ps_key Key to register instrumented memory |
27 | | array Pointer to an array |
28 | | element_size Size of element |
29 | | init_buffer Initial buffer pointer |
30 | | init_alloc Number of initial elements |
31 | | alloc_increment Increment for adding new elements |
32 | | my_flags Flags to my_malloc |
33 | | |
34 | | DESCRIPTION |
35 | | init_dynamic_array() initiates array and allocate space for |
36 | | init_alloc eilements. |
37 | | Array is usable even if space allocation failed, hence, the |
38 | | function never returns TRUE. |
39 | | |
40 | | RETURN VALUE |
41 | | FALSE Ok |
42 | | */ |
43 | | |
44 | | my_bool init_dynamic_array2(PSI_memory_key psi_key, DYNAMIC_ARRAY *array, |
45 | | size_t element_size, void *init_buffer, |
46 | | size_t init_alloc, size_t alloc_increment, |
47 | | myf my_flags) |
48 | 0 | { |
49 | 0 | DBUG_ENTER("init_dynamic_array2"); |
50 | 0 | if (!alloc_increment) |
51 | 0 | { |
52 | 0 | alloc_increment=MY_MAX((8192-MALLOC_OVERHEAD)/element_size,16); |
53 | 0 | if (init_alloc > 8 && alloc_increment > init_alloc * 2) |
54 | 0 | alloc_increment=init_alloc*2; |
55 | 0 | } |
56 | 0 | array->elements=0; |
57 | 0 | array->max_element=init_alloc; |
58 | 0 | array->alloc_increment=alloc_increment; |
59 | 0 | array->size_of_element=element_size; |
60 | 0 | array->m_psi_key= psi_key; |
61 | 0 | array->malloc_flags= my_flags; |
62 | 0 | DBUG_ASSERT((my_flags & MY_INIT_BUFFER_USED) == 0); |
63 | 0 | if ((array->buffer= init_buffer)) |
64 | 0 | { |
65 | 0 | array->malloc_flags|= MY_INIT_BUFFER_USED; |
66 | 0 | DBUG_RETURN(FALSE); |
67 | 0 | } |
68 | | /* |
69 | | Since the dynamic array is usable even if allocation fails here malloc |
70 | | should not throw an error |
71 | | */ |
72 | 0 | if (init_alloc && |
73 | 0 | !(array->buffer= (uchar*) my_malloc(psi_key, element_size*init_alloc, |
74 | 0 | MYF(my_flags)))) |
75 | 0 | array->max_element=0; |
76 | 0 | DBUG_RETURN(FALSE); |
77 | 0 | } |
78 | | |
79 | | /* |
80 | | Insert element at the end of array. Allocate memory if needed. |
81 | | |
82 | | SYNOPSIS |
83 | | insert_dynamic() |
84 | | array |
85 | | element |
86 | | |
87 | | RETURN VALUE |
88 | | TRUE Insert failed |
89 | | FALSE Ok |
90 | | */ |
91 | | |
92 | | my_bool insert_dynamic(DYNAMIC_ARRAY *array, const void * element) |
93 | 0 | { |
94 | 0 | void *buffer; |
95 | 0 | if (unlikely(array->elements == array->max_element)) |
96 | 0 | { /* Call only when necessary */ |
97 | 0 | if (!(buffer=alloc_dynamic(array))) |
98 | 0 | return TRUE; |
99 | 0 | } |
100 | 0 | else |
101 | 0 | { |
102 | 0 | buffer=array->buffer+(array->elements * array->size_of_element); |
103 | 0 | array->elements++; |
104 | 0 | } |
105 | 0 | memcpy(buffer, element, array->size_of_element); |
106 | 0 | return FALSE; |
107 | 0 | } |
108 | | |
109 | | |
110 | | /* Fast version of appending to dynamic array */ |
111 | | |
112 | | void init_append_dynamic(DYNAMIC_ARRAY_APPEND *append, |
113 | | DYNAMIC_ARRAY *array) |
114 | 0 | { |
115 | 0 | append->array= array; |
116 | 0 | append->pos= array->buffer + array->elements * array->size_of_element; |
117 | 0 | append->end= array->buffer + array->max_element * array->size_of_element; |
118 | 0 | } |
119 | | |
120 | | |
121 | | my_bool append_dynamic(DYNAMIC_ARRAY_APPEND *append, |
122 | | const void *element) |
123 | 0 | { |
124 | 0 | DYNAMIC_ARRAY *array= append->array; |
125 | 0 | size_t size_of_element= array->size_of_element; |
126 | 0 | if (unlikely(append->pos == append->end)) |
127 | 0 | { |
128 | 0 | void *buffer; |
129 | 0 | if (!(buffer=alloc_dynamic(array))) |
130 | 0 | return TRUE; |
131 | 0 | append->pos= (uchar*)buffer + size_of_element; |
132 | 0 | append->end= array->buffer + array->max_element * size_of_element; |
133 | 0 | memcpy(buffer, element, size_of_element); |
134 | 0 | } |
135 | 0 | else |
136 | 0 | { |
137 | 0 | array->elements++; |
138 | 0 | memcpy(append->pos, element, size_of_element); |
139 | 0 | append->pos+= size_of_element; |
140 | 0 | } |
141 | 0 | return FALSE; |
142 | 0 | } |
143 | | |
144 | | |
145 | | /* |
146 | | Alloc space for next element(s) |
147 | | |
148 | | SYNOPSIS |
149 | | alloc_dynamic() |
150 | | array |
151 | | |
152 | | DESCRIPTION |
153 | | alloc_dynamic() checks if there is empty space for at least |
154 | | one element if not tries to allocate space for alloc_increment |
155 | | elements at the end of array. |
156 | | |
157 | | RETURN VALUE |
158 | | pointer Pointer to empty space for element |
159 | | 0 Error |
160 | | */ |
161 | | |
162 | | void *alloc_dynamic(DYNAMIC_ARRAY *array) |
163 | 0 | { |
164 | 0 | DBUG_ENTER("alloc_dynamic"); |
165 | |
|
166 | 0 | DBUG_ASSERT(array->size_of_element); /* Ensure init() is called */ |
167 | 0 | if (array->elements == array->max_element) |
168 | 0 | { |
169 | 0 | char *new_ptr; |
170 | 0 | if (array->malloc_flags & MY_INIT_BUFFER_USED) |
171 | 0 | { |
172 | | /* |
173 | | In this scenario, the buffer is statically preallocated, |
174 | | so we have to create an all-new malloc since we overflowed |
175 | | */ |
176 | 0 | if (!(new_ptr= (char *) my_malloc(array->m_psi_key, |
177 | 0 | (array->max_element+ |
178 | 0 | array->alloc_increment) * |
179 | 0 | array->size_of_element, |
180 | 0 | MYF(array->malloc_flags | MY_WME)))) |
181 | 0 | DBUG_RETURN(0); |
182 | 0 | if (array->elements) |
183 | 0 | memcpy(new_ptr, array->buffer, |
184 | 0 | array->elements * array->size_of_element); |
185 | 0 | array->malloc_flags&= ~MY_INIT_BUFFER_USED; |
186 | 0 | } |
187 | 0 | else if (!(new_ptr=(char*) |
188 | 0 | my_realloc(array->m_psi_key, array->buffer, |
189 | 0 | (array->max_element+ array->alloc_increment) * |
190 | 0 | array->size_of_element, |
191 | 0 | MYF(MY_WME | MY_ALLOW_ZERO_PTR | |
192 | 0 | array->malloc_flags)))) |
193 | 0 | DBUG_RETURN(0); |
194 | 0 | array->buffer= (uchar*) new_ptr; |
195 | 0 | array->max_element+=array->alloc_increment; |
196 | 0 | } |
197 | 0 | DBUG_RETURN(array->buffer+(array->elements++ * array->size_of_element)); |
198 | 0 | } |
199 | | |
200 | | |
201 | | /* |
202 | | Pop last element from array. |
203 | | |
204 | | SYNOPSIS |
205 | | pop_dynamic() |
206 | | array |
207 | | |
208 | | RETURN VALUE |
209 | | pointer Ok |
210 | | 0 Array is empty |
211 | | */ |
212 | | |
213 | | void *pop_dynamic(DYNAMIC_ARRAY *array) |
214 | 0 | { |
215 | 0 | if (array->elements) |
216 | 0 | return array->buffer+(--array->elements * array->size_of_element); |
217 | 0 | return 0; |
218 | 0 | } |
219 | | |
220 | | /* |
221 | | Replace element in array with given element and index |
222 | | |
223 | | SYNOPSIS |
224 | | set_dynamic() |
225 | | array |
226 | | element Element to be inserted |
227 | | idx Index where element is to be inserted |
228 | | |
229 | | DESCRIPTION |
230 | | set_dynamic() replaces element in array. |
231 | | If idx > max_element insert new element. Allocate memory if needed. |
232 | | |
233 | | RETURN VALUE |
234 | | TRUE Idx was out of range and allocation of new memory failed |
235 | | FALSE Ok |
236 | | */ |
237 | | |
238 | | my_bool set_dynamic(DYNAMIC_ARRAY *array, const void *element, size_t idx) |
239 | 0 | { |
240 | 0 | if (idx >= array->elements) |
241 | 0 | { |
242 | 0 | if (idx >= array->max_element && allocate_dynamic(array, idx)) |
243 | 0 | return TRUE; |
244 | 0 | bzero((uchar*) (array->buffer+array->elements*array->size_of_element), |
245 | 0 | (idx - array->elements)*array->size_of_element); |
246 | 0 | array->elements=idx+1; |
247 | 0 | } |
248 | 0 | memcpy(array->buffer+(idx * array->size_of_element),element, |
249 | 0 | array->size_of_element); |
250 | 0 | return FALSE; |
251 | 0 | } |
252 | | |
253 | | |
254 | | /* |
255 | | Ensure that dynamic array has enough elements |
256 | | |
257 | | SYNOPSIS |
258 | | allocate_dynamic() |
259 | | array |
260 | | max_elements Numbers of elements that is needed |
261 | | |
262 | | NOTES |
263 | | Any new allocated element are NOT initialized |
264 | | |
265 | | RETURN VALUE |
266 | | FALSE Ok |
267 | | TRUE Allocation of new memory failed |
268 | | */ |
269 | | |
270 | | my_bool allocate_dynamic(DYNAMIC_ARRAY *array, size_t max_elements) |
271 | 0 | { |
272 | 0 | DBUG_ENTER("allocate_dynamic"); |
273 | |
|
274 | 0 | if (max_elements >= array->max_element) |
275 | 0 | { |
276 | 0 | size_t size; |
277 | 0 | uchar *new_ptr; |
278 | 0 | size= (max_elements + array->alloc_increment)/array->alloc_increment; |
279 | 0 | size*= array->alloc_increment; |
280 | 0 | if (array->malloc_flags & MY_INIT_BUFFER_USED) |
281 | 0 | { |
282 | | /* |
283 | | In this senerio, the buffer is statically preallocated, |
284 | | so we have to create an all-new malloc since we overflowed |
285 | | */ |
286 | 0 | if (!(new_ptr= (uchar *) my_malloc(array->m_psi_key, size * |
287 | 0 | array->size_of_element, |
288 | 0 | MYF(array->malloc_flags | MY_WME)))) |
289 | 0 | DBUG_RETURN(0); |
290 | 0 | memcpy(new_ptr, array->buffer, |
291 | 0 | array->elements * array->size_of_element); |
292 | 0 | array->malloc_flags&= ~MY_INIT_BUFFER_USED; |
293 | 0 | } |
294 | 0 | else if (!(new_ptr= (uchar*) my_realloc(array->m_psi_key, |
295 | 0 | array->buffer,size * |
296 | 0 | array->size_of_element, |
297 | 0 | MYF(MY_WME | MY_ALLOW_ZERO_PTR | |
298 | 0 | array->malloc_flags)))) |
299 | 0 | DBUG_RETURN(TRUE); |
300 | 0 | array->buffer= new_ptr; |
301 | 0 | array->max_element= size; |
302 | 0 | } |
303 | 0 | DBUG_RETURN(FALSE); |
304 | 0 | } |
305 | | |
306 | | |
307 | | /* |
308 | | Get an element from array by given index |
309 | | |
310 | | SYNOPSIS |
311 | | get_dynamic() |
312 | | array |
313 | | uchar* Element to be returned. If idx > elements contain zeroes. |
314 | | idx Index of element wanted. |
315 | | */ |
316 | | |
317 | | void get_dynamic(DYNAMIC_ARRAY *array, void *element, size_t idx) |
318 | 0 | { |
319 | 0 | if (unlikely(idx >= array->elements)) |
320 | 0 | { |
321 | 0 | DBUG_PRINT("warning",("To big array idx: %d, array size is %d", |
322 | 0 | idx,array->elements)); |
323 | 0 | bzero(element,array->size_of_element); |
324 | 0 | return; |
325 | 0 | } |
326 | 0 | memcpy(element,array->buffer+idx*array->size_of_element, |
327 | 0 | (size_t) array->size_of_element); |
328 | 0 | } |
329 | | |
330 | | |
331 | | /* |
332 | | Empty array by freeing all memory |
333 | | |
334 | | SYNOPSIS |
335 | | delete_dynamic() |
336 | | array Array to be deleted |
337 | | */ |
338 | | |
339 | | void delete_dynamic(DYNAMIC_ARRAY *array) |
340 | 0 | { |
341 | | /* |
342 | | Just mark as empty if we are using a static buffer |
343 | | */ |
344 | 0 | if (array->buffer && !(array->malloc_flags & MY_INIT_BUFFER_USED)) |
345 | 0 | my_free(array->buffer); |
346 | |
|
347 | 0 | array->buffer= 0; |
348 | 0 | array->elements= array->max_element= 0; |
349 | 0 | } |
350 | | |
351 | | /* |
352 | | Delete element by given index |
353 | | |
354 | | SYNOPSIS |
355 | | delete_dynamic_element() |
356 | | array |
357 | | idx Index of element to be deleted |
358 | | */ |
359 | | |
360 | | void delete_dynamic_element(DYNAMIC_ARRAY *array, size_t idx) |
361 | 0 | { |
362 | 0 | char *ptr= (char*) array->buffer+array->size_of_element*idx; |
363 | 0 | array->elements--; |
364 | 0 | memmove(ptr,ptr+array->size_of_element, |
365 | 0 | (array->elements-idx)*array->size_of_element); |
366 | 0 | } |
367 | | |
368 | | /* |
369 | | Wrapper around delete_dynamic, calling a FREE function on every |
370 | | element, before releasing the memory |
371 | | |
372 | | SYNOPSIS |
373 | | delete_dynamic_with_callback() |
374 | | array |
375 | | f The function to be called on every element before |
376 | | deleting the array; |
377 | | */ |
378 | 0 | void delete_dynamic_with_callback(DYNAMIC_ARRAY *array, FREE_FUNC f) { |
379 | 0 | size_t i; |
380 | 0 | char *ptr= (char*) array->buffer; |
381 | 0 | for (i= 0; i < array->elements; i++, ptr+= array->size_of_element) { |
382 | 0 | f(ptr); |
383 | 0 | } |
384 | 0 | delete_dynamic(array); |
385 | 0 | } |
386 | | /* |
387 | | Free unused memory |
388 | | |
389 | | SYNOPSIS |
390 | | freeze_size() |
391 | | array Array to be freed |
392 | | |
393 | | */ |
394 | | |
395 | | void freeze_size(DYNAMIC_ARRAY *array) |
396 | 0 | { |
397 | 0 | size_t elements; |
398 | | |
399 | | /* |
400 | | Do nothing if we are using a static buffer |
401 | | */ |
402 | 0 | if (array->malloc_flags & MY_INIT_BUFFER_USED) |
403 | 0 | return; |
404 | | |
405 | 0 | elements= MY_MAX(array->elements, 1); |
406 | 0 | if (array->buffer && array->max_element > elements) |
407 | 0 | { |
408 | 0 | array->buffer=(uchar*) my_realloc(array->m_psi_key, array->buffer, |
409 | 0 | elements * array->size_of_element, |
410 | 0 | MYF(MY_WME | array->malloc_flags)); |
411 | 0 | array->max_element= elements; |
412 | 0 | } |
413 | 0 | } |