//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//      FD_Set Wrapper Based on ACE
//
// Author:  Jie Chen
//
// Revision History:
//   cdevFdSet.cc,v
// Revision 1.7  1997/12/12  16:40:18  chen
// add VMS fix
//
// Revision 1.6  1997/03/26  18:34:58  akers
// Ongoing development
//
// Revision 1.5  1997/02/18  15:46:49  chen
// port to linux 2.0.x
//
// Revision 1.4  1995/12/15  15:08:00  chen
// Add VMS Fix
//
// Revision 1.3  1995/10/16  18:20:12  chen
// change to fit under SunOs g++
//
// Revision 1.2  1995/10/05  16:30:06  chen
// Fix for VMS
//
// Revision 1.1.1.1  1995/06/16  17:14:01  epics
// initial import of cdev
//
//
#include "cdevFdSet.h"

#ifndef __VMS

#ifndef  __linux
extern "C" bzero (char *, int);
#endif

#else
extern "C" bzero (char *, unsigned int);
#endif

#ifdef CDEV_HAS_UNDERSCORE_FDBITS
#define fds_bits __fds_bits
#endif

#if defined(__VMS) && defined(_TGV_MULTINET)
// Unfortunately when you use TGV Multinet, the bit offsets in the file*
// descriptor bitmasks used by select() cannot be found by simply using
// the value of the file descriptor. Fortunately, there is a simple rel*
// ship between the file descriptor value and the desired bit offset.
// (These macros are required because this file accesses the file
// descriptor bitmasks directly, without using the FD_SET(), FD_CLR(), *
// FD_ISSSET() macros).
#define FD_TO_BIT_OFFSET( fd ) ((fd)/CHANNELSIZE)
#define FD_FROM_BIT_OFFSET( ibit ) ((ibit)*CHANNELSIZE)
#else
// Under most operating systems the file descriptor value and the assoc*
// bit offset are one and the same.
#define FD_TO_BIT_OFFSET( fd ) (fd)
#define FD_FROM_BIT_OFFSET( ibit ) (ibit)
#endif // defined(__VMS) && defined(_TGV_MULTINET)

inline int  BIT_ENABLED (unsigned long word, int bit = 1) { return (word & bit) != 0; }
inline int  BIT_DISABLED (unsigned long word, int bit = 1) { return (word & bit) == 0; }
inline void SET_BIT (unsigned long &word, int bit) { word |= bit; }
inline void CLR_BIT (unsigned long &word, int bit) { word &= ~bit; }

const char cdevFdSet::nbits_[256] = 
{
  0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
  4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};

/* Counts the number of bits enabled in N.  Uses a table lookup to
   speed up the count. */

int
cdevFdSet::count_bits (unsigned long n) const
{
  return (cdevFdSet::nbits_[n & 0xff] + cdevFdSet::nbits_[(n >> 8) & 0xff] +
	  cdevFdSet::nbits_[(n >> 16) & 0xff] + cdevFdSet::nbits_[n >> 24]);
}

void
cdevFdSet_Iterator::operator++ (void)
{
#ifdef _WIN32
  this->index_++;
#else
  this->val_ = (this->val_ >> 1) & cdevFdSet::MSB_MASK;
  this->num_++;

  if (this->val_ == 0) {
    for (this->index_++; 
	 this->index_ < cdevFdSet::NUM_WORDS && fds_.mask_.fds_bits[this->index_] == 0; 
	 this->index_++)
      ;

    if (this->index_ == cdevFdSet::NUM_WORDS) {
      this->num_ = fds_.max_handle_ + 1;
      return;
    }
    else {
      this->val_ = fds_.mask_.fds_bits[this->index_];
      this->num_ = this->index_ * cdevFdSet::WORD_SIZE;
    }
  }

  for (; BIT_DISABLED (this->val_); this->num_++)
    this->val_ = (this->val_ >> 1) & cdevFdSet::MSB_MASK;
#endif
}

cdevFdSet_Iterator::cdevFdSet_Iterator (cdevFdSet &f)
: fds_ (f), index_ (0), num_ (f.size_ == 0 ? f.max_handle_ + 1 : 0)
{
#ifndef _WIN32
  for (; fds_.mask_.fds_bits[this->index_] == 0; this->index_++)
    this->num_ += cdevFdSet::WORD_SIZE;

  for (this->val_ = this->fds_.mask_.fds_bits[this->index_];
       (BIT_DISABLED (this->val_)) && this->num_ < cdevFdSet::MAX_SIZE;
       this->num_++)
    this->val_ = (this->val_ >> 1) & cdevFdSet::MSB_MASK;
#endif
}

