// file: $isip/class/io/SofSymbolTable/sofs_04.cc
// version: $Id: sofs_04.cc 5502 2000-11-13 15:27:27Z picone $
//

// isip include files
//
#include "SofSymbolTable.h"
#include <MemoryManager.h>

// method: add
//
// arguments:
//  const SysString& name: (input) object name
//
// return: a long index
//
// add a name to the symbol table, and return its reference count
//
long SofSymbolTable::add(const SysString& name_a) {
  
  // make sure it is a valid name
  //
  if (!checkName(name_a)) {
    return NO_SYMB;
  }

  // see if the symbol already exists in the table
  //
  long num = getIndex(name_a);

  if (num != NO_SYMB) {
    
    // symbol already exists, increment reference count
    //
    ref_count_d[num]++;
    return num;
  }

  // new symbol, add to the end of the list
  //
  return addNew(name_a);
}

// method: addNew
//
// arguments:
//  const SysString& name: (input) object name
//
// return: a long index
//
// add a new name to the symbol table
//
long SofSymbolTable::addNew(const SysString& name_a) {
  
  // add the new symbol to the end of the list
  //
  long num = NO_SYMB;
  
  // check if we need to grow the table size
  //
  if (num_syms_d >= table_capacity_d - 1) {

    if (!growCapacity()) {
      Error::handle(name(), L"addNew", Error::NOMEM, __FILE__, __LINE__);
      return NO_SYMB;
    }
    num = num_syms_d;
    table_size_d = num;
  }
  
  else {
    
    num = NO_SYMB;
    
    // find a free space within the list
    //
    for (long i = 0; i < table_capacity_d; i++) {
      if (ref_count_d[i] == 0) {
	num = i;
	break;
      }
    }

    // check for memory error
    //
    if (num == NO_SYMB) {
      Error::handle(name(), L"addNew", Error::MEM, __FILE__, __LINE__);
      return NO_SYMB;
    }

    // incremement table_size_d
    //
    if (num > table_size_d) {
      table_size_d = num;
    }
  }
  
  // copy class data and initialize reference count
  //
  table_d[num].assign(name_a);
  ref_count_d[num] = 1;
  num_syms_d++;

  // exit gracefully
  //
  return num;
}

// method: add
//
// arguments:
//  long ind: (input) index for name
//  long ref: (input) reference count for name
//  const SysString& name: (input) symbol name
//
// return: a boolean value indicating status
//
// manually add an entry. this is used when reconstructing a symbol
// table from a disk file.
//
boolean SofSymbolTable::add(long ind_a, long ref_a, const SysString& name_a) {
  
  // make sure it is a valid name
  //
  if (!checkName(name_a)) {
    return false;
  }
  
  // check the bounds of the input
  //
  if (ind_a < 0) {
    return Error::handle(name(), L"add", ERR_SYMB,
                         __FILE__, __LINE__);
  }

  // increase the capacity of the table as necessary
  //
  while (ind_a >= table_capacity_d) {
    if (!growCapacity()) {
      return Error::handle(name(), L"add", Error::NOMEM,
                           __FILE__, __LINE__);
    }
  }

  // set the data
  //
  ref_count_d[ind_a] = ref_a;
  table_d[ind_a].assign(name_a);
  
  // set the counters
  //
  if (ind_a > table_size_d) {
    table_size_d = ind_a;
  }
  num_syms_d++;
  
  // exit gracefully
  //
  return true;
}

// method: remove
//
// arguments:
//  const SysString& name: (input) class name to remove
//
// return: a boolean value indicating status
//
// remove one instance of name from the symbol table
//
boolean SofSymbolTable::remove(const SysString& name_a) {

  long num = getIndex(name_a);

  if (num == NO_SYMB) {
    return Error::handle(name(), L"remove", ERR_NOTFND, __FILE__, __LINE__);
  }

  // if there are no more references to this symbol, delete it from
  // the list
  //
  if ((--ref_count_d[num]) == 0) {

    // clear the entry
    //
    table_d[num].clear();
    
    // decrease the actual table size if applicable
    //
    while (ref_count_d[table_size_d] == 0) {
      table_size_d--;
    }
    
    // decrease the number of objects
    //
    num_syms_d--;
  }
  
  // exit gracefully
  //
  return true;
}

// method: remove
//
// arguments:
//  long index: (input) index to delete
//
// return: a boolean value indicating status
//
// remove one instance of a symbol of given index from the symbol table
//
boolean SofSymbolTable::remove(long index_a) {
  
  if (index_a < 0) {
    return Error::handle(name(), L"remove", ERR_SYMB, __FILE__, __LINE__);
  }
  
  // if there are no more references to this symbol, delete it from
  // the list
  //
  if ((--ref_count_d[index_a]) == 0) {

    // clear the entry
    //
    table_d[index_a].clear();
    
    // decrease max if applicable
    //
    while ((table_size_d >= 0) && (ref_count_d[table_size_d] == 0)) {
      table_size_d--;
    }
    
    // decrease the number of objects
    //
    num_syms_d--;
  }

  // exit gracefully
  //
  return true;
}

// method: growCapacity
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method grows the symbol table array,  note that we always have
// contiguous memory
//
boolean SofSymbolTable::growCapacity() {
  
  // increase capacity and create new space
  //
  table_capacity_d += GROW_SIZE;
  SysString* tmp_table = (SysString*)NULL;
  long* tmp_ref = (long*)NULL;
  
  tmp_table = new SysString[table_capacity_d];
  tmp_ref = new long[table_capacity_d];
  
  // initialize the reference counts on the new data
  //
  MemoryManager::memset(tmp_ref, 0, table_capacity_d * sizeof(long));
  
  if (table_size_d != NO_SYMB) {
    
    // copy the reference count data
    //
    MemoryManager::memcpy(tmp_ref, ref_count_d,
                          (table_size_d + 1) * sizeof(long));
  
    // "swap" the new, zero length systrings with the old ones. This is
    // faster than copy because it doesn't need to allocate or
    // deallocate any memory.
    //
    for (long i = 0; i <= table_size_d; i++) {
      tmp_table[i].swap(table_d[i]);
    }
  }
  
  // delete the old space
  //
  if (table_d != (SysString*)NULL) {
    delete [] table_d;
  }
  if (ref_count_d != (long*)NULL) {
    delete [] ref_count_d;
  }

  // install the new data
  //
  table_d = tmp_table;
  ref_count_d = tmp_ref;

  // exit gracefully
  //
  return true;
}

// method: checkName
//
// arguments:
//  const SysString& name: (input) symbol name
//
// return: a boolean value indicating status
//
// this method checks if a symbol name is valid
//
boolean SofSymbolTable::checkName(const SysString& name_a) const {

  // if a bad character was found, this is an invalid string
  //
  if (name_a.firstChr(INVALID_CHARS) != Integral::NO_POS) {
    return false;
  }
  
  // if the first character is a number, this is an invalid string
  //
  SysChar c(name_a(0));
  if (c.isDigit()) {
    return false;
  }
  
  // exit gracefully
  //
  return true;
}
