Merge branch 'devel' into faster-nimsuggest

This commit is contained in:
Andreas Rumpf 2017-03-03 13:00:09 +01:00
commit 5d125ca716
29 changed files with 240 additions and 66 deletions

View file

@ -181,7 +181,7 @@ proc mangle*(name: string): string =
of '_': of '_':
# we generate names like 'foo_9' for scope disambiguations and so # we generate names like 'foo_9' for scope disambiguations and so
# disallow this here: # disallow this here:
if i < name.len-1 and name[i+1] in Digits: if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard discard
else: else:
add(result, c) add(result, c)

View file

@ -218,8 +218,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
of tyTuple: of tyTuple:
if inst: if inst:
result = newNodeX(nkTupleTy) result = newNodeX(nkTupleTy)
for s in t.n.sons: # only named tuples have a node, unnamed tuples don't
result.add newIdentDefs(s) if t.n.isNil:
for subType in t.sons:
result.add mapTypeToAst(subType, info)
else:
for s in t.n.sons:
result.add newIdentDefs(s)
else: else:
result = mapTypeToBracket("tuple", mTuple, t, info) result = mapTypeToBracket("tuple", mTuple, t, info)
of tySet: result = mapTypeToBracket("set", mSet, t, info) of tySet: result = mapTypeToBracket("set", mSet, t, info)

View file

@ -516,23 +516,23 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and
pcre.UTF8) > 0u32 pcre.UTF8) > 0u32
let strlen = if endpos == int.high: str.len else: endpos+1 let strlen = if endpos == int.high: str.len else: endpos+1
var offset = start var offset = start
var match: Option[RegexMatch] var match: Option[RegexMatch]
var neverMatched = true
while true: while true:
var flags = 0 var flags = 0
if match.isSome and if match.isSome and
match.get.matchBounds.a > match.get.matchBounds.b: match.get.matchBounds.a > match.get.matchBounds.b:
# 0-len match # 0-len match
flags = pcre.NOTEMPTY_ATSTART flags = pcre.NOTEMPTY_ATSTART
match = str.matchImpl(pattern, offset, endpos, flags) match = str.matchImpl(pattern, offset, endpos, flags)
if match.isNone: if match.isNone:
# either the end of the input or the string # either the end of the input or the string
# cannot be split here # cannot be split here - we also need to bail
if offset >= strlen: # if we've never matched and we've already tried to...
if offset >= strlen or neverMatched:
break break
if matchesCrLf and offset < (str.len - 1) and if matchesCrLf and offset < (str.len - 1) and
@ -546,11 +546,11 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
else: else:
offset += 1 offset += 1
else: else:
neverMatched = false
offset = match.get.matchBounds.b + 1 offset = match.get.matchBounds.b + 1
yield match.get yield match.get
proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
## Finds the given pattern in the string between the end and start ## Finds the given pattern in the string between the end and start
## positions. ## positions.

View file

@ -39,6 +39,7 @@ __clang__
# pragma GCC diagnostic ignored "-Wswitch-bool" # pragma GCC diagnostic ignored "-Wswitch-bool"
# pragma GCC diagnostic ignored "-Wmacro-redefined" # pragma GCC diagnostic ignored "-Wmacro-redefined"
# pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" # pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
# pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
#endif #endif
#if defined(_MSC_VER) #if defined(_MSC_VER)

View file

@ -870,7 +870,7 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
d.config.getOrDefault"doc.listing_end" % id) d.config.getOrDefault"doc.listing_end" % id)
return return
var codeLines = 1 + code.strip.countLines var codeLines = code.strip.countLines
assert codeLines > 0 assert codeLines > 0
result.beginTable = """<table class="line-nums-table"><tbody><tr><td class="blob-line-nums"><pre class="line-nums">""" result.beginTable = """<table class="line-nums-table"><tbody><tr><td class="blob-line-nums"><pre class="line-nums">"""
var line = params.startLine var line = params.startLine

View file

@ -167,8 +167,12 @@ type
callbacks: Deque[proc ()] callbacks: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase) {.inline.} =
while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt: #Process just part if timers at a step
var count = p.timers.len
let t = epochTime()
while count > 0 and t >= p.timers[0].finishAt:
p.timers.pop().fut.complete() p.timers.pop().fut.complete()
dec count
proc processPendingCallbacks(p: PDispatcherBase) = proc processPendingCallbacks(p: PDispatcherBase) =
while p.callbacks.len > 0: while p.callbacks.len > 0:

View file

@ -31,7 +31,7 @@
## ##
## waitFor server.serve(Port(8080), cb) ## waitFor server.serve(Port(8080), cb)
import tables, asyncnet, asyncdispatch, parseutils, uri, strutils, nativesockets import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
import httpcore import httpcore
export httpcore except parseHeader export httpcore except parseHeader
@ -209,7 +209,9 @@ proc processClient(client: AsyncSocket, address: string,
continue continue
else: else:
request.body = await client.recv(contentLength) request.body = await client.recv(contentLength)
assert request.body.len == contentLength if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
continue
elif request.reqMethod == HttpPost: elif request.reqMethod == HttpPost:
await request.respond(Http400, "Bad Request. No Content-Length.") await request.respond(Http400, "Bad Request. No Content-Length.")
continue continue
@ -241,7 +243,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
## specified address and port. ## specified address and port.
## ##
## When a request is made by a client the specified callback will be called. ## When a request is made by a client the specified callback will be called.
server.socket = newAsyncSocket(AF_INET6) server.socket = newAsyncSocket()
if server.reuseAddr: if server.reuseAddr:
server.socket.setSockOpt(OptReuseAddr, true) server.socket.setSockOpt(OptReuseAddr, true)
if server.reusePort: if server.reusePort:

View file

@ -49,7 +49,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
## `newline` is added. ## `newline` is added.
var total = ((len(s) + 2) div 3) * 4 var total = ((len(s) + 2) div 3) * 4
var numLines = (total + lineLen - 1) div lineLen var numLines = (total + lineLen - 1) div lineLen
if numLines > 0: inc(total, (numLines-1) * newLine.len) if numLines > 0: inc(total, (numLines - 1) * newLine.len)
result = newString(total) result = newString(total)
var i = 0 var i = 0
@ -66,7 +66,8 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
inc(r, 4) inc(r, 4)
inc(i, 3) inc(i, 3)
inc(currLine, 4) inc(currLine, 4)
if currLine >= lineLen and i != s.len-2: # avoid index out of bounds when lineLen == encoded length
if currLine >= lineLen and i != s.len-2 and r < total:
for x in items(newLine): for x in items(newLine):
result[r] = x result[r] = x
inc(r) inc(r)
@ -155,12 +156,17 @@ when isMainModule:
assert encode("asure.") == "YXN1cmUu" assert encode("asure.") == "YXN1cmUu"
assert encode("sure.") == "c3VyZS4=" assert encode("sure.") == "c3VyZS4="
const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
const testInputExpands = "++++++++++++++++++++++++++++++"
const longText = """Man is distinguished, not only by his reason, but by this const longText = """Man is distinguished, not only by his reason, but by this
singular passion from other animals, which is a lust of the mind, singular passion from other animals, which is a lust of the mind,
that by a perseverance of delight in the continued and indefatigable that by a perseverance of delight in the continued and indefatigable
generation of knowledge, exceeds the short vehemence of any carnal generation of knowledge, exceeds the short vehemence of any carnal
pleasure.""" pleasure."""
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
"asure.", longText] "asure.", longText, testInputExpandsTo76, testInputExpands]
for t in items(tests): for t in items(tests):
assert decode(encode(t)) == t assert decode(encode(t)) == t
assert decode(encode(t, lineLen=40)) == t
assert decode(encode(t, lineLen=76)) == t

View file

@ -130,6 +130,52 @@ proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool =
withLock t: withLock t:
hasKeyOrPutImpl(enlarge) hasKeyOrPutImpl(enlarge)
proc withKey*[A, B](t: var SharedTable[A, B], key: A,
mapper: proc(key: A, val: var B, pairExists: var bool)) =
## Computes a new mapping for the ``key`` with the specified ``mapper``
## procedure.
##
## The ``mapper`` takes 3 arguments:
## #. ``key`` - the current key, if it exists, or the key passed to
## ``withKey`` otherwise;
## #. ``val`` - the current value, if the key exists, or default value
## of the type otherwise;
## #. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change
## the mapping of the key or delete it from the table.
## When adding a value, make sure to set ``pairExists`` to ``true`` along
## with modifying the ``val``.
##
## The operation is performed atomically and other operations on the table
## will be blocked while the ``mapper`` is invoked, so it should be short and
## simple.
##
## Example usage:
##
## .. code-block:: nim
##
## # If value exists, decrement it.
## # If it becomes zero or less, delete the key
## t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool):
## if pairExists:
## dec v
## if v <= 0:
## pairExists = false
withLock t:
var hc: Hash
var index = rawGet(t, key, hc)
var pairExists = index >= 0
if pairExists:
mapper(t.data[index].key, t.data[index].val, pairExists)
if not pairExists:
delImplIdx(t, index)
else:
var val: B
mapper(key, val, pairExists)
if pairExists:
maybeRehashPutImpl(enlarge)
proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) = proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
withLock t: withLock t:

View file

@ -120,9 +120,7 @@ template default[T](t: typedesc[T]): T =
var v: T var v: T
v v
template delImpl() {.dirty.} = template delImplIdx(t, i) =
var hc: Hash
var i = rawGet(t, key, hc)
let msk = maxHash(t) let msk = maxHash(t)
if i >= 0: if i >= 0:
dec(t.counter) dec(t.counter)
@ -145,6 +143,11 @@ template delImpl() {.dirty.} =
else: else:
shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
template delImpl() {.dirty.} =
var hc: Hash
var i = rawGet(t, key, hc)
delImplIdx(t, i)
template clearImpl() {.dirty.} = template clearImpl() {.dirty.} =
for i in 0 .. <t.data.len: for i in 0 .. <t.data.len:
when compiles(t.data[i].hcode): # CountTable records don't contain a hcode when compiles(t.data[i].hcode): # CountTable records don't contain a hcode

View file

