aboutsummaryrefslogtreecommitdiffstats
path: root/lib/includes/hicn/util/vector.h
blob: e693df9e38006ab5d31a1528d95c9ee3ad5e651f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/*
 * Copyright (c) 2021 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * \file vector.h
 * \brief Resizeable static array
 *
 * A vector is a resizeable area of contiguous memory that contains elements of
 * fixed size. It is mostly useful to serve as the basis for more advanced data
 * structures such as memory pools.
 *
 * The internal API manipulates a pointer to the vector so that it can be
 * seamlessly resized, and a more convenient user interface is provided through
 * macros.
 *
 * A vector starts at index 0, and is typed according to the elements it
 * contains. For that matter, the data structure header precedes the returned
 * pointer which corresponds to the storage area.
 *
 * A vector is by default used as a stack where an end marker is maintained and
 * new elements are pushed right after this end marker (an indication of
 * the size of the vector) after ensuring the vector is sufficiently large.
 *
 * A user should not store any pointer to vector elements as this might change
 * during reallocations, but should use indices instead.
 *
 * NOTE: a maximum size is currently not implemented.
 *
 * It is freely inspired (and simplified) from the VPP infra infrastructure
 * library.
 */

#ifndef UTIL_VECTOR_H
#define UTIL_VECTOR_H

#include <stdint.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <stdbool.h>

#include "../common.h"

/******************************************************************************/
/* Vector header */

typedef struct
{
  size_t cur_size;   /** Vector current size (corresponding to the highest used
			element). */
  size_t alloc_size; /** The currently allocated size. */
  size_t max_size;   /** The maximum allowed size (0 = no limit) */
} vector_hdr_t;

/* Make sure elements following the header are aligned */
#define VECTOR_HDRLEN SIZEOF_ALIGNED (vector_hdr_t)

/* This header actually prepends the actual content of the vector */
#define vector_hdr(vector)                                                    \
  ((vector_hdr_t *) ((uint8_t *) vector - VECTOR_HDRLEN))

/******************************************************************************/
/* Helpers */

/** Local variable naming macro. */
#define _vector_var(v) _vector_##v

/**
 * @brief Allocate and initialize a vector data structure (helper function).
 *
 * @param[in,out] vector_ptr Vector to allocate and initialize.
 * @param[in] elt_size Size of a vector element.
 * @param[in] init_size Initial vector size.
 * @param[in] max_size Maximum vector size (O = unlimited).
 * @return int 0 if successful, -1 otherwise
 */
int _vector_init (void **vector_ptr, size_t elt_size, size_t init_size,
		  size_t max_size);

/**
 * @brief Free a vector data structure.
 *
 * @param vector_ptr[in] Pointer to the vector data structure to free.
 */
void _vector_free (void **vector_ptr);

/**
 * @brief Resize a vector data structure.
 *
 * @param[in] vector_ptr A pointer to the vector data structure to resize.
 * @param[in] elt_size The size of a vector element.
 * @param[in] pos The position at which the vector should be able to hold an
 * element.
 *
 * @return int Flag indicating whether the vector has been correctly resized.
 *
 * NOTE:
 *  - The resize operation does not specify the final size of the vector but
 * instead ensure that it is large enough to hold an element at the specified
 * position. This allows the caller not to care about doing successive calls to
 * this API while the vector is growing in size.
 */
int _vector_resize (void **vector_ptr, size_t elt_size, off_t pos);

/**
 * @brief Ensures a vector is sufficiently large to hold an element at the
 * given position.
 *
 * @param[in] vector_ptr A pointer to the vector data structure to resize.
 * @param[in] elt_size The size of a vector element.
 * @param[in] pos The position to validate.
 *
 * @return int Flag indicating whether the vector is available.
 *
 * NOTE:
 *  - This function should always be called before writing to a vector element
 *  to eventually make room for it (the vector will eventually be resized).
 *  - This function can fail if the vector is full and for any reason it cannot
 *  be resized.
 */
static inline int
_vector_ensure_pos (void **vector_ptr, size_t elt_size, off_t pos)
{
  vector_hdr_t *vh = vector_hdr (*vector_ptr);
  if (pos >= (off_t) vh->alloc_size)
    return _vector_resize (vector_ptr, elt_size, pos + 1);
  return 0;
}

/**
 * @brief Push an element at the end of a vector.
 *
 * @param[in] vector_ptr A pointer to the vector data structure to resize.
 * @param[in] elt_size The size of a vector element.
 * @param[in] elt The element to insert.
 *
 * NOTE:
 *  - This function ensures there is sufficient room for inserting the element,
 *  and evenutually resizes the vector to make room for it (if allowed by
 *  maximum size).
 */
static inline int
_vector_push (void **vector_ptr, size_t elt_size, void *elt)
{
  vector_hdr_t *vh = vector_hdr (*vector_ptr);
  if (_vector_ensure_pos (vector_ptr, elt_size, vh->cur_size) < 0)
    return -1;

  /* Always get header after a potential resize */
  vh = vector_hdr (*vector_ptr);
  memcpy ((uint8_t *) *vector_ptr + vh->cur_size * elt_size, elt, elt_size);
  vh = vector_hdr (*vector_ptr);
  vh->cur_size++;
  return 0;
}

/**
 * @brief Remove all the occurrencies of an element from the vector.
 * The order of the elements is NOT maintained.
 *
 * @param[in, out] vector The vector data structure to resize
 * @param[in] elt_size The size of a vector element
 * @param[in] elt The element to remove
 * @return int Number of elemets (equal to 'elt') removed from the vector
 */
static inline int
_vector_remove_unordered (void *vector, size_t elt_size, void *elt)
{
  size_t num_removed = 0;
  vector_hdr_t *vh = vector_hdr (vector);
  for (size_t i = 0; i < vector_hdr (vector)->cur_size; i++)
    {
      if (memcmp ((uint8_t *) vector + i * elt_size, elt, elt_size) == 0)
	{
	  vh->cur_size--;
	  // Copy last element to current position (hence order is not
	  // maintained)
	  memcpy ((uint8_t *) vector + i * elt_size,
		  (uint8_t *) vector + vh->cur_size * elt_size, elt_size);
	  num_removed++;
	}
    }
  return (int) num_removed;
}

/**
 * @brief Get the element at the specified position and store in 'elt'.
 *
 * @param[in] vector Pointer to the vector data structure to use
 * @param[in] pos Position of the element to retrieve
 * @param[in] elt_size The size of a vector element
 * @param[in] elt The element where the result is stored
 * @return int 0 if successful, -1 otherwise
 */
static inline int
_vector_get (void *vector, off_t pos, size_t elt_size, void *elt)
{
  vector_hdr_t *vh = vector_hdr (vector);
  if ((size_t) pos >= vh->alloc_size)
    return -1;

  memcpy (elt, (uint8_t *) vector + pos * elt_size, elt_size);
  return 0;
}

/**
 * @brief Check if specified element is present in vector.
 *
 * @param[in] vector Pointer to the vector data structure to use
 * @param[in] elt_size The size of a vector element
 * @param[in] elt The element to search for
 * @return true If specified element is contained in the vector
 * @return false
 */
static inline bool
_vector_contains (void *vector, size_t elt_size, void *elt)
{
  for (size_t i = 0; i < vector_hdr (vector)->cur_size; i++)
    {
      if (memcmp ((uint8_t *) vector + i * elt_size, elt, elt_size) == 0)
	return true;
    }

  return false;
}

/**
 * @brief Remove the element at the specified position from the vector.
 * Relative element order is preserved by shifting left the elements after the
 * target.
 *
 * @param[in, out] vector Pointer to the vector data structure to use
 * @param[in] elt_size The size of a vector element
 * @param[in] pos Position of the element to remove
 * @return int 0 if successful, -1 otherwise
 */
static inline int
_vector_remove_at (void **vector_ptr, size_t elt_size, off_t pos)
{
  vector_hdr_t *vh = vector_hdr (*vector_ptr);
  if ((size_t) pos >= vh->cur_size)
    return -1;

  // Shift backward by one position all the elements after the one specified
  memmove ((uint8_t *) (*vector_ptr) + pos * elt_size,
	   (uint8_t *) (*vector_ptr) + (pos + 1) * elt_size,
	   (vh->cur_size - 1 - pos) * elt_size);
  vh->cur_size--;

  return 0;
}

/******************************************************************************/
/* Public API */

/**
 * @brief Allocate and initialize a vector data structure.
 *
 * @param[in,out] vector Vector to allocate and initialize.
 * @param[in] init_size Initial vector size.
 * @param[in] max_size Maximum vector size (nonzero).
 *
 * NOTE:
 *  - Allocated memory is set to 0 (used by bitmap)
 */

#define vector_init(vector, init_size, max_size)                              \
  _vector_init ((void **) &vector, sizeof (vector[0]), init_size, max_size)

/**
 * @brief Free a vector data structure.
 *
 * @param[in] vector The vector data structure to free.
 */
#define vector_free(vector) _vector_free ((void **) &vector)

/**
 * @brief Resize a vector data structure.
 *
 * @param[in] vector The vector data structure to resize.
 * @param[in] pos The position at which the vector should be able to hold an
 * element.
 *
 * @return int Flag indicating whether the vector has been correctly resized.
 *
 * NOTE:
 *  - The resize operation does not specify the final size of the vector but
 * instead ensure that it is large enough to hold an element at the specified
 * position. This allows the caller not to care about doing successive calls to
 * this API while the vector is growing in size.
 *  - If the new size is smaller than the current size, the content of the
 *  vector will be truncated.
 * - Newly allocated memory is set to 0 (used by bitmap)
 */
#define vector_resize(vector)                                                 \
  _vector_resize ((void **) &(vector), sizeof ((vector)[0]), 0)

/**
 * @brief Ensures a vector is sufficiently large to hold an element at the
 * given position.
 *
 * @param[in] vector The vector for which to validate the position.
 * @param[in] pos The position to validate.
 *
 * NOTE:
 *  - This function should always be called before writing to a vector element
 *  to eventually make room for it (the vector will eventually be resized).
 */
#define vector_ensure_pos(vector, pos)                                        \
  _vector_ensure_pos ((void **) &(vector), sizeof ((vector)[0]), pos);

/**
 * @brief Push an element at the end of a vector.
 *
 * @param[in] vector The vector in which to insert the element.
 * @param[in] elt The element to insert.
 *
 * NOTE:
 *  - This function ensures there is sufficient room for inserting the element,
 *  and evenutually resizes the vector to make room for it (if allowed by
 *  maximum size).
 */
#define vector_push(vector, elt)                                              \
  ({                                                                          \
    typeof (elt) _vector_var (x) = elt;                                       \
    _vector_push ((void **) &(vector), sizeof ((vector)[0]),                  \
		  (void *) (&_vector_var (x)));                               \
  })

#define vector_at(vector, pos)                                                \
  ({                                                                          \
    assert ((size_t) pos < vector_hdr (vector)->cur_size);                    \
    (vector)[(pos)];                                                          \
  })

#define vector_set(vector, pos, elt)                                          \
  ({                                                                          \
    assert (pos < vector_hdr (vector)->cur_size);                             \
    (vector)[(pos)] = elt;                                                    \
  })

/**
 * @brief Clear the vector content, i.e. new pushes will insert starting from
 * position 0.
 *
 * @param[in, out] vector The vector to reset
 */
#define vector_reset(vector) (vector_len (vector) = 0)

/**
 * @brief Get the element at the specified position and store in 'elt'.
 *
 * @param[in] vector The vector data structure to use
 * @param[in] pos Position of the element to retrieve
 * @param[in] elt The element where the result is stored
 * @return int 0 if successful, -1 otherwise
 */
#define vector_get(vector, pos, elt)                                          \
  _vector_get ((void *) (vector), (pos), sizeof ((vector)[0]),                \
	       (void *) (&elt));

/**
 * @brief Check if specified element is present in vector.
 *
 * @param[in] vector The vector data structure to use
 * @param[in] elt The element to search for
 * @return true If specified element is contained in the vector
 * @return false
 */
#define vector_contains(vector, elt)                                          \
  ({                                                                          \
    typeof (elt) _vector_var (x) = elt;                                       \
    _vector_contains ((void *) (vector), sizeof ((vector)[0]),                \
		      (void *) (&_vector_var (x)));                           \
  })

/**
 * @brief Remove the element at the specified position from the vector.
 * Relative element order is preserved by shifting left the elements after the
 * target.
 *
 * @param[in, out] vector The vector data structure to use
 * @param[in] pos Position of the element to remove
 * @return int 0 if successful, -1 otherwise
 */
#define vector_remove_at(vector, pos)                                         \
  _vector_remove_at ((void **) &(vector), sizeof ((vector)[0]), (pos))

/**
 * @brief Remove all the occurrencies of an element from the vector.
 * The order of the elements is NOT maintained.
 *
 * @param[in, out] vector The vector data structure to resize
 * @param[in] elt The element to remove
 * @return int Number of elemets (equal to 'elt') removed from the vector
 */
#define vector_remove_unordered(vector, elt)                                  \
  ({                                                                          \
    typeof (elt) x = elt;                                                     \
    _vector_remove_unordered ((void *) (vector), sizeof ((vector)[0]),        \
			      (void *) (&x));                                 \
  })

/**
 * @brief Returns the length of a vector.
 *
 * @param[in] vector The vector from which to get the size.
 *
 * @see vector_ensure_pos
 *
 * NOTE:
 *  - The size of the vector corresponds to the highest accessed index (for
 * example as specified in the resize operation) and not the currently
 * allocated size which will typically be bigger to amortize allocations.
 *  - A user should always call vector_ensure_pos to ensure the vector is
 *  sufficiently large to hold an element at the specified position.
 */
#define vector_len(vector) vector_hdr (vector)->cur_size

/**
 * @brief Returns the allocated size of a vector.
 */
#define vector_get_alloc_size(vector) vector_hdr (vector)->alloc_size

/**
 * @brief Iterate over elements in a vector.
 *
 * @param[in] pool The vector data structure to iterate over
 * @param[in, out] eltp A pointer to the element that will be used for
 * iteration
 * @param[in] BODY Block to execute during iteration
 *
 * @note Iteration will execute BODY with eltp corresponding successively to
 * all elements found in the vector. It is implemented using the more generic
 * enumeration function.
 */
#define vector_foreach(vector, eltp, BODY)                                    \
  ({                                                                          \
    unsigned _vector_var (i);                                                 \
    vector_enumerate ((vector), _vector_var (i), (eltp), BODY);               \
  })

/**
 * @brief Helper function used by vector_foreach().
 */
#define vector_enumerate(vector, i, eltp, BODY)                               \
  ({                                                                          \
    for ((i) = 0; (i) < vector_len (vector); (i)++)                           \
      {                                                                       \
	eltp = (vector) + (i);                                                \
	{                                                                     \
	  BODY;                                                               \
	}                                                                     \
      }                                                                       \
  })

#endif /* UTIL_VECTOR_H */