/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * (c) Copyright 2006,2007,2008 Jari OS Core Team <http://jarios.org>
 * (c) Copyright 2008 Dmitry Gromada <gromada82@gmail.com>
 * (c) Copyright 2010 Alexander Vdolainen <vdo@askele.com>
 *
 * (c) Copyright 2012 - 2013 Askele Oy <http://askele.com>
 *
 * Implements bitwise operations with multiword bitmaps
 */

#ifndef __BITWISE_H__
#define __BITWISE_H__

/* TODO: add arch deps */
//#include <arch/bitwise.h>

/* TODO: add linux headers tests */
#include <asm-generic/bitsperlong.h>

#ifdef __BITS_PER_LONG
#define BITS_PER_LONG  __BITS_PER_LONG
#endif

#if BITS_PER_LONG == 64
#define TYPE_LONG_SHIFT  6
#endif

#if BITS_PER_LONG == 32
#define TYPE_LONG_SHIFT  5
#endif

typedef struct __bitmap {
  int nwords;
  unsigned long *map;
} bitmap_t;

/**
 * @fn static inline void bit_set(void *bitmap, int bit)
 * Set bit number @a bit in the bitmap @a bitmap
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param[out] bitmap - A pointer to the bitmap
 * @param bit - Number of bit to set
 */
#ifndef ARCH_BIT_SET
static inline void bit_set(void *bitmap, int bit)
{
  *(unsigned long *)bitmap |= (1 << bit);
}
#else
#define bit_set(bitmap, bit) arch_bit_set(bitmap, bit)
#endif /* ARCH_BIT_SET */

/**
 * @fn static inline void bit_clear(void *bitmap, int bit)
 * Clear bit number @a bit in the bitmap @a bitmap
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param[out] bitmap - A pointer to the bitmap
 * @param bit - Number of bit to clear
 */
#ifndef ARCH_BIT_CLEAR
static inline void bit_clear(void *bitmap, int bit)
{
  *(unsigned long *)bitmap &= ~(1 << bit);
}
#else
#define bit_clear(bitmap, bit) arch_bit_clear(bitmap, bit)
#endif /* ARCH_BIT_CLEAR */

/**
 * @fn static inline void bit_toggle(void *bitmap, int bit)
 * Toggle bit @a bit in the bitmap @a bitmap.
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param[out] bitmap - A pointer to the bitmap.
 * @param bit - Number of bit to toggle
 */
#ifndef ARCH_BIT_TOGGLE
static inline void bit_toggle(void *bitmap, int bit)
{
  *(unsigned long *)bitmap ^= (1 << bit);
}
#else
#define bit_toggle(bitmap, bit) arch_bit_toggle(bitmap, bit)
#endif /* ARCH_BIT_TOGGLE */

/**
 * @fn static inline int bit_test(void *bitmap, int bitno)
 * Test if bit with number @a bitno is set in the bitmap @a bitmap.
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param bitmap - A pointer to the bitmap.
 * @param bitno - Number of bit to test
 * @return 1 if bit is set and 0 otherwise
 */
#ifndef ARCH_BIT_TEST
static inline int bit_test(void *bitmap, int bitno)
{
  return ((*(unsigned long *)bitmap & (1 << bitno)) >> bitno);
}
#else
#define bit_test(bitmap, bitno) arch_bit_test(bitmap, bitno)
#endif /* ARCH_BIT_TEST */

/**
 * @fn static inline int bit_test_and_set(void *bitmap, int bitno)
 * @brief Get old value of bit with number @a bitno and set @a bitno bit in the bitmap
 *
 * This function is similar to bit_set() except it copies old bit value before
 * setting it to 1 and return that value after needed bit was setted.
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param bitmap - A pointer to the bitmap
 * @param bitno  - The number of bit to test and set
 * @return Old value of bit with number @a bitno
 */
#ifndef ARCH_BIT_TEST_AND_SET
static inline int bit_test_and_set(void *bitmap, int bitno)
{
  int val = (*(unsigned long *)bitmap & (1 << bitno));
  *(unsigned long *)bitmap |= (1 << bitno);

  return val;
}
#else
#define bit_test_and_set(bitmap, bitno) arch_bit_test_and_set(bitmap, bitno)
#endif /* ARCH_BIT_TEST_AND_SET */

/**
 * @fn static inline int bit_test_and_reset(void *bitmap, int bitno)
 * @brief Get old value of bit with number @a bitno and clear @a bitno bit in the bitmap
 *
 * This function is similar to bit_clear() except it copies old bit value before
 * setting it to 1 and return that value after needed bit was setted.
 * @note The limit of @a bitmap = sizeof(unsigned long)
 *
 * @param bitmap - A pointer to the bitmap
 * @param bitno  - The number of bit to test and set
 * @return Old value of bit with number @a bitno
 */
#ifndef ARCH_BIT_TEST_AND_RESET
static inline int bit_test_and_reset(void *bitmap, int bitno)
{
  int val = (*(unsigned long *)bitmap & (1 << bitno));
  *(unsigned long *)bitmap &= ~(1 << bitno);

  return val;
}
#else
#define bit_test_and_reset(bitmap, bitno) arch_bit_test_and_reset(bitmap, bitno)
#endif /* ARCH_BIT_TEST_AND_RESET */

/**
 * @fn static inline long bit_find_lsf(unsigned long word)
 * Find first set least significant bit.
 *
 * @param word - Where to search
 * @return Found bit number on success, negative value on failure.
 */
#ifndef ARCH_BIT_FIND_LSF
static inline long bit_find_lsf(unsigned long word)
{
  long c = -1;

  for (; word; c++, word >>= 1) {
    if ((word & 0x01)) {
      c++;
      break;
    }
  }

  return c;
}
#else
#define bit_find_lsf(word) arch_bit_find_lsf(word)
#endif /* ARCH_BIT_FIND_LSF */

#ifndef ARCH_ZERO_BIT_FIND_LSF
#define zero_bit_find_lsf(word) bit_find_lsf(~(word))
#else
#define zero_bit_find_lsf(word) arch_zero_bit_find_lsf(word)
#endif

/**
 * @fn static inline long bit_find_msf(unsigned long word)
 * Find most significant set bit in the @a word.
 *
 * @param word - Where to search.
 * @return Found bit number on success, negative value on failure.
 */
#ifndef ARCH_BIT_FIND_MSF
static inline long bit_find_msf(unsigned long word)
{
  long c = -1;

  while (word) {
    c++;
    word >>= 1;
  }

  return c;
}
#else
#define bit_find_msf(word) arch_bit_find_msf(word)
#endif /* ARCH_BIT_FIND_MSF */

/**
 * @fn static inline long bit_find_lsfz(unsigned long word)
 * Find first zero least significant bit.
 *
 * @param word - Where to search
 * @return Found bit number on success, negative value on failure.
 */
#ifndef ARCH_BIT_FIND_LSFZ
static inline long bit_find_lsfz(unsigned long word)
{
  long c = -1;

  word = ~word;
  
  for (; word; c++, word >>= 1) {
    if ((word & 0x01)) {
      c++;
      break;
    }      
  }
  
  return c;
}
#else
#define bit_find_lsfz(word) arch_bit_find_lsfz(word)
#endif /* ARCH_BIT_FIND_LSFZ */

/**
 * @fn static inline long bit_find_msfz(unsigned long word)
 * Find most significant zero bit in the @a word.
 *
 * @param word - Where to search.
 * @return Found bit number on success, negative value on failure.
 */
#ifndef ARCH_BIT_FIND_MSFZ
static inline long bit_find_msfz(unsigned long word)
{
  long c = -1;

  word = ~word;

  while (word) {
    c++;
    word >>= 1;
  }

  return c;
}
#else
#define bit_find_msfz(word) arch_bit_find_msfz(word)
#endif /* ARCH_BIT_FIND_MSFZ */

/**
 * @fn static inline void bits_or(void *word, unsigned long flags)
 * Executes logical OR with @a word and @a flags and writes result to @a word
 * @note The limit of @a word = sizeof(unsigned long)
 *
 * @param[out] word - A pointer to memory results will be written to
 * @param flsgs - Flags that will be OR'ed with @a word
 */
#ifndef ARCH_BITS_OR
static inline void bits_or(void *word, unsigned long flags)
{
  *(unsigned long *)word |= flags;
}
#define bits_or(word, flags) panic("bits_or uniplemented")
#else
#define bits_or(word, flags) arch_bits_or(word, flags)
#endif /* ARCH_BITS_OR */

