Merge branch 'devel' into newasync
This commit is contained in:
commit
a3f3efa1ba
848 changed files with 25966 additions and 75620 deletions
|
|
@ -34,7 +34,7 @@ proc reverse*[T](a: var openArray[T]) =
|
|||
## reverses the array `a`.
|
||||
reverse(a, 0, a.high)
|
||||
|
||||
proc binarySearch*[T](a: openarray[T], key: T): int =
|
||||
proc binarySearch*[T](a: openArray[T], key: T): int =
|
||||
## binary search for `key` in `a`. Returns -1 if not found.
|
||||
var b = len(a)
|
||||
while result < b:
|
||||
|
|
@ -79,7 +79,7 @@ proc merge[T](a, b: var openArray[T], lo, m, hi: int,
|
|||
inc(bb)
|
||||
inc(j)
|
||||
else:
|
||||
CopyMem(addr(b[0]), addr(a[j]), sizeof(T)*(m-j+1))
|
||||
copyMem(addr(b[0]), addr(a[j]), sizeof(T)*(m-j+1))
|
||||
j = m+1
|
||||
var i = 0
|
||||
var k = lo
|
||||
|
|
@ -131,3 +131,36 @@ proc sort*[T](a: var openArray[T],
|
|||
dec(m, s*2)
|
||||
s = s*2
|
||||
|
||||
proc product*[T](x: openarray[seq[T]]): seq[seq[T]] =
|
||||
## produces the Cartesian product of the array. Warning: complexity
|
||||
## may explode.
|
||||
result = @[]
|
||||
if x.len == 0:
|
||||
return
|
||||
if x.len == 1:
|
||||
result = @x
|
||||
return
|
||||
var
|
||||
indexes = newSeq[int](x.len)
|
||||
initial = newSeq[int](x.len)
|
||||
index = 0
|
||||
# replace with newSeq as soon as #853 is fixed
|
||||
var next: seq[T] = @[]
|
||||
next.setLen(x.len)
|
||||
for i in 0..(x.len-1):
|
||||
if len(x[i]) == 0: return
|
||||
initial[i] = len(x[i])-1
|
||||
indexes = initial
|
||||
while true:
|
||||
while indexes[index] == -1:
|
||||
indexes[index] = initial[index]
|
||||
index +=1
|
||||
if index == x.len: return
|
||||
indexes[index] -=1
|
||||
for ni, i in indexes:
|
||||
next[ni] = x[ni][i]
|
||||
var res: seq[T]
|
||||
shallowCopy(res, next)
|
||||
result.add(res)
|
||||
index = 0
|
||||
indexes[index] -=1
|
||||
|
|
|
|||
|
|
@ -139,27 +139,27 @@ type
|
|||
proc newDelegate*(): PDelegate =
|
||||
## Creates a new delegate.
|
||||
new(result)
|
||||
result.handleRead = (proc (h: PObject) = nil)
|
||||
result.handleWrite = (proc (h: PObject) = nil)
|
||||
result.handleError = (proc (h: PObject) = nil)
|
||||
result.handleRead = (proc (h: PObject) = discard)
|
||||
result.handleWrite = (proc (h: PObject) = discard)
|
||||
result.handleError = (proc (h: PObject) = discard)
|
||||
result.hasDataBuffered = (proc (h: PObject): bool = return false)
|
||||
result.task = (proc (h: PObject) = nil)
|
||||
result.task = (proc (h: PObject) = discard)
|
||||
result.mode = fmRead
|
||||
|
||||
proc newAsyncSocket(): PAsyncSocket =
|
||||
new(result)
|
||||
result.info = SockIdle
|
||||
|
||||
result.handleRead = (proc (s: PAsyncSocket) = nil)
|
||||
result.handleRead = (proc (s: PAsyncSocket) = discard)
|
||||
result.handleWrite = nil
|
||||
result.handleConnect = (proc (s: PAsyncSocket) = nil)
|
||||
result.handleAccept = (proc (s: PAsyncSocket) = nil)
|
||||
result.handleTask = (proc (s: PAsyncSocket) = nil)
|
||||
result.handleConnect = (proc (s: PAsyncSocket) = discard)
|
||||
result.handleAccept = (proc (s: PAsyncSocket) = discard)
|
||||
result.handleTask = (proc (s: PAsyncSocket) = discard)
|
||||
|
||||
result.lineBuffer = "".TaintedString
|
||||
result.sendBuffer = ""
|
||||
|
||||
proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
protocol: TProtocol = IPPROTO_TCP,
|
||||
buffered = true): PAsyncSocket =
|
||||
## Initialises an AsyncSocket object. If a socket cannot be initialised
|
||||
|
|
@ -233,8 +233,11 @@ proc asyncSockHandleWrite(h: PObject) =
|
|||
let sock = PAsyncSocket(h)
|
||||
try:
|
||||
let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
|
||||
assert bytesSent > 0
|
||||
if bytesSent != sock.sendBuffer.len:
|
||||
if bytesSent == 0:
|
||||
# Apparently the socket cannot be written to. Even though select
|
||||
# just told us that it can be... This used to be an assert. Just
|
||||
# do nothing instead.
|
||||
elif bytesSent != sock.sendBuffer.len:
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
|
||||
elif bytesSent == sock.sendBuffer.len:
|
||||
sock.sendBuffer = ""
|
||||
|
|
@ -686,5 +689,5 @@ when isMainModule:
|
|||
server.listen()
|
||||
d.register(server)
|
||||
|
||||
while d.poll(-1): nil
|
||||
while d.poll(-1): discard
|
||||
|
||||
|
|
|
|||
|
|
@ -1,117 +1,127 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a base64 encoder and decoder.
|
||||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
var numLines = (total + lineLen - 1) div lineLen
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a base64 encoder and decoder.
|
||||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
var numLines = (total + lineLen - 1) div lineLen
|
||||
if numLines > 0: inc(total, (numLines-1) * newLine.len)
|
||||
|
||||
result = newString(total)
|
||||
var i = 0
|
||||
var r = 0
|
||||
var currLine = 0
|
||||
while i < s.len - 2:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
var c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
|
||||
if i < s.len-1:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
var a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
assert(r == result.len)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
|
||||
var i = 0
|
||||
var r = 0
|
||||
while true:
|
||||
while s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
var a = s[i].decodeByte
|
||||
var b = s[i+1].decodeByte
|
||||
var c = s[i+2].decodeByte
|
||||
var d = s[i+3].decodeByte
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText]
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
|
||||
|
||||
result = newString(total)
|
||||
var i = 0
|
||||
var r = 0
|
||||
var currLine = 0
|
||||
while i < s.len - 2:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
var c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
|
||||
if i < s.len-1:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
var a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
assert(r == result.len)
|
||||
|
||||
proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
|
||||
var i = 0
|
||||
var r = 0
|
||||
while true:
|
||||
while s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
var a = s[i].decodeByte
|
||||
var b = s[i+1].decodeByte
|
||||
var c = s[i+2].decodeByte
|
||||
var d = s[i+3].decodeByte
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText]
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ proc openDefaultBrowser*(url: string) =
|
|||
when useWinUnicode:
|
||||
var o = newWideCString("open")
|
||||
var u = newWideCString(url)
|
||||
discard ShellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
|
||||
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
|
||||
else:
|
||||
discard ShellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
|
||||
discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
|
||||
elif defined(macosx):
|
||||
discard execShellCmd("open " & quoteIfContainsWhite(url))
|
||||
discard execShellCmd("open " & quoteShell(url))
|
||||
else:
|
||||
const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
|
||||
var u = quoteIfContainsWhite(url)
|
||||
var u = quoteShell(url)
|
||||
for a in items(attempts):
|
||||
if execShellCmd(a & u) == 0: return
|
||||
for b in getEnv("BROWSER").string.split(PathSep):
|
||||
|
|
@ -45,4 +45,4 @@ proc openDefaultBrowser*(url: string) =
|
|||
discard startProcess(command=b, args=[url], options={poUseShell})
|
||||
return
|
||||
except EOS:
|
||||
nil
|
||||
discard
|
||||
|
|
|
|||
|
|
@ -342,16 +342,35 @@ proc writeContentType*() =
|
|||
##
|
||||
## .. code-block:: Nimrod
|
||||
## write(stdout, "Content-type: text/html\n\n")
|
||||
##
|
||||
## It also modifies the debug stack traces so that they contain
|
||||
## ``<br />`` and are easily readable in a browser.
|
||||
write(stdout, "Content-type: text/html\n\n")
|
||||
system.stackTraceNewLine = "<br />\n"
|
||||
|
||||
proc setStackTraceNewLine*() =
|
||||
## Modifies the debug stack traces so that they contain
|
||||
## ``<br />`` and are easily readable in a browser.
|
||||
system.stackTraceNewLine = "<br />\n"
|
||||
proc resetForStacktrace() =
|
||||
stdout.write """<!--: spam
|
||||
Content-Type: text/html
|
||||
|
||||
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
|
||||
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
|
||||
</font> </font> </font> </script> </object> </blockquote> </pre>
|
||||
</table> </table> </table> </table> </table> </font> </font> </font>
|
||||
"""
|
||||
|
||||
proc writeErrorMessage*(data: string) =
|
||||
## Tries to reset browser state and writes `data` to stdout in
|
||||
## <plaintext> tag.
|
||||
resetForStacktrace()
|
||||
# We use <plaintext> here, instead of escaping, so stacktrace can
|
||||
# be understood by human looking at source.
|
||||
stdout.write("<plaintext>\n")
|
||||
stdout.write(data)
|
||||
|
||||
proc setStackTraceStdout*() =
|
||||
## Makes Nimrod output stacktraces to stdout, instead of server log.
|
||||
errorMessageWriter = writeErrorMessage
|
||||
|
||||
proc setStackTraceNewLine*() {.deprecated.} =
|
||||
## Makes Nimrod output stacktraces to stdout, instead of server log.
|
||||
## Depracated alias for setStackTraceStdout.
|
||||
setStackTraceStdout()
|
||||
|
||||
proc setCookie*(name, value: string) =
|
||||
## Sets a cookie.
|
||||
|
|
@ -374,4 +393,3 @@ when isMainModule:
|
|||
const test1 = "abc\L+def xyz"
|
||||
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
|
||||
assert UrlDecode(UrlEncode(test1)) == test1
|
||||
|
||||
|
|
|
|||
582
lib/pure/collections/LockFreeHash.nim
Normal file
582
lib/pure/collections/LockFreeHash.nim
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
|
||||
|
||||
import baseutils, unsigned, math, hashes
|
||||
|
||||
|
||||
|
||||
const
|
||||
minTableSize = 8
|
||||
reProbeLimit = 12
|
||||
minCopyWork = 4096
|
||||
intSize = sizeof(int)
|
||||
|
||||
|
||||
|
||||
when sizeof(int) == 4: # 32bit
|
||||
type
|
||||
TRaw = range[0..1073741823]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 32 bit platform
|
||||
|
||||
elif sizeof(int) == 8: # 64bit
|
||||
type
|
||||
TRaw = range[0..4611686018427387903]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 64 bit platform
|
||||
else:
|
||||
{.error: "unsupported platform".}
|
||||
|
||||
type
|
||||
TEntry = tuple
|
||||
key: int
|
||||
value: int
|
||||
|
||||
TEntryArr = ptr array[0..10_000_000, TEntry]
|
||||
|
||||
PConcTable[K,V] = ptr object {.pure.}
|
||||
len: int
|
||||
used: int
|
||||
active: int
|
||||
copyIdx: int
|
||||
copyDone: int
|
||||
next: PConcTable[K,V]
|
||||
data: TEntryArr
|
||||
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Create a new table
|
||||
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(TEntry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
tableSize = 7 * intSize
|
||||
tableMem = allocShared0(tableSize)
|
||||
table = cast[PConcTable[K,V]](tableMem)
|
||||
table.len = dataLen
|
||||
table.used = 0
|
||||
table.active = 0
|
||||
table.copyIdx = 0
|
||||
table.copyDone = 0
|
||||
table.next = nil
|
||||
table.data = cast[TEntryArr](dataMem)
|
||||
result = table
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Delete a table
|
||||
proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
|
||||
deallocShared(tbl.data)
|
||||
deallocShared(tbl)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} =
|
||||
table.data[i]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# State flags stored in ptr
|
||||
|
||||
|
||||
proc pack[T](x: T): int {.inline.} =
|
||||
result = (cast[int](x) shl 2)
|
||||
#echo("packKey ",cast[int](x) , " -> ", result)
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc pop(x: int): int {.inline.} =
|
||||
result = x and 0xFFFFFFFC'i32
|
||||
|
||||
# Pop the raw value off of our Key or Val
|
||||
proc popRaw(x: int): int {.inline.} =
|
||||
result = x shr 2
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc popPtr[V](x: int): ptr V {.inline.} =
|
||||
result = cast[ptr V](pop(x))
|
||||
#echo("popPtr " & $x & " -> " & $cast[int](result))
|
||||
|
||||
# Ghost (sentinel)
|
||||
# K or V is no longer valid use new table
|
||||
const Ghost = 0xFFFFFFFC
|
||||
proc isGhost(x: int): bool {.inline.} =
|
||||
result = x == 0xFFFFFFFC
|
||||
|
||||
# Tombstone
|
||||
# applied to V = K is dead
|
||||
proc isTomb(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000002) != 0
|
||||
|
||||
proc setTomb(x: int): int {.inline.} =
|
||||
result = x or 0x00000002
|
||||
|
||||
# Prime
|
||||
# K or V is in new table copied from old
|
||||
proc isPrime(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000001) != 0
|
||||
|
||||
proc setPrime(x: int): int {.inline.} =
|
||||
result = x or 0x00000001
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
##This is for i32 only need to override for i64
|
||||
proc hashInt(x: int):int {.inline.} =
|
||||
var h = uint32(x) #shr 2'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
h *= 0x85ebca6b'u32
|
||||
h = h xor (h shr 13'u32)
|
||||
h *= 0xc2b2ae35'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
result = int(h)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
|
||||
var next = atomic_load_n(self.next.addr, ATOMIC_RELAXED)
|
||||
#echo("next = " & $cast[int](next))
|
||||
if next != nil:
|
||||
#echo("A new table already exists, copy in progress")
|
||||
return next
|
||||
var
|
||||
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
|
||||
newTable = newLFTable[K,V](oldLen*2)
|
||||
success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable,
|
||||
false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if not success:
|
||||
echo("someone beat us to it! delete table we just created and return his " & $cast[int](next))
|
||||
deleteConcTable(newTable)
|
||||
return next
|
||||
else:
|
||||
echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len)
|
||||
return newTable
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
result = false
|
||||
when K is TRaw:
|
||||
if key1 == key2:
|
||||
result = true
|
||||
else:
|
||||
var
|
||||
p1 = popPtr[K](key1)
|
||||
p2 = popPtr[K](key2)
|
||||
if p1 != nil and p2 != nil:
|
||||
if cast[int](p1) == cast[int](p2):
|
||||
return true
|
||||
if p1[] == p2[]:
|
||||
return true
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc tableFull(self: var PConcTable[K,V]) : bool {.inline.} =
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool =
|
||||
#echo("Copy idx " & $idx)
|
||||
var
|
||||
oldVal = 0
|
||||
oldkey = 0
|
||||
ok = false
|
||||
result = false
|
||||
#Block the key so no other threads waste time here
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
|
||||
setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
|
||||
#Prevent new values from appearing in the old table by priming
|
||||
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
||||
while not isPrime(oldVal):
|
||||
var box = if oldVal == NULL or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
else: oldVal.setPrime
|
||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
||||
if isPrime(box) and isTomb(box):
|
||||
return true
|
||||
oldVal = box
|
||||
break
|
||||
#echo("oldVal was = ", oldVal, " set it to prime ", box)
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
#when not (K is TRaw):
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
echo("oldVal is Tomb!!!, should not happen")
|
||||
if pop(oldVal) != NULL:
|
||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL
|
||||
if result:
|
||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||
else:
|
||||
#echo("copy slot failed")
|
||||
# Our copy is done so we disable the old slot
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("disabled old slot")
|
||||
#echo"---------------------"
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc promote[K,V](table: var PConcTable[K,V]) =
|
||||
var
|
||||
newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED)
|
||||
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
|
||||
newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED)
|
||||
|
||||
deallocShared(table.data)
|
||||
atomic_store_n(table.data.addr, newData, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.len.addr, newLen, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.used.addr, newUsed, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED)
|
||||
deallocShared(table.next)
|
||||
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
|
||||
echo("new table swapped!")
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
ok: bool
|
||||
result = false
|
||||
if workDone > 0:
|
||||
#echo("len to copy =" & $oldLen)
|
||||
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
|
||||
copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#if ok: echo("set copyDone")
|
||||
# If the copy is done we can promote this table
|
||||
if copyDone + workDone >= oldLen:
|
||||
# Swap new data
|
||||
#echo("work is done!")
|
||||
table.promote
|
||||
result = true
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int):
|
||||
PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil and copySlot(idx, table, newTable):
|
||||
#echo("copied a single slot, idx = " & $idx)
|
||||
if checkAndPromote(table, 1): return table
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil:
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
copyIdx = 0
|
||||
work = min(oldLen, minCopyWork)
|
||||
#panicStart = -1
|
||||
workDone = 0
|
||||
if copyDone < oldLen:
|
||||
var ok: bool
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
|
||||
copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("copy idx = ", copyIdx)
|
||||
for i in 0..work-1:
|
||||
var idx = (copyIdx + i) and (oldLen - 1)
|
||||
if copySlot(idx, table, newTable):
|
||||
workDone += 1
|
||||
if workDone > 0:
|
||||
#echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]()))
|
||||
if checkAndPromote(table, workDone): return table
|
||||
# In case a thread finished all the work then got stalled before promotion
|
||||
if checkAndPromote(table, 0): return table
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int =
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is TRaw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
var
|
||||
nextTable: PConcTable[K,V]
|
||||
probes = 1
|
||||
# spin until we find a key slot or build and jump to next table
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
#echo("try set idx = " & $idx & "for" & $key)
|
||||
var
|
||||
probedKey = NULL
|
||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if openKey:
|
||||
if val.isTomb:
|
||||
#echo("val was tomb, bail, no reason to set an open slot to tomb")
|
||||
return val
|
||||
#increment used slots
|
||||
#echo("found an open slot, total used = " &
|
||||
#$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED))
|
||||
discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)
|
||||
break # We found an open slot
|
||||
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("we found the matching slot")
|
||||
break # We found a matching slot
|
||||
if (not(expVal != NULL and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||
if key.isTomb: echo("Key is Tombstone")
|
||||
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
||||
#echo("try to resize")
|
||||
#create next bigger table
|
||||
nextTable = resize(table)
|
||||
#help do some copying
|
||||
#echo("help copy old table to new")
|
||||
nextTable = helpCopy(table)
|
||||
#now setVal in the new table instead
|
||||
#echo("jumping to next table to set val")
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
else:
|
||||
idx += 1
|
||||
probes += 1
|
||||
# Done spinning for a new slot
|
||||
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
|
||||
if val == oldVal:
|
||||
#echo("this val is alredy in the slot")
|
||||
return oldVal
|
||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||
if nextTable == nil and
|
||||
((oldVal == NULL and
|
||||
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
||||
(isPrime(oldVal))):
|
||||
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
||||
$(table.used / table.len))
|
||||
if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event")
|
||||
nextTable = resize(table)
|
||||
if nextTable != nil:
|
||||
#echo("tomb old slot then set in new table")
|
||||
nextTable = copySlotAndCheck(table,idx)
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
# Finaly ready to add new val to table
|
||||
while true:
|
||||
if match and oldVal != expVal:
|
||||
#echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal)
|
||||
return oldVal
|
||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||
#echo("val set at table " & $cast[int](table))
|
||||
if expVal != NULL:
|
||||
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
|
||||
elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
|
||||
if oldVal == NULL and expVal != NULL:
|
||||
return setTomb(oldVal)
|
||||
else: return oldVal
|
||||
if isPrime(oldVal):
|
||||
nextTable = copySlotAndCheck(table, idx)
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#echo("-try get- key = " & $key)
|
||||
when K is TRaw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
#echo("get idx ", idx)
|
||||
var
|
||||
probes = 0
|
||||
val: int
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
var
|
||||
newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE)
|
||||
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("found key after ", probes+1)
|
||||
val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE)
|
||||
if not isPrime(val):
|
||||
if isTomb(val):
|
||||
#echo("val was tomb but not prime")
|
||||
return NULL
|
||||
else:
|
||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||
return val
|
||||
else:
|
||||
newTable = copySlotAndCheck(table, idx)
|
||||
return getVal(newTable, key)
|
||||
else:
|
||||
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
|
||||
if probes >= reProbeLimit*4 or key.isTomb:
|
||||
if newTable == nil:
|
||||
#echo("too many probes and no new table ", key, " ", idx )
|
||||
return NULL
|
||||
else:
|
||||
newTable = helpCopy(table)
|
||||
return getVal(newTable, key)
|
||||
idx += 1
|
||||
probes += 1
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||
# discard setVal(table, pack(key), pack(key), NULL, false)
|
||||
|
||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||
# discard setVal(table, pack(key), cast[int](val), NULL, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is TRaw):
|
||||
var newKey = cast[int](copyShared(key))
|
||||
else:
|
||||
var newKey = pack(key)
|
||||
when not (V is TRaw):
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, NULL, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is TRaw):
|
||||
if newVal != oldPtr and oldPtr != NULL:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
|
||||
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
when not (V is TRaw):
|
||||
when not (K is TRaw):
|
||||
return popPtr[V](getVal(table, cast[int](key.addr)))[]
|
||||
else:
|
||||
return popPtr[V](getVal(table, pack(key)))[]
|
||||
else:
|
||||
when not (K is TRaw):
|
||||
return popRaw(getVal(table, cast[int](key.addr)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} =
|
||||
# getVal(table, key)
|
||||
|
||||
#proc `[]=`[K,V](table: var PConcTable[K,V], key: K, val: V): PEntry[K,V] {.inline.} =
|
||||
# setVal(table, key, val)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Tests ----------------------------
|
||||
when isMainModule:
|
||||
import locks, times, mersenne
|
||||
|
||||
const
|
||||
numTests = 100000
|
||||
numThreads = 10
|
||||
|
||||
|
||||
|
||||
type
|
||||
TTestObj = tuple
|
||||
thr: int
|
||||
f0: int
|
||||
f1: int
|
||||
|
||||
TData = tuple[k: string,v: TTestObj]
|
||||
PDataArr = array[0..numTests-1, TData]
|
||||
Dict = PConcTable[string,TTestObj]
|
||||
|
||||
var
|
||||
thr: array[0..numThreads-1, TThread[Dict]]
|
||||
|
||||
table = newLFTable[string,TTestObj](8)
|
||||
rand = newMersenneTwister(2525)
|
||||
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests))
|
||||
for i in 0..len-1:
|
||||
result[i].k = "mark" & $(i+1)
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
result[i].v.thr = 0
|
||||
result[i].v.f0 = i+1
|
||||
result[i].v.f1 = 0
|
||||
#echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr))
|
||||
|
||||
|
||||
|
||||
proc threadProc(tp: Dict) {.thread.} =
|
||||
var t = cpuTime();
|
||||
for i in 1..numTests:
|
||||
var key = "mark" & $(i)
|
||||
var got = table.get(key)
|
||||
got.thr = cast[int](myThreadID[pointer]())
|
||||
got.f1 = got.f1 + 1
|
||||
table.set(key, got)
|
||||
t = cpuTime() - t
|
||||
echo t
|
||||
|
||||
|
||||
var testData = createSampleData(numTests)
|
||||
|
||||
for i in 0..numTests-1:
|
||||
table.set(testData[i].k, testData[i].v)
|
||||
|
||||
var i = 0
|
||||
while i < numThreads:
|
||||
createThread(thr[i], threadProc, table)
|
||||
i += 1
|
||||
|
||||
joinThreads(thr)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var fails = 0
|
||||
|
||||
for i in 0..numTests-1:
|
||||
var got = table.get(testData[i].k)
|
||||
if got.f0 != i+1 or got.f1 != numThreads:
|
||||
fails += 1
|
||||
echo(got)
|
||||
|
||||
echo("Failed read or write = ", fails)
|
||||
|
||||
|
||||
#for i in 1..numTests:
|
||||
# echo(i, " = ", hashInt(i) and 8191)
|
||||
|
||||
deleteConcTable(table)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
41
lib/pure/collections/baseutils.nim
Normal file
41
lib/pure/collections/baseutils.nim
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Useful Constants
|
||||
const NULL* = 0
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Memory Utility Functions
|
||||
|
||||
proc newHeap*[T](): ptr T =
|
||||
result = cast[ptr T](alloc0(sizeof(T)))
|
||||
|
||||
proc copyNew*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = alloc(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
proc copyTo*[T](val: var T, dest: int) =
|
||||
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||
|
||||
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||
|
||||
proc newShared*[T](): ptr T =
|
||||
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||
|
||||
proc copyShared*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = allocShared(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Pointer arithmetic
|
||||
|
||||
proc `+`*(p: pointer, i: int): pointer {.inline.} =
|
||||
cast[pointer](cast[int](p) + i)
|
||||
|
|
@ -50,7 +50,7 @@ proc mustRehash(length, counter: int): bool {.inline.} =
|
|||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc IntSetGet(t: TIntSet, key: int): PTrunk =
|
||||
proc intSetGet(t: TIntSet, key: int): PTrunk =
|
||||
var h = key and t.max
|
||||
while t.data[h] != nil:
|
||||
if t.data[h].key == key:
|
||||
|
|
@ -58,7 +58,7 @@ proc IntSetGet(t: TIntSet, key: int): PTrunk =
|
|||
h = nextTry(h, t.max)
|
||||
result = nil
|
||||
|
||||
proc IntSetRawInsert(t: TIntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
proc intSetRawInsert(t: TIntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
var h = desc.key and t.max
|
||||
while data[h] != nil:
|
||||
assert(data[h] != desc)
|
||||
|
|
@ -66,22 +66,22 @@ proc IntSetRawInsert(t: TIntSet, data: var TTrunkSeq, desc: PTrunk) =
|
|||
assert(data[h] == nil)
|
||||
data[h] = desc
|
||||
|
||||
proc IntSetEnlarge(t: var TIntSet) =
|
||||
proc intSetEnlarge(t: var TIntSet) =
|
||||
var n: TTrunkSeq
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max + 1) * 2) - 1
|
||||
newSeq(n, t.max + 1)
|
||||
for i in countup(0, oldmax):
|
||||
if t.data[i] != nil: IntSetRawInsert(t, n, t.data[i])
|
||||
for i in countup(0, oldMax):
|
||||
if t.data[i] != nil: intSetRawInsert(t, n, t.data[i])
|
||||
swap(t.data, n)
|
||||
|
||||
proc IntSetPut(t: var TIntSet, key: int): PTrunk =
|
||||
proc intSetPut(t: var TIntSet, key: int): PTrunk =
|
||||
var h = key and t.max
|
||||
while t.data[h] != nil:
|
||||
if t.data[h].key == key:
|
||||
return t.data[h]
|
||||
h = nextTry(h, t.max)
|
||||
if mustRehash(t.max + 1, t.counter): IntSetEnlarge(t)
|
||||
if mustRehash(t.max + 1, t.counter): intSetEnlarge(t)
|
||||
inc(t.counter)
|
||||
h = key and t.max
|
||||
while t.data[h] != nil: h = nextTry(h, t.max)
|
||||
|
|
@ -94,7 +94,7 @@ proc IntSetPut(t: var TIntSet, key: int): PTrunk =
|
|||
|
||||
proc contains*(s: TIntSet, key: int): bool =
|
||||
## returns true iff `key` is in `s`.
|
||||
var t = IntSetGet(s, `shr`(key, TrunkShift))
|
||||
var t = intSetGet(s, `shr`(key, TrunkShift))
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
|
||||
|
|
@ -103,14 +103,14 @@ proc contains*(s: TIntSet, key: int): bool =
|
|||
|
||||
proc incl*(s: var TIntSet, key: int) =
|
||||
## includes an element `key` in `s`.
|
||||
var t = IntSetPut(s, `shr`(key, TrunkShift))
|
||||
var t = intSetPut(s, `shr`(key, TrunkShift))
|
||||
var u = key and TrunkMask
|
||||
t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
|
||||
`shl`(1, u and IntMask)
|
||||
|
||||
proc excl*(s: var TIntSet, key: int) =
|
||||
## excludes `key` from the set `s`.
|
||||
var t = IntSetGet(s, `shr`(key, TrunkShift))
|
||||
var t = intSetGet(s, `shr`(key, TrunkShift))
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
|
||||
|
|
@ -119,7 +119,7 @@ proc excl*(s: var TIntSet, key: int) =
|
|||
proc containsOrIncl*(s: var TIntSet, key: int): bool =
|
||||
## returns true if `s` contains `key`, otherwise `key` is included in `s`
|
||||
## and false is returned.
|
||||
var t = IntSetGet(s, `shr`(key, TrunkShift))
|
||||
var t = intSetGet(s, `shr`(key, TrunkShift))
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
|
||||
|
|
|
|||
|
|
@ -41,19 +41,19 @@ type
|
|||
|
||||
proc initSinglyLinkedList*[T](): TSinglyLinkedList[T] =
|
||||
## creates a new singly linked list that is empty.
|
||||
nil
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] =
|
||||
## creates a new doubly linked list that is empty.
|
||||
nil
|
||||
discard
|
||||
|
||||
proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] =
|
||||
## creates a new singly linked ring that is empty.
|
||||
nil
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] =
|
||||
## creates a new doubly linked ring that is empty.
|
||||
nil
|
||||
discard
|
||||
|
||||
proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] =
|
||||
## creates a new doubly linked node with the given `value`.
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) =
|
|||
s[i].shallowCopy(s[j])
|
||||
inc(i)
|
||||
inc(j)
|
||||
setlen(s, newLen)
|
||||
setLen(s, newLen)
|
||||
|
||||
proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
|
||||
## Inserts items from `src` into `dest` at position `pos`. This modifies
|
||||
|
|
@ -276,6 +276,38 @@ template foldr*(sequence, operation: expr): expr =
|
|||
result = operation
|
||||
result
|
||||
|
||||
template mapIt*(seq1, typ, pred: expr): expr =
|
||||
## Convenience template around the ``map`` proc to reduce typing.
|
||||
##
|
||||
## The template injects the ``it`` variable which you can use directly in an
|
||||
## expression. You also need to pass as `typ` the type of the expression,
|
||||
## since the new returned sequence can have a different type than the
|
||||
## original. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## nums = @[1, 2, 3, 4]
|
||||
## strings = nums.mapIt(string, $(4 * it))
|
||||
var result {.gensym.}: seq[typ] = @[]
|
||||
for it {.inject.} in items(seq1):
|
||||
result.add(pred)
|
||||
result
|
||||
|
||||
template mapIt*(varSeq, pred: expr) =
|
||||
## Convenience template around the mutable ``map`` proc to reduce typing.
|
||||
##
|
||||
## The template injects the ``it`` variable which you can use directly in an
|
||||
## expression. The expression has to return the same type as the sequence you
|
||||
## are mutating. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var nums = @[1, 2, 3, 4]
|
||||
## nums.mapIt(it * 3)
|
||||
## assert nums[0] + nums[3] == 15
|
||||
for i in 0 .. <len(varSeq):
|
||||
let it {.inject.} = varSeq[i]
|
||||
varSeq[i] = pred
|
||||
|
||||
when isMainModule:
|
||||
import strutils
|
||||
block: # concat test
|
||||
|
|
@ -381,4 +413,11 @@ when isMainModule:
|
|||
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
|
||||
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
|
||||
|
||||
block: # mapIt tests
|
||||
var
|
||||
nums = @[1, 2, 3, 4]
|
||||
strings = nums.mapIt(string, $(4 * it))
|
||||
nums.mapIt(it * 3)
|
||||
assert nums[0] + nums[3] == 15
|
||||
|
||||
echo "Finished doc tests"
|
||||
|
|
|
|||
|
|
@ -65,38 +65,38 @@ template rawInsertImpl() {.dirty.} =
|
|||
data[h].key = key
|
||||
data[h].slot = seFilled
|
||||
|
||||
proc RawGet[A](s: TSet[A], key: A): int =
|
||||
proc rawGet[A](s: TSet[A], key: A): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc contains*[A](s: TSet[A], key: A): bool =
|
||||
## returns true iff `key` is in `s`.
|
||||
var index = RawGet(s, key)
|
||||
var index = rawGet(s, key)
|
||||
result = index >= 0
|
||||
|
||||
proc RawInsert[A](s: var TSet[A], data: var TKeyValuePairSeq[A], key: A) =
|
||||
proc rawInsert[A](s: var TSet[A], data: var TKeyValuePairSeq[A], key: A) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc Enlarge[A](s: var TSet[A]) =
|
||||
proc enlarge[A](s: var TSet[A]) =
|
||||
var n: TKeyValuePairSeq[A]
|
||||
newSeq(n, len(s.data) * growthFactor)
|
||||
for i in countup(0, high(s.data)):
|
||||
if s.data[i].slot == seFilled: RawInsert(s, n, s.data[i].key)
|
||||
if s.data[i].slot == seFilled: rawInsert(s, n, s.data[i].key)
|
||||
swap(s.data, n)
|
||||
|
||||
template inclImpl() {.dirty.} =
|
||||
var index = RawGet(s, key)
|
||||
var index = rawGet(s, key)
|
||||
if index < 0:
|
||||
if mustRehash(len(s.data), s.counter): Enlarge(s)
|
||||
RawInsert(s, s.data, key)
|
||||
if mustRehash(len(s.data), s.counter): enlarge(s)
|
||||
rawInsert(s, s.data, key)
|
||||
inc(s.counter)
|
||||
|
||||
template containsOrInclImpl() {.dirty.} =
|
||||
var index = RawGet(s, key)
|
||||
var index = rawGet(s, key)
|
||||
if index >= 0:
|
||||
result = true
|
||||
else:
|
||||
if mustRehash(len(s.data), s.counter): Enlarge(s)
|
||||
RawInsert(s, s.data, key)
|
||||
if mustRehash(len(s.data), s.counter): enlarge(s)
|
||||
rawInsert(s, s.data, key)
|
||||
inc(s.counter)
|
||||
|
||||
proc incl*[A](s: var TSet[A], key: A) =
|
||||
|
|
@ -105,7 +105,7 @@ proc incl*[A](s: var TSet[A], key: A) =
|
|||
|
||||
proc excl*[A](s: var TSet[A], key: A) =
|
||||
## excludes `key` from the set `s`.
|
||||
var index = RawGet(s, key)
|
||||
var index = rawGet(s, key)
|
||||
if index >= 0:
|
||||
s.data[index].slot = seDeleted
|
||||
dec(s.counter)
|
||||
|
|
@ -122,7 +122,7 @@ proc initSet*[A](initialSize=64): TSet[A] =
|
|||
result.counter = 0
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
proc toSet*[A](keys: openarray[A]): TSet[A] =
|
||||
proc toSet*[A](keys: openArray[A]): TSet[A] =
|
||||
## creates a new hash set that contains the given `keys`.
|
||||
result = initSet[A](nextPowerOfTwo(keys.len+10))
|
||||
for key in items(keys): result.incl(key)
|
||||
|
|
@ -169,15 +169,15 @@ iterator items*[A](s: TOrderedSet[A]): A =
|
|||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
proc RawGet[A](s: TOrderedSet[A], key: A): int =
|
||||
proc rawGet[A](s: TOrderedSet[A], key: A): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc contains*[A](s: TOrderedSet[A], key: A): bool =
|
||||
## returns true iff `key` is in `s`.
|
||||
var index = RawGet(s, key)
|
||||
var index = rawGet(s, key)
|
||||
result = index >= 0
|
||||
|
||||
proc RawInsert[A](s: var TOrderedSet[A],
|
||||
proc rawInsert[A](s: var TOrderedSet[A],
|
||||
data: var TOrderedKeyValuePairSeq[A], key: A) =
|
||||
rawInsertImpl()
|
||||
data[h].next = -1
|
||||
|
|
@ -185,7 +185,7 @@ proc RawInsert[A](s: var TOrderedSet[A],
|
|||
if s.last >= 0: data[s.last].next = h
|
||||
s.last = h
|
||||
|
||||
proc Enlarge[A](s: var TOrderedSet[A]) =
|
||||
proc enlarge[A](s: var TOrderedSet[A]) =
|
||||
var n: TOrderedKeyValuePairSeq[A]
|
||||
newSeq(n, len(s.data) * growthFactor)
|
||||
var h = s.first
|
||||
|
|
@ -194,7 +194,7 @@ proc Enlarge[A](s: var TOrderedSet[A]) =
|
|||
while h >= 0:
|
||||
var nxt = s.data[h].next
|
||||
if s.data[h].slot == seFilled:
|
||||
RawInsert(s, n, s.data[h].key)
|
||||
rawInsert(s, n, s.data[h].key)
|
||||
h = nxt
|
||||
swap(s.data, n)
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
|
|||
result.last = -1
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
proc toOrderedSet*[A](keys: openarray[A]): TOrderedSet[A] =
|
||||
proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
|
||||
## creates a new ordered hash set that contains the given `keys`.
|
||||
result = initOrderedSet[A](nextPowerOfTwo(keys.len+10))
|
||||
for key in items(keys): result.incl(key)
|
||||
|
|
@ -225,4 +225,19 @@ proc `$`*[A](s: TOrderedSet[A]): string =
|
|||
## The `$` operator for ordered hash sets.
|
||||
dollarImpl()
|
||||
|
||||
proc `<`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a strict subset of t?
|
||||
s.counter != t.counter and s <= t
|
||||
|
||||
proc `<=`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a subset of t?
|
||||
result = false
|
||||
if s.counter > t.counter: return
|
||||
result = true
|
||||
for item in s:
|
||||
if not(t.contains(item)):
|
||||
result = false
|
||||
return
|
||||
|
||||
proc `==`*[A](s, t: TSet[A]): bool =
|
||||
s.counter == t.counter and s <= t
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ template rawInsertImpl() {.dirty.} =
|
|||
data[h].val = val
|
||||
data[h].slot = seFilled
|
||||
|
||||
proc RawGet[A, B](t: TTable[A, B], key: A): int =
|
||||
proc rawGet[A, B](t: TTable[A, B], key: A): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: TTable[A, B], key: A): B =
|
||||
|
|
@ -93,13 +93,13 @@ proc `[]`*[A, B](t: TTable[A, B], key: A): B =
|
|||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var TTable[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
||||
|
||||
|
|
@ -107,39 +107,39 @@ proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
|
|||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc RawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B],
|
||||
proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B],
|
||||
key: A, val: B) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc Enlarge[A, B](t: var TTable[A, B]) =
|
||||
proc enlarge[A, B](t: var TTable[A, B]) =
|
||||
var n: TKeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].slot == seFilled: RawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
|
||||
template AddImpl() {.dirty.} =
|
||||
if mustRehash(len(t.data), t.counter): Enlarge(t)
|
||||
RawInsert(t, t.data, key, val)
|
||||
template addImpl() {.dirty.} =
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
template PutImpl() {.dirty.} =
|
||||
var index = RawGet(t, key)
|
||||
template putImpl() {.dirty.} =
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].val = val
|
||||
else:
|
||||
AddImpl()
|
||||
addImpl()
|
||||
|
||||
when false:
|
||||
# not yet used:
|
||||
template HasKeyOrPutImpl() {.dirty.} =
|
||||
var index = RawGet(t, key)
|
||||
template hasKeyOrPutImpl() {.dirty.} =
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].val = val
|
||||
result = true
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): Enlarge(t)
|
||||
RawInsert(t, t.data, key, val)
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
result = false
|
||||
|
||||
|
|
@ -149,11 +149,11 @@ proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) =
|
|||
|
||||
proc add*[A, B](t: var TTable[A, B], key: A, val: B) =
|
||||
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
|
||||
AddImpl()
|
||||
addImpl()
|
||||
|
||||
proc del*[A, B](t: var TTable[A, B], key: A) =
|
||||
## deletes `key` from hash table `t`.
|
||||
var index = RawGet(t, key)
|
||||
let index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].slot = seDeleted
|
||||
dec(t.counter)
|
||||
|
|
@ -168,7 +168,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] =
|
|||
result.counter = 0
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
proc toTable*[A, B](pairs: openarray[tuple[key: A,
|
||||
proc toTable*[A, B](pairs: openArray[tuple[key: A,
|
||||
val: B]]): TTable[A, B] =
|
||||
## creates a new hash table that contains the given `pairs`.
|
||||
result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
|
||||
|
|
@ -189,6 +189,16 @@ template dollarImpl(): stmt {.dirty.} =
|
|||
proc `$`*[A, B](t: TTable[A, B]): string =
|
||||
## The `$` operator for hash tables.
|
||||
dollarImpl()
|
||||
|
||||
proc `==`*[A, B](s, t: TTable[A, B]): bool =
|
||||
s.counter == t.counter and s.data == t.data
|
||||
|
||||
proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] =
|
||||
## Index the collection with the proc provided.
|
||||
# TODO: As soon as supported, change collection: A to collection: A[B]
|
||||
result = initTable[C, B]()
|
||||
for item in collection:
|
||||
result[index(item)] = item
|
||||
|
||||
# ------------------------------ ordered table ------------------------------
|
||||
|
||||
|
|
@ -240,7 +250,7 @@ iterator mvalues*[A, B](t: var TOrderedTable[A, B]): var B =
|
|||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
proc RawGet[A, B](t: TOrderedTable[A, B], key: A): int =
|
||||
proc rawGet[A, B](t: TOrderedTable[A, B], key: A): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
|
||||
|
|
@ -248,13 +258,13 @@ proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
|
|||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var TOrderedTable[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
||||
|
||||
|
|
@ -262,7 +272,7 @@ proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool =
|
|||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc RawInsert[A, B](t: var TOrderedTable[A, B],
|
||||
proc rawInsert[A, B](t: var TOrderedTable[A, B],
|
||||
data: var TOrderedKeyValuePairSeq[A, B],
|
||||
key: A, val: B) =
|
||||
rawInsertImpl()
|
||||
|
|
@ -271,7 +281,7 @@ proc RawInsert[A, B](t: var TOrderedTable[A, B],
|
|||
if t.last >= 0: data[t.last].next = h
|
||||
t.last = h
|
||||
|
||||
proc Enlarge[A, B](t: var TOrderedTable[A, B]) =
|
||||
proc enlarge[A, B](t: var TOrderedTable[A, B]) =
|
||||
var n: TOrderedKeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
var h = t.first
|
||||
|
|
@ -280,7 +290,7 @@ proc Enlarge[A, B](t: var TOrderedTable[A, B]) =
|
|||
while h >= 0:
|
||||
var nxt = t.data[h].next
|
||||
if t.data[h].slot == seFilled:
|
||||
RawInsert(t, n, t.data[h].key, t.data[h].val)
|
||||
rawInsert(t, n, t.data[h].key, t.data[h].val)
|
||||
h = nxt
|
||||
swap(t.data, n)
|
||||
|
||||
|
|
@ -290,7 +300,7 @@ proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
|
|||
|
||||
proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
|
||||
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
|
||||
AddImpl()
|
||||
addImpl()
|
||||
|
||||
proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
|
||||
## creates a new ordered hash table that is empty.
|
||||
|
|
@ -304,7 +314,7 @@ proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
|
|||
result.last = -1
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
proc toOrderedTable*[A, B](pairs: openarray[tuple[key: A,
|
||||
proc toOrderedTable*[A, B](pairs: openArray[tuple[key: A,
|
||||
val: B]]): TOrderedTable[A, B] =
|
||||
## creates a new ordered hash table that contains the given `pairs`.
|
||||
result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
|
||||
|
|
@ -398,7 +408,7 @@ iterator mvalues*[A](t: TCountTable[A]): var int =
|
|||
for h in 0..high(t.data):
|
||||
if t.data[h].val != 0: yield t.data[h].val
|
||||
|
||||
proc RawGet[A](t: TCountTable[A], key: A): int =
|
||||
proc rawGet[A](t: TCountTable[A], key: A): int =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].val != 0:
|
||||
if t.data[h].key == key: return h
|
||||
|
|
@ -409,13 +419,13 @@ proc `[]`*[A](t: TCountTable[A], key: A): int =
|
|||
## retrieves the value at ``t[key]``. If `key` is not in `t`,
|
||||
## 0 is returned. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A](t: var TCountTable[A], key: A): var int =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
||||
|
||||
|
|
@ -423,24 +433,24 @@ proc hasKey*[A](t: TCountTable[A], key: A): bool =
|
|||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc RawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||
proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||
key: A, val: int) =
|
||||
var h: THash = hash(key) and high(data)
|
||||
while data[h].val != 0: h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc Enlarge[A](t: var TCountTable[A]) =
|
||||
proc enlarge[A](t: var TCountTable[A]) =
|
||||
var n: seq[tuple[key: A, val: int]]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].val != 0: RawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
|
||||
proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) =
|
||||
## puts a (key, value)-pair into `t`. `val` has to be positive.
|
||||
assert val > 0
|
||||
PutImpl()
|
||||
putImpl()
|
||||
|
||||
proc initCountTable*[A](initialSize=64): TCountTable[A] =
|
||||
## creates a new count table that is empty.
|
||||
|
|
@ -463,15 +473,15 @@ proc `$`*[A](t: TCountTable[A]): string =
|
|||
|
||||
proc inc*[A](t: var TCountTable[A], key: A, val = 1) =
|
||||
## increments `t[key]` by `val`.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
inc(t.data[index].val, val)
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): Enlarge(t)
|
||||
RawInsert(t, t.data, key, val)
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
|
||||
proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
|
||||
## returns the largest (key,val)-pair. Efficiency: O(n)
|
||||
assert t.len > 0
|
||||
var minIdx = 0
|
||||
|
|
@ -480,7 +490,7 @@ proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
|
|||
result.key = t.data[minIdx].key
|
||||
result.val = t.data[minIdx].val
|
||||
|
||||
proc Largest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
|
||||
proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
|
||||
## returns the (key,val)-pair with the largest `val`. Efficiency: O(n)
|
||||
assert t.len > 0
|
||||
var maxIdx = 0
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@
|
|||
type
|
||||
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
||||
|
||||
proc LoadLib*(path: string): TLibHandle
|
||||
proc loadLib*(path: string): TLibHandle
|
||||
## loads a library from `path`. Returns nil if the library could not
|
||||
## be loaded.
|
||||
|
||||
proc LoadLib*(): TLibHandle
|
||||
proc loadLib*(): TLibHandle
|
||||
## gets the handle from the current executable. Returns nil if the
|
||||
## library could not be loaded.
|
||||
|
||||
proc UnloadLib*(lib: TLibHandle)
|
||||
proc unloadLib*(lib: TLibHandle)
|
||||
## unloads the library `lib`
|
||||
|
||||
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
|
||||
|
|
@ -60,9 +60,9 @@ when defined(posix):
|
|||
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
|
||||
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 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,14 +78,14 @@ elif defined(windows) or defined(dos):
|
|||
proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
|
||||
proc winLoadLibrary(path: cstring): THINSTANCE {.
|
||||
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
|
||||
proc GetProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
||||
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
||||
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
|
||||
|
||||
proc LoadLib(path: string): TLibHandle =
|
||||
proc loadLib(path: string): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(path))
|
||||
proc LoadLib(): TLibHandle =
|
||||
proc loadLib(): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
result = GetProcAddress(cast[THINSTANCE](lib), name)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -14,21 +14,21 @@ import os, parseutils, strutils
|
|||
|
||||
when not defined(windows):
|
||||
type
|
||||
TConverter = object {.pure, final.}
|
||||
TConverter = object
|
||||
PConverter* = ptr TConverter ## can convert between two character sets
|
||||
|
||||
|
||||
else:
|
||||
type
|
||||
TCodePage = distinct int32
|
||||
PConverter* = object {.pure.}
|
||||
PConverter* = object
|
||||
dest, src: TCodePage
|
||||
|
||||
|
||||
type
|
||||
EInvalidEncoding* = object of EInvalidValue ## exception that is raised
|
||||
## for encoding errors
|
||||
|
||||
when defined(windows):
|
||||
proc EqEncodingNames(a, b: string): bool =
|
||||
proc eqEncodingNames(a, b: string): bool =
|
||||
var i = 0
|
||||
var j = 0
|
||||
while i < a.len and j < b.len:
|
||||
|
|
@ -208,31 +208,31 @@ when defined(windows):
|
|||
when false:
|
||||
# not needed yet:
|
||||
type
|
||||
TCpInfo = object {.pure.}
|
||||
MaxCharSize: int32
|
||||
DefaultChar: array[0..1, char]
|
||||
LeadByte: array[0..12-1, char]
|
||||
TCpInfo = object
|
||||
maxCharSize: int32
|
||||
defaultChar: array[0..1, char]
|
||||
leadByte: array[0..12-1, char]
|
||||
|
||||
proc GetCPInfo(CodePage: TCodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
proc getCPInfo(codePage: TCodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||
|
||||
proc nameToCodePage(name: string): TCodePage =
|
||||
var nameAsInt: int
|
||||
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
|
||||
for no, na in items(winEncodings):
|
||||
if no == nameAsInt or EqEncodingNames(na, name): return TCodePage(no)
|
||||
if no == nameAsInt or eqEncodingNames(na, name): return TCodePage(no)
|
||||
result = TCodePage(-1)
|
||||
|
||||
proc codePageToName(c: TCodePage): string =
|
||||
for no, na in items(winEncodings):
|
||||
if no == int(c):
|
||||
if no == int(c):
|
||||
return if na.len != 0: na else: $no
|
||||
result = ""
|
||||
|
||||
proc GetACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||
proc getACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||
|
||||
proc MultiByteToWideChar(
|
||||
CodePage: TCodePage,
|
||||
proc multiByteToWideChar(
|
||||
codePage: TCodePage,
|
||||
dwFlags: int32,
|
||||
lpMultiByteStr: cstring,
|
||||
cbMultiByte: cint,
|
||||
|
|
@ -240,8 +240,8 @@ when defined(windows):
|
|||
cchWideChar: cint): cint {.
|
||||
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
|
||||
|
||||
proc WideCharToMultiByte(
|
||||
CodePage: TCodePage,
|
||||
proc wideCharToMultiByte(
|
||||
codePage: TCodePage,
|
||||
dwFlags: int32,
|
||||
lpWideCharStr: cstring,
|
||||
cchWideChar: cint,
|
||||
|
|
@ -321,7 +321,6 @@ proc close*(c: PConverter) =
|
|||
iconvClose(c)
|
||||
|
||||
when defined(windows):
|
||||
|
||||
proc convert*(c: PConverter, s: string): string =
|
||||
## converts `s` to `destEncoding` that was given to the converter `c`. It
|
||||
## assumed that `s` is in `srcEncoding`.
|
||||
|
|
@ -333,26 +332,26 @@ when defined(windows):
|
|||
var cap = s.len + s.len shr 2
|
||||
result = newStringOfCap(cap*2)
|
||||
# convert to utf-16 LE
|
||||
var m = MultiByteToWideChar(CodePage = c.src, dwFlags = 0'i32,
|
||||
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(cap))
|
||||
if m == 0:
|
||||
# try again; ask for capacity:
|
||||
cap = MultiByteToWideChar(CodePage = c.src, dwFlags = 0'i32,
|
||||
cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = nil,
|
||||
cchWideChar = cint(0))
|
||||
# and do the conversion properly:
|
||||
result = newStringOfCap(cap*2)
|
||||
m = MultiByteToWideChar(CodePage = c.src, dwFlags = 0'i32,
|
||||
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
|
||||
lpMultiByteStr = cstring(s),
|
||||
cbMultiByte = cint(s.len),
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(cap))
|
||||
if m == 0: OSError()
|
||||
if m == 0: osError(osLastError())
|
||||
setLen(result, m*2)
|
||||
elif m <= cap:
|
||||
setLen(result, m*2)
|
||||
|
|
@ -364,8 +363,8 @@ when defined(windows):
|
|||
# otherwise the fun starts again:
|
||||
cap = s.len + s.len shr 2
|
||||
var res = newStringOfCap(cap)
|
||||
m = WideCharToMultiByte(
|
||||
CodePage = c.dest,
|
||||
m = wideCharToMultiByte(
|
||||
codePage = c.dest,
|
||||
dwFlags = 0'i32,
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(result.len div 2),
|
||||
|
|
@ -373,8 +372,8 @@ when defined(windows):
|
|||
cbMultiByte = cap.cint)
|
||||
if m == 0:
|
||||
# try again; ask for capacity:
|
||||
cap = WideCharToMultiByte(
|
||||
CodePage = c.dest,
|
||||
cap = wideCharToMultiByte(
|
||||
codePage = c.dest,
|
||||
dwFlags = 0'i32,
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(result.len div 2),
|
||||
|
|
@ -382,14 +381,14 @@ when defined(windows):
|
|||
cbMultiByte = cint(0))
|
||||
# and do the conversion properly:
|
||||
res = newStringOfCap(cap)
|
||||
m = WideCharToMultiByte(
|
||||
CodePage = c.dest,
|
||||
m = wideCharToMultiByte(
|
||||
codePage = c.dest,
|
||||
dwFlags = 0'i32,
|
||||
lpWideCharStr = cstring(result),
|
||||
cchWideChar = cint(result.len div 2),
|
||||
lpMultiByteStr = cstring(res),
|
||||
cbMultiByte = cap.cint)
|
||||
if m == 0: OSError()
|
||||
if m == 0: osError(osLastError())
|
||||
setLen(res, m)
|
||||
result = res
|
||||
elif m <= cap:
|
||||
|
|
@ -399,15 +398,14 @@ when defined(windows):
|
|||
assert(false) # cannot happen
|
||||
|
||||
else:
|
||||
|
||||
proc convert*(c: PConverter, s: string): string =
|
||||
result = newString(s.len)
|
||||
var inLen = len(S)
|
||||
var inLen = len(s)
|
||||
var outLen = len(result)
|
||||
var src = cstring(S)
|
||||
var src = cstring(s)
|
||||
var dst = cstring(result)
|
||||
var iconvres: int
|
||||
while InLen > 0:
|
||||
while inLen > 0:
|
||||
iconvres = iconv(c, src, inLen, dst, outLen)
|
||||
if iconvres == -1:
|
||||
var lerr = errno
|
||||
|
|
@ -425,11 +423,11 @@ else:
|
|||
dst = cast[cstring](cast[int](cstring(result)) + offset)
|
||||
outLen = len(result) - offset
|
||||
else:
|
||||
OSError()
|
||||
osError(lerr.TOSErrorCode)
|
||||
# iconv has a buffer that needs flushing, specially if the last char is
|
||||
# not '\0'
|
||||
discard iconv(c, nil, nil, dst, outlen)
|
||||
if iconvres == Cint(-1) and errno == E2BIG:
|
||||
if iconvres == cint(-1) and errno == E2BIG:
|
||||
var offset = cast[int](dst) - cast[int](cstring(result))
|
||||
setLen(result, len(result)+inLen*2+5)
|
||||
# 5 is minimally one utf-8 char
|
||||
|
|
@ -450,7 +448,7 @@ proc convert*(s: string, destEncoding = "UTF-8",
|
|||
finally:
|
||||
close(c)
|
||||
|
||||
when IsMainModule:
|
||||
when isMainModule:
|
||||
let
|
||||
orig = "öäüß"
|
||||
cp1252 = convert(orig, "CP1252", "UTF-8")
|
||||
|
|
|
|||
|
|
@ -34,14 +34,26 @@ proc swapEndian32*(outp, inp: pointer) =
|
|||
o[2] = i[1]
|
||||
o[3] = i[0]
|
||||
|
||||
proc swapEndian16*(outp, inp: pointer) =
|
||||
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
|
||||
## contain at least 2 bytes.
|
||||
var
|
||||
i = cast[cstring](inp)
|
||||
o = cast[cstring](outp)
|
||||
o[0] = i[1]
|
||||
o[1] = i[0]
|
||||
|
||||
when system.cpuEndian == bigEndian:
|
||||
proc littleEndian64*(outp, inp: pointer) {.inline.} = swapEndian64(outp, inp)
|
||||
proc littleEndian32*(outp, inp: pointer) {.inline.} = swapEndian32(outp, inp)
|
||||
proc littleEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp)
|
||||
proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
|
||||
proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
|
||||
proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
|
||||
else:
|
||||
proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
|
||||
proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
|
||||
proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2)
|
||||
proc bigEndian64*(outp, inp: pointer) {.inline.} = swapEndian64(outp, inp)
|
||||
proc bigEndian32*(outp, inp: pointer) {.inline.} = swapEndian32(outp, inp)
|
||||
|
||||
proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp)
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@ const
|
|||
proc newMonitor*(): PFSMonitor =
|
||||
## Creates a new file system monitor.
|
||||
new(result)
|
||||
result.fd = inotifyInit()
|
||||
result.targets = initTable[cint, string]()
|
||||
result.fd = inotifyInit()
|
||||
if result.fd < 0:
|
||||
OSError()
|
||||
OSError(OSLastError())
|
||||
|
||||
proc add*(monitor: PFSMonitor, target: string,
|
||||
filters = {MonitorAll}): cint {.discardable.} =
|
||||
|
|
@ -93,7 +93,7 @@ proc add*(monitor: PFSMonitor, target: string,
|
|||
|
||||
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
|
||||
if result < 0:
|
||||
OSError()
|
||||
OSError(OSLastError())
|
||||
monitor.targets.add(result, target)
|
||||
|
||||
proc del*(monitor: PFSMonitor, wd: cint) =
|
||||
|
|
@ -101,7 +101,7 @@ proc del*(monitor: PFSMonitor, wd: cint) =
|
|||
##
|
||||
## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
|
||||
if inotifyRmWatch(monitor.fd, wd) < 0:
|
||||
OSError()
|
||||
OSError(OSLastError())
|
||||
|
||||
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
|
||||
result = @[]
|
||||
|
|
@ -184,7 +184,7 @@ proc FSMonitorRead(h: PObject) =
|
|||
proc toDelegate(m: PFSMonitor): PDelegate =
|
||||
result = newDelegate()
|
||||
result.deleVal = m
|
||||
result.fd = m.fd
|
||||
result.fd = (type(result.fd))(m.fd)
|
||||
result.mode = fmRead
|
||||
result.handleRead = FSMonitorRead
|
||||
result.open = true
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ type
|
|||
EInvalidReply* = object of ESynch
|
||||
EFTP* = object of ESynch
|
||||
|
||||
proc FTPClient*(address: string, port = TPort(21),
|
||||
proc ftpClient*(address: string, port = TPort(21),
|
||||
user, pass = ""): PFTPClient =
|
||||
## Create a ``PFTPClient`` object.
|
||||
new(result)
|
||||
|
|
@ -298,7 +298,7 @@ proc getLines(ftp: PFTPClient, async: bool = false): bool =
|
|||
var readSocks: seq[TSocket] = @[ftp.getCSock()]
|
||||
# This is only needed here. Asyncio gets this socket...
|
||||
blockingOperation(ftp.getCSock()):
|
||||
if readSocks.select(1) != 0 and ftp.getCSock() notin readSocks:
|
||||
if readSocks.select(1) != 0 and ftp.getCSock() in readSocks:
|
||||
assertReply ftp.expectReply(), "226"
|
||||
return true
|
||||
|
||||
|
|
@ -315,7 +315,7 @@ proc listDirs*(ftp: PFTPClient, dir: string = "",
|
|||
assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): nil
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
result = splitLines(ftp.job.lines)
|
||||
ftp.deleteJob()
|
||||
else: return @[]
|
||||
|
|
@ -390,7 +390,7 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
|
|||
assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): nil
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
result = ftp.job.lines
|
||||
ftp.deleteJob()
|
||||
else:
|
||||
|
|
@ -405,7 +405,7 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string =
|
|||
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): nil
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
result = ftp.job.lines
|
||||
ftp.deleteJob()
|
||||
else:
|
||||
|
|
@ -434,7 +434,7 @@ proc getFile(ftp: PFTPClient, async = false): bool =
|
|||
if not async:
|
||||
var readSocks: seq[TSocket] = @[ftp.getCSock()]
|
||||
blockingOperation(ftp.getCSock()):
|
||||
if readSocks.select(1) != 0 and ftp.getCSock() notin readSocks:
|
||||
if readSocks.select(1) != 0 and ftp.getCSock() in readSocks:
|
||||
assertReply ftp.expectReply(), "226"
|
||||
return true
|
||||
|
||||
|
|
@ -460,7 +460,7 @@ proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
|
|||
ftp.job.filename = file.normalizePathSep
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): nil
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
ftp.deleteJob()
|
||||
|
||||
proc doUpload(ftp: PFTPClient, async = false): bool =
|
||||
|
|
@ -518,7 +518,7 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) =
|
|||
assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): nil
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
ftp.deleteJob()
|
||||
|
||||
proc close*(ftp: PFTPClient) =
|
||||
|
|
@ -554,10 +554,10 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
|||
|
||||
ftp.handleEvent(ftp, r)
|
||||
|
||||
proc AsyncFTPClient*(address: string, port = TPort(21),
|
||||
proc asyncFTPClient*(address: string, port = TPort(21),
|
||||
user, pass = "",
|
||||
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} =
|
||||
(proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = nil)): PAsyncFTPClient =
|
||||
(proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient =
|
||||
## Create a ``PAsyncFTPClient`` object.
|
||||
##
|
||||
## Use this if you want to use asyncio's dispatcher.
|
||||
|
|
@ -604,7 +604,7 @@ when isMainModule:
|
|||
ftp.close()
|
||||
echo d.len
|
||||
else: assert(false)
|
||||
var ftp = AsyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev)
|
||||
var ftp = asyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev)
|
||||
|
||||
d.register(ftp)
|
||||
d.len.echo()
|
||||
|
|
@ -618,7 +618,7 @@ when isMainModule:
|
|||
|
||||
|
||||
when isMainModule and false:
|
||||
var ftp = FTPClient("picheta.me", user = "asdasd", pass = "asfwq")
|
||||
var ftp = ftpClient("picheta.me", user = "asdasd", pass = "asfwq")
|
||||
ftp.connect()
|
||||
echo ftp.pwd()
|
||||
echo ftp.list()
|
||||
|
|
|
|||
|
|
@ -32,26 +32,26 @@ proc `!$`*(h: THash): THash {.inline.} =
|
|||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hashData*(Data: Pointer, Size: int): THash =
|
||||
proc hashData*(data: pointer, size: int): THash =
|
||||
## hashes an array of bytes of size `size`
|
||||
var h: THash = 0
|
||||
when defined(js):
|
||||
var p: cstring
|
||||
asm """`p` = `Data`;"""
|
||||
else:
|
||||
var p = cast[cstring](Data)
|
||||
var p = cast[cstring](data)
|
||||
var i = 0
|
||||
var s = size
|
||||
while s > 0:
|
||||
h = h !& ord(p[i])
|
||||
Inc(i)
|
||||
Dec(s)
|
||||
inc(i)
|
||||
dec(s)
|
||||
result = !$h
|
||||
|
||||
when defined(js):
|
||||
var objectID = 0
|
||||
|
||||
proc hash*(x: Pointer): THash {.inline.} =
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
when defined(js):
|
||||
asm """
|
||||
|
|
@ -126,6 +126,6 @@ proc hash*(x: float): THash {.inline.} =
|
|||
var y = x + 1.0
|
||||
result = cast[ptr THash](addr(y))[]
|
||||
|
||||
proc hash*[A](x: openarray[A]): THash =
|
||||
proc hash*[A](x: openArray[A]): THash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ proc xmlCheckedTag*(e: PNimrodNode, tag: string,
|
|||
result.add(newStrLitNode("</"))
|
||||
result.add(newStrLitNode(tag))
|
||||
result.add(newStrLitNode(">"))
|
||||
result = NestList(!"&", result)
|
||||
result = nestList(!"&", result)
|
||||
|
||||
|
||||
macro a*(e: expr): expr {.immediate.} =
|
||||
|
|
|
|||
|
|
@ -17,11 +17,37 @@
|
|||
##
|
||||
## echo loadHtml("mydirty.html")
|
||||
##
|
||||
##
|
||||
## Every tag in the resulting tree is in lower case.
|
||||
##
|
||||
## **Note:** The resulting ``PXmlNode`` already uses the ``clientData`` field,
|
||||
## so it cannot be used by clients of this library.
|
||||
##
|
||||
## Example: Transforming hyperlinks
|
||||
## ================================
|
||||
##
|
||||
## This code demonstrates how you can iterate over all the tags in an HTML file
|
||||
## and write back the modified version. In this case we look for hyperlinks
|
||||
## ending with the extension ``.rst`` and convert them to ``.html``.
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## import htmlparser
|
||||
## import xmltree # To use '$' for PXmlNode
|
||||
## import strtabs # To access PXmlAttributes
|
||||
## import os # To use splitFile
|
||||
## import strutils # To use cmpIgnoreCase
|
||||
##
|
||||
## proc transformHyperlinks() =
|
||||
## let html = loadHTML("input.html")
|
||||
##
|
||||
## for a in html.findAll("a"):
|
||||
## let href = a.attrs["href"]
|
||||
## if not href.isNil:
|
||||
## let (dir, filename, ext) = splitFile(href)
|
||||
## if cmpIgnoreCase(ext, ".rst") == 0:
|
||||
## a.attrs["href"] = dir / filename & ".html"
|
||||
##
|
||||
## writeFile("output.html", $html)
|
||||
|
||||
import strutils, streams, parsexml, xmltree, unicode, strtabs
|
||||
|
||||
|
|
@ -454,7 +480,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
|||
if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
|
||||
errors.add(expected(x, result))
|
||||
break
|
||||
else: nil
|
||||
else: discard
|
||||
result.addNode(parse(x, errors))
|
||||
of xmlElementEnd:
|
||||
if cmpIgnoreCase(x.elemName, result.tag) == 0:
|
||||
|
|
@ -521,14 +547,14 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
|||
var u = entityToUtf8(x.rawData)
|
||||
if u.len != 0: result = newText(u)
|
||||
next(x)
|
||||
of xmlEof: nil
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseHtml*(s: PStream, filename: string,
|
||||
errors: var seq[string]): PXmlNode =
|
||||
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
|
||||
## occured parsing error is added to the `errors` sequence.
|
||||
var x: TXmlParser
|
||||
open(x, s, filename, {reportComments})
|
||||
open(x, s, filename, {reportComments, reportWhitespace})
|
||||
next(x)
|
||||
# skip the DOCTYPE:
|
||||
if x.kind == xmlSpecial: next(x)
|
||||
|
|
|
|||
|
|
@ -220,9 +220,8 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
|
|||
inc(linei, le)
|
||||
if line[linei] != ':': httpError("invalid headers")
|
||||
inc(linei) # Skip :
|
||||
linei += skipWhitespace(line, linei)
|
||||
|
||||
result.headers[name] = line[linei.. -1]
|
||||
result.headers[name] = line[linei.. -1].strip()
|
||||
if not fullyRead:
|
||||
httpError("Connection was closed before full request has been made")
|
||||
if getBody:
|
||||
|
|
|
|||
|
|
@ -401,8 +401,9 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
|||
var value = ""
|
||||
i = header.parseUntil(key, ':')
|
||||
inc(i) # skip :
|
||||
i += header.skipWhiteSpace(i)
|
||||
i += header.parseUntil(value, {'\c', '\L'}, i)
|
||||
if i < header.len:
|
||||
i += header.skipWhiteSpace(i)
|
||||
i += header.parseUntil(value, {'\c', '\L'}, i)
|
||||
s.headers[key] = value
|
||||
else:
|
||||
s.client.close()
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ type
|
|||
params*: seq[string] ## Parameters of the IRC message
|
||||
origin*: string ## The channel/user that this msg originated from
|
||||
raw*: string ## Raw IRC message
|
||||
timestamp*: TTime ## UNIX epoch time the message was received
|
||||
|
||||
proc send*(irc: PIRC, message: string, sendImmediately = false) =
|
||||
## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
|
||||
|
|
@ -160,9 +161,10 @@ proc isNumber(s: string): bool =
|
|||
result = i == s.len and s.len > 0
|
||||
|
||||
proc parseMessage(msg: string): TIRCEvent =
|
||||
result.typ = EvMsg
|
||||
result.cmd = MUnknown
|
||||
result.raw = msg
|
||||
result.typ = EvMsg
|
||||
result.cmd = MUnknown
|
||||
result.raw = msg
|
||||
result.timestamp = times.getTime()
|
||||
var i = 0
|
||||
# Process the prefix
|
||||
if msg[i] == ':':
|
||||
|
|
@ -344,7 +346,7 @@ proc poll*(irc: PIRC, ev: var TIRCEvent,
|
|||
var line = TaintedString""
|
||||
var socks = @[irc.sock]
|
||||
var ret = socks.select(timeout)
|
||||
if socks.len() == 0 and ret != 0:
|
||||
if socks.len() != 0 and ret != 0:
|
||||
irc.sock.readLine(line)
|
||||
ev = irc.processLine(line.string)
|
||||
result = true
|
||||
|
|
@ -474,12 +476,12 @@ when isMainModule:
|
|||
var client = irc("amber.tenthbit.net", nick="TestBot1234",
|
||||
joinChans = @["#flood"])
|
||||
client.connect()
|
||||
while True:
|
||||
while true:
|
||||
var event: TIRCEvent
|
||||
if client.poll(event):
|
||||
case event.typ
|
||||
of EvConnected:
|
||||
nil
|
||||
discard
|
||||
of EvDisconnected:
|
||||
break
|
||||
of EvMsg:
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ proc str*(my: TJsonParser): string {.inline.} =
|
|||
assert(my.kind in {jsonInt, jsonFloat, jsonString})
|
||||
return my.a
|
||||
|
||||
proc getInt*(my: TJsonParser): biggestInt {.inline.} =
|
||||
proc getInt*(my: TJsonParser): BiggestInt {.inline.} =
|
||||
## returns the number for the event: ``jsonInt``
|
||||
assert(my.kind == jsonInt)
|
||||
return parseBiggestInt(my.a)
|
||||
|
|
@ -151,11 +151,11 @@ proc kind*(my: TJsonParser): TJsonEventKind {.inline.} =
|
|||
|
||||
proc getColumn*(my: TJsonParser): int {.inline.} =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(my, my.bufPos)
|
||||
result = getColNumber(my, my.bufpos)
|
||||
|
||||
proc getLine*(my: TJsonParser): int {.inline.} =
|
||||
## get the current line the parser has arrived at.
|
||||
result = my.linenumber
|
||||
result = my.lineNumber
|
||||
|
||||
proc getFilename*(my: TJsonParser): string {.inline.} =
|
||||
## get the filename of the file that the parser processes.
|
||||
|
|
@ -173,7 +173,7 @@ proc errorMsgExpected*(my: TJsonParser, e: string): string =
|
|||
result = "$1($2, $3) Error: $4" % [
|
||||
my.filename, $getLine(my), $getColumn(my), e & " expected"]
|
||||
|
||||
proc handleHexChar(c: Char, x: var int): bool =
|
||||
proc handleHexChar(c: char, x: var int): bool =
|
||||
result = true # Success
|
||||
case c
|
||||
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
|
||||
|
|
@ -227,11 +227,11 @@ proc parseString(my: var TJsonParser): TTokKind =
|
|||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
of '\c':
|
||||
pos = lexbase.HandleCR(my, pos)
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
buf = my.buf
|
||||
add(my.a, '\c')
|
||||
of '\L':
|
||||
pos = lexbase.HandleLF(my, pos)
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
buf = my.buf
|
||||
add(my.a, '\L')
|
||||
else:
|
||||
|
|
@ -253,11 +253,11 @@ proc skip(my: var TJsonParser) =
|
|||
of '\0':
|
||||
break
|
||||
of '\c':
|
||||
pos = lexbase.HandleCR(my, pos)
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
buf = my.buf
|
||||
break
|
||||
of '\L':
|
||||
pos = lexbase.HandleLF(my, pos)
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
buf = my.buf
|
||||
break
|
||||
else:
|
||||
|
|
@ -271,10 +271,10 @@ proc skip(my: var TJsonParser) =
|
|||
my.err = errEOC_Expected
|
||||
break
|
||||
of '\c':
|
||||
pos = lexbase.HandleCR(my, pos)
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
buf = my.buf
|
||||
of '\L':
|
||||
pos = lexbase.HandleLF(my, pos)
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
buf = my.buf
|
||||
of '*':
|
||||
inc(pos)
|
||||
|
|
@ -286,12 +286,12 @@ proc skip(my: var TJsonParser) =
|
|||
else:
|
||||
break
|
||||
of ' ', '\t':
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
of '\c':
|
||||
pos = lexbase.HandleCR(my, pos)
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
buf = my.buf
|
||||
of '\L':
|
||||
pos = lexbase.HandleLF(my, pos)
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
buf = my.buf
|
||||
else:
|
||||
break
|
||||
|
|
@ -517,7 +517,7 @@ type
|
|||
of JString:
|
||||
str*: string
|
||||
of JInt:
|
||||
num*: biggestInt
|
||||
num*: BiggestInt
|
||||
of JFloat:
|
||||
fnum*: float
|
||||
of JBool:
|
||||
|
|
@ -535,30 +535,30 @@ proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
|
|||
## raises an `EJsonParsingError` exception.
|
||||
raise newException(EJsonParsingError, errorMsgExpected(p, msg))
|
||||
|
||||
proc newJString*(s: String): PJsonNode =
|
||||
proc newJString*(s: string): PJsonNode =
|
||||
## Creates a new `JString PJsonNode`.
|
||||
new(result)
|
||||
result.kind = JString
|
||||
result.str = s
|
||||
|
||||
proc newJStringMove(s: String): PJsonNode =
|
||||
proc newJStringMove(s: string): PJsonNode =
|
||||
new(result)
|
||||
result.kind = JString
|
||||
shallowCopy(result.str, s)
|
||||
|
||||
proc newJInt*(n: biggestInt): PJsonNode =
|
||||
proc newJInt*(n: BiggestInt): PJsonNode =
|
||||
## Creates a new `JInt PJsonNode`.
|
||||
new(result)
|
||||
result.kind = JInt
|
||||
result.num = n
|
||||
|
||||
proc newJFloat*(n: Float): PJsonNode =
|
||||
proc newJFloat*(n: float): PJsonNode =
|
||||
## Creates a new `JFloat PJsonNode`.
|
||||
new(result)
|
||||
result.kind = JFloat
|
||||
result.fnum = n
|
||||
|
||||
proc newJBool*(b: Bool): PJsonNode =
|
||||
proc newJBool*(b: bool): PJsonNode =
|
||||
## Creates a new `JBool PJsonNode`.
|
||||
new(result)
|
||||
result.kind = JBool
|
||||
|
|
@ -587,7 +587,7 @@ proc `%`*(s: string): PJsonNode =
|
|||
result.kind = JString
|
||||
result.str = s
|
||||
|
||||
proc `%`*(n: biggestInt): PJsonNode =
|
||||
proc `%`*(n: BiggestInt): PJsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JInt PJsonNode`.
|
||||
new(result)
|
||||
result.kind = JInt
|
||||
|
|
@ -612,7 +612,7 @@ proc `%`*(keyVals: openArray[tuple[key: string, val: PJsonNode]]): PJsonNode =
|
|||
newSeq(result.fields, keyVals.len)
|
||||
for i, p in pairs(keyVals): result.fields[i] = p
|
||||
|
||||
proc `%`*(elements: openArray[PJSonNode]): PJsonNode =
|
||||
proc `%`*(elements: openArray[PJsonNode]): PJsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JArray PJsonNode`
|
||||
new(result)
|
||||
result.kind = JArray
|
||||
|
|
@ -626,9 +626,9 @@ proc len*(n: PJsonNode): int =
|
|||
case n.kind
|
||||
of JArray: result = n.elems.len
|
||||
of JObject: result = n.fields.len
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc `[]`*(node: PJsonNode, name: String): PJsonNode =
|
||||
proc `[]`*(node: PJsonNode, name: string): PJsonNode =
|
||||
## 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):
|
||||
|
|
@ -636,17 +636,18 @@ proc `[]`*(node: PJsonNode, name: String): PJsonNode =
|
|||
return item
|
||||
return nil
|
||||
|
||||
proc `[]`*(node: PJsonNode, index: Int): PJsonNode =
|
||||
proc `[]`*(node: PJsonNode, index: int): PJsonNode =
|
||||
## Gets the node at `index` in an Array.
|
||||
assert(node.kind == JArray)
|
||||
return node.elems[index]
|
||||
|
||||
proc hasKey*(node: PJsonNode, key: String): Bool =
|
||||
proc hasKey*(node: PJsonNode, key: string): bool =
|
||||
## Checks if `key` exists in `node`.
|
||||
assert(node.kind == JObject)
|
||||
for k, item in items(node.fields):
|
||||
if k == key: return True
|
||||
proc existsKey*(node: PJsonNode, key: String): Bool {.deprecated.} = node.hasKey(key)
|
||||
if k == key: return true
|
||||
|
||||
proc existsKey*(node: PJsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
|
||||
## Deprecated for `hasKey`
|
||||
|
||||
proc add*(father, child: PJsonNode) =
|
||||
|
|
@ -661,7 +662,7 @@ proc add*(obj: PJsonNode, key: string, val: PJsonNode) =
|
|||
assert obj.kind == JObject
|
||||
obj.fields.add((key, val))
|
||||
|
||||
proc `[]=`*(obj: PJsonNode, key: String, val: PJsonNode) =
|
||||
proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) =
|
||||
## Sets a field from a `JObject`. Performs a check for duplicate keys.
|
||||
assert(obj.kind == JObject)
|
||||
for i in 0..obj.fields.len-1:
|
||||
|
|
@ -706,7 +707,7 @@ proc copy*(p: PJsonNode): PJsonNode =
|
|||
proc indent(s: var string, i: int) =
|
||||
s.add(repeatChar(i))
|
||||
|
||||
proc newIndent(curr, indent: int, ml: bool): Int =
|
||||
proc newIndent(curr, indent: int, ml: bool): int =
|
||||
if ml: return curr + indent
|
||||
else: return indent
|
||||
|
||||
|
|
@ -730,8 +731,8 @@ proc escapeJson*(s: string): string =
|
|||
result.add(toHex(r, 4))
|
||||
result.add("\"")
|
||||
|
||||
proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = True,
|
||||
lstArr = False, currIndent = 0) =
|
||||
proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = true,
|
||||
lstArr = false, currIndent = 0) =
|
||||
case node.kind
|
||||
of JObject:
|
||||
if currIndent != 0 and not lstArr: result.nl(ml)
|
||||
|
|
@ -747,7 +748,7 @@ proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = True,
|
|||
result.indent(newIndent(currIndent, indent, ml))
|
||||
result.add(escapeJson(node.fields[i].key))
|
||||
result.add(": ")
|
||||
toPretty(result, node.fields[i].val, indent, ml, False,
|
||||
toPretty(result, node.fields[i].val, indent, ml, false,
|
||||
newIndent(currIndent, indent, ml))
|
||||
result.nl(ml)
|
||||
result.indent(currIndent) # indent the same as {
|
||||
|
|
@ -776,7 +777,7 @@ proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = True,
|
|||
result.add(", ")
|
||||
result.nl(ml) # New Line
|
||||
toPretty(result, node.elems[i], indent, ml,
|
||||
True, newIndent(currIndent, indent, ml))
|
||||
true, newIndent(currIndent, indent, ml))
|
||||
result.nl(ml)
|
||||
result.indent(currIndent)
|
||||
result.add("]")
|
||||
|
|
@ -785,18 +786,18 @@ proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = True,
|
|||
if lstArr: result.indent(currIndent)
|
||||
result.add("null")
|
||||
|
||||
proc pretty*(node: PJsonNode, indent = 2): String =
|
||||
proc pretty*(node: PJsonNode, indent = 2): string =
|
||||
## Converts `node` to its JSON Representation, with indentation and
|
||||
## on multiple lines.
|
||||
result = ""
|
||||
toPretty(result, node, indent)
|
||||
|
||||
proc `$`*(node: PJsonNode): String =
|
||||
proc `$`*(node: PJsonNode): string =
|
||||
## Converts `node` to its JSON Representation on one line.
|
||||
result = ""
|
||||
toPretty(result, node, 1, False)
|
||||
toPretty(result, node, 1, false)
|
||||
|
||||
iterator items*(node: PJsonNode): PJSonNode =
|
||||
iterator items*(node: PJsonNode): PJsonNode =
|
||||
## Iterator for the items of `node`. `node` has to be a JArray.
|
||||
assert node.kind == JArray
|
||||
for i in items(node.elems):
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type
|
|||
buf*: cstring ## the buffer itself
|
||||
bufLen*: int ## length of buffer in characters
|
||||
input: PStream ## the input stream
|
||||
LineNumber*: int ## the current line number
|
||||
lineNumber*: int ## the current line number
|
||||
sentinel: int
|
||||
lineStart: int # index of last line start in buffer
|
||||
fileOpened: bool
|
||||
|
|
@ -48,11 +48,11 @@ proc getCurrentLine*(L: TBaseLexer, marker: bool = true): string
|
|||
proc getColNumber*(L: TBaseLexer, pos: int): int
|
||||
## retrieves the current column.
|
||||
|
||||
proc HandleCR*(L: var TBaseLexer, pos: int): int
|
||||
proc handleCR*(L: var TBaseLexer, pos: int): int
|
||||
## Call this if you scanned over '\c' in the buffer; it returns the the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\c'.
|
||||
proc HandleLF*(L: var TBaseLexer, pos: int): int
|
||||
proc handleLF*(L: var TBaseLexer, pos: int): int
|
||||
## Call this if you scanned over '\L' in the buffer; it returns the the
|
||||
## position to continue the scanning from. `pos` must be the position
|
||||
## of the '\L'.
|
||||
|
|
@ -66,7 +66,7 @@ proc close(L: var TBaseLexer) =
|
|||
dealloc(L.buf)
|
||||
close(L.input)
|
||||
|
||||
proc FillBuffer(L: var TBaseLexer) =
|
||||
proc fillBuffer(L: var TBaseLexer) =
|
||||
var
|
||||
charsRead, toCopy, s: int # all are in characters,
|
||||
# not bytes (in case this
|
||||
|
|
@ -75,10 +75,10 @@ proc FillBuffer(L: var TBaseLexer) =
|
|||
# we know here that pos == L.sentinel, but not if this proc
|
||||
# is called the first time by initBaseLexer()
|
||||
assert(L.sentinel < L.bufLen)
|
||||
toCopy = L.BufLen - L.sentinel - 1
|
||||
toCopy = L.bufLen - L.sentinel - 1
|
||||
assert(toCopy >= 0)
|
||||
if toCopy > 0:
|
||||
MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
# "moveMem" handles overlapping regions
|
||||
charsRead = readData(L.input, addr(L.buf[toCopy]),
|
||||
(L.sentinel + 1) * chrSize) div chrSize
|
||||
|
|
@ -91,7 +91,7 @@ proc FillBuffer(L: var TBaseLexer) =
|
|||
dec(s) # BUGFIX (valgrind)
|
||||
while true:
|
||||
assert(s < L.bufLen)
|
||||
while (s >= 0) and not (L.buf[s] in NewLines): Dec(s)
|
||||
while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
|
||||
if s >= 0:
|
||||
# we found an appropriate character for a sentinel:
|
||||
L.sentinel = s
|
||||
|
|
@ -99,11 +99,11 @@ proc FillBuffer(L: var TBaseLexer) =
|
|||
else:
|
||||
# rather than to give up here because the line is too long,
|
||||
# double the buffer's size and try again:
|
||||
oldBufLen = L.BufLen
|
||||
L.bufLen = L.BufLen * 2
|
||||
oldBufLen = L.bufLen
|
||||
L.bufLen = L.bufLen * 2
|
||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||
assert(L.bufLen - oldBuflen == oldBufLen)
|
||||
charsRead = ReadData(L.input, addr(L.buf[oldBufLen]),
|
||||
assert(L.bufLen - oldBufLen == oldBufLen)
|
||||
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
|
||||
oldBufLen * chrSize) div chrSize
|
||||
if charsRead < oldBufLen:
|
||||
L.buf[oldBufLen + charsRead] = EndOfFile
|
||||
|
|
@ -121,19 +121,19 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
|||
result = 0
|
||||
L.lineStart = result
|
||||
|
||||
proc HandleCR(L: var TBaseLexer, pos: int): int =
|
||||
proc handleCR(L: var TBaseLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\c')
|
||||
inc(L.linenumber)
|
||||
inc(L.lineNumber)
|
||||
result = fillBaseLexer(L, pos)
|
||||
if L.buf[result] == '\L':
|
||||
result = fillBaseLexer(L, result)
|
||||
|
||||
proc HandleLF(L: var TBaseLexer, pos: int): int =
|
||||
proc handleLF(L: var TBaseLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\L')
|
||||
inc(L.linenumber)
|
||||
inc(L.lineNumber)
|
||||
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
|
||||
|
||||
proc skip_UTF_8_BOM(L: var TBaseLexer) =
|
||||
proc skipUtf8Bom(L: var TBaseLexer) =
|
||||
if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'):
|
||||
inc(L.bufpos, 3)
|
||||
inc(L.lineStart, 3)
|
||||
|
|
@ -147,9 +147,9 @@ proc open(L: var TBaseLexer, input: PStream, bufLen: int = 8192) =
|
|||
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
||||
L.sentinel = bufLen - 1
|
||||
L.lineStart = 0
|
||||
L.linenumber = 1 # lines start at 1
|
||||
L.lineNumber = 1 # lines start at 1
|
||||
fillBuffer(L)
|
||||
skip_UTF_8_BOM(L)
|
||||
skipUtf8Bom(L)
|
||||
|
||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||
result = abs(pos - L.lineStart)
|
||||
|
|
@ -163,5 +163,5 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
|||
inc(i)
|
||||
add(result, "\n")
|
||||
if marker:
|
||||
add(result, RepeatChar(getColNumber(L, L.bufpos)) & "^\n")
|
||||
add(result, repeatChar(getColNumber(L, L.bufpos)) & "^\n")
|
||||
|
||||
|
|
|
|||
267
lib/pure/logging.nim
Normal file
267
lib/pure/logging.nim
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a simple logger. It has been designed to be as simple
|
||||
## as possible to avoid bloat, if this library does not fullfill your needs,
|
||||
## write your own.
|
||||
##
|
||||
## Format strings support the following variables which must be prefixed with
|
||||
## the dollar operator (``$``):
|
||||
##
|
||||
## ============ =======================
|
||||
## Operator Output
|
||||
## ============ =======================
|
||||
## $date Current date
|
||||
## $time Current time
|
||||
## $app ``os.getAppFilename()``
|
||||
## ============ =======================
|
||||
##
|
||||
##
|
||||
## The following example demonstrates logging to three different handlers
|
||||
## simultaneously:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## var L = newConsoleLogger()
|
||||
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||
## handlers.add(L)
|
||||
## handlers.add(fL)
|
||||
## handlers.add(rL)
|
||||
## info("920410:52 accepted")
|
||||
## warn("4 8 15 16 23 4-- Error")
|
||||
## error("922044:16 SYSTEM FAILURE")
|
||||
## fatal("SYSTEM FAILURE SYSTEM FAILURE")
|
||||
|
||||
import strutils, os, times
|
||||
|
||||
type
|
||||
TLevel* = enum ## logging level
|
||||
lvlAll, ## all levels active
|
||||
lvlDebug, ## debug level (and any above) active
|
||||
lvlInfo, ## info level (and any above) active
|
||||
lvlWarn, ## warn level (and any above) active
|
||||
lvlError, ## error level (and any above) active
|
||||
lvlFatal, ## fatal level (and any above) active
|
||||
lvlNone ## no levels active
|
||||
|
||||
const
|
||||
LevelNames*: array [TLevel, string] = [
|
||||
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
||||
]
|
||||
|
||||
defaultFmtStr* = "" ## default string between log level and message per logger
|
||||
verboseFmtStr* = "$date $time "
|
||||
|
||||
type
|
||||
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
|
||||
levelThreshold*: TLevel ## only messages of level >= levelThreshold
|
||||
## should be processed
|
||||
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
||||
|
||||
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
|
||||
## console
|
||||
|
||||
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
|
||||
f: TFile
|
||||
|
||||
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
|
||||
## messages to a file and
|
||||
## performs log rotation
|
||||
maxLines: int # maximum number of lines
|
||||
curLine : int
|
||||
baseName: string # initial filename
|
||||
baseMode: TFileMode # initial file mode
|
||||
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
||||
|
||||
proc substituteLog(frmt: string): string =
|
||||
## converts $date to the current date
|
||||
## converts $time to the current time
|
||||
## converts $app to getAppFilename()
|
||||
## converts
|
||||
result = newStringOfCap(frmt.len + 20)
|
||||
var i = 0
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$':
|
||||
result.add(frmt[i])
|
||||
inc(i)
|
||||
else:
|
||||
inc(i)
|
||||
var v = ""
|
||||
var app = getAppFilename()
|
||||
while frmt[i] in IdentChars:
|
||||
v.add(toLower(frmt[i]))
|
||||
inc(i)
|
||||
case v
|
||||
of "date": result.add(getDateStr())
|
||||
of "time": result.add(getClockStr())
|
||||
of "app": result.add(app)
|
||||
of "appdir": result.add(app.splitFile.dir)
|
||||
of "appname": result.add(app.splitFile.name)
|
||||
|
||||
method log*(logger: PLogger, level: TLevel,
|
||||
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
|
||||
## Override this method in custom loggers. Default implementation does
|
||||
## nothing.
|
||||
discard
|
||||
|
||||
method log*(logger: PConsoleLogger, level: TLevel,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to the console using ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
|
||||
frmt % args)
|
||||
|
||||
method log*(logger: PFileLogger, level: TLevel,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to a file using ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
writeln(logger.f, LevelNames[level], " ",
|
||||
substituteLog(logger.fmtStr), frmt % args)
|
||||
|
||||
proc defaultFilename*(): string =
|
||||
## Returns the default filename for a logger.
|
||||
var (path, name, ext) = splitFile(getAppFilename())
|
||||
result = changeFileExt(path / name, "log")
|
||||
|
||||
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
|
||||
## Creates a new console logger. This logger logs to the console.
|
||||
new result
|
||||
result.fmtStr = fmtStr
|
||||
result.levelThreshold = levelThreshold
|
||||
|
||||
proc newFileLogger*(filename = defaultFilename(),
|
||||
mode: TFileMode = fmAppend,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr): PFileLogger =
|
||||
## Creates a new file logger. This logger logs to a file.
|
||||
new(result)
|
||||
result.levelThreshold = levelThreshold
|
||||
result.f = open(filename, mode)
|
||||
result.fmtStr = fmtStr
|
||||
|
||||
# ------
|
||||
|
||||
proc countLogLines(logger: PRollingFileLogger): int =
|
||||
result = 0
|
||||
for line in logger.f.lines():
|
||||
result.inc()
|
||||
|
||||
proc countFiles(filename: string): int =
|
||||
# Example: file.log.1
|
||||
result = 0
|
||||
let (dir, name, ext) = splitFile(filename)
|
||||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile:
|
||||
let llfn = name & ext & ExtSep
|
||||
if path.extractFilename.startsWith(llfn):
|
||||
let numS = path.extractFilename[llfn.len .. -1]
|
||||
try:
|
||||
let num = parseInt(numS)
|
||||
if num > result:
|
||||
result = num
|
||||
except EInvalidValue: discard
|
||||
|
||||
proc newRollingFileLogger*(filename = defaultFilename(),
|
||||
mode: TFileMode = fmReadWrite,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr,
|
||||
maxLines = 1000): PRollingFileLogger =
|
||||
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
|
||||
## a new log file will be started and the old will be renamed.
|
||||
new(result)
|
||||
result.levelThreshold = levelThreshold
|
||||
result.fmtStr = defaultFmtStr
|
||||
result.maxLines = maxLines
|
||||
result.f = open(filename, mode)
|
||||
result.curLine = 0
|
||||
result.baseName = filename
|
||||
result.baseMode = mode
|
||||
|
||||
result.logFiles = countFiles(filename)
|
||||
|
||||
if mode == fmAppend:
|
||||
# We need to get a line count because we will be appending to the file.
|
||||
result.curLine = countLogLines(result)
|
||||
|
||||
proc rotate(logger: PRollingFileLogger) =
|
||||
let (dir, name, ext) = splitFile(logger.baseName)
|
||||
for i in countdown(logger.logFiles, 0):
|
||||
let srcSuff = if i != 0: ExtSep & $i else: ""
|
||||
moveFile(dir / (name & ext & srcSuff),
|
||||
dir / (name & ext & ExtSep & $(i+1)))
|
||||
|
||||
method log*(logger: PRollingFileLogger, level: TLevel,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to a file using rolling ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
if logger.curLine >= logger.maxLines:
|
||||
logger.f.close()
|
||||
rotate(logger)
|
||||
logger.logFiles.inc
|
||||
logger.curLine = 0
|
||||
logger.f = open(logger.baseName, logger.baseMode)
|
||||
|
||||
writeln(logger.f, LevelNames[level], " ", frmt % args)
|
||||
logger.curLine.inc
|
||||
|
||||
# --------
|
||||
|
||||
var
|
||||
level* = lvlAll ## global log filter
|
||||
handlers*: seq[PLogger] = @[] ## handlers with their own log levels
|
||||
|
||||
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
||||
for logger in items(handlers):
|
||||
if level >= logger.levelThreshold:
|
||||
log(logger, level, frmt, args)
|
||||
|
||||
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs a message to all registered handlers at the given level.
|
||||
bind logLoop
|
||||
bind `%`
|
||||
bind logging.Level
|
||||
|
||||
if level >= logging.Level:
|
||||
logLoop(level, frmt, args)
|
||||
|
||||
template debug*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs a debug message to all registered handlers.
|
||||
log(lvlDebug, frmt, args)
|
||||
|
||||
template info*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs an info message to all registered handlers.
|
||||
log(lvlInfo, frmt, args)
|
||||
|
||||
template warn*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs a warning message to all registered handlers.
|
||||
log(lvlWarn, frmt, args)
|
||||
|
||||
template error*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs an error message to all registered handlers.
|
||||
log(lvlError, frmt, args)
|
||||
|
||||
template fatal*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs a fatal error message to all registered handlers.
|
||||
log(lvlFatal, frmt, args)
|
||||
|
||||
|
||||
# --------------
|
||||
|
||||
when isMainModule:
|
||||
var L = newConsoleLogger()
|
||||
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||
handlers.add(L)
|
||||
handlers.add(fL)
|
||||
handlers.add(rL)
|
||||
for i in 0 .. 25:
|
||||
info("hello" & $i, [])
|
||||
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
|
|||
try:
|
||||
discard parseutils.parseInt(s, x, 0)
|
||||
except EOverflow:
|
||||
nil
|
||||
discard
|
||||
if x in validRange: value = x
|
||||
|
||||
when isMainModule:
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ proc nextPowerOfTwo*(x: int): int =
|
|||
result = result or (result shr 4)
|
||||
result = result or (result shr 2)
|
||||
result = result or (result shr 1)
|
||||
Inc(result)
|
||||
inc(result)
|
||||
|
||||
proc countBits32*(n: int32): int {.noSideEffect.} =
|
||||
## counts the set bits in `n`.
|
||||
|
|
@ -103,17 +103,17 @@ proc countBits32*(n: int32): int {.noSideEffect.} =
|
|||
v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
|
||||
result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
|
||||
|
||||
proc sum*[T](x: openarray[T]): T {.noSideEffect.} =
|
||||
proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
|
||||
## computes the sum of the elements in `x`.
|
||||
## If `x` is empty, 0 is returned.
|
||||
for i in items(x): result = result + i
|
||||
|
||||
proc mean*(x: openarray[float]): float {.noSideEffect.} =
|
||||
proc mean*(x: openArray[float]): float {.noSideEffect.} =
|
||||
## computes the mean of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
result = sum(x) / toFloat(len(x))
|
||||
|
||||
proc variance*(x: openarray[float]): float {.noSideEffect.} =
|
||||
proc variance*(x: openArray[float]): float {.noSideEffect.} =
|
||||
## computes the variance of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
result = 0.0
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ type
|
|||
MD5Digest* = array[0..15, int8]
|
||||
MD5Buffer = array[0..63, int8]
|
||||
MD5Context* {.final.} = object
|
||||
State: MD5State
|
||||
Count: array[0..1, int32]
|
||||
Buffer: MD5Buffer
|
||||
state: MD5State
|
||||
count: array[0..1, int32]
|
||||
buffer: MD5Buffer
|
||||
|
||||
const
|
||||
padding: cstring = "\x80\0\0\0" &
|
||||
|
|
@ -32,16 +32,16 @@ const
|
|||
"\0\0\0\0"
|
||||
|
||||
proc F(x, y, z: int32): int32 {.inline.} =
|
||||
Result = (x and y) or ((not x) and z)
|
||||
result = (x and y) or ((not x) and z)
|
||||
|
||||
proc G(x, y, z: int32): int32 {.inline.} =
|
||||
Result = (x and z) or (y and (not z))
|
||||
result = (x and z) or (y and (not z))
|
||||
|
||||
proc H(x, y, z: int32): int32 {.inline.} =
|
||||
Result = x xor y xor z
|
||||
result = x xor y xor z
|
||||
|
||||
proc I(x, y, z: int32): int32 {.inline.} =
|
||||
Result = y xor (x or (not z))
|
||||
result = y xor (x or (not z))
|
||||
|
||||
proc rot(x: var int32, n: int8) {.inline.} =
|
||||
x = toU32(x shl ze(n)) or (x shr toU32(32 -% ze(n)))
|
||||
|
|
@ -75,7 +75,7 @@ proc encode(dest: var MD5Block, src: cstring) =
|
|||
ord(src[j+3]) shl 24)
|
||||
inc(j, 4)
|
||||
|
||||
proc decode(dest: var openarray[int8], src: openarray[int32]) =
|
||||
proc decode(dest: var openArray[int8], src: openArray[int32]) =
|
||||
var i = 0
|
||||
for j in 0..high(src):
|
||||
dest[i] = toU8(src[j] and 0xff'i32)
|
||||
|
|
@ -87,7 +87,7 @@ proc decode(dest: var openarray[int8], src: openarray[int32]) =
|
|||
proc transform(Buffer: pointer, State: var MD5State) =
|
||||
var
|
||||
myBlock: MD5Block
|
||||
encode(myBlock, cast[cstring](buffer))
|
||||
encode(myBlock, cast[cstring](Buffer))
|
||||
var a = State[0]
|
||||
var b = State[1]
|
||||
var c = State[2]
|
||||
|
|
@ -161,7 +161,7 @@ proc transform(Buffer: pointer, State: var MD5State) =
|
|||
State[2] = State[2] +% c
|
||||
State[3] = State[3] +% d
|
||||
|
||||
proc MD5Init*(c: var MD5Context) =
|
||||
proc md5Init*(c: var MD5Context) =
|
||||
## initializes a MD5Context
|
||||
c.State[0] = 0x67452301'i32
|
||||
c.State[1] = 0xEFCDAB89'i32
|
||||
|
|
@ -169,64 +169,64 @@ proc MD5Init*(c: var MD5Context) =
|
|||
c.State[3] = 0x10325476'i32
|
||||
c.Count[0] = 0'i32
|
||||
c.Count[1] = 0'i32
|
||||
ZeroMem(addr(c.Buffer), SizeOf(MD5Buffer))
|
||||
zeroMem(addr(c.Buffer), sizeof(MD5Buffer))
|
||||
|
||||
proc MD5Update*(c: var MD5Context, input: cstring, len: int) =
|
||||
proc md5Update*(c: var MD5Context, input: cstring, len: int) =
|
||||
## updates the MD5Context with the `input` data of length `len`
|
||||
var input = input
|
||||
var Index = (c.Count[0] shr 3) and 0x3F
|
||||
var Index = (c.count[0] shr 3) and 0x3F
|
||||
c.Count[0] = c.count[0] +% toU32(len shl 3)
|
||||
if c.Count[0] < (len shl 3): c.Count[1] = c.count[1] +% 1'i32
|
||||
c.Count[1] = c.count[1] +% toU32(len shr 29)
|
||||
var PartLen = 64 - Index
|
||||
if len >= PartLen:
|
||||
CopyMem(addr(c.Buffer[Index]), Input, PartLen)
|
||||
copyMem(addr(c.Buffer[Index]), input, PartLen)
|
||||
transform(addr(c.Buffer), c.State)
|
||||
var i = PartLen
|
||||
while i + 63 < len:
|
||||
Transform(addr(Input[I]), c.State)
|
||||
transform(addr(input[i]), c.State)
|
||||
inc(i, 64)
|
||||
CopyMem(addr(c.Buffer[0]), addr(Input[i]), len-i)
|
||||
copyMem(addr(c.Buffer[0]), addr(input[i]), len-i)
|
||||
else:
|
||||
CopyMem(addr(c.Buffer[Index]), addr(Input[0]), len)
|
||||
copyMem(addr(c.Buffer[Index]), addr(input[0]), len)
|
||||
|
||||
proc MD5Final*(c: var MD5Context, digest: var MD5Digest) =
|
||||
proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
|
||||
## finishes the MD5Context and stores the result in `digest`
|
||||
var
|
||||
Bits: MD5CBits
|
||||
PadLen: int
|
||||
decode(bits, c.Count)
|
||||
decode(Bits, c.Count)
|
||||
var Index = (c.Count[0] shr 3) and 0x3F
|
||||
if Index < 56: PadLen = 56 - Index
|
||||
else: PadLen = 120 - Index
|
||||
MD5Update(c, padding, PadLen)
|
||||
MD5Update(c, cast[cstring](addr(Bits)), 8)
|
||||
md5Update(c, padding, PadLen)
|
||||
md5Update(c, cast[cstring](addr(Bits)), 8)
|
||||
decode(digest, c.State)
|
||||
ZeroMem(addr(c), SizeOf(MD5Context))
|
||||
zeroMem(addr(c), sizeof(MD5Context))
|
||||
|
||||
proc toMD5*(s: string): MD5Digest =
|
||||
## computes the MD5Digest value for a string `s`
|
||||
var c: MD5Context
|
||||
MD5Init(c)
|
||||
MD5Update(c, cstring(s), len(s))
|
||||
MD5Final(c, result)
|
||||
md5Init(c)
|
||||
md5Update(c, cstring(s), len(s))
|
||||
md5Final(c, result)
|
||||
|
||||
proc `$`*(D: MD5Digest): string =
|
||||
## converts a MD5Digest value into its string representation
|
||||
const digits = "0123456789abcdef"
|
||||
result = ""
|
||||
for i in 0..15:
|
||||
add(result, Digits[(D[I] shr 4) and 0xF])
|
||||
add(result, Digits[D[I] and 0xF])
|
||||
add(result, Digits[(D[i] shr 4) and 0xF])
|
||||
add(result, Digits[D[i] and 0xF])
|
||||
|
||||
proc getMD5*(s: string): string =
|
||||
## computes an MD5 value of `s` and returns its string representation
|
||||
var
|
||||
c: MD5Context
|
||||
d: MD5Digest
|
||||
MD5Init(c)
|
||||
MD5Update(c, cstring(s), len(s))
|
||||
MD5Final(c, d)
|
||||
md5Init(c)
|
||||
md5Update(c, cstring(s), len(s))
|
||||
md5Final(c, d)
|
||||
result = $d
|
||||
|
||||
proc `==`*(D1, D2: MD5Digest): bool =
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
when defined(windows):
|
||||
template fail(errCode: TOSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.fHandle != 0: discard CloseHandle(result.fHandle)
|
||||
if result.mapHandle != 0: discard CloseHandle(result.mapHandle)
|
||||
OSError(errCode)
|
||||
if result.fHandle != 0: discard closeHandle(result.fHandle)
|
||||
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
|
||||
osError(errCode)
|
||||
# return false
|
||||
#raise newException(EIO, msg)
|
||||
|
||||
|
|
@ -69,36 +69,36 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
0)
|
||||
|
||||
when useWinUnicode:
|
||||
result.fHandle = callCreateFile(CreateFileW, newWideCString(filename))
|
||||
result.fHandle = callCreateFile(createFileW, newWideCString(filename))
|
||||
else:
|
||||
result.fHandle = callCreateFile(CreateFileA, filename)
|
||||
|
||||
if result.fHandle == INVALID_HANDLE_VALUE:
|
||||
fail(OSLastError(), "error opening file")
|
||||
fail(osLastError(), "error opening file")
|
||||
|
||||
if newFileSize != -1:
|
||||
var
|
||||
sizeHigh = int32(newFileSize shr 32)
|
||||
sizeLow = int32(newFileSize and 0xffffffff)
|
||||
|
||||
var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
|
||||
var status = setFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
|
||||
FILE_BEGIN)
|
||||
let lastErr = OSLastError()
|
||||
let lastErr = osLastError()
|
||||
if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
|
||||
(SetEndOfFile(result.fHandle) == 0):
|
||||
(setEndOfFile(result.fHandle) == 0):
|
||||
fail(lastErr, "error setting file size")
|
||||
|
||||
# since the strings are always 'nil', we simply always call
|
||||
# CreateFileMappingW which should be slightly faster anyway:
|
||||
result.mapHandle = CreateFileMappingW(
|
||||
result.mapHandle = createFileMappingW(
|
||||
result.fHandle, nil,
|
||||
if readonly: PAGE_READONLY else: PAGE_READWRITE,
|
||||
0, 0, nil)
|
||||
|
||||
if result.mapHandle == 0:
|
||||
fail(OSLastError(), "error creating mapping")
|
||||
fail(osLastError(), "error creating mapping")
|
||||
|
||||
result.mem = MapViewOfFileEx(
|
||||
result.mem = mapViewOfFileEx(
|
||||
result.mapHandle,
|
||||
if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
|
||||
int32(offset shr 32),
|
||||
|
|
@ -107,12 +107,12 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
nil)
|
||||
|
||||
if result.mem == nil:
|
||||
fail(OSLastError(), "error mapping view")
|
||||
fail(osLastError(), "error mapping view")
|
||||
|
||||
var hi, low: int32
|
||||
low = GetFileSize(result.fHandle, addr(hi))
|
||||
low = getFileSize(result.fHandle, addr(hi))
|
||||
if low == INVALID_FILE_SIZE:
|
||||
fail(OSLastError(), "error getting file size")
|
||||
fail(osLastError(), "error getting file size")
|
||||
else:
|
||||
var fileSize = (int64(hi) shr 32) or low
|
||||
if mappedSize != -1: result.size = min(fileSize, mappedSize).int
|
||||
|
|
@ -122,7 +122,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
template fail(errCode: TOSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.handle != 0: discard close(result.handle)
|
||||
OSError(errCode)
|
||||
osError(errCode)
|
||||
|
||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||
|
||||
|
|
@ -133,22 +133,22 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
if result.handle == -1:
|
||||
# XXX: errno is supposed to be set here
|
||||
# Is there an exception that wraps it?
|
||||
fail(OSLastError(), "error opening file")
|
||||
fail(osLastError(), "error opening file")
|
||||
|
||||
if newFileSize != -1:
|
||||
if ftruncate(result.handle, newFileSize) == -1:
|
||||
fail(OSLastError(), "error setting file size")
|
||||
fail(osLastError(), "error setting file size")
|
||||
|
||||
if mappedSize != -1:
|
||||
result.size = mappedSize
|
||||
else:
|
||||
var stat: Tstat
|
||||
var stat: TStat
|
||||
if fstat(result.handle, stat) != -1:
|
||||
# XXX: Hmm, this could be unsafe
|
||||
# Why is mmap taking int anyway?
|
||||
result.size = int(stat.st_size)
|
||||
else:
|
||||
fail(OSLastError(), "error getting file size")
|
||||
fail(osLastError(), "error getting file size")
|
||||
|
||||
result.mem = mmap(
|
||||
nil,
|
||||
|
|
@ -159,7 +159,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
|||
offset)
|
||||
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(OSLastError(), "file mapping failed")
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
proc close*(f: var TMemFile) =
|
||||
## closes the memory mapped file `f`. All changes are written back to the
|
||||
|
|
@ -170,13 +170,13 @@ proc close*(f: var TMemFile) =
|
|||
|
||||
when defined(windows):
|
||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||
lastErr = OSLastError()
|
||||
error = UnmapViewOfFile(f.mem) == 0
|
||||
error = (CloseHandle(f.mapHandle) == 0) or error
|
||||
error = (CloseHandle(f.fHandle) == 0) or error
|
||||
lastErr = osLastError()
|
||||
error = unmapViewOfFile(f.mem) == 0
|
||||
error = (closeHandle(f.mapHandle) == 0) or error
|
||||
error = (closeHandle(f.fHandle) == 0) or error
|
||||
else:
|
||||
if f.handle != 0:
|
||||
lastErr = OSLastError()
|
||||
lastErr = osLastError()
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
error = (close(f.handle) != 0) or error
|
||||
|
||||
|
|
@ -189,5 +189,5 @@ proc close*(f: var TMemFile) =
|
|||
else:
|
||||
f.handle = 0
|
||||
|
||||
if error: OSError(lastErr)
|
||||
if error: osError(lastErr)
|
||||
|
||||
|
|
|
|||
39
lib/pure/mersenne.nim
Normal file
39
lib/pure/mersenne.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import unsigned
|
||||
|
||||
type
|
||||
TMersenneTwister* = object
|
||||
mt: array[0..623, uint32]
|
||||
index: int
|
||||
|
||||
proc newMersenneTwister*(seed: int): TMersenneTwister =
|
||||
result.index = 0
|
||||
result.mt[0]= uint32(seed)
|
||||
for i in 1..623'u32:
|
||||
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
|
||||
|
||||
proc generateNumbers(m: var TMersenneTwister) =
|
||||
for i in 0..623:
|
||||
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
|
||||
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
|
||||
if (y mod 2'u32) != 0:
|
||||
m.mt[i] = m.mt[i] xor 0x9908b0df'u32
|
||||
|
||||
proc getNum*(m: var TMersenneTwister): int =
|
||||
if m.index == 0:
|
||||
generateNumbers(m)
|
||||
var y = m.mt[m.index]
|
||||
y = y xor (y shr 11'u32)
|
||||
y = y xor ((7'u32 shl y) and 0x9d2c5680'u32)
|
||||
y = y xor ((15'u32 shl y) and 0xefc60000'u32)
|
||||
y = y xor (y shr 18'u32)
|
||||
m.index = (m.index+1) mod 624
|
||||
return int(y)
|
||||
|
||||
|
||||
|
||||
# Test
|
||||
when isMainModule:
|
||||
var mt = newMersenneTwister(2525)
|
||||
|
||||
for i in 0..99:
|
||||
echo mt.getNum
|
||||
|
|
@ -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.
|
||||
|
|
@ -28,7 +28,7 @@ proc hexbyte*(hex: char): int =
|
|||
of '0'..'9': result = (ord(hex) - ord('0'))
|
||||
of 'a'..'f': result = (ord(hex) - ord('a') + 10)
|
||||
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc parseOid*(str: cstring): TOid =
|
||||
## parses an OID.
|
||||
|
|
@ -52,6 +52,10 @@ proc oidToString*(oid: TOid, str: cstring) =
|
|||
inc(i)
|
||||
str[24] = '\0'
|
||||
|
||||
proc `$`*(oid: TOid): string =
|
||||
result = newString(25)
|
||||
oidToString(oid, result)
|
||||
|
||||
var
|
||||
incr: int
|
||||
fuzz: int32
|
||||
|
|
|
|||
819
lib/pure/os.nim
819
lib/pure/os.nim
File diff suppressed because it is too large
Load diff
|
|
@ -13,7 +13,7 @@
|
|||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
strutils, os, strtabs, streams
|
||||
strutils, os, strtabs, streams, sequtils
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
|
|
@ -23,7 +23,7 @@ else:
|
|||
type
|
||||
TProcess = object of TObject
|
||||
when defined(windows):
|
||||
FProcessHandle: Thandle
|
||||
fProcessHandle: THandle
|
||||
inHandle, outHandle, errHandle: TFileHandle
|
||||
id: THandle
|
||||
else:
|
||||
|
|
@ -36,18 +36,81 @@ type
|
|||
|
||||
TProcessOption* = enum ## options that can be passed `startProcess`
|
||||
poEchoCmd, ## echo the command before execution
|
||||
poUseShell, ## use the shell to execute the command; NOTE: This
|
||||
## often creates a security hole!
|
||||
poUsePath, ## Asks system to search for executable using PATH environment
|
||||
## variable.
|
||||
## On Windows, this is the default.
|
||||
poEvalCommand, ## Pass `command` directly to the shell, without quoting.
|
||||
## Use it only if `command` comes from trused source.
|
||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||
poParentStreams ## use the parent's streams
|
||||
|
||||
template poUseShell*: TProcessOption {.deprecated.} = poUsePath
|
||||
## Deprecated alias for poUsePath.
|
||||
|
||||
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||
## Quote s, so it can be safely passed to Windows API.
|
||||
## Based on Python's subprocess.list2cmdline
|
||||
## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
let needQuote = {' ', '\t'} in s or s.len == 0
|
||||
|
||||
result = ""
|
||||
var backslashBuff = ""
|
||||
if needQuote:
|
||||
result.add("\"")
|
||||
|
||||
for c in s:
|
||||
if c == '\\':
|
||||
backslashBuff.add(c)
|
||||
elif c == '\"':
|
||||
result.add(backslashBuff)
|
||||
result.add(backslashBuff)
|
||||
backslashBuff.setLen(0)
|
||||
result.add("\\\"")
|
||||
else:
|
||||
if backslashBuff.len != 0:
|
||||
result.add(backslashBuff)
|
||||
backslashBuff.setLen(0)
|
||||
result.add(c)
|
||||
|
||||
if needQuote:
|
||||
result.add("\"")
|
||||
|
||||
proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||
## Quote s, so it can be safely passed to POSIX shell.
|
||||
## Based on Python's pipes.quote
|
||||
const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
|
||||
'0'..'9', 'A'..'Z', 'a'..'z'}
|
||||
if s.len == 0:
|
||||
return "''"
|
||||
|
||||
let safe = s.allCharsInSet(safeUnixChars)
|
||||
|
||||
if safe:
|
||||
return s
|
||||
else:
|
||||
return "'" & s.replace("'", "'\"'\"'") & "'"
|
||||
|
||||
proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||
## Quote s, so it can be safely passed to shell.
|
||||
when defined(Windows):
|
||||
return quoteShellWindows(s)
|
||||
elif defined(posix):
|
||||
return quoteShellPosix(s)
|
||||
else:
|
||||
{.error:"quoteShell is not supported on your system".}
|
||||
|
||||
proc execProcess*(command: string,
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): TaintedString {.
|
||||
poUsePath,
|
||||
poEvalCommand}): TaintedString {.
|
||||
rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FReadIO].}
|
||||
## A convenience procedure that executes ``command`` with ``startProcess``
|
||||
## and returns its output as a string.
|
||||
## WARNING: this function uses poEvalCommand by default for backward compatibility.
|
||||
## Make sure to pass options explicitly.
|
||||
|
||||
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
|
||||
## Executes ``command`` and returns its error code. Standard input, output,
|
||||
|
|
@ -56,9 +119,9 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
|
|||
|
||||
proc startProcess*(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
args: openArray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].}
|
||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||
## process's working directory. If ``workingDir == ""`` the current directory
|
||||
|
|
@ -75,16 +138,10 @@ proc startProcess*(command: string,
|
|||
## but ``EOS`` is raised in case of an error.
|
||||
|
||||
proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): PProcess {.
|
||||
tags: [FExecIO, FReadEnv].} =
|
||||
## a simpler version of `startProcess` that parses the command line into
|
||||
## program and arguments and then calls `startProcess` with the empty string
|
||||
## for `workingDir` and the nil string table for `env`.
|
||||
var c = parseCmdLine(command)
|
||||
var a: seq[string]
|
||||
newSeq(a, c.len-1) # avoid slicing for now (still unstable)
|
||||
for i in 1 .. c.len-1: a[i-1] = c[i]
|
||||
result = startProcess(command=c[0], args=a, options=options)
|
||||
poStdErrToStdOut, poUsePath}): PProcess {.
|
||||
tags: [FExecIO, FReadEnv], deprecated.} =
|
||||
## Deprecated - use `startProcess` directly.
|
||||
result = startProcess(command=command, options=options + {poEvalCommand})
|
||||
|
||||
proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## When the process has finished executing, cleanup related handles
|
||||
|
|
@ -105,7 +162,7 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
|
|||
## returns `p`'s process ID.
|
||||
return p.id
|
||||
|
||||
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
|
||||
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
|
||||
extern: "nosp$1", tags: [].}
|
||||
## waits for the process to finish and returns `p`'s error code.
|
||||
|
||||
|
|
@ -115,19 +172,19 @@ proc peekExitCode*(p: PProcess): int {.tags: [].}
|
|||
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s input stream for writing to.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s output stream for reading from.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s error stream for reading from.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
|
||||
|
|
@ -167,7 +224,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
|||
## returns the numer of the processors/cores the machine has.
|
||||
## Returns 0 if it cannot be detected.
|
||||
when defined(windows):
|
||||
var x = getenv("NUMBER_OF_PROCESSORS")
|
||||
var x = getEnv("NUMBER_OF_PROCESSORS")
|
||||
if x.len > 0: result = parseInt(x.string)
|
||||
elif defined(macosx) or defined(bsd):
|
||||
var
|
||||
|
|
@ -193,15 +250,15 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
|||
|
||||
proc execProcesses*(cmds: openArray[string],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FTime, FReadEnv].} =
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FTime, FReadEnv]} =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
when defined(posix):
|
||||
# poParentStreams causes problems on Posix, so we simply disable it:
|
||||
var options = options - {poParentStreams}
|
||||
|
||||
|
||||
assert n > 0
|
||||
if n > 1:
|
||||
var q: seq[PProcess]
|
||||
|
|
@ -255,13 +312,17 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int
|
|||
|
||||
when not defined(useNimRtl):
|
||||
proc execProcess(command: string,
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): TaintedString =
|
||||
var p = startCmd(command, options=options)
|
||||
poUsePath,
|
||||
poEvalCommand}): TaintedString =
|
||||
var p = startProcess(command, args=args, env=env, options=options)
|
||||
var outp = outputStream(p)
|
||||
result = TaintedString""
|
||||
var line = newStringOfCap(120).TaintedString
|
||||
while true:
|
||||
# FIXME: converts CR-LF to LF.
|
||||
if outp.readLine(line):
|
||||
result.string.add(line.string)
|
||||
result.string.add("\n")
|
||||
|
|
@ -277,18 +338,18 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
handle: THandle
|
||||
atTheEnd: bool
|
||||
|
||||
proc hsClose(s: PStream) = nil # nothing to do here
|
||||
proc hsClose(s: PStream) = discard # nothing to do here
|
||||
proc hsAtEnd(s: PStream): bool = return PFileHandleStream(s).atTheEnd
|
||||
|
||||
proc hsReadData(s: PStream, buffer: pointer, bufLen: int): int =
|
||||
var s = PFileHandleStream(s)
|
||||
if s.atTheEnd: return 0
|
||||
var br: int32
|
||||
var a = winlean.ReadFile(s.handle, buffer, bufLen.cint, br, nil)
|
||||
var a = winlean.readFile(s.handle, buffer, bufLen.cint, br, nil)
|
||||
# TRUE and zero bytes returned (EOF).
|
||||
# TRUE and n (>0) bytes returned (good data).
|
||||
# FALSE and bytes returned undefined (system error).
|
||||
if a == 0 and br != 0: OSError(OSLastError())
|
||||
if a == 0 and br != 0: osError(osLastError())
|
||||
s.atTheEnd = br < bufLen
|
||||
result = br
|
||||
|
||||
|
|
@ -296,7 +357,7 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
var s = PFileHandleStream(s)
|
||||
var bytesWritten: int32
|
||||
var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil)
|
||||
if a == 0: OSError(OSLastError())
|
||||
if a == 0: osError(osLastError())
|
||||
|
||||
proc newFileHandleStream(handle: THandle): PFileHandleStream =
|
||||
new(result)
|
||||
|
|
@ -306,11 +367,11 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
result.readDataImpl = hsReadData
|
||||
result.writeDataImpl = hsWriteData
|
||||
|
||||
proc buildCommandLine(a: string, args: openarray[string]): cstring =
|
||||
var res = quoteIfContainsWhite(a)
|
||||
proc buildCommandLine(a: string, args: openArray[string]): cstring =
|
||||
var res = quoteShell(a)
|
||||
for i in 0..high(args):
|
||||
res.add(' ')
|
||||
res.add(quoteIfContainsWhite(args[i]))
|
||||
res.add(quoteShell(args[i]))
|
||||
result = cast[cstring](alloc0(res.len+1))
|
||||
copyMem(result, cstring(res), res.len)
|
||||
|
||||
|
|
@ -331,52 +392,53 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
|
||||
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
|
||||
|
||||
proc CreatePipeHandles(Rdhandle, WrHandle: var THandle) =
|
||||
var piInheritablePipe: TSecurityAttributes
|
||||
piInheritablePipe.nlength = SizeOF(TSecurityAttributes).cint
|
||||
proc createPipeHandles(rdHandle, wrHandle: var THandle) =
|
||||
var piInheritablePipe: TSECURITY_ATTRIBUTES
|
||||
piInheritablePipe.nLength = sizeof(TSECURITY_ATTRIBUTES).cint
|
||||
piInheritablePipe.lpSecurityDescriptor = nil
|
||||
piInheritablePipe.Binherithandle = 1
|
||||
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
|
||||
OSError(OSLastError())
|
||||
piInheritablePipe.bInheritHandle = 1
|
||||
if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32:
|
||||
osError(osLastError())
|
||||
|
||||
proc fileClose(h: THandle) {.inline.} =
|
||||
if h > 4: discard CloseHandle(h)
|
||||
if h > 4: discard closeHandle(h)
|
||||
|
||||
proc startProcess(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
args: openArray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
|
||||
var
|
||||
SI: TStartupInfo
|
||||
ProcInfo: TProcessInformation
|
||||
si: TSTARTUPINFO
|
||||
procInfo: TPROCESS_INFORMATION
|
||||
success: int
|
||||
hi, ho, he: THandle
|
||||
new(result)
|
||||
SI.cb = SizeOf(SI).cint
|
||||
si.cb = sizeof(si).cint
|
||||
if poParentStreams notin options:
|
||||
SI.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
|
||||
CreatePipeHandles(SI.hStdInput, HI)
|
||||
CreatePipeHandles(HO, Si.hStdOutput)
|
||||
si.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
|
||||
createPipeHandles(si.hStdInput, hi)
|
||||
createPipeHandles(ho, si.hStdOutput)
|
||||
if poStdErrToStdOut in options:
|
||||
SI.hStdError = SI.hStdOutput
|
||||
HE = HO
|
||||
si.hStdError = si.hStdOutput
|
||||
he = ho
|
||||
else:
|
||||
CreatePipeHandles(HE, Si.hStdError)
|
||||
createPipeHandles(he, si.hStdError)
|
||||
result.inHandle = TFileHandle(hi)
|
||||
result.outHandle = TFileHandle(ho)
|
||||
result.errHandle = TFileHandle(he)
|
||||
else:
|
||||
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
|
||||
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
si.hStdError = getStdHandle(STD_ERROR_HANDLE)
|
||||
si.hStdInput = getStdHandle(STD_INPUT_HANDLE)
|
||||
si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE)
|
||||
result.inHandle = TFileHandle(si.hStdInput)
|
||||
result.outHandle = TFileHandle(si.hStdOutput)
|
||||
result.errHandle = TFileHandle(si.hStdError)
|
||||
|
||||
var cmdl: cstring
|
||||
when false: # poUseShell in options:
|
||||
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
|
||||
if poEvalCommand in options:
|
||||
cmdl = command
|
||||
assert args.len == 0
|
||||
else:
|
||||
cmdl = buildCommandLine(command, args)
|
||||
var wd: cstring = nil
|
||||
|
|
@ -388,64 +450,63 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
var tmp = newWideCString(cmdl)
|
||||
var ee = newWideCString(e)
|
||||
var wwd = newWideCString(wd)
|
||||
success = winlean.CreateProcessW(nil,
|
||||
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
|
||||
ee, wwd, SI, ProcInfo)
|
||||
success = winlean.createProcessW(nil,
|
||||
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
|
||||
ee, wwd, si, procInfo)
|
||||
else:
|
||||
success = winlean.CreateProcessA(nil,
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||
let lastError = OSLastError()
|
||||
success = winlean.createProcessA(nil,
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
|
||||
let lastError = osLastError()
|
||||
|
||||
if poParentStreams notin options:
|
||||
FileClose(si.hStdInput)
|
||||
FileClose(si.hStdOutput)
|
||||
fileClose(si.hStdInput)
|
||||
fileClose(si.hStdOutput)
|
||||
if poStdErrToStdOut notin options:
|
||||
FileClose(si.hStdError)
|
||||
fileClose(si.hStdError)
|
||||
|
||||
if e != nil: dealloc(e)
|
||||
dealloc(cmdl)
|
||||
if success == 0: OSError(lastError)
|
||||
if success == 0: osError(lastError)
|
||||
# Close the handle now so anyone waiting is woken:
|
||||
discard closeHandle(procInfo.hThread)
|
||||
result.FProcessHandle = procInfo.hProcess
|
||||
result.id = procInfo.dwProcessID
|
||||
result.fProcessHandle = procInfo.hProcess
|
||||
result.id = procInfo.dwProcessId
|
||||
|
||||
proc close(p: PProcess) =
|
||||
when false:
|
||||
# somehow this does not work on Windows:
|
||||
discard CloseHandle(p.inHandle)
|
||||
discard CloseHandle(p.outHandle)
|
||||
discard CloseHandle(p.errHandle)
|
||||
discard CloseHandle(p.FProcessHandle)
|
||||
discard closeHandle(p.inHandle)
|
||||
discard closeHandle(p.outHandle)
|
||||
discard closeHandle(p.errHandle)
|
||||
discard closeHandle(p.FProcessHandle)
|
||||
|
||||
proc suspend(p: PProcess) =
|
||||
discard SuspendThread(p.FProcessHandle)
|
||||
discard suspendThread(p.fProcessHandle)
|
||||
|
||||
proc resume(p: PProcess) =
|
||||
discard ResumeThread(p.FProcessHandle)
|
||||
discard resumeThread(p.fProcessHandle)
|
||||
|
||||
proc running(p: PProcess): bool =
|
||||
var x = waitForSingleObject(p.FProcessHandle, 50)
|
||||
var x = waitForSingleObject(p.fProcessHandle, 50)
|
||||
return x == WAIT_TIMEOUT
|
||||
|
||||
proc terminate(p: PProcess) =
|
||||
if running(p):
|
||||
discard TerminateProcess(p.FProcessHandle, 0)
|
||||
discard terminateProcess(p.fProcessHandle, 0)
|
||||
|
||||
proc waitForExit(p: PProcess, timeout: int = -1): int =
|
||||
discard WaitForSingleObject(p.FProcessHandle, timeout.int32)
|
||||
discard waitForSingleObject(p.fProcessHandle, timeout.int32)
|
||||
|
||||
var res: int32
|
||||
discard GetExitCodeProcess(p.FProcessHandle, res)
|
||||
discard getExitCodeProcess(p.fProcessHandle, res)
|
||||
result = res
|
||||
discard CloseHandle(p.FProcessHandle)
|
||||
discard closeHandle(p.fProcessHandle)
|
||||
|
||||
proc peekExitCode(p: PProcess): int =
|
||||
var b = waitForSingleObject(p.FProcessHandle, 50) == WAIT_TIMEOUT
|
||||
var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT
|
||||
if b: result = -1
|
||||
else:
|
||||
else:
|
||||
var res: int32
|
||||
discard GetExitCodeProcess(p.FProcessHandle, res)
|
||||
discard getExitCodeProcess(p.fProcessHandle, res)
|
||||
return res
|
||||
|
||||
proc inputStream(p: PProcess): PStream =
|
||||
|
|
@ -459,46 +520,46 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
|
||||
proc execCmd(command: string): int =
|
||||
var
|
||||
SI: TStartupInfo
|
||||
ProcInfo: TProcessInformation
|
||||
si: TSTARTUPINFO
|
||||
procInfo: TPROCESS_INFORMATION
|
||||
process: THandle
|
||||
L: int32
|
||||
SI.cb = SizeOf(SI).cint
|
||||
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
|
||||
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
si.cb = sizeof(si).cint
|
||||
si.hStdError = getStdHandle(STD_ERROR_HANDLE)
|
||||
si.hStdInput = getStdHandle(STD_INPUT_HANDLE)
|
||||
si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE)
|
||||
when useWinUnicode:
|
||||
var c = newWideCString(command)
|
||||
var res = winlean.CreateProcessW(nil, c, nil, nil, 0,
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
||||
var res = winlean.createProcessW(nil, c, nil, nil, 0,
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
|
||||
else:
|
||||
var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
||||
var res = winlean.createProcessA(nil, command, nil, nil, 0,
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
|
||||
if res == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
else:
|
||||
Process = ProcInfo.hProcess
|
||||
discard CloseHandle(ProcInfo.hThread)
|
||||
if WaitForSingleObject(Process, INFINITE) != -1:
|
||||
discard GetExitCodeProcess(Process, L)
|
||||
process = procInfo.hProcess
|
||||
discard closeHandle(procInfo.hThread)
|
||||
if waitForSingleObject(process, INFINITE) != -1:
|
||||
discard getExitCodeProcess(process, L)
|
||||
result = int(L)
|
||||
else:
|
||||
result = -1
|
||||
discard CloseHandle(Process)
|
||||
discard closeHandle(process)
|
||||
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
assert readfds.len <= MAXIMUM_WAIT_OBJECTS
|
||||
var rfds: TWOHandleArray
|
||||
for i in 0..readfds.len()-1:
|
||||
rfds[i] = readfds[i].FProcessHandle
|
||||
|
||||
var ret = waitForMultipleObjects(readfds.len.int32,
|
||||
rfds[i] = readfds[i].fProcessHandle
|
||||
|
||||
var ret = waitForMultipleObjects(readfds.len.int32,
|
||||
addr(rfds), 0'i32, timeout.int32)
|
||||
case ret
|
||||
of WAIT_TIMEOUT:
|
||||
return 0
|
||||
of WAIT_FAILED:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
else:
|
||||
var i = ret - WAIT_OBJECT_0
|
||||
readfds.del(i)
|
||||
|
|
@ -509,22 +570,7 @@ elif not defined(useNimRtl):
|
|||
readIdx = 0
|
||||
writeIdx = 1
|
||||
|
||||
proc addCmdArgs(command: string, args: openarray[string]): string =
|
||||
result = quoteIfContainsWhite(command)
|
||||
for i in 0 .. high(args):
|
||||
add(result, " ")
|
||||
add(result, quoteIfContainsWhite(args[i]))
|
||||
|
||||
proc toCStringArray(b, a: openarray[string]): cstringArray =
|
||||
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
|
||||
for i in 0..high(b):
|
||||
result[i] = cast[cstring](alloc(b[i].len+1))
|
||||
copyMem(result[i], cstring(b[i]), b[i].len+1)
|
||||
for i in 0..high(a):
|
||||
result[i+b.len] = cast[cstring](alloc(a[i].len+1))
|
||||
copyMem(result[i+b.len], cstring(a[i]), a[i].len+1)
|
||||
|
||||
proc ToCStringArray(t: PStringTable): cstringArray =
|
||||
proc envToCStringArray(t: PStringTable): cstringArray =
|
||||
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
|
||||
var i = 0
|
||||
for key, val in pairs(t):
|
||||
|
|
@ -533,7 +579,7 @@ elif not defined(useNimRtl):
|
|||
copyMem(result[i], addr(x[0]), x.len+1)
|
||||
inc(i)
|
||||
|
||||
proc EnvToCStringArray(): cstringArray =
|
||||
proc envToCStringArray(): cstringArray =
|
||||
var counter = 0
|
||||
for key, val in envPairs(): inc counter
|
||||
result = cast[cstringArray](alloc0((counter + 1) * sizeof(cstring)))
|
||||
|
|
@ -543,104 +589,113 @@ elif not defined(useNimRtl):
|
|||
result[i] = cast[cstring](alloc(x.len+1))
|
||||
copyMem(result[i], addr(x[0]), x.len+1)
|
||||
inc(i)
|
||||
|
||||
|
||||
proc startProcess(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
args: openArray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
|
||||
var
|
||||
p_stdin, p_stdout, p_stderr: array [0..1, cint]
|
||||
pStdin, pStdout, pStderr: array [0..1, cint]
|
||||
new(result)
|
||||
result.exitCode = -3 # for ``waitForExit``
|
||||
if poParentStreams notin options:
|
||||
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or
|
||||
pipe(p_stderr) != 0'i32:
|
||||
OSError(OSLastError())
|
||||
|
||||
if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
|
||||
pipe(pStderr) != 0'i32:
|
||||
osError(osLastError())
|
||||
|
||||
var sys_command: string
|
||||
var sys_args_raw: seq[string]
|
||||
if poEvalCommand in options:
|
||||
sys_command = "/bin/sh"
|
||||
sys_args_raw = @[sys_command, "-c", command]
|
||||
assert args.len == 0
|
||||
else:
|
||||
sys_command = command
|
||||
sys_args_raw = @[command]
|
||||
for arg in args.items:
|
||||
sys_args_raw.add arg
|
||||
|
||||
var sys_args = allocCStringArray(sys_args_raw)
|
||||
finally: deallocCStringArray(sys_args)
|
||||
|
||||
var pid: TPid
|
||||
when defined(posix_spawn) and not defined(useFork):
|
||||
var attr: Tposix_spawnattr
|
||||
var fops: Tposix_spawn_file_actions
|
||||
|
||||
template chck(e: expr) =
|
||||
if e != 0'i32: OSError(OSLastError())
|
||||
template chck(e: expr) =
|
||||
if e != 0'i32: osError(osLastError())
|
||||
|
||||
chck posix_spawn_file_actions_init(fops)
|
||||
chck posix_spawnattr_init(attr)
|
||||
|
||||
|
||||
var mask: Tsigset
|
||||
chck sigemptyset(mask)
|
||||
chck posix_spawnattr_setsigmask(attr, mask)
|
||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||
|
||||
|
||||
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
||||
POSIX_SPAWN_SETSIGMASK or
|
||||
POSIX_SPAWN_SETPGROUP)
|
||||
|
||||
if poParentStreams notin options:
|
||||
chck posix_spawn_file_actions_addclose(fops, p_stdin[writeIdx])
|
||||
chck posix_spawn_file_actions_adddup2(fops, p_stdin[readIdx], readIdx)
|
||||
chck posix_spawn_file_actions_addclose(fops, p_stdout[readIdx])
|
||||
chck posix_spawn_file_actions_adddup2(fops, p_stdout[writeIdx], writeIdx)
|
||||
chck posix_spawn_file_actions_addclose(fops, p_stderr[readIdx])
|
||||
chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx])
|
||||
chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx)
|
||||
chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx])
|
||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx)
|
||||
chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx])
|
||||
if poStdErrToStdOut in options:
|
||||
chck posix_spawn_file_actions_adddup2(fops, p_stdout[writeIdx], 2)
|
||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
|
||||
else:
|
||||
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
|
||||
|
||||
var e = if env == nil: EnvToCStringArray() else: ToCStringArray(env)
|
||||
var a: cstringArray
|
||||
|
||||
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
|
||||
var res: cint
|
||||
# This is incorrect!
|
||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
||||
if poUseShell notin options:
|
||||
a = toCStringArray([extractFilename(command)], args)
|
||||
res = posix_spawn(pid, command, fops, attr, a, e)
|
||||
if poUsePath in options:
|
||||
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
|
||||
else:
|
||||
var x = addCmdArgs(command, args)
|
||||
a = toCStringArray(["sh", "-c"], [x])
|
||||
res = posix_spawn(pid, "/bin/sh", fops, attr, a, e)
|
||||
deallocCStringArray(a)
|
||||
deallocCStringArray(e)
|
||||
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
|
||||
deallocCStringArray(sys_env)
|
||||
discard posix_spawn_file_actions_destroy(fops)
|
||||
discard posix_spawnattr_destroy(attr)
|
||||
chck res
|
||||
|
||||
else:
|
||||
|
||||
Pid = fork()
|
||||
if Pid < 0: OSError(OSLastError())
|
||||
pid = fork()
|
||||
if pid < 0: osError(osLastError())
|
||||
if pid == 0:
|
||||
## child process:
|
||||
|
||||
if poParentStreams notin options:
|
||||
discard close(p_stdin[writeIdx])
|
||||
if dup2(p_stdin[readIdx], readIdx) < 0: OSError(OSLastError())
|
||||
if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError())
|
||||
discard close(p_stdout[readIdx])
|
||||
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError(OSLastError())
|
||||
if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError())
|
||||
discard close(p_stderr[readIdx])
|
||||
if poStdErrToStdOut in options:
|
||||
if dup2(p_stdout[writeIdx], 2) < 0: OSError(OSLastError())
|
||||
if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError())
|
||||
else:
|
||||
if dup2(p_stderr[writeIdx], 2) < 0: OSError(OSLastError())
|
||||
if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError())
|
||||
|
||||
# Create a new process group
|
||||
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
|
||||
|
||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
||||
if poUseShell notin options:
|
||||
var a = toCStringArray([extractFilename(command)], args)
|
||||
if env == nil:
|
||||
discard execv(command, a)
|
||||
|
||||
if env == nil:
|
||||
if poUsePath in options:
|
||||
discard execvp(sys_command, sys_args)
|
||||
else:
|
||||
discard execve(command, a, ToCStringArray(env))
|
||||
discard execv(sys_command, sys_args)
|
||||
else:
|
||||
var x = addCmdArgs(command, args)
|
||||
var a = toCStringArray(["sh", "-c"], [x])
|
||||
if env == nil:
|
||||
discard execv("/bin/sh", a)
|
||||
var c_env = envToCStringArray(env)
|
||||
if poUsePath in options:
|
||||
discard execvpe(sys_command, sys_args, c_env)
|
||||
else:
|
||||
discard execve("/bin/sh", a, ToCStringArray(env))
|
||||
discard execve(sys_command, sys_args, c_env)
|
||||
# too risky to raise an exception here:
|
||||
quit("execve call failed: " & $strerror(errno))
|
||||
# Parent process. Copy process information.
|
||||
|
|
@ -657,16 +712,16 @@ elif not defined(useNimRtl):
|
|||
else:
|
||||
result.errHandle = 2
|
||||
else:
|
||||
result.inHandle = p_stdin[writeIdx]
|
||||
result.outHandle = p_stdout[readIdx]
|
||||
result.inHandle = pStdin[writeIdx]
|
||||
result.outHandle = pStdout[readIdx]
|
||||
if poStdErrToStdOut in options:
|
||||
result.errHandle = result.outHandle
|
||||
discard close(p_stderr[readIdx])
|
||||
discard close(pStderr[readIdx])
|
||||
else:
|
||||
result.errHandle = p_stderr[readIdx]
|
||||
discard close(p_stderr[writeIdx])
|
||||
discard close(p_stdin[readIdx])
|
||||
discard close(p_stdout[writeIdx])
|
||||
result.errHandle = pStderr[readIdx]
|
||||
discard close(pStderr[writeIdx])
|
||||
discard close(pStdin[readIdx])
|
||||
discard close(pStdout[writeIdx])
|
||||
|
||||
proc close(p: PProcess) =
|
||||
if p.inStream != nil: close(p.inStream)
|
||||
|
|
@ -677,21 +732,21 @@ elif not defined(useNimRtl):
|
|||
discard close(p.errHandle)
|
||||
|
||||
proc suspend(p: PProcess) =
|
||||
if kill(-p.id, SIGSTOP) != 0'i32: OSError(OSLastError())
|
||||
if kill(-p.id, SIGSTOP) != 0'i32: osError(osLastError())
|
||||
|
||||
proc resume(p: PProcess) =
|
||||
if kill(-p.id, SIGCONT) != 0'i32: OSError(OSLastError())
|
||||
if kill(-p.id, SIGCONT) != 0'i32: osError(osLastError())
|
||||
|
||||
proc running(p: PProcess): bool =
|
||||
var ret = waitPid(p.id, p.exitCode, WNOHANG)
|
||||
var ret = waitpid(p.id, p.exitCode, WNOHANG)
|
||||
if ret == 0: return true # Can't establish status. Assume running.
|
||||
result = ret == int(p.id)
|
||||
|
||||
proc terminate(p: PProcess) =
|
||||
if kill(-p.id, SIGTERM) == 0'i32:
|
||||
if p.running():
|
||||
if kill(-p.id, SIGKILL) != 0'i32: OSError(OSLastError())
|
||||
else: OSError(OSLastError())
|
||||
if kill(-p.id, SIGKILL) != 0'i32: osError(osLastError())
|
||||
else: osError(osLastError())
|
||||
|
||||
proc waitForExit(p: PProcess, timeout: int = -1): int =
|
||||
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
||||
|
|
@ -699,14 +754,14 @@ elif not defined(useNimRtl):
|
|||
# ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
|
||||
# initialized with -3, wrong success exit codes are prevented.
|
||||
if p.exitCode != -3: return p.exitCode
|
||||
if waitPid(p.id, p.exitCode, 0) < 0:
|
||||
if waitpid(p.id, p.exitCode, 0) < 0:
|
||||
p.exitCode = -3
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
result = int(p.exitCode) shr 8
|
||||
|
||||
proc peekExitCode(p: PProcess): int =
|
||||
if p.exitCode != -3: return p.exitCode
|
||||
var ret = waitPid(p.id, p.exitCode, WNOHANG)
|
||||
var ret = waitpid(p.id, p.exitCode, WNOHANG)
|
||||
var b = ret == int(p.id)
|
||||
if b: result = -1
|
||||
if p.exitCode == -3: result = -1
|
||||
|
|
@ -715,7 +770,7 @@ elif not defined(useNimRtl):
|
|||
proc createStream(stream: var PStream, handle: var TFileHandle,
|
||||
fileMode: TFileMode) =
|
||||
var f: TFile
|
||||
if not open(f, handle, fileMode): OSError(OSLastError())
|
||||
if not open(f, handle, fileMode): osError(osLastError())
|
||||
stream = newFileStream(f)
|
||||
|
||||
proc inputStream(p: PProcess): PStream =
|
||||
|
|
@ -736,45 +791,45 @@ elif not defined(useNimRtl):
|
|||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
||||
|
||||
proc execCmd(command: string): int =
|
||||
result = csystem(command)
|
||||
result = csystem(command) shr 8
|
||||
|
||||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
for i in items(s):
|
||||
m = max(m, int(i.outHandle))
|
||||
FD_SET(cint(i.outHandle), fd)
|
||||
|
||||
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
|
||||
|
||||
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
|
||||
var i = 0
|
||||
var L = s.len
|
||||
while i < L:
|
||||
if FD_ISSET(cint(s[i].outHandle), fd) != 0'i32:
|
||||
if FD_ISSET(cint(s[i].outHandle), fd) == 0'i32:
|
||||
s[i] = s[L-1]
|
||||
dec(L)
|
||||
else:
|
||||
inc(i)
|
||||
setLen(s, L)
|
||||
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
|
||||
|
||||
var rd: TFdSet
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
|
||||
|
||||
|
||||
pruneProcessSet(readfds, (rd))
|
||||
|
||||
|
||||
proc execCmdEx*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): tuple[
|
||||
output: TaintedString,
|
||||
poStdErrToStdOut, poUsePath}): tuple[
|
||||
output: TaintedString,
|
||||
exitCode: int] {.tags: [FExecIO, FReadIO].} =
|
||||
## a convenience proc that runs the `command`, grabs all its output and
|
||||
## exit code and returns both.
|
||||
|
|
@ -792,5 +847,14 @@ proc execCmdEx*(command: string, options: set[TProcessOption] = {
|
|||
close(p)
|
||||
|
||||
when isMainModule:
|
||||
var x = execProcess("gcc -v")
|
||||
echo "ECHO ", x
|
||||
assert quoteShellWindows("aaa") == "aaa"
|
||||
assert quoteShellWindows("aaa\"") == "aaa\\\""
|
||||
assert quoteShellWindows("") == "\"\""
|
||||
|
||||
assert quoteShellPosix("aaa") == "aaa"
|
||||
assert quoteShellPosix("aaa a") == "'aaa a'"
|
||||
assert quoteShellPosix("") == "''"
|
||||
assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
|
||||
|
||||
when defined(posix):
|
||||
assert quoteShell("") == "''"
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ proc open*(c: var TCfgParser, input: PStream, filename: string,
|
|||
c.filename = filename
|
||||
c.tok.kind = tkInvalid
|
||||
c.tok.literal = ""
|
||||
inc(c.linenumber, lineOffset)
|
||||
inc(c.lineNumber, lineOffset)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
|
||||
|
|
@ -91,11 +91,11 @@ proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
|
|||
|
||||
proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(c, c.bufPos)
|
||||
result = getColNumber(c, c.bufpos)
|
||||
|
||||
proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||
## get the current line the parser has arrived at.
|
||||
result = c.linenumber
|
||||
result = c.lineNumber
|
||||
|
||||
proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} =
|
||||
## get the filename of the file that the parser processes.
|
||||
|
|
@ -113,7 +113,7 @@ proc handleHexChar(c: var TCfgParser, xi: var int) =
|
|||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
|
||||
proc handleDecChars(c: var TCfgParser, xi: var int) =
|
||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||
|
|
@ -125,74 +125,74 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
|||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
add(tok.literal, "\n")
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'l', 'L':
|
||||
add(tok.literal, '\L')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'f', 'F':
|
||||
add(tok.literal, '\f')
|
||||
inc(c.bufpos)
|
||||
of 'e', 'E':
|
||||
add(tok.literal, '\e')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'a', 'A':
|
||||
add(tok.literal, '\a')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'b', 'B':
|
||||
add(tok.literal, '\b')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'v', 'V':
|
||||
add(tok.literal, '\v')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 't', 'T':
|
||||
add(tok.literal, '\t')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of '\'', '"':
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of '\\':
|
||||
add(tok.literal, '\\')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
var xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
add(tok.literal, Chr(xi))
|
||||
add(tok.literal, chr(xi))
|
||||
of '0'..'9':
|
||||
var xi = 0
|
||||
handleDecChars(c, xi)
|
||||
if (xi <= 255): add(tok.literal, Chr(xi))
|
||||
if (xi <= 255): add(tok.literal, chr(xi))
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc HandleCRLF(c: var TCfgParser, pos: int): int =
|
||||
proc handleCRLF(c: var TCfgParser, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.HandleCR(c, pos)
|
||||
of '\L': result = lexbase.HandleLF(c, pos)
|
||||
of '\c': result = lexbase.handleCR(c, pos)
|
||||
of '\L': result = lexbase.handleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||
var pos = c.bufPos + 1 # skip "
|
||||
var pos = c.bufpos + 1 # skip "
|
||||
var buf = c.buf # put `buf` in a register
|
||||
tok.kind = tkSymbol
|
||||
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
|
||||
# long string literal:
|
||||
inc(pos, 2) # skip ""
|
||||
# skip leading newline:
|
||||
pos = HandleCRLF(c, pos)
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '"':
|
||||
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
|
||||
add(tok.literal, '"')
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
add(tok.literal, "\n")
|
||||
of lexbase.EndOfFile:
|
||||
|
|
@ -200,7 +200,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
|||
break
|
||||
else:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
c.bufpos = pos + 3 # skip the three """
|
||||
else:
|
||||
# ordinary string literal
|
||||
|
|
@ -213,12 +213,12 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
|||
tok.kind = tkInvalid
|
||||
break
|
||||
if (ch == '\\') and not rawMode:
|
||||
c.bufPos = pos
|
||||
c.bufpos = pos
|
||||
getEscapedChar(c, tok)
|
||||
pos = c.bufPos
|
||||
pos = c.bufpos
|
||||
else:
|
||||
add(tok.literal, ch)
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||
|
|
@ -226,7 +226,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
|||
var buf = c.buf
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
if not (buf[pos] in SymChars): break
|
||||
c.bufpos = pos
|
||||
tok.kind = tkSymbol
|
||||
|
|
@ -237,11 +237,11 @@ proc skip(c: var TCfgParser) =
|
|||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
of '#', ';':
|
||||
while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
|
|
@ -249,7 +249,7 @@ proc skip(c: var TCfgParser) =
|
|||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||
tok.kind = tkInvalid
|
||||
setlen(tok.literal, 0)
|
||||
setLen(tok.literal, 0)
|
||||
skip(c)
|
||||
case c.buf[c.bufpos]
|
||||
of '=':
|
||||
|
|
@ -257,8 +257,8 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
|||
inc(c.bufpos)
|
||||
tok.literal = "="
|
||||
of '-':
|
||||
inc(c.bufPos)
|
||||
if c.buf[c.bufPos] == '-': inc(c.bufPos)
|
||||
inc(c.bufpos)
|
||||
if c.buf[c.bufpos] == '-': inc(c.bufpos)
|
||||
tok.kind = tkDashDash
|
||||
tok.literal = "--"
|
||||
of ':':
|
||||
|
|
@ -266,8 +266,8 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
|||
inc(c.bufpos)
|
||||
tok.literal = ":"
|
||||
of 'r', 'R':
|
||||
if c.buf[c.bufPos + 1] == '\"':
|
||||
Inc(c.bufPos)
|
||||
if c.buf[c.bufpos + 1] == '\"':
|
||||
inc(c.bufpos)
|
||||
getString(c, tok, true)
|
||||
else:
|
||||
getSymbol(c, tok)
|
||||
|
|
@ -277,7 +277,7 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
|||
tok.literal = "]"
|
||||
of ']':
|
||||
tok.kind = tkBracketRi
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
tok.literal = "]"
|
||||
of '"':
|
||||
getString(c, tok, false)
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
|
|||
of '\c': my.bufpos = handleCR(my, my.bufpos)
|
||||
of '\l': my.bufpos = handleLF(my, my.bufpos)
|
||||
else: break
|
||||
of '\0': nil
|
||||
of '\0': discard
|
||||
else: error(my, my.bufpos, my.sep & " expected")
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
## This module provides the standard Nimrod command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
|
||||
##
|
||||
## **Deprecated since version 0.9.3:** Use the `parseopt2 <parseopt2.html>`_
|
||||
## module instead as this version has issues with spaces in arguments.
|
||||
{.deprecated.}
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
|
@ -34,7 +37,7 @@ type
|
|||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
when defined(os.ParamCount):
|
||||
when defined(os.paramCount):
|
||||
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||
# access the command line arguments then!
|
||||
|
||||
|
|
@ -47,7 +50,7 @@ when defined(os.ParamCount):
|
|||
result.cmd = cmdline
|
||||
else:
|
||||
result.cmd = ""
|
||||
for i in countup(1, ParamCount()):
|
||||
for i in countup(1, paramCount()):
|
||||
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' '
|
||||
result.kind = cmdEnd
|
||||
result.key = TaintedString""
|
||||
|
|
@ -91,8 +94,8 @@ proc next*(p: var TOptParser) {.
|
|||
var i = p.pos
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
p.pos = i
|
||||
setlen(p.key.string, 0)
|
||||
setlen(p.val.string, 0)
|
||||
setLen(p.key.string, 0)
|
||||
setLen(p.val.string, 0)
|
||||
if p.inShortState:
|
||||
handleShortOption(p)
|
||||
return
|
||||
|
|
@ -102,7 +105,7 @@ proc next*(p: var TOptParser) {.
|
|||
of '-':
|
||||
inc(i)
|
||||
if p.cmd[i] == '-':
|
||||
p.kind = cmdLongOption
|
||||
p.kind = cmdLongoption
|
||||
inc(i)
|
||||
i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
|
||||
while p.cmd[i] in {'\x09', ' '}: inc(i)
|
||||
|
|
|
|||
148
lib/pure/parseopt2.nim
Normal file
148
lib/pure/parseopt2.nim
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides the standard Nimrod command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
##
|
||||
## Supported syntax:
|
||||
##
|
||||
## 1. short options - ``-abcd``, where a, b, c, d are names
|
||||
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
|
||||
## 3. argument - everything else
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
os, strutils
|
||||
|
||||
type
|
||||
TCmdLineKind* = enum ## the detected command line token
|
||||
cmdEnd, ## end of command line reached
|
||||
cmdArgument, ## argument detected
|
||||
cmdLongOption, ## a long option ``--option`` detected
|
||||
cmdShortOption ## a short option ``-c`` detected
|
||||
TOptParser* =
|
||||
object of TObject ## this object implements the command line parser
|
||||
cmd: seq[string]
|
||||
pos: int
|
||||
remainingShortOptions: string
|
||||
kind*: TCmdLineKind ## the dected command line token
|
||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
||||
## Initalizes option parses with cmdline. cmdline should not contain
|
||||
## argument 0 - program name.
|
||||
## If cmdline == nil default to current command line arguments.
|
||||
result.remainingShortOptions = ""
|
||||
when not defined(createNimRtl):
|
||||
if cmdline == nil:
|
||||
result.cmd = commandLineParams()
|
||||
return
|
||||
else:
|
||||
assert cmdline != nil, "Cannot determine command line arguments."
|
||||
|
||||
result.cmd = @cmdline
|
||||
|
||||
proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
||||
## Initalizes option parses with cmdline. Splits cmdline in on spaces
|
||||
## and calls initOptParser(openarray[string])
|
||||
## Do not use.
|
||||
if cmdline == "": # backward compatibilty
|
||||
return initOptParser(seq[string](nil))
|
||||
else:
|
||||
return initOptParser(cmdline.split)
|
||||
|
||||
when not defined(createNimRtl):
|
||||
proc initOptParser*(): TOptParser =
|
||||
## Initializes option parser from current command line arguments.
|
||||
return initOptParser(commandLineParams())
|
||||
|
||||
proc next*(p: var TOptParser) {.rtl, extern: "npo$1".}
|
||||
|
||||
proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
||||
for splitchar in [':', '=']:
|
||||
if splitchar in token:
|
||||
let pos = token.find(splitchar)
|
||||
p.key = token[0..pos-1]
|
||||
p.val = token[pos+1..token.len-1]
|
||||
return
|
||||
|
||||
p.key = token
|
||||
if allowEmpty:
|
||||
p.val = ""
|
||||
else:
|
||||
p.remainingShortOptions = token[0..token.len-1]
|
||||
p.next()
|
||||
|
||||
proc next(p: var TOptParser) =
|
||||
if p.remainingShortOptions.len != 0:
|
||||
p.kind = cmdShortOption
|
||||
p.key = TaintedString(p.remainingShortOptions[0..0])
|
||||
p.val = ""
|
||||
p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1]
|
||||
return
|
||||
|
||||
if p.pos >= p.cmd.len:
|
||||
p.kind = cmdEnd
|
||||
return
|
||||
|
||||
let token = p.cmd[p.pos]
|
||||
p.pos += 1
|
||||
|
||||
if token.startswith("--"):
|
||||
p.kind = cmdLongOption
|
||||
nextOption(p, token[2..token.len-1], allowEmpty=true)
|
||||
elif token.startswith("-"):
|
||||
p.kind = cmdShortOption
|
||||
nextOption(p, token[1..token.len-1], allowEmpty=true)
|
||||
else:
|
||||
p.kind = cmdArgument
|
||||
p.key = token
|
||||
p.val = ""
|
||||
|
||||
proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||
## Returns part of command line string that has not been parsed yet.
|
||||
## Do not use - does not correctly handle whitespace.
|
||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||
|
||||
type
|
||||
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString]
|
||||
|
||||
when defined(paramCount):
|
||||
iterator getopt*(): TGetoptResult =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help:
|
||||
## writeHelp()
|
||||
var p = initOptParser()
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
|
||||
{.pop.}
|
||||
|
|
@ -79,7 +79,7 @@ proc handleHexChar(c: var TSqlLexer, xi: var int) =
|
|||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
|
||||
proc handleOctChar(c: var TSqlLexer, xi: var int) =
|
||||
if c.buf[c.bufpos] in {'0'..'7'}:
|
||||
|
|
@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) =
|
|||
of '+':
|
||||
if not trailingPlusMinus and buf[pos+1] notin operators and
|
||||
tok.literal.len > 0: break
|
||||
of '*', '<', '>', '=': nil
|
||||
of '*', '<', '>', '=': discard
|
||||
else: break
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
|
|
@ -1120,7 +1120,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
|
|||
proc ra(n: PSqlNode, s: var string, indent: int) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkNone: nil
|
||||
of nkNone: discard
|
||||
of nkIdent:
|
||||
if allCharsInSet(n.strVal, {'\33'..'\127'}):
|
||||
s.add(n.strVal)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
|
|||
elif s[i] == '#': inc(i)
|
||||
while true:
|
||||
case s[i]
|
||||
of '_': nil
|
||||
of '_': discard
|
||||
of '0'..'9':
|
||||
number = number shl 4 or (ord(s[i]) - ord('0'))
|
||||
foundDigit = true
|
||||
|
|
@ -74,7 +74,7 @@ proc parseOct*(s: string, number: var int, start = 0): int {.
|
|||
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
|
||||
while true:
|
||||
case s[i]
|
||||
of '_': nil
|
||||
of '_': discard
|
||||
of '0'..'7':
|
||||
number = number shl 3 or (ord(s[i]) - ord('0'))
|
||||
foundDigit = true
|
||||
|
|
@ -189,7 +189,7 @@ proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
|
|||
|
||||
{.push overflowChecks: on.}
|
||||
# this must be compiled with overflow checking turned on:
|
||||
proc rawParseInt(s: string, b: var biggestInt, start = 0): int =
|
||||
proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
|
||||
var
|
||||
sign: BiggestInt = -1
|
||||
i = start
|
||||
|
|
@ -207,12 +207,12 @@ proc rawParseInt(s: string, b: var biggestInt, start = 0): int =
|
|||
result = i - start
|
||||
{.pop.} # overflowChecks
|
||||
|
||||
proc parseBiggestInt*(s: string, number: var biggestInt, start = 0): int {.
|
||||
proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
|
||||
rtl, extern: "npuParseBiggestInt", noSideEffect.} =
|
||||
## parses an integer starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
var res: biggestInt
|
||||
var res: BiggestInt
|
||||
# use 'res' for exception safety (don't write to 'number' in case of an
|
||||
# overflow exception:
|
||||
result = rawParseInt(s, res, start)
|
||||
|
|
@ -223,7 +223,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
|||
## parses an integer starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
var res: biggestInt
|
||||
var res: BiggestInt
|
||||
result = parseBiggestInt(s, res, start)
|
||||
if (sizeof(int) <= 4) and
|
||||
((res < low(int)) or (res > high(int))):
|
||||
|
|
@ -231,7 +231,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
|||
else:
|
||||
number = int(res)
|
||||
|
||||
proc tenToThePowerOf(b: int): biggestFloat =
|
||||
proc tenToThePowerOf(b: int): BiggestFloat =
|
||||
var b = b
|
||||
var a = 10.0
|
||||
result = 1.0
|
||||
|
|
@ -242,7 +242,7 @@ proc tenToThePowerOf(b: int): biggestFloat =
|
|||
if b == 0: break
|
||||
a *= a
|
||||
|
||||
proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int {.
|
||||
proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
|
||||
rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
|
||||
## parses a float starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there occured a parsing
|
||||
|
|
@ -319,7 +319,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
|
|||
## parses a float starting at `start` and stores the value into `number`.
|
||||
## Result is the number of processed chars or 0 if there occured a parsing
|
||||
## error.
|
||||
var bf: biggestFloat
|
||||
var bf: BiggestFloat
|
||||
result = parseBiggestFloat(s, bf, start)
|
||||
number = bf
|
||||
|
||||
|
|
@ -370,7 +370,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
|
|||
of '\0':
|
||||
raise newException(EInvalidValue,
|
||||
"Expected closing '}': " & s[i..s.len])
|
||||
else: nil
|
||||
else: discard
|
||||
inc j
|
||||
inc i, 2 # skip ${
|
||||
kind = ikExpr
|
||||
|
|
|
|||
|
|
@ -249,23 +249,23 @@ proc newLine*: TPeg {.inline.} =
|
|||
## constructs the PEG `newline`:idx: (``\n``)
|
||||
result.kind = pkNewline
|
||||
|
||||
proc UnicodeLetter*: TPeg {.inline.} =
|
||||
proc unicodeLetter*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\letter`` which matches any Unicode letter.
|
||||
result.kind = pkLetter
|
||||
|
||||
proc UnicodeLower*: TPeg {.inline.} =
|
||||
proc unicodeLower*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
|
||||
result.kind = pkLower
|
||||
|
||||
proc UnicodeUpper*: TPeg {.inline.} =
|
||||
proc unicodeUpper*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
|
||||
result.kind = pkUpper
|
||||
|
||||
proc UnicodeTitle*: TPeg {.inline.} =
|
||||
proc unicodeTitle*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\title`` which matches any Unicode title letter.
|
||||
result.kind = pkTitle
|
||||
|
||||
proc UnicodeWhitespace*: TPeg {.inline.} =
|
||||
proc unicodeWhitespace*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\white`` which matches any Unicode
|
||||
## whitespace character.
|
||||
result.kind = pkWhitespace
|
||||
|
|
@ -306,7 +306,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
|
|||
|
||||
proc spaceCost(n: TPeg): int =
|
||||
case n.kind
|
||||
of pkEmpty: nil
|
||||
of pkEmpty: discard
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
||||
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
|
||||
pkAny..pkWhitespace, pkGreedyAny:
|
||||
|
|
@ -340,28 +340,28 @@ proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
|
|||
|
||||
template letters*: expr =
|
||||
## expands to ``charset({'A'..'Z', 'a'..'z'})``
|
||||
charset({'A'..'Z', 'a'..'z'})
|
||||
charSet({'A'..'Z', 'a'..'z'})
|
||||
|
||||
template digits*: expr =
|
||||
## expands to ``charset({'0'..'9'})``
|
||||
charset({'0'..'9'})
|
||||
charSet({'0'..'9'})
|
||||
|
||||
template whitespace*: expr =
|
||||
## expands to ``charset({' ', '\9'..'\13'})``
|
||||
charset({' ', '\9'..'\13'})
|
||||
charSet({' ', '\9'..'\13'})
|
||||
|
||||
template identChars*: expr =
|
||||
## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
|
||||
charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})
|
||||
charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
|
||||
|
||||
template identStartChars*: expr =
|
||||
## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
|
||||
charset({'a'..'z', 'A'..'Z', '_'})
|
||||
charSet({'a'..'z', 'A'..'Z', '_'})
|
||||
|
||||
template ident*: expr =
|
||||
## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
|
||||
sequence(charset({'a'..'z', 'A'..'Z', '_'}),
|
||||
*charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
|
||||
sequence(charSet({'a'..'z', 'A'..'Z', '_'}),
|
||||
*charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
|
||||
|
||||
template natural*: expr =
|
||||
## same as ``\d+``
|
||||
|
|
@ -385,7 +385,7 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string =
|
|||
elif c in reserved: result = '\\' & c
|
||||
else: result = $c
|
||||
|
||||
proc singleQuoteEsc(c: Char): string = return "'" & esc(c, {'\''}) & "'"
|
||||
proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
|
||||
|
||||
proc singleQuoteEsc(str: string): string =
|
||||
result = "'"
|
||||
|
|
@ -409,11 +409,11 @@ proc charSetEscAux(cc: set[char]): string =
|
|||
c1 = c2
|
||||
inc(c1)
|
||||
|
||||
proc CharSetEsc(cc: set[char]): string =
|
||||
proc charSetEsc(cc: set[char]): string =
|
||||
if card(cc) >= 128+64:
|
||||
result = "[^" & CharSetEscAux({'\1'..'\xFF'} - cc) & ']'
|
||||
result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
|
||||
else:
|
||||
result = '[' & CharSetEscAux(cc) & ']'
|
||||
result = '[' & charSetEscAux(cc) & ']'
|
||||
|
||||
proc toStrAux(r: TPeg, res: var string) =
|
||||
case r.kind
|
||||
|
|
@ -590,7 +590,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
|||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isWhitespace(a): dec(result, start)
|
||||
if isWhiteSpace(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
|
|
@ -747,7 +747,7 @@ template fillMatches(s, caps, c: expr) =
|
|||
for k in 0..c.ml-1:
|
||||
caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
|
||||
|
||||
proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
proc match*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||
## the captured substrings in the array ``matches``. If it does not
|
||||
|
|
@ -765,7 +765,7 @@ proc match*(s: string, pattern: TPeg,
|
|||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c) == len(s)-start
|
||||
|
||||
proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## the same as ``match``, but it returns the length of the match,
|
||||
## if there is no match, -1 is returned. Note that a match length
|
||||
|
|
@ -786,7 +786,7 @@ proc matchLen*(s: string, pattern: TPeg,
|
|||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c)
|
||||
|
||||
proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
proc find*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns the starting position of ``pattern`` in ``s`` and the captured
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
|
|
@ -801,7 +801,7 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
|||
return -1
|
||||
# could also use the pattern here: (!P .)* P
|
||||
|
||||
proc findBounds*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
start = 0): tuple[first, last: int] {.
|
||||
nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns the starting position and end position of ``pattern`` in ``s``
|
||||
|
|
@ -869,7 +869,7 @@ template `=~`*(s: string, pattern: TPeg): bool =
|
|||
##
|
||||
bind maxSubpatterns
|
||||
when not definedInScope(matches):
|
||||
var matches {.inject.}: array[0..maxSubpatterns-1, string]
|
||||
var matches {.inject.}: array[0..MaxSubpatterns-1, string]
|
||||
match(s, pattern, matches)
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
|
@ -1074,14 +1074,14 @@ const
|
|||
"@", "built-in", "escaped", "$", "$", "^"
|
||||
]
|
||||
|
||||
proc HandleCR(L: var TPegLexer, pos: int): int =
|
||||
proc handleCR(L: var TPegLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\c')
|
||||
inc(L.linenumber)
|
||||
result = pos+1
|
||||
if L.buf[result] == '\L': inc(result)
|
||||
L.lineStart = result
|
||||
|
||||
proc HandleLF(L: var TPegLexer, pos: int): int =
|
||||
proc handleLF(L: var TPegLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\L')
|
||||
inc(L.linenumber)
|
||||
result = pos+1
|
||||
|
|
@ -1117,45 +1117,45 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
|
|||
of 'A'..'F':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
|
||||
inc(c.bufpos)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
||||
inc(c.bufpos)
|
||||
case c.buf[c.bufpos]
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'l', 'L':
|
||||
add(tok.literal, '\L')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'f', 'F':
|
||||
add(tok.literal, '\f')
|
||||
inc(c.bufpos)
|
||||
of 'e', 'E':
|
||||
add(tok.literal, '\e')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'a', 'A':
|
||||
add(tok.literal, '\a')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'b', 'B':
|
||||
add(tok.literal, '\b')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'v', 'V':
|
||||
add(tok.literal, '\v')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 't', 'T':
|
||||
add(tok.literal, '\t')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
of 'x', 'X':
|
||||
inc(c.bufpos)
|
||||
var xi = 0
|
||||
handleHexChar(c, xi)
|
||||
handleHexChar(c, xi)
|
||||
if xi == 0: tok.kind = tkInvalid
|
||||
else: add(tok.literal, Chr(xi))
|
||||
else: add(tok.literal, chr(xi))
|
||||
of '0'..'9':
|
||||
var val = ord(c.buf[c.bufpos]) - ord('0')
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
var i = 1
|
||||
while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
|
||||
val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
|
||||
|
|
@ -1169,7 +1169,7 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
|||
tok.kind = tkInvalid
|
||||
else:
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
|
||||
proc skip(c: var TPegLexer) =
|
||||
var pos = c.bufpos
|
||||
|
|
@ -1177,14 +1177,14 @@ proc skip(c: var TPegLexer) =
|
|||
while true:
|
||||
case buf[pos]
|
||||
of ' ', '\t':
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
of '#':
|
||||
while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
|
||||
of '\c':
|
||||
pos = HandleCR(c, pos)
|
||||
pos = handleCR(c, pos)
|
||||
buf = c.buf
|
||||
of '\L':
|
||||
pos = HandleLF(c, pos)
|
||||
pos = handleLF(c, pos)
|
||||
buf = c.buf
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
|
|
@ -1209,7 +1209,7 @@ proc getString(c: var TPegLexer, tok: var TToken) =
|
|||
break
|
||||
else:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getDollar(c: var TPegLexer, tok: var TToken) =
|
||||
|
|
@ -1250,7 +1250,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
|||
break
|
||||
else:
|
||||
ch = buf[pos]
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
incl(tok.charset, ch)
|
||||
if buf[pos] == '-':
|
||||
if buf[pos+1] == ']':
|
||||
|
|
@ -1270,7 +1270,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
|||
break
|
||||
else:
|
||||
ch2 = buf[pos]
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
for i in ord(ch)+1 .. ord(ch2):
|
||||
incl(tok.charset, chr(i))
|
||||
c.bufpos = pos
|
||||
|
|
@ -1281,7 +1281,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) =
|
|||
var buf = c.buf
|
||||
while true:
|
||||
add(tok.literal, buf[pos])
|
||||
Inc(pos)
|
||||
inc(pos)
|
||||
if buf[pos] notin strutils.IdentChars: break
|
||||
c.bufpos = pos
|
||||
tok.kind = tkIdentifier
|
||||
|
|
@ -1298,7 +1298,7 @@ proc getBuiltin(c: var TPegLexer, tok: var TToken) =
|
|||
proc getTok(c: var TPegLexer, tok: var TToken) =
|
||||
tok.kind = tkInvalid
|
||||
tok.modifier = modNone
|
||||
setlen(tok.literal, 0)
|
||||
setLen(tok.literal, 0)
|
||||
skip(c)
|
||||
case c.buf[c.bufpos]
|
||||
of '{':
|
||||
|
|
@ -1315,14 +1315,14 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
|
|||
inc(c.bufpos)
|
||||
add(tok.literal, '}')
|
||||
of '[':
|
||||
getCharset(c, tok)
|
||||
getCharSet(c, tok)
|
||||
of '(':
|
||||
tok.kind = tkParLe
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, '(')
|
||||
of ')':
|
||||
tok.kind = tkParRi
|
||||
Inc(c.bufpos)
|
||||
inc(c.bufpos)
|
||||
add(tok.literal, ')')
|
||||
of '.':
|
||||
tok.kind = tkAny
|
||||
|
|
@ -1347,7 +1347,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
|
|||
of "i": tok.modifier = modIgnoreCase
|
||||
of "y": tok.modifier = modIgnoreStyle
|
||||
of "v": tok.modifier = modVerbatim
|
||||
else: nil
|
||||
else: discard
|
||||
setLen(tok.literal, 0)
|
||||
if c.buf[c.bufpos] == '$':
|
||||
getDollar(c, tok)
|
||||
|
|
@ -1452,28 +1452,28 @@ proc modifiedTerm(s: string, m: TModifier): TPeg =
|
|||
|
||||
proc modifiedBackref(s: int, m: TModifier): TPeg =
|
||||
case m
|
||||
of modNone, modVerbatim: result = backRef(s)
|
||||
of modIgnoreCase: result = backRefIgnoreCase(s)
|
||||
of modIgnoreStyle: result = backRefIgnoreStyle(s)
|
||||
of modNone, modVerbatim: result = backref(s)
|
||||
of modIgnoreCase: result = backrefIgnoreCase(s)
|
||||
of modIgnoreStyle: result = backrefIgnoreStyle(s)
|
||||
|
||||
proc builtin(p: var TPegParser): TPeg =
|
||||
# do not use "y", "skip" or "i" as these would be ambiguous
|
||||
case p.tok.literal
|
||||
of "n": result = newLine()
|
||||
of "d": result = charset({'0'..'9'})
|
||||
of "D": result = charset({'\1'..'\xff'} - {'0'..'9'})
|
||||
of "s": result = charset({' ', '\9'..'\13'})
|
||||
of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
|
||||
of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
|
||||
of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
|
||||
of "a": result = charset({'a'..'z', 'A'..'Z'})
|
||||
of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
|
||||
of "d": result = charSet({'0'..'9'})
|
||||
of "D": result = charSet({'\1'..'\xff'} - {'0'..'9'})
|
||||
of "s": result = charSet({' ', '\9'..'\13'})
|
||||
of "S": result = charSet({'\1'..'\xff'} - {' ', '\9'..'\13'})
|
||||
of "w": result = charSet({'a'..'z', 'A'..'Z', '_', '0'..'9'})
|
||||
of "W": result = charSet({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
|
||||
of "a": result = charSet({'a'..'z', 'A'..'Z'})
|
||||
of "A": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
|
||||
of "ident": result = pegs.ident
|
||||
of "letter": result = UnicodeLetter()
|
||||
of "upper": result = UnicodeUpper()
|
||||
of "lower": result = UnicodeLower()
|
||||
of "title": result = UnicodeTitle()
|
||||
of "white": result = UnicodeWhitespace()
|
||||
of "letter": result = unicodeLetter()
|
||||
of "upper": result = unicodeUpper()
|
||||
of "lower": result = unicodeLower()
|
||||
of "title": result = unicodeTitle()
|
||||
of "white": result = unicodeWhitespace()
|
||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||
|
||||
proc token(terminal: TPeg, p: TPegParser): TPeg =
|
||||
|
|
@ -1494,7 +1494,7 @@ proc primary(p: var TPegParser): TPeg =
|
|||
of tkCurlyAt:
|
||||
getTok(p)
|
||||
return !*\primary(p).token(p)
|
||||
else: nil
|
||||
else: discard
|
||||
case p.tok.kind
|
||||
of tkIdentifier:
|
||||
if p.identIsVerbatim:
|
||||
|
|
@ -1505,7 +1505,7 @@ proc primary(p: var TPegParser): TPeg =
|
|||
elif not arrowIsNextTok(p):
|
||||
var nt = getNonTerminal(p, p.tok.literal)
|
||||
incl(nt.flags, ntUsed)
|
||||
result = nonTerminal(nt).token(p)
|
||||
result = nonterminal(nt).token(p)
|
||||
getTok(p)
|
||||
else:
|
||||
pegError(p, "expression expected, but found: " & p.tok.literal)
|
||||
|
|
@ -1517,7 +1517,7 @@ proc primary(p: var TPegParser): TPeg =
|
|||
of tkCharSet:
|
||||
if '\0' in p.tok.charset:
|
||||
pegError(p, "binary zero ('\\0') not allowed in character class")
|
||||
result = charset(p.tok.charset).token(p)
|
||||
result = charSet(p.tok.charset).token(p)
|
||||
getTok(p)
|
||||
of tkParLe:
|
||||
getTok(p)
|
||||
|
|
@ -1549,7 +1549,7 @@ proc primary(p: var TPegParser): TPeg =
|
|||
of tkBackref:
|
||||
var m = p.tok.modifier
|
||||
if m == modNone: m = p.modifier
|
||||
result = modifiedBackRef(p.tok.index, m).token(p)
|
||||
result = modifiedBackref(p.tok.index, m).token(p)
|
||||
if p.tok.index < 0 or p.tok.index > p.captures:
|
||||
pegError(p, "invalid back reference index: " & $p.tok.index)
|
||||
getTok(p)
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ proc next*(s: var TScgistate, timeout: int = -1): bool =
|
|||
## request, if ``timeout`` is `-1` then this function will never time out.
|
||||
## Returns `True` if a new request has been processed.
|
||||
var rsocks = @[s.server]
|
||||
if select(rsocks, timeout) == 1 and rsocks.len == 0:
|
||||
if select(rsocks, timeout) == 1 and rsocks.len == 1:
|
||||
new(s.client)
|
||||
accept(s.server, s.client)
|
||||
var L = 0
|
||||
|
|
|
|||
|
|
@ -66,13 +66,15 @@ proc checkReply(smtp: var TSMTP, reply: string) =
|
|||
if not line.string.startswith(reply):
|
||||
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
|
||||
const compiledWithSsl = defined(ssl)
|
||||
|
||||
proc connect*(address: string, port = 25,
|
||||
ssl = false, debug = false): TSMTP =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with EInvalidReply or with a socket error.
|
||||
result.sock = socket()
|
||||
if ssl:
|
||||
when defined(ssl):
|
||||
when compiledWithSsl:
|
||||
let ctx = newContext(verifyMode = CVerifyNone)
|
||||
ctx.wrapSocket(result.sock)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when hostos == "solaris":
|
||||
when hostOS == "solaris":
|
||||
{.passl: "-lsocket -lnsl".}
|
||||
|
||||
import os, parseutils
|
||||
|
|
@ -132,17 +132,17 @@ type
|
|||
ETimeout* = object of ESynch
|
||||
|
||||
let
|
||||
InvalidSocket*: TSocket = nil ## invalid socket
|
||||
invalidSocket*: TSocket = nil ## invalid socket
|
||||
|
||||
when defined(windows):
|
||||
let
|
||||
OSInvalidSocket = winlean.INVALID_SOCKET
|
||||
osInvalidSocket = winlean.INVALID_SOCKET
|
||||
else:
|
||||
let
|
||||
OSInvalidSocket = posix.INVALID_SOCKET
|
||||
osInvalidSocket = posix.INVALID_SOCKET
|
||||
|
||||
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
||||
if fd == OSInvalidSocket:
|
||||
if fd == osInvalidSocket:
|
||||
return nil
|
||||
new(result)
|
||||
result.fd = fd
|
||||
|
|
@ -154,9 +154,8 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
|||
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||
## ``==`` for ports.
|
||||
|
||||
proc `$`*(p: TPort): string =
|
||||
proc `$`*(p: TPort): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
result = $ze(int16(p))
|
||||
|
||||
proc ntohl*(x: int32): int32 =
|
||||
## Converts 32-bit integers from network to host byte order.
|
||||
|
|
@ -188,22 +187,22 @@ proc htons*(x: int16): int16 =
|
|||
result = sockets.ntohs(x)
|
||||
|
||||
when defined(Posix):
|
||||
proc ToInt(domain: TDomain): cint =
|
||||
proc toInt(domain: TDomain): cint =
|
||||
case domain
|
||||
of AF_UNIX: result = posix.AF_UNIX
|
||||
of AF_INET: result = posix.AF_INET
|
||||
of AF_INET6: result = posix.AF_INET6
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc ToInt(typ: TType): cint =
|
||||
proc toInt(typ: TType): cint =
|
||||
case typ
|
||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
||||
of SOCK_RAW: result = posix.SOCK_RAW
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc ToInt(p: TProtocol): cint =
|
||||
proc toInt(p: TProtocol): cint =
|
||||
case p
|
||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||
|
|
@ -211,16 +210,16 @@ when defined(Posix):
|
|||
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
||||
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
||||
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
else:
|
||||
proc toInt(domain: TDomain): cint =
|
||||
result = toU16(ord(domain))
|
||||
|
||||
proc ToInt(typ: TType): cint =
|
||||
proc toInt(typ: TType): cint =
|
||||
result = cint(ord(typ))
|
||||
|
||||
proc ToInt(p: TProtocol): cint =
|
||||
proc toInt(p: TProtocol): cint =
|
||||
result = cint(ord(p))
|
||||
|
||||
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
|
|
@ -231,7 +230,7 @@ proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
|||
when defined(Windows):
|
||||
result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
|
||||
else:
|
||||
result = newTSocket(posix.socket(ToInt(domain), ToInt(typ), ToInt(protocol)), buffered)
|
||||
result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)
|
||||
|
||||
when defined(ssl):
|
||||
CRYPTO_malloc_init()
|
||||
|
|
@ -312,7 +311,8 @@ when defined(ssl):
|
|||
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
|
||||
if newCTX == nil:
|
||||
SSLError()
|
||||
|
||||
|
||||
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
|
||||
newCTX.loadCertificates(certFile, keyFile)
|
||||
return PSSLContext(newCTX)
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ when defined(ssl):
|
|||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||
SSLError()
|
||||
|
||||
proc SocketError*(socket: TSocket, err: int = -1, async = false) =
|
||||
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
|
||||
|
|
@ -363,28 +363,28 @@ proc SocketError*(socket: TSocket, err: int = -1, async = false) =
|
|||
else: SSLError("Unknown Error")
|
||||
|
||||
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
let lastError = OSLastError()
|
||||
let lastError = osLastError()
|
||||
if async:
|
||||
when defined(windows):
|
||||
if lastError.int32 == WSAEWOULDBLOCK:
|
||||
return
|
||||
else: OSError(lastError)
|
||||
else: osError(lastError)
|
||||
else:
|
||||
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
|
||||
return
|
||||
else: OSError(lastError)
|
||||
else: OSError(lastError)
|
||||
else: osError(lastError)
|
||||
else: osError(lastError)
|
||||
|
||||
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
if listen(socket.fd, cint(backlog)) < 0'i32: OSError(OSLastError())
|
||||
if listen(socket.fd, cint(backlog)) < 0'i32: osError(osLastError())
|
||||
|
||||
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
||||
raise newException(EInvalidValue, "invalid ip4 address: " & s)
|
||||
|
||||
proc parseIp4*(s: string): biggestInt =
|
||||
proc parseIp4*(s: string): BiggestInt =
|
||||
## parses an IP version 4 in dotted decimal form like "a.b.c.d".
|
||||
##
|
||||
## This is equivalent to `inet_ntoa`:idx:.
|
||||
|
|
@ -411,14 +411,14 @@ proc parseIp4*(s: string): biggestInt =
|
|||
if j <= 0: invalidIp4(s)
|
||||
inc(i, j)
|
||||
if s[i] != '\0': invalidIp4(s)
|
||||
result = biggestInt(a shl 24 or b shl 16 or c shl 8 or d)
|
||||
result = BiggestInt(a shl 24 or b shl 16 or c shl 8 or d)
|
||||
|
||||
template gaiNim(a, p, h, list: expr): stmt =
|
||||
block:
|
||||
var gaiResult = getAddrInfo(a, $p, addr(h), list)
|
||||
var gaiResult = getaddrinfo(a, $p, addr(h), list)
|
||||
if gaiResult != 0'i32:
|
||||
when defined(windows):
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
else:
|
||||
raise newException(EOS, $gai_strerror(gaiResult))
|
||||
|
||||
|
|
@ -437,17 +437,17 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
|||
name.sin_port = sockets.htons(int16(port))
|
||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||
sizeof(name).TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
sizeof(name).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
else:
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
var hints: Taddrinfo
|
||||
var aiList: ptr Taddrinfo = nil
|
||||
hints.ai_family = toInt(AF_INET)
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
gaiNim(address, port, hints, aiList)
|
||||
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
|
||||
proc getSockName*(socket: TSocket): TPort =
|
||||
## returns the socket's associated port number.
|
||||
|
|
@ -458,40 +458,40 @@ proc getSockName*(socket: TSocket): TPort =
|
|||
name.sin_family = posix.AF_INET
|
||||
#name.sin_port = htons(cint16(port))
|
||||
#name.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||
var namelen = sizeof(name).TSockLen
|
||||
var namelen = sizeof(name).TSocklen
|
||||
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||
addr(namelen)) == -1'i32:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
result = TPort(sockets.ntohs(name.sin_port))
|
||||
|
||||
template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
sslImplementation: stmt): stmt {.immediate.} =
|
||||
assert(client != nil)
|
||||
var sockAddress: Tsockaddr_in
|
||||
var addrLen = sizeof(sockAddress).TSockLen
|
||||
var addrLen = sizeof(sockAddress).TSocklen
|
||||
var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)),
|
||||
addr(addrLen))
|
||||
|
||||
if sock == OSInvalidSocket:
|
||||
let err = OSLastError()
|
||||
if sock == osInvalidSocket:
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEINPROGRESS:
|
||||
client = InvalidSocket
|
||||
client = invalidSocket
|
||||
address = ""
|
||||
when noClientRet.int == -1:
|
||||
return
|
||||
else:
|
||||
return noClientRet
|
||||
else: OSError(err)
|
||||
else: osError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
client = InvalidSocket
|
||||
client = invalidSocket
|
||||
address = ""
|
||||
when noClientRet.int == -1:
|
||||
return
|
||||
else:
|
||||
return noClientRet
|
||||
else: OSError(err)
|
||||
else: osError(err)
|
||||
else:
|
||||
client.fd = sock
|
||||
client.isBuffered = server.isBuffered
|
||||
|
|
@ -629,7 +629,7 @@ proc accept*(server: TSocket): TSocket {.deprecated, tags: [FReadIO].} =
|
|||
proc close*(socket: TSocket) =
|
||||
## closes a socket.
|
||||
when defined(windows):
|
||||
discard winlean.closeSocket(socket.fd)
|
||||
discard winlean.closesocket(socket.fd)
|
||||
else:
|
||||
discard posix.close(socket.fd)
|
||||
# TODO: These values should not be discarded. An EOS should be raised.
|
||||
|
|
@ -670,7 +670,7 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
|||
result.port = TPort(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
|
||||
proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||
## This function will lookup the hostname of an IP Address.
|
||||
var myaddr: TInAddr
|
||||
myaddr.s_addr = inet_addr(ip)
|
||||
|
|
@ -678,43 +678,43 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
|
|||
when defined(windows):
|
||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||
cint(sockets.AF_INET))
|
||||
if s == nil: OSError(OSLastError())
|
||||
if s == nil: osError(osLastError())
|
||||
else:
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen,
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
||||
cint(posix.AF_INET))
|
||||
if s == nil:
|
||||
raise newException(EOS, $hStrError(h_errno))
|
||||
raise newException(EOS, $hstrerror(h_errno))
|
||||
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
result.addrType = TDomain(s.h_addrtype)
|
||||
result.addrtype = TDomain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
result.addrType = AF_INET
|
||||
result.addrtype = AF_INET
|
||||
elif s.h_addrtype == posix.AF_INET6:
|
||||
result.addrType = AF_INET6
|
||||
result.addrtype = AF_INET6
|
||||
else:
|
||||
raise newException(EOS, "unknown h_addrtype")
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
|
||||
proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
||||
## This function will lookup the IP address of a hostname.
|
||||
when defined(Windows):
|
||||
var s = winlean.gethostbyname(name)
|
||||
else:
|
||||
var s = posix.gethostbyname(name)
|
||||
if s == nil: OSError(OSLastError())
|
||||
if s == nil: osError(osLastError())
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
result.addrType = TDomain(s.h_addrtype)
|
||||
result.addrtype = TDomain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
result.addrType = AF_INET
|
||||
result.addrtype = AF_INET
|
||||
elif s.h_addrtype == posix.AF_INET6:
|
||||
result.addrType = AF_INET6
|
||||
result.addrtype = AF_INET6
|
||||
else:
|
||||
raise newException(EOS, "unknown h_addrtype")
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
|
|
@ -724,10 +724,10 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
|
|||
tags: [FReadIO].} =
|
||||
## getsockopt for integer options.
|
||||
var res: cint
|
||||
var size = sizeof(res).TSockLen
|
||||
var size = sizeof(res).TSocklen
|
||||
if getsockopt(socket.fd, cint(level), cint(optname),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
result = int(res)
|
||||
|
||||
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
||||
|
|
@ -735,8 +735,8 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
|||
## setsockopt for integer options.
|
||||
var value = cint(optval)
|
||||
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
||||
sizeof(value).TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
sizeof(value).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
|
||||
proc toCInt(opt: TSOBool): cint =
|
||||
case opt
|
||||
|
|
@ -752,10 +752,10 @@ proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
|
|||
tags: [FReadIO].} =
|
||||
## Retrieves option ``opt`` as a boolean value.
|
||||
var res: cint
|
||||
var size = sizeof(res).TSockLen
|
||||
var size = sizeof(res).TSocklen
|
||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
result = res != 0
|
||||
|
||||
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
|
||||
|
|
@ -763,8 +763,8 @@ proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET)
|
|||
## Sets option ``opt`` to a boolean value specified by ``value``.
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||
sizeof(valuei).TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
sizeof(valuei).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
|
||||
proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||
af: TDomain = AF_INET) {.tags: [FReadIO].} =
|
||||
|
|
@ -774,8 +774,8 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
|||
## not do it.
|
||||
##
|
||||
## If ``socket`` is an SSL socket a handshake will be automatically performed.
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
var hints: Taddrinfo
|
||||
var aiList: ptr Taddrinfo = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
|
|
@ -785,14 +785,14 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
|||
var lastError: TOSErrorCode
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32:
|
||||
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
|
||||
success = true
|
||||
break
|
||||
else: lastError = OSLastError()
|
||||
else: lastError = osLastError()
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: OSError(lastError)
|
||||
if not success: osError(lastError)
|
||||
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
|
|
@ -837,8 +837,8 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
|||
##
|
||||
## **Note**: For SSL sockets, the ``handshake`` procedure must be called
|
||||
## whenever the socket successfully connects to a server.
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
var hints: Taddrinfo
|
||||
var aiList: ptr Taddrinfo = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
|
|
@ -848,12 +848,12 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
|||
var lastError: TOSErrorCode
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen)
|
||||
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
|
||||
if ret == 0'i32:
|
||||
success = true
|
||||
break
|
||||
else:
|
||||
lastError = OSLastError()
|
||||
lastError = osLastError()
|
||||
when defined(windows):
|
||||
# Windows EINTR doesn't behave same as POSIX.
|
||||
if lastError.int32 == WSAEWOULDBLOCK:
|
||||
|
|
@ -867,7 +867,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
|||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: OSError(lastError)
|
||||
if not success: osError(lastError)
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
socket.sslNoHandshake = true
|
||||
|
|
@ -913,7 +913,7 @@ when defined(ssl):
|
|||
else:
|
||||
SSLError("Socket is not an SSL socket.")
|
||||
|
||||
proc timeValFromMilliseconds(timeout = 500): TTimeVal =
|
||||
proc timeValFromMilliseconds(timeout = 500): Ttimeval =
|
||||
if timeout != -1:
|
||||
var seconds = timeout div 1000
|
||||
result.tv_sec = seconds.int32
|
||||
|
|
@ -929,7 +929,7 @@ proc pruneSocketSet(s: var seq[TSocket], fd: var TFdSet) =
|
|||
var i = 0
|
||||
var L = s.len
|
||||
while i < L:
|
||||
if FD_ISSET(s[i].fd, fd) != 0'i32:
|
||||
if FD_ISSET(s[i].fd, fd) == 0'i32:
|
||||
s[i] = s[L-1]
|
||||
dec(L)
|
||||
else:
|
||||
|
|
@ -971,7 +971,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
|
|||
if buffersFilled > 0:
|
||||
return buffersFilled
|
||||
|
||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
var rd, wr, ex: TFdSet
|
||||
var m = 0
|
||||
|
|
@ -994,7 +994,7 @@ proc select*(readfds, writefds: var seq[TSocket],
|
|||
let buffersFilled = checkBuffer(readfds)
|
||||
if buffersFilled > 0:
|
||||
return buffersFilled
|
||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
var rd, wr: TFdSet
|
||||
var m = 0
|
||||
|
|
@ -1018,7 +1018,7 @@ proc selectWrite*(writefds: var seq[TSocket],
|
|||
##
|
||||
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
|
||||
## an unlimited time.
|
||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
var wr: TFdSet
|
||||
var m = 0
|
||||
|
|
@ -1036,7 +1036,7 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int =
|
|||
let buffersFilled = checkBuffer(readfds)
|
||||
if buffersFilled > 0:
|
||||
return buffersFilled
|
||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
var rd: TFdSet
|
||||
var m = 0
|
||||
|
|
@ -1059,7 +1059,7 @@ proc readIntoBuf(socket: TSocket, flags: int32): int =
|
|||
else:
|
||||
result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
|
||||
if result <= 0:
|
||||
socket.buflen = 0
|
||||
socket.bufLen = 0
|
||||
socket.currPos = 0
|
||||
return result
|
||||
socket.bufLen = result
|
||||
|
|
@ -1143,7 +1143,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
|
|||
var s = @[socket]
|
||||
var startTime = epochTime()
|
||||
let selRet = select(s, timeout - int(waited * 1000.0))
|
||||
if selRet < 0: OSError(OSLastError())
|
||||
if selRet < 0: osError(osLastError())
|
||||
if selRet != 1:
|
||||
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
|
||||
waited += (epochTime() - startTime)
|
||||
|
|
@ -1181,7 +1181,7 @@ proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int =
|
|||
result = recv(socket, cstring(data), size, timeout)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.SocketError(result)
|
||||
socket.socketError(result)
|
||||
data.setLen(result)
|
||||
|
||||
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||
|
|
@ -1195,7 +1195,7 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
|||
result = recv(socket, cstring(data), size)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.SocketError(async = true)
|
||||
socket.socketError(async = true)
|
||||
result = -1
|
||||
data.setLen(result)
|
||||
|
||||
|
|
@ -1259,10 +1259,10 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {.
|
|||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0: return false
|
||||
addNlIfEmpty()
|
||||
addNLIfEmpty()
|
||||
return true
|
||||
elif c == '\L':
|
||||
addNlIfEmpty()
|
||||
addNLIfEmpty()
|
||||
return true
|
||||
add(line.string, c)
|
||||
|
||||
|
|
@ -1292,18 +1292,18 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
|
|||
var c: char
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
var n = recv(socket, addr(c), 1)
|
||||
if n < 0: OSError(OSLastError())
|
||||
if n < 0: socket.socketError()
|
||||
elif n == 0: return
|
||||
if c == '\r':
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
n = peekChar(socket, c)
|
||||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0: OSError(OSLastError())
|
||||
addNlIfEmpty()
|
||||
elif n <= 0: socket.socketError()
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == '\L':
|
||||
addNlIfEmpty()
|
||||
addNLIfEmpty()
|
||||
return
|
||||
add(line.string, c)
|
||||
|
||||
|
|
@ -1353,7 +1353,7 @@ proc readLineAsync*(socket: TSocket,
|
|||
setLen(line.string, 0)
|
||||
|
||||
template errorOrNone =
|
||||
socket.SocketError(async = true)
|
||||
socket.socketError(async = true)
|
||||
return ReadNone
|
||||
|
||||
while true:
|
||||
|
|
@ -1386,7 +1386,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
|
|||
var pos = 0
|
||||
while true:
|
||||
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
|
||||
if bytesRead == -1: OSError(OSLastError())
|
||||
if bytesRead == -1: osError(osLastError())
|
||||
setLen(result.string, pos + bytesRead)
|
||||
if bytesRead != bufSize-1: break
|
||||
# increase capacity:
|
||||
|
|
@ -1455,22 +1455,22 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
|
|||
else: SSLError("Unknown Error")
|
||||
|
||||
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
let err = OSLastError()
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEWOULDBLOCK:
|
||||
return False
|
||||
else: OSError(err)
|
||||
return false
|
||||
else: osError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return False
|
||||
else: OSError(err)
|
||||
return false
|
||||
else: osError(err)
|
||||
|
||||
setLen(s.string, pos + bytesRead)
|
||||
if bytesRead != bufSize-1: break
|
||||
# increase capacity:
|
||||
setLen(s.string, s.string.len + bufSize)
|
||||
inc(pos, bytesRead)
|
||||
result = True
|
||||
result = true
|
||||
|
||||
proc recvFrom*(socket: TSocket, data: var string, length: int,
|
||||
address: var string, port: var TPort, flags = 0'i32): int {.
|
||||
|
|
@ -1489,8 +1489,8 @@ proc recvFrom*(socket: TSocket, data: var string, length: int,
|
|||
# TODO: Buffered sockets
|
||||
data.setLen(length)
|
||||
var sockAddress: Tsockaddr_in
|
||||
var addrLen = sizeof(sockAddress).TSockLen
|
||||
result = recvFrom(socket.fd, cstring(data), length.cint, flags.cint,
|
||||
var addrLen = sizeof(sockAddress).TSocklen
|
||||
result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
|
||||
cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen))
|
||||
|
||||
if result != -1:
|
||||
|
|
@ -1498,7 +1498,7 @@ proc recvFrom*(socket: TSocket, data: var string, length: int,
|
|||
address = $inet_ntoa(sockAddress.sin_addr)
|
||||
port = ntohs(sockAddress.sin_port).TPort
|
||||
|
||||
proc recvFromAsync*(socket: TSocket, data: var String, length: int,
|
||||
proc recvFromAsync*(socket: TSocket, data: var string, length: int,
|
||||
address: var string, port: var TPort,
|
||||
flags = 0'i32): bool {.tags: [FReadIO].} =
|
||||
## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
|
||||
|
|
@ -1508,15 +1508,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
|
|||
result = true
|
||||
var callRes = recvFrom(socket, data, length, address, port, flags)
|
||||
if callRes < 0:
|
||||
let err = OSLastError()
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEWOULDBLOCK:
|
||||
return False
|
||||
else: OSError(err)
|
||||
return false
|
||||
else: osError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return False
|
||||
else: OSError(err)
|
||||
return false
|
||||
else: osError(err)
|
||||
|
||||
proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
|
||||
## skips all the data that is pending for the socket
|
||||
|
|
@ -1524,7 +1524,7 @@ proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
|
|||
## **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
|
||||
while recv(socket, buf, bufSize) == bufSize: discard
|
||||
dealloc(buf)
|
||||
|
||||
proc skip*(socket: TSocket, size: int, timeout = -1) =
|
||||
|
|
@ -1566,7 +1566,7 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
|
|||
if socket.isSSL:
|
||||
SSLError()
|
||||
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
|
||||
if sent != data.len:
|
||||
raise newException(EOS, "Could not send all data.")
|
||||
|
|
@ -1599,15 +1599,15 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
|
|||
else:
|
||||
return
|
||||
if result == -1:
|
||||
let err = OSLastError()
|
||||
let err = osLastError()
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEINPROGRESS:
|
||||
return 0
|
||||
else: OSError(err)
|
||||
else: osError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return 0
|
||||
else: OSError(err)
|
||||
else: osError(err)
|
||||
|
||||
|
||||
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
|
||||
|
|
@ -1623,8 +1623,8 @@ proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer,
|
|||
## this function will try each IP of that hostname.
|
||||
##
|
||||
## **Note:** This proc is not available for SSL sockets.
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
var hints: Taddrinfo
|
||||
var aiList: ptr Taddrinfo = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
|
|
@ -1634,8 +1634,8 @@ proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer,
|
|||
var success = false
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
result = sendTo(socket.fd, data, size.cint, flags.cint, it.ai_addr,
|
||||
it.ai_addrlen.TSockLen)
|
||||
result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
|
||||
it.ai_addrlen.TSocklen)
|
||||
if result != -1'i32:
|
||||
success = true
|
||||
break
|
||||
|
|
@ -1662,16 +1662,16 @@ 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 ioctlsocket(TSocketHandle(s.fd), FIONBIO, addr(mode)) == -1:
|
||||
OSError(OSLastError())
|
||||
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
|
||||
osError(osLastError())
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(s.fd, F_GETFL, 0)
|
||||
if x == -1:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
else:
|
||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||
if fcntl(s.fd, F_SETFL, mode) == -1:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
s.nonblocking = not blocking
|
||||
|
||||
discard """ proc setReuseAddr*(s: TSocket) =
|
||||
|
|
@ -1710,7 +1710,7 @@ proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
|
|||
## Determines whether ``socket`` is blocking.
|
||||
|
||||
when defined(Windows):
|
||||
var wsa: TWSADATA
|
||||
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
||||
var wsa: TWSAData
|
||||
if wsaStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ proc fsClose(s: PStream) =
|
|||
close(PFileStream(s).f)
|
||||
PFileStream(s).f = nil
|
||||
proc fsFlush(s: PStream) = flushFile(PFileStream(s).f)
|
||||
proc fsAtEnd(s: PStream): bool = return EndOfFile(PFileStream(s).f)
|
||||
proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f)
|
||||
proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos)
|
||||
proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f))
|
||||
|
||||
|
|
@ -277,11 +277,11 @@ proc newFileStream*(filename: string, mode: TFileMode): PFileStream =
|
|||
## 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)
|
||||
if open(f, filename, mode): result = newFileStream(f)
|
||||
|
||||
|
||||
when true:
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
type
|
||||
TFileHandle* = cint ## Operating system file handle
|
||||
|
|
|
|||
|
|
@ -88,10 +88,10 @@ proc mustRehash(length, counter: int): bool =
|
|||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc RawGet(t: PStringTable, key: string): int =
|
||||
proc rawGet(t: PStringTable, key: string): int =
|
||||
var h: THash = myhash(t, key) and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if mycmp(t, t.data[h].key, key):
|
||||
if myCmp(t, t.data[h].key, key):
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
|
@ -100,7 +100,7 @@ proc `[]`*(t: PStringTable, key: string): string {.rtl, extern: "nstGet".} =
|
|||
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = ""
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ proc mget*(t: PStringTable, key: string): var string {.
|
|||
rtl, extern: "nstTake".} =
|
||||
## retrieves the location at ``t[key]``. If `key` is not in `t`, the
|
||||
## ``EInvalidKey`` exception is raised.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(EInvalidKey, "key does not exist: " & key)
|
||||
|
||||
|
|
@ -116,31 +116,31 @@ proc hasKey*(t: PStringTable, key: string): bool {.rtl, extern: "nst$1".} =
|
|||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
|
||||
proc rawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
|
||||
var h: THash = myhash(t, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc Enlarge(t: PStringTable) =
|
||||
proc enlarge(t: PStringTable) =
|
||||
var n: TKeyValuePairSeq
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
if not isNil(t.data[i].key): rawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
|
||||
proc `[]=`*(t: PStringTable, key, val: string) {.rtl, extern: "nstPut".} =
|
||||
## puts a (key, value)-pair into `t`.
|
||||
var index = RawGet(t, key)
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): Enlarge(t)
|
||||
RawInsert(t, t.data, key, val)
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc RaiseFormatException(s: string) =
|
||||
proc raiseFormatException(s: string) =
|
||||
var e: ref EInvalidValue
|
||||
new(e)
|
||||
e.msg = "format string: key not found: " & s
|
||||
|
|
@ -184,7 +184,7 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
|
|||
## var mytab = newStringTable({"key1": "val1", "key2": "val2"},
|
||||
## modeCaseInsensitive)
|
||||
result = newStringTable(mode)
|
||||
for key, val in items(keyvaluePairs): result[key] = val
|
||||
for key, val in items(keyValuePairs): result[key] = val
|
||||
|
||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {.
|
||||
rtl, extern: "nstFormat".} =
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -27,15 +27,15 @@ when defined(windows):
|
|||
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
||||
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
|
||||
proc getCursorPos(): tuple [x,y: int] =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError(OSLastError())
|
||||
var c: TCONSOLESCREENBUFFERINFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: osError(osLastError())
|
||||
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
||||
|
||||
proc getAttributes(): int16 =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var c: TCONSOLESCREENBUFFERINFO
|
||||
# workaround Windows bugs: try several times
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
|
||||
return c.wAttributes
|
||||
|
|
@ -48,10 +48,10 @@ proc setCursorPos*(x, y: int) =
|
|||
## sets the terminal's cursor to the (x,y) position. (0,0) is the
|
||||
## upper left of the screen.
|
||||
when defined(windows):
|
||||
var c: TCoord
|
||||
var c: TCOORD
|
||||
c.x = int16(x)
|
||||
c.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: OSError(OSLastError())
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $y & ';' & $x & 'f')
|
||||
|
||||
|
|
@ -59,12 +59,12 @@ proc setCursorXPos*(x: int) =
|
|||
## sets the terminal's cursor to the x position. The y position is
|
||||
## not changed.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = int16(x)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $x & 'G')
|
||||
|
||||
|
|
@ -73,16 +73,16 @@ when defined(windows):
|
|||
## sets the terminal's cursor to the y position. The x position is
|
||||
## not changed. **Warning**: This is not supported on UNIX!
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
|
||||
proc CursorUp*(count=1) =
|
||||
proc cursorUp*(count=1) =
|
||||
## Moves the cursor up by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
|
|
@ -91,7 +91,7 @@ proc CursorUp*(count=1) =
|
|||
else:
|
||||
stdout.write("\e[" & $count & 'A')
|
||||
|
||||
proc CursorDown*(count=1) =
|
||||
proc cursorDown*(count=1) =
|
||||
## Moves the cursor down by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
|
|
@ -100,7 +100,7 @@ proc CursorDown*(count=1) =
|
|||
else:
|
||||
stdout.write("\e[" & $count & 'B')
|
||||
|
||||
proc CursorForward*(count=1) =
|
||||
proc cursorForward*(count=1) =
|
||||
## Moves the cursor forward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
|
|
@ -109,7 +109,7 @@ proc CursorForward*(count=1) =
|
|||
else:
|
||||
stdout.write("\e[" & $count & 'C')
|
||||
|
||||
proc CursorBackward*(count=1) =
|
||||
proc cursorBackward*(count=1) =
|
||||
## Moves the cursor backward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
|
|
@ -119,78 +119,78 @@ proc CursorBackward*(count=1) =
|
|||
stdout.write("\e[" & $count & 'D')
|
||||
|
||||
when true:
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
proc EraseLineEnd* =
|
||||
proc eraseLineEnd* =
|
||||
## Erases from the current cursor position to the end of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
stdout.write("\e[K")
|
||||
|
||||
proc EraseLineStart* =
|
||||
proc eraseLineStart* =
|
||||
## Erases from the current cursor position to the start of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
stdout.write("\e[1K")
|
||||
|
||||
proc EraseDown* =
|
||||
proc eraseDown* =
|
||||
## Erases the screen from the current line down to the bottom of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
stdout.write("\e[J")
|
||||
|
||||
proc EraseUp* =
|
||||
proc eraseUp* =
|
||||
## Erases the screen from the current line up to the top of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
stdout.write("\e[1J")
|
||||
|
||||
proc EraseLine* =
|
||||
proc eraseLine* =
|
||||
## Erases the entire current line.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var numwrote: DWORD
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = 0'i16
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
var ht = scrbuf.dwSize.Y - origin.Y
|
||||
var wt = scrbuf.dwSize.X - origin.X
|
||||
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
||||
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[2K")
|
||||
setCursorXPos(0)
|
||||
|
||||
proc EraseScreen* =
|
||||
proc eraseScreen* =
|
||||
## Erases the screen with the background colour and moves the cursor to home.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var numwrote: DWORD
|
||||
var origin: TCoord # is inititalized to 0, 0
|
||||
var origin: TCOORD # is inititalized to 0, 0
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
||||
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
osError(osLastError())
|
||||
setCursorXPos(0)
|
||||
else:
|
||||
stdout.write("\e[2J")
|
||||
|
||||
proc ResetAttributes* {.noconv.} =
|
||||
proc resetAttributes* {.noconv.} =
|
||||
## resets all attributes; it is advisable to register this as a quit proc
|
||||
## with ``system.addQuitProc(resetAttributes)``.
|
||||
when defined(windows):
|
||||
|
|
@ -227,7 +227,7 @@ proc setStyle*(style: set[TStyle]) =
|
|||
for s in items(style):
|
||||
stdout.write("\e[" & $ord(s) & 'm')
|
||||
|
||||
proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||
proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||
## writes the text `txt` in a given `style`.
|
||||
when defined(windows):
|
||||
var old = getAttributes()
|
||||
|
|
@ -320,8 +320,8 @@ proc isatty*(f: TFile): bool =
|
|||
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
|
||||
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
|
||||
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
|
||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
|
||||
proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color
|
||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color
|
||||
|
||||
macro styledEcho*(m: varargs[expr]): stmt =
|
||||
## to be documented.
|
||||
|
|
@ -345,4 +345,4 @@ when isMainModule:
|
|||
writeln(stdout, "ordinary text")
|
||||
|
||||
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [].}
|
|||
## converts the calendar time `t` to broken-down time representation,
|
||||
## expressed in Coordinated Universal Time (UTC).
|
||||
|
||||
proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [].}
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [].}
|
||||
## converts a broken-down time structure to
|
||||
## calendar time representation. The function ignores the specified
|
||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||
|
|
@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
|
|||
result.months = months
|
||||
result.years = years
|
||||
|
||||
proc isLeapYear(year: int): bool =
|
||||
proc isLeapYear*(year: int): bool =
|
||||
## returns true if ``year`` is a leap year
|
||||
|
||||
if year mod 400 == 0:
|
||||
return true
|
||||
elif year mod 100 == 0:
|
||||
|
|
@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
|
|||
else:
|
||||
return false
|
||||
|
||||
proc getDaysInMonth(month: TMonth, year: int): int =
|
||||
proc getDaysInMonth*(month: TMonth, year: int): int =
|
||||
## gets the amount of days in a ``month`` of a ``year``
|
||||
|
||||
# http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
|
||||
case month
|
||||
of mFeb: result = if isLeapYear(year): 29 else: 28
|
||||
|
|
@ -246,7 +250,8 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
|
|||
else:
|
||||
curMonth.inc()
|
||||
result += float(newinterv.days * 24 * 60 * 60)
|
||||
result += float(newinterv.minutes * 60 * 60)
|
||||
result += float(newinterv.hours * 60 * 60)
|
||||
result += float(newinterv.minutes * 60)
|
||||
result += float(newinterv.seconds)
|
||||
result += newinterv.miliseconds / 1000
|
||||
|
||||
|
|
@ -255,7 +260,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
|||
##
|
||||
## **Note:** This has been only briefly tested and it may not be
|
||||
## very accurate.
|
||||
let t = toSeconds(TimeInfoToTime(a))
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
if a.tzname == "UTC":
|
||||
result = getGMTime(fromSeconds(t + secs))
|
||||
|
|
@ -267,7 +272,7 @@ proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
|||
##
|
||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||
## when you subtract so much that you reach the Julian calendar.
|
||||
let t = toSeconds(TimeInfoToTime(a))
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
if a.tzname == "UTC":
|
||||
result = getGMTime(fromSeconds(t - secs))
|
||||
|
|
@ -296,7 +301,7 @@ when not defined(JS):
|
|||
when not defined(JS):
|
||||
# C wrapper:
|
||||
type
|
||||
structTM {.importc: "struct tm", final.} = object
|
||||
StructTM {.importc: "struct tm", final.} = object
|
||||
second {.importc: "tm_sec".},
|
||||
minute {.importc: "tm_min".},
|
||||
hour {.importc: "tm_hour".},
|
||||
|
|
@ -307,7 +312,7 @@ when not defined(JS):
|
|||
yearday {.importc: "tm_yday".},
|
||||
isdst {.importc: "tm_isdst".}: cint
|
||||
|
||||
PTimeInfo = ptr structTM
|
||||
PTimeInfo = ptr StructTM
|
||||
PTime = ptr TTime
|
||||
|
||||
TClock {.importc: "clock_t".} = distinct int
|
||||
|
|
@ -318,11 +323,11 @@ when not defined(JS):
|
|||
importc: "gmtime", header: "<time.h>", tags: [].}
|
||||
proc timec(timer: PTime): TTime {.
|
||||
importc: "time", header: "<time.h>", tags: [].}
|
||||
proc mktime(t: structTM): TTime {.
|
||||
proc mktime(t: StructTM): TTime {.
|
||||
importc: "mktime", header: "<time.h>", tags: [].}
|
||||
proc asctime(tblock: structTM): CString {.
|
||||
proc asctime(tblock: StructTM): cstring {.
|
||||
importc: "asctime", header: "<time.h>", tags: [].}
|
||||
proc ctime(time: PTime): CString {.
|
||||
proc ctime(time: PTime): cstring {.
|
||||
importc: "ctime", header: "<time.h>", tags: [].}
|
||||
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
|
||||
# importc: "strftime", header: "<time.h>".}
|
||||
|
|
@ -334,7 +339,7 @@ when not defined(JS):
|
|||
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
|
||||
|
||||
# our own procs on top of that:
|
||||
proc tmToTimeInfo(tm: structTM, local: bool): TTimeInfo =
|
||||
proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo =
|
||||
const
|
||||
weekDays: array [0..6, TWeekDay] = [
|
||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||
|
|
@ -344,11 +349,11 @@ when not defined(JS):
|
|||
monthday: int(tm.monthday),
|
||||
month: TMonth(tm.month),
|
||||
year: tm.year + 1900'i32,
|
||||
weekday: weekDays[int(tm.weekDay)],
|
||||
weekday: weekDays[int(tm.weekday)],
|
||||
yearday: int(tm.yearday),
|
||||
isDST: tm.isDST > 0,
|
||||
isDST: tm.isdst > 0,
|
||||
tzname: if local:
|
||||
if tm.isDST > 0:
|
||||
if tm.isdst > 0:
|
||||
getTzname().DST
|
||||
else:
|
||||
getTzname().nonDST
|
||||
|
|
@ -357,7 +362,7 @@ when not defined(JS):
|
|||
timezone: if local: getTimezone() else: 0
|
||||
)
|
||||
|
||||
proc timeInfoToTM(t: TTimeInfo): structTM =
|
||||
proc timeInfoToTM(t: TTimeInfo): StructTM =
|
||||
const
|
||||
weekDays: array [TWeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
|
||||
result.second = t.second
|
||||
|
|
@ -366,7 +371,7 @@ when not defined(JS):
|
|||
result.monthday = t.monthday
|
||||
result.month = ord(t.month)
|
||||
result.year = t.year - 1900
|
||||
result.weekday = weekDays[t.weekDay]
|
||||
result.weekday = weekDays[t.weekday]
|
||||
result.yearday = t.yearday
|
||||
result.isdst = if t.isDST: 1 else: 0
|
||||
|
||||
|
|
@ -400,7 +405,7 @@ when not defined(JS):
|
|||
# copying is needed anyway to provide reentrancity; thus
|
||||
# the conversion is not expensive
|
||||
|
||||
proc TimeInfoToTime(timeInfo: TTimeInfo): TTime =
|
||||
proc timeInfoToTime(timeInfo: TTimeInfo): TTime =
|
||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
||||
# because the header of mktime is broken in my version of libc
|
||||
return mktime(timeInfoToTM(cTimeInfo))
|
||||
|
|
@ -452,7 +457,7 @@ when not defined(JS):
|
|||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
|
||||
elif defined(windows):
|
||||
var f: winlean.TFiletime
|
||||
GetSystemTimeAsFileTime(f)
|
||||
getSystemTimeAsFileTime(f)
|
||||
var i64 = rdFileTime(f) - epochDiff
|
||||
var secs = i64 div rateDiff
|
||||
var subsecs = i64 mod rateDiff
|
||||
|
|
@ -497,7 +502,7 @@ elif defined(JS):
|
|||
result.weekday = weekDays[t.getUTCDay()]
|
||||
result.yearday = 0
|
||||
|
||||
proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime =
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime =
|
||||
result = internGetTime()
|
||||
result.setSeconds(timeInfo.second)
|
||||
result.setMinutes(timeInfo.minute)
|
||||
|
|
@ -531,7 +536,7 @@ proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} =
|
|||
## gets the current date as a string of the format ``YYYY-MM-DD``.
|
||||
var ti = getLocalTime(getTime())
|
||||
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
|
||||
'-' & intToStr(ti.monthDay, 2)
|
||||
'-' & intToStr(ti.monthday, 2)
|
||||
|
||||
proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} =
|
||||
## gets the current clock time as a string of the format ``HH:MM:SS``.
|
||||
|
|
@ -592,7 +597,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
|||
result = ""
|
||||
var i = 0
|
||||
var currentF = ""
|
||||
while True:
|
||||
while true:
|
||||
case f[i]
|
||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||
case currentF
|
||||
|
|
@ -697,7 +702,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
|||
of "ZZZ":
|
||||
result.add(info.tzname)
|
||||
of "":
|
||||
nil # Do nothing.
|
||||
discard
|
||||
else:
|
||||
raise newException(EInvalidValue, "Invalid format string: " & currentF)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
irune = int # underlying type of TRune
|
||||
TRune* = distinct irune ## type that can hold any Unicode character
|
||||
IRune = int # underlying type of TRune
|
||||
TRune* = distinct IRune ## type that can hold any Unicode character
|
||||
TRune16* = distinct int16 ## 16 bit Unicode character
|
||||
|
||||
proc `<=%`*(a, b: TRune): bool = return int(a) <=% int(b)
|
||||
|
|
@ -55,31 +55,31 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
|||
result = TRune(ord(s[i]))
|
||||
when doInc: inc(i)
|
||||
elif ord(s[i]) shr 5 == 0b110:
|
||||
assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and (ones(5))) shl 6 or
|
||||
(ord(s[i+1]) and ones(6)))
|
||||
when doInc: inc(i, 2)
|
||||
elif ord(s[i]) shr 4 == 0b1110:
|
||||
assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(4)) shl 12 or
|
||||
(ord(s[i+1]) and ones(6)) shl 6 or
|
||||
(ord(s[i+2]) and ones(6)))
|
||||
when doInc: inc(i, 3)
|
||||
elif ord(s[i]) shr 3 == 0b11110:
|
||||
assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(3)) shl 18 or
|
||||
(ord(s[i+1]) and ones(6)) shl 12 or
|
||||
(ord(s[i+2]) and ones(6)) shl 6 or
|
||||
(ord(s[i+3]) and ones(6)))
|
||||
when doInc: inc(i, 4)
|
||||
elif ord(s[i]) shr 2 == 0b111110:
|
||||
assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(2)) shl 24 or
|
||||
(ord(s[i+1]) and ones(6)) shl 18 or
|
||||
(ord(s[i+2]) and ones(6)) shl 12 or
|
||||
|
|
@ -87,11 +87,11 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
|||
(ord(s[i+4]) and ones(6)))
|
||||
when doInc: inc(i, 5)
|
||||
elif ord(s[i]) shr 1 == 0b1111110:
|
||||
assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
assert(ord(s[i+5]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+5]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(1)) shl 30 or
|
||||
(ord(s[i+1]) and ones(6)) shl 24 or
|
||||
(ord(s[i+2]) and ones(6)) shl 18 or
|
||||
|
|
@ -109,7 +109,7 @@ proc runeAt*(s: string, i: int): TRune =
|
|||
|
||||
proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||
## converts a rune into its UTF8 representation
|
||||
var i = irune(c)
|
||||
var i = IRune(c)
|
||||
if i <=% 127:
|
||||
result = newString(1)
|
||||
result[0] = chr(i)
|
||||
|
|
@ -132,6 +132,10 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
|||
result = newString(1)
|
||||
result[0] = chr(i)
|
||||
|
||||
proc `$`*(rune: TRune): string =
|
||||
## converts a rune to a string
|
||||
rune.toUTF8
|
||||
|
||||
proc `$`*(runes: seq[TRune]): string =
|
||||
## converts a sequence of runes to a string
|
||||
result = ""
|
||||
|
|
@ -1096,7 +1100,7 @@ const
|
|||
0x01f1, 501, #
|
||||
0x01f3, 499] #
|
||||
|
||||
proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
|
||||
proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
|
||||
var n = len
|
||||
var t = 0
|
||||
while n > 1:
|
||||
|
|
@ -1114,29 +1118,29 @@ proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
|
|||
proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
## Converts `c` into lower case. This works for any Unicode character.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = irune(c)
|
||||
var p = binarySearch(c, tolowerRanges, len(toLowerRanges) div 3, 3)
|
||||
var c = IRune(c)
|
||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||
return TRune(c + tolowerRanges[p+2] - 500)
|
||||
p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
|
||||
if p >= 0 and c == toLowerSinglets[p]:
|
||||
return TRune(c + toLowerSinglets[p+1] - 500)
|
||||
p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
|
||||
if p >= 0 and c == tolowerSinglets[p]:
|
||||
return TRune(c + tolowerSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
|
||||
proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
## Converts `c` into upper case. This works for any Unicode character.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = irune(c)
|
||||
var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
|
||||
return TRune(c + toUpperRanges[p+2] - 500)
|
||||
p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
|
||||
if p >= 0 and c == toUpperSinglets[p]:
|
||||
return TRune(c + toUpperSinglets[p+1] - 500)
|
||||
var c = IRune(c)
|
||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||
return TRune(c + toupperRanges[p+2] - 500)
|
||||
p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
|
||||
if p >= 0 and c == toupperSinglets[p]:
|
||||
return TRune(c + toupperSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
|
||||
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
var c = irune(c)
|
||||
var c = IRune(c)
|
||||
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
||||
if p >= 0 and c == toTitleSinglets[p]:
|
||||
return TRune(c + toTitleSinglets[p+1] - 500)
|
||||
|
|
@ -1145,32 +1149,32 @@ proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
|||
proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a lower case Unicode character
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = irune(c)
|
||||
var c = IRune(c)
|
||||
# Note: toUpperRanges is correct here!
|
||||
var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
|
||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||
return true
|
||||
p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
|
||||
if p >= 0 and c == toUpperSinglets[p]:
|
||||
p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
|
||||
if p >= 0 and c == toupperSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a upper case Unicode character
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = irune(c)
|
||||
var c = IRune(c)
|
||||
# Note: toLowerRanges is correct here!
|
||||
var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3)
|
||||
if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]:
|
||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||
return true
|
||||
p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
|
||||
if p >= 0 and c == toLowerSinglets[p]:
|
||||
p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
|
||||
if p >= 0 and c == tolowerSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
||||
if isUpper(c) or isLower(c):
|
||||
return true
|
||||
var c = irune(c)
|
||||
var c = IRune(c)
|
||||
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
|
||||
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
|
||||
return true
|
||||
|
|
@ -1183,7 +1187,7 @@ proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
|||
|
||||
proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a Unicode whitespace character
|
||||
var c = irune(c)
|
||||
var c = IRune(c)
|
||||
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
||||
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
|
||||
return true
|
||||
|
|
@ -1210,7 +1214,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
|
|||
# slow path:
|
||||
fastRuneAt(a, i, ar)
|
||||
fastRuneAt(b, j, br)
|
||||
result = irune(toLower(ar)) - irune(toLower(br))
|
||||
result = IRune(toLower(ar)) - IRune(toLower(br))
|
||||
if result != 0: return
|
||||
result = a.len - b.len
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ var
|
|||
|
||||
checkpoints: seq[string] = @[]
|
||||
|
||||
template TestSetupIMPL*: stmt {.immediate, dirty.} = nil
|
||||
template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
|
||||
template TestSetupIMPL*: stmt {.immediate, dirty.} = discard
|
||||
template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard
|
||||
|
||||
proc shouldRun(testName: string): bool =
|
||||
result = true
|
||||
|
|
@ -98,8 +98,12 @@ template fail* =
|
|||
|
||||
when not defined(ECMAScript):
|
||||
if AbortOnError: quit(1)
|
||||
|
||||
TestStatusIMPL = FAILED
|
||||
|
||||
when defined(TestStatusIMPL):
|
||||
TestStatusIMPL = FAILED
|
||||
else:
|
||||
program_result += 1
|
||||
|
||||
checkpoints = @[]
|
||||
|
||||
macro check*(conditions: stmt): stmt {.immediate.} =
|
||||
|
|
@ -111,7 +115,8 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
|||
counter = 0
|
||||
|
||||
template asgn(a, value: expr): stmt =
|
||||
let a = value
|
||||
var a = value # XXX: we need "var: var" here in order to
|
||||
# preserve the semantics of var params
|
||||
|
||||
template print(name, value: expr): stmt =
|
||||
when compiles(string($value)):
|
||||
|
|
@ -121,7 +126,7 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
|||
for i in 1 .. <exp.len:
|
||||
if exp[i].kind notin nnkLiterals:
|
||||
inc counter
|
||||
var arg = newIdentNode(":p" & ($counter))
|
||||
var arg = newIdentNode(":p" & $counter)
|
||||
var argStr = exp[i].toStrLit
|
||||
if exp[i].kind in nnkCallKinds: inspectArgs(exp[i])
|
||||
argsAsgns.add getAst(asgn(arg, exp[i]))
|
||||
|
|
@ -141,12 +146,14 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
|||
|
||||
var checkedStr = checked.toStrLit
|
||||
inspectArgs(checked)
|
||||
result = getAst(rewrite(checked, checked.lineinfo, checkedStr, argsAsgns, argsPrintOuts))
|
||||
result = getAst(rewrite(checked, checked.lineinfo, checkedStr,
|
||||
argsAsgns, argsPrintOuts))
|
||||
|
||||
of nnkStmtList:
|
||||
result = newNimNode(nnkStmtList)
|
||||
for i in countup(0, checked.len - 1):
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
if checked[i].kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
|
||||
else:
|
||||
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
|
||||
|
|
@ -170,7 +177,7 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
|
|||
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
|
||||
fail()
|
||||
except errorTypes:
|
||||
nil
|
||||
discard
|
||||
|
||||
var body = exp[exp.len - 1]
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
|||
## &entity;
|
||||
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
|
||||
next(x)
|
||||
of xmlEof: nil
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseXml*(s: PStream, filename: string,
|
||||
errors: var seq[string]): PXmlNode =
|
||||
|
|
@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string,
|
|||
of xmlElementOpen, xmlElementStart:
|
||||
result = parse(x, errors)
|
||||
break
|
||||
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it
|
||||
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
|
||||
of xmlError:
|
||||
errors.add(errorMsg(x))
|
||||
else:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue