merged upstream master
This commit is contained in:
commit
81a3585872
127 changed files with 4440 additions and 1496 deletions
|
|
@ -45,8 +45,16 @@ type
|
|||
nnkConstDef, nnkTypeDef,
|
||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
||||
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
||||
nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
|
||||
nnkDiscardStmt, nnkStmtList,
|
||||
|
||||
nnkImportStmt,
|
||||
nnkImportExceptStmt,
|
||||
nnkExportStmt,
|
||||
nnkExportExceptStmt,
|
||||
nnkFromStmt,
|
||||
nnkIncludeStmt,
|
||||
|
||||
nnkBindStmt, nnkMixinStmt,
|
||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta, Andreas Rumpf
|
||||
# (c) Copyright 2013 Dominik Picheta, Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -139,12 +139,15 @@ proc invokeNewSeq*(x: TAny, len: int) =
|
|||
var z = newSeq(x.rawType, len)
|
||||
genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc extendSeq*(x: TAny, elems = 1) =
|
||||
## performs ``setLen(x, x.len+elems)``. `x` needs to represent a ``seq``.
|
||||
proc extendSeq*(x: TAny) =
|
||||
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
|
||||
assert x.rawType.kind == tySequence
|
||||
var y = cast[ptr PGenSeq](x.value)[]
|
||||
var z = incrSeq(y, x.rawType.base.size * elems)
|
||||
genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
var z = incrSeq(y, x.rawType.base.size)
|
||||
# 'incrSeq' already freed the memory for us and copied over the RC!
|
||||
# So we simply copy the raw pointer into 'x.value':
|
||||
cast[ppointer](x.value)[] = z
|
||||
#genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc setObjectRuntimeType*(x: TAny) =
|
||||
## this needs to be called to set `x`'s runtime object type field.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
## Currently only requesting URLs is implemented. The implementation depends
|
||||
## on the libcurl library!
|
||||
##
|
||||
## **Deprecated since version 0.8.8:** Use the ``httpclient`` module instead.
|
||||
## **Deprecated since version 0.8.8:** Use the
|
||||
## `httpclient <httpclient.html>`_ module instead.
|
||||
##
|
||||
|
||||
{.deprecated.}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ import sockets, os
|
|||
## that in the future this type's fields will not be exported therefore breaking
|
||||
## your code.
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
##
|
||||
## Asynchronous sockets
|
||||
## ====================
|
||||
##
|
||||
|
|
@ -123,6 +126,7 @@ type
|
|||
handleTask*: proc (s: PAsyncSocket) {.closure.}
|
||||
|
||||
lineBuffer: TaintedString ## Temporary storage for ``recvLine``
|
||||
sendBuffer: string ## Temporary storage for ``send``
|
||||
sslNeedAccept: bool
|
||||
proto: TProtocol
|
||||
deleg: PDelegate
|
||||
|
|
@ -152,6 +156,7 @@ proc newAsyncSocket(): PAsyncSocket =
|
|||
result.handleTask = (proc (s: PAsyncSocket) = nil)
|
||||
|
||||
result.lineBuffer = "".TaintedString
|
||||
result.sendBuffer = ""
|
||||
|
||||
proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
protocol: TProtocol = IPPROTO_TCP,
|
||||
|
|
@ -222,10 +227,22 @@ proc asyncSockHandleWrite(h: PObject) =
|
|||
else:
|
||||
PAsyncSocket(h).deleg.mode = fmReadWrite
|
||||
else:
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
if PAsyncSocket(h).sendBuffer != "":
|
||||
let sock = PAsyncSocket(h)
|
||||
let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
|
||||
assert bytesSent > 0
|
||||
if bytesSent != sock.sendBuffer.len:
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
|
||||
elif bytesSent == sock.sendBuffer.len:
|
||||
sock.sendBuffer = ""
|
||||
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
else:
|
||||
PAsyncSocket(h).deleg.mode = fmRead
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
else:
|
||||
PAsyncSocket(h).deleg.mode = fmRead
|
||||
|
||||
when defined(ssl):
|
||||
proc asyncSockDoHandshake(h: PObject) =
|
||||
|
|
@ -337,7 +354,8 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
|
|||
# deleg.open is set in ``toDelegate``.
|
||||
|
||||
client.socket = c
|
||||
client.lineBuffer = ""
|
||||
client.lineBuffer = "".TaintedString
|
||||
client.sendBuffer = ""
|
||||
client.info = SockConnected
|
||||
|
||||
proc accept*(server: PAsyncSocket, client: var PAsyncSocket) =
|
||||
|
|
@ -422,6 +440,9 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool =
|
|||
## sockets properly. This function guarantees that ``line`` is a full line,
|
||||
## if this function can only retrieve some data; it will save this data and
|
||||
## add it to the result when a full line is retrieved.
|
||||
##
|
||||
## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL
|
||||
## exception if an error occurs.
|
||||
setLen(line.string, 0)
|
||||
var dataReceived = "".TaintedString
|
||||
var ret = s.socket.recvLineAsync(dataReceived)
|
||||
|
|
@ -440,8 +461,29 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool =
|
|||
of RecvDisconnected:
|
||||
result = true
|
||||
of RecvFail:
|
||||
s.SocketError(async = true)
|
||||
result = false
|
||||
|
||||
proc send*(sock: PAsyncSocket, data: string) =
|
||||
## Sends ``data`` to socket ``sock``. This is basically a nicer implementation
|
||||
## of ``sockets.sendAsync``.
|
||||
##
|
||||
## If ``data`` cannot be sent immediately it will be buffered and sent
|
||||
## when ``sock`` becomes writeable (during the ``handleWrite`` event).
|
||||
## It's possible that only a part of ``data`` will be sent immediately, while
|
||||
## the rest of it will be buffered and sent later.
|
||||
if sock.sendBuffer.len != 0:
|
||||
sock.sendBuffer.add(data)
|
||||
return
|
||||
let bytesSent = sock.socket.sendAsync(data)
|
||||
assert bytesSent >= 0
|
||||
if bytesSent == 0:
|
||||
sock.sendBuffer.add(data)
|
||||
sock.deleg.mode = fmReadWrite
|
||||
elif bytesSent != data.len:
|
||||
sock.sendBuffer.add(data[bytesSent .. -1])
|
||||
sock.deleg.mode = fmReadWrite
|
||||
|
||||
proc timeValFromMilliseconds(timeout = 500): TTimeVal =
|
||||
if timeout != -1:
|
||||
var seconds = timeout div 1000
|
||||
|
|
@ -556,7 +598,9 @@ when isMainModule:
|
|||
|
||||
proc testRead(s: PAsyncSocket, no: int) =
|
||||
echo("Reading! " & $no)
|
||||
var data = s.getSocket.recv()
|
||||
var data = ""
|
||||
if not s.recvLine(data):
|
||||
OSError()
|
||||
if data == "":
|
||||
echo("Closing connection. " & $no)
|
||||
s.close()
|
||||
|
|
|
|||
|
|
@ -190,6 +190,11 @@ proc `$`*(s: TIntSet): string =
|
|||
## The `$` operator for int sets.
|
||||
dollarImpl()
|
||||
|
||||
proc empty*(s: TIntSet): bool {.inline.} =
|
||||
## returns true if `s` is empty. This is safe to call even before
|
||||
## the set has been initialized with `initIntSet`.
|
||||
result = s.counter == 0
|
||||
|
||||
when isMainModule:
|
||||
var x = initIntSet()
|
||||
x.incl(1)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,16 @@
|
|||
|
||||
## :Author: Alex Mitchell
|
||||
##
|
||||
## This module implements operations for the built-in `seq`:idx: type
|
||||
## which were inspired by functional programming languages.
|
||||
## This module implements operations for the built-in `seq`:idx: type which
|
||||
## were inspired by functional programming languages. If you are looking for
|
||||
## the typical `map` function which applies a function to every element in a
|
||||
## sequence, it already exists in the `system <system.html>`_ module in both
|
||||
## mutable and immutable styles.
|
||||
##
|
||||
## Also, for functional style programming you may want to pass `anonymous procs
|
||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||
## which is more convenient in certain situations.
|
||||
##
|
||||
## **Note**: This interface will change as soon as the compiler supports
|
||||
## closures and proper coroutines.
|
||||
|
|
@ -19,7 +27,17 @@ when not defined(nimhygiene):
|
|||
{.pragma: dirty.}
|
||||
|
||||
proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||
## Takes several sequences' items and returns them inside of one sequence.
|
||||
## Takes several sequences' items and returns them inside a new sequence.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## s1 = @[1, 2, 3]
|
||||
## s2 = @[4, 5]
|
||||
## s3 = @[6, 7]
|
||||
## total = concat(s1, s2, s3)
|
||||
## assert total == @[1, 2, 3, 4, 5, 6, 7]
|
||||
var L = 0
|
||||
for seqitm in items(seqs): inc(L, len(seqitm))
|
||||
newSeq(result, L)
|
||||
|
|
@ -30,14 +48,42 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
|||
inc(i)
|
||||
|
||||
proc distnct*[T](seq1: seq[T]): seq[T] =
|
||||
## Removes duplicates from a sequence and returns it.
|
||||
## Returns a new sequence without duplicates.
|
||||
##
|
||||
## This proc is `misspelled` on purpose to avoid a clash with the keyword
|
||||
## ``distinct`` used to `define a derived type incompatible with its base
|
||||
## type <manual.html#distinct-type>`_. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
|
||||
## dup2 = @["a", "a", "c", "d", "d"]
|
||||
## unique1 = distnct(dup1)
|
||||
## unique2 = distnct(dup2)
|
||||
## assert unique1 == @[1, 3, 4, 2, 8]
|
||||
## assert unique2 == @["a", "c", "d"]
|
||||
result = @[]
|
||||
for itm in items(seq1):
|
||||
if not result.contains(itm): result.add(itm)
|
||||
|
||||
proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
|
||||
## Combines two sequences. If one sequence is too short,
|
||||
## the remaining items in the longer sequence are discarded.
|
||||
## Returns a new sequence with a combination of the two input sequences.
|
||||
##
|
||||
## For convenience you can access the returned tuples through the named
|
||||
## fields `a` and `b`. If one sequence is shorter, the remaining items in the
|
||||
## longer sequence are discarded. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## short = @[1, 2, 3]
|
||||
## long = @[6, 5, 4, 3, 2, 1]
|
||||
## words = @["one", "two", "three"]
|
||||
## zip1 = zip(short, long)
|
||||
## zip2 = zip(short, words)
|
||||
## assert zip1 == @[(1, 6), (2, 5), (3, 4)]
|
||||
## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
|
||||
## assert zip1[2].b == 4
|
||||
## assert zip2[2].b == "three"
|
||||
var m = min(seq1.len, seq2.len)
|
||||
newSeq(result, m)
|
||||
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
|
||||
|
|
@ -45,21 +91,124 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
|
|||
iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
|
||||
## Iterates through a sequence and yields every item that fulfills the
|
||||
## predicate.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||
## echo($n)
|
||||
## # echoes 4, 8, 4 in separate lines
|
||||
for i in countup(0, len(seq1) -1):
|
||||
var item = seq1[i]
|
||||
if pred(item): yield seq1[i]
|
||||
|
||||
proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
|
||||
## Returns all items in a sequence that fulfilled the predicate.
|
||||
## Returns a new sequence with all the items that fulfilled the predicate.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## colors = @["red", "yellow", "black"]
|
||||
## f1 = filter(colors, proc(x: string): bool = x.len < 6)
|
||||
## f2 = filter(colors) do (x: string) -> bool : x.len > 5
|
||||
## assert f1 == @["red", "black"]
|
||||
## assert f2 == @["yellow"]
|
||||
accumulateResult(filter(seq1, pred))
|
||||
|
||||
template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} =
|
||||
## Finds a specific item in a sequence as long as the
|
||||
## predicate returns true. The predicate needs to be an expression
|
||||
## containing ``it``: ``filterIt("abcxyz", it == 'x')``.
|
||||
block:
|
||||
var result: type(seq1) = @[]
|
||||
for it in items(seq1):
|
||||
if pred: result.add(it)
|
||||
result
|
||||
## Returns a new sequence with all the items that fulfilled the predicate.
|
||||
##
|
||||
## Unlike the `proc` version, the predicate needs to be an expression using
|
||||
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
|
||||
## acceptable = filterIt(temperatures, it < 50 and it > -10)
|
||||
## assert acceptable == @[-2.0, 24.5, 44.31]
|
||||
var result {.gensym.}: type(seq1) = @[]
|
||||
for it in items(seq1):
|
||||
if pred: result.add(it)
|
||||
result
|
||||
|
||||
template toSeq*(iter: expr): expr {.immediate.} =
|
||||
## Transforms any iterator into a sequence.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||
## if x mod 2 == 1:
|
||||
## result = true)
|
||||
## assert odd_numbers == @[1, 3, 5, 7, 9]
|
||||
##
|
||||
var result {.gensym.}: seq[type(iter)] = @[]
|
||||
for x in iter: add(result, x)
|
||||
result
|
||||
|
||||
when isMainModule:
|
||||
import strutils
|
||||
proc toStr(x: int): string {.procvar.} = $x
|
||||
# concat test
|
||||
let
|
||||
s1 = @[1, 2, 3]
|
||||
s2 = @[4, 5]
|
||||
s3 = @[6, 7]
|
||||
total = concat(s1, s2, s3)
|
||||
assert total == @[1, 2, 3, 4, 5, 6, 7]
|
||||
|
||||
# duplicates test
|
||||
let
|
||||
dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
|
||||
dup2 = @["a", "a", "c", "d", "d"]
|
||||
unique1 = distnct(dup1)
|
||||
unique2 = distnct(dup2)
|
||||
assert unique1 == @[1, 3, 4, 2, 8]
|
||||
assert unique2 == @["a", "c", "d"]
|
||||
|
||||
# zip test
|
||||
let
|
||||
short = @[1, 2, 3]
|
||||
long = @[6, 5, 4, 3, 2, 1]
|
||||
words = @["one", "two", "three"]
|
||||
zip1 = zip(short, long)
|
||||
zip2 = zip(short, words)
|
||||
assert zip1 == @[(1, 6), (2, 5), (3, 4)]
|
||||
assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
|
||||
assert zip1[2].b == 4
|
||||
assert zip2[2].b == "three"
|
||||
|
||||
# filter proc test
|
||||
let
|
||||
colors = @["red", "yellow", "black"]
|
||||
f1 = filter(colors, proc(x: string): bool = x.len < 6)
|
||||
f2 = filter(colors) do (x: string) -> bool : x.len > 5
|
||||
assert f1 == @["red", "black"]
|
||||
assert f2 == @["yellow"]
|
||||
|
||||
# filter iterator test
|
||||
let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||
echo($n)
|
||||
# echoes 4, 8, 4 in separate lines
|
||||
|
||||
# filterIt test
|
||||
let
|
||||
temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
|
||||
acceptable = filterIt(temperatures, it < 50 and it > -10)
|
||||
assert acceptable == @[-2.0, 24.5, 44.31]
|
||||
|
||||
# toSeq test
|
||||
let
|
||||
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||
if x mod 2 == 1:
|
||||
result = true)
|
||||
assert odd_numbers == @[1, 3, 5, 7, 9]
|
||||
|
||||
echo "Finished doc tests"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -308,6 +308,52 @@ proc `$`*[A, B](t: TOrderedTable[A, B]): string =
|
|||
## The `$` operator for ordered hash tables.
|
||||
dollarImpl()
|
||||
|
||||
proc sort*[A, B](t: var TOrderedTable[A, B],
|
||||
cmp: proc (x,y: tuple[key: A, val: B]): int) =
|
||||
## sorts `t` according to `cmp`. This modifies the internal list
|
||||
## that kept the insertion order, so insertion order is lost after this
|
||||
## call but key lookup and insertions remain possible after `sort` (in
|
||||
## contrast to the `sort` for count tables).
|
||||
var list = t.first
|
||||
var
|
||||
p, q, e, tail, oldhead: int
|
||||
nmerges, psize, qsize, i: int
|
||||
if t.counter == 0: return
|
||||
var insize = 1
|
||||
while true:
|
||||
p = list; oldhead = list
|
||||
list = -1; tail = -1; nmerges = 0
|
||||
while p >= 0:
|
||||
inc(nmerges)
|
||||
q = p
|
||||
psize = 0
|
||||
i = 0
|
||||
while i < insize:
|
||||
inc(psize)
|
||||
q = t.data[q].next
|
||||
if q < 0: break
|
||||
inc(i)
|
||||
qsize = insize
|
||||
while psize > 0 or (qsize > 0 and q >= 0):
|
||||
if psize == 0:
|
||||
e = q; q = t.data[q].next; dec(qsize)
|
||||
elif qsize == 0 or q < 0:
|
||||
e = p; p = t.data[p].next; dec(psize)
|
||||
elif cmp((t.data[p].key, t.data[p].val),
|
||||
(t.data[q].key, t.data[q].val)) <= 0:
|
||||
e = p; p = t.data[p].next; dec(psize)
|
||||
else:
|
||||
e = q; q = t.data[q].next; dec(qsize)
|
||||
if tail >= 0: t.data[tail].next = e
|
||||
else: list = e
|
||||
tail = e
|
||||
p = q
|
||||
t.data[tail].next = -1
|
||||
if nmerges <= 1: break
|
||||
insize = insize * 2
|
||||
t.first = list
|
||||
t.last = tail
|
||||
|
||||
# ------------------------------ count tables -------------------------------
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -18,6 +18,10 @@ proc LoadLib*(path: string): TLibHandle
|
|||
## loads a library from `path`. Returns nil if the library could not
|
||||
## be loaded.
|
||||
|
||||
proc LoadLib*(): TLibHandle
|
||||
## gets the handle from the current executable. Returns nil if the
|
||||
## library could not be loaded.
|
||||
|
||||
proc UnloadLib*(lib: TLibHandle)
|
||||
## unloads the library `lib`
|
||||
|
||||
|
|
@ -57,6 +61,7 @@ when defined(posix):
|
|||
importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
||||
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
||||
proc UnloadLib(lib: TLibHandle) = dlclose(lib)
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
return dlsym(lib, name)
|
||||
|
|
@ -78,6 +83,8 @@ elif defined(windows) or defined(dos):
|
|||
|
||||
proc LoadLib(path: string): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(path))
|
||||
proc LoadLib(): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
|
|
|
|||
|
|
@ -254,6 +254,8 @@ when defined(windows):
|
|||
else:
|
||||
when defined(haiku):
|
||||
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
|
||||
elif defined(macosx):
|
||||
const iconvDll = "libiconv.dylib"
|
||||
else:
|
||||
const iconvDll = "(libc.so.6|libiconv.so)"
|
||||
|
||||
|
|
@ -449,8 +451,13 @@ proc convert*(s: string, destEncoding = "UTF-8",
|
|||
close(c)
|
||||
|
||||
when IsMainModule:
|
||||
var orig = "öäüß"
|
||||
var crap = convert(orig, "CP1252", "UTF-8")
|
||||
echo convert(crap, "ibm850", "CP1252")
|
||||
echo getCurrentEncoding()
|
||||
let
|
||||
orig = "öäüß"
|
||||
cp1252 = convert(orig, "CP1252", "UTF-8")
|
||||
ibm850 = convert(cp1252, "ibm850", "CP1252")
|
||||
current = getCurrentEncoding()
|
||||
echo "Original string from source code: ", orig
|
||||
echo "Forced ibm850 encoding: ", ibm850
|
||||
echo "Current encoding: ", current
|
||||
echo "From ibm850 to current: ", convert(ibm850, current, "ibm850")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@
|
|||
## module will therefore not work with any Linux kernel prior to that, unless
|
||||
## it has been patched to support inotify.
|
||||
|
||||
when defined(windows):
|
||||
{.error: "Windows is not yet supported by this module.".}
|
||||
elif defined(linux):
|
||||
when defined(linux) or defined(nimdoc):
|
||||
from posix import read
|
||||
else:
|
||||
{.error: "Your platform is not supported.".}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ import sockets, strutils, parseutils, times, os, asyncio
|
|||
## var ftp = FTPClient("example.org", user = "user", pass = "pass")
|
||||
## ftp.connect()
|
||||
## ftp.retrFile("file.ext", "file.ext")
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
|
||||
type
|
||||
TFTPClient* = object of TObject
|
||||
|
|
@ -92,8 +96,9 @@ type
|
|||
EFTP* = object of ESynch
|
||||
|
||||
proc FTPClient*(address: string, port = TPort(21),
|
||||
user, pass = ""): TFTPClient =
|
||||
## Create a ``TFTPClient`` object.
|
||||
user, pass = ""): PFTPClient =
|
||||
## Create a ``PFTPClient`` object.
|
||||
new(result)
|
||||
result.user = user
|
||||
result.pass = pass
|
||||
result.address = address
|
||||
|
|
@ -109,7 +114,7 @@ proc getDSock(ftp: PFTPClient): TSocket =
|
|||
proc getCSock(ftp: PFTPClient): TSocket =
|
||||
if ftp.isAsync: return ftp.asyncCSock else: return ftp.csock
|
||||
|
||||
template blockingOperation(sock: TSocket, body: stmt) =
|
||||
template blockingOperation(sock: TSocket, body: stmt) {.immediate.} =
|
||||
if ftp.isAsync:
|
||||
sock.setBlocking(true)
|
||||
body
|
||||
|
|
@ -274,11 +279,20 @@ proc getLines(ftp: PFTPClient, async: bool = false): bool =
|
|||
## It doesn't if `async` is true, because it doesn't check for 226 then.
|
||||
if ftp.dsockConnected:
|
||||
var r = TaintedString""
|
||||
if getDSock(ftp).recvAsync(r):
|
||||
if r.string != "":
|
||||
ftp.job.lines.add(r.string)
|
||||
else:
|
||||
ftp.dsockConnected = False
|
||||
if ftp.isAsync:
|
||||
if ftp.asyncDSock.recvLine(r):
|
||||
if r.string == "":
|
||||
ftp.dsockConnected = false
|
||||
else:
|
||||
ftp.job.lines.add(r.string & "\n")
|
||||
else:
|
||||
assert(not async)
|
||||
if ftp.dsock.recvLine(r):
|
||||
if r.string == "":
|
||||
ftp.dsockConnected = false
|
||||
else:
|
||||
ftp.job.lines.add(r.string & "\n")
|
||||
else: OSError()
|
||||
|
||||
if not async:
|
||||
var readSocks: seq[TSocket] = @[ftp.getCSock()]
|
||||
|
|
@ -385,7 +399,7 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
|
|||
proc retrText*(ftp: PFTPClient, file: string, async = false): string =
|
||||
## Retrieves ``file``. File must be ASCII text.
|
||||
## If ``async`` is true, this function will return immediately and
|
||||
## it will be your job to call ``poll`` to progress this operation.
|
||||
## it will be your job to call asyncio's ``poll`` to progress this operation.
|
||||
ftp.createJob(getLines, JRetrText)
|
||||
ftp.pasv()
|
||||
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
|
||||
|
|
@ -400,12 +414,14 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string =
|
|||
proc getFile(ftp: PFTPClient, async = false): bool =
|
||||
if ftp.dsockConnected:
|
||||
var r = "".TaintedString
|
||||
var bytesRead = 0
|
||||
var returned = false
|
||||
if async:
|
||||
if not ftp.isAsync: raise newException(EFTP, "FTPClient must be async.")
|
||||
returned = ftp.AsyncDSock.recvAsync(r)
|
||||
bytesRead = ftp.AsyncDSock.recvAsync(r, BufferSize)
|
||||
returned = bytesRead != -1
|
||||
else:
|
||||
r = getDSock(ftp).recv()
|
||||
bytesRead = getDSock(ftp).recv(r, BufferSize)
|
||||
returned = true
|
||||
let r2 = r.string
|
||||
if r2 != "":
|
||||
|
|
@ -425,8 +441,9 @@ proc getFile(ftp: PFTPClient, async = false): bool =
|
|||
proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
|
||||
## Downloads ``file`` and saves it to ``dest``. Usage of this function
|
||||
## asynchronously is recommended to view the progress of the download.
|
||||
## The ``EvRetr`` event is given by ``poll`` when the download is finished,
|
||||
## and the ``filename`` field will be equal to ``file``.
|
||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||
## when the download is finished, and the ``filename`` field will be equal
|
||||
## to ``file``.
|
||||
ftp.createJob(getFile, JRetr)
|
||||
ftp.job.file = open(dest, mode = fmWrite)
|
||||
ftp.pasv()
|
||||
|
|
@ -450,11 +467,13 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
|
|||
if ftp.dsockConnected:
|
||||
if ftp.job.toStore.len() > 0:
|
||||
assert(async)
|
||||
if ftp.asyncDSock.sendAsync(ftp.job.toStore):
|
||||
let bytesSent = ftp.asyncDSock.sendAsync(ftp.job.toStore)
|
||||
if bytesSent == ftp.job.toStore.len:
|
||||
ftp.job.toStore = ""
|
||||
ftp.job.progress.inc(ftp.job.toStore.len)
|
||||
ftp.job.oneSecond.inc(ftp.job.toStore.len)
|
||||
|
||||
elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
|
||||
ftp.job.toStore = ftp.job.toStore[bytesSent .. -1]
|
||||
ftp.job.progress.inc(bytesSent)
|
||||
ftp.job.oneSecond.inc(bytesSent)
|
||||
else:
|
||||
var s = newStringOfCap(4000)
|
||||
var len = ftp.job.file.readBuffer(addr(s[0]), 4000)
|
||||
|
|
@ -472,8 +491,12 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
|
|||
if not async:
|
||||
getDSock(ftp).send(s)
|
||||
else:
|
||||
if not ftp.asyncDSock.sendAsync(s):
|
||||
ftp.job.toStore = s
|
||||
let bytesSent = ftp.asyncDSock.sendAsync(s)
|
||||
if bytesSent == 0:
|
||||
ftp.job.toStore.add(s)
|
||||
elif bytesSent != s.len:
|
||||
ftp.job.toStore.add(s[bytesSent .. -1])
|
||||
len = bytesSent
|
||||
|
||||
ftp.job.progress.inc(len)
|
||||
ftp.job.oneSecond.inc(len)
|
||||
|
|
@ -482,8 +505,9 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) =
|
|||
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
||||
## function asynchronously is recommended to view the progress of
|
||||
## the download.
|
||||
## The ``EvStore`` event is given by ``poll`` when the upload is finished,
|
||||
## and the ``filename`` field will be equal to ``file``.
|
||||
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
||||
## when the upload is finished, and the ``filename`` field will be
|
||||
## equal to ``file``.
|
||||
ftp.createJob(doUpload, JStore)
|
||||
ftp.job.file = open(file)
|
||||
ftp.job.total = ftp.job.file.getFileSize()
|
||||
|
|
@ -508,16 +532,6 @@ proc close*(ftp: PFTPClient) =
|
|||
ftp.csock.close()
|
||||
ftp.dsock.close()
|
||||
|
||||
discard """proc getSocket(h: PObject): tuple[info: TInfo, sock: TSocket] =
|
||||
result = (SockIdle, InvalidSocket)
|
||||
var ftp = PAsyncFTPClient(h)
|
||||
if ftp.jobInProgress:
|
||||
case ftp.job.typ
|
||||
of JRetrText, JRetr, JStore:
|
||||
if ftp.dsockStatus == SockConnecting or ftp.dsockStatus == SockConnected:
|
||||
result = (ftp.dsockStatus, ftp.dsock)
|
||||
else: result = (SockIdle, ftp.dsock)"""
|
||||
|
||||
proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||
if ftp.jobInProgress:
|
||||
assertReply ftp.expectReply(), "226" # Make sure the transfer completed.
|
||||
|
|
@ -540,32 +554,6 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
|||
|
||||
ftp.handleEvent(ftp, r)
|
||||
|
||||
discard """proc handleConnect(h: PObject) =
|
||||
var ftp = PAsyncFTPClient(h)
|
||||
ftp.dsockStatus = SockConnected
|
||||
assert(ftp.jobInProgress)
|
||||
if ftp.job.typ == JStore:
|
||||
ftp.dele.mode = MWriteable
|
||||
else:
|
||||
ftp.dele.mode = MReadable"""
|
||||
|
||||
discard """proc handleRead(h: PObject) =
|
||||
var ftp = PAsyncFTPClient(h)
|
||||
assert(ftp.jobInProgress)
|
||||
assert(ftp.job.typ != JStore)
|
||||
# This can never return true, because it shouldn't check for code
|
||||
# 226 from csock.
|
||||
assert(not ftp.job.prc(ftp[], true))
|
||||
"""
|
||||
|
||||
discard """proc csockGetSocket(h: PObject): tuple[info: TInfo, sock: TSocket] =
|
||||
# This only returns the csock if a job is in progress. Otherwise handle read
|
||||
# would capture data which is not for it to capture.
|
||||
result = (SockIdle, InvalidSocket)
|
||||
var ftp = PAsyncFTPClient(h)
|
||||
if ftp.jobInProgress:
|
||||
result = (SockConnected, ftp.csock)"""
|
||||
|
||||
proc AsyncFTPClient*(address: string, port = TPort(21),
|
||||
user, pass = "",
|
||||
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} =
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ type
|
|||
tagVar ## the HTML ``var`` element
|
||||
|
||||
const
|
||||
tagStrs = [
|
||||
tagToStr* = [
|
||||
"a", "abbr", "acronym", "address", "applet", "area",
|
||||
"b", "base", "basefont", "bdo", "big", "blockquote", "body",
|
||||
"br", "button", "caption", "center", "cite", "code",
|
||||
|
|
@ -243,13 +243,13 @@ proc binaryStrSearch(x: openarray[string], y: string): int =
|
|||
proc htmlTag*(n: PXmlNode): THtmlTag =
|
||||
## gets `n`'s tag as a ``THtmlTag``.
|
||||
if n.clientData == 0:
|
||||
n.clientData = binaryStrSearch(tagStrs, n.tag)+1
|
||||
n.clientData = binaryStrSearch(tagToStr, n.tag)+1
|
||||
result = THtmlTag(n.clientData)
|
||||
|
||||
proc htmlTag*(s: string): THtmlTag =
|
||||
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
## returned.
|
||||
result = THtmlTag(binaryStrSearch(tagStrs, s.toLower)+1)
|
||||
result = THtmlTag(binaryStrSearch(tagToStr, s.toLower)+1)
|
||||
|
||||
proc entityToUtf8*(entity: string): string =
|
||||
## converts an HTML entity name like ``Ü`` to its UTF-8 equivalent.
|
||||
|
|
|
|||
|
|
@ -80,62 +80,43 @@ proc fileError(msg: string) =
|
|||
e.msg = msg
|
||||
raise e
|
||||
|
||||
proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} =
|
||||
result = d[i]
|
||||
while result == '\0':
|
||||
d = string(s.recv())
|
||||
i = 0
|
||||
result = d[i]
|
||||
|
||||
proc parseChunks(s: TSocket): string =
|
||||
# get chunks:
|
||||
var i = 0
|
||||
result = ""
|
||||
var d = s.recv().string
|
||||
var ri = 0
|
||||
while true:
|
||||
var chunkSizeStr = ""
|
||||
var chunkSize = 0
|
||||
var digitFound = false
|
||||
while true:
|
||||
case d[i]
|
||||
of '0'..'9':
|
||||
digitFound = true
|
||||
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('0'))
|
||||
of 'a'..'f':
|
||||
digitFound = true
|
||||
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('a') + 10)
|
||||
of 'A'..'F':
|
||||
digitFound = true
|
||||
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('A') + 10)
|
||||
of '\0':
|
||||
d = string(s.recv())
|
||||
i = -1
|
||||
else: break
|
||||
inc(i)
|
||||
if not digitFound: httpError("Chunksize expected")
|
||||
if s.recvLine(chunkSizeStr):
|
||||
var i = 0
|
||||
if chunkSizeStr == "":
|
||||
httpError("Server terminated connection prematurely")
|
||||
while true:
|
||||
case chunkSizeStr[i]
|
||||
of '0'..'9':
|
||||
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
|
||||
of 'a'..'f':
|
||||
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
|
||||
of 'A'..'F':
|
||||
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
|
||||
of '\0':
|
||||
break
|
||||
of ';':
|
||||
# http://tools.ietf.org/html/rfc2616#section-3.6.1
|
||||
# We don't care about chunk-extensions.
|
||||
break
|
||||
else:
|
||||
httpError("Invalid chunk size: " & chunkSizeStr)
|
||||
inc(i)
|
||||
if chunkSize <= 0: break
|
||||
while charAt(d, i, s) notin {'\C', '\L', '\0'}: inc(i)
|
||||
if charAt(d, i, s) == '\C': inc(i)
|
||||
if charAt(d, i, s) == '\L': inc(i)
|
||||
else: httpError("CR-LF after chunksize expected")
|
||||
|
||||
var x = substr(d, i, i+chunkSize-1)
|
||||
var size = x.len
|
||||
result.add(x)
|
||||
inc(i, size)
|
||||
if size < chunkSize:
|
||||
# read in the rest:
|
||||
var missing = chunkSize - size
|
||||
var L = result.len
|
||||
setLen(result, L + missing)
|
||||
while missing > 0:
|
||||
var bytesRead = s.recv(addr(result[L]), missing)
|
||||
inc(L, bytesRead)
|
||||
dec(missing, bytesRead)
|
||||
# next chunk:
|
||||
d = string(s.recv())
|
||||
i = 0
|
||||
# skip trailing CR-LF:
|
||||
while charAt(d, i, s) in {'\C', '\L'}: inc(i)
|
||||
result.setLen(ri+chunkSize)
|
||||
var bytesRead = 0
|
||||
while bytesRead != chunkSize:
|
||||
let ret = recv(s, addr(result[ri]), chunkSize-bytesRead)
|
||||
ri += ret
|
||||
bytesRead += ret
|
||||
s.skip(2) # Skip \c\L
|
||||
# Trailer headers will only be sent if the request specifies that we want
|
||||
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1
|
||||
|
||||
proc parseBody(s: TSocket,
|
||||
headers: PStringTable): string =
|
||||
|
|
@ -238,12 +219,18 @@ type
|
|||
httpCONNECT ## Converts the request connection to a transparent
|
||||
## TCP/IP tunnel, usually used for proxies.
|
||||
|
||||
when not defined(ssl):
|
||||
type PSSLContext = ref object
|
||||
let defaultSSLContext: PSSLContext = nil
|
||||
else:
|
||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||
|
||||
proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||
body = ""): TResponse =
|
||||
body = "",
|
||||
sslContext: PSSLContext = defaultSSLContext): TResponse =
|
||||
## | Requests ``url`` with the specified ``httpMethod``.
|
||||
## | Extra headers can be specified and must be seperated by ``\c\L``
|
||||
var r = parseUrl(url)
|
||||
|
||||
var headers = substr($httpMethod, len("http"))
|
||||
headers.add(" /" & r.path & r.query)
|
||||
|
||||
|
|
@ -257,7 +244,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
|||
var port = TPort(80)
|
||||
if r.scheme == "https":
|
||||
when defined(ssl):
|
||||
s.wrapSocket(verifyMode = CVerifyNone)
|
||||
sslContext.wrapSocket(s)
|
||||
else:
|
||||
raise newException(EHttpRequestErr, "SSL support was not compiled in. Cannot connect over SSL.")
|
||||
port = TPort(443)
|
||||
|
|
@ -277,32 +264,32 @@ proc redirection(status: string): bool =
|
|||
if status.startsWith(i):
|
||||
return True
|
||||
|
||||
proc get*(url: string, maxRedirects = 5): TResponse =
|
||||
## | GET's the ``url`` and returns a ``TResponse`` object
|
||||
proc get*(url: string, maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
|
||||
## | GETs the ``url`` and returns a ``TResponse`` object
|
||||
## | This proc also handles redirection
|
||||
result = request(url)
|
||||
for i in 1..maxRedirects:
|
||||
if result.status.redirection():
|
||||
var locationHeader = result.headers["Location"]
|
||||
if locationHeader == "": httpError("location header expected")
|
||||
result = request(locationHeader)
|
||||
result = request(locationHeader, sslContext = sslContext)
|
||||
|
||||
proc getContent*(url: string): string =
|
||||
## | GET's the body and returns it as a string.
|
||||
proc getContent*(url: string, sslContext: PSSLContext = defaultSSLContext): string =
|
||||
## | GETs the body and returns it as a string.
|
||||
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
|
||||
var r = get(url)
|
||||
var r = get(url, sslContext = sslContext)
|
||||
if r.status[0] in {'4','5'}:
|
||||
raise newException(EHTTPRequestErr, r.status)
|
||||
else:
|
||||
return r.body
|
||||
|
||||
proc post*(url: string, extraHeaders = "", body = "",
|
||||
maxRedirects = 5): TResponse =
|
||||
## | POST's ``body`` to the ``url`` and returns a ``TResponse`` object.
|
||||
maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
|
||||
## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object.
|
||||
## | This proc adds the necessary Content-Length header.
|
||||
## | This proc also handles redirection.
|
||||
var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
|
||||
result = request(url, httpPOST, xh, body)
|
||||
result = request(url, httpPOST, xh, body, sslContext)
|
||||
for i in 1..maxRedirects:
|
||||
if result.status.redirection():
|
||||
var locationHeader = result.headers["Location"]
|
||||
|
|
@ -310,8 +297,9 @@ proc post*(url: string, extraHeaders = "", body = "",
|
|||
var meth = if result.status != "307": httpGet else: httpPost
|
||||
result = request(locationHeader, meth, xh, body)
|
||||
|
||||
proc postContent*(url: string, extraHeaders = "", body = ""): string =
|
||||
## | POST's ``body`` to ``url`` and returns the response's body as a string
|
||||
proc postContent*(url: string, extraHeaders = "", body = "",
|
||||
sslContext: PSSLContext = defaultSSLContext): string =
|
||||
## | POSTs ``body`` to ``url`` and returns the response's body as a string
|
||||
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
|
||||
var r = post(url, extraHeaders, body)
|
||||
if r.status[0] in {'4','5'}:
|
||||
|
|
@ -319,11 +307,12 @@ proc postContent*(url: string, extraHeaders = "", body = ""): string =
|
|||
else:
|
||||
return r.body
|
||||
|
||||
proc downloadFile*(url: string, outputFilename: string) =
|
||||
proc downloadFile*(url: string, outputFilename: string,
|
||||
sslContext: PSSLContext = defaultSSLContext) =
|
||||
## Downloads ``url`` and saves it to ``outputFilename``
|
||||
var f: TFile
|
||||
if open(f, outputFilename, fmWrite):
|
||||
f.write(getContent(url))
|
||||
f.write(getContent(url, sslContext))
|
||||
f.close()
|
||||
else:
|
||||
fileError("Unable to open file")
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
##
|
||||
## run(handleRequest, TPort(80))
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio
|
||||
|
||||
|
|
|
|||
202
lib/pure/irc.nim
202
lib/pure/irc.nim
|
|
@ -16,17 +16,23 @@
|
|||
## the amount of lag.
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## var client = irc("irc.server.net", joinChans = @["#channel"])
|
||||
##
|
||||
## var client = irc("picheta.me", joinChans = @["#bots"])
|
||||
## client.connect()
|
||||
## while True:
|
||||
## var event: TIRCEvent
|
||||
## if client.poll(event):
|
||||
## case event.typ
|
||||
## of EvDisconnected: break
|
||||
## of EvConnected: nil
|
||||
## of EvDisconnected:
|
||||
## client.reconnect()
|
||||
## of EvMsg:
|
||||
## # Where all the magic happens.
|
||||
## # Write your message reading code here.
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
import sockets, strutils, parseutils, times, asyncio
|
||||
import sockets, strutils, parseutils, times, asyncio, os
|
||||
|
||||
type
|
||||
TIRC* = object of TObject
|
||||
|
|
@ -34,11 +40,15 @@ type
|
|||
port: TPort
|
||||
nick, user, realname, serverPass: string
|
||||
case isAsync: bool
|
||||
of false:
|
||||
sock: TSocket
|
||||
of true:
|
||||
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
asyncSock: PAsyncSocket
|
||||
myDispatcher: PDispatcher
|
||||
of false:
|
||||
dummyA: pointer
|
||||
dummyB: pointer # workaround a Nimrod API issue
|
||||
dummyC: pointer
|
||||
sock: TSocket
|
||||
status: TInfo
|
||||
lastPing: float
|
||||
lastPong: float
|
||||
|
|
@ -46,6 +56,9 @@ type
|
|||
channelsToJoin: seq[string]
|
||||
msgLimit: bool
|
||||
messageBuffer: seq[tuple[timeToSend: float, m: string]]
|
||||
lastReconnect: float
|
||||
|
||||
PIRC* = ref TIRC
|
||||
|
||||
PAsyncIRC* = ref TAsyncIRC
|
||||
TAsyncIRC* = object of TIRC
|
||||
|
|
@ -68,9 +81,13 @@ type
|
|||
MError
|
||||
|
||||
TIRCEventType* = enum
|
||||
EvMsg, EvDisconnected
|
||||
EvMsg, EvConnected, EvDisconnected
|
||||
TIRCEvent* = object ## IRC Event
|
||||
case typ*: TIRCEventType
|
||||
of EvConnected:
|
||||
## Connected to server.
|
||||
## Only occurs with AsyncIRC.
|
||||
nil
|
||||
of EvDisconnected:
|
||||
## Disconnected from the server
|
||||
nil
|
||||
|
|
@ -82,7 +99,7 @@ type
|
|||
origin*: string ## The channel/user that this msg originated from
|
||||
raw*: string ## Raw IRC message
|
||||
|
||||
proc send*(irc: var TIRC, message: string, sendImmediately = false) =
|
||||
proc send*(irc: PIRC, message: string, sendImmediately = false) =
|
||||
## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
|
||||
var sendMsg = true
|
||||
if irc.msgLimit and not sendImmediately:
|
||||
|
|
@ -104,15 +121,15 @@ proc send*(irc: var TIRC, message: string, sendImmediately = false) =
|
|||
# but I can't exactly check for EBrokenPipe.
|
||||
irc.status = SockClosed
|
||||
|
||||
proc privmsg*(irc: var TIRC, target, message: string) =
|
||||
proc privmsg*(irc: PIRC, target, message: string) =
|
||||
## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
|
||||
irc.send("PRIVMSG $1 :$2" % [target, message])
|
||||
|
||||
proc notice*(irc: var TIRC, target, message: string) =
|
||||
proc notice*(irc: PIRC, target, message: string) =
|
||||
## Sends ``notice`` to ``target``. ``Target`` can be a channel, or a user.
|
||||
irc.send("NOTICE $1 :$2" % [target, message])
|
||||
|
||||
proc join*(irc: var TIRC, channel: string, key = "") =
|
||||
proc join*(irc: PIRC, channel: string, key = "") =
|
||||
## Joins ``channel``.
|
||||
##
|
||||
## If key is not ``""``, then channel is assumed to be key protected and this
|
||||
|
|
@ -122,16 +139,19 @@ proc join*(irc: var TIRC, channel: string, key = "") =
|
|||
else:
|
||||
irc.send("JOIN " & channel & " " & key)
|
||||
|
||||
proc part*(irc: var TIRC, channel, message: string) =
|
||||
proc part*(irc: PIRC, channel, message: string) =
|
||||
## Leaves ``channel`` with ``message``.
|
||||
irc.send("PART " & channel & " :" & message)
|
||||
|
||||
proc close*(irc: var TIRC) =
|
||||
proc close*(irc: PIRC) =
|
||||
## Closes connection to an IRC server.
|
||||
##
|
||||
## **Warning:** This procedure does not send a ``QUIT`` message to the server.
|
||||
irc.status = SockClosed
|
||||
irc.sock.close()
|
||||
if irc.isAsync:
|
||||
irc.asyncSock.close()
|
||||
else:
|
||||
irc.sock.close()
|
||||
|
||||
proc isNumber(s: string): bool =
|
||||
## Checks if `s` contains only numbers.
|
||||
|
|
@ -202,12 +222,11 @@ proc parseMessage(msg: string): TIRCEvent =
|
|||
inc(i) # Skip `:`.
|
||||
result.params.add(msg[i..msg.len-1])
|
||||
|
||||
proc connect*(irc: var TIRC) =
|
||||
proc connect*(irc: PIRC) =
|
||||
## Connects to an IRC server as specified by ``irc``.
|
||||
assert(irc.address != "")
|
||||
assert(irc.port != TPort(0))
|
||||
|
||||
irc.sock = socket()
|
||||
irc.sock.connect(irc.address, irc.port)
|
||||
|
||||
irc.status = SockConnected
|
||||
|
|
@ -217,13 +236,28 @@ proc connect*(irc: var TIRC) =
|
|||
irc.send("NICK " & irc.nick, true)
|
||||
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||
|
||||
proc reconnect*(irc: PIRC, timeout = 5000) =
|
||||
## Reconnects to an IRC server.
|
||||
##
|
||||
## ``Timeout`` specifies the time to wait in miliseconds between multiple
|
||||
## consecutive reconnections.
|
||||
##
|
||||
## This should be used when an ``EvDisconnected`` event occurs.
|
||||
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
|
||||
if secSinceReconnect < timeout:
|
||||
sleep(timeout - secSinceReconnect)
|
||||
irc.sock = socket()
|
||||
irc.connect()
|
||||
irc.lastReconnect = epochTime()
|
||||
|
||||
proc irc*(address: string, port: TPort = 6667.TPort,
|
||||
nick = "NimrodBot",
|
||||
user = "NimrodBot",
|
||||
realname = "NimrodBot", serverPass = "",
|
||||
joinChans: seq[string] = @[],
|
||||
msgLimit: bool = true): TIRC =
|
||||
msgLimit: bool = true): PIRC =
|
||||
## Creates a ``TIRC`` object.
|
||||
new(result)
|
||||
result.address = address
|
||||
result.port = port
|
||||
result.nick = nick
|
||||
|
|
@ -237,8 +271,9 @@ proc irc*(address: string, port: TPort = 6667.TPort,
|
|||
result.msgLimit = msgLimit
|
||||
result.messageBuffer = @[]
|
||||
result.status = SockIdle
|
||||
result.sock = socket()
|
||||
|
||||
proc processLine(irc: var TIRC, line: string): TIRCEvent =
|
||||
proc processLine(irc: PIRC, line: string): TIRCEvent =
|
||||
if line.len == 0:
|
||||
irc.close()
|
||||
result.typ = EvDisconnected
|
||||
|
|
@ -271,7 +306,7 @@ proc processLine(irc: var TIRC, line: string): TIRCEvent =
|
|||
if result.nick == irc.nick:
|
||||
irc.nick = result.params[0]
|
||||
|
||||
proc processOther(irc: var TIRC, ev: var TIRCEvent): bool =
|
||||
proc processOther(irc: PIRC, ev: var TIRCEvent): bool =
|
||||
result = false
|
||||
if epochTime() - irc.lastPing >= 20.0:
|
||||
irc.lastPing = epochTime()
|
||||
|
|
@ -290,7 +325,7 @@ proc processOther(irc: var TIRC, ev: var TIRCEvent): bool =
|
|||
break # messageBuffer is guaranteed to be from the quickest to the
|
||||
# later-est.
|
||||
|
||||
proc poll*(irc: var TIRC, ev: var TIRCEvent,
|
||||
proc poll*(irc: PIRC, ev: var TIRCEvent,
|
||||
timeout: int = 500): bool =
|
||||
## This function parses a single message from the IRC server and returns
|
||||
## a TIRCEvent.
|
||||
|
|
@ -316,46 +351,32 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
|
|||
|
||||
if processOther(irc, ev): result = true
|
||||
|
||||
proc getLag*(irc: var TIRC): float =
|
||||
proc getLag*(irc: PIRC): float =
|
||||
## Returns the latency between this client and the IRC server in seconds.
|
||||
##
|
||||
## If latency is unknown, returns -1.0.
|
||||
return irc.lag
|
||||
|
||||
proc isConnected*(irc: var TIRC): bool =
|
||||
proc isConnected*(irc: PIRC): bool =
|
||||
## Returns whether this IRC client is connected to an IRC server.
|
||||
return irc.status == SockConnected
|
||||
|
||||
proc getNick*(irc: var TIRC): string =
|
||||
proc getNick*(irc: PIRC): string =
|
||||
## Returns the current nickname of the client.
|
||||
return irc.nick
|
||||
|
||||
# -- Asyncio dispatcher
|
||||
|
||||
proc connect*(irc: PAsyncIRC) =
|
||||
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
|
||||
assert(irc.address != "")
|
||||
assert(irc.port != TPort(0))
|
||||
|
||||
irc.asyncSock = AsyncSocket()
|
||||
irc.asyncSock.connect(irc.address, irc.port)
|
||||
|
||||
proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||
# Greet the server :)
|
||||
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
|
||||
irc[].send("NICK " & irc.nick, true)
|
||||
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||
|
||||
discard """proc handleConnect(h: PObject) =
|
||||
var irc = PAsyncIRC(h)
|
||||
|
||||
# Greet the server :)
|
||||
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
|
||||
irc[].send("NICK " & irc.nick, true)
|
||||
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||
|
||||
if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
|
||||
irc.send("NICK " & irc.nick, true)
|
||||
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||
irc.status = SockConnected
|
||||
"""
|
||||
|
||||
var ev: TIRCEvent
|
||||
ev.typ = EvConnected
|
||||
irc.handleEvent(irc, ev)
|
||||
|
||||
proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||
var line = "".TaintedString
|
||||
|
|
@ -363,42 +384,55 @@ proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
|
|||
if ret:
|
||||
if line == "":
|
||||
var ev: TIRCEvent
|
||||
irc[].close()
|
||||
irc.close()
|
||||
ev.typ = EvDisconnected
|
||||
irc[].handleEvent(irc[], ev)
|
||||
irc.handleEvent(irc, ev)
|
||||
else:
|
||||
var ev = irc[].processLine(line.string)
|
||||
irc[].handleEvent(irc[], ev)
|
||||
|
||||
discard """proc handleRead(h: PObject) =
|
||||
var irc = PAsyncIRC(h)
|
||||
var line = "".TaintedString
|
||||
var ret = irc.sock.recvLineAsync(line)
|
||||
case ret
|
||||
of RecvFullLine:
|
||||
var ev = irc[].processLine(irc.lineBuffer.string & line.string)
|
||||
irc.handleEvent(irc[], ev, irc.userArg)
|
||||
irc.lineBuffer = "".TaintedString
|
||||
of RecvPartialLine:
|
||||
if line.string != "":
|
||||
string(irc.lineBuffer).add(line.string)
|
||||
of RecvDisconnected:
|
||||
var ev: TIRCEvent
|
||||
irc[].close()
|
||||
ev.typ = EvDisconnected
|
||||
irc.handleEvent(irc[], ev, irc.userArg)
|
||||
of RecvFail: nil"""
|
||||
var ev = irc.processLine(line.string)
|
||||
irc.handleEvent(irc, ev)
|
||||
|
||||
proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||
var ev: TIRCEvent
|
||||
if irc[].processOther(ev):
|
||||
irc.handleEvent(irc[], ev)
|
||||
if irc.processOther(ev):
|
||||
irc.handleEvent(irc, ev)
|
||||
|
||||
proc register*(d: PDispatcher, irc: PAsyncIRC) =
|
||||
## Registers ``irc`` with dispatcher ``d``.
|
||||
irc.asyncSock.handleConnect =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleConnect(s, irc)
|
||||
irc.asyncSock.handleRead =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleRead(s, irc)
|
||||
irc.asyncSock.handleTask =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleTask(s, irc)
|
||||
d.register(irc.asyncSock)
|
||||
irc.myDispatcher = d
|
||||
|
||||
proc connect*(irc: PAsyncIRC) =
|
||||
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
|
||||
assert(irc.address != "")
|
||||
assert(irc.port != TPort(0))
|
||||
|
||||
discard """proc handleTask(h: PObject) =
|
||||
var irc = PAsyncIRC(h)
|
||||
var ev: TIRCEvent
|
||||
if PAsyncIRC(h)[].processOther(ev):
|
||||
irc.handleEvent(irc[], ev, irc.userArg)"""
|
||||
irc.asyncSock.connect(irc.address, irc.port)
|
||||
|
||||
proc reconnect*(irc: PAsyncIRC, timeout = 5000) =
|
||||
## Reconnects to an IRC server.
|
||||
##
|
||||
## ``Timeout`` specifies the time to wait in miliseconds between multiple
|
||||
## consecutive reconnections.
|
||||
##
|
||||
## This should be used when an ``EvDisconnected`` event occurs.
|
||||
##
|
||||
## When successfully reconnected an ``EvConnected`` event will occur.
|
||||
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
|
||||
if secSinceReconnect < timeout:
|
||||
sleep(timeout - secSinceReconnect)
|
||||
irc.asyncSock = AsyncSocket()
|
||||
irc.myDispatcher.register(irc)
|
||||
irc.connect()
|
||||
irc.lastReconnect = epochTime()
|
||||
|
||||
proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
||||
nick = "NimrodBot",
|
||||
|
|
@ -406,7 +440,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
|||
realname = "NimrodBot", serverPass = "",
|
||||
joinChans: seq[string] = @[],
|
||||
msgLimit: bool = true,
|
||||
ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
): PAsyncIRC =
|
||||
## Use this function if you want to use asyncio's dispatcher.
|
||||
##
|
||||
|
|
@ -429,19 +463,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
|||
result.msgLimit = msgLimit
|
||||
result.messageBuffer = @[]
|
||||
result.handleEvent = ircEvent
|
||||
|
||||
proc register*(d: PDispatcher, irc: PAsyncIRC) =
|
||||
## Registers ``irc`` with dispatcher ``d``.
|
||||
irc.asyncSock.handleConnect =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleConnect(s, irc)
|
||||
irc.asyncSock.handleRead =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleRead(s, irc)
|
||||
irc.asyncSock.handleTask =
|
||||
proc (s: PAsyncSocket) =
|
||||
handleTask(s, irc)
|
||||
d.register(irc.asyncSock)
|
||||
result.asyncSock = AsyncSocket()
|
||||
|
||||
when isMainModule:
|
||||
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
|
||||
|
|
@ -456,6 +478,8 @@ when isMainModule:
|
|||
var event: TIRCEvent
|
||||
if client.poll(event):
|
||||
case event.typ
|
||||
of EvConnected:
|
||||
nil
|
||||
of EvDisconnected:
|
||||
break
|
||||
of EvMsg:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,23 @@
|
|||
## (unlike XML). It is easy for machines to parse and generate.
|
||||
## JSON is based on a subset of the JavaScript Programming Language,
|
||||
## Standard ECMA-262 3rd Edition - December 1999.
|
||||
##
|
||||
## Usage example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## small_json = """{"test": 1.3, "key2": true}"""
|
||||
## jobj = parseJson(small_json)
|
||||
## assert (jobj.kind == JObject)
|
||||
## echo($jobj["test"].fnum)
|
||||
## echo($jobj["key2"].bval)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## 1.3000000000000000e+00
|
||||
## true
|
||||
|
||||
import
|
||||
hashes, strutils, lexbase, streams, unicode
|
||||
|
|
@ -524,6 +541,11 @@ proc newJString*(s: String): PJsonNode =
|
|||
result.kind = JString
|
||||
result.str = s
|
||||
|
||||
proc newJStringMove(s: String): PJsonNode =
|
||||
new(result)
|
||||
result.kind = JString
|
||||
shallowCopy(result.str, s)
|
||||
|
||||
proc newJInt*(n: biggestInt): PJsonNode =
|
||||
## Creates a new `JInt PJsonNode`.
|
||||
new(result)
|
||||
|
|
@ -607,7 +629,7 @@ proc len*(n: PJsonNode): int =
|
|||
else: nil
|
||||
|
||||
proc `[]`*(node: PJsonNode, name: String): PJsonNode =
|
||||
## Gets a field from a `JObject`.
|
||||
## Gets a field from a `JObject`. Returns nil if the key is not found.
|
||||
assert(node.kind == JObject)
|
||||
for key, item in items(node.fields):
|
||||
if key == name:
|
||||
|
|
@ -792,7 +814,9 @@ proc parseJson(p: var TJsonParser): PJsonNode =
|
|||
## Parses JSON from a JSON Parser `p`.
|
||||
case p.tok
|
||||
of tkString:
|
||||
result = newJString(p.a)
|
||||
# we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
|
||||
result = newJStringMove(p.a)
|
||||
p.a = ""
|
||||
discard getTok(p)
|
||||
of tkInt:
|
||||
result = newJInt(parseBiggestInt(p.a))
|
||||
|
|
@ -893,6 +917,10 @@ when isMainModule:
|
|||
echo(parsed["keyÄÖöoßß"])
|
||||
echo()
|
||||
echo(pretty(parsed2))
|
||||
try:
|
||||
echo(parsed["key2"][12123])
|
||||
raise newException(EInvalidValue, "That line was expected to fail")
|
||||
except EInvalidIndex: echo()
|
||||
|
||||
discard """
|
||||
while true:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -141,6 +141,11 @@ proc randomize*()
|
|||
## number, i.e. a tickcount. Note: Does nothing for the ECMAScript target,
|
||||
## as ECMAScript does not support this.
|
||||
|
||||
proc randomize*(seed: int)
|
||||
## initializes the random number generator with a specific seed.
|
||||
## Note: Does nothing for the ECMAScript target,
|
||||
## as ECMAScript does not support this.
|
||||
|
||||
when not defined(ECMAScript):
|
||||
proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".}
|
||||
## computes the square root of `x`.
|
||||
|
|
@ -190,15 +195,17 @@ when not defined(ECMAScript):
|
|||
proc rand(): cint {.importc: "rand", nodecl.}
|
||||
|
||||
when not defined(windows):
|
||||
proc srand48(seed: cint) {.importc: "srand48", nodecl.}
|
||||
proc srand48(seed: clong) {.importc: "srand48", nodecl.}
|
||||
proc drand48(): float {.importc: "drand48", nodecl.}
|
||||
proc random(max: float): float =
|
||||
result = drand48() * max
|
||||
|
||||
proc randomize() =
|
||||
let x = gettime(nil)
|
||||
srand(x)
|
||||
when defined(srand48): srand48(x)
|
||||
randomize(gettime(nil))
|
||||
|
||||
proc randomize(seed: int) =
|
||||
srand(cint(seed))
|
||||
when defined(srand48): srand48(seed)
|
||||
proc random(max: int): int =
|
||||
result = int(rand()) mod max
|
||||
|
||||
|
|
@ -217,6 +224,7 @@ else:
|
|||
proc random(max: float): float =
|
||||
result = float(mathrandom() * float(max))
|
||||
proc randomize() = nil
|
||||
proc randomize(seed: int) = nil
|
||||
|
||||
proc sqrt*(x: float): float {.importc: "Math.sqrt", nodecl.}
|
||||
proc ln*(x: float): float {.importc: "Math.log", nodecl.}
|
||||
|
|
@ -301,3 +309,18 @@ proc standardDeviation*(s: TRunningStat): float =
|
|||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
|
||||
when isMainModule and not defined(ECMAScript):
|
||||
# Verifies random seed initialization.
|
||||
let seed = gettime(nil)
|
||||
randomize(seed)
|
||||
const SIZE = 10
|
||||
var buf : array[0..SIZE, int]
|
||||
# Fill the buffer with random values
|
||||
for i in 0..SIZE-1:
|
||||
buf[i] = random(high(int))
|
||||
# Check that the second random calls are the same for each position.
|
||||
randomize(seed)
|
||||
for i in 0..SIZE-1:
|
||||
assert buf[i] == random(high(int)), "non deterministic random seeding"
|
||||
echo "random values equal after reseeding"
|
||||
|
|
|
|||
|
|
@ -554,7 +554,8 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
|||
var dotPos = path.len
|
||||
for i in countdown(len(path)-1, 0):
|
||||
if path[i] == ExtSep:
|
||||
if dotPos == path.len and i > 0: dotPos = i
|
||||
if dotPos == path.len and i > 0 and
|
||||
path[i-1] notin {dirsep, altsep}: dotPos = i
|
||||
elif path[i] in {dirsep, altsep}:
|
||||
sepPos = i
|
||||
break
|
||||
|
|
@ -1448,7 +1449,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} =
|
|||
else:
|
||||
var a, b: Ttimespec
|
||||
a.tv_sec = TTime(milsecs div 1000)
|
||||
a.tv_nsec = (milsecs mod 1000) * 1000
|
||||
a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
|
||||
discard posix.nanosleep(a, b)
|
||||
|
||||
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -201,9 +201,9 @@ proc execProcesses*(cmds: openArray[string],
|
|||
q[r] = startCmd(cmds[i], options=options)
|
||||
inc(i)
|
||||
if i > high(cmds): break
|
||||
for i in 0..m-1:
|
||||
if q[i] != nil: close(q[i])
|
||||
result = max(waitForExit(q[i]), result)
|
||||
for j in 0..m-1:
|
||||
if q[j] != nil: close(q[j])
|
||||
result = max(waitForExit(q[j]), result)
|
||||
else:
|
||||
for i in 0..high(cmds):
|
||||
var p = startCmd(cmds[i], options=options)
|
||||
|
|
|
|||
|
|
@ -49,26 +49,35 @@ proc raiseNoOK(status: string) =
|
|||
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
||||
|
||||
proc parseStatus(r: TRedis): TRedisStatus =
|
||||
var line = r.socket.recv.string
|
||||
var line = ""
|
||||
if r.socket.recvLine(line):
|
||||
if line == "":
|
||||
raise newException(ERedis, "Server closed connection prematurely")
|
||||
|
||||
if line[0] == '-':
|
||||
raise newException(ERedis, strip(line))
|
||||
if line[0] != '+':
|
||||
raiseInvalidReply('+', line[0])
|
||||
|
||||
return line.substr(1, line.len-3) # Strip '+' and \c\L.
|
||||
if line[0] == '-':
|
||||
raise newException(ERedis, strip(line))
|
||||
if line[0] != '+':
|
||||
raiseInvalidReply('+', line[0])
|
||||
|
||||
return line.substr(1) # Strip '+'
|
||||
else:
|
||||
OSError()
|
||||
|
||||
proc parseInteger(r: TRedis): TRedisInteger =
|
||||
var line = r.socket.recv.string
|
||||
var line = ""
|
||||
if r.socket.recvLine(line):
|
||||
if line == "":
|
||||
raise newException(ERedis, "Server closed connection prematurely")
|
||||
|
||||
if line[0] == '-':
|
||||
raise newException(ERedis, strip(line))
|
||||
if line[0] != ':':
|
||||
raiseInvalidReply(':', line[0])
|
||||
|
||||
# Strip ':' and \c\L.
|
||||
if parseBiggestInt(line, result, 1) == 0:
|
||||
raise newException(EInvalidReply, "Unable to parse integer.")
|
||||
if line[0] == '-':
|
||||
raise newException(ERedis, strip(line))
|
||||
if line[0] != ':':
|
||||
raiseInvalidReply(':', line[0])
|
||||
|
||||
# Strip ':'
|
||||
if parseBiggestInt(line, result, 1) == 0:
|
||||
raise newException(EInvalidReply, "Unable to parse integer.")
|
||||
else: OSError()
|
||||
|
||||
proc recv(sock: TSocket, size: int): TaintedString =
|
||||
result = newString(size).TaintedString
|
||||
|
|
@ -838,8 +847,11 @@ proc save*(r: TRedis) =
|
|||
proc shutdown*(r: TRedis) =
|
||||
## Synchronously save the dataset to disk and then shut down the server
|
||||
r.sendCommand("SHUTDOWN")
|
||||
var s = r.socket.recv()
|
||||
if s.string.len != 0: raise newException(ERedis, s.string)
|
||||
var s = "".TaintedString
|
||||
if r.socket.recvLine(s):
|
||||
if s.string.len != 0: raise newException(ERedis, s.string)
|
||||
else:
|
||||
OSError()
|
||||
|
||||
proc slaveof*(r: TRedis, host: string, port: string) =
|
||||
## Make the server a slave of another instance, or promote it as master
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
##
|
||||
## run(handleRequest)
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
import sockets, strutils, os, strtabs, asyncio
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
## For OpenSSL support compile with ``-d:ssl``. When using SSL be aware that
|
||||
## most functions will then raise ``ESSL`` on SSL errors.
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when hostos == "solaris":
|
||||
{.passl: "-lsocket -lnsl".}
|
||||
|
||||
|
|
@ -45,12 +47,15 @@ when defined(ssl):
|
|||
TSSLAcceptResult* = enum
|
||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||
|
||||
const
|
||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||
|
||||
type
|
||||
TSocketImpl = object ## socket type
|
||||
fd: cint
|
||||
case isBuffered: bool # determines whether this socket is buffered.
|
||||
of true:
|
||||
buffer: array[0..4000, char]
|
||||
buffer: array[0..BufferSize, char]
|
||||
currPos: int # current index in buffer
|
||||
bufLen: int # current length of buffer
|
||||
of false: nil
|
||||
|
|
@ -60,6 +65,8 @@ type
|
|||
sslHandle: PSSL
|
||||
sslContext: PSSLContext
|
||||
sslNoHandshake: bool # True if needs handshake.
|
||||
sslHasPeekChar: bool
|
||||
sslPeekChar: char
|
||||
of false: nil
|
||||
|
||||
TSocket* = ref TSocketImpl
|
||||
|
|
@ -256,7 +263,10 @@ when defined(ssl):
|
|||
of protSSLv23:
|
||||
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
|
||||
of protSSLv2:
|
||||
newCTX = SSL_CTX_new(SSLv2_method())
|
||||
when not defined(linux):
|
||||
newCTX = SSL_CTX_new(SSLv2_method())
|
||||
else:
|
||||
SSLError()
|
||||
of protSSLv3:
|
||||
newCTX = SSL_CTX_new(SSLv3_method())
|
||||
of protTLSv1:
|
||||
|
|
@ -286,12 +296,55 @@ when defined(ssl):
|
|||
socket.sslContext = ctx
|
||||
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext))
|
||||
socket.sslNoHandshake = false
|
||||
socket.sslHasPeekChar = false
|
||||
if socket.sslHandle == nil:
|
||||
SSLError()
|
||||
|
||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||
SSLError()
|
||||
|
||||
proc SocketError*(socket: TSocket, err: int = -1, async = false) =
|
||||
## Raises proper errors based on return values of ``recv`` functions.
|
||||
##
|
||||
## If ``async`` is ``True`` no error will be thrown in the case when the
|
||||
## error was caused by no data being available to be read.
|
||||
##
|
||||
## If ``err`` is not lower than 0 no exception will be raised.
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
if err <= 0:
|
||||
var ret = SSLGetError(socket.sslHandle, err.cint)
|
||||
case ret
|
||||
of SSL_ERROR_ZERO_RETURN:
|
||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||
if async:
|
||||
return
|
||||
else: SSLError("Not enough data on socket.")
|
||||
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||
if async:
|
||||
return
|
||||
else: SSLError("Not enough data on socket.")
|
||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||
SSLError("Function for x509 lookup has been called.")
|
||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||
SSLError()
|
||||
else: SSLError("Unknown Error")
|
||||
|
||||
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
if async:
|
||||
when defined(windows):
|
||||
# TODO: Test on Windows
|
||||
var err = WSAGetLastError()
|
||||
if err == WSAEWOULDBLOCK:
|
||||
return
|
||||
else: OSError()
|
||||
else:
|
||||
if errno == EAGAIN or errno == EWOULDBLOCK:
|
||||
return
|
||||
else: OSError()
|
||||
else: OSError()
|
||||
|
||||
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
|
|
@ -844,11 +897,8 @@ proc checkBuffer(readfds: var seq[TSocket]): int =
|
|||
var res: seq[TSocket] = @[]
|
||||
result = 0
|
||||
for s in readfds:
|
||||
if s.isBuffered:
|
||||
if s.bufLen <= 0 or s.currPos == s.bufLen:
|
||||
res.add(s)
|
||||
else:
|
||||
inc(result)
|
||||
if hasDataBuffered(s):
|
||||
inc(result)
|
||||
else:
|
||||
res.add(s)
|
||||
readfds = res
|
||||
|
|
@ -970,47 +1020,76 @@ template retRead(flags, readBytes: int) =
|
|||
|
||||
proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} =
|
||||
## receives data from a socket
|
||||
if size == 0: return
|
||||
if socket.isBuffered:
|
||||
if socket.bufLen == 0:
|
||||
retRead(0'i32, 0)
|
||||
|
||||
when true:
|
||||
var read = 0
|
||||
while read < size:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
retRead(0'i32, read)
|
||||
|
||||
let chunk = min(socket.bufLen-socket.currPos, size-read)
|
||||
var d = cast[cstring](data)
|
||||
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
|
||||
read.inc(chunk)
|
||||
socket.currPos.inc(chunk)
|
||||
else:
|
||||
var read = 0
|
||||
while read < size:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
retRead(0'i32, read)
|
||||
|
||||
var d = cast[cstring](data)
|
||||
d[read] = socket.buffer[socket.currPos]
|
||||
read.inc(1)
|
||||
socket.currPos.inc(1)
|
||||
var read = 0
|
||||
while read < size:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
retRead(0'i32, read)
|
||||
|
||||
let chunk = min(socket.bufLen-socket.currPos, size-read)
|
||||
var d = cast[cstring](data)
|
||||
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
|
||||
read.inc(chunk)
|
||||
socket.currPos.inc(chunk)
|
||||
|
||||
result = read
|
||||
else:
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
result = SSLRead(socket.sslHandle, data, size)
|
||||
if socket.sslHasPeekChar:
|
||||
copyMem(data, addr(socket.sslPeekChar), 1)
|
||||
socket.sslHasPeekChar = false
|
||||
if size-1 > 0:
|
||||
var d = cast[cstring](data)
|
||||
result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1
|
||||
else:
|
||||
result = 1
|
||||
else:
|
||||
result = SSLRead(socket.sslHandle, data, size)
|
||||
else:
|
||||
result = recv(socket.fd, data, size.cint, 0'i32)
|
||||
else:
|
||||
result = recv(socket.fd, data, size.cint, 0'i32)
|
||||
|
||||
proc recv*(socket: TSocket, data: var string, size: int): int =
|
||||
## higher-level version of the above
|
||||
##
|
||||
## When 0 is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an EOS exception when an error occurs. A value
|
||||
## lower than 0 is never returned.
|
||||
##
|
||||
## **Note**: ``data`` must be initialised.
|
||||
data.setLen(size)
|
||||
result = recv(socket, cstring(data), size)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.SocketError(result)
|
||||
data.setLen(result)
|
||||
|
||||
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||
## Async version of the above.
|
||||
##
|
||||
## When socket is non-blocking and no data is available on the socket,
|
||||
## ``-1`` will be returned and ``data`` will be ``""``.
|
||||
##
|
||||
## **Note**: ``data`` must be initialised.
|
||||
data.setLen(size)
|
||||
result = recv(socket, cstring(data), size)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.SocketError(async = true)
|
||||
result = -1
|
||||
data.setLen(result)
|
||||
|
||||
proc waitFor(socket: TSocket, waited: var float, timeout: int): int {.
|
||||
tags: [FTime].} =
|
||||
## returns the number of characters available to be read. In unbuffered
|
||||
## sockets this is always 1, otherwise this may as big as the buffer, currently
|
||||
## 4000.
|
||||
## sockets this is always 1, otherwise this may as big as ``BufferSize``.
|
||||
result = 1
|
||||
if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos:
|
||||
result = socket.bufLen - socket.currPos
|
||||
|
|
@ -1040,6 +1119,18 @@ proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
|
|||
|
||||
result = read
|
||||
|
||||
proc recv*(socket: TSocket, data: var string, size: int, timeout: int): int =
|
||||
## higher-level version of the above.
|
||||
##
|
||||
## Similar to the non-timeout version this will throw an EOS exception
|
||||
## when an error occurs.
|
||||
data.setLen(size)
|
||||
result = recv(socket, cstring(data), size, timeout)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.SocketError()
|
||||
data.setLen(result)
|
||||
|
||||
proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} =
|
||||
if socket.isBuffered:
|
||||
result = 1
|
||||
|
|
@ -1052,8 +1143,12 @@ proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} =
|
|||
else:
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
raise newException(ESSL, "Sorry, you cannot use recvLine on an unbuffered SSL socket.")
|
||||
|
||||
if not socket.sslHasPeekChar:
|
||||
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
|
||||
socket.sslHasPeekChar = true
|
||||
|
||||
c = socket.sslPeekChar
|
||||
return
|
||||
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
||||
|
||||
proc recvLine*(socket: TSocket, line: var TaintedString): bool {.
|
||||
|
|
@ -1062,14 +1157,12 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool {.
|
|||
## added to ``line``, however if solely ``\r\L`` is received then ``line``
|
||||
## will be set to it.
|
||||
##
|
||||
## ``True`` is returned if data is available. ``False`` usually suggests an
|
||||
## error, EOS exceptions are not raised in favour of this.
|
||||
## ``True`` is returned if data is available. ``False`` suggests an
|
||||
## error, EOS exceptions are not raised and ``False`` is simply returned
|
||||
## instead.
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
|
||||
## will be returned.
|
||||
##
|
||||
## **Warning:** Using this function on a unbuffered ssl socket will result
|
||||
## in an error.
|
||||
template addNLIfEmpty(): stmt =
|
||||
if line.len == 0:
|
||||
line.add("\c\L")
|
||||
|
|
@ -1096,6 +1189,8 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool {.
|
|||
tags: [FReadIO, FTime].} =
|
||||
## variant with a ``timeout`` parameter, the timeout parameter specifies
|
||||
## how many miliseconds to wait for data.
|
||||
##
|
||||
## ``ETimeout`` will be raised if ``timeout`` is exceeded.
|
||||
template addNLIfEmpty(): stmt =
|
||||
if line.len == 0:
|
||||
line.add("\c\L")
|
||||
|
|
@ -1148,11 +1243,13 @@ proc recvLineAsync*(socket: TSocket,
|
|||
elif c == '\L': return RecvFullLine
|
||||
add(line.string, c)
|
||||
|
||||
proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO].} =
|
||||
proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
|
||||
## receives all the available data from the socket.
|
||||
## Socket errors will result in an ``EOS`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``""`` will be returned.
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function is not safe for use.
|
||||
const bufSize = 4000
|
||||
result = newStringOfCap(bufSize).TaintedString
|
||||
var pos = 0
|
||||
|
|
@ -1177,25 +1274,31 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO].} =
|
|||
add(result.string, buf)
|
||||
if bytesRead != bufSize-1: break
|
||||
|
||||
{.push warning[deprecated]: off.}
|
||||
proc recvTimeout*(socket: TSocket, timeout: int): TaintedString {.
|
||||
tags: [FReadIO].} =
|
||||
tags: [FReadIO], deprecated.} =
|
||||
## overloaded variant to support a ``timeout`` parameter, the ``timeout``
|
||||
## parameter specifies the amount of miliseconds to wait for data on the
|
||||
## socket.
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function is not safe for use.
|
||||
if socket.bufLen == 0:
|
||||
var s = @[socket]
|
||||
if s.select(timeout) != 1:
|
||||
raise newException(ETimeout, "Call to recv() timed out.")
|
||||
|
||||
return socket.recv
|
||||
{.pop.}
|
||||
|
||||
proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
|
||||
tags: [FReadIO].} =
|
||||
tags: [FReadIO], deprecated.} =
|
||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||
## and there are no messages available, `False` will be returned.
|
||||
## Other socket errors will result in an ``EOS`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``s`` will be set to ``""``.
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function is not safe for use.
|
||||
const bufSize = 1000
|
||||
# ensure bufSize capacity:
|
||||
setLen(s.string, bufSize)
|
||||
|
|
@ -1282,13 +1385,25 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
|
|||
return False
|
||||
else: OSError()
|
||||
|
||||
proc skip*(socket: TSocket) {.tags: [FReadIO].} =
|
||||
proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
|
||||
## skips all the data that is pending for the socket
|
||||
##
|
||||
## **Deprecated since version 0.9.2**: This function is not safe for use.
|
||||
const bufSize = 1000
|
||||
var buf = alloc(bufSize)
|
||||
while recv(socket, buf, bufSize) == bufSize: nil
|
||||
dealloc(buf)
|
||||
|
||||
proc skip*(socket: TSocket, size: int) =
|
||||
## Skips ``size`` amount of bytes.
|
||||
##
|
||||
## Returns the number of skipped bytes.
|
||||
var dummy = alloc(size)
|
||||
var bytesSkipped = 0
|
||||
while bytesSkipped != size:
|
||||
bytesSkipped += recv(socket, dummy, size-bytesSkipped)
|
||||
dealloc(dummy)
|
||||
|
||||
proc send*(socket: TSocket, data: pointer, size: int): int {.
|
||||
tags: [FWriteIO].} =
|
||||
## sends data to a socket.
|
||||
|
|
@ -1312,21 +1427,26 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
|
|||
|
||||
OSError()
|
||||
|
||||
proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
|
||||
## sends data to a non-blocking socket. Returns whether ``data`` was sent.
|
||||
result = true
|
||||
var bytesSent = send(socket, cstring(data), data.len)
|
||||
proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
|
||||
## sends data to a non-blocking socket.
|
||||
## Returns ``0`` if no data could be sent, if data has been sent
|
||||
## returns the amount of bytes of ``data`` that was successfully sent. This
|
||||
## number may not always be the length of ``data`` but typically is.
|
||||
##
|
||||
## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error
|
||||
## occurs.
|
||||
result = send(socket, cstring(data), data.len)
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
if bytesSent <= 0:
|
||||
let ret = SSLGetError(socket.sslHandle, bytesSent.cint)
|
||||
if result <= 0:
|
||||
let ret = SSLGetError(socket.sslHandle, result.cint)
|
||||
case ret
|
||||
of SSL_ERROR_ZERO_RETURN:
|
||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||
SSLError("Unexpected error occured.") # This should just not happen.
|
||||
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||
return false
|
||||
return 0
|
||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||
SSLError("Function for x509 lookup has been called.")
|
||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||
|
|
@ -1334,17 +1454,18 @@ proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
|
|||
else: SSLError("Unknown Error")
|
||||
else:
|
||||
return
|
||||
if bytesSent == -1:
|
||||
if result == -1:
|
||||
when defined(windows):
|
||||
var err = WSAGetLastError()
|
||||
# TODO: Test on windows.
|
||||
if err == WSAEINPROGRESS:
|
||||
return false
|
||||
return 0
|
||||
else: OSError()
|
||||
else:
|
||||
if errno == EAGAIN or errno == EWOULDBLOCK:
|
||||
return false
|
||||
return 0
|
||||
else: OSError()
|
||||
|
||||
|
||||
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
|
||||
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
|
||||
|
|
@ -1385,8 +1506,7 @@ proc sendTo*(socket: TSocket, address: string, port: TPort,
|
|||
result = socket.sendTo(address, port, cstring(data), data.len)
|
||||
|
||||
when defined(Windows):
|
||||
const
|
||||
SOCKET_ERROR = -1
|
||||
const
|
||||
IOCPARM_MASK = 127
|
||||
IOC_IN = int(-2147483648)
|
||||
FIONBIO = int(IOC_IN or ((sizeof(int) and IOCPARM_MASK) shl 16) or
|
||||
|
|
@ -1399,7 +1519,7 @@ when defined(Windows):
|
|||
proc setBlocking(s: TSocket, blocking: bool) =
|
||||
when defined(Windows):
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if SOCKET_ERROR == ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)):
|
||||
if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
|
||||
OSError()
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(s.fd, F_GETFL, 0)
|
||||
|
|
|
|||
|
|
@ -267,7 +267,8 @@ proc newFileStream*(f: TFile): PFileStream =
|
|||
|
||||
proc newFileStream*(filename: string, mode: TFileMode): PFileStream =
|
||||
## creates a new stream from the file named `filename` with the mode `mode`.
|
||||
## If the file cannot be opened, nil is returned.
|
||||
## If the file cannot be opened, nil is returned. See the `system
|
||||
## <system.html>`_ module for a list of available TFileMode enums.
|
||||
var f: TFile
|
||||
if Open(f, filename, mode): result = newFileStream(f)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,11 +36,23 @@ proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
|
|||
result = t.counter
|
||||
|
||||
iterator pairs*(t: PStringTable): tuple[key, value: string] =
|
||||
## iterates over any (key, value) pair in the table `t`.
|
||||
## iterates over every (key, value) pair in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if not isNil(t.data[h].key):
|
||||
yield (t.data[h].key, t.data[h].val)
|
||||
|
||||
iterator keys*(t: PStringTable): string =
|
||||
## iterates over every key in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if not isNil(t.data[h].key):
|
||||
yield t.data[h].key
|
||||
|
||||
iterator values*(t: PStringTable): string =
|
||||
## iterates over every value in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if not isNil(t.data[h].key):
|
||||
yield t.data[h].val
|
||||
|
||||
type
|
||||
TFormatFlag* = enum ## flags for the `%` operator
|
||||
useEnvironment, ## use environment variable if the ``$key``
|
||||
|
|
|
|||
|
|
@ -186,7 +186,24 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
|||
## for word in split(";;this;is;an;;example;;;", {';'}):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## produces the same output.
|
||||
## produces the same output. The code:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let date = "2012-11-20T22:08:08.398990"
|
||||
## let separators = {' ', '-', ':', 'T'}
|
||||
## for number in split(date, separators):
|
||||
## writeln(stdout, number)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "2012"
|
||||
## "11"
|
||||
## "20"
|
||||
## "22"
|
||||
## "08"
|
||||
## "08.398990"
|
||||
##
|
||||
var last = 0
|
||||
assert(not ('\0' in seps))
|
||||
while last < len(s):
|
||||
|
|
@ -833,13 +850,51 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
|||
for c in items(s):
|
||||
case c
|
||||
of '\0'..'\31', '\128'..'\255':
|
||||
add(result, '\\')
|
||||
add(result, "\\x")
|
||||
add(result, toHex(ord(c), 2))
|
||||
of '\\': add(result, "\\\\")
|
||||
of '\'': add(result, "\\'")
|
||||
of '\"': add(result, "\\\"")
|
||||
else: add(result, c)
|
||||
add(result, suffix)
|
||||
|
||||
proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||
rtl, extern: "nsuUnescape".} =
|
||||
## Unescapes a string `s`. This complements ``escape`` as it performs the
|
||||
## opposite operations.
|
||||
##
|
||||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a EInvalidValue
|
||||
## exception will be raised.
|
||||
result = newStringOfCap(s.len)
|
||||
var i = 0
|
||||
if s[0 .. prefix.len-1] != prefix:
|
||||
raise newException(EInvalidValue,
|
||||
"String does not start with a prefix of: " & prefix)
|
||||
i.inc()
|
||||
while True:
|
||||
if i == s.len-suffix.len: break
|
||||
case s[i]
|
||||
of '\\':
|
||||
case s[i+1]:
|
||||
of 'x':
|
||||
let j = parseHexInt(s[i+2 .. i+3])
|
||||
result.add(chr(j))
|
||||
inc(i, 2)
|
||||
of '\\':
|
||||
result.add('\\')
|
||||
of '\'':
|
||||
result.add('\'')
|
||||
of '\"':
|
||||
result.add('\"')
|
||||
else: result.add("\\" & s[i+1])
|
||||
inc(i)
|
||||
of '\0': break
|
||||
else:
|
||||
result.add(s[i])
|
||||
i.inc()
|
||||
if s[i .. -1] != suffix:
|
||||
raise newException(EInvalidValue,
|
||||
"String does not end with a suffix of: " & suffix)
|
||||
|
||||
proc validIdentifier*(s: string): bool {.noSideEffect,
|
||||
rtl, extern: "nsuValidIdentifier".} =
|
||||
|
|
|
|||
577
lib/system.nim
577
lib/system.nim
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -38,7 +38,8 @@ type
|
|||
char* {.magic: Char.} ## built-in 8 bit character type (unsigned)
|
||||
string* {.magic: String.} ## built-in string type
|
||||
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
|
||||
pointer* {.magic: Pointer.} ## built-in pointer type
|
||||
pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
|
||||
## operator to get a pointer to a variable
|
||||
|
||||
const
|
||||
on* = true ## alias for ``true``
|
||||
|
|
@ -116,7 +117,13 @@ proc new*(T: typedesc): ref T =
|
|||
## creates a new object of type ``T`` and returns a safe (traced)
|
||||
## reference to it as result value
|
||||
new(result)
|
||||
|
||||
|
||||
proc unsafeNew*[T](a: var ref T, size: int) {.magic: "New", noSideEffect.}
|
||||
## creates a new object of type ``T`` and returns a safe (traced)
|
||||
## reference to it in ``a``. This is **unsafe** as it allocates an object
|
||||
## of the passed ``size``. This should only be used for optimization
|
||||
## purposes when you know what you're doing!
|
||||
|
||||
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||
## leaked implementation detail. Do not use.
|
||||
|
||||
|
|
@ -1060,86 +1067,87 @@ proc substr*(s: string, first, last: int): string {.
|
|||
## is used instead: This means ``substr`` can also be used to `cut`:idx:
|
||||
## or `limit`:idx: a string's length.
|
||||
|
||||
proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
|
||||
## overwrites the contents of the memory at ``p`` with the value 0.
|
||||
## Exactly ``size`` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is *unsafe*.
|
||||
when not defined(nimrodVM):
|
||||
proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
|
||||
## overwrites the contents of the memory at ``p`` with the value 0.
|
||||
## Exactly ``size`` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is *unsafe*.
|
||||
|
||||
proc copyMem*(dest, source: Pointer, size: int) {.importc: "memcpy", noDecl.}
|
||||
## copies the contents from the memory at ``source`` to the memory
|
||||
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
|
||||
## regions may not overlap. Like any procedure dealing with raw
|
||||
## memory this is *unsafe*.
|
||||
proc copyMem*(dest, source: Pointer, size: int) {.importc: "memcpy", noDecl.}
|
||||
## copies the contents from the memory at ``source`` to the memory
|
||||
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
|
||||
## regions may not overlap. Like any procedure dealing with raw
|
||||
## memory this is *unsafe*.
|
||||
|
||||
proc moveMem*(dest, source: Pointer, size: int) {.importc: "memmove", noDecl.}
|
||||
## copies the contents from the memory at ``source`` to the memory
|
||||
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
|
||||
## regions may overlap, ``moveMem`` handles this case appropriately
|
||||
## and is thus somewhat more safe than ``copyMem``. Like any procedure
|
||||
## dealing with raw memory this is still *unsafe*, though.
|
||||
proc moveMem*(dest, source: Pointer, size: int) {.importc: "memmove", noDecl.}
|
||||
## copies the contents from the memory at ``source`` to the memory
|
||||
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
|
||||
## regions may overlap, ``moveMem`` handles this case appropriately
|
||||
## and is thus somewhat more safe than ``copyMem``. Like any procedure
|
||||
## dealing with raw memory this is still *unsafe*, though.
|
||||
|
||||
proc equalMem*(a, b: Pointer, size: int): bool {.
|
||||
importc: "equalMem", noDecl, noSideEffect.}
|
||||
## compares the memory blocks ``a`` and ``b``. ``size`` bytes will
|
||||
## be compared. If the blocks are equal, true is returned, false
|
||||
## otherwise. Like any procedure dealing with raw memory this is
|
||||
## *unsafe*.
|
||||
proc equalMem*(a, b: Pointer, size: int): bool {.
|
||||
importc: "equalMem", noDecl, noSideEffect.}
|
||||
## compares the memory blocks ``a`` and ``b``. ``size`` bytes will
|
||||
## be compared. If the blocks are equal, true is returned, false
|
||||
## otherwise. Like any procedure dealing with raw memory this is
|
||||
## *unsafe*.
|
||||
|
||||
proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is not initialized, so reading
|
||||
## from it before writing to it is undefined behaviour!
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared` to allocate from a shared heap.
|
||||
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is initialized with all bytes
|
||||
## containing zero, so it is somewhat safer than ``alloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared0` to allocate from a shared heap.
|
||||
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
|
||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||
## memory block is returned. In either way the block has at least
|
||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
||||
## be freed with ``dealloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `reallocShared` to reallocate from a shared heap.
|
||||
proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
|
||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||
## ``realloc``. This procedure is dangerous! If one forgets to
|
||||
## free the memory a leak occurs; if one tries to access freed
|
||||
## memory (or just freeing it twice!) a core dump may happen
|
||||
## or other memory may be corrupted.
|
||||
## The freed memory must belong to its allocating thread!
|
||||
## Use `deallocShared` to deallocate from a shared heap.
|
||||
proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is not initialized, so reading
|
||||
## from it before writing to it is undefined behaviour!
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared` to allocate from a shared heap.
|
||||
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||
## allocates a new memory block with at least ``size`` bytes. The
|
||||
## block has to be freed with ``realloc(block, 0)`` or
|
||||
## ``dealloc(block)``. The block is initialized with all bytes
|
||||
## containing zero, so it is somewhat safer than ``alloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `allocShared0` to allocate from a shared heap.
|
||||
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
|
||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||
## memory block is returned. In either way the block has at least
|
||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
||||
## be freed with ``dealloc``.
|
||||
## The allocated memory belongs to its allocating thread!
|
||||
## Use `reallocShared` to reallocate from a shared heap.
|
||||
proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
|
||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||
## ``realloc``. This procedure is dangerous! If one forgets to
|
||||
## free the memory a leak occurs; if one tries to access freed
|
||||
## memory (or just freeing it twice!) a core dump may happen
|
||||
## or other memory may be corrupted.
|
||||
## The freed memory must belong to its allocating thread!
|
||||
## Use `deallocShared` to deallocate from a shared heap.
|
||||
|
||||
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
||||
## allocates a new memory block on the shared heap with at
|
||||
## least ``size`` bytes. The block has to be freed with
|
||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
||||
## is not initialized, so reading from it before writing to it is
|
||||
## undefined behaviour!
|
||||
proc allocShared0*(size: int): pointer {.noconv, rtl.}
|
||||
## allocates a new memory block on the shared heap with at
|
||||
## least ``size`` bytes. The block has to be freed with
|
||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
||||
## The block is initialized with all bytes
|
||||
## containing zero, so it is somewhat safer than ``allocShared``.
|
||||
proc reallocShared*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
|
||||
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||
## then a new memory block is returned. In either way the block has at least
|
||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
||||
## block has to be freed with ``deallocShared``.
|
||||
proc deallocShared*(p: Pointer) {.noconv, rtl.}
|
||||
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
||||
## ``reallocShared``. This procedure is dangerous! If one forgets to
|
||||
## free the memory a leak occurs; if one tries to access freed
|
||||
## memory (or just freeing it twice!) a core dump may happen
|
||||
## or other memory may be corrupted.
|
||||
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
||||
## allocates a new memory block on the shared heap with at
|
||||
## least ``size`` bytes. The block has to be freed with
|
||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
||||
## is not initialized, so reading from it before writing to it is
|
||||
## undefined behaviour!
|
||||
proc allocShared0*(size: int): pointer {.noconv, rtl.}
|
||||
## allocates a new memory block on the shared heap with at
|
||||
## least ``size`` bytes. The block has to be freed with
|
||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
||||
## The block is initialized with all bytes
|
||||
## containing zero, so it is somewhat safer than ``allocShared``.
|
||||
proc reallocShared*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
|
||||
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||
## then a new memory block is returned. In either way the block has at least
|
||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
||||
## block has to be freed with ``deallocShared``.
|
||||
proc deallocShared*(p: Pointer) {.noconv, rtl.}
|
||||
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
||||
## ``reallocShared``. This procedure is dangerous! If one forgets to
|
||||
## free the memory a leak occurs; if one tries to access freed
|
||||
## memory (or just freeing it twice!) a core dump may happen
|
||||
## or other memory may be corrupted.
|
||||
|
||||
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
||||
## swaps the values `a` and `b`. This is often more efficient than
|
||||
|
|
@ -1215,15 +1223,16 @@ const
|
|||
|
||||
# GC interface:
|
||||
|
||||
proc getOccupiedMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process and hold data.
|
||||
when not defined(nimrodVM):
|
||||
proc getOccupiedMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process and hold data.
|
||||
|
||||
proc getFreeMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process, but do not
|
||||
## hold any meaningful data.
|
||||
proc getFreeMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process, but do not
|
||||
## hold any meaningful data.
|
||||
|
||||
proc getTotalMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process.
|
||||
proc getTotalMem*(): int {.rtl.}
|
||||
## returns the number of bytes that are owned by the process.
|
||||
|
||||
|
||||
iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
|
||||
|
|
@ -1452,15 +1461,51 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
|
|||
result = s[L]
|
||||
setLen(s, L)
|
||||
|
||||
proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] =
|
||||
proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] {.
|
||||
deprecated.} =
|
||||
## The well-known ``map`` operation from functional programming. Applies
|
||||
## `op` to every item in `data` and returns the result as a sequence.
|
||||
##
|
||||
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
|
||||
newSeq(result, data.len)
|
||||
for i in 0..data.len-1: result[i] = op(data[i])
|
||||
|
||||
proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) =
|
||||
proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) {.
|
||||
deprecated.} =
|
||||
## The well-known ``map`` operation from functional programming. Applies
|
||||
## `op` to every item in `data`.
|
||||
## `op` to every item in `data` modifying it directly.
|
||||
##
|
||||
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
|
||||
for i in 0..data.len-1: op(data[i])
|
||||
|
||||
proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] =
|
||||
## Returns a new sequence with the results of `op` applied to every item in
|
||||
## `data`.
|
||||
##
|
||||
## Since the input is not modified you can use this version of ``map`` to
|
||||
## transform the type of the elements in the input sequence. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## a = @[1, 2, 3, 4]
|
||||
## b = map(a, proc(x: int): string = $x)
|
||||
## assert b == @["1", "2", "3", "4"]
|
||||
newSeq(result, data.len)
|
||||
for i in 0..data.len-1: result[i] = op(data[i])
|
||||
|
||||
proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) =
|
||||
## Applies `op` to every item in `data` modifying it directly.
|
||||
##
|
||||
## Note that this version of ``map`` requires your input and output types to
|
||||
## be the same, since they are modified in-place. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var a = @["1", "2", "3", "4"]
|
||||
## echo repr(a)
|
||||
## # --> ["1", "2", "3", "4"]
|
||||
## map(a, proc(x: var string) = x &= "42")
|
||||
## echo repr(a)
|
||||
## # --> ["142", "242", "342", "442"]
|
||||
for i in 0..data.len-1: op(data[i])
|
||||
|
||||
iterator fields*[T: tuple](x: T): TObject {.
|
||||
|
|
@ -1541,41 +1586,42 @@ when false:
|
|||
|
||||
# ----------------- GC interface ---------------------------------------------
|
||||
|
||||
proc GC_disable*() {.rtl, inl.}
|
||||
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
|
||||
## reactivate the GC. Note that in most circumstances one should only disable
|
||||
## the mark and sweep phase with `GC_disableMarkAndSweep`.
|
||||
when not defined(nimrodVM):
|
||||
proc GC_disable*() {.rtl, inl.}
|
||||
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
|
||||
## reactivate the GC. Note that in most circumstances one should only disable
|
||||
## the mark and sweep phase with `GC_disableMarkAndSweep`.
|
||||
|
||||
proc GC_enable*() {.rtl, inl.}
|
||||
## enables the GC again.
|
||||
proc GC_enable*() {.rtl, inl.}
|
||||
## enables the GC again.
|
||||
|
||||
proc GC_fullCollect*() {.rtl.}
|
||||
## forces a full garbage collection pass.
|
||||
## Ordinary code does not need to call this (and should not).
|
||||
proc GC_fullCollect*() {.rtl.}
|
||||
## forces a full garbage collection pass.
|
||||
## Ordinary code does not need to call this (and should not).
|
||||
|
||||
type
|
||||
TGC_Strategy* = enum ## the strategy the GC should use for the application
|
||||
gcThroughput, ## optimize for throughput
|
||||
gcResponsiveness, ## optimize for responsiveness (default)
|
||||
gcOptimizeTime, ## optimize for speed
|
||||
gcOptimizeSpace ## optimize for memory footprint
|
||||
type
|
||||
TGC_Strategy* = enum ## the strategy the GC should use for the application
|
||||
gcThroughput, ## optimize for throughput
|
||||
gcResponsiveness, ## optimize for responsiveness (default)
|
||||
gcOptimizeTime, ## optimize for speed
|
||||
gcOptimizeSpace ## optimize for memory footprint
|
||||
|
||||
proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.}
|
||||
## tells the GC the desired strategy for the application.
|
||||
## **Deprecated** since version 0.8.14. This has always been a nop.
|
||||
proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.}
|
||||
## tells the GC the desired strategy for the application.
|
||||
## **Deprecated** since version 0.8.14. This has always been a nop.
|
||||
|
||||
proc GC_enableMarkAndSweep*() {.rtl.}
|
||||
proc GC_disableMarkAndSweep*() {.rtl.}
|
||||
## the current implementation uses a reference counting garbage collector
|
||||
## with a seldomly run mark and sweep phase to free cycles. The mark and
|
||||
## sweep phase may take a long time and is not needed if the application
|
||||
## does not create cycles. Thus the mark and sweep phase can be deactivated
|
||||
## and activated separately from the rest of the GC.
|
||||
proc GC_enableMarkAndSweep*() {.rtl.}
|
||||
proc GC_disableMarkAndSweep*() {.rtl.}
|
||||
## the current implementation uses a reference counting garbage collector
|
||||
## with a seldomly run mark and sweep phase to free cycles. The mark and
|
||||
## sweep phase may take a long time and is not needed if the application
|
||||
## does not create cycles. Thus the mark and sweep phase can be deactivated
|
||||
## and activated separately from the rest of the GC.
|
||||
|
||||
proc GC_getStatistics*(): string {.rtl.}
|
||||
## returns an informative string about the GC's activity. This may be useful
|
||||
## for tweaking.
|
||||
|
||||
proc GC_getStatistics*(): string {.rtl.}
|
||||
## returns an informative string about the GC's activity. This may be useful
|
||||
## for tweaking.
|
||||
|
||||
proc GC_ref*[T](x: ref T) {.magic: "GCref".}
|
||||
proc GC_ref*[T](x: seq[T]) {.magic: "GCref".}
|
||||
proc GC_ref*(x: string) {.magic: "GCref".}
|
||||
|
|
@ -1701,28 +1747,29 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
|
|||
## get type information for `x`. Ordinary code should not use this, but
|
||||
## the `typeinfo` module instead.
|
||||
|
||||
when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
when not defined(EcmaScript): #and not defined(NimrodVM):
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
|
||||
proc initGC()
|
||||
when not defined(boehmgc) and not defined(useMalloc):
|
||||
proc initAllocator() {.inline.}
|
||||
when not defined(NimrodVM):
|
||||
proc initGC()
|
||||
when not defined(boehmgc) and not defined(useMalloc):
|
||||
proc initAllocator() {.inline.}
|
||||
|
||||
proc initStackBottom() {.inline, compilerproc.} =
|
||||
# WARNING: This is very fragile! An array size of 8 does not work on my
|
||||
# Linux 64bit system. Very strange, but we are at the will of GCC's
|
||||
# optimizer...
|
||||
when defined(setStackBottom):
|
||||
var locals {.volatile.}: pointer
|
||||
locals = addr(locals)
|
||||
setStackBottom(locals)
|
||||
proc initStackBottom() {.inline, compilerproc.} =
|
||||
# WARNING: This is very fragile! An array size of 8 does not work on my
|
||||
# Linux 64bit system. -- That's because the stack direction is the other
|
||||
# way round.
|
||||
when defined(setStackBottom):
|
||||
var locals {.volatile.}: pointer
|
||||
locals = addr(locals)
|
||||
setStackBottom(locals)
|
||||
|
||||
var
|
||||
strDesc: TNimType
|
||||
var
|
||||
strDesc: TNimType
|
||||
|
||||
strDesc.size = sizeof(string)
|
||||
strDesc.kind = tyString
|
||||
strDesc.flags = {ntfAcyclic}
|
||||
strDesc.size = sizeof(string)
|
||||
strDesc.kind = tyString
|
||||
strDesc.flags = {ntfAcyclic}
|
||||
|
||||
include "system/ansi_c"
|
||||
|
||||
|
|
@ -1730,28 +1777,27 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
result = int(c_strcmp(x, y))
|
||||
|
||||
const pccHack = if defined(pcc): "_" else: "" # Hack for PCC
|
||||
when defined(windows):
|
||||
# work-around C's sucking abstraction:
|
||||
# BUGFIX: stdin and stdout should be binary files!
|
||||
proc setmode(handle, mode: int) {.importc: pccHack & "setmode",
|
||||
header: "<io.h>".}
|
||||
proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno",
|
||||
header: "<fcntl.h>".}
|
||||
var
|
||||
O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int
|
||||
when not defined(NimrodVM):
|
||||
when defined(windows):
|
||||
# work-around C's sucking abstraction:
|
||||
# BUGFIX: stdin and stdout should be binary files!
|
||||
proc setmode(handle, mode: int) {.importc: pccHack & "setmode",
|
||||
header: "<io.h>".}
|
||||
proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno",
|
||||
header: "<fcntl.h>".}
|
||||
var
|
||||
O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int
|
||||
|
||||
# we use binary mode in Windows:
|
||||
setmode(fileno(c_stdin), O_BINARY)
|
||||
setmode(fileno(c_stdout), O_BINARY)
|
||||
|
||||
when defined(endb):
|
||||
proc endbStep()
|
||||
# we use binary mode in Windows:
|
||||
setmode(fileno(c_stdin), O_BINARY)
|
||||
setmode(fileno(c_stdout), O_BINARY)
|
||||
|
||||
when defined(endb):
|
||||
proc endbStep()
|
||||
|
||||
# ----------------- IO Part ------------------------------------------------
|
||||
|
||||
type
|
||||
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||
# data hiding
|
||||
CFile {.importc: "FILE", nodecl, final, incompletestruct.} = object
|
||||
TFile* = ptr CFile ## The type representing a file handle.
|
||||
|
||||
TFileMode* = enum ## The file mode when opening a file.
|
||||
|
|
@ -1777,7 +1823,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
## The standard error stream.
|
||||
##
|
||||
## Note: In my opinion, this should not be used -- the concept of a
|
||||
## separate error stream is a design flaw of UNIX. A seperate *message
|
||||
## separate error stream is a design flaw of UNIX. A separate *message
|
||||
## stream* is a good idea, but since it is named ``stderr`` there are few
|
||||
## programs out there that distinguish properly between ``stdout`` and
|
||||
## ``stderr``. So, that's what you get if you don't name your variables
|
||||
|
|
@ -1934,31 +1980,32 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
proc allocCStringArray*(a: openArray[string]): cstringArray =
|
||||
## creates a NULL terminated cstringArray from `a`. The result has to
|
||||
## be freed with `deallocCStringArray` after it's not needed anymore.
|
||||
result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
|
||||
for i in 0 .. a.high:
|
||||
# XXX get rid of this string copy here:
|
||||
var x = a[i]
|
||||
result[i] = cast[cstring](alloc0(x.len+1))
|
||||
copyMem(result[i], addr(x[0]), x.len)
|
||||
when not defined(NimrodVM):
|
||||
proc allocCStringArray*(a: openArray[string]): cstringArray =
|
||||
## creates a NULL terminated cstringArray from `a`. The result has to
|
||||
## be freed with `deallocCStringArray` after it's not needed anymore.
|
||||
result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
|
||||
for i in 0 .. a.high:
|
||||
# XXX get rid of this string copy here:
|
||||
var x = a[i]
|
||||
result[i] = cast[cstring](alloc0(x.len+1))
|
||||
copyMem(result[i], addr(x[0]), x.len)
|
||||
|
||||
proc deallocCStringArray*(a: cstringArray) =
|
||||
## frees a NULL terminated cstringArray.
|
||||
var i = 0
|
||||
while a[i] != nil:
|
||||
dealloc(a[i])
|
||||
inc(i)
|
||||
dealloc(a)
|
||||
proc deallocCStringArray*(a: cstringArray) =
|
||||
## frees a NULL terminated cstringArray.
|
||||
var i = 0
|
||||
while a[i] != nil:
|
||||
dealloc(a[i])
|
||||
inc(i)
|
||||
dealloc(a)
|
||||
|
||||
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||
## atomic increment of `memLoc`. Returns the value after the operation.
|
||||
|
||||
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||
## atomic decrement of `memLoc`. Returns the value after the operation.
|
||||
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||
## atomic increment of `memLoc`. Returns the value after the operation.
|
||||
|
||||
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||
## atomic decrement of `memLoc`. Returns the value after the operation.
|
||||
|
||||
include "system/atomics"
|
||||
include "system/atomics"
|
||||
|
||||
type
|
||||
PSafePoint = ptr TSafePoint
|
||||
|
|
@ -1974,71 +2021,76 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
when hasThreadSupport:
|
||||
include "system/syslocks"
|
||||
include "system/threads"
|
||||
elif not defined(nogc):
|
||||
elif not defined(nogc) and not defined(NimrodVM):
|
||||
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
|
||||
initGC()
|
||||
|
||||
proc setControlCHook*(hook: proc () {.noconv.})
|
||||
## allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
|
||||
proc writeStackTrace*() {.tags: [FWriteIO].}
|
||||
## writes the current stack trace to ``stderr``. This is only works
|
||||
## for debug builds.
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string
|
||||
## gets the current stack trace. This only works for debug builds.
|
||||
|
||||
proc getStackTrace*(e: ref E_Base): string
|
||||
## gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the ``raise`` statement. This only works for debug builds.
|
||||
when not defined(NimrodVM):
|
||||
proc setControlCHook*(hook: proc () {.noconv.})
|
||||
## allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
include "system/excpt"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/arithm"
|
||||
{.pop.} # stack trace
|
||||
{.pop.} # stack trace
|
||||
|
||||
when hostOS != "standalone": include "system/dyncalls"
|
||||
include "system/sets"
|
||||
proc writeStackTrace*() {.tags: [FWriteIO].}
|
||||
## writes the current stack trace to ``stderr``. This is only works
|
||||
## for debug builds.
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string
|
||||
## gets the current stack trace. This only works for debug builds.
|
||||
|
||||
const
|
||||
GenericSeqSize = (2 * sizeof(int))
|
||||
|
||||
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
|
||||
var d: int
|
||||
var a = cast[TAddress](aa)
|
||||
case n.typ.size
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)[])
|
||||
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
|
||||
return d
|
||||
|
||||
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
var discr = getDiscriminant(aa, n)
|
||||
if discr <% n.len:
|
||||
result = n.sons[discr]
|
||||
if result == nil: result = n.sons[n.len]
|
||||
# n.sons[n.len] contains the ``else`` part (but may be nil)
|
||||
proc getStackTrace*(e: ref E_Base): string
|
||||
## gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the ``raise`` statement. This only works for debug builds.
|
||||
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
include "system/excpt"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/arithm"
|
||||
{.pop.} # stack trace
|
||||
{.pop.} # stack trace
|
||||
|
||||
when hostOS != "standalone" and not defined(NimrodVM):
|
||||
include "system/dyncalls"
|
||||
when not defined(NimrodVM):
|
||||
include "system/sets"
|
||||
|
||||
include "system/mmdisp"
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hostOS != "standalone": include "system/sysstr"
|
||||
{.pop.}
|
||||
const
|
||||
GenericSeqSize = (2 * sizeof(int))
|
||||
|
||||
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
|
||||
var d: int
|
||||
var a = cast[TAddress](aa)
|
||||
case n.typ.size
|
||||
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
|
||||
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr int32](a +% n.offset)[])
|
||||
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
|
||||
return d
|
||||
|
||||
include "system/sysio"
|
||||
when hasThreadSupport:
|
||||
include "system/channels"
|
||||
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
var discr = getDiscriminant(aa, n)
|
||||
if discr <% n.len:
|
||||
result = n.sons[discr]
|
||||
if result == nil: result = n.sons[n.len]
|
||||
# n.sons[n.len] contains the ``else`` part (but may be nil)
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
include "system/mmdisp"
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hostOS != "standalone": include "system/sysstr"
|
||||
{.pop.}
|
||||
|
||||
include "system/sysio"
|
||||
when hasThreadSupport:
|
||||
include "system/channels"
|
||||
else:
|
||||
include "system/sysio"
|
||||
|
||||
iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
|
||||
## Iterate over any line in the file named `filename`.
|
||||
|
|
@ -2053,7 +2105,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
var res = TaintedString(newStringOfCap(80))
|
||||
while f.readLine(res): yield TaintedString(res)
|
||||
|
||||
when hostOS != "standalone":
|
||||
when hostOS != "standalone" and not defined(NimrodVM):
|
||||
include "system/assign"
|
||||
include "system/repr"
|
||||
|
||||
|
|
@ -2078,43 +2130,46 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
excHandler.raiseAction = action
|
||||
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when defined(endb):
|
||||
when defined(endb) and not defined(NimrodVM):
|
||||
include "system/debugger"
|
||||
|
||||
when defined(profiler) or defined(memProfiler):
|
||||
include "system/profiler"
|
||||
{.pop.} # stacktrace
|
||||
|
||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be likely. This is a hint for the
|
||||
## optimizer.
|
||||
|
||||
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
||||
## optimizer.
|
||||
when not defined(NimrodVM):
|
||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be likely. This is a hint for the
|
||||
## optimizer.
|
||||
|
||||
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||
## retrieves the raw proc pointer of the closure `x`. This is
|
||||
## useful for interfacing closures with C.
|
||||
{.emit: """
|
||||
`result` = `x`.ClPrc;
|
||||
""".}
|
||||
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
||||
## optimizer.
|
||||
|
||||
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||
## retrieves the raw proc pointer of the closure `x`. This is
|
||||
## useful for interfacing closures with C.
|
||||
{.emit: """
|
||||
`result` = `x`.ClPrc;
|
||||
""".}
|
||||
|
||||
proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||
## retrieves the raw environment pointer of the closure `x`. This is
|
||||
## useful for interfacing closures with C.
|
||||
{.emit: """
|
||||
`result` = `x`.ClEnv;
|
||||
""".}
|
||||
proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||
## retrieves the raw environment pointer of the closure `x`. This is
|
||||
## useful for interfacing closures with C.
|
||||
{.emit: """
|
||||
`result` = `x`.ClEnv;
|
||||
""".}
|
||||
|
||||
proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
|
||||
## can be used to determine if a first class iterator has finished.
|
||||
{.emit: """
|
||||
`result` = *((NI*) `x`.ClEnv) < 0;
|
||||
""".}
|
||||
proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
|
||||
## can be used to determine if a first class iterator has finished.
|
||||
{.emit: """
|
||||
`result` = *((NI*) `x`.ClEnv) < 0;
|
||||
""".}
|
||||
|
||||
elif defined(ecmaScript) or defined(NimrodVM):
|
||||
elif defined(ecmaScript):
|
||||
# Stubs:
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
||||
|
||||
proc GC_disable() = nil
|
||||
proc GC_enable() = nil
|
||||
proc GC_fullCollect() = nil
|
||||
|
|
@ -2145,6 +2200,10 @@ elif defined(ecmaScript) or defined(NimrodVM):
|
|||
if x == y: return 0
|
||||
if x < y: return -1
|
||||
return 1
|
||||
|
||||
when defined(nimffi):
|
||||
include "system/sysio"
|
||||
|
||||
|
||||
proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
||||
## a shorthand for ``echo(errormsg); quit(errorcode)``.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -21,9 +21,8 @@ proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
|
|||
proc c_memset(p: pointer, value: cint, size: int) {.nodecl, importc: "memset".}
|
||||
|
||||
type
|
||||
C_TextFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||
# data hiding
|
||||
C_BinaryFile {.importc: "FILE", nodecl, final.} = object
|
||||
C_TextFile {.importc: "FILE", nodecl, final, incompleteStruct.} = object
|
||||
C_BinaryFile {.importc: "FILE", nodecl, final, incompleteStruct.} = object
|
||||
C_TextFileStar = ptr CTextFile
|
||||
C_BinaryFileStar = ptr CBinaryFile
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -579,6 +579,10 @@ proc hash(Data: Pointer, Size: int): THash =
|
|||
Dec(s)
|
||||
result = !$h
|
||||
|
||||
proc hashGcHeader(data: pointer): THash =
|
||||
const headerSize = sizeof(int)*2
|
||||
result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
|
||||
|
||||
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
|
||||
h: THash): THash
|
||||
proc genericHashAux(dest: Pointer, n: ptr TNimNode, shallow: bool,
|
||||
|
|
@ -606,20 +610,22 @@ proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
|
|||
result = h
|
||||
if x != nil:
|
||||
let s = cast[NimString](x)
|
||||
when true:
|
||||
result = result !& hash(x, s.len)
|
||||
when defined(trackGcHeaders):
|
||||
result = result !& hashGcHeader(x)
|
||||
else:
|
||||
let y = cast[pointer](cast[int](x) -% 2*sizeof(int))
|
||||
result = result !& hash(y, s.len + 2*sizeof(int))
|
||||
result = result !& hash(x, s.len)
|
||||
of tySequence:
|
||||
var x = cast[ppointer](dest)
|
||||
var dst = cast[taddress](cast[ppointer](dest)[])
|
||||
result = h
|
||||
if dst != 0:
|
||||
for i in 0..cast[pgenericseq](dst).len-1:
|
||||
result = result !& genericHashAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
mt.Base, shallow, result)
|
||||
when defined(trackGcHeaders):
|
||||
result = result !& hashGcHeader(cast[ppointer](dest)[])
|
||||
else:
|
||||
for i in 0..cast[pgenericseq](dst).len-1:
|
||||
result = result !& genericHashAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
mt.Base, shallow, result)
|
||||
of tyObject, tyTuple:
|
||||
# we don't need to copy m_type field for tyObject, as they are equal anyway
|
||||
result = genericHashAux(dest, mt.node, shallow, h)
|
||||
|
|
@ -630,13 +636,18 @@ proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
|
|||
result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size),
|
||||
mt.base, shallow, result)
|
||||
of tyRef:
|
||||
if shallow:
|
||||
result = h !& hash(dest, mt.size)
|
||||
else:
|
||||
result = h
|
||||
when defined(trackGcHeaders):
|
||||
var s = cast[ppointer](dest)[]
|
||||
if s != nil:
|
||||
result = result !& genericHashAux(s, mt.base, shallow, result)
|
||||
result = result !& hashGcHeader(s)
|
||||
else:
|
||||
if shallow:
|
||||
result = h !& hash(dest, mt.size)
|
||||
else:
|
||||
result = h
|
||||
var s = cast[ppointer](dest)[]
|
||||
if s != nil:
|
||||
result = result !& genericHashAux(s, mt.base, shallow, result)
|
||||
# hash the object header:
|
||||
#const headerSize = sizeof(int)*2
|
||||
#result = result !& hash(cast[pointer](cast[int](s) -% headerSize),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -16596,6 +16596,9 @@ proc message_dialog_new*(parent: PWindow, flags: TDialogFlags,
|
|||
thetype: TMessageType, buttons: TButtonsType,
|
||||
message_format: cstring): PMessageDialog{.varargs,
|
||||
cdecl, importc: "gtk_message_dialog_new", dynlib: lib.}
|
||||
proc set_markup*(msgDialog: PMessageDialog, str: cstring) {.cdecl,
|
||||
importc: "gtk_message_dialog_set_markup", dynlib: lib.}
|
||||
|
||||
proc signal_new*(name: cstring, signal_flags: TSignalRunType,
|
||||
object_type: TType, function_offset: guint,
|
||||
marshaller: TSignalMarshaller, return_val: TType, n_args: guint): guint{.
|
||||
|
|
@ -16893,6 +16896,15 @@ type
|
|||
proc set_tooltip_text*(w: PWidget, t: cstring){.cdecl,
|
||||
dynlib: lib, importc: "gtk_widget_set_tooltip_text".}
|
||||
|
||||
proc get_tooltip_text*(w: PWidget): cstring{.cdecl,
|
||||
dynlib: lib, importc: "gtk_widget_get_tooltip_text".}
|
||||
|
||||
proc set_tooltip_markup*(w: PWidget, m: cstring) {.cdecl, dynlib: lib,
|
||||
importc: "gtk_widget_set_tooltip_markup".}
|
||||
|
||||
proc get_tooltip_markup*(w: PWidget): cstring {.cdecl, dynlib: lib,
|
||||
importc: "gtk_widget_get_tooltip_markup".}
|
||||
|
||||
proc set_tooltip_column*(w: PTreeview, column: gint){.cdecl,
|
||||
dynlib: lib, importc: "gtk_tree_view_set_tooltip_column".}
|
||||
|
||||
|
|
@ -16905,6 +16917,9 @@ proc trigger_tooltip_query*(widg: PTooltip){.cdecl, dynlib: lib,
|
|||
proc set_has_tooltip*(widget: PWidget, b: gboolean){.cdecl, dynlib: lib,
|
||||
importc: "gtk_widget_set_has_tooltip".}
|
||||
|
||||
proc get_has_tooltip*(widget: PWidget): gboolean{.cdecl, dynlib: lib,
|
||||
importc: "gtk_widget_get_has_tooltip".}
|
||||
|
||||
proc set_markup*(tp: PTooltip, mk: cstring){.cdecl, dynlib: lib,
|
||||
importc: "gtk_tooltip_set_markup".}
|
||||
|
||||
|
|
@ -17035,6 +17050,10 @@ proc remove*(combo_box: PComboBoxText; position: gint){.cdecl,
|
|||
importc: "gtk_combo_box_text_remove", dynlib: lib.}
|
||||
proc get_active_text*(combo_box: PComboBoxText): cstring{.cdecl,
|
||||
importc: "gtk_combo_box_text_get_active_text", dynlib: lib.}
|
||||
proc is_active*(win: PWindow): gboolean{.cdecl,
|
||||
importc: "gtk_window_is_active", dynlib: lib.}
|
||||
proc has_toplevel_focus*(win: PWindow): gboolean{.cdecl,
|
||||
importc: "gtk_window_has_toplevel_focus", dynlib: lib.}
|
||||
|
||||
proc nimrod_init*() =
|
||||
var
|
||||
|
|
|
|||
149
lib/wrappers/libffi.nim
Normal file
149
lib/wrappers/libffi.nim
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# -----------------------------------------------------------------*-C-*-
|
||||
# libffi 3.0.10 - Copyright (c) 2011 Anthony Green
|
||||
# - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the ``Software''), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use, copy,
|
||||
# modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
# of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when defined(windows):
|
||||
const libffidll* = "libffi.dll"
|
||||
elif defined(macosx):
|
||||
const libffidll* = "libffi.dylib"
|
||||
else:
|
||||
const libffidll* = "libffi.so"
|
||||
|
||||
type
|
||||
TArg* = int
|
||||
TSArg* = int
|
||||
|
||||
when defined(windows) and defined(x86):
|
||||
type
|
||||
TABI* {.size: sizeof(cint).} = enum
|
||||
FIRST_ABI, SYSV, STDCALL
|
||||
|
||||
const DEFAULT_ABI* = SYSV
|
||||
elif defined(amd64) and defined(windows):
|
||||
type
|
||||
TABI* {.size: sizeof(cint).} = enum
|
||||
FIRST_ABI, WIN64
|
||||
const DEFAULT_ABI* = WIN64
|
||||
else:
|
||||
type
|
||||
TABI* {.size: sizeof(cint).} = enum
|
||||
FIRST_ABI, SYSV, UNIX64
|
||||
|
||||
when defined(i386):
|
||||
const DEFAULT_ABI* = SYSV
|
||||
else:
|
||||
const DEFAULT_ABI* = UNIX64
|
||||
|
||||
const
|
||||
tkVOID* = 0
|
||||
tkINT* = 1
|
||||
tkFLOAT* = 2
|
||||
tkDOUBLE* = 3
|
||||
tkLONGDOUBLE* = 4
|
||||
tkUINT8* = 5
|
||||
tkSINT8* = 6
|
||||
tkUINT16* = 7
|
||||
tkSINT16* = 8
|
||||
tkUINT32* = 9
|
||||
tkSINT32* = 10
|
||||
tkUINT64* = 11
|
||||
tkSINT64* = 12
|
||||
tkSTRUCT* = 13
|
||||
tkPOINTER* = 14
|
||||
|
||||
tkLAST = tkPOINTER
|
||||
tkSMALL_STRUCT_1B* = (tkLAST + 1)
|
||||
tkSMALL_STRUCT_2B* = (tkLAST + 2)
|
||||
tkSMALL_STRUCT_4B* = (tkLAST + 3)
|
||||
|
||||
type
|
||||
TType* = object
|
||||
size*: int
|
||||
alignment*: uint16
|
||||
typ*: uint16
|
||||
elements*: ptr ptr TType
|
||||
|
||||
var
|
||||
type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType
|
||||
type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType
|
||||
type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType
|
||||
type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType
|
||||
type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType
|
||||
type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType
|
||||
type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType
|
||||
type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType
|
||||
type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType
|
||||
type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType
|
||||
type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType
|
||||
type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType
|
||||
type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType
|
||||
|
||||
type
|
||||
Tstatus* {.size: sizeof(cint).} = enum
|
||||
OK, BAD_TYPEDEF, BAD_ABI
|
||||
TTypeKind* = cuint
|
||||
TCif* {.pure, final.} = object
|
||||
abi*: TABI
|
||||
nargs*: cuint
|
||||
arg_types*: ptr ptr TType
|
||||
rtype*: ptr TType
|
||||
bytes*: cuint
|
||||
flags*: cuint
|
||||
|
||||
type
|
||||
TRaw* = object
|
||||
sint*: TSArg
|
||||
|
||||
proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
|
||||
avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call",
|
||||
dynlib: libffidll.}
|
||||
proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl,
|
||||
importc: "ffi_ptrarray_to_raw", dynlib: libffidll.}
|
||||
proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl,
|
||||
importc: "ffi_raw_to_ptrarray", dynlib: libffidll.}
|
||||
proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size",
|
||||
dynlib: libffidll.}
|
||||
|
||||
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
|
||||
atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif",
|
||||
dynlib: libffidll.}
|
||||
proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
|
||||
avalue: ptr pointer) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
|
||||
|
||||
# the same with an easier interface:
|
||||
type
|
||||
TParamList* = array[0..100, ptr TType]
|
||||
TArgList* = array[0..100, pointer]
|
||||
|
||||
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
|
||||
atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif",
|
||||
dynlib: libffidll.}
|
||||
proc call*(cif: var Tcif; fn, rvalue: pointer;
|
||||
avalue: TArgList) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
|
||||
|
||||
# Useful for eliminating compiler warnings
|
||||
##define FFI_FN(f) ((void (*)(void))f)
|
||||
|
|
@ -35,19 +35,32 @@
|
|||
#** In french or in english
|
||||
#
|
||||
|
||||
when defined(MACOSX):
|
||||
const
|
||||
NAME* = "liblua(|5.2|5.1|5.0).dylib"
|
||||
LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib"
|
||||
elif defined(UNIX):
|
||||
const
|
||||
NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
|
||||
LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
|
||||
else:
|
||||
const
|
||||
NAME* = "lua(|5.2|5.1|5.0).dll"
|
||||
LIB_NAME* = "lua(|5.2|5.1|5.0).dll"
|
||||
|
||||
when defined(useLuajit):
|
||||
when defined(MACOSX):
|
||||
const
|
||||
NAME* = "libluajit.dylib"
|
||||
LIB_NAME* = "libluajit.dylib"
|
||||
elif defined(UNIX):
|
||||
const
|
||||
NAME* = "libluajit.so(|.0)"
|
||||
LIB_NAME* = "libluajit.so(|.0)"
|
||||
else:
|
||||
const
|
||||
NAME* = "luajit.dll"
|
||||
LIB_NAME* = "luajit.dll"
|
||||
else:
|
||||
when defined(MACOSX):
|
||||
const
|
||||
NAME* = "liblua(|5.2|5.1|5.0).dylib"
|
||||
LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib"
|
||||
elif defined(UNIX):
|
||||
const
|
||||
NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
|
||||
LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
|
||||
else:
|
||||
const
|
||||
NAME* = "lua(|5.2|5.1|5.0).dll"
|
||||
LIB_NAME* = "lua(|5.2|5.1|5.0).dll"
|
||||
|
||||
const
|
||||
VERSION* = "Lua 5.1"
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
## OpenSSL support
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when defined(WINDOWS):
|
||||
const
|
||||
DLLSSLName = "(ssleay32|libssl32).dll"
|
||||
|
|
|
|||
|
|
@ -128,19 +128,19 @@
|
|||
#
|
||||
#******************************************************************************
|
||||
|
||||
import
|
||||
import
|
||||
sdl
|
||||
|
||||
when defined(windows):
|
||||
const
|
||||
when defined(windows):
|
||||
const
|
||||
ImageLibName = "SDL_Image.dll"
|
||||
elif defined(macosx):
|
||||
const
|
||||
elif defined(macosx):
|
||||
const
|
||||
ImageLibName = "libSDL_image-1.2.0.dylib"
|
||||
else:
|
||||
const
|
||||
ImageLibName = "libSDL_image.so"
|
||||
const
|
||||
else:
|
||||
const
|
||||
ImageLibName = "libSDL_image(.so|-1.2.so.0)"
|
||||
const
|
||||
IMAGE_MAJOR_VERSION* = 1
|
||||
IMAGE_MINOR_VERSION* = 2
|
||||
IMAGE_PATCHLEVEL* = 5
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue