-- | Interface to @fd_set@. See @select(2)@. ---- The type 'I.FdSet' is opaque, but is implemented internally as a-- pointer to an @fd_set@. All operations on 'I.FdSet's must adhere to-- the requirements of @FD_CLR@, @FD_ISSET@, @FD_SET@ and @FD_ZERO@-- (see @select(2)@). This includes requiring /valid/ file descriptors-- for all operations. Most functions in this module are kept in the-- 'IO' monad to make it easier to guarantee validity of the file-- descriptors, but since invalid ones seem to work fine in practice-- (at least on Linux), the module-- "System.Posix.IO.Select.FdSet.Unsafe" provides a non-'IO'-- interface.---- Functions that return an 'I.FdSet', such as 'insert', copy the-- underlying @fd_set@ in order to be referentially transparent.---- In the documentation that follows, a file descriptor is said to be-- /in range/ if it is non-negative and strictly smaller than the-- system-defined @FD_SETSIZE@. Many functions silently ignore file-- descriptors that are not in range.moduleSystem.Posix.IO.Select.FdSet(I.FdSet(),fromList,insert,insertList,empty,elem,remove,removeList,inList,inRange,bound,duplicate)whereimportPreludehiding(elem)importqualifiedSystem.Posix.IO.Select.FdSet.InternalasIimportForeignimportqualifiedSystem.IO.UnsafeasUNSAFEimportSystem.Posix.TypesimportMiscimportControl.Monad-- | Create an 'FdSet' from a list of file descriptors. File-- descriptors not in range (see above) are silently ignored.fromList::[Fd]->IOI.FdSetfromListfds=I.allocate'>>=\ptr->withForeignPtrptrI.c_fd_zero_wrapper>>mapM_((flipI.insert')ptr)(filterinRangefds)>>return(I.FdSetptr(maximum(0:fds)))-- | Insert a file descriptor.insert::Fd->I.FdSet->IOI.FdSetinsertfd=insertList[fd]-- | Insert multiple file descriptors. This is more efficient than-- multiple 'insert's (only a single copy of the set is made).insertList::[Fd]->I.FdSet->IOI.FdSetinsertListfds(I.FdSetptrl)=I.duplicate'ptr>>=\newPtr->mapM_((flipI.insert')newPtr)(filterinRangefds)>>return(I.FdSetnewPtr(maxl(maximum(0:fds))))-- | An empty 'FdSet'.empty::IOI.FdSetempty=I.allocate'>>=\ptr->withForeignPtrptrI.c_fd_zero_wrapper>>return(I.FdSetptr0)-- | Test for membership. Recall that POSIX allows undefined behavior-- if the file descriptor is not valid (it does, however, seem to work-- fine on Linux).elem::Fd->I.FdSet->IOBoolelemfd(I.FdSetptr_)=I.elem'fdptr>>=(return.cTrue)-- | Remove a file descriptor.remove::Fd->I.FdSet->IOI.FdSetremovefd=removeList[fd]-- | Remove multiple file descriptors. This is more efficient than-- multiple 'remove's (only a single copy of the set is made).removeList::[Fd]->I.FdSet->IOI.FdSetremoveListfds(I.FdSetptrl)=I.duplicate'ptr>>=\newPtr->mapM_((flipI.remove')newPtr)(filterinRangefds)>>return(I.FdSetnewPtrl)-- We don't actually shrink the maximum fd here. Should be ok!-- | @'inList' fds fdset@ gives a list of all file descriptors in-- @fd@ that are in @fdset@.inList::[Fd]->I.FdSet->IO[Fd]inListfds(I.FdSetptrl)=filterM(\fd->I.elem'fdptr>>=(return.cTrue))fds'wherefds'=filter(\fd->fd<=l&&inRangefd)fds-- | Test if a file descriptor is in range (see introduction).inRange::Fd->BoolinRangefd=fd'>=0&&fd'<limitwherefd'=fromIntegralfdlimit=UNSAFE.unsafePerformIOI.c_fd_setsize_wrapper-- | This file descriptor is at least as large as the largest in the-- set. If no file descriptors have ever been removed from the set,-- the value is /the largest/ in the set, but this assumption may not-- hold after removals or other operations.bound::I.FdSet->Fdbound(I.FdSet_l)=l-- | Copy an 'FdSet'.duplicate::I.FdSet->IOI.FdSetduplicate(I.FdSetptrl)=I.duplicate'ptr>>=\newPtr->return(I.FdSetnewPtrl)