vppinfra: use stored vec header size to find header
[vpp.git] / src / vppinfra / vec.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16   Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #include <vppinfra/vec.h>
39 #include <vppinfra/mem.h>
40
41 #ifndef CLIB_VECTOR_GROW_BY_ONE
42 #define CLIB_VECTOR_GROW_BY_ONE 0
43 #endif
44
45 /* Vector resize operator.  Called as needed by various macros such as
46    vec_add1() when we need to allocate memory. */
47 __clib_export void *
48 vec_resize_allocate_memory (void *v,
49                             word length_increment,
50                             uword data_bytes,
51                             uword header_bytes, uword data_align,
52                             uword numa_id)
53 {
54   vec_header_t *vh = _vec_find (v);
55   uword old_alloc_bytes, new_alloc_bytes;
56   void *old, *new;
57   void *oldheap;
58
59   header_bytes = vec_header_bytes (header_bytes);
60   data_align = data_align == 0 ? 1 : data_align;
61
62   data_bytes += header_bytes;
63
64   if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
65     {
66       oldheap = clib_mem_get_per_cpu_heap ();
67       clib_mem_set_per_cpu_heap (clib_mem_get_per_numa_heap (numa_id));
68     }
69
70   /* alignment must be power of 2 */
71   ASSERT (count_set_bits (data_align) == 1);
72
73   if (!v)
74     {
75       new = clib_mem_alloc_aligned_at_offset (data_bytes, data_align, header_bytes, 1   /* yes, call os_out_of_memory */
76         );
77       new_alloc_bytes = clib_mem_size (new);
78       CLIB_MEM_UNPOISON (new + data_bytes, new_alloc_bytes - data_bytes);
79       clib_memset (new, 0, new_alloc_bytes);
80       CLIB_MEM_POISON (new + data_bytes, new_alloc_bytes - data_bytes);
81       v = new + header_bytes;
82       _vec_len (v) = length_increment;
83       _vec_numa (v) = numa_id;
84       ASSERT (header_bytes / VEC_HEADER_ROUND <= 255);
85       _vec_find (v)->hdr_size = header_bytes / VEC_HEADER_ROUND;
86       _vec_find (v)->log2_align = min_log2 (data_align);
87       if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
88         clib_mem_set_per_cpu_heap (oldheap);
89       return v;
90     }
91
92   ASSERT (_vec_find (v)->hdr_size * VEC_HEADER_ROUND == header_bytes);
93   header_bytes = _vec_find (v)->hdr_size * VEC_HEADER_ROUND;
94
95   ASSERT (data_align == (1 << _vec_find (v)->log2_align));
96   data_align = 1 << _vec_find (v)->log2_align;
97
98   vh->len += length_increment;
99   old = v - header_bytes;
100
101   /* Vector header must start heap object. */
102   ASSERT (clib_mem_is_heap_object (old));
103
104   old_alloc_bytes = clib_mem_size (old);
105
106   /* Need to resize? */
107   if (data_bytes <= old_alloc_bytes)
108     {
109       CLIB_MEM_UNPOISON (v, data_bytes);
110       if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
111         clib_mem_set_per_cpu_heap (oldheap);
112       return v;
113     }
114
115 #if CLIB_VECTOR_GROW_BY_ONE > 0
116   new_alloc_bytes = data_bytes;
117 #else
118   new_alloc_bytes = (old_alloc_bytes * 3) / 2;
119   if (new_alloc_bytes < data_bytes)
120     new_alloc_bytes = data_bytes;
121 #endif
122
123   new =
124     clib_mem_alloc_aligned_at_offset (new_alloc_bytes, data_align,
125                                       header_bytes,
126                                       1 /* yes, call os_out_of_memory */ );
127
128   /* FIXME fail gracefully. */
129   if (!new)
130     clib_panic
131       ("vec_resize fails, length increment %d, data bytes %d, alignment %d",
132        length_increment, data_bytes, data_align);
133
134   CLIB_MEM_UNPOISON (old, old_alloc_bytes);
135   clib_memcpy_fast (new, old, old_alloc_bytes);
136   clib_mem_free (old);
137
138   /* Allocator may give a bit of extra room. */
139   new_alloc_bytes = clib_mem_size (new);
140   v = new;
141
142   /* Zero new memory. */
143   CLIB_MEM_UNPOISON (new + data_bytes, new_alloc_bytes - data_bytes);
144   memset (v + old_alloc_bytes, 0, new_alloc_bytes - old_alloc_bytes);
145   CLIB_MEM_POISON (new + data_bytes, new_alloc_bytes - data_bytes);
146
147   _vec_numa ((v + header_bytes)) = numa_id;
148   if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
149     clib_mem_set_per_cpu_heap (oldheap);
150
151   return v + header_bytes;
152 }
153
154 __clib_export uword
155 clib_mem_is_vec_h (void *v, uword header_bytes)
156 {
157   return clib_mem_is_heap_object (vec_header (v));
158 }
159
160 __clib_export u32
161 vec_len_not_inline (void *v)
162 {
163   return vec_len (v);
164 }
165
166 __clib_export void
167 vec_free_not_inline (void *v)
168 {
169   vec_free (v);
170 }
171
172 /** \cond */
173
174 #ifdef TEST
175
176 #include <stdio.h>
177
178 void
179 main (int argc, char *argv[])
180 {
181   word n = atoi (argv[1]);
182   word i, *x = 0;
183
184   typedef struct
185   {
186     word x, y, z;
187   } FOO;
188
189   FOO *foos = vec_init (FOO, 10), *f;
190
191   vec_validate (foos, 100);
192   foos[100].x = 99;
193
194   _vec_len (foos) = 0;
195   for (i = 0; i < n; i++)
196     {
197       vec_add1 (x, i);
198       vec_add2 (foos, f, 1);
199       f->x = 2 * i;
200       f->y = 3 * i;
201       f->z = 4 * i;
202     }
203
204   {
205     word n = 2;
206     word m = 42;
207     vec_delete (foos, n, m);
208   }
209
210   {
211     word n = 2;
212     word m = 42;
213     vec_insert (foos, n, m);
214   }
215
216   vec_free (x);
217   vec_free (foos);
218   exit (0);
219 }
220 #endif
221 /** \endcond */
222
223 /*
224  * fd.io coding-style-patch-verification: ON
225  *
226  * Local Variables:
227  * eval: (c-set-style "gnu")
228  * End:
229  */