nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// bitvector.c
//
// One of the questions that came up when designing NakedMud is, how do we
// efficiently implement bitvectors while maintaining the modularity of the mud?
// For those unfamiliar with the purposes of a bitvector, let me elaborate:
// there are many instances in which we may want to know about the presence or
// absence of a property (e.g. is the room dark? is the character blind?). We
// could easily have a variable for each of these properties in the relevant
// datastructures, but this is often quite wasteful on memory. What would be
// really nice is if we could compress the space that it took to store one
// bit of information (in C, the smallest basic datatype is 8 bits ... i.e. we
// are wasting 7 bits to store a piece of information that be represented as
// on/off). In a nutshell, this is the purpose of a bitvector (there are more
// reasons why bitvectors are attractive, but this is perhaps the most important
// one in the context of muds).
//
// For anyone who has ever worked on a DIKU-derivative, they know that
// bits in a bitvector are typically declared on after another like this:
//    #define BIT_ONE    (1 << 0)
//    #define BIT_TWO    (1 << 1)
//    #define BIT_THREE  (1 << 2)
//    ...
//
// Of course, this is hugely unattractive for us: If we had a module that wanted
// to add a new bit, we surely would not want to add it to some global header
// file; we would want to keep the definition within the module headers. And,
// most certainly we cannot do this because we have to assocciate a position
// with each bit name, and every other module has to know that position so that
// noone else adds a new bit with the same position. This module is an attempt
// to address this problem, allowing modules to use bits, keep their 
// declarations in some place local to the module, and make sure there are no
// conflicting definitions. This also ensures that bits are persistent for all
// rooms, objects, and characters (ie. they save if we crash, or the player
// logs off, or whatnot).
//
//*****************************************************************************

#include "mud.h"
#include "utils.h"

#include "bitvector.h"



//*****************************************************************************
// local functions, datastructures, and defines
//*****************************************************************************
// a table of mappings between bitvector names, and the 
// data assocciated with them (i.e. bit:value mappings)
HASHTABLE *bitvector_table = NULL;

typedef struct bitvector_data {
  HASHTABLE *bitmap; // a mapping from bit name to bit number
  char        *name; // which bitvector is this?
} BITVECTOR_DATA;

struct bitvector {
  BITVECTOR_DATA *data; // the data corresponding to this bitvector
  char           *bits; // the bits we have set/unset
};

BITVECTOR_DATA *newBitvectorData(const char *name) {
  BITVECTOR_DATA *data = malloc(sizeof(BITVECTOR_DATA));
  data->bitmap = newHashtable();
  data->name   = strdup(name);
  return data;
}




//*****************************************************************************
// implementation of bitvector.h
//*****************************************************************************
void init_bitvectors() {
  // create the bitvector table
  bitvector_table = newHashtable();

  // and also create some of the basic bitvectors and 
  // bits that come stock and are required for the core release
  bitvectorCreate("char_prfs");

  bitvectorCreate("obj_bits");
  bitvectorAddBit("obj_bits", "notake");

  bitvectorCreate("room_bits");

  bitvectorCreate("user_groups");
  bitvectorAddBit("user_groups", "admin");
  bitvectorAddBit("user_groups", "scripter");
  bitvectorAddBit("user_groups", "builder");
  bitvectorAddBit("user_groups", "player");
  bitvectorAddBit("user_groups", "playtester");
}

void bitvectorAddBit(const char *name, const char *bit) {
  BITVECTOR_DATA *data = hashGet(bitvector_table, name);
  if(data != NULL)
    hashPut(data->bitmap, bit, (void *)(hashSize(data->bitmap) + 1));
}

void bitvectorCreate(const char *name) {
  if(!hashGet(bitvector_table, name))
    hashPut(bitvector_table, name, newBitvectorData(name));
}

BITVECTOR *newBitvector() {
  BITVECTOR *v = calloc(1, sizeof(BITVECTOR));
  return v;
}

BITVECTOR   *bitvectorInstanceOf(const char *name) {
  BITVECTOR_DATA *data = hashGet(bitvector_table, name);
  BITVECTOR    *vector = NULL;
  if(data != NULL) {
    int vector_len = hashSize(data->bitmap)/8 + 1;
    vector = newBitvector();
    vector->data = data;
    vector->bits = calloc(vector_len, sizeof(char));
  }
  return vector;
}

void         deleteBitvector(BITVECTOR *v) {
  if(v->bits) free(v->bits);
  free(v);
}

void         bitvectorCopyTo(BITVECTOR *from, BITVECTOR *to) {
  int bit_len = 1 + hashSize(from->data->bitmap)/8;
  to->data = from->data;
  if(to->bits) free(to->bits);
  to->bits = malloc(sizeof(char) * bit_len);
  to->bits = memcpy(to->bits, from->bits, bit_len);
}

BITVECTOR   *bitvectorCopy(BITVECTOR *v) {
  BITVECTOR *newvector = bitvectorInstanceOf(v->data->name);
  bitvectorCopyTo(v, newvector);
  return newvector;
}

bool bitIsSet(BITVECTOR *v, const char *bit) {
  LIST    *bits = parse_keywords(bit);
  char *one_bit = NULL; 
  bool    found = FALSE;

  // check for one
  while( !found && (one_bit = listPop(bits)) != NULL) {
    found = bitIsOneSet(v, one_bit);
    free(one_bit);
  }
  
  // clean up our mess
  deleteListWith(bits, free);
  return found;
}

bool bitIsAllSet(BITVECTOR *v, const char *bit) {
  LIST    *bits = parse_keywords(bit);
  char *one_bit = NULL;
  bool    found = TRUE;

  // check for each one
  while( found && (one_bit = listPop(bits)) != NULL) {
    found = bitIsOneSet(v, one_bit);
    free(one_bit);
  }

  // clean up our mess
  deleteListWith(bits, free);
  return found;
}

bool bitIsOneSet(BITVECTOR *v, const char *bit) {
  int val = (int)hashGet(v->data->bitmap, bit);
  return IS_SET(v->bits[val/8], (1 << (val % 8)));
}

void bitSet(BITVECTOR *v, const char *name) {
  LIST    *bits = parse_keywords(name);
  char *one_bit = NULL;

  // set each one
  while( (one_bit = listPop(bits)) != NULL) {
    int val = (int)hashGet(v->data->bitmap, one_bit);
    free(one_bit);
    // 0 is a filler meaning 'this is not an actual name for a bit'
    if(val == 0) continue;
    SET_BIT(v->bits[val/8], (1 << (val % 8)));
  }

  // garbage collection
  deleteListWith(bits, free);
}

void bitClear(BITVECTOR *v) {
  int i;
  for(i = 1; i <= hashSize(v->data->bitmap); i++)
    REMOVE_BIT(v->bits[i/8], (1 << (i % 8)));
}

void bitRemove(BITVECTOR *v, const char *name) {
  LIST    *bits = parse_keywords(name);
  char *one_bit = NULL;

  // remove each one
  while( (one_bit = listPop(bits)) != NULL) {
    int val = (int)hashGet(v->data->bitmap, one_bit);
    REMOVE_BIT(v->bits[val/8], (1 << (val % 8)));
    free(one_bit);
  }

  // garbage collection
  deleteListWith(bits, free);
}

void bitToggle(BITVECTOR *v, const char *name) {
  LIST    *bits = parse_keywords(name);
  char *one_bit = NULL;

  // toggle each one
  while( (one_bit = listPop(bits)) != NULL) {
    int val = (int)hashGet(v->data->bitmap, one_bit);
    free(one_bit);
    // 0 is a filler meaning 'this is not an actual name for a bit'
    if(val == 0) continue;
    TOGGLE_BIT(v->bits[val/8], (1 << (val % 8)));
  }

  // garbage collection
  deleteListWith(bits, free);
}

const char *bitvectorGetBits(BITVECTOR *v) {
  static char bits[MAX_BUFFER];
  HASH_ITERATOR *hash_i = newHashIterator(v->data->bitmap);
  const char *key = NULL;
  void *val       = NULL;
  int bit_i       = 0;
  *bits = '\0';

  // add each set bit to our list to store
  ITERATE_HASH(key, val, hash_i) {
    if(bitIsOneSet(v, key)) {
      bit_i += snprintf(bits+bit_i, MAX_BUFFER-bit_i, "%s%s", 
			(bit_i == 0 ? "" : ", "), key);
    }
  }
  deleteHashIterator(hash_i);
  return bits;
}

int bitvectorSize(BITVECTOR *v) {
  return hashSize(v->data->bitmap);
}

LIST *bitvectorListBits(BITVECTOR *v) {
  return hashCollect(v->data->bitmap);
}