//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*****************************************************************************/
/**                            Microsoft Windows                            **/
/*****************************************************************************/

/*

select.c

select related winsock2 functions


FILE HISTORY:
	OmarM     16-Oct-2000

*/


#include "winsock2p.h"
#include "windev.h"

// structure to allow optimizing the predominant case of select'ing on one or two sockets
typedef struct _STACK_SOCK_FD {
    u_int osf_count_select1;
    SOCKET osf_array_select1;
    u_int osf_count_select2;
    SOCKET osf_array_select2;
    u_int osf_count_map1;
    SOCKET osf_array_map1;
    u_int osf_count_map2;
    SOCKET osf_array_map2;
} STACK_SOCK_FD;

#define STACK_FDS_SIZE 2    // Number of select sockets supported on stack

void DerefFds(fd_set *pFds, int *pcRefs) {
	
	u_int		i;

	__try {
		if (pFds) {
			for (i = 0; (i < pFds->fd_count) && (*pcRefs > 0); i++) {
				DerefSocketHandle(pFds->fd_array[i]);
				(*pcRefs)--;
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER) {
		;	// don't need to do anything here yet.
	}

}	// DerefFds()


int CopyFds(fd_set **pNewSelect, fd_set **pNewMap, fd_set *pUserFds, int *pcTotal, int *pcRefs, 
			WsProvider **ppProv, STACK_SOCK_FD * pStackFds) {

	int			Err;
	u_int		cCount, i;
	WsSocket	*pSock;
    fd_set      *pSelect;
    fd_set      *pMap;
    BOOL        bFromHeap;
	
	Err = 0;
    pSelect = NULL;
    bFromHeap = TRUE;

	__try {
		if (pUserFds && (cCount = pUserFds->fd_count)) {
            i = cCount * sizeof(SOCKET) + sizeof(int);
            if (STACK_FDS_SIZE >= cCount) {
                pSelect = (fd_set *)pStackFds;
                bFromHeap = FALSE;
            } else {
                pSelect = LocalAlloc(LPTR, i*2);    // allocate enough for pSelect and pMap
            }
            if (NULL == pSelect) {
                Err = WSA_NOT_ENOUGH_MEMORY;
            } else {
                pMap = (fd_set *)((DWORD)pSelect + i);
                *pcTotal         += cCount;
                pMap->fd_count    = cCount;
                pSelect->fd_count = cCount;
                for (i = 0; i < cCount; i++) {
                    if (Err = RefSocketHandle(pUserFds->fd_array[i], &pSock)) {
                        break;
                    } else {
                        (*pcRefs)++;
                        pMap->fd_array[i] = pSock->hWSPSock;
                        pSelect->fd_array[i] = pSock->hWSPSock;
                        if (*ppProv) {
                            if (*ppProv != pSock->pProvider) {
                                Err = WSAEINVAL;
                                break;
                            }
                         } else
                            *ppProv = pSock->pProvider;
                    }	
                }
                if (0 == Err) {
                    *pNewSelect = pSelect;
                    *pNewMap = pMap;
                }
            }
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER) {
		Err = WSAEFAULT;
	}

    if ((Err) && (bFromHeap) && (pSelect)) {
        LocalFree(pSelect);
    }
	
	return Err;

}	// CopyFds()


//
// Map select results from WSP socket contexts to WS2 user socket handles
//
void ResultFds(fd_set *pUser, fd_set *pSelect, fd_set *pMap) {
    u_int i, j;
    u_int n, m;

    if (pUser && pSelect && pMap) {
        n = pSelect->fd_count;
        m =  pMap->fd_count;

        for (i = 0; i < n; i++) {
            for (j = 0; j < m; j++) {
                if (pSelect->fd_array[i] == pMap->fd_array[j]) {
                    pSelect->fd_array[i] = pUser->fd_array[j];
                    break;
                }
            }
        }

        pUser->fd_count = n;
        for (i = 0; i < n; i++) {
            pUser->fd_array[i] = pSelect->fd_array[i];
        }
    } else if (pUser) {
		__try {
			pUser->fd_count = 0;
		}
		__except(EXCEPTION_EXECUTE_HANDLER) {
			;	// don't need to do anything here yet.
		}
    }

}   // ResultFds


int WSAAPI select (
    IN int			nfds,
    IN OUT fd_set	*readfds,
    IN OUT fd_set	*writefds,
    IN OUT fd_set	*exceptfds,
    IN const struct timeval FAR * timeout) {
    
    WsProvider  *pProv;
    int         Err;
    int         i;
    fd_set      * Select[3];
    fd_set      * Map[3];       // auxillary array to help map from WSP socket context to WS2 user handle.
    fd_set      * User[3];
    STACK_SOCK_FD StackFds[3];  // stack allocated space to avoid heap allocations for small select requests
    
    int				cTotal, cRefs, cReady;
    struct timeval	Time, *pTime;

	cTotal = cRefs = 0;

	Err = 0;
    for (i = 0; i < 3; i++) {
        Select[i] = Map[i] = NULL;
    }
    User[0] = readfds;
    User[1] = writefds;
    User[2] = exceptfds;

	cReady = SOCKET_ERROR;
	
	// copy timeout to make sure that we don't crash in lower layers b/c of
	// bad timeout address...
	if (pTime = (struct timeval *)timeout) {
		__try {
			pTime = &Time;
			memcpy(&Time, timeout, sizeof(Time));
		}
		__except(EXCEPTION_EXECUTE_HANDLER) {
			Err = WSAEFAULT;
		}
	}
	if (! Err) {
		pProv = NULL;

        for (i = 0; (i < 3) && (0 == Err); i++) {
            Err = CopyFds(&Select[i], &Map[i], User[i], &cTotal, &cRefs, &pProv, &StackFds[i]);
        }
        if (!Err) {
    		if (!cTotal || !pProv) {
    			Err = WSAEINVAL;
    		} else {
    			cReady = pProv->ProcTable.lpWSPSelect(nfds, Select[0], Select[1], Select[2],
    													pTime, &Err);
    		}
        }
	}

	// cleanup

    for (i=0; i<3; i++) {
        DerefFds(User[i], &cRefs);
        ResultFds(User[i], Select[i], Map[i]);
        if ((Select[i]) && (Select[i] != (fd_set *)&StackFds[i])) {
            LocalFree(Select[i]);
        }
    }

	if (SOCKET_ERROR == cReady) {
		SetLastError(Err);
    }
	return cReady;

}	// select()


int __WSAFDIsSet (
	SOCKET       fd,
	FD_SET FAR * set) {

	int i = (set->fd_count & 0xFFFF);

	while (i--)
		if (set->fd_array[i] == fd)
			return 1;

	return 0;

}   // __WSAFDIsSet()


int WPUFDIsSet(
	SOCKET s,  
	FD_SET FAR * set){

	return __WSAFDIsSet(s, set);

}	// WPUFDIsSet()


int WSAAPI WSAEventSelect(
	IN SOCKET s,
	IN WSAEVENT hEventObject,
	IN long lNetworkEvents) {

	int			Err, Status;
	WsSocket	*pSock;

	if (! (Err = RefSocketHandle(s, &pSock))) {

		Status = pSock->pProvider->ProcTable.lpWSPEventSelect(pSock->hWSPSock, 
			hEventObject, lNetworkEvents, &Err);

		if (Status && ! Err)
			Err = WSASYSCALLFAILURE;

		DerefSocket(pSock);
	}

	if (Err) {
		Status = SOCKET_ERROR;
		SetLastError(Err);
	} else
		Status = 0;
	
	return Status;

}	// WSAEventSelect()


int WSAAPI WSAEnumNetworkEvents(
	IN SOCKET s,
	IN WSAEVENT hEventObject,
	OUT LPWSANETWORKEVENTS lpNetworkEvents) {

	int			Err, Status;
	WsSocket	*pSock;

	if (! (Err = RefSocketHandle(s, &pSock))) {

		Status = pSock->pProvider->ProcTable.lpWSPEnumNetworkEvents(
			pSock->hWSPSock, hEventObject, lpNetworkEvents, &Err);

		if (Status && ! Err)
			Err = WSASYSCALLFAILURE;

		DerefSocket(pSock);
	}

	if (Err) {
		Status = SOCKET_ERROR;
		SetLastError(Err);
	} else
		Status = 0;
	
	return Status;

}	// WSAEnumNetworkEvents()

