/*
* This is Runter's system for infinite bits. It was made for use
* in Feltain. I, Runter, require no credit, no
* help file documentation of your use, and no email confirmation.
* The only thing I do ask is if you find a bug or have a major change
* that makes the system more efficient or more intelligent than
* let me know about it. Just remember what goes around comes around.
* We all benifit when we work together.
*
* To successfully use this you will use the variable BITMASK
* "BITMASK act;"
* instead of int in your struct declarations for your bitmasks.
* On this new variable type just pass the variables address to
* the new functions-- set_bit, remove_bit, is_set.
* example: if(is_set(&ch->act, ACT_NPC))
*
* This system passes normal numbers as the bits so you'll need
* to redeclare stuff as you are passing it.
*
* if(is_set(&ch->act, 400)) // is the 400th bit set?
*
* A good way to change your entire system over would be to redo
* your definitions of A B C D E... aa bb cc and such from defines
* to one enum struct.
*
* Another important note is that you need to use the new loading
* and saving functions provided when saving your bitmasks. It
* saves them to a single line and loads them from a single line.
*
*/
/*
* Minor cleanups, a few small fixes, and a few minor edits.
* --Kline
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "globals.h"
bool remove_bit( BITMASK *mask, sh_int bit ) /* Returns FALSE if nothing removed. TRUE if so. */
{
BM_LIST *pBlist, *last = 0;
if( !is_set(mask,bit) )
return FALSE;
--bit;
for( pBlist = mask->int_list; pBlist; pBlist = pBlist->next) /* Loop through each bitmask we have allocated. */
{
if( pBlist->set == bit / 32 ) /* Is this the correct set? */
{
pBlist->tar_mask &= ~(1 << (bit % 32)); /* Remove it. */
--mask->bits;
if( pBlist->tar_mask == 0 )
{
if( last )
last->next = pBlist->next;
else
mask->int_list = pBlist->next;
free(pBlist);
--mask->masks;
}
return TRUE; /* We found and removed it above :) */
}
last = pBlist;
}
return FALSE; /* A bug happened. Wee. Somehow. */
}
bool set_bit( BITMASK *mask, sh_int bit ) /* Returns TRUE if a bit was set. FALSE if already set. */
{
BM_LIST *pBlist;
--bit;
for( pBlist = mask->int_list; pBlist; pBlist = pBlist->next ) /* Loop through each bitmask already allocated. */
{
if( pBlist->set == bit / 32 ) /* Is this the correct set? */
{
if( pBlist->tar_mask & (1 << (bit % 32)) )
return FALSE; /* Found it, don't set again. */
pBlist->tar_mask |= 1 << (bit % 32); /* Set our bit inside the mask */
++mask->bits;
return TRUE; /* We did something, yay! */
}
}
/* Let's allocate a new one! */
pBlist = malloc(sizeof(BM_LIST));
++mask->masks;
pBlist->next = mask->int_list;
mask->int_list = pBlist;
pBlist->tar_mask = 0;
pBlist->set = bit / 32;
pBlist->tar_mask |= 1 << (bit % 32); /* Set our bit on the new mask */
++mask->bits;
return TRUE; /* We did something, yay! */
}
bool is_set( BITMASK *mask, sh_int bit )
{
BM_LIST *pBlist;
--bit;
for( pBlist = mask->int_list; pBlist; pBlist = pBlist->next )
if( pBlist->set == bit / 32 )
{
if( pBlist->tar_mask & 1 << (bit % 32) )
return TRUE;
else
return FALSE;
}
return FALSE;
}
/*
* Let's serialize this and return all bits that are set in a dynamic list.
* This is useful when you just want to know what to traverse in a bitmask.
* Good for high level solutions to lists of pointers! This is a high level
* solution that *does not* maintain an ordered queue for specific BFS or
* DFS algorithms. This is for algorithms that do not matter.
*/
int *serialize_bitmask( BITMASK *mask )
{
BM_LIST *pBlist;
int *ilist = malloc(sizeof(int) * mask->bits +1), i = 0, z;
ilist[mask->bits] = 0; /* Zero terminates it. 0th bit CAN NOT BE SET. */
for( pBlist = mask->int_list; pBlist; pBlist = pBlist->next )
{
for( z = 0; z < 32; ++z )
{
if( i > mask->bits )
{
/*
* more found than we have allocated. Maybe residual.
* May add a check before setting ilist to make sure it
* doesn't over run but this should be fine. Just don't
* bug it here for sure. It may not be an error at this
* point. If you do decide to error check this better
* this break should be replaced to let it go through.
*/
break;
}
if( pBlist->tar_mask & 1 << z )
ilist[i++] = pBlist->set * 32 + z + 1;
}
}
if( i < mask->bits + 1 )
{
/* Error -- we have less recorded bits than allocated. */
}
/*
* This must be freed somewhere with free(ilist) or it will leak.
* Using a static buffer here would be a poor idea. Using temporary
* memory allocation would be a good one. Too bad there isn't an
* ANSI thing for that in C.
*/
return ilist;
}
bool free_bitmask( BITMASK *pBmask ) /* Frees a bitmask; safe to call dry. Return TRUE if freed, FALSE otherwise. */
{
BM_LIST *pBMlist, *next;
bool found = FALSE;
for( pBMlist = pBmask->int_list; pBMlist; pBMlist = next )
{
next = pBMlist->next;
free(pBMlist);
found = TRUE;
}
return found;
}
/*
* Initialze a bitmask.
* bm = init_bitmask(NULL);
* init_bitmask(&bm);
* Both of the above work.
*/
BITMASK init_bitmask( BITMASK *bm )
{
static BITMASK bmzero;
if( bm == 0 )
return bmzero;
*bm = bmzero;
return bmzero;
}
void load_bitmask( BITMASK *pBmask, FILE *fp ) /* #masks #bits #mask #vector #mask #vector ... */
{
int i;
BM_LIST *pBMlist;
pBmask->masks = fread_number(fp);
pBmask->bits = fread_number(fp);
for( i = 0; i < pBmask->masks; ++i )
{
pBMlist = malloc(sizeof(BM_LIST));
pBMlist->set = fread_number(fp);
pBMlist->next = pBmask->int_list;
pBmask->int_list = pBMlist;
pBMlist->tar_mask = fread_number(fp);
}
}
char *save_bitmask( BITMASK *pBmask ) /* Make this a string so it's easier to pass to existing save routines. --Kline */
{
BM_LIST *pBMlist;
static char buf[MAX_STRING_LENGTH];
xprintf(buf,"%ld %ld",pBmask->masks,pBmask->bits);
for( pBMlist = pBmask->int_list; pBMlist; pBMlist = pBMlist->next )
xprintf(buf," %ld %ld",pBMlist->set,pBMlist->tar_mask);
return buf;
}