big rename

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

View File

@@ -16,7 +16,7 @@ type
{.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: TSortOrder): int {.inline.} =
proc `*`*(x: int, order: SortOrder): int {.inline.} =
## flips `x` if ``order == Descending``;
## if ``order == Ascending`` then `x` is returned.
## `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 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) =
when false:
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],
cmp: proc (x, y: T): int {.closure.},
order = TSortOrder.Ascending) =
order = SortOrder.Ascending) =
## Default Nimrod sort. The sorting is guaranteed to be stable and
## the worst case is guaranteed to be O(n log n).
## The current implementation uses an iterative

View File

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

View File

@@ -37,7 +37,7 @@ type
{.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>`_.
##
## 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!
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`.
##
## 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
result = s.counter
proc card*[A](s: TSet[A]): int =
proc card*[A](s: HashSet[A]): int =
## Alias for `len() <#len,TSet[A]>`_.
##
## Card stands for the `cardinality
## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
result = s.counter
iterator items*[A](s: TSet[A]): A =
iterator items*[A](s: HashSet[A]): A =
## Iterates over keys in the set `s`.
##
## 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].slot = seFilled
proc rawGet[A](s: TSet[A], key: A): int =
proc rawGet[A](s: HashSet[A], key: A): int =
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
## value as 'key' or raises the ``EInvalidKey`` exception. This is useful
## 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
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`.
##
## Example:
@@ -149,10 +149,10 @@ proc contains*[A](s: TSet[A], key: A): bool =
var index = rawGet(s, key)
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()
proc enlarge[A](s: var TSet[A]) =
proc enlarge[A](s: var HashSet[A]) =
var n: KeyValuePairSeq[A]
newSeq(n, len(s.data) * growthFactor)
for i in countup(0, high(s.data)):
@@ -175,7 +175,7 @@ template containsOrInclImpl() {.dirty.} =
rawInsert(s, s.data, key)
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`.
##
## 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."
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`.
##
## 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."
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`.
##
## 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
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`.
##
## 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."
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`.
##
## 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."
containsOrInclImpl()
proc init*[A](s: var TSet[A], initialSize=64) =
proc init*[A](s: var HashSet[A], initialSize=64) =
## Initializes a hash set.
##
## 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
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
## sets.
##
@@ -285,7 +285,7 @@ proc initSet*[A](initialSize=64): TSet[A] =
## a.incl(2)
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`.
##
## Example:
@@ -304,7 +304,7 @@ template dollarImpl(): stmt {.dirty.} =
result.add($key)
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.
##
## 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."
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`.
##
## 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
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`.
##
## 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:
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`.
##
## 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):
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`.
##
## 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:
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>`_.
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>`_.
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>`_.
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>`_.
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.
##
## Example:
@@ -428,7 +428,7 @@ proc disjoint*[A](s1, s2: TSet[A]): bool =
if item in s2: return false
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`.
##
## 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)
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`.
##
## 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
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.
##
## Example:
@@ -477,7 +477,7 @@ proc `==`*[A](s, t: TSet[A]): bool =
## assert a == b
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`.
##
## 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
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]>`_.
##
## Card stands for the `cardinality
@@ -548,7 +548,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if s.data[h].slot == seFilled: yieldStmt
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.
##
## 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:
yield s.data[h].key
proc rawGet[A](s: TOrderedSet[A], key: A): int =
proc rawGet[A](s: OrderedSet[A], key: A): int =
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`.
##
## Example:
@@ -587,7 +587,7 @@ proc contains*[A](s: TOrderedSet[A], key: A): bool =
var index = rawGet(s, key)
result = index >= 0
proc rawInsert[A](s: var TOrderedSet[A],
proc rawInsert[A](s: var OrderedSet[A],
data: var OrderedKeyValuePairSeq[A], key: A) =
rawInsertImpl()
data[h].next = -1
@@ -595,7 +595,7 @@ proc rawInsert[A](s: var TOrderedSet[A],
if s.last >= 0: data[s.last].next = h
s.last = h
proc enlarge[A](s: var TOrderedSet[A]) =
proc enlarge[A](s: var OrderedSet[A]) =
var n: OrderedKeyValuePairSeq[A]
newSeq(n, len(s.data) * growthFactor)
var h = s.first
@@ -608,7 +608,7 @@ proc enlarge[A](s: var TOrderedSet[A]) =
h = nxt
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`.
##
## 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."
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`.
##
## 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."
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`.
##
## 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."
containsOrInclImpl()
proc init*[A](s: var TOrderedSet[A], initialSize=64) =
proc init*[A](s: var OrderedSet[A], initialSize=64) =
## Initializes an ordered hash set.
##
## 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
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
## ordered hash sets.
##
@@ -688,7 +688,7 @@ proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
## a.incl(2)
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`.
##
## Example:
@@ -700,7 +700,7 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
result = initOrderedSet[A](nextPowerOfTwo(keys.len+10))
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.
##
## 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."
dollarImpl()
proc `==`*[A](s, t: TOrderedSet[A]): bool =
proc `==`*[A](s, t: OrderedSet[A]): bool =
## Equality for ordered sets.
if s.counter != t.counter: return false
var h = s.first
@@ -738,7 +738,7 @@ proc testModule() =
## Internal micro test to validate docstrings and such.
block isValidTest:
var options: HashSet[string]
proc savePreferences(options: TSet[string]) =
proc savePreferences(options: HashSet[string]) =
assert options.isValid, "Pass an initialized set!"
options = initSet[string]()
options.savePreferences
@@ -839,7 +839,7 @@ proc testModule() =
block isValidTest:
var cards: OrderedSet[string]
proc saveTarotCards(cards: TOrderedSet[string]) =
proc saveTarotCards(cards: OrderedSet[string]) =
assert cards.isValid, "Pass an initialized set!"
cards = initOrderedSet[string]()
cards.saveTarotCards
@@ -891,7 +891,7 @@ proc testModule() =
a.incl(2)
assert a.len == 1
var b: TSet[int]
var b: HashSet[int]
b.init(4)
b.incl(2)
b.init

View File

@@ -74,32 +74,32 @@ type
when not defined(nimhygiene):
{.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`.
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`.
for h in 0..high(t.data):
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
## can be modified.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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.
for h in 0..high(t.data):
if t.data[h].slot == seFilled: yield t.data[h].val
@@ -130,10 +130,10 @@ template rawInsertImpl() {.dirty.} =
data[h].val = val
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()
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`,
## default empty value for the type `B` is returned
## 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)
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
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`.
var h: THash = hash(key) and high(t.data)
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
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`.
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) =
rawInsertImpl()
proc enlarge[A, B](t: var TTable[A, B]) =
proc enlarge[A, B](t: var Table[A, B]) =
var n: KeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
@@ -196,22 +196,22 @@ when false:
inc(t.counter)
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`.
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.
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`.
let index = rawGet(t, key)
if index >= 0:
t.data[index].slot = seDeleted
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.
##
## `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)
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`.
result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
for key, val in items(pairs): result[key] = val
@@ -239,7 +239,7 @@ template dollarImpl(): stmt {.dirty.} =
result.add($val)
result.add("}")
proc `$`*[A, B](t: TTable[A, B]): string =
proc `$`*[A, B](t: Table[A, B]): string =
## The `$` operator for hash tables.
dollarImpl()
@@ -253,93 +253,93 @@ template equalsImpl() =
if t[key] != val: return false
return true
proc `==`*[A, B](s, t: TTable[A, B]): bool =
proc `==`*[A, B](s, t: Table[A, B]): bool =
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.
# TODO: As soon as supported, change collection: A to collection: A[B]
result = initTable[C, B]()
for item in collection:
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`.
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`.
for h in 0..high(t.data):
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
## can be modified.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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.
for h in 0..high(t.data):
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`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
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`.
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`.
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.
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`.
t[].del(key)
proc newTable*[A, B](initialSize=64): PTable[A, B] =
proc newTable*[A, B](initialSize=64): TableRef[A, B] =
new(result)
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`.
new(result)
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.
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)
elif isNil(t): result = false
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.
# TODO: As soon as supported, change collection: A to collection: A[B]
result = newTable[C, B]()
@@ -360,7 +360,7 @@ type
{.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`.
result = t.counter
@@ -371,38 +371,38 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if t.data[h].slot == seFilled: yieldStmt
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
## order.
forAllOrderedPairs:
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
## order. The values can be modified.
forAllOrderedPairs:
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.
forAllOrderedPairs:
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.
forAllOrderedPairs:
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
## can be modified.
forAllOrderedPairs:
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()
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`,
## default empty value for the type `B` is returned
## 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)
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
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`.
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],
key: A, val: B) =
rawInsertImpl()
@@ -430,7 +430,7 @@ proc rawInsert[A, B](t: var TOrderedTable[A, B],
if t.last >= 0: data[t.last].next = 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]
newSeq(n, len(t.data) * growthFactor)
var h = t.first
@@ -443,15 +443,15 @@ proc enlarge[A, B](t: var TOrderedTable[A, B]) =
h = nxt
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`.
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.
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.
##
## `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)
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`.
result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
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.
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) =
## sorts `t` according to `cmp`. This modifies the internal list
## 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.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`.
result = t.counter
@@ -530,59 +530,59 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
if t.data[h].slot == seFilled: yieldStmt
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
## order.
forAllOrderedPairs:
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
## order. The values can be modified.
forAllOrderedPairs:
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.
forAllOrderedPairs:
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.
forAllOrderedPairs:
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
## can be modified.
forAllOrderedPairs:
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`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
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`.
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`.
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.
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.
##
## `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]()
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`.
result = newOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
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.
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) =
## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this
@@ -620,81 +620,81 @@ type
{.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`.
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`.
for h in 0..high(t.data):
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
## be modified.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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.
for h in 0..high(t.data):
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
while t.data[h].val != 0:
if t.data[h].key == key: return h
h = nextTry(h, high(t.data))
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`,
## 0 is returned. One can check with ``hasKey`` whether the key
## exists.
var index = rawGet(t, key)
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
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`.
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) =
var h: THash = hash(key) and high(data)
while data[h].val != 0: h = nextTry(h, high(data))
data[h].key = key
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]]
newSeq(n, len(t.data) * growthFactor)
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)
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.
assert val > 0
putImpl()
proc initCountTable*[A](initialSize=64): TCountTable[A] =
proc initCountTable*[A](initialSize=64): CountTable[A] =
## creates a new count table that is empty.
##
## `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
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.
result = initCountTable[A](nextPowerOfTwo(keys.len+10))
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.
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`.
var index = rawGet(t, key)
if index >= 0:
@@ -723,7 +723,7 @@ proc inc*[A](t: var TCountTable[A], key: A, val = 1) =
rawInsert(t, t.data, key, val)
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)
assert t.len > 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.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)
assert t.len > 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.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
## first. This is destructive! You must not modify `t` afterwards!
## 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 h == 1: break
proc len*[A](t: PCountTable[A]): int =
proc len*[A](t: CountTableRef[A]): int =
## returns the number of keys in `t`.
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`.
for h in 0..high(t.data):
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
## be modified.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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`.
for h in 0..high(t.data):
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.
for h in 0..high(t.data):
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`,
## 0 is returned. One can check with ``hasKey`` whether the key
## exists.
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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
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`.
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.
assert val > 0
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.
##
## `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)
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.
result = newCountTable[A](nextPowerOfTwo(keys.len+10))
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.
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`.
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)
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)
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
## first. This is destructive! You must not modify `t` afterwards!
## 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.
for f in socketFlags:
case f
of TSocketFlags.Peek:
of SocketFlag.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
new(result)
result.fd = fd
@@ -106,14 +106,14 @@ proc createSocket(fd: TSocketHandle, isBuff: bool): PSocket =
if isBuff:
result.currPos = 0
proc newSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP, buffered = true): PSocket =
proc newSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
## Creates a new socket.
##
## If an error occurs EOS will be raised.
let fd = newRawSocket(domain, typ, protocol)
if fd == osInvalidSocket:
osError(osLastError())
raiseOSError(osLastError())
result = createSocket(fd, buffered)
when defined(ssl):
@@ -218,8 +218,8 @@ when defined(ssl):
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
SSLError()
proc socketError*(socket: PSocket, err: int = -1, async = false,
lastError = (-1).TOSErrorCode) =
proc socketError*(socket: Socket, err: int = -1, async = false,
lastError = (-1).OSErrorCode) =
## Raises an EOS error based on the error code returned by ``SSLGetError``
## (for SSL sockets) and ``osLastError`` otherwise.
##
@@ -254,23 +254,23 @@ proc socketError*(socket: PSocket, err: int = -1, async = false,
when useWinVersion:
if lastE.int32 == WSAEWOULDBLOCK:
return
else: osError(lastE)
else: raiseOSError(lastE)
else:
if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK:
return
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.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
##
## 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 = "") {.
tags: [FReadIO].} =
proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
tags: [ReadIOEffect].} =
## Binds ``address``:``port`` to the socket.
##
## 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)
if bindAddr(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
else:
var aiList = getAddrInfo(address, port, AF_INET)
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList)
osError(osLastError())
raiseOSError(osLastError())
dealloc(aiList)
proc acceptAddr*(server: PSocket, client: var PSocket, address: var string,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} =
proc acceptAddr*(server: Socket, client: var Socket, address: var string,
flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
## 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
## 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
## accept will be called again.
assert(client != nil)
var sockAddress: Tsockaddr_in
var sockAddress: TSockaddr_in
var addrLen = sizeof(sockAddress).TSocklen
var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)),
addr(addrLen))
@@ -320,7 +320,7 @@ proc acceptAddr*(server: PSocket, client: var PSocket, address: var string,
let err = osLastError()
if flags.isDisconnectionError(err):
acceptAddr(server, client, address, flags)
osError(err)
raiseOSError(err)
else:
client.fd = sock
client.isBuffered = server.isBuffered
@@ -389,8 +389,8 @@ when false: #defined(ssl):
acceptAddrPlain(AcceptNoClient, AcceptSuccess):
doHandshake()
proc accept*(server: PSocket, client: var PSocket,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FReadIO].} =
proc accept*(server: Socket, client: var Socket,
flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
## socket.
##
@@ -404,7 +404,7 @@ proc accept*(server: PSocket, client: var PSocket,
var addrDummy = ""
acceptAddr(server, client, addrDummy, flags)
proc close*(socket: PSocket) =
proc close*(socket: Socket) =
## Closes a socket.
socket.fd.close()
when defined(ssl):
@@ -416,7 +416,7 @@ proc close*(socket: PSocket) =
elif res != 1:
socketError(socket)
proc toCInt(opt: TSOBool): cint =
proc toCInt(opt: SOBool): cint =
case opt
of OptAcceptConn: SO_ACCEPTCONN
of OptBroadcast: SO_BROADCAST
@@ -426,20 +426,20 @@ proc toCInt(opt: TSOBool): cint =
of OptOOBInline: SO_OOBINLINE
of OptReuseAddr: SO_REUSEADDR
proc getSockOpt*(socket: PSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
tags: [FReadIO].} =
proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
tags: [ReadIOEffect].} =
## 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
proc setSockOpt*(socket: PSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
tags: [FWriteIO].} =
proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
tags: [WriteIOEffect].} =
## Sets option ``opt`` to a boolean value specified by ``value``.
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),
af: TDomain = AF_INET) {.tags: [FReadIO].} =
proc connect*(socket: Socket, address: string, port = Port(0),
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
## 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
## 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)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var lastError: OSErrorCode
var it = aiList
while it != nil:
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
dealloc(aiList)
if not success: osError(lastError)
if not success: raiseOSError(lastError)
when defined(ssl):
if socket.isSSL:
@@ -507,7 +507,7 @@ when defined(ssl):
else:
SSLError("Socket is not an SSL socket.")
proc hasDataBuffered*(s: PSocket): bool =
proc hasDataBuffered*(s: Socket): bool =
## Determines whether a socket has data buffered.
result = false
if s.isBuffered:
@@ -517,15 +517,15 @@ proc hasDataBuffered*(s: PSocket): bool =
if s.isSSL and not result:
result = s.sslHasPeekChar
proc select(readfd: PSocket, timeout = 500): int =
proc select(readfd: Socket, timeout = 500): int =
## Used for socket operation timeouts.
if readfd.hasDataBuffered:
return 1
var fds = @[readFd.fd]
var fds = @[readfd.fd]
result = select(fds, timeout)
proc readIntoBuf(socket: PSocket, flags: int32): int =
proc readIntoBuf(socket: Socket, flags: int32): int =
result = 0
when defined(ssl):
if socket.isSSL:
@@ -549,7 +549,7 @@ template retRead(flags, readBytes: int) {.dirty.} =
else:
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.
##
## **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:
result = recv(socket.fd, data, size.cint, 0'i32)
proc waitFor(socket: PSocket, waited: var float, timeout, size: int,
funcName: string): int {.tags: [FTime].} =
proc waitFor(socket: Socket, waited: var float, timeout, size: int,
funcName: string): int {.tags: [TimeEffect].} =
## determines the amount of characters that can be read. Result will never
## be larger than ``size``. For unbuffered sockets this will be ``1``.
## 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)
else:
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):
if socket.isSSL:
@@ -619,13 +619,13 @@ proc waitFor(socket: PSocket, waited: var float, timeout, size: int,
var startTime = epochTime()
let selRet = select(socket, timeout - int(waited * 1000.0))
if selRet < 0: osError(osLastError())
if selRet < 0: raiseOSError(osLastError())
if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime)
proc recv*(socket: PSocket, data: pointer, size: int, timeout: int): int {.
tags: [FReadIO, FTime].} =
proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
tags: [ReadIOEffect, FTime].} =
## overload with a ``timeout`` parameter in miliseconds.
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
proc recv*(socket: PSocket, data: var string, size: int, timeout = -1,
flags = {TSocketFlags.SafeDisconn}): int =
proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
flags = {SocketFlag.SafeDisconn}): int =
## Higher-level version of ``recv``.
##
## 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)
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:
result = 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
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1,
flags = {TSocketFlags.SafeDisconn}) {.
tags: [FReadIO, FTime].} =
proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
flags = {SocketFlag.SafeDisconn}) {.
tags: [ReadIOEffect, FTime].} =
## Reads a line of data from ``socket``.
##
## If a full line is read ``\r\L`` is not
@@ -735,9 +735,9 @@ proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1,
return
add(line.string, c)
proc recvFrom*(socket: PSocket, data: var string, length: int,
address: var string, port: var TPort, flags = 0'i32): int {.
tags: [FReadIO].} =
proc recvFrom*(socket: Socket, data: var string, length: int,
address: var string, port: var Port, flags = 0'i32): int {.
tags: [ReadIOEffect].} =
## Receives data from ``socket``. This function should normally be used with
## connection-less sockets (UDP sockets).
##
@@ -751,7 +751,7 @@ proc recvFrom*(socket: PSocket, data: var string, length: int,
# TODO: Buffered sockets
data.setLen(length)
var sockAddress: Tsockaddr_in
var sockAddress: TSockaddr_in
var addrLen = sizeof(sockAddress).TSocklen
result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen))
@@ -759,11 +759,11 @@ proc recvFrom*(socket: PSocket, data: var string, length: int,
if result != -1:
data.setLen(result)
address = $inet_ntoa(sockAddress.sin_addr)
port = ntohs(sockAddress.sin_port).TPort
port = ntohs(sockAddress.sin_port).Port
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.
##
## 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)
dealloc(dummy)
proc send*(socket: PSocket, data: pointer, size: int): int {.
tags: [FWriteIO].} =
proc send*(socket: Socket, data: pointer, size: int): int {.
tags: [WriteIOEffect].} =
## Sends data to a socket.
##
## **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
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
proc send*(socket: PSocket, data: string,
flags = {TSocketFlags.SafeDisconn}) {.tags: [FWriteIO].} =
proc send*(socket: Socket, data: string,
flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} =
## sends data to a socket.
let sent = send(socket, cstring(data), data.len)
if sent < 0:
@@ -805,16 +805,16 @@ proc send*(socket: PSocket, data: string,
socketError(socket, lastError = lastError)
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,
## and instead returns ``false`` on failure.
result = send(socket, cstring(data), data.len) == data.len
proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer,
size: int, af: TDomain = AF_INET, flags = 0'i32): int {.
tags: [FWriteIO].} =
proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## 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)
proc sendTo*(socket: PSocket, address: string, port: TPort,
data: string): int {.tags: [FWriteIO].} =
proc sendTo*(socket: Socket, address: string, port: Port,
data: string): int {.tags: [WriteIOEffect].} =
## This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## 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.
result = socket.sendTo(address, port, cstring(data), data.len)
proc connectAsync(socket: PSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) {.tags: [FReadIO].} =
proc connectAsync(socket: Socket, name: string, port = Port(0),
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
## A variant of ``connect`` for non-blocking sockets.
##
## 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)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var lastError: OSErrorCode
var it = aiList
while it != nil:
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
dealloc(aiList)
if not success: osError(lastError)
if not success: raiseOSError(lastError)
proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int,
af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} =
proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
af: Domain = AF_INET) {.tags: [ReadIOEffect, FWriteIO].} =
## Connects to server as specified by ``address`` on port specified by ``port``.
##
## 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)
var s = @[socket.fd]
if selectWrite(s, timeout) != 1:
raise newException(ETimeout, "Call to 'connect' timed out.")
raise newException(TimeoutError, "Call to 'connect' timed out.")
else:
when defined(ssl):
if socket.isSSL:
@@ -904,10 +904,10 @@ proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int,
doAssert socket.handshake()
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.
proc getFD*(socket: PSocket): TSocketHandle = return socket.fd
proc getFD*(socket: Socket): TSocketHandle = return socket.fd
## Returns the socket's file descriptor
type
@@ -1042,23 +1042,23 @@ proc parseIPv4Address(address_str: string): TIpAddress =
currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0'))
if currentByte > 255'u16:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v4[byteCount] = cast[uint8](currentByte)
currentByte = 0
byteCount.inc
seperatorValid = false
else:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
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)
proc parseIPv6Address(address_str: string): TIpAddress =
@@ -1066,7 +1066,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6
if address_str.len < 2:
raise newException(EInvalidValue, "Invalid IP Address")
raise newException(ValueError, "Invalid IP Address")
var
groupCount = 0
@@ -1081,17 +1081,17 @@ proc parseIPv6Address(address_str: string): TIpAddress =
for i,c in address_str:
if c == ':':
if not seperatorValid:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid seperator")
if lastWasColon:
if dualColonGroup != -1:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount
seperatorValid = false
elif i != 0 and i != high(address_str):
if groupCount >= 8:
raise newException(EInvalidValue,
raise newException(ValueError,
"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+1] = cast[uint8](currentShort and 0xFF)
@@ -1100,17 +1100,17 @@ proc parseIPv6Address(address_str: string): TIpAddress =
if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with ::
if address_str[1] != ':':
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':':
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"")
lastWasColon = true
currentGroupStart = i + 1
elif c == '.': # Switch to parse IPv4 mode
if i < 3 or not seperatorValid or groupCount >= 7:
raise newException(EInvalidValue, "Invalid IP Address")
raise newException(ValueError, "Invalid IP Address")
v4StartPos = currentGroupStart
currentShort = 0
seperatorValid = false
@@ -1123,19 +1123,19 @@ proc parseIPv6Address(address_str: string): TIpAddress =
else: # Upper case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
if currentShort > 65535'u32:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
lastWasColon = false
seperatorValid = true
else:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
if seperatorValid: # Copy remaining data
if groupCount >= 8:
raise newException(EInvalidValue,
raise newException(ValueError,
"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+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
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif c == '.': # IPv4 address separator
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)
currentShort = 0
byteCount.inc()
seperatorValid = false
else: # Invalid character
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
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)
groupCount += 2
# Shift and fill zeros in case of ::
if groupCount > 8:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
elif groupCount < 8: # must fill
if dualColonGroup == -1:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. The address consists of too few groups")
var toFill = 8 - groupCount # The number of groups to fill
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
result.address_v6[dualColonGroup*2+i] = 0
elif dualColonGroup != -1:
raise newException(EInvalidValue,
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): TIpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
raise newException(EInvalidValue, "IP Address string is nil")
raise newException(ValueError, "IP Address string is nil")
if address_str.contains(':'):
return parseIPv6Address(address_str)
else:

View File

@@ -14,7 +14,7 @@
import unsigned, os
when hostos == "solaris":
when hostOS == "solaris":
{.passl: "-lsocket -lnsl".}
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) =
## closes a socket.
when useWinVersion:
discard winlean.closeSocket(socket)
discard winlean.closesocket(socket)
else:
discard posix.close(socket)
# 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 =
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.
## ``Backlog`` specifies the maximum length of the
## 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.
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
## which the service name specified by ``name`` matches the s_name 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)
else:
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.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port)
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
## which the port specified by ``port`` matches the s_port member and the
## 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)
else:
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.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port)
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.
var myaddr: InAddr
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.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.
when useWinVersion:
var s = winlean.gethostbyname(name)
@@ -314,28 +314,28 @@ proc getSockName*(socket: SocketHandle): Port =
name.sin_family = posix.AF_INET
#name.sin_port = htons(cint16(port))
#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)),
addr(namelen)) == -1'i32:
raiseOSError(osLastError())
result = Port(rawsockets.ntohs(name.sin_port))
proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
tags: [FReadIO].} =
tags: [ReadIOEffect].} =
## getsockopt for integer options.
var res: cint
var size = sizeof(res).Socklen
var size = sizeof(res).SockLen
if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
raiseOSError(osLastError())
result = int(res)
proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
tags: [FWriteIO].} =
tags: [WriteIOEffect].} =
## setsockopt for integer options.
var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).Socklen) < 0'i32:
sizeof(value).SockLen) < 0'i32:
raiseOSError(osLastError())
proc setBlocking*(s: SocketHandle, blocking: bool) =
@@ -361,13 +361,13 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
result.tv_sec = seconds.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)
for i in items(s):
m = max(m, int(i))
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 L = s.len
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
## be read/written to or has errors (``exceptfds``).
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
var tv {.noInit.}: TTimeval = timeValFromMilliseconds(timeout)
var rd: TFdSet
var m = 0
@@ -400,7 +400,7 @@ proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
pruneSocketSet(readfds, (rd))
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
## 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

View File

@@ -425,7 +425,7 @@ type
TFdSet* {.pure, final.} = object
fd_count*: cint # unsigned
fd_array*: array[0..FD_SETSIZE-1, TSocketHandle]
fd_array*: array[0..FD_SETSIZE-1, SocketHandle]
TTimeval* {.pure, final.} = object
tv_sec*, tv_usec*: int32
@@ -444,7 +444,7 @@ type
var
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
SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording
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_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 {.
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 {.
stdcall, importc: "gethostbyname", dynlib: ws2dll.}
proc socket*(af, typ, protocol: cint): TSocketHandle {.
proc socket*(af, typ, protocol: cint): SocketHandle {.
stdcall, importc: "socket", dynlib: ws2dll.}
proc closesocket*(s: TSocketHandle): cint {.
proc closesocket*(s: SocketHandle): cint {.
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.}
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.}
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.}
proc getsockname*(s: TSocketHandle, name: ptr TSockAddr,
proc getsockname*(s: SocketHandle, name: ptr TSockAddr,
namelen: ptr TSockLen): cint {.
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 {.
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 {.
stdcall, importc: "setsockopt", dynlib: ws2dll.}
proc listen*(s: TSocketHandle, backlog: cint): cint {.
proc listen*(s: SocketHandle, backlog: cint): cint {.
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.}
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 {.
stdcall, importc: "recvfrom", dynlib: ws2dll.}
proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet,
timeout: ptr TTimeval): cint {.
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.}
proc sendto*(s: TSocketHandle, buf: pointer, len, flags: cint,
proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint,
to: ptr TSockAddr, tolen: TSockLen): cint {.
stdcall, importc: "sendto", dynlib: ws2dll.}
proc shutdown*(s: TSocketHandle, how: cint): cint {.
proc shutdown*(s: SocketHandle, how: cint): cint {.
stdcall, importc: "shutdown", dynlib: ws2dll.}
proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen,
@@ -523,13 +523,13 @@ proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen,
proc inet_addr*(cp: cstring): int32 {.
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.}
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
proc FD_SET*(Socket: TSocketHandle, FDSet: var TFdSet) =
proc FD_SET*(Socket: SocketHandle, FDSet: var TFdSet) =
if FDSet.fd_count < FD_SETSIZE:
FDSet.fd_array[int(FDSet.fd_count)] = Socket
inc(FDSet.fd_count)
@@ -699,7 +699,7 @@ var
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])
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED,
lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
@@ -710,12 +710,12 @@ type
len*: ULONG
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,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
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,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}