@ -23,7 +23,6 @@ when not nimCoroutines and not defined(nimdoc):
{.error: "Coroutines require -d:nimCoroutines".} {.error: "Coroutines require -d:nimCoroutines".}
import os import os
import macros
import lists import lists
include system/timers include system/timers
@ -120,27 +119,38 @@ const
CORO_FINISHED = 2 CORO_FINISHED = 2
type type
Stack = object Stack {.pure.} = object
top: pointer # Top of the stack. Pointer used for deallocating stack if we own it. top: pointer # Top of the stack. Pointer used for deallocating stack if we own it.
bottom: pointer # Very bottom of the stack, acts as unique stack identifier. bottom: pointer # Very bottom of the stack, acts as unique stack identifier.
size: int size: int
Coroutine = ref object Coroutine {.pure.} = object
execContext: Context execContext: Context
fn: proc() fn: proc()
state: int state: int
lastRun: Ticks lastRun: Ticks
sleepTime: float sleepTime: float
stack: Stack stack: Stack
reference: CoroutineRef
CoroutinePtr = ptr Coroutine
CoroutineRef* = ref object
## CoroutineRef holds a pointer to actual coroutine object. Public API always returns
## CoroutineRef instead of CoroutinePtr in order to allow holding a reference to coroutine
## object while it can be safely deallocated by coroutine scheduler loop. In this case
## Coroutine.reference.coro is set to nil. Public API checks for for it being nil and
## gracefully fails if it is nil.
coro: CoroutinePtr
CoroutineLoopContext = ref object CoroutineLoopContext = ref object
coroutines: DoublyLinkedList[Coroutine] coroutines: DoublyLinkedList[CoroutinePtr]
current: DoublyLinkedNode[Coroutine] current: DoublyLinkedNode[CoroutinePtr]
loop: Coroutine loop: Coroutine
var ctx {.threadvar.}: CoroutineLoopContext var ctx {.threadvar.}: CoroutineLoopContext
proc getCurrent(): Coroutine = proc getCurrent(): CoroutinePtr =
## Returns current executing coroutine object. ## Returns current executing coroutine object.
var node = ctx.current var node = ctx.current
if node != nil: if node != nil:
@ -151,7 +161,7 @@ proc initialize() =
## Initializes coroutine state of current thread. ## Initializes coroutine state of current thread.
if ctx == nil: if ctx == nil:
ctx = CoroutineLoopContext() ctx = CoroutineLoopContext()
ctx.coroutines = initDoublyLinkedList[Coroutine]() ctx.coroutines = initDoublyLinkedList[CoroutinePtr]()
ctx.loop = Coroutine() ctx.loop = Coroutine()
ctx.loop.state = CORO_EXECUTING ctx.loop.state = CORO_EXECUTING
when coroBackend == CORO_BACKEND_FIBERS: when coroBackend == CORO_BACKEND_FIBERS:
@ -159,7 +169,7 @@ proc initialize() =
proc runCurrentTask() proc runCurrentTask()
proc switchTo(current, to: Coroutine) = proc switchTo(current, to: CoroutinePtr) =
## Switches execution from `current` into `to` context. ## Switches execution from `current` into `to` context.
to.lastRun = getTicks() to.lastRun = getTicks()
# Update position of current stack so gc invoked from another stack knows how much to scan. # Update position of current stack so gc invoked from another stack knows how much to scan.
@ -192,7 +202,7 @@ proc suspend*(sleepTime: float=0) =
## Until then other coroutines are executed. ## Until then other coroutines are executed.
var current = getCurrent() var current = getCurrent()
current.sleepTime = sleepTime current.sleepTime = sleepTime
switchTo(current, ctx.loop) switchTo(current, addr(ctx.loop))
proc runCurrentTask() = proc runCurrentTask() =
## Starts execution of current coroutine and updates it's state through coroutine's life. ## Starts execution of current coroutine and updates it's state through coroutine's life.
@ -218,31 +228,33 @@ proc runCurrentTask() =
suspend(0) # Exit coroutine without returning from coroExecWithStack() suspend(0) # Exit coroutine without returning from coroExecWithStack()
doAssert false doAssert false
proc start*(c: proc(), stacksize: int=defaultStackSize) = proc start*(c: proc(), stacksize: int=defaultStackSize): CoroutineRef {.discardable.} =
## Schedule coroutine for execution. It does not run immediately. ## Schedule coroutine for execution. It does not run immediately.
if ctx == nil: if ctx == nil:
initialize() initialize()
var coro = Coroutine() var coro: CoroutinePtr
coro.fn = c
when coroBackend == CORO_BACKEND_FIBERS: when coroBackend == CORO_BACKEND_FIBERS:
coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine)))
coro.execContext = CreateFiberEx(stacksize, stacksize, coro.execContext = CreateFiberEx(stacksize, stacksize,
FIBER_FLAG_FLOAT_SWITCH, (proc(p: pointer): void {.stdcall.} = runCurrentTask()), nil) FIBER_FLAG_FLOAT_SWITCH, (proc(p: pointer): void {.stdcall.} = runCurrentTask()), nil)
coro.stack.size = stacksize coro.stack.size = stacksize
else: else:
var stack: pointer coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize))
while stack == nil: coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine))
stack = alloc0(stacksize) coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize)
coro.stack.top = stack
when coroBackend == CORO_BACKEND_UCONTEXT: when coroBackend == CORO_BACKEND_UCONTEXT:
discard getcontext(coro.execContext) discard getcontext(coro.execContext)
coro.execContext.uc_stack.ss_sp = cast[pointer](cast[ByteAddress](stack) + stacksize) coro.execContext.uc_stack.ss_sp = coro.stack.top
coro.execContext.uc_stack.ss_size = coro.stack.size coro.execContext.uc_stack.ss_size = stacksize
coro.execContext.uc_link = addr ctx.loop.execContext coro.execContext.uc_link = addr(ctx.loop.execContext)
makecontext(coro.execContext, runCurrentTask, 0) makecontext(coro.execContext, runCurrentTask, 0)
coro.fn = c
coro.stack.size = stacksize coro.stack.size = stacksize
coro.state = CORO_CREATED coro.state = CORO_CREATED
coro.reference = CoroutineRef(coro: coro)
ctx.coroutines.append(coro) ctx.coroutines.append(coro)
return coro.reference
proc run*() = proc run*() =
initialize() initialize()
@ -256,7 +268,7 @@ proc run*() =
var remaining = current.sleepTime - (float(getTicks() - current.lastRun) / 1_000_000_000) var remaining = current.sleepTime - (float(getTicks() - current.lastRun) / 1_000_000_000)
if remaining <= 0: if remaining <= 0:
# Save main loop context. Suspending coroutine will resume after this statement with # Save main loop context. Suspending coroutine will resume after this statement with
switchTo(ctx.loop, current) switchTo(addr(ctx.loop), current)
else: else:
if minDelay > 0 and remaining > 0: if minDelay > 0 and remaining > 0:
minDelay = min(remaining, minDelay) minDelay = min(remaining, minDelay)
@ -269,14 +281,14 @@ proc run*() =
# If first coroutine ends then `prev` is nil even if more coroutines # If first coroutine ends then `prev` is nil even if more coroutines
# are to be scheduled. # are to be scheduled.
next = ctx.current.next next = ctx.current.next
current.reference.coro = nil
ctx.coroutines.remove(ctx.current) ctx.coroutines.remove(ctx.current)
GC_removeStack(current.stack.bottom) GC_removeStack(current.stack.bottom)
when coroBackend == CORO_BACKEND_FIBERS: when coroBackend == CORO_BACKEND_FIBERS:
DeleteFiber(current.execContext) DeleteFiber(current.execContext)
else: else:
dealloc(current.stack.top) dealloc(current.stack.top)
current.stack.top = nil dealloc(current)
current.stack.bottom = nil
ctx.current = next ctx.current = next
elif ctx.current == nil or ctx.current.next == nil: elif ctx.current == nil or ctx.current.next == nil:
ctx.current = ctx.coroutines.head ctx.current = ctx.coroutines.head
@ -284,13 +296,10 @@ proc run*() =
else: else:
ctx.current = ctx.current.next ctx.current = ctx.current.next
proc alive*(c: proc()): bool = proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED
## Returns ``true`` if coroutine has not returned, ``false`` otherwise. ## Returns ``true`` if coroutine has not returned, ``false`` otherwise.
for coro in items(ctx.coroutines):
if coro.fn == c:
return coro.state != CORO_FINISHED
proc wait*(c: proc(), interval=0.01) = proc wait*(c: CoroutineRef, interval=0.01) =
## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often. ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often.
while alive(c): while alive(c):
suspend(interval) suspend(interval)

View file

@ -337,8 +337,16 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
when not defined(ssl): when not defined(ssl):
type SSLContext = ref object type SSLContext = ref object
var defaultSSLContext {.threadvar.}: SSLContext var defaultSSLContext {.threadvar.}: SSLContext
when defined(ssl): when defined(ssl):
defaultSSLContext = newContext(verifyMode = CVerifyNone) defaultSSLContext = newContext(verifyMode = CVerifyNone)
template contextOrDefault(ctx: SSLContext): SSLContext =
var result = ctx
if ctx == nil:
if defaultSSLContext == nil:
defaultSSLContext = newContext(verifyMode = CVerifyNone)
result = defaultSSLContext
result
proc newProxy*(url: string, auth = ""): Proxy = proc newProxy*(url: string, auth = ""): Proxy =
## Constructs a new ``TProxy`` object. ## Constructs a new ``TProxy`` object.
@ -805,7 +813,7 @@ proc newHttpClient*(userAgent = defUserAgent,
result.bodyStream = newStringStream() result.bodyStream = newStringStream()
result.getBody = true result.getBody = true
when defined(ssl): when defined(ssl):
result.sslContext = sslContext result.sslContext = contextOrDefault(sslContext)
type type
AsyncHttpClient* = HttpClientBase[AsyncSocket] AsyncHttpClient* = HttpClientBase[AsyncSocket]
@ -837,7 +845,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
result.bodyStream = newFutureStream[string]("newAsyncHttpClient") result.bodyStream = newFutureStream[string]("newAsyncHttpClient")
result.getBody = true result.getBody = true
when defined(ssl): when defined(ssl):
result.sslContext = sslContext result.sslContext = contextOrDefault(sslContext)
proc close*(client: HttpClient | AsyncHttpClient) = proc close*(client: HttpClient | AsyncHttpClient) =
## Closes any connections held by the HTTP client. ## Closes any connections held by the HTTP client.

View file

@ -139,7 +139,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
result.add(arg) result.add(arg)
method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
raises: [Exception], raises: [Exception], gcsafe,
tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} = tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} =
## Override this method in custom loggers. Default implementation does ## Override this method in custom loggers. Default implementation does
## nothing. ## nothing.

View file

@ -374,6 +374,22 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
proc getHostname*(): string {.tags: [ReadIOEffect].} =
## Returns the local hostname (not the FQDN)
# https://tools.ietf.org/html/rfc1035#section-2.3.1
# https://tools.ietf.org/html/rfc2181#section-11
const size = 64
result = newString(size)
when useWinVersion:
let success = winlean.getHostname(result, size)
else:
# Posix
let success = posix.getHostname(result, size)
if success != 0.cint:
raiseOSError(osLastError())
let x = len(cstring(result))
result.setLen(x)
proc getSockDomain*(socket: SocketHandle): Domain = proc getSockDomain*(socket: SocketHandle): Domain =
## returns the socket's domain (AF_INET or AF_INET6). ## returns the socket's domain (AF_INET or AF_INET6).
var name: SockAddr var name: SockAddr

View file

@ -767,20 +767,16 @@ proc splitLines*(s: string): seq[string] {.noSideEffect,
proc countLines*(s: string): int {.noSideEffect, proc countLines*(s: string): int {.noSideEffect,
rtl, extern: "nsuCountLines".} = rtl, extern: "nsuCountLines".} =
## Returns the number of new line separators in the string `s`. ## Returns the number of lines in the string `s`.
## ##
## This is the same as ``len(splitLines(s))``, but much more efficient ## This is the same as ``len(splitLines(s))``, but much more efficient
## because it doesn't modify the string creating temporal objects. Every ## because it doesn't modify the string creating temporal objects. Every
## `character literal <manual.html#character-literals>`_ newline combination ## `character literal <manual.html#character-literals>`_ newline combination
## (CR, LF, CR-LF) is supported. ## (CR, LF, CR-LF) is supported.
## ##
## Despite its name this proc might not actually return the *number of lines* ## In this context, a line is any string seperated by a newline combination.
## in `s` because the concept of what a line is can vary. For example, a ## A line can be an empty string.
## string like ``Hello world`` is a line of text, but the proc will return a var i = 1
## value of zero because there are no newline separators. Also, text editors
## usually don't count trailing newline characters in a text file as a new
## empty line, but this proc will.
var i = 0
while i < s.len: while i < s.len:
case s[i] case s[i]
of '\c': of '\c':

View file

@ -47,6 +47,8 @@
## Tests can be nested, however failure of a nested test will not mark the ## Tests can be nested, however failure of a nested test will not mark the
## parent test as failed. Setup and teardown are inherited. Setup can be ## parent test as failed. Setup and teardown are inherited. Setup can be
## overridden locally. ## overridden locally.
## Compiled test files return the number of failed test as exit code, while
## nim c -r <testfile.nim> exits with 0 or 1
import import
macros macros

View file

@ -13,6 +13,8 @@
## ##
## **Note:** The current implementation of message passing is slow and does ## **Note:** The current implementation of message passing is slow and does
## not work with cyclic data structures. ## not work with cyclic data structures.
## **Note:** Channels cannot be passed between threads. Use globals or pass
## them by `ptr`.
when not declared(NimString): when not declared(NimString):
{.error: "You must not import this module explicitly".} {.error: "You must not import this module explicitly".}

View file

@ -290,9 +290,6 @@ elif stackIncreases:
template forEachStackSlot(gch, gcMark: untyped) {.dirty.} = template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
var registers {.noinit.}: C_JmpBuf var registers {.noinit.}: C_JmpBuf
# sp will traverse the JMP_BUF as well (jmp_buf size is added,
# otherwise sp would be below the registers structure).
var regAddr = addr(registers) +% jmpbufSize
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
for stack in gch.stack.items(): for stack in gch.stack.items():

View file

@ -138,8 +138,12 @@ type
callbacks: Deque[proc ()] callbacks: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase) {.inline.} =
while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt: #Process just part if timers at a step
var count = p.timers.len
let t = epochTime()
while count > 0 and t >= p.timers[0].finishAt:
p.timers.pop().fut.complete() p.timers.pop().fut.complete()
dec count
proc processPendingCallbacks(p: PDispatcherBase) = proc processPendingCallbacks(p: PDispatcherBase) =
while p.callbacks.len > 0: while p.callbacks.len > 0:

View file

@ -542,6 +542,9 @@ proc gethostbyaddr*(ip: ptr InAddr, len: cuint, theType: cint): ptr Hostent {.
proc gethostbyname*(name: cstring): ptr Hostent {. proc gethostbyname*(name: cstring): ptr Hostent {.
stdcall, importc: "gethostbyname", dynlib: ws2dll.} stdcall, importc: "gethostbyname", dynlib: ws2dll.}
proc gethostname*(hostname: cstring, len: cint): cint {.
stdcall, importc: "gethostname", dynlib: ws2dll.}
proc socket*(af, typ, protocol: cint): SocketHandle {. proc socket*(af, typ, protocol: cint): SocketHandle {.
stdcall, importc: "socket", dynlib: ws2dll.} stdcall, importc: "socket", dynlib: ws2dll.}

View file

@ -1,8 +1,9 @@
discard """ discard """
cmd: "nim c --threads:on $file"
output: '''true''' output: '''true'''
""" """
import hashes, tables import hashes, tables, sharedtables
const const
data = { data = {
@ -211,6 +212,29 @@ block clearCountTableTest:
t.clear() t.clear()
assert t.len() == 0 assert t.len() == 0
block withKeyTest:
var t = initSharedTable[int, int]()
t.withKey(1) do (k: int, v: var int, pairExists: var bool):
assert(v == 0)
pairExists = true
v = 42
assert(t.mget(1) == 42)
t.withKey(1) do (k: int, v: var int, pairExists: var bool):
assert(v == 42)
pairExists = false
try:
discard t.mget(1)
assert(false, "KeyError expected")
except KeyError:
discard
t.withKey(2) do (k: int, v: var int, pairExists: var bool):
pairExists = false
try:
discard t.mget(2)
assert(false, "KeyError expected")
except KeyError:
discard
proc orderedTableSortTest() = proc orderedTableSortTest() =
var t = initOrderedTable[string, int](2) var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val for key, val in items(data): t[key] = val

View file

@ -0,0 +1,19 @@
discard """
output: "Exit 1\nExit 2"
"""
import coro
var coro1: CoroutineRef
proc testCoroutine1() =
for i in 0..<10:
suspend(0)
echo "Exit 1"
proc testCoroutine2() =
coro1.wait()
echo "Exit 2"
coro1 = coro.start(testCoroutine1)
coro.start(testCoroutine2)
run()

View file

@ -0,0 +1 @@
-d:nimCoroutines

View file

@ -120,3 +120,7 @@ test(Tree):
right: ref Tree right: ref Tree
test(proc (a: int, b: Foo[2,float])) test(proc (a: int, b: Foo[2,float]))
test(proc (a: int, b: Foo[2,float]): Bar[3,int]) test(proc (a: int, b: Foo[2,float]): Bar[3,int])
# bug #4862
static:
discard typedesc[(int, int)].getTypeImpl

View file

@ -240,7 +240,7 @@ proc add*(m: PMessageArea, msg: ScChat) =
of CPriv, CSystem: mmm.color = Green of CPriv, CSystem: mmm.color = Green
of CError: mmm.color = Red of CError: mmm.color = Red
mmm.lines = countLines(mmm.text)+1 mmm.lines = countLines(mmm.text)
m.messages.add mmm m.messages.add mmm
update m update m

View file

@ -1,6 +1,7 @@
import unittest, sequtils import unittest, sequtils
import nre except toSeq import nre except toSeq
import optional_nonstrict import optional_nonstrict
import times, strutils
suite "find": suite "find":
test "find text": test "find text":
@ -25,3 +26,16 @@ suite "find":
check("word word".findAll(re"\b") == @["", "", "", ""]) check("word word".findAll(re"\b") == @["", "", "", ""])
check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""]) check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""])
check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""]) check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""])
test "bail early":
## we expect nothing to be found and we should be bailing out early which means that
## the timing difference between searching in small and large data should be well
## within a tolerance margin
const small = 10
const large = 1000
var smallData = repeat("url.sequence = \"http://whatever.com/jwhrejrhrjrhrjhrrjhrjrhrjrh\" ", small)
var largeData = repeat("url.sequence = \"http://whatever.com/jwhrejrhrjrhrjhrrjhrjrhrjrh\" ", large)
var expression = re"^url.* = &#34;(.*?)&#34;"
check(smallData.findAll(expression) == newSeq[string]())
check(largeData.findAll(expression) == newSeq[string]())

View file

@ -0,0 +1,8 @@
import nativesockets, unittest
suite "nativesockets":
test "getHostname":
let hostname = getHostname()
check hostname.len > 0
check hostname.len < 64

View file

@ -125,7 +125,7 @@ General FAQ
What about editor support? What about editor support?
-------------------------- --------------------------
- Nim IDE: https://github.com/nim-lang/Aporia - Native Nim Editor: https://github.com/nim-lang/Aporia
- Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=kosz78.nim - Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=kosz78.nim
- Emacs: https://github.com/nim-lang/nim-mode - Emacs: https://github.com/nim-lang/nim-mode
- Vim: https://github.com/zah/nimrod.vim/ - Vim: https://github.com/zah/nimrod.vim/

View file

@ -8,7 +8,7 @@ Authors: "Andreas Rumpf and contributors"
# Underscores are replaced with a space. # Underscores are replaced with a space.
# Everything after ; is the ID # Everything after ; is the ID
Community: "community.html;link_forum" Community: "community.html;link_forum"
Aporia_IDE: "https://github.com/nim-lang/Aporia;link_aporia" ; Aporia_IDE: "https://github.com/nim-lang/Aporia;link_aporia"
GitHub_Repo: "http://github.com/nim-lang/Nim;link_github" GitHub_Repo: "http://github.com/nim-lang/Nim;link_github"