//-----------------------------------------------------------------------------
// 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
//

#include "cdevHandleSet.h"

#ifndef __VMS
	#ifndef  __linux
		extern "C" bzero (char *, int);
	#endif
#else
	extern "C" bzero (char *, unsigned int);
#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 relationship 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 
	// * associated bit offset are one and the same.
	// *********************************************************************
	#define FD_TO_BIT_OFFSET( fd ) (fd)
	#define FD_FROM_BIT_OFFSET( ibit ) (ibit)
#endif

#ifdef CDEV_HAS_UNDERSCORE_FDBITS
#define fds_bits __fds_bits
#endif

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 cdevHandleSet::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 cdevHandleSet::count_bits (unsigned long n) const
	{
	return (cdevHandleSet::nbits_[n & 0xff] + cdevHandleSet::nbits_[(n >> 8) & 0xff] +
		cdevHandleSet::nbits_[(n >> 16) & 0xff] + cdevHandleSet::nbits_[n >> 24]);
	}

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

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

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

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

cdevHandleSetIterator::cdevHandleSetIterator (cdevHandleSet &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_ += cdevHandleSet::WORD_SIZE;

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

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

	for (int i = max / cdevHandleSet::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 cdevHandleSet::set_max (int current_max)
	{
	#ifndef _WIN32
	int i = 0;

	if (this->size_ == 0)
		this->max_handle_ = -1;
	else
		{
		for (i = current_max / cdevHandleSet::WORD_SIZE;
			this->mask_.fds_bits[i] == 0;
			i--)
			;

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

// *****************************************************************************
// * Debugging method that prints out the underlying mask. 
// *****************************************************************************
int cdevHandleSet::asciiDump (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 cdevHandleSet::reset (void)
	{
	this->max_handle_ = -1;
	this->size_   = 0;
	FD_ZERO (&this->mask_);
	}

// *****************************************************************************
// * Constructor, initializes the bitmask to all 0s. 
// *****************************************************************************
cdevHandleSet::cdevHandleSet (void)
	{
	this->reset ();
	}

cdevHandleSet::cdevHandleSet (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 cdevHandleSet::max_set (void) const
	{
	return this->max_handle_;
	}

// *****************************************************************************
// * Checks whether FD is enabled. 
// *****************************************************************************
int cdevHandleSet::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 cdevHandleSet::set_bit (int fd)
	{
	if (!this->is_set (fd))
		{
		#ifdef _WIN32
			FD_SET ((SOCKET)fd, &this->mask_);
		#else 
			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 cdevHandleSet::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 cdevHandleSet::num_set (void) const
	{
	#ifdef _WIN32
		return this->mask_.fd_count;
	#else
		return this->size_;
	#endif
	}

int cdevHandleSetIterator::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
	}
