big rename

This commit is contained in:
Araq 2014-08-28 00:36:14 +02:00
commit 27869b6c7b
7 changed files with 380 additions and 380 deletions

View file

@ -16,7 +16,7 @@ type
{.deprecated: [TSortOrder: SortOrder].} {.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: TSortOrder): int {.inline.} = proc `*`*(x: int, order: SortOrder): int {.inline.} =
## flips `x` if ``order == Descending``; ## flips `x` if ``order == Descending``;
## if ``order == Ascending`` then `x` is returned. ## if ``order == Ascending`` then `x` is returned.
## `x` is supposed to be the result of a comparator, ie ``< 0`` for ## `x` is supposed to be the result of a comparator, ie ``< 0`` for
@ -103,7 +103,7 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T])
proc merge[T](a, b: var openArray[T], lo, m, hi: int, proc merge[T](a, b: var openArray[T], lo, m, hi: int,
cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) = cmp: proc (x, y: T): int {.closure.}, order: SortOrder) =
template `<-` (a, b: expr) = template `<-` (a, b: expr) =
when false: when false:
a = b a = b
@ -150,7 +150,7 @@ proc merge[T](a, b: var openArray[T], lo, m, hi: int,
proc sort*[T](a: var openArray[T], proc sort*[T](a: var openArray[T],
cmp: proc (x, y: T): int {.closure.}, cmp: proc (x, y: T): int {.closure.},
order = TSortOrder.Ascending) = order = SortOrder.Ascending) =
## Default Nimrod sort. The sorting is guaranteed to be stable and ## Default Nimrod sort. The sorting is guaranteed to be stable and
## the worst case is guaranteed to be O(n log n). ## the worst case is guaranteed to be O(n log n).
## The current implementation uses an iterative ## The current implementation uses an iterative

View file

@ -156,12 +156,12 @@ proc read*[T](future: Future[T]): T =
return future.value return future.value
else: else:
# TODO: Make a custom exception type for this? # TODO: Make a custom exception type for this?
raise newException(EInvalidValue, "Future still in progress.") raise newException(ValueError, "Future still in progress.")
proc readError*[T](future: Future[T]): ref EBase = proc readError*[T](future: Future[T]): ref Exception =
if future.error != nil: return future.error if future.error != nil: return future.error
else: else:
raise newException(EInvalidValue, "No error in future.") raise newException(ValueError, "No error in future.")
proc finished*[T](future: Future[T]): bool = proc finished*[T](future: Future[T]): bool =
## Determines whether ``future`` has completed. ## Determines whether ``future`` has completed.
@ -206,7 +206,7 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
fut2.callback = cb fut2.callback = cb
type type
PDispatcherBase = ref object of PObject PDispatcherBase = ref object of RootRef
timers: seq[tuple[finishAt: float, fut: Future[void]]] timers: seq[tuple[finishAt: float, fut: Future[void]]]
proc processTimers(p: PDispatcherBase) = proc processTimers(p: PDispatcherBase) =
@ -226,11 +226,11 @@ when defined(windows) or defined(nimdoc):
TCompletionData* = object TCompletionData* = object
sock: TAsyncFD sock: TAsyncFD
cb: proc (sock: TAsyncFD, bytesTransferred: DWORD, cb: proc (sock: TAsyncFD, bytesTransferred: DWORD,
errcode: TOSErrorCode) {.closure,gcsafe.} errcode: OSErrorCode) {.closure,gcsafe.}
PDispatcher* = ref object of PDispatcherBase PDispatcher* = ref object of PDispatcherBase
ioPort: THandle ioPort: THandle
handles: TSet[TAsyncFD] handles: HashSet[TAsyncFD]
TCustomOverlapped = object of TOVERLAPPED TCustomOverlapped = object of TOVERLAPPED
data*: TCompletionData data*: TCompletionData
@ -245,7 +245,7 @@ when defined(windows) or defined(nimdoc):
proc newDispatcher*(): PDispatcher = proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance. ## Creates a new Dispatcher instance.
new result new result
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) result.ioPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
result.handles = initSet[TAsyncFD]() result.handles = initSet[TAsyncFD]()
result.timers = @[] result.timers = @[]
@ -258,16 +258,16 @@ when defined(windows) or defined(nimdoc):
proc register*(sock: TAsyncFD) = proc register*(sock: TAsyncFD) =
## Registers ``sock`` with the dispatcher. ## Registers ``sock`` with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if CreateIOCompletionPort(sock.THandle, p.ioPort, if CreateIoCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0: cast[TCompletionKey](sock), 1) == 0:
osError(osLastError()) raiseOSError(osLastError())
p.handles.incl(sock) p.handles.incl(sock)
proc verifyPresence(sock: TAsyncFD) = proc verifyPresence(sock: TAsyncFD) =
## Ensures that socket has been registered with the dispatcher. ## Ensures that socket has been registered with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if sock notin p.handles: if sock notin p.handles:
raise newException(EInvalidValue, raise newException(ValueError,
"Operation performed on a socket which has not been registered with" & "Operation performed on a socket which has not been registered with" &
" the dispatcher yet.") " the dispatcher yet.")
@ -275,7 +275,7 @@ when defined(windows) or defined(nimdoc):
## Waits for completion events and processes them. ## Waits for completion events and processes them.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if p.handles.len == 0 and p.timers.len == 0: if p.handles.len == 0 and p.timers.len == 0:
raise newException(EInvalidValue, raise newException(ValueError,
"No handles or timers registered in dispatcher.") "No handles or timers registered in dispatcher.")
let llTimeout = let llTimeout =
@ -286,7 +286,7 @@ when defined(windows) or defined(nimdoc):
var customOverlapped: PCustomOverlapped var customOverlapped: PCustomOverlapped
let res = GetQueuedCompletionStatus(p.ioPort, let res = GetQueuedCompletionStatus(p.ioPort,
addr lpNumberOfBytesTransferred, addr lpCompletionKey, addr lpNumberOfBytesTransferred, addr lpCompletionKey,
cast[ptr POverlapped](addr customOverlapped), llTimeout).bool cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
# http://stackoverflow.com/a/12277264/492186 # http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
@ -295,7 +295,7 @@ when defined(windows) or defined(nimdoc):
assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD
customOverlapped.data.cb(customOverlapped.data.sock, customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, TOSErrorCode(-1)) lpNumberOfBytesTransferred, OSErrorCode(-1))
GC_unref(customOverlapped) GC_unref(customOverlapped)
else: else:
let errCode = osLastError() let errCode = osLastError()
@ -308,7 +308,7 @@ when defined(windows) or defined(nimdoc):
if errCode.int32 == WAIT_TIMEOUT: if errCode.int32 == WAIT_TIMEOUT:
# Timed out # Timed out
discard discard
else: osError(errCode) else: raiseOSError(errCode)
# Timer processing. # Timer processing.
processTimers(p) processTimers(p)
@ -317,9 +317,9 @@ when defined(windows) or defined(nimdoc):
var acceptExPtr: pointer = nil var acceptExPtr: pointer = nil
var getAcceptExSockAddrsPtr: pointer = nil var getAcceptExSockAddrsPtr: pointer = nil
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool = proc initPointer(s: SocketHandle, func: var pointer, guid: var TGUID): bool =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWord var bytesRet: DWORD
func = nil func = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid, result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD, sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
@ -328,34 +328,34 @@ when defined(windows) or defined(nimdoc):
proc initAll() = proc initAll() =
let dummySock = newRawSocket() let dummySock = newRawSocket()
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
osError(osLastError()) raiseOSError(osLastError())
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
osError(osLastError()) raiseOSError(osLastError())
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
osError(osLastError()) raiseOSError(osLastError())
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, proc connectEx(s: SocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword, lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool = lpdwBytesSent: PDWORD, lpOverlapped: POVERLAPPED): bool =
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().") if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().")
let func = let func =
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, cast[proc (s: SocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword, lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](connectExPtr) lpdwBytesSent: PDWORD, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr)
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
lpOverlapped) lpOverlapped)
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, proc acceptEx(listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool = lpOverlapped: POVERLAPPED): bool =
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().") if acceptExPtr.isNil: raise newException(ValueError, "Need to initialise AcceptEx().")
let func = let func =
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, cast[proc (listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](acceptExPtr) lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](acceptExPtr)
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength, result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped) lpOverlapped)
@ -365,7 +365,7 @@ when defined(windows) or defined(nimdoc):
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint, LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) = RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
if getAcceptExSockAddrsPtr.isNil: if getAcceptExSockAddrsPtr.isNil:
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().") raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().")
let func = let func =
cast[proc (lpOutputBuffer: pointer, cast[proc (lpOutputBuffer: pointer,
@ -378,7 +378,7 @@ when defined(windows) or defined(nimdoc):
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength) RemoteSockaddr, RemoteSockaddrLength)
proc connect*(socket: TAsyncFD, address: string, port: TPort, proc connect*(socket: TAsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = AF_INET): Future[void] =
## Connects ``socket`` to server at ``address:port``. ## Connects ``socket`` to server at ``address:port``.
## ##
@ -391,13 +391,13 @@ when defined(windows) or defined(nimdoc):
saddr.sin_family = int16(toInt(af)) saddr.sin_family = int16(toInt(af))
saddr.sin_port = 0 saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(socket.TSocketHandle, cast[ptr TSockAddr](addr(saddr)), if bindAddr(socket.SocketHandle, cast[ptr TSockAddr](addr(saddr)),
sizeof(saddr).TSockLen) < 0'i32: sizeof(saddr).TSockLen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
var aiList = getAddrInfo(address, port, af) var aiList = getAddrInfo(address, port, af)
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
# "the OVERLAPPED structure must remain valid until the I/O completes" # "the OVERLAPPED structure must remain valid until the I/O completes"
@ -405,17 +405,17 @@ when defined(windows) or defined(nimdoc):
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = proc (sock: TAsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == TOSErrorCode(-1): if errcode == OSErrorCode(-1):
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) retFuture.fail(newException(OSError, osErrorMsg(errcode)))
) )
var ret = connectEx(socket.TSocketHandle, it.ai_addr, var ret = connectEx(socket.SocketHandle, it.ai_addr,
sizeof(TSockAddrIn).cint, nil, 0, nil, sizeof(Tsockaddr_in).cint, nil, 0, nil,
cast[POverlapped](ol)) cast[POVERLAPPED](ol))
if ret: if ret:
# Request to connect completed immediately. # Request to connect completed immediately.
success = true success = true
@ -437,11 +437,11 @@ when defined(windows) or defined(nimdoc):
dealloc(aiList) dealloc(aiList)
if not success: if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: TAsyncFD, size: int,
flags = {TSocketFlags.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## Reads **up to** ``size`` bytes from ``socket``. Returned future will
## complete once all the data requested is read, a part of the data has been ## complete once all the data requested is read, a part of the data has been
## read, or the socket has disconnected in which case the future will ## read, or the socket has disconnected in which case the future will
@ -460,14 +460,14 @@ when defined(windows) or defined(nimdoc):
dataBuf.buf = cast[cstring](alloc0(size)) dataBuf.buf = cast[cstring](alloc0(size))
dataBuf.len = size dataBuf.len = size
var bytesReceived: DWord var bytesReceived: DWORD
var flagsio = flags.toOSFlags().DWord var flagsio = flags.toOSFlags().DWORD
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = proc (sock: TAsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == TOSErrorCode(-1): if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0': if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete("") retFuture.complete("")
else: else:
@ -479,14 +479,14 @@ when defined(windows) or defined(nimdoc):
if flags.isDisconnectionError(errcode): if flags.isDisconnectionError(errcode):
retFuture.complete("") retFuture.complete("")
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) retFuture.fail(newException(OSError, osErrorMsg(errcode)))
if dataBuf.buf != nil: if dataBuf.buf != nil:
dealloc dataBuf.buf dealloc dataBuf.buf
dataBuf.buf = nil dataBuf.buf = nil
) )
let ret = WSARecv(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
addr flagsio, cast[POverlapped](ol), nil) addr flagsio, cast[POVERLAPPED](ol), nil)
if ret == -1: if ret == -1:
let err = osLastError() let err = osLastError()
if err.int32 != ERROR_IO_PENDING: if err.int32 != ERROR_IO_PENDING:
@ -497,7 +497,7 @@ when defined(windows) or defined(nimdoc):
if flags.isDisconnectionError(err): if flags.isDisconnectionError(err):
retFuture.complete("") retFuture.complete("")
else: else:
retFuture.fail(newException(EOS, osErrorMsg(err))) retFuture.fail(newException(OSError, osErrorMsg(err)))
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
# We have to ensure that the buffer is empty because WSARecv will tell # We have to ensure that the buffer is empty because WSARecv will tell
# us immediatelly when it was disconnected, even when there is still # us immediatelly when it was disconnected, even when there is still
@ -529,7 +529,7 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {TSocketFlags.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
verifyPresence(socket) verifyPresence(socket)
@ -539,23 +539,23 @@ when defined(windows) or defined(nimdoc):
dataBuf.buf = data # since this is not used in a callback, this is fine dataBuf.buf = data # since this is not used in a callback, this is fine
dataBuf.len = data.len dataBuf.len = data.len
var bytesReceived, lowFlags: DWord var bytesReceived, lowFlags: DWORD
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = proc (sock: TAsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == TOSErrorCode(-1): if errcode == OSErrorCode(-1):
retFuture.complete() retFuture.complete()
else: else:
if flags.isDisconnectionError(errcode): if flags.isDisconnectionError(errcode):
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) retFuture.fail(newException(OSError, osErrorMsg(errcode)))
) )
let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
lowFlags, cast[POverlapped](ol), nil) lowFlags, cast[POVERLAPPED](ol), nil)
if ret == -1: if ret == -1:
let err = osLastError() let err = osLastError()
if err.int32 != ERROR_IO_PENDING: if err.int32 != ERROR_IO_PENDING:
@ -563,7 +563,7 @@ when defined(windows) or defined(nimdoc):
if flags.isDisconnectionError(err): if flags.isDisconnectionError(err):
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(EOS, osErrorMsg(err))) retFuture.fail(newException(OSError, osErrorMsg(err)))
else: else:
retFuture.complete() retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed # We don't deallocate ``ol`` here because even though this completed
@ -571,7 +571,7 @@ when defined(windows) or defined(nimdoc):
# free ``ol``. # free ``ol``.
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: TAsyncFD]] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
@ -588,21 +588,21 @@ when defined(windows) or defined(nimdoc):
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr") var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr")
var clientSock = newRawSocket() var clientSock = newRawSocket()
if clientSock == osInvalidSocket: osError(osLastError()) if clientSock == osInvalidSocket: raiseOSError(osLastError())
const lpOutputLen = 1024 const lpOutputLen = 1024
var lpOutputBuf = newString(lpOutputLen) var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: DWORD var dwBytesReceived: DWORD
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read. let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16) let dwLocalAddressLength = DWORD(sizeof (Tsockaddr_in) + 16)
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16) let dwRemoteAddressLength = DWORD(sizeof(Tsockaddr_in) + 16)
template completeAccept(): stmt {.immediate, dirty.} = template completeAccept(): stmt {.immediate, dirty.} =
var listenSock = socket var listenSock = socket
let setoptRet = setsockopt(clientSock, SOL_SOCKET, let setoptRet = setsockopt(clientSock, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
sizeof(listenSock).TSockLen) sizeof(listenSock).TSockLen)
if setoptRet != 0: osError(osLastError()) if setoptRet != 0: raiseOSError(osLastError())
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
var localLen, remoteLen: int32 var localLen, remoteLen: int32
@ -627,25 +627,25 @@ when defined(windows) or defined(nimdoc):
else: else:
retFuture.complete(newAcceptFut.read) retFuture.complete(newAcceptFut.read)
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) retFuture.fail(newException(OSError, osErrorMsg(errcode)))
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
GC_ref(ol) GC_ref(ol)
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = proc (sock: TAsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
if not retFuture.finished: if not retFuture.finished:
if errcode == TOSErrorCode(-1): if errcode == OSErrorCode(-1):
completeAccept() completeAccept()
else: else:
failAccept(errcode) failAccept(errcode)
) )
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
let ret = acceptEx(socket.TSocketHandle, clientSock, addr lpOutputBuf[0], let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0],
dwReceiveDataLength, dwReceiveDataLength,
dwLocalAddressLength, dwLocalAddressLength,
dwRemoteAddressLength, dwRemoteAddressLength,
addr dwBytesReceived, cast[POverlapped](ol)) addr dwBytesReceived, cast[POVERLAPPED](ol))
if not ret: if not ret:
let err = osLastError() let err = osLastError()
@ -660,17 +660,17 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc newAsyncRawSocket*(domain: TDomain = AF_INET, proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: TType = SOCK_STREAM, typ: SockType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TAsyncFD = protocol: Protocol = IPPROTO_TCP): TAsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly. ## Creates a new socket and registers it with the dispatcher implicitly.
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.TSocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc closeSocket*(socket: TAsyncFD) = proc closeSocket*(socket: TAsyncFD) =
## Closes a socket and ensures that it is unregistered. ## Closes a socket and ensures that it is unregistered.
socket.TSocketHandle.close() socket.SocketHandle.close()
getGlobalDispatcher().handles.excl(socket) getGlobalDispatcher().handles.excl(socket)
proc unregister*(fd: TAsyncFD) = proc unregister*(fd: TAsyncFD) =
@ -925,7 +925,7 @@ proc sleepAsync*(ms: int): Future[void] =
return retFuture return retFuture
proc accept*(socket: TAsyncFD, proc accept*(socket: TAsyncFD,
flags = {TSocketFlags.SafeDisconn}): Future[TAsyncFD] = flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
@ -1010,14 +1010,14 @@ proc processBody(node, retFutureSym: PNimrodNode,
of nnkReturnStmt: of nnkReturnStmt:
result = newNimNode(nnkStmtList, node) result = newNimNode(nnkStmtList, node)
if node[0].kind == nnkEmpty: if node[0].kind == nnkEmpty:
if not subtypeIsVoid: if not subTypeIsVoid:
result.add newCall(newIdentNode("complete"), retFutureSym, result.add newCall(newIdentNode("complete"), retFutureSym,
newIdentNode("result")) newIdentNode("result"))
else: else:
result.add newCall(newIdentNode("complete"), retFutureSym) result.add newCall(newIdentNode("complete"), retFutureSym)
else: else:
result.add newCall(newIdentNode("complete"), retFutureSym, result.add newCall(newIdentNode("complete"), retFutureSym,
node[0].processBody(retFutureSym, subtypeIsVoid, exceptBranch)) node[0].processBody(retFutureSym, subTypeIsVoid, exceptBranch))
result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt return # Don't process the children of this return stmt
@ -1072,7 +1072,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
res: PNimrodNode): bool {.compileTime.} = res: PNimrodNode): bool {.compileTime.} =
result = false result = false
while i < n[0].len: while i < n[0].len:
var processed = processBody(n[0][i], retFutureSym, subtypeIsVoid, n[1]) var processed = processBody(n[0][i], retFutureSym, subTypeIsVoid, n[1])
if processed.kind != n[0][i].kind or processed.len != n[0][i].len: if processed.kind != n[0][i].kind or processed.len != n[0][i].len:
expectKind(processed, nnkStmtList) expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse) expectKind(processed[2][1], nnkElse)
@ -1092,7 +1092,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
else: discard else: discard
for i in 0 .. <result.len: for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym, subtypeIsVoid, exceptBranch) result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, exceptBranch)
proc getName(node: PNimrodNode): string {.compileTime.} = proc getName(node: PNimrodNode): string {.compileTime.} =
case node.kind case node.kind
@ -1214,7 +1214,7 @@ proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
if c.len == 0: if c.len == 0:
return "" return ""
if c == "\r": if c == "\r":
c = await recv(socket, 1, {TSocketFlags.SafeDisconn, TSocketFlags.Peek}) c = await recv(socket, 1, {SocketFlag.SafeDisconn, SocketFlag.Peek})
if c.len > 0 and c == "\L": if c.len > 0 and c == "\L":
discard await recv(socket, 1) discard await recv(socket, 1)
addNLIfEmpty() addNLIfEmpty()

