aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/hicn/base
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/hicn/base')
-rw-r--r--hicn-light/src/hicn/base/CMakeLists.txt4
-rw-r--r--hicn-light/src/hicn/base/bitmap.h183
-rw-r--r--hicn-light/src/hicn/base/common.h4
-rw-r--r--hicn-light/src/hicn/base/loop.c340
-rw-r--r--hicn-light/src/hicn/base/loop.h98
-rw-r--r--hicn-light/src/hicn/base/pool.c133
-rw-r--r--hicn-light/src/hicn/base/pool.h212
-rw-r--r--hicn-light/src/hicn/base/test/CMakeLists.txt30
-rw-r--r--hicn-light/src/hicn/base/test/test-bitmap.cc141
-rw-r--r--hicn-light/src/hicn/base/test/test-hash.cc57
-rw-r--r--hicn-light/src/hicn/base/test/test-khash.cc139
-rw-r--r--hicn-light/src/hicn/base/test/test-loop.cc286
-rw-r--r--hicn-light/src/hicn/base/test/test-pool.cc210
-rw-r--r--hicn-light/src/hicn/base/test/test-vector.cc117
-rw-r--r--hicn-light/src/hicn/base/vector.c60
-rw-r--r--hicn-light/src/hicn/base/vector.h233
16 files changed, 1858 insertions, 389 deletions
diff --git a/hicn-light/src/hicn/base/CMakeLists.txt b/hicn-light/src/hicn/base/CMakeLists.txt
index 5b2677ed1..6e5ed4a3e 100644
--- a/hicn-light/src/hicn/base/CMakeLists.txt
+++ b/hicn-light/src/hicn/base/CMakeLists.txt
@@ -36,3 +36,7 @@ set(TO_INSTALL_HEADER_FILES
${HEADER_FILES}
PARENT_SCOPE
)
+
+if (BUILD_TESTS)
+ add_subdirectory(test)
+endif() \ No newline at end of file
diff --git a/hicn-light/src/hicn/base/bitmap.h b/hicn-light/src/hicn/base/bitmap.h
index df94b8039..46d473ff8 100644
--- a/hicn-light/src/hicn/base/bitmap.h
+++ b/hicn-light/src/hicn/base/bitmap.h
@@ -23,33 +23,186 @@
#ifndef UTIL_BITMAP_H
#define UTIL_BITMAP_H
+#include <assert.h>
#include <string.h>
+#include <sys/param.h> // MIN, MAX
+
+#include <hicn/util/log.h>
+
#include "common.h"
+#include "vector.h"
+
+typedef uint_fast32_t bitmap_t;
#define BITMAP_WIDTH(bitmap) (sizeof((bitmap)[0]) * 8)
-#define bitmap_init(bitmap, size) \
- vector_init(bitmap, next_pow2(size / BITMAP_WIDTH(bitmap)))
+/**
+ * @brief Allocate and initialize a bitmap
+ *
+ * @param[in,out] bitmap Bitmap to allocate and initialize
+ * @param[in] max_size Bitmap max_size
+ */
+#define bitmap_init(bitmap, init_size, max_size) \
+ vector_init(bitmap, next_pow2((init_size) / BITMAP_WIDTH(bitmap)), \
+ max_size == 0 ? 0 : next_pow2((max_size) / BITMAP_WIDTH(bitmap)))
-#define bitmap_ensure_pos(bitmap, pos) vector_ensure_pos(bitmap, pos / BITMAP_WIDTH(bitmap))
+/*
+ * @brief Ensures a bitmap is sufficiently large to hold an element at the
+ * given position.
+ *
+ * @param[in] bitmap The bitmap for which to validate the position.
+ * @param[in] pos The position to validate.
+ *
+ * NOTE:
+ * - This function should always be called before writing to a bitmap element
+ * to eventually make room for it (the bitmap will eventually be resized).
+ */
+static inline
+int
+bitmap_ensure_pos(bitmap_t ** bitmap, off_t pos)
+{
+ size_t offset = pos / BITMAP_WIDTH(*bitmap);
+ return vector_ensure_pos(*bitmap, offset);
+}
-#define bitmap_get(bitmap, i) ((bitmap)[(i) / BITMAP_WIDTH(bitmap)] & (1 << ((i) % BITMAP_WIDTH(bitmap))))
+/**
+ * @brief Returns the allocated size of a bitmap.
+ *
+ * @see listener_table_get_by_id
+ */
+#define bitmap_get_alloc_size(bitmap) vector_get_alloc_size(bitmap)
-#define bitmap_is_set(bitmap, i) (bitmap_get((bitmap), (i)) == 1)
+/**
+ * @brief Retrieve the state of the i-th bit in the bitmap.
+ *
+ * @param[in] bitmap The bitmap to access.
+ * @param[in] i The bit position.
+ */
+static inline
+int
+bitmap_get(const bitmap_t * bitmap, off_t i)
+{
+ size_t offset = i / BITMAP_WIDTH(bitmap);
+ assert(offset < bitmap_get_alloc_size(bitmap));
+ size_t pos = i % BITMAP_WIDTH(bitmap);
+ size_t shift = BITMAP_WIDTH(bitmap) - pos - 1;
+ return (bitmap[offset] >> shift) & 1;
+}
+/*
+ * @brief Returns whether the i-th bit is set (equal to 1) in a bitmap.
+ *
+ * @param[in] bitmap The bitmap to access.
+ * @param[in] i The bit position.
+ *
+ * @return bool
+ */
+#define bitmap_is_set(bitmap, i) (bitmap_get((bitmap), (i)) == 1)
#define bitmap_is_unset(bitmap, i) (bitmap_get((bitmap), (i)) == 0)
-#define bitmap_set(bitmap, i) bitmap[(i) / BITMAP_WIDTH(bitmap)] |= 1 << ((i) % BITMAP_WIDTH(bitmap))
+/*
+ * @brief Returns whether the i-th bit is unset (equal to 0) in a bitmap.
+ *
+ * @param[in] bitmap The bitmap to access.
+ * @param[in] i The bit position.
+ *
+ * @return bool
+ */
+#define bitmap_set(bitmap, i) \
+ _bitmap_set((bitmap_t**)&bitmap, i)
+
+/*
+ * @brief Returns whether the i-th bit is unset (equal to 0) in a bitmap (helper).
+ *
+ * @param[in] bitmap The bitmap to access.
+ * @param[in] i The bit position.
+ *
+ * @return bool
+ */
+static inline
+int
+_bitmap_set(bitmap_t ** bitmap, off_t i)
+{
+ if (bitmap_ensure_pos(bitmap, i) < 0)
+ return -1;
+ size_t offset = i / BITMAP_WIDTH(bitmap);
+ size_t pos = i % BITMAP_WIDTH(bitmap);
+ size_t shift = BITMAP_WIDTH(bitmap) - pos - 1;
+ (*bitmap)[offset] |= 1ul << shift;
+ return 0;
+}
+
+static inline
+int
+bitmap_unset(bitmap_t * bitmap, off_t i)
+{
+ if (bitmap_ensure_pos(&bitmap, i) < 0)
+ return -1;
+ size_t offset = i / BITMAP_WIDTH(bitmap);
+ size_t pos = i % BITMAP_WIDTH(bitmap);
+ size_t shift = BITMAP_WIDTH(bitmap) - pos - 1;
+ bitmap[offset] &= ~ (1ul << shift);
+ return 0;
+}
+
+
+static inline
+int
+bitmap_set_range(bitmap_t * bitmap, off_t from, off_t to)
+{
+ assert(from <= to);
+ ssize_t offset_from = from / BITMAP_WIDTH(bitmap);
+ ssize_t offset_to = to / BITMAP_WIDTH(bitmap);
+ size_t pos_from = from % BITMAP_WIDTH(bitmap);
+ size_t pos_to = to % BITMAP_WIDTH(bitmap);
+
+
+ /*
+ * First block initialization is needed if <from> is not aligned with the
+ * bitmap element size or if to is within the same one.
+ */
+ if ((pos_from != 0) || ((offset_to == offset_from) && (pos_to != BITMAP_WIDTH(bitmap) - 1))) {
+ size_t from_end = MIN(to, (offset_from + 1) * BITMAP_WIDTH(bitmap));
+ for (size_t k = from; k < from_end; k++) {
+ if (bitmap_set(bitmap, k) < 0)
+ goto END;
+ }
+ }
+
+ /*
+ * Second block is needed if <to> is not aligned with the bitmap element
+ * size
+ */
+ if ((pos_to != BITMAP_WIDTH(bitmap) - 1) && (offset_to != offset_from)) {
+ size_t to_start = MAX(from, offset_to * BITMAP_WIDTH(bitmap));
+ for (size_t k = to_start; k < (size_t) to; k++) {
+ if (bitmap_set(bitmap, k) < 0)
+ goto END;
+ }
+ }
+
+ if (pos_from != 0)
+ offset_from += 1;
+ if (pos_to != BITMAP_WIDTH(bitmap) - 1)
+ offset_to -= 1;
+
+ /*
+ * We need to cover both elements at position offset_from and offset_to
+ * provided that offset_from is not bigger
+ */
+ if (offset_to >= offset_from) {
+ memset(&bitmap[offset_from], 0xFF, (offset_to - offset_from + 1) * sizeof(bitmap[0]));
+ }
+
+ return 0;
+
+END:
+ ERROR("Error setting bitmap range\n");
+ return -1;
+}
-#define bitmap_unset(bitmap, i) bitmap[(i) / BITMAP_WIDTH(bitmap)] &= ~ (1 << ((i) % BITMAP_WIDTH(bitmap)))
+#define bitmap_set_to(bitmap, to) bitmap_set_range((bitmap), 0, (to))
-#define bitmap_set_to(bitmap, pos) \
-do { \
- size_t offset = (pos / BITMAP_WIDTH(bitmap) + 1); \
- memset(bitmap, 0xFF, pos * sizeof(bitmap[0])); \
- size_t set_bits = offset * BITMAP_WIDTH(bitmap); \
- for (unsigned i = pos; i < set_bits; i++) \
- bitmap_unset(bitmap, i); \
-} while(0);
+#define bitmap_free(bitmap) vector_free(bitmap)
#endif /* UTIL_BITMAP_H */
diff --git a/hicn-light/src/hicn/base/common.h b/hicn-light/src/hicn/base/common.h
index 996050308..9ba426bb8 100644
--- a/hicn-light/src/hicn/base/common.h
+++ b/hicn-light/src/hicn/base/common.h
@@ -46,7 +46,7 @@ uint32_t __inline __builtin_clz(uint32_t value) {
return 32;
}
-uint32_t __inline __builtin_clzll(uint64_t value) {
+uint32_t __inline __builtin_clzl2(uint64_t value) {
uint32_t leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value))
return 63 - leading_zero;
@@ -57,6 +57,6 @@ uint32_t __inline __builtin_clzll(uint64_t value) {
#define __builtin_clzl __builtin_clzll
#endif
-#define next_pow2(x) (x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)))
+#define next_pow2(x) (x <= 1 ? 1 : 1ul <<(64-__builtin_clzl(x-1)))
#endif /* UTIL_COMMON_H */
diff --git a/hicn-light/src/hicn/base/loop.c b/hicn-light/src/hicn/base/loop.c
index fecab9c5f..d27e81f5b 100644
--- a/hicn-light/src/hicn/base/loop.c
+++ b/hicn-light/src/hicn/base/loop.c
@@ -20,51 +20,57 @@
#include <assert.h>
#include <event2/event.h>
+#include <event2/event_struct.h>
#include <event2/thread.h>
-#include <fcntl.h> // fcntl
+#include <fcntl.h> // fcntl
#ifdef WITH_THREAD
#include <pthread.h>
#endif /* WITH_THREAD */
-#include <stdlib.h>
-#include <sys/timerfd.h>
-#include <unistd.h> // fcntl
-
#include <hicn/util/log.h>
-#include <hicn/util/map.h>
+#include <stdlib.h>
+#include <unistd.h> // fcntl
#include "loop.h"
-loop_t * MAIN_LOOP = NULL;
+loop_t *MAIN_LOOP = NULL;
/**
* \brief Holds all callback parameters
*/
typedef struct {
- void * owner;
+ void *owner;
fd_callback_t callback;
- void * data;
+ void *data;
} cb_wrapper_args_t;
-TYPEDEF_MAP_H(event_map, int, struct event *);
-TYPEDEF_MAP(event_map, int, struct event *, int_cmp, int_snprintf, generic_snprintf);
-
-/* Map that associates timer fds with their associated cb_wrapper_args_t */
-TYPEDEF_MAP_H(timer_fd_map, int, cb_wrapper_args_t *);
-TYPEDEF_MAP(timer_fd_map, int, cb_wrapper_args_t *, int_cmp, int_snprintf, generic_snprintf);
+typedef enum {
+ EVTYPE_TIMER,
+ EVTYPE_FD,
+} event_type_t;
struct loop_s {
- struct event_base * event_base;
- event_map_t * event_map;
- timer_fd_map_t * timer_fd_map;
-#ifdef WITH_THREAD
- pthread_t thread;
-#endif /* WITH_THREAD */
+ /* Libevent-based implementation */
+ struct event_base *event_base;
+};
+
+struct event_s {
+ /* Reference to loop */
+ loop_t *loop;
+
+ /* Event type*/
+ event_type_t event_type;
+
+ /* Raw event */
+ struct event raw_event;
+
+ /* Callback on event */
+ cb_wrapper_args_t callback;
};
-loop_t *
-loop_create()
+loop_t *loop_create()
{
- loop_t * loop = malloc(sizeof(loop_t));
+ loop_t *loop = malloc(sizeof(loop_t));
+
if (!loop) {
ERROR("[loop_create] Failed to allocate memory");
goto ERR_MALLOC;
@@ -75,281 +81,135 @@ loop_create()
#endif /* WITH_THREAD */
loop->event_base = event_base_new();
- if (!loop)
- goto ERR_EVENT;
-
- loop->event_map = event_map_create();
- if (!loop->event_map) {
- ERROR("[loop_create] Failed to create event_map");
- goto ERR_EVENT_MAP;
- }
-
- loop->timer_fd_map = timer_fd_map_create();
- if (!loop->timer_fd_map) {
- ERROR("[loop_create] Failed to create timer_fd_map");
- goto ERR_TIMER_FD_MAP;
- }
+ if (!loop->event_base) goto ERR_EVENT;
event_set_log_callback(NULL);
return loop;
- timer_fd_map_free(loop->timer_fd_map);
-ERR_TIMER_FD_MAP:
- event_map_free(loop->event_map);
-ERR_EVENT_MAP:
- event_base_free(loop->event_base);
ERR_EVENT:
- free(loop);
ERR_MALLOC:
return NULL;
}
-void
-loop_free(loop_t * loop)
-{
- /*
- * Release all timer cb_wrapper_args_t
- *
- * We need to stop all timers, this should release associated fd events at
- * the same time... for that reason, this code has to be called before
- * releasing events
- */
-
- int * timer_fd_map_array;
- int n = timer_fd_map_get_key_array(loop->timer_fd_map, &timer_fd_map_array);
- if (n < 0) {
- ERROR("[loop_free] Could not get event map array");
- } else {
- for (unsigned i = 0; i < n; i++) {
- int fd = timer_fd_map_array[i];
- if (loop_unregister_timer(loop, fd) < 0) {
- ERROR("[loop_free] Could not unregister timer");
- }
- }
- free(timer_fd_map_array);
- }
- timer_fd_map_free(loop->timer_fd_map);
-
- /* Release all events */
-
- int * event_map_array;
- n = event_map_get_key_array(loop->event_map, &event_map_array);
- if (n < 0) {
- ERROR("[loop_free] Could not get event map array");
- } else {
- for (unsigned i = 0; i < n; i++) {
- int fd = event_map_array[i];
- if (loop_unregister_fd(loop, fd) < 0) {
- ERROR("[loop_free] Could not unregister fd");
- }
- }
- free(event_map_array);
- }
- event_map_free(loop->event_map);
-
+void loop_free(loop_t *loop) {
event_base_free(loop->event_base);
-
free(loop);
}
-int
-loop_dispatch(loop_t * loop)
+int _loop_dispatch(loop_t *loop, int flags)
{
-#ifdef WITH_THREAD
- if (pthread_create(&loop->thread, NULL, (void * (*)(void *))event_base_dispatch, loop->event_base)) {
- fprintf(stderr, "Error creating thread\n");
- return -1;
- }
-#else
- event_base_dispatch(loop->event_base);
-#endif /* WITH_THREAD */
+ event_base_loop(loop->event_base, flags);
return 0;
}
-int
-loop_undispatch(loop_t * loop)
+int loop_dispatch(loop_t *loop)
{
-#ifdef WITH_THREAD
- DEBUG("Waiting for loop to terminate...");
- if(pthread_join(loop->thread, NULL)) {
- fprintf(stderr, "Error joining thread\n");
- return -1;
- }
- DEBUG("Loop terminated !");
-#endif /* WITH_THREAD */
- return 0;
+ return _loop_dispatch(loop, EVLOOP_NO_EXIT_ON_EMPTY);
}
-void
-loop_break(loop_t * loop)
-{
- event_base_loopbreak(loop->event_base);
-}
+int loop_undispatch(loop_t *loop) { return 0; }
+
+void loop_break(loop_t *loop) { event_base_loopbreak(loop->event_base); }
-void cb_wrapper(evutil_socket_t fd, short what, void * arg) {
- cb_wrapper_args_t * cb_wrapper_args = arg;
- cb_wrapper_args->callback(cb_wrapper_args->owner, fd, cb_wrapper_args->data);
+void cb_wrapper(evutil_socket_t fd, short what, void *arg)
+{
+ cb_wrapper_args_t *cb_wrapper_args = arg;
+ cb_wrapper_args->callback(cb_wrapper_args->owner, fd,
+ cb_wrapper_args->data);
}
-int
-loop_register_fd(loop_t * loop, int fd, void * callback_owner,
- fd_callback_t callback, void * callback_data)
+static inline void _event_create(event_t **event, loop_t *loop,
+ event_type_t type, void *callback_owner,
+ fd_callback_t callback, void *callback_data)
{
- /* This will be freed with the event */
- cb_wrapper_args_t * cb_wrapper_args = malloc(sizeof(cb_wrapper_args_t));
- *cb_wrapper_args = (cb_wrapper_args_t) {
+ *event = malloc(sizeof(event_t));
+ (*event)->callback = (cb_wrapper_args_t){
.owner = callback_owner,
.callback = callback,
.data = callback_data,
};
+ (*event)->event_type = type;
+ (*event)->loop = loop;
+}
+
+int loop_fd_event_create(event_t **event, loop_t *loop, int fd,
+ void *callback_owner, fd_callback_t callback,
+ void *callback_data)
+{
+ _event_create(event, loop, EVTYPE_FD, callback_owner, callback,
+ callback_data);
evutil_make_socket_nonblocking(fd);
- struct event * event = event_new(loop->event_base, fd, EV_READ | EV_PERSIST, cb_wrapper, cb_wrapper_args);
- if (!event) {
- ERROR("[loop_register_fd] event_new");
- goto ERR_EVENT_NEW;
- }
+ event_assign(&(*event)->raw_event, loop->event_base, fd,
+ EV_READ | EV_PERSIST, cb_wrapper, &(*event)->callback);
+
+ return 0;
+}
- if (event_add(event, NULL) < 0) {
+int loop_fd_event_register(event_t *event)
+{
+ assert(event->event_type == EVTYPE_FD);
+
+ if (event_add(&event->raw_event, NULL) < 0) {
ERROR("[loop_register_fd] event_add");
goto ERR_EVENT_ADD;
}
- if (event_map_add(loop->event_map, fd, event) < 0) {
- ERROR("[loop_register_fd] event_map_add");
- goto ERR_EVENT_MAP;
- }
-
return 0;
-ERR_EVENT_MAP:
ERR_EVENT_ADD:
- event_free(event);
-ERR_EVENT_NEW:
return -1;
}
-int
-loop_unregister_fd(loop_t * loop, int fd)
+int loop_event_unregister(event_t *event)
{
- struct event * event = NULL;
-
- if (event_map_remove(loop->event_map, fd, &event) < 0) {
- ERROR("[loop_unregister_fd] Error removing event associated to fd");
- return -1;
- }
-
- assert(event);
-
- cb_wrapper_args_t * cb_wrapper_args = event_get_callback_arg(event);
- free(cb_wrapper_args);
-
- event_del(event);
- event_free(event);
-
+ event_del(&event->raw_event);
return 0;
}
-int
-loop_timer_callback(loop_t * loop, int fd, void * data)
+int loop_timer_create(event_t **timer, loop_t *loop, void *callback_owner,
+ fd_callback_t callback, void *callback_data)
{
- char buf[1024]; /* size is not important */
- cb_wrapper_args_t * cb_wrapper_args = data;
- while (read(fd, &buf, sizeof(buf)) > 0)
- ;
+ _event_create(timer, loop, EVTYPE_TIMER, callback_owner, callback,
+ callback_data);
- int rc = cb_wrapper_args->callback(cb_wrapper_args->owner, fd,
- cb_wrapper_args->data);
+ evtimer_assign(&(*timer)->raw_event, loop->event_base, cb_wrapper,
+ &(*timer)->callback);
- return rc;
+ return 0;
}
-int
-_loop_register_timer(loop_t * loop, unsigned delay_ms, void * owner,
- fd_callback_t callback, void * data)
+static inline void _ms_to_timeval(unsigned delay_ms, struct timeval *tv)
{
- int fd = timerfd_create(CLOCK_MONOTONIC, 0);
- if (fd == -1) {
- perror("timerfd_create");
- return -1;
- }
-
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
- perror("fcntl");
- return -1;
- }
-
- struct itimerspec ts = {
- .it_interval = {
- .tv_sec = delay_ms / 1000,
- .tv_nsec = (delay_ms % 1000) * 1000000,
- },
- .it_value = {
- .tv_sec = delay_ms / 1000,
- .tv_nsec = (delay_ms % 1000) * 1000000,
- }
- };
-
- if (timerfd_settime(fd, 0, &ts, NULL) == -1) {
- perror("timerfd_settime");
- return -1;
- }
+ tv->tv_sec = delay_ms / 1000;
+ tv->tv_usec = (delay_ms % 1000) * 1000;
+}
- /* This should be freed together with the timer release */
- cb_wrapper_args_t * cb_wrapper_args = malloc(sizeof(cb_wrapper_args_t));
- *cb_wrapper_args = (cb_wrapper_args_t) {
- .owner = owner,
- .callback = callback,
- .data = data,
- };
+int loop_timer_register(event_t *timer, unsigned delay_ms)
+{
+ assert(timer->event_type == EVTYPE_TIMER);
- if (timer_fd_map_add(loop->timer_fd_map, fd, cb_wrapper_args) < 0) {
- ERROR("[loop_register_timer] Could not add cb_wrapper to timer map");
- return -1;
- }
+ struct timeval tv;
+ _ms_to_timeval(delay_ms, &tv);
- if (loop_register_fd(loop, fd, loop,
- (fd_callback_t) loop_timer_callback, cb_wrapper_args) < 0) {
- ERROR("[loop_register_timer] Error registering fd to event loop");
- return -1;
+ if (tv.tv_sec == 0 && tv.tv_usec == 0) {
+ event_active(&timer->raw_event, EV_TIMEOUT, 0);
+ } else {
+ event_add(&timer->raw_event, &tv);
}
- return fd;
+ return 0;
}
-int
-loop_unregister_timer(loop_t * loop, int fd)
+int loop_timer_is_enabled(event_t *timer)
{
- struct itimerspec ts = {
- .it_interval = {
- .tv_sec = 0,
- .tv_nsec = 0,
- },
- .it_value = { /* This value disables the timer */
- .tv_sec = 0,
- .tv_nsec = 0,
- }
- };
- ts.it_value.tv_sec = 0;
-
- if (timerfd_settime(fd, 0, &ts, NULL) == -1) {
- perror("timerfd_settime");
- return -1;
- }
-
- cb_wrapper_args_t * cb_wrapper_args;
- if (timer_fd_map_remove(loop->timer_fd_map, fd, &cb_wrapper_args) < 0) {
- ERROR("[loop_unregister_timer] Could not remove cb_wrapper from timer map");
- return -1;
- }
- assert(cb_wrapper_args);
- free(cb_wrapper_args);
-
- if (loop_unregister_fd(loop, fd) < 0) {
- ERROR("[loop_unregister_timer] Error unregistering fd from event loop");
- return -1;
- }
+ return evtimer_pending(&timer->raw_event, NULL) != 0;
+}
- return 0;
+int loop_event_free(event_t *event)
+{
+ int ret = loop_event_unregister(event);
+ free(event);
+ return ret;
}
diff --git a/hicn-light/src/hicn/base/loop.h b/hicn-light/src/hicn/base/loop.h
index b73d91738..544d73cd7 100644
--- a/hicn-light/src/hicn/base/loop.h
+++ b/hicn-light/src/hicn/base/loop.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2020 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:
@@ -18,87 +18,125 @@
* \brief Face manager main loop
*/
-#ifndef FACEMGR_LOOP_H
-#define FACEMGR_LOOP_H
+#ifndef UTIL_LOOP_H
+#define UTIL_LOOP_H
/* fd & timer callbacks */
-typedef int (*fd_callback_t)(void * owner, int fd, void * data);
+typedef int (*fd_callback_t)(void *owner, int fd, void *data);
typedef struct {
int fd;
void *owner;
fd_callback_t callback;
- //int (*callback)(void * owner, int fd, void * data);
void *data;
} fd_callback_data_t;
/* loop */
typedef struct loop_s loop_t;
+typedef struct event_s event_t;
-/* Global loop to be used in single threaded applications */
-extern loop_t * MAIN_LOOP;
+extern loop_t *MAIN_LOOP;
/**
* \brief Creates a main loop
* \return Pointer to the newly created loop, or NULL in case of error
*/
-loop_t * loop_create();
+loop_t *loop_create();
/**
* \brief Releases a loop instance and frees all associated memory
* \param [in] loop - Pointer to the loop instance to free
*/
-void loop_free(loop_t * loop);
+void loop_free(loop_t *loop);
/**
- * \brief Runs the loop instance to process events
+ * \brief Runs the loop instance to process events (helper).
* \param [in] loop - Pointer to the loop instance
+ * \param [in] flags - Loop mode: EVLOOP_ONCE, EVLOOP_NONBLOCK or EVLOOP_NO_EXIT_ON_EMPTY
* \return 0 if successful, -1 otherwise
*/
-int loop_dispatch(loop_t * loop);
+int _loop_dispatch(loop_t *loop, int flags);
+
+/**
+ * \brief Runs the loop instance to process events.
+ * \param [in] loop - Pointer to the loop instance
+ * \return 0 if successful, -1 otherwise
+ */
+int loop_dispatch(loop_t *loop);
/**
* \brief Terminates the dispatching of events
* \param [in] loop - Pointer to the loop instance
*/
-int loop_undispatch(loop_t * loop);
+int loop_undispatch(loop_t *loop);
/**
* \brief Breaks out of the loop
* \param [in] loop - Pointer to the loop instance
*/
-void loop_break(loop_t * loop);
+void loop_break(loop_t *loop);
-/**
- * \brief Registers a new file descriptor to the event loop
- * \param [in] fd - File descriptor to register
+/** Create new event associated with given fd.
+ * \param [out] event - Struct representing new fd event
+ * \param [in] loop - Loop running events
+ * \param [in] fd - fd to register
* \param [in] callback_owner - Pointer to the owner of the callack (first
* parameter of callback function)
* \param [in] callback - Callback function
* \param [in] callback_data - User data to pass alongside callback invocation
* \return 0 in case of success, -1 otherwise
*/
-int
-loop_register_fd(loop_t * loop, int fd, void * callback_owner,
- fd_callback_t callback, void * callback_data);
+int loop_fd_event_create(event_t **event, loop_t *loop, int fd,
+ void *callback_owner, fd_callback_t callback,
+ void *callback_data);
/**
- * \brief Unregisters a file descriptor from the event loop
- * \param [in] fd - File descriptor to unregister
+ * Register event in corresponding event loop.
+ * \param [in] event - Struct representing fd event
* \return 0 in case of success, -1 otherwise
*/
-int
-loop_unregister_fd(loop_t * loop, int fd);
+int loop_fd_event_register(event_t *event);
-int _loop_register_timer(loop_t * loop, unsigned delay_ms, void * owner,
- fd_callback_t callback, void * data);
+/**
+ * Unregister event from corresponding event loop.
+ * \param [in] event - Struct representing fd event
+ * \return 0 in case of success, -1 otherwise
+ */
+int loop_event_unregister(event_t *event);
-#define loop_register_timer(loop, delay_ms, owner, callback, data) \
- _loop_register_timer(loop, delay_ms, owner, (fd_callback_t) callback, data)
+/**
+ * Free event object.
+ * \param [in] event - Struct representing the event
+ * \return 0 in case of success, -1 otherwise
+ */
+int loop_event_free(event_t *event);
-int
-loop_unregister_timer(loop_t * loop, int fd);
+/** Create new timer event.
+ * \param [out] event - Struct representing new timer event
+ * \param [in] loop - Loop running events
+ * \param [in] callback_owner - Pointer to the owner of the callack (first
+ * parameter of callback function)
+ * \param [in] callback - Callback function
+ * \param [in] callback_data - User data to pass alongside callback invocation
+ * \return 0 in case of success, -1 otherwise
+ */
+int loop_timer_create(event_t **timer, loop_t *loop, void *callback_owner,
+ fd_callback_t callback, void *callback_data);
+
+/**
+ * Register event in corresponding event loop.
+ * \param [in] timer - Struct representing timer event
+ * \return 0 in case of success, -1 otherwise
+ */
+int loop_timer_register(event_t *timer, unsigned delay_ms);
+
+/**
+ * Check if timer is enabled.
+ * \param [in] timer - Struct representing timer event
+ * \return 1 if enabled, 0 otherwise
+ */
+int loop_timer_is_enabled(event_t *timer);
-#endif /* FACEMGR_LOOP_H */
+#endif /* UTIL_LOOP_H */
diff --git a/hicn-light/src/hicn/base/pool.c b/hicn-light/src/hicn/base/pool.c
index abca86422..662c1233c 100644
--- a/hicn-light/src/hicn/base/pool.c
+++ b/hicn-light/src/hicn/base/pool.c
@@ -15,48 +15,76 @@
/**
* \file pool.c
- * \brief Implementation of fixed-size pool allocator
+ * \brief Implementation of fixed-size pool allocator.
+ *
+ * NOTE:
+ * - Ideally, we should have a single realloc per resize, that would encompass
+ * both the free indices vector and bitmap, by nesting data structures. Because
+ * of the added complexity, and by lack of evidence of the need for this, we
+ * currently rely on a simpler implementation.
*/
+#include <assert.h>
#include <stdlib.h> // calloc
#include "common.h"
#include "pool.h"
+#include <stdio.h> // XXX
-/**
- * \brief Initialize the pool data structure
- * \param [in,out] pool - Pointer to the pool structure storage
- * \param [in] elt_size - Size of elements in vector
- * \param [in] max_elts - Maximum size
- *
- * Note that an empty pool might be equal to NULL
- */
void
-_pool_init(void ** pool_ptr, size_t elt_size, size_t max_elts)
+_pool_init(void ** pool_ptr, size_t elt_size, size_t init_size, size_t max_size)
{
- pool_hdr_t * ph = calloc(POOL_HDRLEN + elt_size * max_elts, 1);
+ assert(pool_ptr);
+ assert(elt_size);
+
+ init_size = next_pow2(init_size);
+
+ if (max_size && init_size > max_size)
+ goto ERR_MAX_SIZE;
+
+ /* The initial pool size is rounded to the next power of two */
+ size_t alloc_size = next_pow2(init_size);
+
+ pool_hdr_t * ph = calloc(POOL_HDRLEN + alloc_size * elt_size, 1);
if (!ph)
- abort();
+ goto ERR_MALLOC;
+
+ ph->elt_size = elt_size;
+ ph->alloc_size = alloc_size;
+ ph->max_size = max_size;
/* Free indices */
off_t * free_indices;
- vector_init(free_indices, max_elts);
+ vector_init(free_indices, init_size, max_size);
+ for(unsigned i = 0; i < init_size; i++)
+ free_indices[i] = (init_size - 1) - i;
+ vector_len(free_indices) = init_size;
+ ph->free_indices = free_indices;
+ /* Free bitmap */
uint_fast32_t * fb = ph->free_bitmap;
- bitmap_init(fb, max_elts);
- bitmap_set_to(fb, max_elts);
-
- for(unsigned i = 0; i < max_elts; i++)
- free_indices[i] = (max_elts - 1) - i;
- ph->free_indices = free_indices;
+ bitmap_init(fb, init_size, max_size);
+ bitmap_set_to(fb, init_size);
+ ph->free_bitmap = fb;
*pool_ptr = (uint8_t*)ph + POOL_HDRLEN;
+
+ return;
+
+ERR_MALLOC:
+ERR_MAX_SIZE:
+ *pool_ptr = NULL;
+ return;
}
void
_pool_free(void ** pool_ptr)
{
+ pool_hdr_t * ph = pool_hdr(*pool_ptr);
+ vector_free(ph->free_indices);
+ bitmap_free(ph->free_bitmap);
+
free(pool_hdr(*pool_ptr));
*pool_ptr = NULL;
}
@@ -65,24 +93,69 @@ void
_pool_resize(void ** pool_ptr, size_t elt_size)
{
pool_hdr_t * ph = pool_hdr(*pool_ptr);
- size_t old_elts = ph->max_elts;
- size_t new_elts = old_elts * 2;
+ size_t old_size = ph->alloc_size;
+ size_t new_size = old_size * 2;
+
+ if (ph->max_size && new_size > ph->max_size)
+ goto ERR_MAX_SIZE;
/* Double pool storage */
- ph = realloc(ph, POOL_HDRLEN + new_elts * elt_size);
+ ph = realloc(ph, POOL_HDRLEN + new_size * elt_size);
if (!ph)
- abort();
- ph->max_elts = new_elts;
+ goto ERR_REALLOC;
+ ph->elt_size = elt_size;
+ ph->alloc_size = new_size;
/*
- * After resize, the pool will have old_elts free indices, ranging from
- * old_elts to (new_elts - 1)
+ * After resize, the pool will have new free indices, ranging from
+ * old_size to (new_size - 1)
*/
- off_t * free_indices = ph->free_indices;
- vector_ensure_pos(free_indices, old_elts);
- for (unsigned i = 0; i < old_elts; i++)
- free_indices[i] = new_elts - 1 - i;
+ vector_ensure_pos(ph->free_indices, old_size);
+ for (unsigned i = 0; i < old_size; i++)
+ ph->free_indices[i] = new_size - 1 - i;
+ vector_len(ph->free_indices) = old_size;
+
+ /* We also need to update the bitmap */
+ bitmap_ensure_pos(&(ph->free_bitmap), new_size - 1);
+ bitmap_set_range(ph->free_bitmap, old_size, new_size - 1);
/* Reassign pool pointer */
*pool_ptr = (uint8_t*)ph + POOL_HDRLEN;
+
+ return;
+
+ERR_REALLOC:
+ERR_MAX_SIZE:
+ *pool_ptr = NULL;
+ return;
+}
+
+off_t
+_pool_get(void ** pool_ptr, void ** elt, size_t elt_size)
+{
+ pool_hdr_t * ph = pool_hdr(*pool_ptr);
+ uint64_t l = vector_len(ph->free_indices);
+ if (l == 0) {
+ _pool_resize(pool_ptr, elt_size);
+ ph = pool_hdr(*pool_ptr);
+ l = vector_len(ph->free_indices);
+ }
+ off_t free_id = ph->free_indices[l - 1];
+ vector_len(ph->free_indices)--;
+ bitmap_unset(ph->free_bitmap, free_id);
+ *elt = *pool_ptr + free_id * elt_size;
+ memset(*elt, 0, elt_size);
+ return free_id;
+}
+
+void
+_pool_put(void ** pool_ptr, void ** elt, size_t elt_size)
+{
+ pool_hdr_t * ph = pool_hdr(*pool_ptr);
+ uint64_t l = vector_len(ph->free_indices);
+ vector_ensure_pos(ph->free_indices, l);
+ off_t freed_id = (*elt - *pool_ptr) / elt_size;
+ ph->free_indices[l] = freed_id;
+ vector_len(ph->free_indices)++;
+ bitmap_set(ph->free_bitmap, freed_id);
}
diff --git a/hicn-light/src/hicn/base/pool.h b/hicn-light/src/hicn/base/pool.h
index 360dedc5e..85117c452 100644
--- a/hicn-light/src/hicn/base/pool.h
+++ b/hicn-light/src/hicn/base/pool.h
@@ -15,7 +15,28 @@
/**
* \file array.h
- * \brief Fixed-size pool allocator
+ * \brief Fixed-size pool allocator.
+ *
+ * This memory pool allocates a single block of memory that is used to efficiently
+ * allocate/deallocate fixed-size blocks for high churn data structures.
+ *
+ * Internally this data structure leverages a vector for managing elements (and
+ * it thus resizeable if needed), as well as a list of free indices (in the
+ * form of another vector) and a bitmap marking free indices also (for fast
+ * iteration).
+ *
+ * The internal API manipulates a pointer to the vector that that is can be
+ * seamlessly resized, and a more convenient user interface is provided through
+ * macros.
+ *
+ * The vector of free indices is managed as a stack where elements indices are
+ * retrieved from and put back to the end of the vector. In the bitmap,
+ * available elements are set to 1, and unset to 0 when in use.
+ *
+ * The pool is not currently resized down when releasing elements.
+ *
+ * It is freely inspired (and simplified) from the VPP infra infrastructure
+ * library.
*/
#ifndef UTIL_POOL_H
@@ -26,66 +47,163 @@
#include "bitmap.h"
#include "vector.h"
-/** Local variable naming macro. */
-#define _pool_var(v) _pool_##v
-
+/* Pool header */
typedef struct {
size_t elt_size;
- size_t max_elts;
- uint_fast32_t * free_bitmap;
+ size_t alloc_size;
+ size_t max_size;
+ bitmap_t * free_bitmap; /* bitmap of free indices */
off_t * free_indices; /* vector of free indices */
} pool_hdr_t;
-void _pool_init(void ** pool_ptr, size_t elt_size, size_t max_elts);
+#define POOL_HDRLEN SIZEOF_ALIGNED(pool_hdr_t)
+
+/* This header actually prepends the actual content of the pool. */
+#define pool_hdr(pool) ((pool_hdr_t *)((uint8_t*)(pool) - POOL_HDRLEN))
+
+/******************************************************************************/
+/* Helpers */
+
+/** Local variable naming macro. */
+#define _pool_var(v) _pool_##v
+
+/**
+ * @brief Allocate and initialize a pool data structure (helper).
+ *
+ * @param[in,out] pool_ptr Pointer to the pool data structure.
+ * @param[in] elt_size Size of elements in vector.
+ * @param[in] max_size Maximum size.
+ *
+ * NOTE: that an empty pool might be equal to NULL.
+ */
+void _pool_init(void ** pool_ptr, size_t elt_size, size_t init_size, size_t max_size);
+
+/**
+ * @brief Free a pool data structure (helper).
+ *
+ * @param[in] pool_ptr Pointer to the pool data structure.
+ */
void _pool_free(void ** pool_ptr);
+
+/**
+ * @brief Resize a pool data structure (helper).
+ *
+ * @param pool_ptr Pointer to the pool data structure.
+ *
+ * This function should only be called internally, as the resize is implicitly
+ * done (if allowed by the maximum size) when the user tries to get a new slot.
+ */
void _pool_resize(void ** pool_ptr, size_t elt_size);
-#define POOL_HDRLEN SIZEOF_ALIGNED(pool_hdr_t)
+/**
+ * @brief Get a free element from the pool data structure (helper).
+ *
+ * @param[in] pool Pointer to the pool data structure to use.
+ * @param[in,out] elt Pointer to an empty element that will be used to return
+ * the allocated one from the pool.
+ *
+ * NOTES:
+ * - The memory chunk is cleared upon attribution
+ */
+off_t _pool_get(void ** pool, void ** elt, size_t elt_size);
-/* This header actually prepends the actual content of the pool */
-#define pool_hdr(pool) ((pool_hdr_t *)((uint8_t*)(pool) - POOL_HDRLEN))
+/**
+ * @brief Put an element back into the pool data structure (helper).
+ *
+ * @param[in] pool_ptr Pointer to the pool data structure to use.
+ * @param[in] elt Pointer to the pool element to put back.
+ */
+void _pool_put(void ** pool, void ** elt, size_t elt_size);
-// XXX TODO need common naming for cur_len, len, max_len
-#define pool_elts(pool) \
- (pool_hdr(pool)->max_elts - vector_len((pool_hdr(pool)->free_indices)))
+/******************************************************************************/
+/* Public API */
-#define pool_init(pool, max_elts) \
- _pool_init((void**)&pool, sizeof(pool[0]), max_elts);
+/**
+ * @brief Allocate and initialize a pool data structure.
+ *
+ * @param[in,out] pool Pointer to the pool data structure.
+ * @param[in] elt_size Size of elements in pool.
+ * @param[in] max_size Maximum size.
+ *
+ * NOTE: that an empty pool might be equal to NULL.
+ */
+#define pool_init(pool, init_size, max_size) \
+ _pool_init((void**)&pool, sizeof(pool[0]), init_size, max_size);
-#define pool_free(pool) \
- _pool_free((void**)&pool);
+/**
+ * @brief Free a pool data structure.
+ *
+ * @param[in] pool The pool data structure to free.
+ */
+#define pool_free(pool) _pool_free((void**)&pool);
-#define pool_get(pool, elt) \
-do { \
- pool_hdr_t * _pool_var(ph) = pool_hdr(pool); \
- u64 _pool_var(l) = vector_len(_pool_var(ph)->free_indices); \
- if (_pool_var(l) == 0) \
- _pool_resize((void**)&(pool), sizeof((pool)[0])); \
- off_t _pool_var(free_id) = \
- _pool_var(ph)->free_indices[_pool_var(l) - 1]; \
- elt = (pool) + _pool_var(free_id); \
- memset(&elt, 0, sizeof(elt)); \
-} while(0)
+/**
+ * @brief Get a free element from the pool data structure.
+ *
+ * @param[in] pool The pool data structure to use.
+ * @param[in,out] elt An empty element that will be used to return the
+ * allocated one from the pool.
+ *
+ * NOTES:
+ * - The memory chunk is cleared upon attribution
+ */
+#define pool_get(pool, elt) _pool_get((void**)&pool, (void**)&elt, sizeof(*elt))
-#define pool_put(pool, elt) \
-do { \
- pool_hdr_t * _pool_var(ph) = pool_hdr(pool); \
- u64 _pool_var(l) = vector_len(_pool_var(ph)->free_indices); \
- vector_ensure_pos(_pool_var(ph)->free_indices, _pool_var(l)); \
- _pool_var(ph)->free_indices[_pool_var(l)] = (elt) - (pool); \
- vector_len(_pool_var(ph)->free_indices)++; \
- bitmap_set(_pool_var(ph)->free_bitmap, _pool_var(l)); \
-} while(0)
+/**
+ * @brief Put an element back into the pool data structure.
+ *
+ * @param[in] pool The pool data structure to use.
+ * @param[in] elt The pool element to put back.
+ */
+#define pool_put(pool, elt) _pool_put((void**)&pool, (void**)&elt, sizeof(*elt))
+/**
+ * @brief Validate a pool element by index.
+ *
+ * @param[in] pool The pool data structure to use.
+ * @param[in] id The index of the element to validate.
+ *
+ * @return bool A flag indicating whether the index is valid or not.
+ */
#define pool_validate_id(pool, id) \
bitmap_is_unset((pool_hdr(pool))->free_bitmap, (id))
+#define pool_get_free_indices_size(pool) vector_len(pool_hdr(pool)->free_indices)
+
+/**
+ * @brief Returns the current length of the pool.
+ *
+ * @param[in] pool The pool data structure for which to return the length.
+ *
+ * @return size_t The current length of the pool.
+ *
+ * NOTE:
+ * - The pool length corresponds to the number of allocated elements, not the
+ * size of the pool.
+ */
+#define pool_len(pool) \
+ (pool_hdr(pool)->alloc_size - pool_get_free_indices_size(pool))
+
+/**
+ * @brief Enumerate elements from a pool.
+ *
+ * @param[in] pool The pool data structure to enumerate.
+ * @param[in, out] i An integer that will be used for enumeration.
+ * @param[in, out] eltp A pointer to the element type that will be used for
+ * enumeration.
+ * @param[in] BODY Block to execute during enumeration.
+ *
+ * Enumeration will iteratively execute BODY with (i, eltp) corresponding
+ * respectively to the index and element found in the pool.
+ *
+ * NOTE: i stars at 0.
+ */
#define pool_enumerate(pool, i, eltp, BODY) \
do { \
pool_hdr_t * _pool_var(ph) = pool_hdr(pool); \
uint_fast32_t * _pool_var(fb) = _pool_var(ph)->free_bitmap; \
- for((i) = 0; (i) < _pool_var(ph)->max_elts; (i)++) { \
+ for((i) = 0; (i) < _pool_var(ph)->max_size; (i)++) { \
if (bitmap_is_unset(_pool_var(fb), (i))) \
continue; \
eltp = (pool) + (i); \
@@ -93,12 +211,28 @@ do { \
} \
} while(0)
+/**
+ * @brief Iterate over elements in a pool.
+ *
+ * @param[in] pool The pool data structure to iterate over.
+ * @param[in,out] eltp A pointer to the element type that will be used for
+ * iteration.
+ * @param[in] BODY Block to execute during iteration.
+ *
+ * Iteration will execute BODY with eltp corresponding successively to all
+ * elements found in the pool. It is implemented using the more generic
+ * enumeration function.
+ */
#define pool_foreach(pool, eltp, BODY) \
do { \
unsigned _pool_var(i); \
pool_enumerate((pool), _pool_var(i), (eltp), BODY); \
} while(0)
-
+#ifdef WITH_TESTS
+#define pool_get_alloc_size(bitmap) pool_hdr(pool)->alloc_size
+#define pool_get_free_indices(pool) pool_hdr(pool)->free_indices
+#define pool_get_free_bitmap(pool) pool_hdr(pool)->free_bitmap
+#endif /* WITH_TESTS */
#endif /* UTIL_POOL_H */
diff --git a/hicn-light/src/hicn/base/test/CMakeLists.txt b/hicn-light/src/hicn/base/test/CMakeLists.txt
new file mode 100644
index 000000000..cdf6ed11b
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+
+include(BuildMacros)
+
+list(APPEND TESTS
+ test-bitmap
+ test-hash
+ test-khash
+ test-loop
+ test-pool
+ test-vector
+)
+
+foreach(test ${TESTS})
+ build_executable(${test}
+ NO_INSTALL
+ SOURCES ${test}.cc
+ LINK_LIBRARIES ${LIBHICN_LIGHT_SHARED} ${LIBHICNCTRL_SHARED} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
+ DEPENDS gtest ${LIBHICNCTRL_SHARED} ${LIBHICN_LIGHT_SHARED}
+ COMPONENT ${HICN_LIGHT}
+ DEFINITIONS "${COMPILER_DEFINITIONS}"
+ )
+
+ if(${CMAKE_VERSION} VERSION_GREATER "3.10.0")
+ gtest_discover_tests(${test}-bin TEST_PREFIX new:)
+ else()
+ add_test(NAME ${test}-bin COMMAND ${test})
+ endif()
+endforeach()
diff --git a/hicn-light/src/hicn/base/test/test-bitmap.cc b/hicn-light/src/hicn/base/test/test-bitmap.cc
new file mode 100644
index 000000000..d1b335ace
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-bitmap.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#define WITH_TESTS
+#include <hicn/base/bitmap.h>
+}
+
+#define DEFAULT_SIZE 10
+
+class BitmapTest : public ::testing::Test {
+protected:
+ BitmapTest() { }
+
+ virtual ~BitmapTest() { }
+
+ bitmap_t * bitmap;
+};
+
+/*
+ * TEST: bitmap allocation
+ */
+TEST_F(BitmapTest, BitmapAllocation)
+{
+ int rc;
+
+ /*
+ * We take a value < 32 on purpose to avoid confusion on the choice of a 32
+ * or 64 bit integer for storage
+ */
+ size_t size_not_pow2 = DEFAULT_SIZE;
+ bitmap_init(bitmap, size_not_pow2, 0);
+
+ /*
+ * Bitmap should have been allocated with a size rounded to the next power
+ * of 2
+ */
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 1UL);
+
+ /* By default, no element should be set */
+ EXPECT_FALSE(bitmap_is_set(bitmap, 0));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, 0));
+
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 1UL);
+
+ EXPECT_FALSE(bitmap_is_set(bitmap, size_not_pow2 - 1));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, size_not_pow2 - 1));
+
+ /* Bitmap should not have been reallocated */
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 1UL);
+
+ /* After setting a bit after the end, bitmap should have been reallocated */
+ bitmap_set(bitmap, sizeof(bitmap[0]) * 8 - 1);
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 1UL);
+
+ /* After setting a bit after the end, bitmap should have been reallocated */
+ rc = bitmap_set(bitmap, sizeof(bitmap[0]) * 8);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 2UL);
+
+ rc = bitmap_set(bitmap, sizeof(bitmap[0]) * 8 + 1);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 2UL);
+
+ bitmap_free(bitmap);
+
+ size_t size_pow2 = 16;
+
+ /* Limiting test for allocation size */
+ bitmap_init(bitmap, size_pow2, 0);
+ EXPECT_EQ(bitmap_get_alloc_size(bitmap), 1UL);
+
+ bitmap_free(bitmap);
+}
+
+TEST_F(BitmapTest, BitmapSet)
+{
+ bitmap_init(bitmap, DEFAULT_SIZE, 0);
+
+ bitmap_set(bitmap, 20);
+ EXPECT_TRUE(bitmap_is_set(bitmap, 20));
+ EXPECT_FALSE(bitmap_is_unset(bitmap, 20));
+ EXPECT_FALSE(bitmap_is_set(bitmap, 19));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, 19));
+
+ bitmap_free(bitmap);
+}
+
+TEST_F(BitmapTest, BitmapUnSet) {
+ bitmap_init(bitmap, DEFAULT_SIZE, 0);
+
+ bitmap_set(bitmap, 20);
+ bitmap_set(bitmap, 19);
+ bitmap_unset(bitmap, 20);
+ EXPECT_FALSE(bitmap_is_set(bitmap, 20));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, 20));
+ EXPECT_TRUE(bitmap_is_set(bitmap, 19));
+ EXPECT_FALSE(bitmap_is_unset(bitmap, 19));
+
+ bitmap_free(bitmap);
+}
+
+TEST_F(BitmapTest, BitmapSetTo) {
+ bitmap_init(bitmap, DEFAULT_SIZE, 0);
+
+ bitmap_set_to(bitmap, 40);
+ EXPECT_TRUE(bitmap_is_set(bitmap, 20));
+ EXPECT_TRUE(bitmap_is_set(bitmap, 21));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, 41));
+ EXPECT_TRUE(bitmap_is_unset(bitmap, 42));
+
+ bitmap_free(bitmap);
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/test/test-hash.cc b/hicn-light/src/hicn/base/test/test-hash.cc
new file mode 100644
index 000000000..78e318794
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-hash.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#include <hicn/base/hash.h>
+}
+
+class HashTest : public ::testing::Test {
+ protected:
+ HashTest() {
+ }
+
+ virtual ~HashTest() {
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ virtual void SetUp() {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ virtual void TearDown() {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/test/test-khash.cc b/hicn-light/src/hicn/base/test/test-khash.cc
new file mode 100644
index 000000000..798063ca5
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-khash.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#include <hicn/base/khash.h>
+
+}
+
+KHASH_MAP_INIT_INT(int, unsigned char)
+
+typedef struct {
+ unsigned key;
+ unsigned char val;
+} int_unpack_t;
+
+typedef struct {
+ unsigned key;
+ unsigned char val;
+} __attribute__ ((__packed__)) int_packed_t;
+
+#define hash_eq(a, b) ((a).key == (b).key)
+#define hash_func(a) ((a).key)
+
+KHASH_INIT(iun, int_unpack_t, char, 0, hash_func, hash_eq)
+KHASH_INIT(ipk, int_packed_t, char, 0, hash_func, hash_eq)
+
+class KHashTest : public ::testing::Test {
+ protected:
+ KHashTest() {
+ }
+
+ virtual ~KHashTest() {
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ virtual void SetUp() {
+
+ khash = kh_init(int);
+ }
+
+ virtual void TearDown() {
+
+ kh_destroy(int, khash);
+
+ }
+ khash_t(int) *khash;
+};
+
+
+TEST_F(KHashTest, KhashIntSize)
+{
+ int ret;
+ int k;
+ int size = kh_size(khash);
+
+ EXPECT_EQ(size, 0);
+ k = kh_put(int, khash, 10, &ret);
+ if (ret == 1) {
+ kh_val(khash, k) = 10;
+ }
+ size = kh_size(khash);
+ EXPECT_EQ(size, 1);
+
+}
+
+TEST_F(KHashTest, KhashIntPut)
+{
+ int ret;
+ int k;
+ k = kh_put(int, khash, 10, &ret);
+ if (ret == 1) {
+ kh_val(khash, k) = 10;
+ }
+ int size = kh_size(khash);
+ EXPECT_EQ(size, 1);
+ k = kh_put(int, khash, 20, &ret);
+ if (ret == 1) {
+ kh_val(khash, k) = 20;
+ }
+ size = kh_size(khash);
+ EXPECT_EQ(size, 2);
+}
+
+TEST_F(KHashTest, KhashCheckValue)
+{
+ int ret;
+ int k;
+ k = kh_put(int, khash, 10, &ret);
+ if (ret == 1) {
+ kh_val(khash, k) = 100;
+ }
+ k = kh_put(int, khash, 20, &ret);
+ if (ret == 1) {
+ kh_val(khash, k) = 200;
+ }
+
+ k = kh_put(int, khash, 10, &ret);
+ int val = -1;
+ if (!ret)
+ val = kh_val(khash, k);
+ EXPECT_EQ(val, 100);
+
+ k = kh_put(int, khash, 20, &ret);
+ val = -1;
+ if (!ret)
+ val = kh_val(khash, k);
+ EXPECT_EQ(val, 200);
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/test/test-loop.cc b/hicn-light/src/hicn/base/test/test-loop.cc
new file mode 100644
index 000000000..d50b2472a
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-loop.cc
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#include <hicn/base/loop.h>
+}
+
+class LoopTest : public ::testing::Test {
+ static constexpr uint16_t BUFFER_SIZE = 1024;
+ protected:
+ LoopTest()
+ : server_port_(9191),
+ loop_(nullptr),
+ timer_tick_(2000),
+ connection_socket_(-1),
+ event_(nullptr),
+ timer_(nullptr) {
+ }
+
+ virtual ~LoopTest() {
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ virtual void SetUp() {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ virtual void TearDown() {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+
+ static int onFirstTimerExpiration(void *owner, int fd, void* arg) {
+ std::cout << "This function should never be called" << std::endl;
+ EXPECT_TRUE(false);
+ return -1;
+ }
+
+ static int onSecondTimerExpiration(void *owner, int fd, void* arg) {
+ std::cout << "First timer expired. Cancel second timer." << std::endl;
+ LoopTest *test = (LoopTest *)(arg);
+ loop_event_unregister(test->timer_);
+ return 0;
+ }
+
+ static int onTimerExpiration(void *owner, int fd, void* arg) {
+ // Create client socket
+ struct sockaddr_in addr;
+ int client_socket;
+ LoopTest *test = (LoopTest *)(arg);
+
+ client_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (client_socket == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(test->server_port_);
+
+ if (connect(client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
+ perror("connect");
+ return -1;
+ }
+
+ if (send(client_socket, "Hello, world!\n", 14, 0) == -1){
+ perror("send");
+ return -1;
+ }
+
+ close(client_socket);
+ loop_event_unregister(test->timer_);
+
+ return 0;
+ }
+
+ static int onNewConnection(void *owner, int fd, void* arg) {
+ LoopTest *test = (LoopTest *)arg;
+ struct sockaddr_in addr;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_family = AF_INET;
+
+ socklen_t addr_len = sizeof(struct sockaddr_in);
+ int ret;
+
+ int client_fd = accept(test->connection_socket_, (struct sockaddr*)(&addr), &addr_len);
+ if (client_fd == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ fprintf(stderr, "accept failed");
+ }
+
+ perror("accept");
+ return -1;
+ }
+
+ // Read whatever data available and close connection.
+ ret = read(client_fd, test->buffer, BUFFER_SIZE);
+ if (ret < 0) {
+ perror("read");
+ return -1;
+ }
+
+ test->buffer[ret] = '\0';
+ std::cout << "Received: " << (char*)test->buffer << std::endl;
+
+ close(client_fd);
+ loop_event_unregister(test->event_);
+
+ return 0;
+ }
+
+ void createTcpSocketServer() {
+ struct sockaddr_in addr;
+ int ret;
+
+ /* Create local socket. */
+
+ connection_socket_ = socket(AF_INET, SOCK_STREAM, 0);
+ if (connection_socket_ == -1) {
+ perror("socket");
+ return;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(server_port_);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ ret = bind(connection_socket_, (const struct sockaddr *) &addr,
+ sizeof(struct sockaddr_in));
+ if (ret == -1) {
+ perror("bind");
+ return;
+ }
+
+ ret = listen(connection_socket_, 20);
+ if (ret == -1) {
+ perror("listen");
+ return;
+ }
+}
+
+ uint16_t server_port_;
+ loop_t *loop_;
+ unsigned timer_tick_;
+ int connection_socket_;
+ event_t *event_;
+ event_t *timer_;
+ char buffer[BUFFER_SIZE];
+};
+
+TEST_F(LoopTest, LoopCreateAndFree)
+{
+ loop_ = loop_create();
+ EXPECT_TRUE(loop_ != NULL);
+ loop_free (loop_);
+ EXPECT_TRUE(loop_ != NULL);
+}
+
+TEST_F(LoopTest, EventCreateAndFree)
+{
+ int ret;
+
+ // Fake fd
+ int fd = 1;
+ loop_ = loop_create();
+
+ ret = loop_fd_event_create(&event_, loop_, fd, nullptr, &LoopTest::onNewConnection, this);
+ EXPECT_TRUE(ret >= 0);
+ EXPECT_TRUE(event_);
+
+ // Register the event
+ ret = loop_fd_event_register(event_);
+ EXPECT_TRUE(ret >= 0);
+
+ // Unregister the event
+ ret = loop_event_free(event_);
+ EXPECT_TRUE(ret >= 0);
+
+ // Free event loop
+ loop_free (loop_);
+}
+
+TEST_F(LoopTest, TimerCreateAndCancel)
+{
+ int ret;
+ event_t *timer2;
+ loop_ = loop_create();
+
+ ret = loop_timer_create(&timer_, loop_, nullptr, &LoopTest::onFirstTimerExpiration, this);
+ EXPECT_TRUE(ret >= 0);
+ EXPECT_TRUE(timer_);
+
+ ret = loop_timer_create(&timer2, loop_, nullptr, &LoopTest::onSecondTimerExpiration, this);
+ EXPECT_TRUE(ret >= 0);
+ EXPECT_TRUE(timer2);
+
+ // Register the 1st timer
+ ret = loop_timer_register(timer_, timer_tick_);
+ EXPECT_TRUE(ret >= 0);
+
+ // Register the 2nd timer
+ ret = loop_timer_register(timer2, timer_tick_ / 2);
+ EXPECT_TRUE(ret >= 0);
+
+ _loop_dispatch(loop_, 0);
+
+ loop_undispatch(loop_);
+
+ // Unregister the events
+ ret = loop_event_free(timer_);
+ ret += loop_event_free(timer2);
+ EXPECT_TRUE(ret >= 0);
+
+ // Free event loop
+ loop_free (loop_);
+}
+
+TEST_F(LoopTest, LoopDispatch)
+{
+ int ret;
+
+ // Create new unix socket
+ createTcpSocketServer();
+ loop_ = loop_create();
+
+ ret = loop_fd_event_create(&event_, loop_, connection_socket_, nullptr, &LoopTest::onNewConnection, this);
+ EXPECT_TRUE(ret >= 0);
+ EXPECT_TRUE(event_);
+
+ ret = loop_fd_event_register(event_);
+ EXPECT_TRUE(ret >= 0);
+
+ // Create timer.
+ ret = loop_timer_create(&timer_, loop_, nullptr, &LoopTest::onTimerExpiration, this);
+ EXPECT_TRUE(ret >= 0);
+ EXPECT_TRUE(timer_);
+
+ ret = loop_timer_register(timer_, timer_tick_);
+ EXPECT_TRUE(ret >= 0);
+
+ // Start event dispatching
+ _loop_dispatch(loop_, 0);
+
+ // Stop dispatching
+ loop_undispatch(loop_);
+
+ // Free events
+ loop_event_free(timer_);
+ loop_event_free(event_);
+
+ // Free event loop
+ loop_free (loop_);
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/test/test-pool.cc b/hicn-light/src/hicn/base/test/test-pool.cc
new file mode 100644
index 000000000..fd0d5988b
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-pool.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#define WITH_TESTS
+#include <hicn/base/pool.h>
+}
+
+/*
+ * TODO
+ * - test max_size
+ */
+
+#define DEFAULT_SIZE 10
+
+class PoolTest : public ::testing::Test {
+protected:
+ PoolTest() { }
+ virtual ~PoolTest() { }
+
+ int *pool;
+};
+
+TEST_F(PoolTest, PoolAllocation)
+{
+ int rc;
+
+ pool_init(pool, DEFAULT_SIZE, 0);
+
+ size_t pool_size = next_pow2(DEFAULT_SIZE);
+
+ EXPECT_EQ(pool_get_alloc_size(pool), pool_size);
+
+ /* Check that free indices and bitmaps are correctly initialize */
+ off_t * fi = pool_get_free_indices(pool);
+ EXPECT_EQ(vector_len(fi), pool_size);
+ EXPECT_EQ(fi[0], (long) (pool_size - 1));
+ EXPECT_EQ(fi[pool_size - 1], 0);
+
+ /* The allocated size of the underlying vector should be the next power of two */
+ EXPECT_EQ(vector_get_alloc_size(fi), pool_size);
+
+ bitmap_t * fb = pool_get_free_bitmap(pool);
+ EXPECT_TRUE(bitmap_is_set(fb, 0));
+ EXPECT_TRUE(bitmap_is_set(fb, pool_size - 2));
+ EXPECT_TRUE(bitmap_is_set(fb, pool_size - 1));
+ EXPECT_TRUE(bitmap_is_unset(fb, pool_size));
+
+ /* Getting elements from the pool should correctly update the free indices
+ * and bitmap */
+ int * elt;
+
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(vector_len(fi), pool_size - 1);
+ EXPECT_TRUE(bitmap_is_unset(fb, 0));
+
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(vector_len(fi), pool_size - 2);
+ EXPECT_TRUE(bitmap_is_unset(fb, 1));
+
+ for (unsigned i = 0; i < pool_size - 4; i++) {
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+ }
+
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(vector_len(fi), 1UL);
+ EXPECT_TRUE(bitmap_is_unset(fb, pool_size - 2));
+
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+ EXPECT_EQ(vector_len(fi), 0UL);
+ EXPECT_TRUE(bitmap_is_unset(fb, pool_size - 1));
+
+ /*
+ * Getting elements within the allocated range should not have triggered a
+ * resize
+ */
+ EXPECT_EQ(pool_len(pool), pool_size);
+
+ /*
+ * Getting elements once the allocated range has been exceeded should
+ * trigger a resize
+ */
+ rc = pool_get(pool, elt);
+ EXPECT_GE(rc, 0);
+
+ EXPECT_EQ(pool_get_alloc_size(pool), pool_size * 2);
+
+ EXPECT_EQ(pool_len(pool), pool_size + 1);
+
+ /*
+ * Doubling the size, we should have again pool_size elements free, minus 1
+ */
+ EXPECT_EQ(pool_get_free_indices_size(pool), pool_size - 1);
+
+ /*
+ * NOTE: this is wrong as there has been a realloc and the old fi
+ * pointer is now invalid
+ */
+ //EXPECT_EQ(vector_len(fi), pool_size - 1);
+
+ /* And the bitmap should also be correctly modified */
+ fb = pool_get_free_bitmap(pool);
+ EXPECT_TRUE(bitmap_is_unset(fb, pool_size));
+
+ /* Check that surrounding values are also correct */
+ EXPECT_TRUE(bitmap_is_unset(fb, pool_size - 1));
+ EXPECT_TRUE(bitmap_is_set(fb, pool_size + 1));
+
+ /* Setting elements after should through */
+
+ /* Check that free indices and bitmaps are correctly updated */
+
+ pool_free(pool);
+}
+
+TEST_F(PoolTest, PoolPut)
+{
+ pool_init(pool, DEFAULT_SIZE, 0);
+
+ int* elt;
+ pool_get(pool, elt);
+ *elt = 10;
+ pool_put(pool, elt);
+
+ pool_free(pool);
+}
+
+TEST_F(PoolTest, PoolGetForceBitmapRealloc)
+{
+ const int N = 64;
+ int *elts[N];
+ int *elt = NULL;
+ pool_init(pool, N, 0);
+
+ for (int i = 0; i < N; i++)
+ pool_get(pool, elts[i]);
+ pool_get(pool, elt);
+
+ pool_free(pool);
+}
+
+TEST_F(PoolTest, PoolGetAfterReleasing)
+{
+ int *elt1 = NULL, *elt2 = NULL, *tmp = NULL;
+ pool_init(pool, DEFAULT_SIZE, 0);
+
+ // If two elements are requested...
+ off_t id1 = pool_get(pool, elt1);
+ pool_get(pool, tmp);
+
+ // ...and the first one is released...
+ pool_put(pool, elt1);
+
+ // ...requesting a new one should return
+ // the first one (that was freed)
+ off_t id2 = pool_get(pool, elt2);
+ EXPECT_EQ(id1, id2);
+ EXPECT_EQ(elt1, elt2);
+
+ pool_free(pool);
+}
+
+TEST_F(PoolTest, PoolGetMultipleElementsAfterReleasing)
+{
+ const int N = 2;
+ int *elts[N];
+ pool_init(pool, N, 0);
+
+ for (int i = 0; i < N; i++)
+ pool_get(pool, elts[i]);
+ for (int i = 0; i < N; i++)
+ pool_put(pool, elts[i]);
+ for (int i = 0; i < N; i++)
+ pool_get(pool, elts[i]);
+
+ pool_free(pool);
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/test/test-vector.cc b/hicn-light/src/hicn/base/test/test-vector.cc
new file mode 100644
index 000000000..aaec7a92c
--- /dev/null
+++ b/hicn-light/src/hicn/base/test/test-vector.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+extern "C" {
+#define WITH_TESTS
+#include <hicn/base/vector.h>
+}
+
+/*
+ * TODO
+ * - test max_size
+ */
+
+#define DEFAULT_SIZE 10
+
+class VectorTest : public ::testing::Test {
+protected:
+ VectorTest() { }
+ virtual ~VectorTest() { }
+
+ int *vector = NULL;
+};
+
+/* TEST: Vector allocation and initialization */
+TEST_F(VectorTest, VectorAllocate)
+{
+ vector_init(vector, DEFAULT_SIZE, 0);
+
+ /* Allocated size should be the next power of two */
+ EXPECT_EQ(vector_get_alloc_size(vector), 16UL);
+
+ /* Setting elements within the allocated size should not trigger a resize */
+ vector_ensure_pos(vector, 15);
+ EXPECT_EQ(vector_get_alloc_size(vector), 16UL);
+
+ /* Setting elements after should through */
+ vector_ensure_pos(vector, 16);
+ EXPECT_EQ(vector_get_alloc_size(vector), 32UL);
+
+ /* Check that free indices and bitmaps are correctly updated */
+
+ vector_free(vector);
+}
+
+TEST_F(VectorTest, VectorSize)
+{
+ vector_init(vector, DEFAULT_SIZE, 0);
+
+ vector_push(vector, 109);
+ int size = vector_len(vector);
+ EXPECT_EQ(size, 1);
+ vector_push(vector, 109);
+ size = vector_len(vector);
+ EXPECT_EQ(size, 2);
+ vector_push(vector, 109);
+ size = vector_len(vector);
+ EXPECT_EQ(size, 3);
+
+ vector_free(vector);
+}
+
+TEST_F(VectorTest, VectorCheckValue)
+{
+ vector_init(vector, DEFAULT_SIZE, 0);
+
+ vector_push(vector, 109);
+ vector_push(vector, 200);
+ EXPECT_EQ(vector[0], 109);
+ EXPECT_EQ(vector[1], 200);
+
+ vector_free(vector);
+}
+
+TEST_F(VectorTest, VectorEnsurePos)
+{
+ vector_init(vector, DEFAULT_SIZE, 0);
+
+ printf (" %p\n", vector);
+ vector_ensure_pos(vector, 1025);
+ for (int i = 0; i <1025; i++) {
+ //printf("i %d\n", i);
+ //printf (" %p\n", vector);
+ vector_push(vector, i);
+ }
+ int size = vector_len(vector);
+ EXPECT_EQ(size, 1025);
+
+ vector_free(vector);
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/hicn-light/src/hicn/base/vector.c b/hicn-light/src/hicn/base/vector.c
index 00ed7c305..43110414a 100644
--- a/hicn-light/src/hicn/base/vector.c
+++ b/hicn-light/src/hicn/base/vector.c
@@ -18,16 +18,30 @@
* \brief Implementation of resizeable static array
*/
+#include <assert.h>
#include <stddef.h> // size_t
#include <stdlib.h> // calloc
+#include <stdio.h>
#include "vector.h"
+#define DEFAULT_VECTOR_SIZE 64
+
void
-_vector_init(void ** vector_ptr, size_t elt_size, size_t max_elts)
+_vector_init(void ** vector_ptr, size_t elt_size, size_t init_size, size_t max_size)
{
- vector_hdr_t * vh = calloc(VECTOR_HDRLEN + elt_size * max_elts, 1);
- *vector_ptr = (uint8_t*)vh - VECTOR_HDRLEN;
+ assert(vector_ptr);
+ assert(max_size == 0 || init_size < max_size);
+
+ if (init_size == 0)
+ init_size = DEFAULT_VECTOR_SIZE;
+
+ *vector_ptr = NULL;
+ _vector_resize(vector_ptr, elt_size, init_size);
+
+ vector_hdr_t * vh = vector_hdr(*vector_ptr);
+ vh->cur_size = 0;
+ vh->max_size = max_size;
}
void
@@ -37,18 +51,42 @@ _vector_free(void ** vector_ptr)
*vector_ptr = NULL;
}
-void
+int
_vector_resize(void ** vector_ptr, size_t elt_size, off_t pos)
{
- vector_hdr_t * vh = vector_hdr(*vector_ptr);
- size_t new_elts = (pos > 0) ? next_pow2(pos) : vh->max_elts * 2;
+ vector_hdr_t * vh;
- /* Double the allocated vector size */
- vh = realloc(vh, VECTOR_HDRLEN + new_elts * elt_size);
+ size_t old_size;
+
+ if (*vector_ptr) {
+ vh = vector_hdr(*vector_ptr);
+ old_size = vh->alloc_size;
+ } else {
+ vh = NULL;
+ old_size = 0;
+ }
+
+ /* Round the allocated size to the next power of 2 of the requested position */
+ size_t new_size = next_pow2(pos);
+
+ /* Don't grow the vector back */
+ if (new_size < old_size)
+ return 0;
+
+ /* Don't exceed maximum size (for init, check is done beforehand) */
+ if (vh && vh->max_size && new_size > vh->max_size)
+ return -1;
+
+ vh = realloc(vh, VECTOR_HDRLEN + new_size * elt_size);
if (!vh)
- abort();
- vh->max_elts = new_elts;
+ return -1;
+ vh->alloc_size = new_size;
+
+ /* Zero out the newly allocated memory (except headers) */
+ memset((uint8_t*)vh + VECTOR_HDRLEN + old_size * elt_size, 0, (new_size - old_size) * elt_size);
/* Reassign vector pointer */
- *vector_ptr = (uint8_t*) + VECTOR_HDRLEN;
+ *vector_ptr = (uint8_t*)vh + VECTOR_HDRLEN;
+
+ return 0;
}
diff --git a/hicn-light/src/hicn/base/vector.h b/hicn-light/src/hicn/base/vector.h
index 9b99ba813..fd8401e6d 100644
--- a/hicn-light/src/hicn/base/vector.h
+++ b/hicn-light/src/hicn/base/vector.h
@@ -16,55 +16,244 @@
/**
* \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 "common.h"
-/** Local variable naming macro. */
-#define _vector_var(v) _vector_##v
+/******************************************************************************/
+/* Vector header */
typedef struct {
- size_t num_elts;
- size_t max_elts;
+ 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;
-void _vector_init(void ** vector_ptr, size_t elt_size, size_t max_elts);
-void _vector_free(void ** vector_ptr);
-void _vector_resize(void ** vector_ptr, size_t elt_size, off_t pos);
-
/* 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))
-#define vector_init(vector, max_elts) \
- _vector_init((void**)&vector, sizeof(vector[0]), max_elts)
+/******************************************************************************/
+/* Helpers */
-#define vector_free(vector) \
- _vector_free(&vector)
+/** 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] max_size Maximum vector size (O = unlimited).
+ */
+void _vector_init(void ** vector_ptr, size_t elt_size, size_t init_size, size_t max_size);
-#define vector_len(vector) (vector_hdr(vector)->num_elts)
+/**
+ * @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 */
+ memcpy((uint8_t*)*vector_ptr + vh->cur_size * elt_size, elt, elt_size);
+ vh = vector_hdr(*vector_ptr);
+ 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] 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)
-#define vector_ensure_pos(vector, pos) \
-do { \
- if ((pos) >= vector_len(vector)) \
- _vector_resize((void**)&(vector), sizeof((vector)[0]), pos); \
-} while(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);
-#define vector_push(vector, elt) \
-do { \
- vector_ensure_pos(vector_len(vector)); \
- vector[vector_len(vector)++] = elt; \
+/**
+ * @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) \
+do { \
+ typeof(elt) x = elt; \
+ _vector_push((void**)&(vector), sizeof((vector)[0]), (void*)(&x)); \
} while(0)
+/**
+ * @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
+
#endif /* UTIL_VECTOR_H */