Merge branch 'devel' into araq
This commit is contained in:
commit
c3c37dbb15
29 changed files with 517 additions and 277 deletions
|
|
@ -332,7 +332,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
|||
|
||||
var alreadyPoppedCnt = p.inExceptBlock
|
||||
for i in countup(1, howManyTrys):
|
||||
if not p.module.compileToCpp:
|
||||
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
|
||||
# Pop safe points generated by try
|
||||
if alreadyPoppedCnt > 0:
|
||||
dec alreadyPoppedCnt
|
||||
|
|
@ -354,7 +354,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
|||
for i in countdown(howManyTrys-1, 0):
|
||||
p.nestedTryStmts.add(stack[i])
|
||||
|
||||
if not p.module.compileToCpp:
|
||||
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
|
||||
# Pop exceptions that was handled by the
|
||||
# except-blocks we are in
|
||||
for i in countdown(howManyExcepts-1, 0):
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
|||
var disp = newNodeI(nkIfStmt, base.info)
|
||||
var ands = getSysSym("and")
|
||||
var iss = getSysSym("of")
|
||||
for col in countup(1, paramLen - 1):
|
||||
if contains(relevantCols, col):
|
||||
let param = base.typ.n.sons[col].sym
|
||||
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||
addSon(nilchecks, newTree(nkCall,
|
||||
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
|
||||
for meth in countup(0, high(methods)):
|
||||
var curr = methods[meth] # generate condition:
|
||||
var cond: PNode = nil
|
||||
|
|
@ -242,9 +248,6 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
|||
addSon(isn, newSymNode(iss))
|
||||
let param = base.typ.n.sons[col].sym
|
||||
addSon(isn, newSymNode(param))
|
||||
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||
addSon(nilchecks, newTree(nkCall,
|
||||
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
|
||||
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||
if cond != nil:
|
||||
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||
|
|
|
|||
|
|
@ -2272,7 +2272,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
|||
genModule(p, n)
|
||||
add(p.g.code, p.locals)
|
||||
add(p.g.code, p.body)
|
||||
globals.unique = p.unique
|
||||
|
||||
proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
|
||||
for prc in globals.forwarded:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
|||
u = rope(lengthOrd(field.typ))
|
||||
else: internalError(n.info, "genObjectFields(nkRecCase)")
|
||||
if result != nil: add(result, ", " & tnl)
|
||||
addf(result, "[SetConstr($1), $2]",
|
||||
addf(result, "[setConstr($1), $2]",
|
||||
[u, genObjectFields(p, typ, lastSon(b))])
|
||||
result = ("{kind: 3, offset: \"$1\", len: $3, " &
|
||||
"typ: $2, name: $4, sons: [$5]}") % [
|
||||
|
|
|
|||
|
|
@ -1651,8 +1651,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
|
|||
for i in 0 .. <gp.len:
|
||||
if sfImmediate notin sym.flags:
|
||||
let idx = sym.typ.len + i
|
||||
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
|
||||
if idx < n.len:
|
||||
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
|
||||
else:
|
||||
dec(evalMacroCounter)
|
||||
c.callsite = nil
|
||||
localError(n.info, "expected " & $gp.len &
|
||||
" generic parameter(s)")
|
||||
elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
|
||||
dec(evalMacroCounter)
|
||||
c.callsite = nil
|
||||
globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
|
||||
# temporary storage:
|
||||
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
|
||||
|
|
|
|||
|
|
@ -175,7 +175,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
|||
result.add mapTypeToAst(t.sons[i], info)
|
||||
else:
|
||||
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
|
||||
of tyGenericBody, tyOrdinal:
|
||||
of tyGenericBody:
|
||||
if inst:
|
||||
result = mapTypeToAstX(t.lastSon, info, inst, true)
|
||||
else:
|
||||
result = mapTypeToAst(t.lastSon, info)
|
||||
of tyOrdinal:
|
||||
result = mapTypeToAst(t.lastSon, info)
|
||||
of tyDistinct:
|
||||
if inst:
|
||||
|
|
|
|||
12
doc/tut1.rst
12
doc/tut1.rst
|
|
@ -599,7 +599,7 @@ Result variable
|
|||
A procedure that returns a value has an implicit ``result`` variable declared
|
||||
that represents the return value. A ``return`` statement with no expression is a
|
||||
shorthand for ``return result``. The ``result`` value is always returned
|
||||
automatically at the end a procedure if there is no ``return`` statement at
|
||||
automatically at the end of a procedure if there is no ``return`` statement at
|
||||
the exit.
|
||||
|
||||
.. code-block:: nim
|
||||
|
|
@ -1074,8 +1074,8 @@ at runtime by 0, the second by 1 and so on. For example:
|
|||
Direction = enum
|
||||
north, east, south, west
|
||||
|
||||
var x = south # `x` is of type `Direction`; its value is `south`
|
||||
echo x # writes "south" to `stdout`
|
||||
var x = south # `x` is of type `Direction`; its value is `south`
|
||||
echo x # writes "south" to `stdout`
|
||||
|
||||
All the comparison operators can be used with enumeration types.
|
||||
|
||||
|
|
@ -1132,11 +1132,11 @@ A subrange type is a range of values from an integer or enumeration type
|
|||
|
||||
.. code-block:: nim
|
||||
type
|
||||
Subrange = range[0..5]
|
||||
MySubrange = range[0..5]
|
||||
|
||||
|
||||
``Subrange`` is a subrange of ``int`` which can only hold the values 0
|
||||
to 5. Assigning any other value to a variable of type ``Subrange`` is a
|
||||
``MySubrange`` is a subrange of ``int`` which can only hold the values 0
|
||||
to 5. Assigning any other value to a variable of type ``MySubrange`` is a
|
||||
compile-time or runtime error. Assignments from the base type to one of its
|
||||
subrange types (and vice versa) are allowed.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
include "system/inclrtl"
|
||||
|
||||
import os, oids, tables, strutils, times, heapqueue, options
|
||||
import os, tables, strutils, times, heapqueue, options
|
||||
|
||||
import nativesockets, net, deques
|
||||
|
||||
|
|
@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
|
|
@ -931,6 +936,11 @@ else:
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc update(fd: AsyncFD, events: set[Event]) =
|
||||
let p = getGlobalDispatcher()
|
||||
assert fd.SocketHandle in p.selector
|
||||
|
|
|
|||
|
|
@ -339,13 +339,17 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
|||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == size.int32
|
||||
f.offset.inc(size)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
)
|
||||
# passing -1 here should work according to MSDN, but doesn't. For more
|
||||
# information see
|
||||
# http://stackoverflow.com/questions/33650899/does-asynchronous-file-
|
||||
# appending-in-windows-preserve-order
|
||||
ol.offset = DWord(f.offset and 0xffffffff)
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
f.offset.inc(size)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
|
||||
|
|
@ -364,7 +368,6 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
|||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
else:
|
||||
assert bytesWritten == size.int32
|
||||
f.offset.inc(size)
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
|
@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == data.len.int32
|
||||
f.offset.inc(data.len)
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
|
|
@ -420,6 +422,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
)
|
||||
ol.offset = DWord(f.offset and 0xffffffff)
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
f.offset.inc(data.len)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
|
||||
|
|
@ -441,7 +444,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
|||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
else:
|
||||
assert bytesWritten == data.len.int32
|
||||
f.offset.inc(data.len)
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
|
|
|||
|
|
@ -647,9 +647,12 @@ when defineSsl:
|
|||
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
|
||||
|
||||
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
|
||||
handshake: SslHandshakeType) =
|
||||
handshake: SslHandshakeType,
|
||||
hostname: string = nil) =
|
||||
## Wraps a connected socket in an SSL context. This function effectively
|
||||
## turns ``socket`` into an SSL socket.
|
||||
## ``hostname`` should be specified so that the client knows which hostname
|
||||
## the server certificate should be validated against.
|
||||
##
|
||||
## This should be called on a connected socket, and will perform
|
||||
## an SSL handshake immediately.
|
||||
|
|
@ -660,6 +663,10 @@ when defineSsl:
|
|||
|
||||
case handshake
|
||||
of handshakeAsClient:
|
||||
if not hostname.isNil and not isIpAddress(hostname):
|
||||
# Set the SNI address for this connection. This call can fail if
|
||||
# we're not using TLSv1+.
|
||||
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
|
||||
sslSetConnectState(socket.sslHandle)
|
||||
of handshakeAsServer:
|
||||
sslSetAcceptState(socket.sslHandle)
|
||||
|
|
|
|||
|
|
@ -269,6 +269,18 @@ proc del*[A, B](t: var Table[A, B], key: A) =
|
|||
## deletes `key` from hash table `t`.
|
||||
delImpl()
|
||||
|
||||
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
||||
## Deletes the ``key`` from the table.
|
||||
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
result = index >= 0
|
||||
if result:
|
||||
shallowCopy(val, t.data[index].val)
|
||||
delImplIdx(t, index)
|
||||
|
||||
proc enlarge[A, B](t: var Table[A, B]) =
|
||||
var n: KeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
|
|
@ -424,6 +436,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
|
|||
## deletes `key` from hash table `t`.
|
||||
t[].del(key)
|
||||
|
||||
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
|
||||
## Deletes the ``key`` from the table.
|
||||
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
result = t[].take(key, val)
|
||||
|
||||
proc newTable*[A, B](initialSize=64): TableRef[A, B] =
|
||||
new(result)
|
||||
result[] = initTable[A, B](initialSize)
|
||||
|
|
@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
|
|||
while ht >= 0 and hs >= 0:
|
||||
var nxtt = t.data[ht].next
|
||||
var nxts = s.data[hs].next
|
||||
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
|
||||
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
|
||||
if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
|
||||
return false
|
||||
ht = nxtt
|
||||
|
|
@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
|
|||
proc clear*[A](t: var CountTable[A]) =
|
||||
## Resets the table so that it is empty.
|
||||
clearImpl()
|
||||
|
||||
|
||||
iterator pairs*[A](t: CountTable[A]): (A, int) =
|
||||
## iterates over any (key, value) pair in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
|
|
@ -1256,17 +1275,17 @@ when isMainModule:
|
|||
var b = newOrderedTable[string, string](initialSize=2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == b
|
||||
|
||||
block: #5482
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string](initialSize=2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == b
|
||||
|
||||
block: #5487
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string]() # notice, default size!
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
|
|
@ -1279,13 +1298,13 @@ when isMainModule:
|
|||
b.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
|
||||
block:
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
block:
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
var c = newOrderedTable[string, string]() # notice, default size!
|
||||
c.add("wrong?", "foo")
|
||||
c.add("wrong?", "foo2")
|
||||
c.add("wrong?", "foo2")
|
||||
assert a == b
|
||||
assert a == c
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
raise newException(HttpRequestError,
|
||||
"The proxy server rejected a CONNECT request, " &
|
||||
"so a secure connection could not be established.")
|
||||
sslContext.wrapConnectedSocket(s, handshakeAsClient)
|
||||
sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
|
||||
else:
|
||||
raise newException(HttpRequestError, "SSL support not available. Cannot connect via proxy over SSL")
|
||||
else:
|
||||
|
|
@ -1060,7 +1060,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
|
|||
when defined(ssl):
|
||||
if isSsl:
|
||||
try:
|
||||
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
|
||||
client.sslContext.wrapConnectedSocket(
|
||||
client.socket, handshakeAsClient, url.hostname)
|
||||
except:
|
||||
client.socket.close()
|
||||
raise getCurrentException()
|
||||
|
|
@ -1102,7 +1103,8 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
|
|||
raise newException(HttpRequestError,
|
||||
"The proxy server rejected a CONNECT request, " &
|
||||
"so a secure connection could not be established.")
|
||||
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
|
||||
client.sslContext.wrapConnectedSocket(
|
||||
client.socket, handshakeAsClient, requestUrl.hostname)
|
||||
client.proxy = nil
|
||||
else:
|
||||
raise newException(HttpRequestError,
|
||||
|
|
|
|||
358
lib/pure/net.nim
358
lib/pure/net.nim
|
|
@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
|||
raiseOSError(osLastError())
|
||||
result = newSocket(fd, domain, sockType, protocol, buffered)
|
||||
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## 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(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(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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(ValueError, "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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
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(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)
|
||||
currentShort = 0
|
||||
groupCount.inc()
|
||||
if dualColonGroup != -1: seperatorValid = false
|
||||
elif i == 0: # only valid if address starts with ::
|
||||
if address_str[1] != ':':
|
||||
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(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(ValueError, "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(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
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(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)
|
||||
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(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
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(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
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 ::
|
||||
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(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress*(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(ValueError, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||
## Checks if a string is an IP address
|
||||
## Returns true if it is, false otherwise
|
||||
try:
|
||||
discard parseIpAddress(address_str)
|
||||
except ValueError:
|
||||
return false
|
||||
return true
|
||||
|
||||
when defineSsl:
|
||||
CRYPTO_malloc_init()
|
||||
SslLibraryInit()
|
||||
|
|
@ -438,9 +612,12 @@ when defineSsl:
|
|||
raiseSSLError()
|
||||
|
||||
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
|
||||
handshake: SslHandshakeType) =
|
||||
handshake: SslHandshakeType,
|
||||
hostname: string = nil) =
|
||||
## Wraps a connected socket in an SSL context. This function effectively
|
||||
## turns ``socket`` into an SSL socket.
|
||||
## ``hostname`` should be specified so that the client knows which hostname
|
||||
## the server certificate should be validated against.
|
||||
##
|
||||
## This should be called on a connected socket, and will perform
|
||||
## an SSL handshake immediately.
|
||||
|
|
@ -450,6 +627,10 @@ when defineSsl:
|
|||
wrapSocket(ctx, socket)
|
||||
case handshake
|
||||
of handshakeAsClient:
|
||||
if not hostname.isNil and not isIpAddress(hostname):
|
||||
# Discard result in case OpenSSL version doesn't support SNI, or we're
|
||||
# not using TLSv1+
|
||||
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
|
||||
let ret = SSLConnect(socket.sslHandle)
|
||||
socketError(socket, ret)
|
||||
of handshakeAsServer:
|
||||
|
|
@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
|
|||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## 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(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(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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
if address_str.len < 2:
|
||||
raise newException(ValueError, "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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid seperator")
|
||||
if lastWasColon:
|
||||
if dualColonGroup != -1:
|
||||
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(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)
|
||||
currentShort = 0
|
||||
groupCount.inc()
|
||||
if dualColonGroup != -1: seperatorValid = false
|
||||
elif i == 0: # only valid if address starts with ::
|
||||
if address_str[1] != ':':
|
||||
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(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(ValueError, "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(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
lastWasColon = false
|
||||
seperatorValid = true
|
||||
else:
|
||||
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(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)
|
||||
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(ValueError,
|
||||
"Invalid IP Address. Value is out of range")
|
||||
seperatorValid = true
|
||||
elif c == '.': # IPv4 address separator
|
||||
if not seperatorValid or byteCount >= 3:
|
||||
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(ValueError,
|
||||
"Invalid IP Address. Address contains an invalid character")
|
||||
|
||||
if byteCount != 3 or not seperatorValid:
|
||||
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(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
elif groupCount < 8: # must fill
|
||||
if dualColonGroup == -1:
|
||||
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 ::
|
||||
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(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
|
||||
proc parseIpAddress*(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
raise newException(ValueError, "IP Address string is nil")
|
||||
if address_str.contains(':'):
|
||||
return parseIPv6Address(address_str)
|
||||
else:
|
||||
return parseIPv4Address(address_str)
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||
## Checks if a string is an IP address
|
||||
## Returns true if it is, false otherwise
|
||||
try:
|
||||
discard parseIpAddress(address_str)
|
||||
except ValueError:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc dial*(address: string, port: Port,
|
||||
protocol = IPPROTO_TCP, buffered = true): Socket
|
||||
{.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
|
|
|
|||
|
|
@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
|
|||
if afterRunEvent != nil: afterRunEvent(i, p)
|
||||
close(p)
|
||||
|
||||
proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
|
||||
proc select*(readfds: var seq[Process], timeout = 500): int
|
||||
{.benign, deprecated.}
|
||||
## `select` with a sensible Nim interface. `timeout` is in milliseconds.
|
||||
## Specify -1 for no timeout. Returns the number of processes that are
|
||||
## ready to read from. The processes that are ready to be read from are
|
||||
|
|
@ -343,6 +344,9 @@ proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
|
|||
##
|
||||
## **Warning**: This function may give unexpected or completely wrong
|
||||
## results on Windows.
|
||||
##
|
||||
## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
|
||||
## and so should not be used in newly written code.
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc execProcess(command: string,
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
|
|||
|
||||
proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
|
||||
## Finds the first occurrence of ``first``, then returns everything from there
|
||||
## up to ``second``(if ``second`` is '\0', then ``first`` is used).
|
||||
## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
|
||||
var i = skipUntil(s, first, start)+1+start
|
||||
result = ""
|
||||
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
||||
|
|
|
|||
|
|
@ -1881,6 +1881,8 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
|||
## of significant digits to be printed.
|
||||
## `precision`'s default value is the maximum number of meaningful digits
|
||||
## after the decimal point for Nim's ``float`` type.
|
||||
##
|
||||
## If ``precision == 0``, it tries to format it nicely.
|
||||
result = formatBiggestFloat(f, format, precision, decimalSep)
|
||||
|
||||
proc trimZeros*(x: var string) {.noSideEffect.} =
|
||||
|
|
|
|||
|
|
@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
|
|||
|
||||
template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
|
||||
when defined(cpp):
|
||||
when sizeof(int) == 8:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
|
||||
header: "<intrin.h>".}
|
||||
else:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
|
||||
header: "<intrin.h>".}
|
||||
else:
|
||||
when sizeof(int) == 8:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
|
||||
else:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
|
||||
|
||||
proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
|
||||
|
||||
|
|
@ -180,6 +194,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
|
|||
result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
result = addAndFetch(memLoc.addr, x)
|
||||
inc(result, x)
|
||||
else:
|
||||
inc(memLoc, x)
|
||||
result = memLoc
|
||||
|
|
@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
|
|||
result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
|
||||
elif defined(vcc) and hasThreadSupport:
|
||||
result = addAndFetch(memLoc.addr, -x)
|
||||
dec(result, x)
|
||||
else:
|
||||
dec(memLoc, x)
|
||||
result = memLoc
|
||||
|
|
|
|||
|
|
@ -299,8 +299,13 @@ proc raiseExceptionAux(e: ref Exception) =
|
|||
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
|
||||
if e.name.isNil: e.name = ename
|
||||
when hasSomeStackTrace:
|
||||
e.trace = ""
|
||||
rawWriteStackTrace(e.trace)
|
||||
if e.trace.isNil:
|
||||
e.trace = ""
|
||||
rawWriteStackTrace(e.trace)
|
||||
elif framePtr != nil:
|
||||
e.trace.add "[[reraised from:\n"
|
||||
auxWriteStackTrace(framePtr, e.trace)
|
||||
e.trace.add "]]\n"
|
||||
raiseExceptionAux(e)
|
||||
|
||||
proc reraiseException() {.compilerRtl.} =
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ elif defined(genode):
|
|||
|
||||
else:
|
||||
type
|
||||
SysLock {.importc: "pthread_mutex_t", pure, final,
|
||||
SysLockObj {.importc: "pthread_mutex_t", pure, final,
|
||||
header: """#include <sys/types.h>
|
||||
#include <pthread.h>""".} = object
|
||||
when defined(linux) and defined(amd64):
|
||||
|
|
@ -111,7 +111,7 @@ else:
|
|||
when defined(linux) and defined(amd64):
|
||||
abi: array[4 div sizeof(cint), cint] # actually a cint
|
||||
|
||||
SysCond {.importc: "pthread_cond_t", pure, final,
|
||||
SysCondObj {.importc: "pthread_cond_t", pure, final,
|
||||
header: """#include <sys/types.h>
|
||||
#include <pthread.h>""".} = object
|
||||
when defined(linux) and defined(amd64):
|
||||
|
|
@ -119,8 +119,62 @@ else:
|
|||
|
||||
SysLockType = distinct cint
|
||||
|
||||
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) {.
|
||||
proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
|
||||
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
|
||||
proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
|
||||
|
||||
proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_lock", header: "<pthread.h>".}
|
||||
proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
|
||||
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||
|
||||
proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
|
||||
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
||||
|
||||
when defined(ios):
|
||||
# iOS will behave badly if sync primitives are moved in memory. In order
|
||||
# to prevent this once and for all, we're doing an extra malloc when
|
||||
# initializing the primitive.
|
||||
type
|
||||
SysLock = ptr SysLockObj
|
||||
SysCond = ptr SysCondObj
|
||||
|
||||
when not declared(c_malloc):
|
||||
proc c_malloc(size: csize): pointer {.
|
||||
importc: "malloc", header: "<stdlib.h>".}
|
||||
proc c_free(p: pointer) {.
|
||||
importc: "free", header: "<stdlib.h>".}
|
||||
|
||||
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
|
||||
L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
|
||||
initSysLockAux(L[], attr)
|
||||
|
||||
proc deinitSys(L: var SysLock) =
|
||||
deinitSysAux(L[])
|
||||
c_free(L)
|
||||
|
||||
template acquireSys(L: var SysLock) =
|
||||
acquireSysAux(L[])
|
||||
template tryAcquireSys(L: var SysLock): bool =
|
||||
tryAcquireSysAux(L[]) == 0'i32
|
||||
template releaseSys(L: var SysLock) =
|
||||
releaseSysAux(L[])
|
||||
else:
|
||||
type
|
||||
SysLock = SysLockObj
|
||||
SysCond = SysCondObj
|
||||
|
||||
template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
|
||||
initSysLockAux(L, attr)
|
||||
template deinitSys(L: var SysLock) =
|
||||
deinitSysAux(L)
|
||||
template acquireSys(L: var SysLock) =
|
||||
acquireSysAux(L)
|
||||
template tryAcquireSys(L: var SysLock): bool =
|
||||
tryAcquireSysAux(L) == 0'i32
|
||||
template releaseSys(L: var SysLock) =
|
||||
releaseSysAux(L)
|
||||
|
||||
when insideRLocksModule:
|
||||
proc SysLockType_Reentrant: SysLockType =
|
||||
|
|
@ -130,27 +184,39 @@ else:
|
|||
proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
|
||||
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
|
||||
|
||||
proc acquireSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_lock", header: "<pthread.h>".}
|
||||
proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
|
||||
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
|
||||
|
||||
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
|
||||
result = tryAcquireSysAux(L) == 0'i32
|
||||
|
||||
proc releaseSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
|
||||
proc deinitSys(L: var SysLock) {.noSideEffect,
|
||||
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
|
||||
|
||||
when not insideRLocksModule:
|
||||
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
|
||||
else:
|
||||
proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
|
||||
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
|
||||
proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
|
||||
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
|
||||
proc signalSysCond(cond: var SysCond) {.
|
||||
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
|
||||
proc deinitSysCond(cond: var SysCond) {.noSideEffect,
|
||||
proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
|
||||
importc: "pthread_cond_destroy", header: "<pthread.h>".}
|
||||
|
||||
proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
|
||||
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
|
||||
proc signalSysCondAux(cond: var SysCondObj) {.
|
||||
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
|
||||
|
||||
when defined(ios):
|
||||
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
|
||||
cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
|
||||
initSysCondAux(cond[], cond_attr)
|
||||
|
||||
proc deinitSysCond(cond: var SysCond) =
|
||||
deinitSysCondAux(cond[])
|
||||
c_free(cond)
|
||||
|
||||
template waitSysCond(cond: var SysCond, lock: var SysLock) =
|
||||
waitSysCondAux(cond[], lock[])
|
||||
template signalSysCond(cond: var SysCond) =
|
||||
signalSysCondAux(cond[])
|
||||
else:
|
||||
template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
|
||||
initSysCondAux(cond, cond_attr)
|
||||
template deinitSysCond(cond: var SysCond) =
|
||||
deinitSysCondAux(cond)
|
||||
|
||||
template waitSysCond(cond: var SysCond, lock: var SysLock) =
|
||||
waitSysCondAux(cond, lock)
|
||||
template signalSysCond(cond: var SysCond) =
|
||||
signalSysCondAux(cond)
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
include "system/inclrtl"
|
||||
|
||||
import os, oids, tables, strutils, times, heapqueue, lists, options
|
||||
import os, tables, strutils, times, heapqueue, lists, options
|
||||
|
||||
import nativesockets, net, deques
|
||||
|
||||
|
|
@ -219,6 +219,11 @@ when defined(windows) or defined(nimdoc):
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
|
|
@ -1080,6 +1085,11 @@ else:
|
|||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc setGlobalDispatcher*(disp: PDispatcher) =
|
||||
if not gDisp.isNil:
|
||||
assert gDisp.callbacks.len == 0
|
||||
gDisp = disp
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
|
|
|
|||
|
|
@ -595,6 +595,8 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
|
|||
raise newException(IOError,
|
||||
"Cannot find Nim standard library: Nim compiler not in PATH")
|
||||
gPrefixDir = binaryPath.splitPath().head.parentDir()
|
||||
if not dirExists(gPrefixDir / "lib"): gPrefixDir = ""
|
||||
|
||||
#msgs.writelnHook = proc (line: string) = log(line)
|
||||
myLog("START " & gProjectFull)
|
||||
|
||||
|
|
|
|||
17
tests/async/tasyncfilewrite.nim
Normal file
17
tests/async/tasyncfilewrite.nim
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
discard """
|
||||
output: '''string 1
|
||||
string 2
|
||||
string 3'''
|
||||
"""
|
||||
# bug #5532
|
||||
import os, asyncfile, asyncdispatch
|
||||
|
||||
removeFile("test.txt")
|
||||
let f = openAsync("test.txt", fmWrite)
|
||||
var futs = newSeq[Future[void]]()
|
||||
for i in 1..3:
|
||||
futs.add(f.write("string " & $i & "\n"))
|
||||
waitFor(all(futs))
|
||||
f.close()
|
||||
echo readFile("test.txt")
|
||||
|
||||
|
|
@ -235,6 +235,21 @@ block withKeyTest:
|
|||
except KeyError:
|
||||
discard
|
||||
|
||||
block takeTest:
|
||||
var t = initTable[string, int]()
|
||||
t["key"] = 123
|
||||
|
||||
var val = 0
|
||||
assert(t.take("key", val))
|
||||
assert(val == 123)
|
||||
|
||||
val = -1
|
||||
assert(not t.take("key", val))
|
||||
assert(val == -1)
|
||||
|
||||
assert(not t.take("otherkey", val))
|
||||
assert(val == -1)
|
||||
|
||||
proc orderedTableSortTest() =
|
||||
var t = initOrderedTable[string, int](2)
|
||||
for key, val in items(data): t[key] = val
|
||||
|
|
|
|||
|
|
@ -21,49 +21,67 @@ proc symToIdent(x: NimNode): NimNode =
|
|||
for c in x:
|
||||
result.add symToIdent(c)
|
||||
|
||||
# check getTypeInst and getTypeImpl for given symbol x
|
||||
macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
|
||||
# check that getTypeInst(x) equals inst0
|
||||
let inst = x.getTypeInst
|
||||
let impl = x.getTypeImpl
|
||||
let inst0r = inst0.symToIdent.treeRepr
|
||||
let instr = inst.symToIdent.treeRepr
|
||||
#echo inst0r
|
||||
let inst0r = inst0.symToIdent.treeRepr
|
||||
#echo instr
|
||||
#echo inst0r
|
||||
doAssert(instr == inst0r)
|
||||
|
||||
# check that getTypeImpl(x) is correct
|
||||
# if implX is nil then compare to inst0
|
||||
# else we expect implX to be a type definition
|
||||
# and we extract the implementation from that
|
||||
let impl = x.getTypeImpl
|
||||
var impl0 =
|
||||
if implX.kind == nnkNilLit: inst0
|
||||
else: implX[0][2]
|
||||
let impl0r = impl0.symToIdent.treerepr
|
||||
let implr = impl.symToIdent.treerepr
|
||||
#echo impl0r
|
||||
let impl0r = impl0.symToIdent.treerepr
|
||||
#echo implr
|
||||
#echo impl0r
|
||||
doAssert(implr == impl0r)
|
||||
template echoString(s:string) = echo s.replace("\n","\n ")
|
||||
|
||||
result = newStmtList()
|
||||
#template echoString(s: string) = echo s.replace("\n","\n ")
|
||||
#result.add getAst(echoString(" " & inst0.repr))
|
||||
#result.add getAst(echoString(" " & inst.repr))
|
||||
#result.add getAst(echoString(" " & impl0.repr))
|
||||
#result.add getAst(echoString(" " & impl.repr))
|
||||
|
||||
if recurse:
|
||||
template testDecl(n, m :typed) =
|
||||
# now test using a newly formed variable of type getTypeInst(x)
|
||||
template testDecl(n,m: typed) =
|
||||
testV(n, false):
|
||||
type _ = m
|
||||
result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
|
||||
|
||||
# test with a variable (instance) of type
|
||||
template testV(inst, recurse, impl) =
|
||||
block:
|
||||
#echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
|
||||
var x: inst
|
||||
testX(x, inst, recurse, impl)
|
||||
template testT(inst, recurse) =
|
||||
block:
|
||||
type myType = inst
|
||||
testV(myType, recurse):
|
||||
type _ = inst
|
||||
|
||||
# test with a newly created typedesc (myType)
|
||||
# using the passed type as the implementation
|
||||
template testT(impl, recurse) =
|
||||
block:
|
||||
type myType = impl
|
||||
testV(myType, recurse):
|
||||
type _ = impl
|
||||
|
||||
# test a built-in type whose instance is equal to the implementation
|
||||
template test(inst) =
|
||||
testT(inst, false)
|
||||
testV(inst, true, nil)
|
||||
template test(inst, impl) = testV(inst, true, impl)
|
||||
|
||||
# test a custom type with provided implementation
|
||||
template test(inst, impl) =
|
||||
testV(inst, true, impl)
|
||||
|
||||
type
|
||||
Model = object of RootObj
|
||||
|
|
@ -87,9 +105,12 @@ type
|
|||
value:T
|
||||
Foo[N:static[int],T] = object
|
||||
Bar[N:static[int],T] = object
|
||||
#baz:Foo[N+1,GenericObject[T]]
|
||||
#baz:Foo[N+1,GenericObject[T]] # currently fails
|
||||
baz:Foo[N,GenericObject[T]]
|
||||
|
||||
Generic[T] = seq[int]
|
||||
Concrete = Generic[int]
|
||||
|
||||
test(bool)
|
||||
test(char)
|
||||
test(int)
|
||||
|
|
@ -97,13 +118,17 @@ test(float)
|
|||
test(ptr int)
|
||||
test(ref int)
|
||||
test(array[1..10,Bar[2,Foo[3,float]]])
|
||||
test(array[MyEnum,Bar[2,Foo[3,float]]])
|
||||
test(distinct Bar[2,Foo[3,float]])
|
||||
test(tuple[a:int,b:Foo[-1,float]])
|
||||
#test(MyEnum):
|
||||
# type _ = enum
|
||||
# valueA, valueB, valueC
|
||||
test(set[MyEnum])
|
||||
test(seq[int])
|
||||
test(set[MyEnum])
|
||||
test(proc (a: int, b: Foo[2,float]))
|
||||
test(proc (a: int, b: Foo[2,float]): Bar[3,int])
|
||||
|
||||
test(MyEnum):
|
||||
type _ = enum
|
||||
valueA, valueB, valueC
|
||||
test(Bar[2,Foo[3,float]]):
|
||||
type _ = object
|
||||
baz: Foo[2, GenericObject[Foo[3, float]]]
|
||||
|
|
@ -118,8 +143,12 @@ test(Tree):
|
|||
value: int
|
||||
left: ref Tree
|
||||
right: ref Tree
|
||||
test(proc (a: int, b: Foo[2,float]))
|
||||
test(proc (a: int, b: Foo[2,float]): Bar[3,int])
|
||||
test(Concrete):
|
||||
type _ = Generic[int]
|
||||
test(Generic[int]):
|
||||
type _ = seq[int]
|
||||
test(Generic[float]):
|
||||
type _ = seq[int]
|
||||
|
||||
# bug #4862
|
||||
static:
|
||||
|
|
|
|||
|
|
@ -104,15 +104,17 @@ proc syncTest() =
|
|||
|
||||
client.close()
|
||||
|
||||
# Timeout test.
|
||||
client = newHttpClient(timeout = 1)
|
||||
try:
|
||||
resp = client.request("http://example.com/")
|
||||
doAssert false, "TimeoutError should have been raised."
|
||||
except TimeoutError:
|
||||
discard
|
||||
except:
|
||||
doAssert false, "TimeoutError should have been raised."
|
||||
when false:
|
||||
# Disabled for now because it causes troubles with AppVeyor
|
||||
# Timeout test.
|
||||
client = newHttpClient(timeout = 1)
|
||||
try:
|
||||
resp = client.request("http://example.com/")
|
||||
doAssert false, "TimeoutError should have been raised."
|
||||
except TimeoutError:
|
||||
discard
|
||||
except:
|
||||
doAssert false, "TimeoutError should have been raised."
|
||||
|
||||
proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
|
||||
let fd = newNativeSocket(AF_INET6)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,11 @@
|
|||
|
||||
import parseutils, strutils, os, osproc, streams, parsecfg
|
||||
|
||||
const
|
||||
cmdTemplate* = r"compiler" / "nim $target --lib:lib --hints:on -d:testing $options $file"
|
||||
|
||||
var compilerPrefix* = "compiler" / "nim "
|
||||
|
||||
proc cmdTemplate*(): string =
|
||||
compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
|
||||
|
||||
type
|
||||
TTestAction* = enum
|
||||
|
|
@ -100,7 +103,7 @@ proc specDefaults*(result: var TSpec) =
|
|||
result.outp = ""
|
||||
result.nimout = ""
|
||||
result.ccodeCheck = ""
|
||||
result.cmd = cmdTemplate
|
||||
result.cmd = cmdTemplate()
|
||||
result.line = 0
|
||||
result.column = 0
|
||||
result.tfile = ""
|
||||
|
|
@ -173,7 +176,7 @@ proc parseSpec*(filename: string): TSpec =
|
|||
raise newException(ValueError, "cannot interpret as a bool: " & e.value)
|
||||
of "cmd":
|
||||
if e.value.startsWith("nim "):
|
||||
result.cmd = "compiler" / e.value
|
||||
result.cmd = compilerPrefix & e.value[4..^1]
|
||||
else:
|
||||
result.cmd = e.value
|
||||
of "ccodecheck": result.ccodeCheck = e.value
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ Options:
|
|||
--failing only show failing/ignored tests
|
||||
--pedantic return non-zero status code if there are failures
|
||||
--targets:"c c++ js objc" run tests for specified targets (default: all)
|
||||
--nim:path use a particular nim executable (default: compiler/nim)
|
||||
""" % resultsFile
|
||||
|
||||
type
|
||||
|
|
@ -367,7 +368,7 @@ proc testNoSpec(r: var TResults, test: TTest) =
|
|||
# does not extract the spec because the file is not supposed to have any
|
||||
#let tname = test.name.addFileExt(".nim")
|
||||
inc(r.total)
|
||||
let given = callCompiler(cmdTemplate, test.name, test.options, test.target)
|
||||
let given = callCompiler(cmdTemplate(), test.name, test.options, test.target)
|
||||
r.addResult(test, "", given.msg, given.err)
|
||||
if given.err == reSuccess: inc(r.passed)
|
||||
|
||||
|
|
@ -376,7 +377,7 @@ proc testC(r: var TResults, test: TTest) =
|
|||
let tname = test.name.addFileExt(".c")
|
||||
inc(r.total)
|
||||
styledEcho "Processing ", fgCyan, extractFilename(tname)
|
||||
var given = callCCompiler(cmdTemplate, test.name & ".c", test.options, test.target)
|
||||
var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, test.target)
|
||||
if given.err != reSuccess:
|
||||
r.addResult(test, "", given.msg, given.err)
|
||||
elif test.action == actionRun:
|
||||
|
|
@ -424,6 +425,7 @@ proc main() =
|
|||
of "failing": optFailing = true
|
||||
of "pedantic": optPedantic = true
|
||||
of "targets": targets = parseTargets(p.val.string)
|
||||
of "nim": compilerPrefix = p.val.string
|
||||
else: quit Usage
|
||||
p.next()
|
||||
if p.kind != cmdArgument: quit Usage
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ when defined(windows):
|
|||
proc progress(status: DownloadStatus, progress: uint, total: uint,
|
||||
message: string) {.procvar, gcsafe.} =
|
||||
echo "Downloading " & url
|
||||
echo clamp(int(progress.BiggestInt*100 div total.BiggestInt), 0, 100), "%"
|
||||
let t = total.BiggestInt
|
||||
if t != 0:
|
||||
echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%"
|
||||
else:
|
||||
echo "0%"
|
||||
|
||||
downloadToFile(url, file, {optUseCache}, progress)
|
||||
echo "100%"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ Changes affecting backwards compatibility
|
|||
AST that is the same as what is used to define an enum. Previously the AST
|
||||
returned had a repeated ``EnumTy`` node and was missing the initial pragma
|
||||
node (which is currently empty for an enum).
|
||||
- ``macros.getTypeImpl`` now correctly returns the implementation for a symbol
|
||||
of type ``tyGenericBody``.
|
||||
- If the dispatcher parameter's value used in multi method is ``nil``,
|
||||
a ``NilError`` exception is raised. The old behavior was that the method
|
||||
would be a ``nop`` then.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue