#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <strings.h>
#include "bgpd.h"
#define BITMAP_BITS 64
#define BITMAP_ALLOCBITS (4 * BITMAP_BITS)
#define BITMAP_ROUNDUP(x, y) ((((x) + (y) - 1)/(y)) * (y))
#define BITMAP_SETPTR(x) ((uint64_t)(x) | 0x1)
#define BITMAP_GETPTR(x) (uint64_t *)((x) & ~0x1)
size_t bitmap_size;
uint64_t bitmap_cnt;
static inline void
bitmap_getset(struct bitmap *map, uint64_t **ptr, uint32_t *max)
{
if ((map->data[0] & 0x1) == 0) {
*ptr = map->data;
*max = nitems(map->data) * BITMAP_BITS;
} else {
*ptr = BITMAP_GETPTR(map->data[0]);
*max = map->data[1];
}
}
static void
bitmap_free(struct bitmap *map)
{
uint64_t *ptr;
uint32_t max;
if (map->data[0] & 0x1) {
bitmap_getset(map, &ptr, &max);
bitmap_size -= max / 8;
bitmap_cnt--;
free(ptr);
}
}
static int
bitmap_resize(struct bitmap *map, uint32_t bid)
{
uint64_t *ptr, *new;
uint32_t elm, size, oldmax, newmax;
bitmap_getset(map, &ptr, &oldmax);
newmax = BITMAP_ROUNDUP(bid + 1, BITMAP_ALLOCBITS);
if ((new = malloc(newmax / 8)) == NULL)
return -1;
size = oldmax / BITMAP_BITS;
for (elm = 0; elm < size; elm++)
new[elm] = ptr[elm];
size = newmax / BITMAP_BITS;
for ( ; elm < size; elm++)
new[elm] = 0;
bitmap_free(map);
map->data[0] = BITMAP_SETPTR(new);
map->data[1] = newmax;
bitmap_size += newmax / 8;
bitmap_cnt++;
return 0;
}
int
bitmap_set(struct bitmap *map, uint32_t bid)
{
uint64_t *ptr;
uint32_t max, elm;
bitmap_getset(map, &ptr, &max);
if (bid == 0) {
errno = EINVAL;
return -1;
}
if (bid >= max) {
if (bitmap_resize(map, bid) == -1)
return -1;
bitmap_getset(map, &ptr, &max);
}
elm = bid / BITMAP_BITS;
bid %= BITMAP_BITS;
ptr[elm] |= (1ULL << bid);
return 0;
}
int
bitmap_test(struct bitmap *map, uint32_t bid)
{
uint64_t *ptr;
uint32_t max, elm;
bitmap_getset(map, &ptr, &max);
if (bid >= max || bid == 0)
return 0;
elm = bid / BITMAP_BITS;
bid %= BITMAP_BITS;
return (ptr[elm] & (1ULL << bid)) != 0;
}
void
bitmap_clear(struct bitmap *map, uint32_t bid)
{
uint64_t *ptr;
uint32_t max, elm;
bitmap_getset(map, &ptr, &max);
if (bid >= max || bid == 0)
return;
elm = bid / BITMAP_BITS;
bid %= BITMAP_BITS;
ptr[elm] &= ~(1ULL << bid);
}
int
bitmap_empty(struct bitmap *map)
{
uint64_t *ptr, m;
uint32_t elm, max, end;
bitmap_getset(map, &ptr, &max);
end = max / BITMAP_BITS;
for (elm = 0; elm < end; elm++) {
m = ptr[elm];
if (elm == 0)
m &= ~0x1;
if (m != 0)
return 0;
}
return 1;
}
int
bitmap_id_get(struct bitmap *map, uint32_t *bid)
{
uint64_t *ptr, m;
uint32_t elm, max, end;
bitmap_getset(map, &ptr, &max);
end = max / BITMAP_BITS;
for (elm = 0; elm < end; elm++) {
m = ~ptr[elm];
if (elm == 0)
m &= ~0x1;
if (m == 0)
continue;
*bid = elm * BITMAP_BITS + ffsll(m) - 1;
if (bitmap_set(map, *bid) == -1)
return -1;
return 0;
}
if (bitmap_set(map, max) == -1)
return -1;
*bid = max;
return 0;
}
void
bitmap_id_put(struct bitmap *map, uint32_t bid)
{
bitmap_clear(map, bid);
}
void
bitmap_init(struct bitmap *map)
{
map->data[0] = 0;
map->data[1] = 0;
}
void
bitmap_reset(struct bitmap *map)
{
bitmap_free(map);
bitmap_init(map);
}
void
bitmap_get_stats(long long *cnt, long long *size)
{
*cnt = bitmap_cnt;
*size = bitmap_size;
}