View file

@ -37,7 +37,7 @@ type
{.deprecated: [TSet: HashSet].} {.deprecated: [TSet: HashSet].}
proc isValid*[A](s: TSet[A]): bool = proc isValid*[A](s: HashSet[A]): bool =
## Returns `true` if the set has been initialized with `initSet <#initSet>`_. ## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
## ##
## Most operations over an uninitialized set will crash at runtime and ## Most operations over an uninitialized set will crash at runtime and
@ -51,7 +51,7 @@ proc isValid*[A](s: TSet[A]): bool =
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
result = not s.data.isNil result = not s.data.isNil
proc len*[A](s: TSet[A]): int = proc len*[A](s: HashSet[A]): int =
## Returns the number of keys in `s`. ## Returns the number of keys in `s`.
## ##
## Due to an implementation detail you can call this proc on variables which ## Due to an implementation detail you can call this proc on variables which
@ -65,14 +65,14 @@ proc len*[A](s: TSet[A]): int =
## assert values.len == 0 ## assert values.len == 0
result = s.counter result = s.counter
proc card*[A](s: TSet[A]): int = proc card*[A](s: HashSet[A]): int =
## Alias for `len() <#len,TSet[A]>`_. ## Alias for `len() <#len,TSet[A]>`_.
## ##
## Card stands for the `cardinality ## Card stands for the `cardinality
## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set. ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
result = s.counter result = s.counter
iterator items*[A](s: TSet[A]): A = iterator items*[A](s: HashSet[A]): A =
## Iterates over keys in the set `s`. ## Iterates over keys in the set `s`.
## ##
## If you need a sequence with the keys you can use `sequtils.toSeq() ## If you need a sequence with the keys you can use `sequtils.toSeq()
@ -120,10 +120,10 @@ template rawInsertImpl() {.dirty.} =
data[h].key = key data[h].key = key
data[h].slot = seFilled data[h].slot = seFilled
proc rawGet[A](s: TSet[A], key: A): int = proc rawGet[A](s: HashSet[A], key: A): int =
rawGetImpl() rawGetImpl()
proc mget*[A](s: var TSet[A], key: A): var A = proc mget*[A](s: var HashSet[A], key: A): var A =
## returns the element that is actually stored in 's' which has the same ## returns the element that is actually stored in 's' which has the same
## value as 'key' or raises the ``EInvalidKey`` exception. This is useful ## value as 'key' or raises the ``EInvalidKey`` exception. This is useful
## when one overloaded 'hash' and '==' but still needs reference semantics ## when one overloaded 'hash' and '==' but still needs reference semantics
@ -133,7 +133,7 @@ proc mget*[A](s: var TSet[A], key: A): var A =
if index >= 0: result = t.data[index].key if index >= 0: result = t.data[index].key
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
proc contains*[A](s: TSet[A], key: A): bool = proc contains*[A](s: HashSet[A], key: A): bool =
## Returns true iff `key` is in `s`. ## Returns true iff `key` is in `s`.
## ##
## Example: ## Example:
@ -149,10 +149,10 @@ proc contains*[A](s: TSet[A], key: A): bool =
var index = rawGet(s, key) var index = rawGet(s, key)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var TSet[A], data: var KeyValuePairSeq[A], key: A) = proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A](s: var TSet[A]) = proc enlarge[A](s: var HashSet[A]) =
var n: KeyValuePairSeq[A] var n: KeyValuePairSeq[A]
newSeq(n, len(s.data) * growthFactor) newSeq(n, len(s.data) * growthFactor)
for i in countup(0, high(s.data)): for i in countup(0, high(s.data)):
@ -175,7 +175,7 @@ template containsOrInclImpl() {.dirty.} =
rawInsert(s, s.data, key) rawInsert(s, s.data, key)
inc(s.counter) inc(s.counter)
proc incl*[A](s: var TSet[A], key: A) = proc incl*[A](s: var HashSet[A], key: A) =
## Includes an element `key` in `s`. ## Includes an element `key` in `s`.
## ##
## This doesn't do anything if `key` is already in `s`. Example: ## This doesn't do anything if `key` is already in `s`. Example:
@ -188,7 +188,7 @@ proc incl*[A](s: var TSet[A], key: A) =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
inclImpl() inclImpl()
proc incl*[A](s: var TSet[A], other: TSet[A]) = proc incl*[A](s: var HashSet[A], other: TSet[A]) =
## Includes all elements from `other` into `s`. ## Includes all elements from `other` into `s`.
## ##
## Example: ## Example:
@ -203,7 +203,7 @@ proc incl*[A](s: var TSet[A], other: TSet[A]) =
assert other.isValid, "The set `other` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized."
for item in other: incl(s, item) for item in other: incl(s, item)
proc excl*[A](s: var TSet[A], key: A) = proc excl*[A](s: var HashSet[A], key: A) =
## Excludes `key` from the set `s`. ## Excludes `key` from the set `s`.
## ##
## This doesn't do anything if `key` is not found in `s`. Example: ## This doesn't do anything if `key` is not found in `s`. Example:
@ -219,7 +219,7 @@ proc excl*[A](s: var TSet[A], key: A) =
s.data[index].slot = seDeleted s.data[index].slot = seDeleted
dec(s.counter) dec(s.counter)
proc excl*[A](s: var TSet[A], other: TSet[A]) = proc excl*[A](s: var HashSet[A], other: TSet[A]) =
## Excludes everything in `other` from `s`. ## Excludes everything in `other` from `s`.
## ##
## Example: ## Example:
@ -235,7 +235,7 @@ proc excl*[A](s: var TSet[A], other: TSet[A]) =
assert other.isValid, "The set `other` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized."
for item in other: excl(s, item) for item in other: excl(s, item)
proc containsOrIncl*[A](s: var TSet[A], key: A): bool = proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`. ## Includes `key` in the set `s` and tells if `key` was added to `s`.
## ##
## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is ## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is
@ -250,7 +250,7 @@ proc containsOrIncl*[A](s: var TSet[A], key: A): bool =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
containsOrInclImpl() containsOrInclImpl()
proc init*[A](s: var TSet[A], initialSize=64) = proc init*[A](s: var HashSet[A], initialSize=64) =
## Initializes a hash set. ## Initializes a hash set.
## ##
## The `initialSize` parameter needs to be a power of too. You can use ## The `initialSize` parameter needs to be a power of too. You can use
@ -273,7 +273,7 @@ proc init*[A](s: var TSet[A], initialSize=64) =
s.counter = 0 s.counter = 0
newSeq(s.data, initialSize) newSeq(s.data, initialSize)
proc initSet*[A](initialSize=64): TSet[A] = proc initSet*[A](initialSize=64): HashSet[A] =
## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash ## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash
## sets. ## sets.
## ##
@ -285,7 +285,7 @@ proc initSet*[A](initialSize=64): TSet[A] =
## a.incl(2) ## a.incl(2)
result.init(initialSize) result.init(initialSize)
proc toSet*[A](keys: openArray[A]): TSet[A] = proc toSet*[A](keys: openArray[A]): HashSet[A] =
## Creates a new hash set that contains the given `keys`. ## Creates a new hash set that contains the given `keys`.
## ##
## Example: ## Example:
@ -304,7 +304,7 @@ template dollarImpl(): stmt {.dirty.} =
result.add($key) result.add($key)
result.add("}") result.add("}")
proc `$`*[A](s: TSet[A]): string = proc `$`*[A](s: HashSet[A]): string =
## Converts the set `s` to a string, mostly for logging purposes. ## Converts the set `s` to a string, mostly for logging purposes.
## ##
## Don't use this proc for serialization, the representation may change at ## Don't use this proc for serialization, the representation may change at
@ -320,7 +320,7 @@ proc `$`*[A](s: TSet[A]): string =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
dollarImpl() dollarImpl()
proc union*[A](s1, s2: TSet[A]): TSet[A] = proc union*[A](s1, s2: HashSet[A]): TSet[A] =
## Returns the union of the sets `s1` and `s2`. ## Returns the union of the sets `s1` and `s2`.
## ##
## The union of two sets is represented mathematically as *A ∪ B* and is the ## The union of two sets is represented mathematically as *A ∪ B* and is the
@ -337,7 +337,7 @@ proc union*[A](s1, s2: TSet[A]): TSet[A] =
result = s1 result = s1
incl(result, s2) incl(result, s2)
proc intersection*[A](s1, s2: TSet[A]): TSet[A] = proc intersection*[A](s1, s2: HashSet[A]): TSet[A] =
## Returns the intersection of the sets `s1` and `s2`. ## Returns the intersection of the sets `s1` and `s2`.
## ##
## The intersection of two sets is represented mathematically as *A ∩ B* and ## The intersection of two sets is represented mathematically as *A ∩ B* and
@ -356,7 +356,7 @@ proc intersection*[A](s1, s2: TSet[A]): TSet[A] =
for item in s1: for item in s1:
if item in s2: incl(result, item) if item in s2: incl(result, item)
proc difference*[A](s1, s2: TSet[A]): TSet[A] = proc difference*[A](s1, s2: HashSet[A]): TSet[A] =
## Returns the difference of the sets `s1` and `s2`. ## Returns the difference of the sets `s1` and `s2`.
## ##
## The difference of two sets is represented mathematically as *A \ B* and is ## The difference of two sets is represented mathematically as *A \ B* and is
@ -376,7 +376,7 @@ proc difference*[A](s1, s2: TSet[A]): TSet[A] =
if not contains(s2, item): if not contains(s2, item):
incl(result, item) incl(result, item)
proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = proc symmetricDifference*[A](s1, s2: HashSet[A]): TSet[A] =
## Returns the symmetric difference of the sets `s1` and `s2`. ## Returns the symmetric difference of the sets `s1` and `s2`.
## ##
## The symmetric difference of two sets is represented mathematically as *A △ ## The symmetric difference of two sets is represented mathematically as *A △
@ -395,23 +395,23 @@ proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] =
for item in s2: for item in s2:
if containsOrIncl(result, item): excl(result, item) if containsOrIncl(result, item): excl(result, item)
proc `+`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = proc `+`*[A](s1, s2: HashSet[A]): TSet[A] {.inline.} =
## Alias for `union(s1, s2) <#union>`_. ## Alias for `union(s1, s2) <#union>`_.
result = union(s1, s2) result = union(s1, s2)
proc `*`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = proc `*`*[A](s1, s2: HashSet[A]): TSet[A] {.inline.} =
## Alias for `intersection(s1, s2) <#intersection>`_. ## Alias for `intersection(s1, s2) <#intersection>`_.
result = intersection(s1, s2) result = intersection(s1, s2)
proc `-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = proc `-`*[A](s1, s2: HashSet[A]): TSet[A] {.inline.} =
## Alias for `difference(s1, s2) <#difference>`_. ## Alias for `difference(s1, s2) <#difference>`_.
result = difference(s1, s2) result = difference(s1, s2)
proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = proc `-+-`*[A](s1, s2: HashSet[A]): TSet[A] {.inline.} =
## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_. ## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_.
result = symmetricDifference(s1, s2) result = symmetricDifference(s1, s2)
proc disjoint*[A](s1, s2: TSet[A]): bool = proc disjoint*[A](s1, s2: HashSet[A]): bool =
## Returns true iff the sets `s1` and `s2` have no items in common. ## Returns true iff the sets `s1` and `s2` have no items in common.
## ##
## Example: ## Example:
@ -428,7 +428,7 @@ proc disjoint*[A](s1, s2: TSet[A]): bool =
if item in s2: return false if item in s2: return false
return true return true
proc `<`*[A](s, t: TSet[A]): bool = proc `<`*[A](s, t: HashSet[A]): bool =
## Returns true if `s` is a strict or proper subset of `t`. ## Returns true if `s` is a strict or proper subset of `t`.
## ##
## A strict or proper subset `s` has all of its members in `t` but `t` has ## A strict or proper subset `s` has all of its members in `t` but `t` has
@ -443,7 +443,7 @@ proc `<`*[A](s, t: TSet[A]): bool =
## assert((a < a) == false) ## assert((a < a) == false)
s.counter != t.counter and s <= t s.counter != t.counter and s <= t
proc `<=`*[A](s, t: TSet[A]): bool = proc `<=`*[A](s, t: HashSet[A]): bool =
## Returns true if `s` is subset of `t`. ## Returns true if `s` is subset of `t`.
## ##
## A subset `s` has all of its members in `t` and `t` doesn't necessarily ## A subset `s` has all of its members in `t` and `t` doesn't necessarily
@ -464,7 +464,7 @@ proc `<=`*[A](s, t: TSet[A]): bool =
result = false result = false
return return
proc `==`*[A](s, t: TSet[A]): bool = proc `==`*[A](s, t: HashSet[A]): bool =
## Returns true if both `s` and `t` have the same members and set size. ## Returns true if both `s` and `t` have the same members and set size.
## ##
## Example: ## Example:
@ -477,7 +477,7 @@ proc `==`*[A](s, t: TSet[A]): bool =
## assert a == b ## assert a == b
s.counter == t.counter and s <= t s.counter == t.counter and s <= t
proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
## Returns a new set after applying `op` on each of the elements of `data`. ## Returns a new set after applying `op` on each of the elements of `data`.
## ##
## You can use this proc to transform the elements from a set. Example: ## You can use this proc to transform the elements from a set. Example:
@ -534,7 +534,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
## assert values.len == 0 ## assert values.len == 0
result = s.counter result = s.counter
proc card*[A](s: TOrderedSet[A]): int {.inline.} = proc card*[A](s: OrderedSet[A]): int {.inline.} =
## Alias for `len() <#len,TOrderedSet[A]>`_. ## Alias for `len() <#len,TOrderedSet[A]>`_.
## ##
## Card stands for the `cardinality ## Card stands for the `cardinality
@ -548,7 +548,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if s.data[h].slot == seFilled: yieldStmt if s.data[h].slot == seFilled: yieldStmt
h = nxt h = nxt
iterator items*[A](s: TOrderedSet[A]): A = iterator items*[A](s: OrderedSet[A]): A =
## Iterates over keys in the ordered set `s` in insertion order. ## Iterates over keys in the ordered set `s` in insertion order.
## ##
## If you need a sequence with the keys you can use `sequtils.toSeq() ## If you need a sequence with the keys you can use `sequtils.toSeq()
@ -570,10 +570,10 @@ iterator items*[A](s: TOrderedSet[A]): A =
forAllOrderedPairs: forAllOrderedPairs:
yield s.data[h].key yield s.data[h].key
proc rawGet[A](s: TOrderedSet[A], key: A): int = proc rawGet[A](s: OrderedSet[A], key: A): int =
rawGetImpl() rawGetImpl()
proc contains*[A](s: TOrderedSet[A], key: A): bool = proc contains*[A](s: OrderedSet[A], key: A): bool =
## Returns true iff `key` is in `s`. ## Returns true iff `key` is in `s`.
## ##
## Example: ## Example:
@ -587,7 +587,7 @@ proc contains*[A](s: TOrderedSet[A], key: A): bool =
var index = rawGet(s, key) var index = rawGet(s, key)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var TOrderedSet[A], proc rawInsert[A](s: var OrderedSet[A],
data: var OrderedKeyValuePairSeq[A], key: A) = data: var OrderedKeyValuePairSeq[A], key: A) =
rawInsertImpl() rawInsertImpl()
data[h].next = -1 data[h].next = -1
@ -595,7 +595,7 @@ proc rawInsert[A](s: var TOrderedSet[A],
if s.last >= 0: data[s.last].next = h if s.last >= 0: data[s.last].next = h
s.last = h s.last = h
proc enlarge[A](s: var TOrderedSet[A]) = proc enlarge[A](s: var OrderedSet[A]) =
var n: OrderedKeyValuePairSeq[A] var n: OrderedKeyValuePairSeq[A]
newSeq(n, len(s.data) * growthFactor) newSeq(n, len(s.data) * growthFactor)
var h = s.first var h = s.first
@ -608,7 +608,7 @@ proc enlarge[A](s: var TOrderedSet[A]) =
h = nxt h = nxt
swap(s.data, n) swap(s.data, n)
proc incl*[A](s: var TOrderedSet[A], key: A) = proc incl*[A](s: var OrderedSet[A], key: A) =
## Includes an element `key` in `s`. ## Includes an element `key` in `s`.
## ##
## This doesn't do anything if `key` is already in `s`. Example: ## This doesn't do anything if `key` is already in `s`. Example:
@ -621,7 +621,7 @@ proc incl*[A](s: var TOrderedSet[A], key: A) =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
inclImpl() inclImpl()
proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = proc incl*[A](s: var HashSet[A], other: TOrderedSet[A]) =
## Includes all elements from `other` into `s`. ## Includes all elements from `other` into `s`.
## ##
## Example: ## Example:
@ -636,7 +636,7 @@ proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) =
assert other.isValid, "The set `other` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized."
for item in other: incl(s, item) for item in other: incl(s, item)
proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`. ## Includes `key` in the set `s` and tells if `key` was added to `s`.
## ##
## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc ## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc
@ -651,7 +651,7 @@ proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
containsOrInclImpl() containsOrInclImpl()
proc init*[A](s: var TOrderedSet[A], initialSize=64) = proc init*[A](s: var OrderedSet[A], initialSize=64) =
## Initializes an ordered hash set. ## Initializes an ordered hash set.
## ##
## The `initialSize` parameter needs to be a power of too. You can use ## The `initialSize` parameter needs to be a power of too. You can use
@ -676,7 +676,7 @@ proc init*[A](s: var TOrderedSet[A], initialSize=64) =
s.last = -1 s.last = -1
newSeq(s.data, initialSize) newSeq(s.data, initialSize)
proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = proc initOrderedSet*[A](initialSize=64): OrderedSet[A] =
## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of ## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of
## ordered hash sets. ## ordered hash sets.
## ##
@ -688,7 +688,7 @@ proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
## a.incl(2) ## a.incl(2)
result.init(initialSize) result.init(initialSize)
proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
## Creates a new ordered hash set that contains the given `keys`. ## Creates a new ordered hash set that contains the given `keys`.
## ##
## Example: ## Example:
@ -700,7 +700,7 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
result = initOrderedSet[A](nextPowerOfTwo(keys.len+10)) result = initOrderedSet[A](nextPowerOfTwo(keys.len+10))
for key in items(keys): result.incl(key) for key in items(keys): result.incl(key)
proc `$`*[A](s: TOrderedSet[A]): string = proc `$`*[A](s: OrderedSet[A]): string =
## Converts the ordered hash set `s` to a string, mostly for logging purposes. ## Converts the ordered hash set `s` to a string, mostly for logging purposes.
## ##
## Don't use this proc for serialization, the representation may change at ## Don't use this proc for serialization, the representation may change at
@ -716,7 +716,7 @@ proc `$`*[A](s: TOrderedSet[A]): string =
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
dollarImpl() dollarImpl()
proc `==`*[A](s, t: TOrderedSet[A]): bool = proc `==`*[A](s, t: OrderedSet[A]): bool =
## Equality for ordered sets. ## Equality for ordered sets.
if s.counter != t.counter: return false if s.counter != t.counter: return false
var h = s.first var h = s.first
@ -738,7 +738,7 @@ proc testModule() =
## Internal micro test to validate docstrings and such. ## Internal micro test to validate docstrings and such.
block isValidTest: block isValidTest:
var options: HashSet[string] var options: HashSet[string]
proc savePreferences(options: TSet[string]) = proc savePreferences(options: HashSet[string]) =
assert options.isValid, "Pass an initialized set!" assert options.isValid, "Pass an initialized set!"
options = initSet[string]() options = initSet[string]()
options.savePreferences options.savePreferences
@ -839,7 +839,7 @@ proc testModule() =
block isValidTest: block isValidTest:
var cards: OrderedSet[string] var cards: OrderedSet[string]
proc saveTarotCards(cards: TOrderedSet[string]) = proc saveTarotCards(cards: OrderedSet[string]) =
assert cards.isValid, "Pass an initialized set!" assert cards.isValid, "Pass an initialized set!"
cards = initOrderedSet[string]() cards = initOrderedSet[string]()
cards.saveTarotCards cards.saveTarotCards
@ -891,7 +891,7 @@ proc testModule() =
a.incl(2) a.incl(2)
assert a.len == 1 assert a.len == 1
var b: TSet[int] var b: HashSet[int]
b.init(4) b.init(4)
b.incl(2) b.incl(2)
b.init b.init

View file

@ -74,32 +74,32 @@ type
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
proc len*[A, B](t: TTable[A, B]): int = proc len*[A, B](t: Table[A, B]): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
iterator pairs*[A, B](t: TTable[A, B]): tuple[key: A, val: B] = iterator pairs*[A, B](t: Table[A, B]): tuple[key: A, val: B] =
## iterates over any (key, value) pair in the table `t`. ## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A, B](t: var TTable[A, B]): tuple[key: A, val: var B] = iterator mpairs*[A, B](t: var Table[A, B]): tuple[key: A, val: var B] =
## iterates over any (key, value) pair in the table `t`. The values ## iterates over any (key, value) pair in the table `t`. The values
## can be modified. ## can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
iterator keys*[A, B](t: TTable[A, B]): A = iterator keys*[A, B](t: Table[A, B]): A =
## iterates over any key in the table `t`. ## iterates over any key in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].key if t.data[h].slot == seFilled: yield t.data[h].key
iterator values*[A, B](t: TTable[A, B]): B = iterator values*[A, B](t: Table[A, B]): B =
## iterates over any value in the table `t`. ## iterates over any value in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].val if t.data[h].slot == seFilled: yield t.data[h].val
iterator mvalues*[A, B](t: var TTable[A, B]): var B = iterator mvalues*[A, B](t: var Table[A, B]): var B =
## iterates over any value in the table `t`. The values can be modified. ## iterates over any value in the table `t`. The values can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].val if t.data[h].slot == seFilled: yield t.data[h].val
@ -130,10 +130,10 @@ template rawInsertImpl() {.dirty.} =
data[h].val = val data[h].val = val
data[h].slot = seFilled data[h].slot = seFilled
proc rawGet[A, B](t: TTable[A, B], key: A): int = proc rawGet[A, B](t: Table[A, B], key: A): int =
rawGetImpl() rawGetImpl()
proc `[]`*[A, B](t: TTable[A, B], key: A): B = proc `[]`*[A, B](t: Table[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
@ -141,14 +141,14 @@ proc `[]`*[A, B](t: TTable[A, B], key: A): B =
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var TTable[A, B], key: A): var B = proc mget*[A, B](t: var Table[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
iterator allValues*[A, B](t: TTable[A, B]; key: A): B = iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`. ## iterates over any value in the table `t` that belongs to the given `key`.
var h: THash = hash(key) and high(t.data) var h: THash = hash(key) and high(t.data)
while t.data[h].slot != seEmpty: while t.data[h].slot != seEmpty:
@ -156,15 +156,15 @@ iterator allValues*[A, B](t: TTable[A, B]; key: A): B =
yield t.data[h].val yield t.data[h].val
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
proc hasKey*[A, B](t: TTable[A, B], key: A): bool = proc hasKey*[A, B](t: Table[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc rawInsert[A, B](t: var TTable[A, B], data: var KeyValuePairSeq[A, B], proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B],
key: A, val: B) = key: A, val: B) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A, B](t: var TTable[A, B]) = proc enlarge[A, B](t: var Table[A, B]) =
var n: KeyValuePairSeq[A, B] var n: KeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
@ -196,22 +196,22 @@ when false:
inc(t.counter) inc(t.counter)
result = false result = false
proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) = proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
putImpl() putImpl()
proc add*[A, B](t: var TTable[A, B], key: A, val: B) = proc add*[A, B](t: var Table[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
addImpl() addImpl()
proc del*[A, B](t: var TTable[A, B], key: A) = proc del*[A, B](t: var Table[A, B], key: A) =
## deletes `key` from hash table `t`. ## deletes `key` from hash table `t`.
let index = rawGet(t, key) let index = rawGet(t, key)
if index >= 0: if index >= 0:
t.data[index].slot = seDeleted t.data[index].slot = seDeleted
dec(t.counter) dec(t.counter)
proc initTable*[A, B](initialSize=64): TTable[A, B] = proc initTable*[A, B](initialSize=64): Table[A, B] =
## creates a new hash table that is empty. ## creates a new hash table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
@ -222,7 +222,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] =
newSeq(result.data, initialSize) newSeq(result.data, initialSize)
proc toTable*[A, B](pairs: openArray[tuple[key: A, proc toTable*[A, B](pairs: openArray[tuple[key: A,
val: B]]): TTable[A, B] = val: B]]): Table[A, B] =
## creates a new hash table that contains the given `pairs`. ## creates a new hash table that contains the given `pairs`.
result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
for key, val in items(pairs): result[key] = val for key, val in items(pairs): result[key] = val
@ -239,7 +239,7 @@ template dollarImpl(): stmt {.dirty.} =
result.add($val) result.add($val)
result.add("}") result.add("}")
proc `$`*[A, B](t: TTable[A, B]): string = proc `$`*[A, B](t: Table[A, B]): string =
## The `$` operator for hash tables. ## The `$` operator for hash tables.
dollarImpl() dollarImpl()
@ -253,93 +253,93 @@ template equalsImpl() =
if t[key] != val: return false if t[key] != val: return false
return true return true
proc `==`*[A, B](s, t: TTable[A, B]): bool = proc `==`*[A, B](s, t: Table[A, B]): bool =
equalsImpl() equalsImpl()
proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] = proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
## Index the collection with the proc provided. ## Index the collection with the proc provided.
# TODO: As soon as supported, change collection: A to collection: A[B] # TODO: As soon as supported, change collection: A to collection: A[B]
result = initTable[C, B]() result = initTable[C, B]()
for item in collection: for item in collection:
result[index(item)] = item result[index(item)] = item
proc len*[A, B](t: PTable[A, B]): int = proc len*[A, B](t: TableRef[A, B]): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
iterator pairs*[A, B](t: PTable[A, B]): tuple[key: A, val: B] = iterator pairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: B] =
## iterates over any (key, value) pair in the table `t`. ## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A, B](t: PTable[A, B]): tuple[key: A, val: var B] = iterator mpairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: var B] =
## iterates over any (key, value) pair in the table `t`. The values ## iterates over any (key, value) pair in the table `t`. The values
## can be modified. ## can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val)
iterator keys*[A, B](t: PTable[A, B]): A = iterator keys*[A, B](t: TableRef[A, B]): A =
## iterates over any key in the table `t`. ## iterates over any key in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].key if t.data[h].slot == seFilled: yield t.data[h].key
iterator values*[A, B](t: PTable[A, B]): B = iterator values*[A, B](t: TableRef[A, B]): B =
## iterates over any value in the table `t`. ## iterates over any value in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].val if t.data[h].slot == seFilled: yield t.data[h].val
iterator mvalues*[A, B](t: PTable[A, B]): var B = iterator mvalues*[A, B](t: TableRef[A, B]): var B =
## iterates over any value in the table `t`. The values can be modified. ## iterates over any value in the table `t`. The values can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].val if t.data[h].slot == seFilled: yield t.data[h].val
proc `[]`*[A, B](t: PTable[A, B], key: A): B = proc `[]`*[A, B](t: TableRef[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
## exists. ## exists.
result = t[][key] result = t[][key]
proc mget*[A, B](t: PTable[A, B], key: A): var B = proc mget*[A, B](t: TableRef[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
t[].mget(key) t[].mget(key)
proc hasKey*[A, B](t: PTable[A, B], key: A): bool = proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = t[].hasKey(key) result = t[].hasKey(key)
proc `[]=`*[A, B](t: PTable[A, B], key: A, val: B) = proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
t[][key] = val t[][key] = val
proc add*[A, B](t: PTable[A, B], key: A, val: B) = proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
t[].add(key, val) t[].add(key, val)
proc del*[A, B](t: PTable[A, B], key: A) = proc del*[A, B](t: TableRef[A, B], key: A) =
## deletes `key` from hash table `t`. ## deletes `key` from hash table `t`.
t[].del(key) t[].del(key)
proc newTable*[A, B](initialSize=64): PTable[A, B] = proc newTable*[A, B](initialSize=64): TableRef[A, B] =
new(result) new(result)
result[] = initTable[A, B](initialSize) result[] = initTable[A, B](initialSize)
proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): PTable[A, B] = proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): TableRef[A, B] =
## creates a new hash table that contains the given `pairs`. ## creates a new hash table that contains the given `pairs`.
new(result) new(result)
result[] = toTable[A, B](pairs) result[] = toTable[A, B](pairs)
proc `$`*[A, B](t: PTable[A, B]): string = proc `$`*[A, B](t: TableRef[A, B]): string =
## The `$` operator for hash tables. ## The `$` operator for hash tables.
dollarImpl() dollarImpl()
proc `==`*[A, B](s, t: PTable[A, B]): bool = proc `==`*[A, B](s, t: TableRef[A, B]): bool =
if isNil(s): result = isNil(t) if isNil(s): result = isNil(t)
elif isNil(t): result = false elif isNil(t): result = false
else: result = equalsImpl() else: result = equalsImpl()
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] = proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
## Index the collection with the proc provided. ## Index the collection with the proc provided.
# TODO: As soon as supported, change collection: A to collection: A[B] # TODO: As soon as supported, change collection: A to collection: A[B]
result = newTable[C, B]() result = newTable[C, B]()
@ -360,7 +360,7 @@ type
{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].} {.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].}
proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} = proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
@ -371,38 +371,38 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if t.data[h].slot == seFilled: yieldStmt if t.data[h].slot == seFilled: yieldStmt
h = nxt h = nxt
iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: B] = iterator pairs*[A, B](t: OrderedTable[A, B]): tuple[key: A, val: B] =
## iterates over any (key, value) pair in the table `t` in insertion ## iterates over any (key, value) pair in the table `t` in insertion
## order. ## order.
forAllOrderedPairs: forAllOrderedPairs:
yield (t.data[h].key, t.data[h].val) yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A, B](t: var TOrderedTable[A, B]): tuple[key: A, val: var B] = iterator mpairs*[A, B](t: var OrderedTable[A, B]): tuple[key: A, val: var B] =
## iterates over any (key, value) pair in the table `t` in insertion ## iterates over any (key, value) pair in the table `t` in insertion
## order. The values can be modified. ## order. The values can be modified.
forAllOrderedPairs: forAllOrderedPairs:
yield (t.data[h].key, t.data[h].val) yield (t.data[h].key, t.data[h].val)
iterator keys*[A, B](t: TOrderedTable[A, B]): A = iterator keys*[A, B](t: OrderedTable[A, B]): A =
## iterates over any key in the table `t` in insertion order. ## iterates over any key in the table `t` in insertion order.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].key yield t.data[h].key
iterator values*[A, B](t: TOrderedTable[A, B]): B = iterator values*[A, B](t: OrderedTable[A, B]): B =
## iterates over any value in the table `t` in insertion order. ## iterates over any value in the table `t` in insertion order.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
iterator mvalues*[A, B](t: var TOrderedTable[A, B]): var B = iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
## iterates over any value in the table `t` in insertion order. The values ## iterates over any value in the table `t` in insertion order. The values
## can be modified. ## can be modified.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
proc rawGet[A, B](t: TOrderedTable[A, B], key: A): int = proc rawGet[A, B](t: OrderedTable[A, B], key: A): int =
rawGetImpl() rawGetImpl()
proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B = proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
@ -410,18 +410,18 @@ proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var TOrderedTable[A, B], key: A): var B = proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool = proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc rawInsert[A, B](t: var TOrderedTable[A, B], proc rawInsert[A, B](t: var OrderedTable[A, B],
data: var OrderedKeyValuePairSeq[A, B], data: var OrderedKeyValuePairSeq[A, B],
key: A, val: B) = key: A, val: B) =
rawInsertImpl() rawInsertImpl()
@ -430,7 +430,7 @@ proc rawInsert[A, B](t: var TOrderedTable[A, B],
if t.last >= 0: data[t.last].next = h if t.last >= 0: data[t.last].next = h
t.last = h t.last = h
proc enlarge[A, B](t: var TOrderedTable[A, B]) = proc enlarge[A, B](t: var OrderedTable[A, B]) =
var n: OrderedKeyValuePairSeq[A, B] var n: OrderedKeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)
var h = t.first var h = t.first
@ -443,15 +443,15 @@ proc enlarge[A, B](t: var TOrderedTable[A, B]) =
h = nxt h = nxt
swap(t.data, n) swap(t.data, n)
proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
putImpl() putImpl()
proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
addImpl() addImpl()
proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] = proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
## creates a new ordered hash table that is empty. ## creates a new ordered hash table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
@ -464,16 +464,16 @@ proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
newSeq(result.data, initialSize) newSeq(result.data, initialSize)
proc toOrderedTable*[A, B](pairs: openArray[tuple[key: A, proc toOrderedTable*[A, B](pairs: openArray[tuple[key: A,
val: B]]): TOrderedTable[A, B] = val: B]]): OrderedTable[A, B] =
## creates a new ordered hash table that contains the given `pairs`. ## creates a new ordered hash table that contains the given `pairs`.
result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
for key, val in items(pairs): result[key] = val for key, val in items(pairs): result[key] = val
proc `$`*[A, B](t: TOrderedTable[A, B]): string = proc `$`*[A, B](t: OrderedTable[A, B]): string =
## The `$` operator for ordered hash tables. ## The `$` operator for ordered hash tables.
dollarImpl() dollarImpl()
proc sort*[A, B](t: var TOrderedTable[A, B], proc sort*[A, B](t: var OrderedTable[A, B],
cmp: proc (x,y: tuple[key: A, val: B]): int) = cmp: proc (x,y: tuple[key: A, val: B]): int) =
## sorts `t` according to `cmp`. This modifies the internal list ## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this ## that kept the insertion order, so insertion order is lost after this
@ -519,7 +519,7 @@ proc sort*[A, B](t: var TOrderedTable[A, B],
t.first = list t.first = list
t.last = tail t.last = tail
proc len*[A, B](t: POrderedTable[A, B]): int {.inline.} = proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
@ -530,59 +530,59 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if t.data[h].slot == seFilled: yieldStmt if t.data[h].slot == seFilled: yieldStmt
h = nxt h = nxt
iterator pairs*[A, B](t: POrderedTable[A, B]): tuple[key: A, val: B] = iterator pairs*[A, B](t: OrderedTableRef[A, B]): tuple[key: A, val: B] =
## iterates over any (key, value) pair in the table `t` in insertion ## iterates over any (key, value) pair in the table `t` in insertion
## order. ## order.
forAllOrderedPairs: forAllOrderedPairs:
yield (t.data[h].key, t.data[h].val) yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A, B](t: POrderedTable[A, B]): tuple[key: A, val: var B] = iterator mpairs*[A, B](t: OrderedTableRef[A, B]): tuple[key: A, val: var B] =
## iterates over any (key, value) pair in the table `t` in insertion ## iterates over any (key, value) pair in the table `t` in insertion
## order. The values can be modified. ## order. The values can be modified.
forAllOrderedPairs: forAllOrderedPairs:
yield (t.data[h].key, t.data[h].val) yield (t.data[h].key, t.data[h].val)
iterator keys*[A, B](t: POrderedTable[A, B]): A = iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
## iterates over any key in the table `t` in insertion order. ## iterates over any key in the table `t` in insertion order.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].key yield t.data[h].key
iterator values*[A, B](t: POrderedTable[A, B]): B = iterator values*[A, B](t: OrderedTableRef[A, B]): B =
## iterates over any value in the table `t` in insertion order. ## iterates over any value in the table `t` in insertion order.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
iterator mvalues*[A, B](t: POrderedTable[A, B]): var B = iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
## iterates over any value in the table `t` in insertion order. The values ## iterates over any value in the table `t` in insertion order. The values
## can be modified. ## can be modified.
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
proc `[]`*[A, B](t: POrderedTable[A, B], key: A): B = proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
## exists. ## exists.
result = t[][key] result = t[][key]
proc mget*[A, B](t: POrderedTable[A, B], key: A): var B = proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
result = t[].mget(key) result = t[].mget(key)
proc hasKey*[A, B](t: POrderedTable[A, B], key: A): bool = proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = t[].hasKey(key) result = t[].hasKey(key)
proc `[]=`*[A, B](t: POrderedTable[A, B], key: A, val: B) = proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
t[][key] = val t[][key] = val
proc add*[A, B](t: POrderedTable[A, B], key: A, val: B) = proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
t[].add(key, val) t[].add(key, val)
proc newOrderedTable*[A, B](initialSize=64): POrderedTable[A, B] = proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
## creates a new ordered hash table that is empty. ## creates a new ordered hash table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
@ -592,16 +592,16 @@ proc newOrderedTable*[A, B](initialSize=64): POrderedTable[A, B] =
result[] = initOrderedTable[A, B]() result[] = initOrderedTable[A, B]()
proc newOrderedTable*[A, B](pairs: openArray[tuple[key: A, proc newOrderedTable*[A, B](pairs: openArray[tuple[key: A,
val: B]]): POrderedTable[A, B] = val: B]]): OrderedTableRef[A, B] =
## creates a new ordered hash table that contains the given `pairs`. ## creates a new ordered hash table that contains the given `pairs`.
result = newOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) result = newOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
for key, val in items(pairs): result[key] = val for key, val in items(pairs): result[key] = val
proc `$`*[A, B](t: POrderedTable[A, B]): string = proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
## The `$` operator for ordered hash tables. ## The `$` operator for ordered hash tables.
dollarImpl() dollarImpl()
proc sort*[A, B](t: POrderedTable[A, B], proc sort*[A, B](t: OrderedTableRef[A, B],
cmp: proc (x,y: tuple[key: A, val: B]): int) = cmp: proc (x,y: tuple[key: A, val: B]): int) =
## sorts `t` according to `cmp`. This modifies the internal list ## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this ## that kept the insertion order, so insertion order is lost after this
@ -620,81 +620,81 @@ type
{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].} {.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].}
proc len*[A](t: TCountTable[A]): int = proc len*[A](t: CountTable[A]): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
iterator pairs*[A](t: TCountTable[A]): tuple[key: A, val: int] = iterator pairs*[A](t: CountTable[A]): tuple[key: A, val: int] =
## iterates over any (key, value) pair in the table `t`. ## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A](t: var TCountTable[A]): tuple[key: A, val: var int] = iterator mpairs*[A](t: var CountTable[A]): tuple[key: A, val: var int] =
## iterates over any (key, value) pair in the table `t`. The values can ## iterates over any (key, value) pair in the table `t`. The values can
## be modified. ## be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
iterator keys*[A](t: TCountTable[A]): A = iterator keys*[A](t: CountTable[A]): A =
## iterates over any key in the table `t`. ## iterates over any key in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].key if t.data[h].val != 0: yield t.data[h].key
iterator values*[A](t: TCountTable[A]): int = iterator values*[A](t: CountTable[A]): int =
## iterates over any value in the table `t`. ## iterates over any value in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
iterator mvalues*[A](t: TCountTable[A]): var int = iterator mvalues*[A](t: CountTable[A]): var int =
## iterates over any value in the table `t`. The values can be modified. ## iterates over any value in the table `t`. The values can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
proc rawGet[A](t: TCountTable[A], key: A): int = proc rawGet[A](t: CountTable[A], key: A): int =
var h: THash = hash(key) and high(t.data) # start with real hash value var h: THash = hash(key) and high(t.data) # start with real hash value
while t.data[h].val != 0: while t.data[h].val != 0:
if t.data[h].key == key: return h if t.data[h].key == key: return h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = -1 result = -1
proc `[]`*[A](t: TCountTable[A], key: A): int = proc `[]`*[A](t: CountTable[A], key: A): int =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## 0 is returned. One can check with ``hasKey`` whether the key ## 0 is returned. One can check with ``hasKey`` whether the key
## exists. ## exists.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
proc mget*[A](t: var TCountTable[A], key: A): var int = proc mget*[A](t: var CountTable[A], key: A): var int =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
proc hasKey*[A](t: TCountTable[A], key: A): bool = proc hasKey*[A](t: CountTable[A], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]], proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
key: A, val: int) = key: A, val: int) =
var h: THash = hash(key) and high(data) var h: THash = hash(key) and high(data)
while data[h].val != 0: h = nextTry(h, high(data)) while data[h].val != 0: h = nextTry(h, high(data))
data[h].key = key data[h].key = key
data[h].val = val data[h].val = val
proc enlarge[A](t: var TCountTable[A]) = proc enlarge[A](t: var CountTable[A]) =
var n: seq[tuple[key: A, val: int]] var n: seq[tuple[key: A, val: int]]
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val) if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
swap(t.data, n) swap(t.data, n)
proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) = proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
## puts a (key, value)-pair into `t`. `val` has to be positive. ## puts a (key, value)-pair into `t`. `val` has to be positive.
assert val > 0 assert val > 0
putImpl() putImpl()
proc initCountTable*[A](initialSize=64): TCountTable[A] = proc initCountTable*[A](initialSize=64): CountTable[A] =
## creates a new count table that is empty. ## creates a new count table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
@ -704,16 +704,16 @@ proc initCountTable*[A](initialSize=64): TCountTable[A] =
result.counter = 0 result.counter = 0
newSeq(result.data, initialSize) newSeq(result.data, initialSize)
proc toCountTable*[A](keys: openArray[A]): TCountTable[A] = proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
## creates a new count table with every key in `keys` having a count of 1. ## creates a new count table with every key in `keys` having a count of 1.
result = initCountTable[A](nextPowerOfTwo(keys.len+10)) result = initCountTable[A](nextPowerOfTwo(keys.len+10))
for key in items(keys): result[key] = 1 for key in items(keys): result[key] = 1
proc `$`*[A](t: TCountTable[A]): string = proc `$`*[A](t: CountTable[A]): string =
## The `$` operator for count tables. ## The `$` operator for count tables.
dollarImpl() dollarImpl()
proc inc*[A](t: var TCountTable[A], key: A, val = 1) = proc inc*[A](t: var CountTable[A], key: A, val = 1) =
## increments `t[key]` by `val`. ## increments `t[key]` by `val`.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: if index >= 0:
@ -723,7 +723,7 @@ proc inc*[A](t: var TCountTable[A], key: A, val = 1) =
rawInsert(t, t.data, key, val) rawInsert(t, t.data, key, val)
inc(t.counter) inc(t.counter)
proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
## returns the largest (key,val)-pair. Efficiency: O(n) ## returns the largest (key,val)-pair. Efficiency: O(n)
assert t.len > 0 assert t.len > 0
var minIdx = 0 var minIdx = 0
@ -732,7 +732,7 @@ proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
result.key = t.data[minIdx].key result.key = t.data[minIdx].key
result.val = t.data[minIdx].val result.val = t.data[minIdx].val
proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n)
assert t.len > 0 assert t.len > 0
var maxIdx = 0 var maxIdx = 0
@ -741,7 +741,7 @@ proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
result.key = t.data[maxIdx].key result.key = t.data[maxIdx].key
result.val = t.data[maxIdx].val result.val = t.data[maxIdx].val
proc sort*[A](t: var TCountTable[A]) = proc sort*[A](t: var CountTable[A]) =
## sorts the count table so that the entry with the highest counter comes ## sorts the count table so that the entry with the highest counter comes
## first. This is destructive! You must not modify `t` afterwards! ## first. This is destructive! You must not modify `t` afterwards!
## You can use the iterators `pairs`, `keys`, and `values` to iterate over ## You can use the iterators `pairs`, `keys`, and `values` to iterate over
@ -762,57 +762,57 @@ proc sort*[A](t: var TCountTable[A]) =
if j < h: break if j < h: break
if h == 1: break if h == 1: break
proc len*[A](t: PCountTable[A]): int = proc len*[A](t: CountTableRef[A]): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
iterator pairs*[A](t: PCountTable[A]): tuple[key: A, val: int] = iterator pairs*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
## iterates over any (key, value) pair in the table `t`. ## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
iterator mpairs*[A](t: PCountTable[A]): tuple[key: A, val: var int] = iterator mpairs*[A](t: CountTableRef[A]): tuple[key: A, val: var int] =
## iterates over any (key, value) pair in the table `t`. The values can ## iterates over any (key, value) pair in the table `t`. The values can
## be modified. ## be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
iterator keys*[A](t: PCountTable[A]): A = iterator keys*[A](t: CountTableRef[A]): A =
## iterates over any key in the table `t`. ## iterates over any key in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].key if t.data[h].val != 0: yield t.data[h].key
iterator values*[A](t: PCountTable[A]): int = iterator values*[A](t: CountTableRef[A]): int =
## iterates over any value in the table `t`. ## iterates over any value in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
iterator mvalues*[A](t: PCountTable[A]): var int = iterator mvalues*[A](t: CountTableRef[A]): var int =
## iterates over any value in the table `t`. The values can be modified. ## iterates over any value in the table `t`. The values can be modified.
for h in 0..high(t.data): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
proc `[]`*[A](t: PCountTable[A], key: A): int = proc `[]`*[A](t: CountTableRef[A], key: A): int =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`,
## 0 is returned. One can check with ``hasKey`` whether the key ## 0 is returned. One can check with ``hasKey`` whether the key
## exists. ## exists.
result = t[][key] result = t[][key]
proc mget*[A](t: PCountTable[A], key: A): var int = proc mget*[A](t: CountTableRef[A], key: A): var int =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
result = t[].mget(key) result = t[].mget(key)
proc hasKey*[A](t: PCountTable[A], key: A): bool = proc hasKey*[A](t: CountTableRef[A], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = t[].hasKey(key) result = t[].hasKey(key)
proc `[]=`*[A](t: PCountTable[A], key: A, val: int) = proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
## puts a (key, value)-pair into `t`. `val` has to be positive. ## puts a (key, value)-pair into `t`. `val` has to be positive.
assert val > 0 assert val > 0
t[][key] = val t[][key] = val
proc newCountTable*[A](initialSize=64): PCountTable[A] = proc newCountTable*[A](initialSize=64): CountTableRef[A] =
## creates a new count table that is empty. ## creates a new count table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
@ -821,28 +821,28 @@ proc newCountTable*[A](initialSize=64): PCountTable[A] =
new(result) new(result)
result[] = initCountTable[A](initialSize) result[] = initCountTable[A](initialSize)
proc newCountTable*[A](keys: openArray[A]): PCountTable[A] = proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
## creates a new count table with every key in `keys` having a count of 1. ## creates a new count table with every key in `keys` having a count of 1.
result = newCountTable[A](nextPowerOfTwo(keys.len+10)) result = newCountTable[A](nextPowerOfTwo(keys.len+10))
for key in items(keys): result[key] = 1 for key in items(keys): result[key] = 1
proc `$`*[A](t: PCountTable[A]): string = proc `$`*[A](t: CountTableRef[A]): string =
## The `$` operator for count tables. ## The `$` operator for count tables.
dollarImpl() dollarImpl()
proc inc*[A](t: PCountTable[A], key: A, val = 1) = proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
## increments `t[key]` by `val`. ## increments `t[key]` by `val`.
t[].inc(key, val) t[].inc(key, val)
proc smallest*[A](t: PCountTable[A]): tuple[key: A, val: int] = proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
## returns the largest (key,val)-pair. Efficiency: O(n) ## returns the largest (key,val)-pair. Efficiency: O(n)
t[].smallest t[].smallest
proc largest*[A](t: PCountTable[A]): tuple[key: A, val: int] = proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n)
t[].largest t[].largest
proc sort*[A](t: PCountTable[A]) = proc sort*[A](t: CountTableRef[A]) =
## sorts the count table so that the entry with the highest counter comes ## sorts the count table so that the entry with the highest counter comes
## first. This is destructive! You must not modify `t` afterwards! ## first. This is destructive! You must not modify `t` afterwards!
## You can use the iterators `pairs`, `keys`, and `values` to iterate over ## You can use the iterators `pairs`, `keys`, and `values` to iterate over

View file

@ -94,11 +94,11 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
## Converts the flags into the underlying OS representation. ## Converts the flags into the underlying OS representation.
for f in socketFlags: for f in socketFlags:
case f case f
of TSocketFlags.Peek: of SocketFlag.Peek:
result = result or MSG_PEEK result = result or MSG_PEEK
of TSocketFlags.SafeDisconn: continue of SocketFlag.SafeDisconn: continue
proc createSocket(fd: TSocketHandle, isBuff: bool): PSocket = proc createSocket(fd: TSocketHandle, isBuff: bool): Socket =
assert fd != osInvalidSocket assert fd != osInvalidSocket
new(result) new(result)
result.fd = fd result.fd = fd
@ -106,14 +106,14 @@ proc createSocket(fd: TSocketHandle, isBuff: bool): PSocket =
if isBuff: if isBuff:
result.currPos = 0 result.currPos = 0
proc newSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP, buffered = true): PSocket = protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
## Creates a new socket. ## Creates a new socket.
## ##
## If an error occurs EOS will be raised. ## If an error occurs EOS will be raised.
let fd = newRawSocket(domain, typ, protocol) let fd = newRawSocket(domain, typ, protocol)
if fd == osInvalidSocket: if fd == osInvalidSocket:
osError(osLastError()) raiseOSError(osLastError())
result = createSocket(fd, buffered) result = createSocket(fd, buffered)
when defined(ssl): when defined(ssl):
@ -218,8 +218,8 @@ when defined(ssl):
if SSLSetFd(socket.sslHandle, socket.fd) != 1: if SSLSetFd(socket.sslHandle, socket.fd) != 1:
SSLError() SSLError()
proc socketError*(socket: PSocket, err: int = -1, async = false, proc socketError*(socket: Socket, err: int = -1, async = false,
lastError = (-1).TOSErrorCode) = lastError = (-1).OSErrorCode) =
## Raises an EOS error based on the error code returned by ``SSLGetError`` ## Raises an EOS error based on the error code returned by ``SSLGetError``
## (for SSL sockets) and ``osLastError`` otherwise. ## (for SSL sockets) and ``osLastError`` otherwise.
## ##
@ -254,23 +254,23 @@ proc socketError*(socket: PSocket, err: int = -1, async = false,
when useWinVersion: when useWinVersion:
if lastE.int32 == WSAEWOULDBLOCK: if lastE.int32 == WSAEWOULDBLOCK:
return return
else: osError(lastE) else: raiseOSError(lastE)
else: else:
if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK: if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK:
return return
else: osError(lastE) else: osError(lastE)
else: osError(lastE) else: raiseOSError(lastE)
proc listen*(socket: PSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
## ##
## Raises an EOS error upon failure. ## Raises an EOS error upon failure.
if listen(socket.fd, backlog) < 0'i32: osError(osLastError()) if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError())
proc bindAddr*(socket: PSocket, port = TPort(0), address = "") {. proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## Binds ``address``:``port`` to the socket. ## Binds ``address``:``port`` to the socket.
## ##
## If ``address`` is "" then ADDR_ANY will be bound. ## If ``address`` is "" then ADDR_ANY will be bound.
@ -285,16 +285,16 @@ proc bindAddr*(socket: PSocket, port = TPort(0), address = "") {.
name.sin_addr.s_addr = htonl(INADDR_ANY) name.sin_addr.s_addr = htonl(INADDR_ANY)
if bindAddr(socket.fd, cast[ptr TSockAddr](addr(name)), if bindAddr(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32: sizeof(name).TSocklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
else: else:
var aiList = getAddrInfo(address, port, AF_INET) var aiList = getAddrInfo(address, port, AF_INET)
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList) dealloc(aiList)
osError(osLastError()) raiseOSError(osLastError())
dealloc(aiList) dealloc(aiList)
proc acceptAddr*(server: PSocket, client: var PSocket, address: var string, proc acceptAddr*(server: Socket, client: var Socket, address: var string,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} = flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
## Blocks until a connection is being made from a client. When a connection ## Blocks until a connection is being made from a client. When a connection
## is made sets ``client`` to the client socket and ``address`` to the address ## is made sets ``client`` to the client socket and ``address`` to the address
## of the connecting client. ## of the connecting client.
@ -311,7 +311,7 @@ proc acceptAddr*(server: PSocket, client: var PSocket, address: var string,
## flag is specified then this error will not be raised and instead ## flag is specified then this error will not be raised and instead
## accept will be called again. ## accept will be called again.
assert(client != nil) assert(client != nil)
var sockAddress: Tsockaddr_in var sockAddress: TSockaddr_in
var addrLen = sizeof(sockAddress).TSocklen var addrLen = sizeof(sockAddress).TSocklen
var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)), var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)),
addr(addrLen)) addr(addrLen))
@ -320,7 +320,7 @@ proc acceptAddr*(server: PSocket, client: var PSocket, address: var string,
let err = osLastError() let err = osLastError()
if flags.isDisconnectionError(err): if flags.isDisconnectionError(err):
acceptAddr(server, client, address, flags) acceptAddr(server, client, address, flags)
osError(err) raiseOSError(err)
else: else:
client.fd = sock client.fd = sock
client.isBuffered = server.isBuffered client.isBuffered = server.isBuffered
@ -389,8 +389,8 @@ when false: #defined(ssl):
acceptAddrPlain(AcceptNoClient, AcceptSuccess): acceptAddrPlain(AcceptNoClient, AcceptSuccess):
doHandshake() doHandshake()
proc accept*(server: PSocket, client: var PSocket, proc accept*(server: Socket, client: var Socket,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} = flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
## Equivalent to ``acceptAddr`` but doesn't return the address, only the ## Equivalent to ``acceptAddr`` but doesn't return the address, only the
## socket. ## socket.
## ##
@ -404,7 +404,7 @@ proc accept*(server: PSocket, client: var PSocket,
var addrDummy = "" var addrDummy = ""
acceptAddr(server, client, addrDummy, flags) acceptAddr(server, client, addrDummy, flags)
proc close*(socket: PSocket) = proc close*(socket: Socket) =
## Closes a socket. ## Closes a socket.
socket.fd.close() socket.fd.close()
when defined(ssl): when defined(ssl):
@ -416,7 +416,7 @@ proc close*(socket: PSocket) =
elif res != 1: elif res != 1:
socketError(socket) socketError(socket)
proc toCInt(opt: TSOBool): cint = proc toCInt(opt: SOBool): cint =
case opt case opt
of OptAcceptConn: SO_ACCEPTCONN of OptAcceptConn: SO_ACCEPTCONN
of OptBroadcast: SO_BROADCAST of OptBroadcast: SO_BROADCAST
@ -426,20 +426,20 @@ proc toCInt(opt: TSOBool): cint =
of OptOOBInline: SO_OOBINLINE of OptOOBInline: SO_OOBINLINE
of OptReuseAddr: SO_REUSEADDR of OptReuseAddr: SO_REUSEADDR
proc getSockOpt*(socket: PSocket, opt: TSOBool, level = SOL_SOCKET): bool {. proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## Retrieves option ``opt`` as a boolean value. ## Retrieves option ``opt`` as a boolean value.
var res = getsockoptint(socket.fd, cint(level), toCInt(opt)) var res = getSockOptInt(socket.fd, cint(level), toCInt(opt))
result = res != 0 result = res != 0
proc setSockOpt*(socket: PSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {. proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
tags: [FWriteIO].} = tags: [WriteIOEffect].} =
## Sets option ``opt`` to a boolean value specified by ``value``. ## Sets option ``opt`` to a boolean value specified by ``value``.
var valuei = cint(if value: 1 else: 0) var valuei = cint(if value: 1 else: 0)
setsockoptint(socket.fd, cint(level), toCInt(opt), valuei) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
proc connect*(socket: PSocket, address: string, port = TPort(0), proc connect*(socket: Socket, address: string, port = Port(0),
af: TDomain = AF_INET) {.tags: [FReadIO].} = af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
## host name. If ``address`` is a host name, this function will try each IP ## host name. If ``address`` is a host name, this function will try each IP
## of that host name. ``htons`` is already performed on ``port`` so you must ## of that host name. ``htons`` is already performed on ``port`` so you must
@ -449,7 +449,7 @@ proc connect*(socket: PSocket, address: string, port = TPort(0),
var aiList = getAddrInfo(address, port, af) var aiList = getAddrInfo(address, port, af)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32: if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
@ -459,7 +459,7 @@ proc connect*(socket: PSocket, address: string, port = TPort(0),
it = it.ai_next it = it.ai_next
dealloc(aiList) dealloc(aiList)
if not success: osError(lastError) if not success: raiseOSError(lastError)
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -507,7 +507,7 @@ when defined(ssl):
else: else:
SSLError("Socket is not an SSL socket.") SSLError("Socket is not an SSL socket.")
proc hasDataBuffered*(s: PSocket): bool = proc hasDataBuffered*(s: Socket): bool =
## Determines whether a socket has data buffered. ## Determines whether a socket has data buffered.
result = false result = false
if s.isBuffered: if s.isBuffered:
@ -517,15 +517,15 @@ proc hasDataBuffered*(s: PSocket): bool =
if s.isSSL and not result: if s.isSSL and not result:
result = s.sslHasPeekChar result = s.sslHasPeekChar
proc select(readfd: PSocket, timeout = 500): int = proc select(readfd: Socket, timeout = 500): int =
## Used for socket operation timeouts. ## Used for socket operation timeouts.
if readfd.hasDataBuffered: if readfd.hasDataBuffered:
return 1 return 1
var fds = @[readFd.fd] var fds = @[readfd.fd]
result = select(fds, timeout) result = select(fds, timeout)
proc readIntoBuf(socket: PSocket, flags: int32): int = proc readIntoBuf(socket: Socket, flags: int32): int =
result = 0 result = 0
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -549,7 +549,7 @@ template retRead(flags, readBytes: int) {.dirty.} =
else: else:
return res return res
proc recv*(socket: PSocket, data: pointer, size: int): int {.tags: [FReadIO].} = proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect].} =
## Receives data from a socket. ## Receives data from a socket.
## ##
## **Note**: This is a low-level function, you may be interested in the higher ## **Note**: This is a low-level function, you may be interested in the higher
@ -590,8 +590,8 @@ proc recv*(socket: PSocket, data: pointer, size: int): int {.tags: [FReadIO].} =
else: else:
result = recv(socket.fd, data, size.cint, 0'i32) result = recv(socket.fd, data, size.cint, 0'i32)
proc waitFor(socket: PSocket, waited: var float, timeout, size: int, proc waitFor(socket: Socket, waited: var float, timeout, size: int,
funcName: string): int {.tags: [FTime].} = funcName: string): int {.tags: [TimeEffect].} =
## determines the amount of characters that can be read. Result will never ## determines the amount of characters that can be read. Result will never
## be larger than ``size``. For unbuffered sockets this will be ``1``. ## be larger than ``size``. For unbuffered sockets this will be ``1``.
## For buffered sockets it can be as big as ``BufferSize``. ## For buffered sockets it can be as big as ``BufferSize``.
@ -606,7 +606,7 @@ proc waitFor(socket: PSocket, waited: var float, timeout, size: int,
result = min(result, size) result = min(result, size)
else: else:
if timeout - int(waited * 1000.0) < 1: if timeout - int(waited * 1000.0) < 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.") raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -619,13 +619,13 @@ proc waitFor(socket: PSocket, waited: var float, timeout, size: int,
var startTime = epochTime() var startTime = epochTime()
let selRet = select(socket, timeout - int(waited * 1000.0)) let selRet = select(socket, timeout - int(waited * 1000.0))
if selRet < 0: osError(osLastError()) if selRet < 0: raiseOSError(osLastError())
if selRet != 1: if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.") raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime) waited += (epochTime() - startTime)
proc recv*(socket: PSocket, data: pointer, size: int, timeout: int): int {. proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
tags: [FReadIO, FTime].} = tags: [ReadIOEffect, FTime].} =
## overload with a ``timeout`` parameter in miliseconds. ## overload with a ``timeout`` parameter in miliseconds.
var waited = 0.0 # number of seconds already waited var waited = 0.0 # number of seconds already waited
@ -642,8 +642,8 @@ proc recv*(socket: PSocket, data: pointer, size: int, timeout: int): int {.
result = read result = read
proc recv*(socket: PSocket, data: var string, size: int, timeout = -1, proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
flags = {TSocketFlags.SafeDisconn}): int = flags = {SocketFlag.SafeDisconn}): int =
## Higher-level version of ``recv``. ## Higher-level version of ``recv``.
## ##
## When 0 is returned the socket's connection has been closed. ## When 0 is returned the socket's connection has been closed.
@ -666,7 +666,7 @@ proc recv*(socket: PSocket, data: var string, size: int, timeout = -1,
socket.socketError(result, lastError = lastError) socket.socketError(result, lastError = lastError)
data.setLen(result) data.setLen(result)
proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} = proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
if socket.isBuffered: if socket.isBuffered:
result = 1 result = 1
if socket.bufLen == 0 or socket.currPos > socket.bufLen-1: if socket.bufLen == 0 or socket.currPos > socket.bufLen-1:
@ -686,9 +686,9 @@ proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} =
return return
result = recv(socket.fd, addr(c), 1, MSG_PEEK) result = recv(socket.fd, addr(c), 1, MSG_PEEK)
proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1, proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
flags = {TSocketFlags.SafeDisconn}) {. flags = {SocketFlag.SafeDisconn}) {.
tags: [FReadIO, FTime].} = tags: [ReadIOEffect, FTime].} =
## Reads a line of data from ``socket``. ## Reads a line of data from ``socket``.
## ##
## If a full line is read ``\r\L`` is not ## If a full line is read ``\r\L`` is not
@ -735,9 +735,9 @@ proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1,
return return
add(line.string, c) add(line.string, c)
proc recvFrom*(socket: PSocket, data: var string, length: int, proc recvFrom*(socket: Socket, data: var string, length: int,
address: var string, port: var TPort, flags = 0'i32): int {. address: var string, port: var Port, flags = 0'i32): int {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## Receives data from ``socket``. This function should normally be used with ## Receives data from ``socket``. This function should normally be used with
## connection-less sockets (UDP sockets). ## connection-less sockets (UDP sockets).
## ##
@ -751,7 +751,7 @@ proc recvFrom*(socket: PSocket, data: var string, length: int,
# TODO: Buffered sockets # TODO: Buffered sockets
data.setLen(length) data.setLen(length)
var sockAddress: Tsockaddr_in var sockAddress: TSockaddr_in
var addrLen = sizeof(sockAddress).TSocklen var addrLen = sizeof(sockAddress).TSocklen
result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen))
@ -759,11 +759,11 @@ proc recvFrom*(socket: PSocket, data: var string, length: int,
if result != -1: if result != -1:
data.setLen(result) data.setLen(result)
address = $inet_ntoa(sockAddress.sin_addr) address = $inet_ntoa(sockAddress.sin_addr)
port = ntohs(sockAddress.sin_port).TPort port = ntohs(sockAddress.sin_port).Port
else: else:
osError(osLastError()) raiseOSError(osLastError())
proc skip*(socket: PSocket, size: int, timeout = -1) = proc skip*(socket: Socket, size: int, timeout = -1) =
## Skips ``size`` amount of bytes. ## Skips ``size`` amount of bytes.
## ##
## An optional timeout can be specified in miliseconds, if skipping the ## An optional timeout can be specified in miliseconds, if skipping the
@ -778,8 +778,8 @@ proc skip*(socket: PSocket, size: int, timeout = -1) =
bytesSkipped += recv(socket, dummy, avail) bytesSkipped += recv(socket, dummy, avail)
dealloc(dummy) dealloc(dummy)
proc send*(socket: PSocket, data: pointer, size: int): int {. proc send*(socket: Socket, data: pointer, size: int): int {.
tags: [FWriteIO].} = tags: [WriteIOEffect].} =
## Sends data to a socket. ## Sends data to a socket.
## ##
## **Note**: This is a low-level version of ``send``. You likely should use ## **Note**: This is a low-level version of ``send``. You likely should use
@ -795,8 +795,8 @@ proc send*(socket: PSocket, data: pointer, size: int): int {.
const MSG_NOSIGNAL = 0 const MSG_NOSIGNAL = 0
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
proc send*(socket: PSocket, data: string, proc send*(socket: Socket, data: string,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FWriteIO].} = flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} =
## sends data to a socket. ## sends data to a socket.
let sent = send(socket, cstring(data), data.len) let sent = send(socket, cstring(data), data.len)
if sent < 0: if sent < 0:
@ -805,16 +805,16 @@ proc send*(socket: PSocket, data: string,
socketError(socket, lastError = lastError) socketError(socket, lastError = lastError)
if sent != data.len: if sent != data.len:
raise newException(EOS, "Could not send all data.") raise newException(OSError, "Could not send all data.")
proc trySend*(socket: PSocket, data: string): bool {.tags: [FWriteIO].} = proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
## Safe alternative to ``send``. Does not raise an EOS when an error occurs, ## Safe alternative to ``send``. Does not raise an EOS when an error occurs,
## and instead returns ``false`` on failure. ## and instead returns ``false`` on failure.
result = send(socket, cstring(data), data.len) == data.len result = send(socket, cstring(data), data.len) == data.len
proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer, proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
size: int, af: TDomain = AF_INET, flags = 0'i32): int {. size: int, af: Domain = AF_INET, flags = 0'i32): int {.
tags: [FWriteIO].} = tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``, ## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified ## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname. ## this function will try each IP of that hostname.
@ -839,8 +839,8 @@ proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer,
dealloc(aiList) dealloc(aiList)
proc sendTo*(socket: PSocket, address: string, port: TPort, proc sendTo*(socket: Socket, address: string, port: Port,
data: string): int {.tags: [FWriteIO].} = data: string): int {.tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``, ## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified ## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname. ## this function will try each IP of that hostname.
@ -848,8 +848,8 @@ proc sendTo*(socket: PSocket, address: string, port: TPort,
## This is the high-level version of the above ``sendTo`` function. ## This is the high-level version of the above ``sendTo`` function.
result = socket.sendTo(address, port, cstring(data), data.len) result = socket.sendTo(address, port, cstring(data), data.len)
proc connectAsync(socket: PSocket, name: string, port = TPort(0), proc connectAsync(socket: Socket, name: string, port = Port(0),
af: TDomain = AF_INET) {.tags: [FReadIO].} = af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
## A variant of ``connect`` for non-blocking sockets. ## A variant of ``connect`` for non-blocking sockets.
## ##
## This procedure will immediatelly return, it will not block until a connection ## This procedure will immediatelly return, it will not block until a connection
@ -861,7 +861,7 @@ proc connectAsync(socket: PSocket, name: string, port = TPort(0),
var aiList = getAddrInfo(name, port, af) var aiList = getAddrInfo(name, port, af)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
@ -883,10 +883,10 @@ proc connectAsync(socket: PSocket, name: string, port = TPort(0),
it = it.ai_next it = it.ai_next
dealloc(aiList) dealloc(aiList)
if not success: osError(lastError) if not success: raiseOSError(lastError)
proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int, proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} = af: Domain = AF_INET) {.tags: [ReadIOEffect, FWriteIO].} =
## Connects to server as specified by ``address`` on port specified by ``port``. ## Connects to server as specified by ``address`` on port specified by ``port``.
## ##
## The ``timeout`` paremeter specifies the time in miliseconds to allow for ## The ``timeout`` paremeter specifies the time in miliseconds to allow for
@ -896,7 +896,7 @@ proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int,
socket.connectAsync(address, port, af) socket.connectAsync(address, port, af)
var s = @[socket.fd] var s = @[socket.fd]
if selectWrite(s, timeout) != 1: if selectWrite(s, timeout) != 1:
raise newException(ETimeout, "Call to 'connect' timed out.") raise newException(TimeoutError, "Call to 'connect' timed out.")
else: else:
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -904,10 +904,10 @@ proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int,
doAssert socket.handshake() doAssert socket.handshake()
socket.fd.setBlocking(true) socket.fd.setBlocking(true)
proc isSSL*(socket: PSocket): bool = return socket.isSSL proc isSSL*(socket: Socket): bool = return socket.isSSL
## Determines whether ``socket`` is a SSL socket. ## Determines whether ``socket`` is a SSL socket.
proc getFD*(socket: PSocket): TSocketHandle = return socket.fd proc getFD*(socket: Socket): TSocketHandle = return socket.fd
## Returns the socket's file descriptor ## Returns the socket's file descriptor
type type
@ -1042,23 +1042,23 @@ proc parseIPv4Address(address_str: string): TIpAddress =
currentByte = currentByte * 10 + currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0')) cast[uint16](ord(address_str[i]) - ord('0'))
if currentByte > 255'u16: if currentByte > 255'u16:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Value is out of range") "Invalid IP Address. Value is out of range")
seperatorValid = true seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator elif address_str[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3: if not seperatorValid or byteCount >= 3:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
result.address_v4[byteCount] = cast[uint8](currentByte) result.address_v4[byteCount] = cast[uint8](currentByte)
currentByte = 0 currentByte = 0
byteCount.inc byteCount.inc
seperatorValid = false seperatorValid = false
else: else:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character") "Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid: if byteCount != 3 or not seperatorValid:
raise newException(EInvalidValue, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte) result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): TIpAddress = proc parseIPv6Address(address_str: string): TIpAddress =
@ -1066,7 +1066,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6 result.family = IpAddressFamily.IPv6
if address_str.len < 2: if address_str.len < 2:
raise newException(EInvalidValue, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
var var
groupCount = 0 groupCount = 0
@ -1081,17 +1081,17 @@ proc parseIPv6Address(address_str: string): TIpAddress =
for i,c in address_str: for i,c in address_str:
if c == ':': if c == ':':
if not seperatorValid: if not seperatorValid:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address contains an invalid seperator") "Invalid IP Address. Address contains an invalid seperator")
if lastWasColon: if lastWasColon:
if dualColonGroup != -1: if dualColonGroup != -1:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address contains more than one \"::\" seperator") "Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount dualColonGroup = groupCount
seperatorValid = false seperatorValid = false
elif i != 0 and i != high(address_str): elif i != 0 and i != high(address_str):
if groupCount >= 8: if groupCount >= 8:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
@ -1100,17 +1100,17 @@ proc parseIPv6Address(address_str: string): TIpAddress =
if dualColonGroup != -1: seperatorValid = false if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with :: elif i == 0: # only valid if address starts with ::
if address_str[1] != ':': if address_str[1] != ':':
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"") "Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with :: else: # i == high(address_str) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':': if address_str[high(address_str)-1] != ':':
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"") "Invalid IP Address. Address may not end with \":\"")
lastWasColon = true lastWasColon = true
currentGroupStart = i + 1 currentGroupStart = i + 1
elif c == '.': # Switch to parse IPv4 mode elif c == '.': # Switch to parse IPv4 mode
if i < 3 or not seperatorValid or groupCount >= 7: if i < 3 or not seperatorValid or groupCount >= 7:
raise newException(EInvalidValue, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
v4StartPos = currentGroupStart v4StartPos = currentGroupStart
currentShort = 0 currentShort = 0
seperatorValid = false seperatorValid = false
@ -1123,19 +1123,19 @@ proc parseIPv6Address(address_str: string): TIpAddress =
else: # Upper case hex else: # Upper case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
if currentShort > 65535'u32: if currentShort > 65535'u32:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Value is out of range") "Invalid IP Address. Value is out of range")
lastWasColon = false lastWasColon = false
seperatorValid = true seperatorValid = true
else: else:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character") "Invalid IP Address. Address contains an invalid character")
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
if seperatorValid: # Copy remaining data if seperatorValid: # Copy remaining data
if groupCount >= 8: if groupCount >= 8:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
@ -1145,32 +1145,32 @@ proc parseIPv6Address(address_str: string): TIpAddress =
if c in strutils.Digits: # Character is a number if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32: if currentShort > 255'u32:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Value is out of range") "Invalid IP Address. Value is out of range")
seperatorValid = true seperatorValid = true
elif c == '.': # IPv4 address separator elif c == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3: if not seperatorValid or byteCount >= 3:
raise newException(EInvalidValue, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
currentShort = 0 currentShort = 0
byteCount.inc() byteCount.inc()
seperatorValid = false seperatorValid = false
else: # Invalid character else: # Invalid character
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character") "Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid: if byteCount != 3 or not seperatorValid:
raise newException(EInvalidValue, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
groupCount += 2 groupCount += 2
# Shift and fill zeros in case of :: # Shift and fill zeros in case of ::
if groupCount > 8: if groupCount > 8:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
elif groupCount < 8: # must fill elif groupCount < 8: # must fill
if dualColonGroup == -1: if dualColonGroup == -1:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too few groups") "Invalid IP Address. The address consists of too few groups")
var toFill = 8 - groupCount # The number of groups to fill var toFill = 8 - groupCount # The number of groups to fill
var toShift = groupCount - dualColonGroup # Nr of known groups after :: var toShift = groupCount - dualColonGroup # Nr of known groups after ::
@ -1179,14 +1179,14 @@ proc parseIPv6Address(address_str: string): TIpAddress =
for i in 0..2*toFill-1: # fill with 0s for i in 0..2*toFill-1: # fill with 0s
result.address_v6[dualColonGroup*2+i] = 0 result.address_v6[dualColonGroup*2+i] = 0
elif dualColonGroup != -1: elif dualColonGroup != -1:
raise newException(EInvalidValue, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): TIpAddress = proc parseIpAddress*(address_str: string): TIpAddress =
## Parses an IP address ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if address_str == nil:
raise newException(EInvalidValue, "IP Address string is nil") raise newException(ValueError, "IP Address string is nil")
if address_str.contains(':'): if address_str.contains(':'):
return parseIPv6Address(address_str) return parseIPv6Address(address_str)
else: else:

View file

@ -14,7 +14,7 @@
import unsigned, os import unsigned, os
when hostos == "solaris": when hostOS == "solaris":
{.passl: "-lsocket -lnsl".} {.passl: "-lsocket -lnsl".}
const useWinVersion = defined(Windows) or defined(nimdoc) const useWinVersion = defined(Windows) or defined(nimdoc)
@ -156,7 +156,7 @@ proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
proc close*(socket: SocketHandle) = proc close*(socket: SocketHandle) =
## closes a socket. ## closes a socket.
when useWinVersion: when useWinVersion:
discard winlean.closeSocket(socket) discard winlean.closesocket(socket)
else: else:
discard posix.close(socket) discard posix.close(socket)
# TODO: These values should not be discarded. An EOS should be raised. # TODO: These values should not be discarded. An EOS should be raised.
@ -165,7 +165,7 @@ proc close*(socket: SocketHandle) =
proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
result = bindSocket(socket, name, namelen) result = bindSocket(socket, name, namelen)
proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} = proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
@ -223,7 +223,7 @@ proc htons*(x: int16): int16 =
## order, this is a no-op; otherwise, it performs a 2-byte swap operation. ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = rawsockets.ntohs(x) result = rawsockets.ntohs(x)
proc getServByName*(name, proto: string): Servent {.tags: [FReadIO].} = proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the service name specified by ``name`` matches the s_name member ## which the service name specified by ``name`` matches the s_name member
## and the protocol name specified by ``proto`` matches the s_proto member. ## and the protocol name specified by ``proto`` matches the s_proto member.
@ -233,13 +233,13 @@ proc getServByName*(name, proto: string): Servent {.tags: [FReadIO].} =
var s = winlean.getservbyname(name, proto) var s = winlean.getservbyname(name, proto)
else: else:
var s = posix.getservbyname(name, proto) var s = posix.getservbyname(name, proto)
if s == nil: raise newException(EOS, "Service not found.") if s == nil: raise newException(OSError, "Service not found.")
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port) result.port = Port(s.s_port)
result.proto = $s.s_proto result.proto = $s.s_proto
proc getServByPort*(port: Port, proto: string): Servent {.tags: [FReadIO].} = proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the port specified by ``port`` matches the s_port member and the ## which the port specified by ``port`` matches the s_port member and the
## protocol name specified by ``proto`` matches the s_proto member. ## protocol name specified by ``proto`` matches the s_proto member.
@ -249,13 +249,13 @@ proc getServByPort*(port: Port, proto: string): Servent {.tags: [FReadIO].} =
var s = winlean.getservbyport(ze(int16(port)).cint, proto) var s = winlean.getservbyport(ze(int16(port)).cint, proto)
else: else:
var s = posix.getservbyport(ze(int16(port)).cint, proto) var s = posix.getservbyport(ze(int16(port)).cint, proto)
if s == nil: raise newException(EOS, "Service not found.") if s == nil: raise newException(OSError, "Service not found.")
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port) result.port = Port(s.s_port)
result.proto = $s.s_proto result.proto = $s.s_proto
proc getHostByAddr*(ip: string): Hostent {.tags: [FReadIO].} = proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the hostname of an IP Address. ## This function will lookup the hostname of an IP Address.
var myaddr: InAddr var myaddr: InAddr
myaddr.s_addr = inet_addr(ip) myaddr.s_addr = inet_addr(ip)
@ -284,7 +284,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [FReadIO].} =
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
proc getHostByName*(name: string): Hostent {.tags: [FReadIO].} = proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the IP address of a hostname. ## This function will lookup the IP address of a hostname.
when useWinVersion: when useWinVersion:
var s = winlean.gethostbyname(name) var s = winlean.gethostbyname(name)
@ -314,28 +314,28 @@ proc getSockName*(socket: SocketHandle): Port =
name.sin_family = posix.AF_INET name.sin_family = posix.AF_INET
#name.sin_port = htons(cint16(port)) #name.sin_port = htons(cint16(port))
#name.sin_addr.s_addr = htonl(INADDR_ANY) #name.sin_addr.s_addr = htonl(INADDR_ANY)
var namelen = sizeof(name).Socklen var namelen = sizeof(name).SockLen
if getsockname(socket, cast[ptr SockAddr](addr(name)), if getsockname(socket, cast[ptr SockAddr](addr(name)),
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = Port(rawsockets.ntohs(name.sin_port)) result = Port(rawsockets.ntohs(name.sin_port))
proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
tags: [FReadIO].} = tags: [ReadIOEffect].} =
## getsockopt for integer options. ## getsockopt for integer options.
var res: cint var res: cint
var size = sizeof(res).Socklen var size = sizeof(res).SockLen
if getsockopt(socket, cint(level), cint(optname), if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = int(res) result = int(res)
proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {. proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
tags: [FWriteIO].} = tags: [WriteIOEffect].} =
## setsockopt for integer options. ## setsockopt for integer options.
var value = cint(optval) var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value), if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).Socklen) < 0'i32: sizeof(value).SockLen) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc setBlocking*(s: SocketHandle, blocking: bool) = proc setBlocking*(s: SocketHandle, blocking: bool) =
@ -361,13 +361,13 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) = proc createFdSet(fd: var FD_SET, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)
for i in items(s): for i in items(s):
m = max(m, int(i)) m = max(m, int(i))
FD_SET(i, fd) FD_SET(i, fd)
proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) = proc pruneSocketSet(s: var seq[SocketHandle], fd: var FD_SET) =
var i = 0 var i = 0
var L = s.len var L = s.len
while i < L: while i < L:
@ -386,7 +386,7 @@ proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
## ##
## A socket is removed from the specific ``seq`` when it has data waiting to ## A socket is removed from the specific ``seq`` when it has data waiting to
## be read/written to or has errors (``exceptfds``). ## be read/written to or has errors (``exceptfds``).
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) var tv {.noInit.}: TTimeval = timeValFromMilliseconds(timeout)
var rd: TFdSet var rd: TFdSet
var m = 0 var m = 0
@ -400,7 +400,7 @@ proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
pruneSocketSet(readfds, (rd)) pruneSocketSet(readfds, (rd))
proc selectWrite*(writefds: var seq[SocketHandle], proc selectWrite*(writefds: var seq[SocketHandle],
timeout = 500): int {.tags: [FReadIO].} = timeout = 500): int {.tags: [ReadIOEffect].} =
## When a socket in ``writefds`` is ready to be written to then a non-zero ## When a socket in ``writefds`` is ready to be written to then a non-zero
## value will be returned specifying the count of the sockets which can be ## value will be returned specifying the count of the sockets which can be
## written to. The sockets which can be written to will also be removed ## written to. The sockets which can be written to will also be removed

View file

@ -425,7 +425,7 @@ type
TFdSet* {.pure, final.} = object TFdSet* {.pure, final.} = object
fd_count*: cint # unsigned fd_count*: cint # unsigned
fd_array*: array[0..FD_SETSIZE-1, TSocketHandle] fd_array*: array[0..FD_SETSIZE-1, SocketHandle]
TTimeval* {.pure, final.} = object TTimeval* {.pure, final.} = object
tv_sec*, tv_usec*: int32 tv_sec*, tv_usec*: int32
@ -444,7 +444,7 @@ type
var var
SOMAXCONN* {.importc, header: "Winsock2.h".}: cint SOMAXCONN* {.importc, header: "Winsock2.h".}: cint
INVALID_SOCKET* {.importc, header: "Winsock2.h".}: TSocketHandle INVALID_SOCKET* {.importc, header: "Winsock2.h".}: SocketHandle
SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint
SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording
SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen() SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen()
@ -460,7 +460,7 @@ var
SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse
SO_ERROR* {.importc, header: "Winsock2.h".}: cint SO_ERROR* {.importc, header: "Winsock2.h".}: cint
proc `==`*(x, y: TSocketHandle): bool {.borrow.} proc `==`*(x, y: SocketHandle): bool {.borrow.}
proc getservbyname*(name, proto: cstring): ptr TServent {. proc getservbyname*(name, proto: cstring): ptr TServent {.
stdcall, importc: "getservbyname", dynlib: ws2dll.} stdcall, importc: "getservbyname", dynlib: ws2dll.}
@ -474,45 +474,45 @@ proc gethostbyaddr*(ip: ptr TInAddr, len: cuint, theType: cint): ptr Thostent {.
proc gethostbyname*(name: cstring): ptr Thostent {. proc gethostbyname*(name: cstring): ptr Thostent {.
stdcall, importc: "gethostbyname", dynlib: ws2dll.} stdcall, importc: "gethostbyname", dynlib: ws2dll.}
proc socket*(af, typ, protocol: cint): TSocketHandle {. proc socket*(af, typ, protocol: cint): SocketHandle {.
stdcall, importc: "socket", dynlib: ws2dll.} stdcall, importc: "socket", dynlib: ws2dll.}
proc closesocket*(s: TSocketHandle): cint {. proc closesocket*(s: SocketHandle): cint {.
stdcall, importc: "closesocket", dynlib: ws2dll.} stdcall, importc: "closesocket", dynlib: ws2dll.}
proc accept*(s: TSocketHandle, a: ptr TSockAddr, addrlen: ptr TSockLen): TSocketHandle {. proc accept*(s: SocketHandle, a: ptr TSockAddr, addrlen: ptr TSockLen): SocketHandle {.
stdcall, importc: "accept", dynlib: ws2dll.} stdcall, importc: "accept", dynlib: ws2dll.}
proc bindSocket*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {. proc bindSocket*(s: SocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {.
stdcall, importc: "bind", dynlib: ws2dll.} stdcall, importc: "bind", dynlib: ws2dll.}
proc connect*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {. proc connect*(s: SocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {.
stdcall, importc: "connect", dynlib: ws2dll.} stdcall, importc: "connect", dynlib: ws2dll.}
proc getsockname*(s: TSocketHandle, name: ptr TSockAddr, proc getsockname*(s: SocketHandle, name: ptr TSockAddr,
namelen: ptr TSockLen): cint {. namelen: ptr TSockLen): cint {.
stdcall, importc: "getsockname", dynlib: ws2dll.} stdcall, importc: "getsockname", dynlib: ws2dll.}
proc getsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer, proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer,
optlen: ptr TSockLen): cint {. optlen: ptr TSockLen): cint {.
stdcall, importc: "getsockopt", dynlib: ws2dll.} stdcall, importc: "getsockopt", dynlib: ws2dll.}
proc setsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer, proc setsockopt*(s: SocketHandle, level, optname: cint, optval: pointer,
optlen: TSockLen): cint {. optlen: TSockLen): cint {.
stdcall, importc: "setsockopt", dynlib: ws2dll.} stdcall, importc: "setsockopt", dynlib: ws2dll.}
proc listen*(s: TSocketHandle, backlog: cint): cint {. proc listen*(s: SocketHandle, backlog: cint): cint {.
stdcall, importc: "listen", dynlib: ws2dll.} stdcall, importc: "listen", dynlib: ws2dll.}
proc recv*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {. proc recv*(s: SocketHandle, buf: pointer, len, flags: cint): cint {.
stdcall, importc: "recv", dynlib: ws2dll.} stdcall, importc: "recv", dynlib: ws2dll.}
proc recvfrom*(s: TSocketHandle, buf: cstring, len, flags: cint, proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint,
fromm: ptr TSockAddr, fromlen: ptr TSockLen): cint {. fromm: ptr TSockAddr, fromlen: ptr TSockLen): cint {.
stdcall, importc: "recvfrom", dynlib: ws2dll.} stdcall, importc: "recvfrom", dynlib: ws2dll.}
proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet, proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet,
timeout: ptr TTimeval): cint {. timeout: ptr TTimeval): cint {.
stdcall, importc: "select", dynlib: ws2dll.} stdcall, importc: "select", dynlib: ws2dll.}
proc send*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {. proc send*(s: SocketHandle, buf: pointer, len, flags: cint): cint {.
stdcall, importc: "send", dynlib: ws2dll.} stdcall, importc: "send", dynlib: ws2dll.}
proc sendto*(s: TSocketHandle, buf: pointer, len, flags: cint, proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint,
to: ptr TSockAddr, tolen: TSockLen): cint {. to: ptr TSockAddr, tolen: TSockLen): cint {.
stdcall, importc: "sendto", dynlib: ws2dll.} stdcall, importc: "sendto", dynlib: ws2dll.}
proc shutdown*(s: TSocketHandle, how: cint): cint {. proc shutdown*(s: SocketHandle, how: cint): cint {.
stdcall, importc: "shutdown", dynlib: ws2dll.} stdcall, importc: "shutdown", dynlib: ws2dll.}
proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen, proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen,
@ -523,13 +523,13 @@ proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen,
proc inet_addr*(cp: cstring): int32 {. proc inet_addr*(cp: cstring): int32 {.
stdcall, importc: "inet_addr", dynlib: ws2dll.} stdcall, importc: "inet_addr", dynlib: ws2dll.}
proc WSAFDIsSet(s: TSocketHandle, FDSet: var TFdSet): bool {. proc WSAFDIsSet(s: SocketHandle, FDSet: var TFdSet): bool {.
stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.} stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.}
proc FD_ISSET*(Socket: TSocketHandle, FDSet: var TFdSet): cint = proc FD_ISSET*(Socket: SocketHandle, FDSet: var TFdSet): cint =
result = if WSAFDIsSet(Socket, FDSet): 1'i32 else: 0'i32 result = if WSAFDIsSet(Socket, FDSet): 1'i32 else: 0'i32
proc FD_SET*(Socket: TSocketHandle, FDSet: var TFdSet) = proc FD_SET*(Socket: SocketHandle, FDSet: var TFdSet) =
if FDSet.fd_count < FD_SETSIZE: if FDSet.fd_count < FD_SETSIZE:
FDSet.fd_array[int(FDSet.fd_count)] = Socket FDSet.fd_array[int(FDSet.fd_count)] = Socket
inc(FDSet.fd_count) inc(FDSet.fd_count)
@ -699,7 +699,7 @@ var
WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD, cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED, lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED,
lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
@ -710,12 +710,12 @@ type
len*: ULONG len*: ULONG
buf*: cstring buf*: cstring
proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, proc WSARecv*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesReceived, flags: PDWORD, lpOverlapped: POVERLAPPED, bytesReceived, flags: PDWORD, lpOverlapped: POVERLAPPED,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".} stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWORD, flags: DWORD, lpOverlapped: POVERLAPPED, bytesSent: PDWORD, flags: DWORD, lpOverlapped: POVERLAPPED,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}