/* Synchronize the underlying FD_SET with the MAX_FD and the SIZE. */
void
cdevFdSet::sync (int max)
{
#ifndef _WIN32
  this->size_ = 0;

  for (int i = max / cdevFdSet::WORD_SIZE; i >= 0; i--)
    this->size_ += count_bits (this->mask_.fds_bits[i]);

  this->set_max (max);
#endif
}

/* Resets the MAX_FD after a clear of the original MAX_FD. */

void
cdevFdSet::set_max (int current_max)
{
#ifndef _WIN32
  int i = 0;
  // SunOs g++ compiler does not like the i declared inside for loop
  // --jie chen 10/16/95
  if (this->size_ == 0)
    this->max_handle_ = -1;
  else {
    for (i = current_max / cdevFdSet::WORD_SIZE;
	 this->mask_.fds_bits[i] == 0; 
	 i--)
      ;

    this->max_handle_ = i * cdevFdSet::WORD_SIZE;
    for (fd_mask val = this->mask_.fds_bits[i]; 
	 (val & ~1) != 0;
	 val = (val >> 1) & cdevFdSet::MSB_MASK)
      this->max_handle_++;
  }
#endif
}

/* Debugging method that prints out the underlying mask. */

int
cdevFdSet::pr_mask (FILE * fp) 
{
  fprintf(fp, "[ ");
  for (int i = 0; i < this->max_handle_ + 1; i++) 
    if (this->is_set (i))
      fprintf(fp," %d ", i);

  fprintf(fp, " ]");
  return this->size_;
}

void 
cdevFdSet::reset (void)
{
  this->max_handle_ = -1;
  this->size_   = 0;
  FD_ZERO (&this->mask_);
}

/* Constructor, initializes the bitmask to all 0s. */

cdevFdSet::cdevFdSet (void)
{
  this->reset ();
}

cdevFdSet::cdevFdSet (const fd_set &fd_mask): size_ (0)
{
  memcpy ((void *) &this->mask_, (void *) &fd_mask, sizeof this->mask_);
  // sync is empty for WIN32
  this->sync (FD_SETSIZE);
}

/* Returns the number of the large bit. */

int  
cdevFdSet::max_set (void) const
{
  return this->max_handle_;
}

/* Checks whether FD is enabled. */

int
cdevFdSet::is_set (int fd) const
{
#ifdef _WIN32
  return FD_ISSET ((SOCKET)fd, &this->mask_);
#else
  return FD_ISSET (fd, &this->mask_);
#endif
}

/* Enables the FD. */

void 
cdevFdSet::set_bit (int fd)
{
  if (!this->is_set (fd)) {
#ifdef _WIN32
    FD_SET ((SOCKET)fd, &this->mask_);
#else  /* WIN32 */
    FD_SET (fd, &this->mask_);
    this->size_++;
// again VMS system has different idea :-----------
#if defined(__VMS) && defined(_TGV_MULTINET)
    if (FD_TO_BIT_OFFSET(fd) > this->max_handle_)
      this->max_handle_ = FD_TO_BIT_OFFSET(fd);
#else
    if (fd > this->max_handle_)
      this->max_handle_ = fd;
#endif

#endif
  }
}

/* Disables the FD. */

void 
cdevFdSet::clr_bit (int fd)
{
  if (this->is_set (fd)) {
#ifdef _WIN32
    FD_CLR ((SOCKET)fd, &this->mask_);
#else
    FD_CLR (fd, &this->mask_);
    this->size_--;

#if defined(__VMS) && defined(_TGV_MULTINET)
    if (FD_TO_BIT_OFFSET(fd) == this->max_handle_)
      this->set_max (this->max_handle_);
#else
    if (fd == this->max_handle_)
      this->set_max (this->max_handle_);
#endif

#endif
  }
}

/* Returns a count of the number of enabled bits. */
int 
cdevFdSet::num_set (void) const
{
#ifdef _WIN32
  return this->mask_.fd_count;
#else
  return this->size_;
#endif
}

int
cdevFdSet_Iterator::operator () (void)
{
#ifdef _WIN32
  return this->index_ < this->fds_.mask_.fd_count 
    ? fds_.mask_.fd_array[this->index_] : -1;
#else

#if defined(__VMS) && defined(_TGV_MULTINET)
     return FD_FROM_BIT_OFFSET(this->num_ <= this->fds_.max_handle_ ?
                               this->num_ :-1);
#else  
  return this->num_ <= this->fds_.max_handle_ ? this->num_ : -1;
#endif

#endif
}