/**
 * @fn static inline void bits_and(void *word, unsigned long mask)
 * Executes logical AND with @a word and @a mask and writes result to @a word.
 * @note The limit of @a word = sizeof(unsigned long)
 *
 * @param[out] word - A pointer to memory results will be written to
 * @param mask - A mask that will be AND'ed with @a word
 */
#ifndef ARCH_BITS_AND
static inline void bits_and(void *word, unsigned long mask)
{
  *(unsigned long *)word &= mask;
}
#else
#define bits_and(word, mask) arch_bits_and(word, mask)
#endif /* ARCH_BITS_AND */


/*
 * Initialize multiword bitmap
 *
 * @param map - pointer to the multiword bitmap
 * @param bitno - memory size in bits to allocate
 *
 * @return 0 on success, -1 if can't allocate memory
 */
int init_bitmap(bitmap_t *bitmap, int nbits);

/*
 * Free memory taken by multiword bitmap
 *
 * @param bitmap - bitmap to free
 */
void free_bitmap(bitmap_t *map);

/**
 * Set bit number @a bit in the bitmap @a bitmap
 *
 * @param bitmap - A pointer to the bitmap
 * @param bit - Number of bit to set
 */
void bit_set_multi(bitmap_t *bitmap, int bitno);

/**
 * test bit of a multiword bitmap
 *
 * @param bitmap - A pointer to the bitmap
 * @param bitno  - The number of bit to test
 *
 * @return value of the specified bit
 */
int bit_test_multi(bitmap_t *bitmap, int bitno);

/**
 * @brief Get old value of bit with number @a bitno and set @a bitno bit in the bitmap
 *
 * This function is similar to bit_set() except it copies old bit value before
 * setting it to 1 and return that value after needed bit was setted.
 *
 * @param bitmap - A pointer to the bitmap
 * @param bitno  - The number of bit to test and set
 *
 * @return Old value of bit with number @a bitno or -1 if bitno is illegal
 */
int bit_test_and_set_multi(bitmap_t *bitmap, int bitno);

/**
 * @brief Get old value of bit with number @a bitno and clear @a bitno bit in the bitmap
 *
 * This function is similar to bit_set() except it copies old bit value before
 * setting it to 1 and return that value after needed bit was setted.
 *
 * @param bitmap - A pointer to the bitmap
 * @param bitno  - The number of bit to test and set
 *
 * @return Old value of bit with number @a bitno or -1 if bitno is illegal
 */
int bit_test_and_reset_multi(bitmap_t *bitmap, int bitno);


/**
 * Clear bit number @a bit in the bitmap @a bitmap
 *
 * @param bitmap - A pointer to the bitmap
 * @param bit - Number of bit to clear
 */
void bit_clear_multi(bitmap_t *bitmap, int bitno);

/*
 * Set multiple bits in the multiword bitmap
 *
 * @param bitmap - pointer to the multimap bitmap
 * @param start_bit - bit to set from
 * int end_bit - bit to set upto
 */
void bitrange_set_multi(bitmap_t *bitmap, int start_bit, int end_bit);

/*
 * Clear multiple bits in the multiword bitmap
 *
 * @param bitmap - pointer to the multimap bitmap
 * @param start_bit - bit to clear from
 * int end_bit - bit to clear upto
 */
void bitrange_clear_multi(bitmap_t *bitmap, int start_bit, int end_bit);

/**
 * Find most significant set bit in multimap word which from the 0 bit
 * upto @end_bit
 *
 * @param word - Where to search.
 * @param end_bit - most bit to search upto
 * @return Found bit number on success, negative value on failure.
 */
int bit_find_msf_multi(bitmap_t *bitmap, int mbit);

/**
 * Find first set least significant bit.
 *
 * @param word - Where to search
 * @param sbit - least bit to search from
 * @return Found bit number on success, negative value on failure.
 */
int bit_find_lsf_multi(bitmap_t *bitmap, int lbit);

#ifndef ARCH_BIT_TEST_AND_CLEAR
static inline int bit_test_and_clear(void *bitmap, int bitno)
{
  int val = bit_test(bitmap, bitno);
  bit_clear(bitmap, bitno);
  return val;
}
#else
#define bit_test_and_clear(bitmap, bitno) arch_bit_test_and_clear(bitmap, bitno)
#endif

#endif /* __BITWISE_H__ */