ba842e64d25b32acbdf11434d31ace7e1c1d1b88
[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 u32
155 vec_len_not_inline (void *v)
156 {
157   return vec_len (v);
158 }
159
160 __clib_export void
161 vec_free_not_inline (void *v)
162 {
163   vec_free (v);
164 }
165
166 /** \cond */
167
168 #ifdef TEST
169
170 #include <stdio.h>
171
172 void
173 main (int argc, char *argv[])
174 {
175   word n = atoi (argv[1]);
176   word i, *x = 0;
177
178   typedef struct
179   {
180     word x, y, z;
181   } FOO;
182
183   FOO *foos = vec_init (FOO, 10), *f;
184
185   vec_validate (foos, 100);
186   foos[100].x = 99;
187
188   _vec_len (foos) = 0;
189   for (i = 0; i < n; i++)
190     {
191       vec_add1 (x, i);
192       vec_add2 (foos, f, 1);
193       f->x = 2 * i;
194       f->y = 3 * i;
195       f->z = 4 * i;
196     }
197
198   {
199     word n = 2;
200     word m = 42;
201     vec_delete (foos, n, m);
202   }
203
204   {
205     word n = 2;
206     word m = 42;
207     vec_insert (foos, n, m);
208   }
209
210   vec_free (x);
211   vec_free (foos);
212   exit (0);
213 }
214 #endif
215 /** \endcond */
216
217 /*
218  * fd.io coding-style-patch-verification: ON
219  *
220  * Local Variables:
221  * eval: (c-set-style "gnu")
222  * End:
223  */