1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 /* Handling of arrays that can grow dynamicly. */
29 
30 #include "mysys_priv.h"
31 #include "m_string.h"
32 #include "my_sys.h"
33 
34 /*
35   Initiate dynamic array
36 
37   SYNOPSIS
38     my_init_dynamic_array()
39       array		Pointer to an array
40       element_size	Size of element
41       init_buffer       Initial buffer pointer
42       init_alloc	Number of initial elements
43       alloc_increment	Increment for adding new elements
44 
45   DESCRIPTION
46     init_dynamic_array() initiates array and allocate space for
47     init_alloc eilements.
48     Array is usable even if space allocation failed, hence, the
49     function never returns TRUE.
50     Static buffers must begin immediately after the array structure.
51 
52   RETURN VALUE
53     FALSE	Ok
54 */
55 
my_init_dynamic_array(DYNAMIC_ARRAY * array,PSI_memory_key psi_key,uint element_size,void * init_buffer,uint init_alloc,uint alloc_increment)56 my_bool my_init_dynamic_array(DYNAMIC_ARRAY *array,
57                               PSI_memory_key psi_key,
58                               uint element_size,
59                               void *init_buffer,
60                               uint init_alloc,
61                               uint alloc_increment)
62 {
63   DBUG_ENTER("init_dynamic_array");
64   if (!alloc_increment)
65   {
66     alloc_increment=MY_MAX((8192-MALLOC_OVERHEAD)/element_size,16);
67     if (init_alloc > 8 && alloc_increment > init_alloc * 2)
68       alloc_increment=init_alloc*2;
69   }
70 
71   if (!init_alloc)
72   {
73     init_alloc=alloc_increment;
74     init_buffer= 0;
75   }
76   array->elements=0;
77   array->max_element=init_alloc;
78   array->alloc_increment=alloc_increment;
79   array->size_of_element=element_size;
80   array->m_psi_key= psi_key;
81   if ((array->buffer= init_buffer))
82     DBUG_RETURN(FALSE);
83   /*
84     Since the dynamic array is usable even if allocation fails here malloc
85     should not throw an error
86   */
87   if (!(array->buffer= (uchar*) my_malloc(psi_key,
88                                           element_size*init_alloc, MYF(0))))
89     array->max_element=0;
90   DBUG_RETURN(FALSE);
91 }
92 
init_dynamic_array(DYNAMIC_ARRAY * array,uint element_size,uint init_alloc,uint alloc_increment)93 my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
94                            uint init_alloc, uint alloc_increment)
95 {
96   /* placeholder to preserve ABI */
97   return my_init_dynamic_array(array,
98                                PSI_INSTRUMENT_ME,
99                                element_size,
100                                NULL,              /* init_buffer */
101                                init_alloc,
102                                alloc_increment);
103 }
104 /*
105   Insert element at the end of array. Allocate memory if needed.
106 
107   SYNOPSIS
108     insert_dynamic()
109       array
110       element
111 
112   RETURN VALUE
113     TRUE	Insert failed
114     FALSE	Ok
115 */
116 
insert_dynamic(DYNAMIC_ARRAY * array,const void * element)117 my_bool insert_dynamic(DYNAMIC_ARRAY *array, const void *element)
118 {
119   uchar* buffer;
120   if (array->elements == array->max_element)
121   {						/* Call only when nessesary */
122     if (!(buffer=alloc_dynamic(array)))
123       return TRUE;
124   }
125   else
126   {
127     buffer=array->buffer+(array->elements * array->size_of_element);
128     array->elements++;
129   }
130   memcpy(buffer,element,(size_t) array->size_of_element);
131   return FALSE;
132 }
133 
134 
135 /*
136   Alloc space for next element(s)
137 
138   SYNOPSIS
139     alloc_dynamic()
140       array
141 
142   DESCRIPTION
143     alloc_dynamic() checks if there is empty space for at least
144     one element if not tries to allocate space for alloc_increment
145     elements at the end of array.
146 
147   RETURN VALUE
148     pointer	Pointer to empty space for element
149     0		Error
150 */
151 
alloc_dynamic(DYNAMIC_ARRAY * array)152 void *alloc_dynamic(DYNAMIC_ARRAY *array)
153 {
154   if (array->elements == array->max_element)
155   {
156     char *new_ptr;
157     if (array->buffer == (uchar *)(array + 1))
158     {
159       /*
160         In this senerio, the buffer is statically preallocated,
161         so we have to create an all-new malloc since we overflowed
162       */
163       if (!(new_ptr= (char *) my_malloc(array->m_psi_key,
164                                         (array->max_element+
165                                          array->alloc_increment) *
166                                         array->size_of_element,
167                                         MYF(MY_WME))))
168         return 0;
169       memcpy(new_ptr, array->buffer,
170              array->elements * array->size_of_element);
171     }
172     else
173     if (!(new_ptr=(char*) my_realloc(array->m_psi_key,
174                                      array->buffer,(array->max_element+
175                                      array->alloc_increment)*
176                                      array->size_of_element,
177                                      MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
178       return 0;
179     array->buffer= (uchar*) new_ptr;
180     array->max_element+=array->alloc_increment;
181   }
182   return array->buffer+(array->elements++ * array->size_of_element);
183 }
184 
185 
186 /*
187   Pop last element from array.
188 
189   SYNOPSIS
190     pop_dynamic()
191       array
192 
193   RETURN VALUE
194     pointer	Ok
195     0		Array is empty
196 */
197 
pop_dynamic(DYNAMIC_ARRAY * array)198 void *pop_dynamic(DYNAMIC_ARRAY *array)
199 {
200   if (array->elements)
201     return array->buffer+(--array->elements * array->size_of_element);
202   return 0;
203 }
204 
205 
206 /*
207   Get an element from array by given index
208 
209   SYNOPSIS
210     get_dynamic()
211       array
212       uchar*	Element to be returned. If idx > elements contain zeroes.
213       idx	Index of element wanted.
214 */
215 
get_dynamic(DYNAMIC_ARRAY * array,void * element,uint idx)216 void get_dynamic(DYNAMIC_ARRAY *array, void *element, uint idx)
217 {
218   if (idx >= array->elements)
219   {
220     DBUG_PRINT("warning",("To big array idx: %d, array size is %d",
221                           idx,array->elements));
222     memset(element, 0, array->size_of_element);
223     return;
224   }
225   memcpy(element,array->buffer+idx*array->size_of_element,
226          (size_t) array->size_of_element);
227 }
228 
claim_dynamic(DYNAMIC_ARRAY * array)229 void claim_dynamic(DYNAMIC_ARRAY *array)
230 {
231   /*
232     Check for a static buffer
233   */
234   if (array->buffer == (uchar *)(array + 1))
235     return;
236 
237   my_claim(array->buffer);
238 }
239 
240 
241 /*
242   Empty array by freeing all memory
243 
244   SYNOPSIS
245     delete_dynamic()
246       array	Array to be deleted
247 */
248 
delete_dynamic(DYNAMIC_ARRAY * array)249 void delete_dynamic(DYNAMIC_ARRAY *array)
250 {
251   /*
252     Just mark as empty if we are using a static buffer
253   */
254   if (array->buffer == (uchar *)(array + 1))
255     array->elements= 0;
256   else
257   if (array->buffer)
258   {
259     my_free(array->buffer);
260     array->buffer=0;
261     array->elements=array->max_element=0;
262   }
263 }
264 
265 
266 /*
267   Free unused memory
268 
269   SYNOPSIS
270     freeze_size()
271       array	Array to be freed
272 
273 */
274 
freeze_size(DYNAMIC_ARRAY * array)275 void freeze_size(DYNAMIC_ARRAY *array)
276 {
277   uint elements=MY_MAX(array->elements,1);
278 
279   /*
280     Do nothing if we are using a static buffer
281   */
282   if (array->buffer == (uchar *)(array + 1))
283     return;
284 
285   if (array->buffer && array->max_element != elements)
286   {
287     array->buffer=(uchar*) my_realloc(array->m_psi_key,
288                                       array->buffer,
289                                      elements*array->size_of_element,
290                                      MYF(MY_WME));
291     array->max_element=elements;
292   }
293 }
294