Merge branch 'devel' into faster-nimsuggest
This commit is contained in:
commit
5d125ca716
29 changed files with 240 additions and 66 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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':
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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".}
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
19
tests/coroutines/twait.nim
Normal file
19
tests/coroutines/twait.nim
Normal 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()
|
||||||
1
tests/coroutines/twait.nim.cfg
Normal file
1
tests/coroutines/twait.nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
-d:nimCoroutines
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.* = "(.*?)""
|
||||||
|
|
||||||
|
check(smallData.findAll(expression) == newSeq[string]())
|
||||||
|
check(largeData.findAll(expression) == newSeq[string]())
|
||||||
|
|
|
||||||
8
tests/stdlib/tnativesockets.nim
Normal file
8
tests/stdlib/tnativesockets.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import nativesockets, unittest
|
||||||
|
|
||||||
|
suite "nativesockets":
|
||||||
|
test "getHostname":
|
||||||
|
let hostname = getHostname()
|
||||||
|
check hostname.len > 0
|
||||||
|
check hostname.len < 64
|
||||||
|
|
||||||
|
|
@ -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/
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue