Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Araq
2017-11-26 03:25:21 +01:00
9 changed files with 154 additions and 116 deletions

View File

@@ -29,21 +29,8 @@
## writeLine(stdout, "your password: " & myData["password"])
## writeLine(stdout, "</body></html>")
import strutils, os, strtabs, cookies
proc encodeUrl*(s: string): string =
## Encodes a value to be HTTP safe: This means that characters in the set
## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
## a space is converted to ``'+'`` and every other character is encoded as
## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
for i in 0..s.len-1:
case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
of ' ': add(result, '+')
else:
add(result, '%')
add(result, toHex(ord(s[i]), 2))
import strutils, os, strtabs, cookies, uri
export uri.encodeUrl, uri.decodeUrl
proc handleHexChar(c: char, x: var int) {.inline.} =
case c
@@ -52,28 +39,6 @@ proc handleHexChar(c: char, x: var int) {.inline.} =
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: assert(false)
proc decodeUrl*(s: string): string =
## Decodes a value from its HTTP representation: This means that a ``'+'``
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
## value) is converted to the character with ordinal number ``xx``, and
## and every other character is carried over.
result = newString(s.len)
var i = 0
var j = 0
while i < s.len:
case s[i]
of '%':
var x = 0
handleHexChar(s[i+1], x)
handleHexChar(s[i+2], x)
inc(i, 2)
result[j] = chr(x)
of '+': result[j] = ' '
else: result[j] = s[i]
inc(i)
inc(j)
setLen(result, j)
proc addXmlChar(dest: var string, c: char) {.inline.} =
case c
of '&': add(dest, "&amp;")
@@ -390,8 +355,3 @@ proc existsCookie*(name: string): bool =
## Checks if a cookie of `name` exists.
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
result = hasKey(gcookies, name)
when isMainModule:
const test1 = "abc\L+def xyz"
assert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
assert decodeUrl(encodeUrl(test1)) == test1

View File

@@ -46,7 +46,7 @@ template default[T](t: typedesc[T]): T =
var v: T
v
proc clear*[A](s: var HashSet[A]) =
proc clear*[A](s: var HashSet[A]) =
## Clears the HashSet back to an empty state, without shrinking
## any of the existing storage. O(n) where n is the size of the hash bucket.
s.counter = 0
@@ -610,7 +610,7 @@ type
{.deprecated: [TOrderedSet: OrderedSet].}
proc clear*[A](s: var OrderedSet[A]) =
proc clear*[A](s: var OrderedSet[A]) =
## Clears the OrderedSet back to an empty state, without shrinking
## any of the existing storage. O(n) where n is the size of the hash bucket.
s.counter = 0
@@ -911,13 +911,13 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
## Equality for ordered sets.
if s.counter != t.counter: return false
var h = s.first
var g = s.first
var g = t.first
var compared = 0
while h >= 0 and g >= 0:
var nxh = s.data[h].next
var nxg = t.data[g].next
if isFilled(s.data[h].hcode) and isFilled(s.data[g].hcode):
if s.data[h].key == s.data[g].key:
if isFilled(s.data[h].hcode) and isFilled(t.data[g].hcode):
if s.data[h].key == t.data[g].key:
inc compared
else:
return false
@@ -1120,6 +1120,22 @@ when isMainModule and not defined(release):
assert s.missingOrExcl(4) == true
assert s.missingOrExcl(6) == false
block orderedSetEquality:
type pair = tuple[a, b: int]
var aa = initOrderedSet[pair]()
var bb = initOrderedSet[pair]()
var x = (a:1,b:2)
var y = (a:3,b:4)
aa.incl(x)
aa.incl(y)
bb.incl(x)
bb.incl(y)
assert aa == bb
when not defined(testing):
echo "Micro tests run successfully."

View File

@@ -145,7 +145,7 @@ type
SOBool* = enum ## Boolean socket options.
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
OptOOBInline, OptReuseAddr, OptReusePort
OptOOBInline, OptReuseAddr, OptReusePort, OptNoDelay
ReadLineResult* = enum ## result for readLineAsync
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
@@ -869,6 +869,11 @@ proc close*(socket: Socket) =
socket.fd.close()
when defined(posix):
from posix import TCP_NODELAY
else:
from winlean import TCP_NODELAY
proc toCInt*(opt: SOBool): cint =
## Converts a ``SOBool`` into its Socket Option cint representation.
case opt
@@ -880,6 +885,7 @@ proc toCInt*(opt: SOBool): cint =
of OptOOBInline: SO_OOBINLINE
of OptReuseAddr: SO_REUSEADDR
of OptReusePort: SO_REUSEPORT
of OptNoDelay: TCP_NODELAY
proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
tags: [ReadIOEffect].} =
@@ -902,6 +908,12 @@ proc getPeerAddr*(socket: Socket): (string, Port) =
proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
tags: [WriteIOEffect].} =
## Sets option ``opt`` to a boolean value specified by ``value``.
##
## .. code-block:: Nim
## var socket = newSocket()
## socket.setSockOpt(OptReusePort, true)
## socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
##
var valuei = cint(if value: 1 else: 0)
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)

View File

@@ -558,3 +558,68 @@ proc expandTilde*(path: string): string {.
result = getHomeDir() / path.substr(2)
else:
result = path
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".}
when isMainModule:
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("") == "''"

View File

@@ -15,6 +15,9 @@ include "system/inclrtl"
import
strutils, os, strtabs, streams, cpuinfo
from ospaths import quoteShell, quoteShellWindows, quoteShellPosix
export quoteShell, quoteShellWindows, quoteShellPosix
when defined(windows):
import winlean
else:
@@ -60,58 +63,6 @@ type
const poUseShell* {.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: StringTableRef = nil,
@@ -1291,16 +1242,3 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
result[1] = peekExitCode(p)
if result[1] != -1: break
close(p)
when isMainModule:
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("") == "''"

View File

@@ -47,6 +47,49 @@ proc add*(url: var Url, a: Url) {.deprecated.} =
url = url / a
{.pop.}
proc encodeUrl*(s: string): string =
## Encodes a value to be HTTP safe: This means that characters in the set
## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
## a space is converted to ``'+'`` and every other character is encoded as
## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
for i in 0..s.len-1:
case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
of ' ': add(result, '+')
else:
add(result, '%')
add(result, toHex(ord(s[i]), 2))
proc decodeUrl*(s: string): string =
## Decodes a value from its HTTP representation: This means that a ``'+'``
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
## value) is converted to the character with ordinal number ``xx``, and
## and every other character is carried over.
proc handleHexChar(c: char, x: var int) {.inline.} =
case c
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: assert(false)
result = newString(s.len)
var i = 0
var j = 0
while i < s.len:
case s[i]
of '%':
var x = 0
handleHexChar(s[i+1], x)
handleHexChar(s[i+2], x)
inc(i, 2)
result[j] = chr(x)
of '+': result[j] = ' '
else: result[j] = s[i]
inc(i)
inc(j)
setLen(result, j)
proc parseAuthority(authority: string, result: var Uri) =
var i = 0
var inPort = false
@@ -327,6 +370,11 @@ proc `$`*(u: Uri): string =
result.add(u.anchor)
when isMainModule:
block:
const test1 = "abc\L+def xyz"
doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
doAssert decodeUrl(encodeUrl(test1)) == test1
block:
let str = "http://localhost"
let test = parseUri(str)

View File

@@ -343,7 +343,6 @@ elif defined(gogc):
const goFlagNoZero: uint32 = 1 shl 3
proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
proc goFree(v: pointer) {.importc: "__go_free", dynlib: goLib.}
proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
@@ -376,7 +375,6 @@ elif defined(gogc):
result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
copyMem(result, old, oldsize)
zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
goFree(old)
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard

View File

@@ -541,6 +541,7 @@ var
SO_DONTLINGER* {.importc, header: "winsock2.h".}: cint
SO_EXCLUSIVEADDRUSE* {.importc, header: "winsock2.h".}: cint # disallow local address reuse
SO_ERROR* {.importc, header: "winsock2.h".}: cint
TCP_NODELAY* {.importc, header: "winsock2.h".}: cint
proc `==`*(x, y: SocketHandle): bool {.borrow.}

View File

@@ -60,7 +60,7 @@ srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet"
srcdoc2: "pure/nativesockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future"
srcdoc2: "deprecated/pure/ftpclient;pure/collections/chains"
srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/ioselectors"
srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/lenientops"
srcdoc2: "pure/md5;pure/rationals"
srcdoc2: "posix/posix;pure/distros;pure/oswalkdir"
srcdoc2: "pure/collections/heapqueue"