Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
5aac789e0e
53 changed files with 1264 additions and 329 deletions
|
|
@ -43,6 +43,14 @@ proc complete*[T](future: PFuture[T], val: T) =
|
|||
if future.cb != nil:
|
||||
future.cb()
|
||||
|
||||
proc complete*(future: PFuture[void]) =
|
||||
## Completes a void ``future``.
|
||||
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
assert(future.error == nil)
|
||||
future.finished = true
|
||||
if future.cb != nil:
|
||||
future.cb()
|
||||
|
||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||
## Completes ``future`` with ``error``.
|
||||
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
|
|
@ -76,7 +84,8 @@ proc read*[T](future: PFuture[T]): T =
|
|||
## If the result of the future is an error then that error will be raised.
|
||||
if future.finished:
|
||||
if future.error != nil: raise future.error
|
||||
return future.value
|
||||
when T isnot void:
|
||||
return future.value
|
||||
else:
|
||||
# TODO: Make a custom exception type for this?
|
||||
raise newException(EInvalidValue, "Future still in progress.")
|
||||
|
|
@ -132,7 +141,6 @@ when defined(windows) or defined(nimdoc):
|
|||
cast[TCompletionKey](sock), 1) == 0:
|
||||
OSError(OSLastError())
|
||||
p.handles.incl(sock)
|
||||
# TODO: fd closure detection, we need to remove the fd from handles set
|
||||
|
||||
proc verifyPresence(p: PDispatcher, sock: TSocketHandle) =
|
||||
## Ensures that socket has been registered with the dispatcher.
|
||||
|
|
@ -244,13 +252,13 @@ when defined(windows) or defined(nimdoc):
|
|||
RemoteSockaddr, RemoteSockaddrLength)
|
||||
|
||||
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
||||
af = AF_INET): PFuture[int] =
|
||||
af = AF_INET): PFuture[void] =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
## Returns a ``PFuture`` which will complete when the connection succeeds
|
||||
## or an error occurs.
|
||||
verifyPresence(p, socket)
|
||||
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
|
||||
var retFuture = newFuture[void]()
|
||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
||||
var saddr: Tsockaddr_in
|
||||
saddr.sin_family = int16(toInt(af))
|
||||
|
|
@ -272,7 +280,7 @@ when defined(windows) or defined(nimdoc):
|
|||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
|
@ -282,7 +290,7 @@ when defined(windows) or defined(nimdoc):
|
|||
if ret:
|
||||
# Request to connect completed immediately.
|
||||
success = true
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
# We don't deallocate ``ol`` here because even though this completed
|
||||
# immediately poll will still be notified about its completion and it will
|
||||
# free ``ol``.
|
||||
|
|
@ -363,11 +371,11 @@ when defined(windows) or defined(nimdoc):
|
|||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
## data has been sent.
|
||||
verifyPresence(p, socket)
|
||||
var retFuture = newFuture[int]()
|
||||
var retFuture = newFuture[void]()
|
||||
|
||||
var dataBuf: TWSABuf
|
||||
dataBuf.buf = data
|
||||
|
|
@ -379,7 +387,7 @@ when defined(windows) or defined(nimdoc):
|
|||
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == TOSErrorCode(-1):
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||
)
|
||||
|
|
@ -392,7 +400,7 @@ when defined(windows) or defined(nimdoc):
|
|||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||
dealloc(ol)
|
||||
else:
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
# We don't deallocate ``ol`` here because even though this completed
|
||||
# immediately poll will still be notified about its completion and it will
|
||||
# free ``ol``.
|
||||
|
|
@ -475,6 +483,11 @@ when defined(windows) or defined(nimdoc):
|
|||
result = socket(domain, typ, protocol)
|
||||
disp.register(result)
|
||||
|
||||
proc close*(disp: PDispatcher, socket: TSocketHandle) =
|
||||
## Closes a socket and ensures that it is unregistered.
|
||||
socket.close()
|
||||
disp.handles.excl(socket)
|
||||
|
||||
initAll()
|
||||
else:
|
||||
import selectors
|
||||
|
|
@ -508,6 +521,10 @@ else:
|
|||
result = socket(domain, typ, protocol)
|
||||
disp.register(result)
|
||||
|
||||
proc close*(disp: PDispatcher, sock: TSocketHandle) =
|
||||
sock.close()
|
||||
disp.selector.unregister(sock)
|
||||
|
||||
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
|
||||
if sock notin p.selector:
|
||||
raise newException(EInvalidValue, "File descriptor not registered.")
|
||||
|
|
@ -556,12 +573,12 @@ else:
|
|||
# (e.g. socket disconnected).
|
||||
|
||||
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
||||
af = AF_INET): PFuture[int] =
|
||||
var retFuture = newFuture[int]()
|
||||
af = AF_INET): PFuture[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
|
||||
proc cb(sock: TSocketHandle): bool =
|
||||
# We have connected.
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
return true
|
||||
|
||||
var aiList = getAddrInfo(address, port, af)
|
||||
|
|
@ -573,7 +590,7 @@ else:
|
|||
if ret == 0:
|
||||
# Request to connect completed immediately.
|
||||
success = true
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
break
|
||||
else:
|
||||
lastError = osLastError()
|
||||
|
|
@ -627,8 +644,8 @@ else:
|
|||
addRead(p, socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
||||
var retFuture = newFuture[int]()
|
||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
|
||||
var written = 0
|
||||
|
||||
|
|
@ -648,7 +665,7 @@ else:
|
|||
if res != netSize:
|
||||
result = false # We still have data to send.
|
||||
else:
|
||||
retFuture.complete(0)
|
||||
retFuture.complete()
|
||||
addWrite(p, socket, cb)
|
||||
return retFuture
|
||||
|
||||
|
|
@ -781,12 +798,17 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
|||
|
||||
hint("Processing " & prc[0].getName & " as an async proc.")
|
||||
|
||||
let returnType = prc[3][0]
|
||||
var subtypeName = ""
|
||||
# Verify that the return type is a PFuture[T]
|
||||
if prc[3][0].kind == nnkIdent:
|
||||
error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
|
||||
elif prc[3][0].kind == nnkBracketExpr:
|
||||
if $prc[3][0][0] != "PFuture":
|
||||
error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
|
||||
if returnType.kind == nnkIdent:
|
||||
error("Expected return type of 'PFuture' got '" & $returnType & "'")
|
||||
elif returnType.kind == nnkBracketExpr:
|
||||
if $returnType[0] != "PFuture":
|
||||
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
|
||||
subtypeName = $returnType[1].ident
|
||||
elif returnType.kind == nnkEmpty:
|
||||
subtypeName = "void"
|
||||
|
||||
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
|
||||
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
|
||||
|
|
@ -799,20 +821,24 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
|||
newVarStmt(retFutureSym,
|
||||
newCall(
|
||||
newNimNode(nnkBracketExpr).add(
|
||||
newIdentNode("newFuture"),
|
||||
prc[3][0][1])))) # Get type from return type of this proc.
|
||||
|
||||
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
|
||||
newIdentNode(subtypeName))))) # Get type from return type of this proc
|
||||
echo(treeRepr(outerProcBody))
|
||||
# -> iterator nameIter(): PFutureBase {.closure.} =
|
||||
# -> var result: T
|
||||
# -> <proc_body>
|
||||
# -> complete(retFuture, result)
|
||||
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
|
||||
var procBody = prc[6].processBody(retFutureSym)
|
||||
procBody.insert(0, newNimNode(nnkVarSection).add(
|
||||
newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
|
||||
procBody.add(
|
||||
newCall(newIdentNode("complete"),
|
||||
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
|
||||
if subtypeName != "void":
|
||||
procBody.insert(0, newNimNode(nnkVarSection).add(
|
||||
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
|
||||
procBody.add(
|
||||
newCall(newIdentNode("complete"),
|
||||
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
|
||||
else:
|
||||
# -> complete(retFuture)
|
||||
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
||||
|
||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
||||
procBody, nnkIteratorDef)
|
||||
|
|
@ -847,6 +873,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
|||
for i in 0 .. <result[4].len:
|
||||
if result[4][i].ident == !"async":
|
||||
result[4].del(i)
|
||||
if subtypeName == "void":
|
||||
# Add discardable pragma.
|
||||
result[4].add(newIdentNode("discardable"))
|
||||
if returnType.kind == nnkEmpty:
|
||||
# Add PFuture[void]
|
||||
result[3][0] = parseExpr("PFuture[void]")
|
||||
|
||||
result[6] = outerProcBody
|
||||
|
||||
|
|
@ -892,7 +924,7 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.}
|
|||
when isMainModule:
|
||||
|
||||
var p = newDispatcher()
|
||||
var sock = socket()
|
||||
var sock = p.socket()
|
||||
sock.setBlocking false
|
||||
|
||||
|
||||
|
|
|
|||
261
lib/pure/net.nim
261
lib/pure/net.nim
|
|
@ -9,7 +9,264 @@
|
|||
|
||||
## This module implements a high-level cross-platform sockets interface.
|
||||
|
||||
import sockets2, os
|
||||
import sockets2, os, strutils, unsigned
|
||||
|
||||
type
|
||||
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
|
||||
IPv6, ## IPv6 address
|
||||
IPv4 ## IPv4 address
|
||||
|
||||
TIpAddress* = object ## stores an arbitrary IP address
|
||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||
of IpAddressFamily.IPv6:
|
||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in case of IPv6
|
||||
of IpAddressFamily.IPv4:
|
||||
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in case of IPv4
|
||||
|
||||
proc IPv4_any*(): TIpAddress =
|
||||
## Returns the IPv4 any address, which can be used to listen on all available
|
||||
## network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [0'u8, 0'u8, 0'u8, 0'u8])
|
||||
|
||||
proc IPv4_loopback*(): TIpAddress =
|
||||
## Returns the IPv4 loopback address (127.0.0.1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [127'u8, 0'u8, 0'u8, 1'u8])
|
||||
|
||||
proc IPv4_broadcast*(): TIpAddress =
|
||||
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [255'u8, 255'u8, 255'u8, 255'u8])
|
||||
|
||||
proc IPv6_any*(): TIpAddress =
|
||||
## Returns the IPv6 any address (::0), which can be used
|
||||
## to listen on all available network adapters
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8])
|
||||
|
||||
proc IPv6_loopback*(): TIpAddress =
|
||||
## Returns the IPv6 loopback address (::1)
|
||||
result = TIpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8])
|
||||
|
||||
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||
if lhs.family != rhs.family: return false
|
||||
if lhs.family == IpAddressFamily.IPv4:
|
||||
for i in low(lhs.address_v4) .. high(lhs.address_v4):
|
||||
if lhs.address_v4[i] != rhs.address_v4[i]: return false
|
||||
else: # IPv6
|
||||
for i in low(lhs.address_v6) .. high(lhs.address_v6):
|
||||
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||
return true
|
||||
|
||||
proc `$`*(address: TIpAddress): string =
|
||||
## Converts an TIpAddress into the textual representation
|
||||
result = ""
|
||||
case address.family
|
||||
of IpAddressFamily.IPv4:
|
||||
for i in 0 .. 3:
|
||||
if i != 0:
|
||||
result.add('.')
|
||||
result.add($address.address_v4[i])
|
||||
of IpAddressFamily.IPv6:
|
||||
var
|
||||
currentZeroStart = -1
|
||||
currentZeroCount = 0
|
||||
biggestZeroStart = -1
|
||||
biggestZeroCount = 0
|
||||
# Look for the largest block of zeros
|
||||
for i in 0..7:
|
||||
var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0
|
||||
if isZero:
|
||||
if currentZeroStart == -1:
|
||||
currentZeroStart = i
|
||||
currentZeroCount = 1
|
||||
else:
|
||||
currentZeroCount.inc()
|
||||
if currentZeroCount > biggestZeroCount:
|
||||
biggestZeroCount = currentZeroCount
|
||||
biggestZeroStart = currentZeroStart
|
||||
else:
|
||||
currentZeroStart = -1
|
||||
|
||||
if biggestZeroCount == 8: # Special case ::0
|
||||
result.add("::")
|
||||
else: # Print address
|
||||
var printedLastGroup = false
|
||||
for i in 0..7:
|
||||
var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
|
||||
word = word or cast[uint16](address.address_v6[i*2+1])
|
||||
|
||||
if biggestZeroCount != 0 and # Check if group is in skip group
|
||||
(i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)):
|
||||
if i == biggestZeroStart: # skip start
|
||||
result.add("::")
|
||||
printedLastGroup = false
|
||||
else:
|
||||
if printedLastGroup:
|
||||
result.add(':')
|
||||
var
|
||||
afterLeadingZeros = false
|
||||
mask = 0xF000'u16
|
||||
for j in 0'u16..3'u16:
|
||||
var val = (mask and word) shr (4'u16*(3'u16-j))
|
||||
if val != 0 or afterLeadingZeros:
|
||||
if val < 0xA:
|
||||
result.add(chr(uint16(ord('0'))+val))
|
||||
else: # val >= 0xA
|
||||
result.add(chr(uint16(ord('a'))+val-0xA))
|
||||
afterLeadingZeros = true
|
||||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
var
|
||||
byteCount = 0
|
||||
currentByte:uint16 = 0
|
||||
seperatorValid = false
|
||||
|
||||
result.family = IpAddressFamily.IPv4
|
||||
|
||||
for i in 0 .. high(address_str):
|
||||
if address_str[i] in strutils.Digits: # Character is a number
|
||||
currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0'))
|
||||
if currentByte > 255'u16:
|
||||
raise newException(EInvalidValue, "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, "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, "Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2: raise newException(EInvalidValue, "Invalid IP Address")
|
||||
|
||||
var
|
||||
groupCount = 0
|
||||
currentGroupStart = 0
|
||||
currentShort:uint32 = 0
|
||||
seperatorValid = true
|
||||
dualColonGroup = -1
|
||||
lastWasColon = false
|
||||
v4StartPos = -1
|
||||
byteCount = 0
|
||||
|
||||
for i,c in address_str:
|
||||
if c == ':':
|
||||
if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1: raise newException(EInvalidValue, "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, "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)
|
||||
currentShort = 0
|
||||
groupCount.inc()
|
||||
if dualColonGroup != -1: seperatorValid = false
|
||||
elif i == 0: # only valid if address starts with ::
|
||||
if address_str[1] != ':':
|
||||
raise newException(EInvalidValue, "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, "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")
|
||||
v4StartPos = currentGroupStart
|
||||
currentShort = 0
|
||||
seperatorValid = false
|
||||
break
|
||||
elif c in strutils.HexDigits:
|
||||
if c in strutils.Digits: # Normal digit
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
|
||||
elif c >= 'a' and c <= 'f': # Lower case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
|
||||
else: # Upper case hex
|
||||
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
|
||||
if currentShort > 65535'u32:
|
||||
raise newException(EInvalidValue, "Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
raise newException(EInvalidValue, "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, "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)
|
||||
groupCount.inc()
|
||||
else: # Must parse IPv4 address
|
||||
for i,c in address_str[v4StartPos..high(address_str)]:
|
||||
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, "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")
|
||||
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
|
||||
currentShort = 0
|
||||
byteCount.inc()
|
||||
seperatorValid = false
|
||||
else: # Invalid character
|
||||
raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(EInvalidValue, "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, "Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1: raise newException(EInvalidValue, "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 ::
|
||||
for i in 0..2*toShift-1: # shift
|
||||
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
|
||||
for i in 0..2*toFill-1: # fill with 0s
|
||||
result.address_v6[dualColonGroup*2+i] = 0
|
||||
elif dualColonGroup != -1: raise newException(EInvalidValue, "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")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
|
||||
type
|
||||
TSocket* = TSocketHandle
|
||||
|
|
@ -52,4 +309,4 @@ proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
|
|||
else:
|
||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||
if fcntl(s, F_SETFL, mode) == -1:
|
||||
osError(osLastError())
|
||||
osError(osLastError())
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ when defined(linux) or defined(nimdoc):
|
|||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||
## Updates the events which ``fd`` wants notifications for.
|
||||
if s.fds[fd].events != events:
|
||||
echo("Update ", fd.cint, " to ", events)
|
||||
var event = createEventStruct(events, fd)
|
||||
|
||||
s.fds[fd].events = events
|
||||
|
|
@ -70,7 +69,9 @@ when defined(linux) or defined(nimdoc):
|
|||
|
||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||
OSError(OSLastError())
|
||||
let err = OSLastError()
|
||||
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
||||
OSError(err)
|
||||
result = s.fds[fd]
|
||||
s.fds.del(fd)
|
||||
|
||||
|
|
@ -78,6 +79,15 @@ when defined(linux) or defined(nimdoc):
|
|||
if s.epollFD.close() != 0: OSError(OSLastError())
|
||||
dealloc(addr s.events) # TODO: Test this
|
||||
|
||||
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
|
||||
result = true
|
||||
var event = createEventStruct(s.fds[fd].events, fd)
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||
let err = osLastError()
|
||||
if err.cint in {ENOENT, EBADF}:
|
||||
return false
|
||||
OSError(OSLastError())
|
||||
|
||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||
##
|
||||
## The ``events`` field of the returned ``key`` contains the original events
|
||||
|
|
@ -85,24 +95,19 @@ when defined(linux) or defined(nimdoc):
|
|||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||
## on the ``fd``.
|
||||
result = @[]
|
||||
|
||||
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||
if evNum < 0: OSError(OSLastError())
|
||||
if evNum == 0: return @[]
|
||||
for i in 0 .. <evNum:
|
||||
let fd = s.events[i].data.fd.TSocketHandle
|
||||
|
||||
var evSet: set[TEvent] = {}
|
||||
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
|
||||
let selectorKey = s.fds[fd]
|
||||
assert selectorKey != nil
|
||||
result.add((selectorKey, evSet))
|
||||
|
||||
if (s.events[i].events and EPOLLHUP) != 0 or
|
||||
(s.events[i].events and EPOLLRDHUP) != 0:
|
||||
# fd closed
|
||||
#echo("fd closed ", s.events[i].data.fd)
|
||||
s.unregister(s.events[i].data.fd.TSocketHandle)
|
||||
|
||||
|
||||
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
||||
|
||||
proc newSelector*(): PSelector =
|
||||
|
|
@ -116,15 +121,8 @@ when defined(linux) or defined(nimdoc):
|
|||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||
## Determines whether selector contains a file descriptor.
|
||||
if s.fds.hasKey(fd):
|
||||
result = true
|
||||
|
||||
# Ensure the underlying epoll instance still contains this fd.
|
||||
var event = createEventStruct(s.fds[fd].events, fd)
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||
let err = osLastError()
|
||||
if err.cint in {ENOENT, EBADF}:
|
||||
return false
|
||||
OSError(OSLastError())
|
||||
result = epollHasFd(s, fd)
|
||||
else:
|
||||
return false
|
||||
|
||||
|
|
|
|||
|
|
@ -2364,12 +2364,32 @@ when not defined(JS): #and not defined(NimrodVM):
|
|||
|
||||
when not defined(NimrodVM):
|
||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be likely. This is a hint for the
|
||||
## optimizer.
|
||||
## Hints the optimizer that `val` is likely going to be true.
|
||||
##
|
||||
## You can use this proc to decorate a branch condition. On certain
|
||||
## platforms this can help the processor predict better which branch is
|
||||
## going to be run. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for value in inputValues:
|
||||
## if likely(value <= 100):
|
||||
## process(value)
|
||||
## else:
|
||||
## echo "Value too big!"
|
||||
|
||||
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
||||
## optimizer.
|
||||
## Hints the optimizer that `val` is likely going to be false.
|
||||
##
|
||||
## You can use this proc to decorate a branch condition. On certain
|
||||
## platforms this can help the processor predict better which branch is
|
||||
## going to be run. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for value in inputValues:
|
||||
## if unlikely(value > 100):
|
||||
## echo "Value too big!"
|
||||
## else:
|
||||
## process(value)
|
||||
|
||||
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||
## retrieves the raw proc pointer of the closure `x`. This is
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ when defined(windows):
|
|||
elif defined(macosx):
|
||||
const libz = "libz.dylib"
|
||||
else:
|
||||
const libz = "libz.so"
|
||||
const libz = "libz.so.1"
|
||||
|
||||
type
|
||||
Uint* = int32
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue