/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2016-2020 Intel Corporation
 */

#ifndef __DLB2_OSDEP_BITMAP_H
#define __DLB2_OSDEP_BITMAP_H

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <rte_bitmap.h>
#include <rte_string_fns.h>
#include <rte_malloc.h>
#include <rte_errno.h>
#include "../dlb2_main.h"

/*************************/
/*** Bitmap operations ***/
/*************************/
struct dlb2_bitmap {
	struct rte_bitmap *map;
	unsigned int len;
};

/**
 * dlb2_bitmap_alloc() - alloc a bitmap data structure
 * @bitmap: pointer to dlb2_bitmap structure pointer.
 * @len: number of entries in the bitmap.
 *
 * This function allocates a bitmap and initializes it with length @len. All
 * entries are initially zero.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or len is 0.
 * ENOMEM - could not allocate memory for the bitmap data structure.
 */
static inline int dlb2_bitmap_alloc(struct dlb2_bitmap **bitmap,
				    unsigned int len)
{
	struct dlb2_bitmap *bm;
	void *mem;
	uint32_t alloc_size;
	uint32_t nbits = (uint32_t)len;

	if (bitmap == NULL || nbits == 0)
		return -EINVAL;

	/* Allocate DLB2 bitmap control struct */
	bm = rte_malloc("DLB2_PF",
			sizeof(struct dlb2_bitmap),
			RTE_CACHE_LINE_SIZE);

	if (bm == NULL)
		return -ENOMEM;

	/* Allocate bitmap memory */
	alloc_size = rte_bitmap_get_memory_footprint(nbits);
	mem = rte_malloc("DLB2_PF_BITMAP", alloc_size, RTE_CACHE_LINE_SIZE);
	if (mem == NULL) {
		rte_free(bm);
		return -ENOMEM;
	}

	bm->map = rte_bitmap_init(len, mem, alloc_size);
	if (bm->map == NULL) {
		rte_free(mem);
		rte_free(bm);
		return -ENOMEM;
	}

	bm->len = len;

	*bitmap = bm;

	return 0;
}

/**
 * dlb2_bitmap_free() - free a previously allocated bitmap data structure
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * This function frees a bitmap that was allocated with dlb2_bitmap_alloc().
 */
static inline void dlb2_bitmap_free(struct dlb2_bitmap *bitmap)
{
	if (bitmap == NULL)
		return;

	rte_free(bitmap->map);
	rte_free(bitmap);
}

/**
 * dlb2_bitmap_fill() - fill a bitmap with all 1s
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * This function sets all bitmap values to 1.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized.
 */
static inline int dlb2_bitmap_fill(struct dlb2_bitmap *bitmap)
{
	unsigned int i;

	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	for (i = 0; i != bitmap->len; i++)
		rte_bitmap_set(bitmap->map, i);

	return 0;
}

/**
 * dlb2_bitmap_fill() - fill a bitmap with all 0s
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * This function sets all bitmap values to 0.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized.
 */
static inline int dlb2_bitmap_zero(struct dlb2_bitmap *bitmap)
{
	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	rte_bitmap_reset(bitmap->map);

	return 0;
}

/**
 * dlb2_bitmap_set() - set a bitmap entry
 * @bitmap: pointer to dlb2_bitmap structure.
 * @bit: bit index.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized, or bit is larger than the
 *	    bitmap length.
 */
static inline int dlb2_bitmap_set(struct dlb2_bitmap *bitmap,
				  unsigned int bit)
{
	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	if (bitmap->len <= bit)
		return -EINVAL;

	rte_bitmap_set(bitmap->map, bit);

	return 0;
}

/**
 * dlb2_bitmap_set_range() - set a range of bitmap entries
 * @bitmap: pointer to dlb2_bitmap structure.
 * @bit: starting bit index.
 * @len: length of the range.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized, or the range exceeds the bitmap
 *	    length.
 */
static inline int dlb2_bitmap_set_range(struct dlb2_bitmap *bitmap,
					unsigned int bit,
					unsigned int len)
{
	unsigned int i;

	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	if (bitmap->len <= bit)
		return -EINVAL;

	for (i = 0; i != len; i++)
		rte_bitmap_set(bitmap->map, bit + i);

	return 0;
}

/**
 * dlb2_bitmap_clear() - clear a bitmap entry
 * @bitmap: pointer to dlb2_bitmap structure.
 * @bit: bit index.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized, or bit is larger than the
 *	    bitmap length.
 */
static inline int dlb2_bitmap_clear(struct dlb2_bitmap *bitmap,
				    unsigned int bit)
{
	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	if (bitmap->len <= bit)
		return -EINVAL;

	rte_bitmap_clear(bitmap->map, bit);

	return 0;
}

/**
 * dlb2_bitmap_clear_range() - clear a range of bitmap entries
 * @bitmap: pointer to dlb2_bitmap structure.
 * @bit: starting bit index.
 * @len: length of the range.
 *
 * Return:
 * Returns 0 upon success, < 0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized, or the range exceeds the bitmap
 *	    length.
 */
static inline int dlb2_bitmap_clear_range(struct dlb2_bitmap *bitmap,
					  unsigned int bit,
					  unsigned int len)
{
	unsigned int i;

	if (bitmap  == NULL || bitmap->map == NULL)
		return -EINVAL;

	if (bitmap->len <= bit)
		return -EINVAL;

	for (i = 0; i != len; i++)
		rte_bitmap_clear(bitmap->map, bit + i);

	return 0;
}

/**
 * dlb2_bitmap_find_set_bit_range() - find an range of set bits
 * @bitmap: pointer to dlb2_bitmap structure.
 * @len: length of the range.
 *
 * This function looks for a range of set bits of length @len.
 *
 * Return:
 * Returns the base bit index upon success, < 0 otherwise.
 *
 * Errors:
 * ENOENT - unable to find a length *len* range of set bits.
 * EINVAL - bitmap is NULL or is uninitialized, or len is invalid.
 */
static inline int dlb2_bitmap_find_set_bit_range(struct dlb2_bitmap *bitmap,
						 unsigned int len)
{
	unsigned int i, j = 0;

	if (bitmap  == NULL || bitmap->map == NULL || len == 0)
		return -EINVAL;

	if (bitmap->len < len)
		return -ENOENT;

	for (i = 0; i != bitmap->len; i++) {
		if  (rte_bitmap_get(bitmap->map, i)) {
			if (++j == len)
				return i - j + 1;
		} else {
			j = 0;
		}
	}

	/* No set bit range of length len? */
	return -ENOENT;
}

/**
 * dlb2_bitmap_find_set_bit() - find an range of set bits
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * This function looks for a single set bit.
 *
 * Return:
 * Returns the base bit index upon success, -1 if not found, <-1 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized, or len is invalid.
 */
static inline int dlb2_bitmap_find_set_bit(struct dlb2_bitmap *bitmap)
{
	unsigned int i;

	if (bitmap == NULL)
		return -EINVAL;

	if (bitmap->map == NULL)
		return -EINVAL;

	for (i = 0; i != bitmap->len; i++) {
		if  (rte_bitmap_get(bitmap->map, i))
			return i;
	}

	return -ENOENT;
}

/**
 * dlb2_bitmap_count() - returns the number of set bits
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * This function looks for a single set bit.
 *
 * Return:
 * Returns the number of set bits upon success, <0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized.
 */
static inline int dlb2_bitmap_count(struct dlb2_bitmap *bitmap)
{
	int weight = 0;
	unsigned int i;

	if (bitmap == NULL)
		return -EINVAL;

	if (bitmap->map == NULL)
		return -EINVAL;

	for (i = 0; i != bitmap->len; i++) {
		if  (rte_bitmap_get(bitmap->map, i))
			weight++;
	}
	return weight;
}

/**
 * dlb2_bitmap_longest_set_range() - returns longest contiguous range of set
 *				      bits
 * @bitmap: pointer to dlb2_bitmap structure.
 *
 * Return:
 * Returns the bitmap's longest contiguous range of set bits upon success,
 * <0 otherwise.
 *
 * Errors:
 * EINVAL - bitmap is NULL or is uninitialized.
 */
static inline int dlb2_bitmap_longest_set_range(struct dlb2_bitmap *bitmap)
{
	int max_len = 0, len = 0;
	unsigned int i;

	if (bitmap == NULL)
		return -EINVAL;

	if (bitmap->map == NULL)
		return -EINVAL;

	for (i = 0; i != bitmap->len; i++) {
		if  (rte_bitmap_get(bitmap->map, i)) {
			len++;
		} else {
			if (len > max_len)
				max_len = len;
			len = 0;
		}
	}

	if (len > max_len)
		max_len = len;

	return max_len;
}

/**
 * dlb2_bitmap_or() - store the logical 'or' of two bitmaps into a third
 * @dest: pointer to dlb2_bitmap structure, which will contain the results of
 *	  the 'or' of src1 and src2.
 * @src1: pointer to dlb2_bitmap structure, will be 'or'ed with src2.
 * @src2: pointer to dlb2_bitmap structure, will be 'or'ed with src1.
 *
 * This function 'or's two bitmaps together and stores the result in a third
 * bitmap. The source and destination bitmaps can be the same.
 *
 * Return:
 * Returns the number of set bits upon success, <0 otherwise.
 *
 * Errors:
 * EINVAL - One of the bitmaps is NULL or is uninitialized.
 */
static inline int dlb2_bitmap_or(struct dlb2_bitmap *dest,
				 struct dlb2_bitmap *src1,
				 struct dlb2_bitmap *src2)
{
	unsigned int i, min;
	int numset = 0;

	if (dest  == NULL || dest->map == NULL ||
	    src1  == NULL || src1->map == NULL ||
	    src2  == NULL || src2->map == NULL)
		return -EINVAL;

	min = dest->len;
	min = (min > src1->len) ? src1->len : min;
	min = (min > src2->len) ? src2->len : min;

	for (i = 0; i != min; i++) {
		if  (rte_bitmap_get(src1->map, i) ||
		     rte_bitmap_get(src2->map, i)) {
			rte_bitmap_set(dest->map, i);
			numset++;
		} else {
			rte_bitmap_clear(dest->map, i);
		}
	}

	return numset;
}

#endif /*  __DLB2_OSDEP_BITMAP_H */
