diff options
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/httpclient.nim | 5 | ||||
-rw-r--r-- | lib/pure/marshal.nim | 2 | ||||
-rw-r--r-- | lib/pure/math.nim | 68 | ||||
-rw-r--r-- | lib/pure/net.nim | 10 | ||||
-rw-r--r-- | lib/pure/os.nim | 18 | ||||
-rw-r--r-- | lib/pure/parsexml.nim | 4 | ||||
-rw-r--r-- | lib/pure/rawsockets.nim | 16 | ||||
-rw-r--r-- | lib/pure/securehash.nim | 199 | ||||
-rw-r--r-- | lib/pure/selectors.nim | 3 | ||||
-rw-r--r-- | lib/pure/sockets.nim | 14 | ||||
-rw-r--r-- | lib/pure/times.nim | 25 | ||||
-rw-r--r-- | lib/pure/unicode.nim | 59 | ||||
-rw-r--r-- | lib/pure/unittest.nim | 4 |
13 files changed, 345 insertions, 82 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 2ca2098b3..e6b8088c5 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -834,11 +834,12 @@ proc post*(client: AsyncHttpClient, url: string, body = "", multipart: Multipart else: x var xb = mpBody.withNewLine() & body - client.headers["Content-Type"] = mpHeader.split(": ")[1] + if multipart != nil: + client.headers["Content-Type"] = mpHeader.split(": ")[1] client.headers["Content-Length"] = $len(xb) result = await client.request(url, httpPOST, xb) - + when not defined(testing) and isMainModule: when true: # Async diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 49f049e46..173cd1e81 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -17,7 +17,7 @@ ## .. code-block:: nim ## ## type -## A = object +## A = object of RootObj ## B = object of A ## f: int ## diff --git a/lib/pure/math.nim b/lib/pure/math.nim index d3f1f3814..821ab738b 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -56,7 +56,7 @@ type fcNegInf ## value is negative infinity proc classify*(x: float): FloatClass = - ## classifies a floating point value. Returns `x`'s class as specified by + ## Classifies a floating point value. Returns `x`'s class as specified by ## `FloatClass`. # JavaScript and most C compilers have no classify: @@ -74,7 +74,7 @@ proc classify*(x: float): FloatClass = proc binom*(n, k: int): int {.noSideEffect.} = - ## computes the binomial coefficient + ## Computes the binomial coefficient if k <= 0: return 1 if 2*k > n: return binom(n, n-k) result = n @@ -82,18 +82,18 @@ proc binom*(n, k: int): int {.noSideEffect.} = result = (result * (n + 1 - i)) div i proc fac*(n: int): int {.noSideEffect.} = - ## computes the faculty/factorial function. + ## Computes the faculty/factorial function. result = 1 for i in countup(2, n): result = result * i proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = - ## returns true, if `x` is a power of two, false otherwise. + ## Returns true, if `x` is a power of two, false otherwise. ## Zero and negative numbers are not a power of two. return (x > 0) and ((x and (x - 1)) == 0) proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = - ## returns `x` rounded up to the nearest power of two. + ## Returns `x` rounded up to the nearest power of two. ## Zero and negative numbers get rounded up to 1. result = x - 1 when defined(cpu64): @@ -108,28 +108,28 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = result += 1 + ord(x<=0) proc countBits32*(n: int32): int {.noSideEffect.} = - ## counts the set bits in `n`. + ## Counts the set bits in `n`. var v = n v = v -% ((v shr 1'i32) and 0x55555555'i32) 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.} = - ## computes the sum of the elements in `x`. + ## Computes the sum of the elements in `x`. ## If `x` is empty, 0 is returned. for i in items(x): result = result + i template toFloat(f: float): float = f proc mean*[T](x: openArray[T]): float {.noSideEffect.} = - ## computes the mean of the elements in `x`, which are first converted to floats. + ## Computes the mean of the elements in `x`, which are first converted to floats. ## If `x` is empty, NaN is returned. ## ``toFloat(x: T): float`` must be defined. for i in items(x): result = result + toFloat(i) result = result / toFloat(len(x)) proc variance*[T](x: openArray[T]): float {.noSideEffect.} = - ## computes the variance of the elements in `x`. + ## Computes the variance of the elements in `x`. ## If `x` is empty, NaN is returned. ## ``toFloat(x: T): float`` must be defined. result = 0.0 @@ -140,41 +140,43 @@ proc variance*[T](x: openArray[T]): float {.noSideEffect.} = result = result / toFloat(len(x)) proc random*(max: int): int {.benign.} - ## returns a random number in the range 0..max-1. The sequence of + ## Returns a random number in the range 0..max-1. The sequence of ## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random" ## number, i.e. a tickcount. proc random*(max: float): float {.benign.} - ## returns a random number in the range 0..<max. The sequence of + ## Returns a random number in the range 0..<max. The sequence of ## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random" ## number, i.e. a tickcount. This has a 16-bit resolution on windows ## and a 48-bit resolution on other platforms. proc randomize*() {.benign.} - ## initializes the random number generator with a "random" + ## Initializes the random number generator with a "random" ## number, i.e. a tickcount. Note: Does nothing for the JavaScript target, ## as JavaScript does not support this. proc randomize*(seed: int) {.benign.} - ## initializes the random number generator with a specific seed. + ## Initializes the random number generator with a specific seed. ## Note: Does nothing for the JavaScript target, ## as JavaScript does not support this. {.push noSideEffect.} when not defined(JS): proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".} - ## computes the square root of `x`. + ## Computes the square root of `x`. proc cbrt*(x: float): float {.importc: "cbrt", header: "<math.h>".} - ## computes the cubic root of `x` + ## Computes the cubic root of `x` proc ln*(x: float): float {.importc: "log", header: "<math.h>".} - ## computes ln(x). + ## Computes the natural log of `x` proc log10*(x: float): float {.importc: "log10", header: "<math.h>".} + ## Computes the common logarithm (base 10) of `x` proc log2*(x: float): float = return ln(x) / ln(2.0) + ## Computes the binary logarithm (base 2) of `x` proc exp*(x: float): float {.importc: "exp", header: "<math.h>".} - ## computes e**x. + ## Computes the exponential function of `x` (pow(E, x)) proc frexp*(x: float, exponent: var int): float {. importc: "frexp", header: "<math.h>".} @@ -185,11 +187,14 @@ when not defined(JS): ## m. proc round*(x: float): int {.importc: "lrint", header: "<math.h>".} - ## converts a float to an int by rounding. + ## Converts a float to an int by rounding. proc arccos*(x: float): float {.importc: "acos", header: "<math.h>".} + ## Computes the arc cosine of `x` proc arcsin*(x: float): float {.importc: "asin", header: "<math.h>".} + ## Computes the arc sine of `x` proc arctan*(x: float): float {.importc: "atan", header: "<math.h>".} + ## Calculate the arc tangent of `y` / `x` proc arctan2*(y, x: float): float {.importc: "atan2", header: "<math.h>".} ## Calculate the arc tangent of `y` / `x`. ## `atan2` returns the arc tangent of `y` / `x`; it produces correct @@ -197,16 +202,23 @@ when not defined(JS): ## (`x` near 0). proc cos*(x: float): float {.importc: "cos", header: "<math.h>".} + ## Computes the cosine of `x` proc cosh*(x: float): float {.importc: "cosh", header: "<math.h>".} + ## Computes the hyperbolic cosine of `x` proc hypot*(x, y: float): float {.importc: "hypot", header: "<math.h>".} - ## same as ``sqrt(x*x + y*y)``. + ## Computes the hypotenuse of a right-angle triangle with `x` and + ## `y` as its base and height. Equivalent to ``sqrt(x*x + y*y)``. proc sinh*(x: float): float {.importc: "sinh", header: "<math.h>".} + ## Computes the hyperbolic sine of `x` proc sin*(x: float): float {.importc: "sin", header: "<math.h>".} + ## Computes the sine of `x` proc tan*(x: float): float {.importc: "tan", header: "<math.h>".} + ## Computes the tangent of `x` proc tanh*(x: float): float {.importc: "tanh", header: "<math.h>".} + ## Computes the hyperbolic tangent of `x` proc pow*(x, y: float): float {.importc: "pow", header: "<math.h>".} - ## computes x to power raised of y. + ## Computes `x` to power of `y`. proc erf*(x: float): float {.importc: "erf", header: "<math.h>".} ## The error function @@ -269,10 +281,26 @@ when not defined(JS): result = int(rand()) mod max proc trunc*(x: float): float {.importc: "trunc", header: "<math.h>".} + ## Truncates `x` to the decimal point + ## + ## .. code-block:: nim + ## echo trunc(PI) # 3.0 proc floor*(x: float): float {.importc: "floor", header: "<math.h>".} + ## Computes the floor function (i.e., the largest integer not greater than `x`) + ## + ## .. code-block:: nim + ## echo floor(-3.5) ## -4.0 proc ceil*(x: float): float {.importc: "ceil", header: "<math.h>".} + ## Computes the ceiling function (i.e., the smallest integer not less than `x`) + ## + ## .. code-block:: nim + ## echo ceil(-2.1) ## -2.0 proc fmod*(x, y: float): float {.importc: "fmod", header: "<math.h>".} + ## Computes the remainder of `x` divided by `y` + ## + ## .. code-block:: nim + ## echo fmod(-2.5, 0.3) ## -0.1 else: proc mathrandom(): float {.importc: "Math.random", nodecl.} diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d9f4845f3..141543c70 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -294,7 +294,7 @@ proc getSocketError*(socket: Socket): OSErrorCode = if result == 0.OSErrorCode: result = socket.lastError if result == 0.OSErrorCode: - raise newException(OSError, "No valid socket error code available") + raiseOSError(result, "No valid socket error code available") proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode) = @@ -332,10 +332,8 @@ proc socketError*(socket: Socket, err: int = -1, async = false, else: let errStr = $ErrErrorString(sslErr, nil) raiseSSLError(errStr & ": " & errStr) - let osMsg = osErrorMsg osLastError() - if osMsg != "": - errStr.add ". The OS reports: " & osMsg - raise newException(OSError, errStr) + let osErr = osLastError() + raiseOSError(osErr, errStr) of SSL_ERROR_SSL: raiseSSLError() else: raiseSSLError("Unknown Error") @@ -921,7 +919,7 @@ proc send*(socket: Socket, data: string, socketError(socket, lastError = lastError) if sent != data.len: - raise newException(OSError, "Could not send all data.") + raiseOSError(osLastError(), "Could not send all data.") proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} = ## Safe alternative to ``send``. Does not raise an EOS when an error occurs, diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 59a56a1ab..48d255dca 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -621,6 +621,20 @@ proc parentDir*(path: string): string {. else: result = "" +proc tailDir*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Returns the tail part of `path`.. + ## + ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``. + ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``. + ## | Example: ``tailDir("bin") == ""``. + var q = 1 + if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 + for i in 0..len(path)-q: + if path[i] in {DirSep, AltSep}: + return substr(path, i+1) + result = "" + proc isRootDir*(path: string): bool {. noSideEffect, rtl, extern: "nos$1".} = ## Checks whether a given `path` is a root directory @@ -1022,7 +1036,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", if moveFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError()) else: if c_rename(source, dest) != 0'i32: - raise newException(OSError, $strerror(errno)) + raiseOSError(osLastError(), $strerror(errno)) when not declared(ENOENT) and not defined(Windows): when NoFakeVars: @@ -1057,7 +1071,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} raiseOSError(osLastError()) else: if c_remove(file) != 0'i32 and errno != ENOENT: - raise newException(OSError, $strerror(errno)) + raiseOSError(osLastError(), $strerror(errno)) proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", tags: [ExecIOEffect].} = diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index e1abb0a4f..f8b2c3d8d 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -535,6 +535,10 @@ proc parseAttribute(my: var XmlParser) = pos = lexbase.handleLF(my, pos) buf = my.buf pendingSpace = true + of '/': + pos = lexbase.handleRefillChar(my, pos) + buf = my.buf + add(my.b, '/') else: if buf[pos] == quote: inc(pos) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index 349b0d97a..7873e7226 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -207,7 +207,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, when useWinVersion: raiseOSError(osLastError()) else: - raise newException(OSError, $gai_strerror(gaiResult)) + raiseOSError(osLastError(), $gai_strerror(gaiResult)) proc dealloc*(ai: ptr AddrInfo) = freeaddrinfo(ai) @@ -251,7 +251,7 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = var s = winlean.getservbyname(name, proto) else: var s = posix.getservbyname(name, proto) - if s == nil: raise newException(OSError, "Service not found.") + if s == nil: raiseOSError(osLastError(), "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = Port(s.s_port) @@ -267,7 +267,7 @@ proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} var s = winlean.getservbyport(ze(int16(port)).cint, proto) else: var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raise newException(OSError, "Service not found.") + if s == nil: raiseOSError(osLastError(), "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = Port(s.s_port) @@ -286,7 +286,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, cint(posix.AF_INET)) if s == nil: - raise newException(OSError, $hstrerror(h_errno)) + raiseOSError(osLastError(), $hstrerror(h_errno)) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) @@ -298,7 +298,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(OSError, "unknown h_addrtype") + raiseOSError(osLastError(), "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -319,7 +319,7 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(OSError, "unknown h_addrtype") + raiseOSError(osLastError(), "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -335,7 +335,7 @@ proc getSockDomain*(socket: SocketHandle): Domain = elif name.sa_family == rawAfInet6: result = AF_INET6 else: - raise newException(OSError, "unknown socket family in getSockFamily") + raiseOSError(osLastError(), "unknown socket family in getSockFamily") proc getAddrString*(sockAddr: ptr SockAddr): string = @@ -353,7 +353,7 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: result = result.substr("::ffff:".len) else: - raise newException(OSError, "unknown socket family in getAddrString") + raiseOSError(osLastError(), "unknown socket family in getAddrString") proc getSockName*(socket: SocketHandle): Port = diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim new file mode 100644 index 000000000..8ac6acb0e --- /dev/null +++ b/lib/pure/securehash.nim @@ -0,0 +1,199 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Nim Contributers +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import + strutils, unsigned + +const Sha1DigestSize = 20 + +type + Sha1Digest = array[0 .. Sha1DigestSize-1, uint8] + SecureHash* = distinct Sha1Digest + +proc sha1(src: string) : Sha1Digest + +proc secureHash*(str: string): SecureHash = SecureHash(sha1(str)) +proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename)) +proc `$`*(self: SecureHash): string = + result = "" + for v in Sha1Digest(self): + result.add(toHex(int(v), 2)) + +proc parseSecureHash*(hash: string): SecureHash = + for i in 0.. <Sha1DigestSize: + Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1])) + +proc `==`*(a, b: SecureHash): bool = + # Not a constant-time comparison, but that's acceptable in this context + Sha1Digest(a) == Sha1Digest(b) + + +when isMainModule: + let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]") + doAssert hash1 == hash1 + doAssert parseSecureHash($hash1) == hash1 + + +# Copyright (c) 2011, Micael Hildenborg +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Micael Hildenborg nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Ported to Nim by Erik O'Leary + +type + Sha1State = array[0 .. 5-1, uint32] + Sha1Buffer = array[0 .. 80-1, uint32] + +template clearBuffer(w: Sha1Buffer, len = 16) = + zeroMem(addr(w), len * sizeof(uint32)) + +proc init(result: var Sha1State) = + result[0] = 0x67452301'u32 + result[1] = 0xefcdab89'u32 + result[2] = 0x98badcfe'u32 + result[3] = 0x10325476'u32 + result[4] = 0xc3d2e1f0'u32 + +proc innerHash(state: var Sha1State, w: var Sha1Buffer) = + var + a = state[0] + b = state[1] + c = state[2] + d = state[3] + e = state[4] + + var round = 0 + + template rot(value, bits: uint32): uint32 {.immediate.} = + (value shl bits) or (value shr (32 - bits)) + + template sha1(fun, val: uint32): stmt = + let t = rot(a, 5) + fun + e + val + w[round] + e = d + d = c + c = rot(b, 30) + b = a + a = t + + template process(body: stmt): stmt = + w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1) + body + inc(round) + + template wrap(dest, value: expr): stmt {.immediate.} = + let v = dest + value + dest = v + + while round < 16: + sha1((b and c) or (not b and d), 0x5a827999'u32) + inc(round) + + while round < 20: + process: + sha1((b and c) or (not b and d), 0x5a827999'u32) + + while round < 40: + process: + sha1(b xor c xor d, 0x6ed9eba1'u32) + + while round < 60: + process: + sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32) + + while round < 80: + process: + sha1(b xor c xor d, 0xca62c1d6'u32) + + wrap state[0], a + wrap state[1], b + wrap state[2], c + wrap state[3], d + wrap state[4], e + +template computeInternal(src: expr): stmt {.immediate.} = + #Initialize state + var state: Sha1State + init(state) + + #Create w buffer + var w: Sha1Buffer + + #Loop through all complete 64byte blocks. + let byteLen = src.len + let endOfFullBlocks = byteLen - 64 + var endCurrentBlock = 0 + var currentBlock = 0 + + while currentBlock <= endOfFullBlocks: + endCurrentBlock = currentBlock + 64 + + var i = 0 + while currentBlock < endCurrentBlock: + w[i] = uint32(src[currentBlock+3]) or + uint32(src[currentBlock+2]) shl 8'u32 or + uint32(src[currentBlock+1]) shl 16'u32 or + uint32(src[currentBlock]) shl 24'u32 + currentBlock += 4 + inc(i) + + innerHash(state, w) + + #Handle last and not full 64 byte block if existing + endCurrentBlock = byteLen - currentBlock + clearBuffer(w) + var lastBlockBytes = 0 + + while lastBlockBytes < endCurrentBlock: + + var value = uint32(src[lastBlockBytes + currentBlock]) shl + ((3'u32 - (lastBlockBytes and 3)) shl 3) + + w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value + inc(lastBlockBytes) + + w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or ( + 0x80'u32 shl ((3'u32 - (lastBlockBytes and 3)) shl 3) + ) + + if endCurrentBlock >= 56: + innerHash(state, w) + clearBuffer(w) + + w[15] = uint32(byteLen) shl 3 + innerHash(state, w) + + # Store hash in result pointer, and make sure we get in in the correct order + # on both endian models. + for i in 0 .. Sha1DigestSize-1: + result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255) + +proc sha1(src: string) : Sha1Digest = + ## Calculate SHA1 from input string + computeInternal(src) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index aa8ad39d1..bfc393a96 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -295,7 +295,8 @@ proc contains*(s: Selector, key: SelectorKey): bool = ## ensures that the keys are equal. File descriptors may not always be ## unique especially when an fd is closed and then a new one is opened, ## the new one may have the same value. - return key.fd in s and s.fds[key.fd] == key + when not defined(nimdoc): + return key.fd in s and s.fds[key.fd] == key {.deprecated: [TEvent: Event, PSelectorKey: SelectorKey, TReadyInfo: ReadyInfo, PSelector: Selector].} diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index a10255e5b..18b2ab1e9 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -441,7 +441,7 @@ template gaiNim(a, p, h, list: expr): stmt = when defined(windows): raiseOSError(osLastError()) else: - raise newException(OSError, $gai_strerror(gaiResult)) + raiseOSError(osLastError(), $gai_strerror(gaiResult)) proc bindAddr*(socket: Socket, port = Port(0), address = "") {. tags: [ReadIOEffect].} = @@ -671,7 +671,7 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = var s = winlean.getservbyname(name, proto) else: var s = posix.getservbyname(name, proto) - if s == nil: raise newException(OSError, "Service not found.") + if s == nil: raiseOSError(osLastError(), "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = Port(s.s_port) @@ -687,7 +687,7 @@ proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} var s = winlean.getservbyport(ze(int16(port)).cint, proto) else: var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raise newException(OSError, "Service not found.") + if s == nil: raiseOSError(osLastError(), "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = Port(s.s_port) @@ -706,7 +706,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, cint(posix.AF_INET)) if s == nil: - raise newException(OSError, $hstrerror(h_errno)) + raiseOSError(osLastError(), $hstrerror(h_errno)) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) @@ -718,7 +718,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(OSError, "unknown h_addrtype") + raiseOSError(osLastError(), "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -739,7 +739,7 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(OSError, "unknown h_addrtype") + raiseOSError(osLastError(), "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -1594,7 +1594,7 @@ proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} = raiseOSError(osLastError()) if sent != data.len: - raise newException(OSError, "Could not send all data.") + raiseOSError(osLastError(), "Could not send all data.") proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} = ## sends data to a non-blocking socket. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e4d3f7494..f1315a9fd 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -789,7 +789,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = of "sat": info.weekday = dSat else: - raise newException(ValueError, "invalid day of week ") + raise newException(ValueError, + "Couldn't parse day of week (ddd), got: " & value[j..j+2]) j += 3 of "dddd": if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0: @@ -814,7 +815,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = info.weekday = dSat j += 8 else: - raise newException(ValueError, "invalid day of week ") + raise newException(ValueError, + "Couldn't parse day of week (dddd), got: " & value) of "h", "H": var pd = parseInt(value[j..j+1], sv) info.hour = sv @@ -865,7 +867,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = of "dec": info.month = mDec else: - raise newException(ValueError, "invalid month") + raise newException(ValueError, + "Couldn't parse month (MMM), got: " & value) j += 3 of "MMMM": if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0: @@ -905,7 +908,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = info.month = mDec j += 8 else: - raise newException(ValueError, "invalid month") + raise newException(ValueError, + "Couldn't parse month (MMMM), got: " & value) of "s": var pd = parseInt(value[j..j+1], sv) info.second = sv @@ -936,7 +940,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = elif value[j] == '-': info.timezone = 0-parseInt($value[j+1]) else: - raise newException(ValueError, "Sign for timezone " & value[j]) + raise newException(ValueError, + "Couldn't parse timezone offset (z), got: " & value[j]) j += 2 of "zz": if value[j] == '+': @@ -944,7 +949,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = elif value[j] == '-': info.timezone = 0-value[j+1..j+2].parseInt() else: - raise newException(ValueError, "Sign for timezone " & value[j]) + raise newException(ValueError, + "Couldn't parse timezone offset (zz), got: " & value[j]) j += 3 of "zzz": if value[j] == '+': @@ -952,7 +958,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = elif value[j] == '-': info.timezone = 0-value[j+1..j+2].parseInt() else: - raise newException(ValueError, "Sign for timezone " & value[j]) + raise newException(ValueError, + "Couldn't parse timezone offset (zzz), got: " & value[j]) j += 6 of "ZZZ": info.tzname = value[j..j+2].toUpper() @@ -1020,10 +1027,10 @@ proc parse*(value, layout: string): TimeInfo = # These are literals in both the layout and the value string if layout[i] == '\'': inc(i) - inc(j) while layout[i] != '\'' and layout.len-1 > i: inc(i) inc(j) + inc(i) else: inc(i) inc(j) @@ -1112,6 +1119,8 @@ when isMainModule: s = "2006-01-12T15:04:05Z-07:00" f = "yyyy-MM-ddTHH:mm:ssZzzz" assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") + f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz" + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" s = "2006-01-12T15:04:05.999999999Z-07:00" f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz" diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 4446eaa0c..5e20db32b 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -27,7 +27,7 @@ proc `==`*(a, b: Rune): bool = return int(a) == int(b) template ones(n: expr): expr = ((1 shl n)-1) proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} = - ## returns the number of Unicode characters of the string `s`. + ## Returns the number of Unicode characters of the string ``s`` var i = 0 while i < len(s): if ord(s[i]) <=% 127: inc(i) @@ -40,7 +40,7 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} = inc(result) proc runeLenAt*(s: string, i: Natural): int = - ## returns the number of bytes the rune starting at ``s[i]`` takes. + ## Returns the number of bytes the rune starting at ``s[i]`` takes if ord(s[i]) <=% 127: result = 1 elif ord(s[i]) shr 5 == 0b110: result = 2 elif ord(s[i]) shr 4 == 0b1110: result = 3 @@ -50,8 +50,8 @@ proc runeLenAt*(s: string, i: Natural): int = else: result = 1 template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = - ## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true`` - ## `i` is incremented by the number of bytes that have been processed. + ## Returns the Unicode character ``s[i]`` in ``result``. If ``doInc == true`` + ## ``i`` is incremented by the number of bytes that have been processed. bind ones if ord(s[i]) <=% 127: result = Rune(ord(s[i])) @@ -106,8 +106,8 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = when doInc: inc(i) proc validateUtf8*(s: string): int = - ## returns the position of the invalid byte in ``s`` if the string ``s`` does - ## not hold valid UTF-8 data. Otherwise -1 is returned. + ## Returns the position of the invalid byte in ``s`` if the string ``s`` does + ## not hold valid UTF-8 data. Otherwise ``-1`` is returned. var i = 0 let L = s.len while i < L: @@ -131,11 +131,11 @@ proc validateUtf8*(s: string): int = return -1 proc runeAt*(s: string, i: Natural): Rune = - ## returns the unicode character in `s` at byte index `i` + ## Returns the unicode character in ``s`` at byte index ``i`` fastRuneAt(s, i, result, false) proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = - ## converts a rune into its UTF8 representation + ## Converts a rune into its UTF-8 representation var i = RuneImpl(c) if i <=% 127: result = newString(1) @@ -174,11 +174,11 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = discard # error, exception? proc `$`*(rune: Rune): string = - ## converts a rune to a string + ## Converts a Rune to a string rune.toUTF8 proc `$`*(runes: seq[Rune]): string = - ## converts a sequence of runes to a string + ## Converts a sequence of Runes to a string result = "" for rune in runes: result.add(rune.toUTF8) @@ -1163,8 +1163,8 @@ proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int return -1 proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = - ## Converts `c` into lower case. This works for any Unicode character. - ## If possible, prefer `toLower` over `toUpper`. + ## Converts ``c`` into lower case. This works for any Unicode character. + ## If possible, prefer ``toLower`` over ``toUpper``. var c = RuneImpl(c) var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]: @@ -1175,8 +1175,8 @@ proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c) proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = - ## Converts `c` into upper case. This works for any Unicode character. - ## If possible, prefer `toLower` over `toUpper`. + ## Converts ``c`` into upper case. This works for any Unicode character. + ## If possible, prefer ``toLower`` over ``toUpper``. var c = RuneImpl(c) var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]: @@ -1187,6 +1187,7 @@ proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c) proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = + ## Converts ``c`` to title case var c = RuneImpl(c) var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2) if p >= 0 and c == toTitleSinglets[p]: @@ -1194,8 +1195,8 @@ proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c) proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = - ## returns true iff `c` is a lower case Unicode character - ## If possible, prefer `isLower` over `isUpper`. + ## Returns true iff ``c`` is a lower case Unicode character. + ## If possible, prefer ``isLower`` over ``isUpper``. var c = RuneImpl(c) # Note: toUpperRanges is correct here! var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) @@ -1206,8 +1207,8 @@ proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = return true proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = - ## returns true iff `c` is a upper case Unicode character - ## If possible, prefer `isLower` over `isUpper`. + ## Returns true iff ``c`` is a upper case Unicode character. + ## If possible, prefer ``isLower`` over ``isUpper``. var c = RuneImpl(c) # Note: toLowerRanges is correct here! var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) @@ -1218,7 +1219,7 @@ proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = return true proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = - ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter) + ## Returns true iff ``c`` is an *alpha* Unicode character (i.e., a letter) if isUpper(c) or isLower(c): return true var c = RuneImpl(c) @@ -1230,17 +1231,18 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = return true proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = + ## Returns true iff ``c`` is a Unicode titlecase character return isUpper(c) and isLower(c) proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = - ## returns true iff `c` is a Unicode whitespace character + ## Returns true iff ``c`` is a Unicode whitespace character var c = RuneImpl(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 proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = - ## returns true iff `c` is a Unicode combining character + ## Returns true iff ``c`` is a Unicode combining character var c = RuneImpl(c) # Optimized to return false immediately for ASCII @@ -1251,7 +1253,7 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = (c >= 0xfe20 and c <= 0xfe2f)) iterator runes*(s: string): Rune = - ## iterates over any unicode character of the string `s`. + ## Iterates over any unicode character of the string ``s`` var i = 0 result: Rune @@ -1259,8 +1261,14 @@ iterator runes*(s: string): Rune = fastRuneAt(s, i, result, true) yield result +proc toRunes*(s: string): seq[Rune] = + ## Obtains a sequence containing the Runes in ``s`` + result = newSeq[Rune]() + for r in s.runes: + result.add(r) + proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = - ## compares two UTF8 strings and ignores the case. Returns: + ## Compares two UTF-8 strings and ignores the case. Returns: ## ## | 0 iff a == b ## | < 0 iff a < b @@ -1277,8 +1285,8 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = result = a.len - b.len proc reversed*(s: string): string = - ## returns the reverse of `s`, interpreting it as unicode characters. Unicode - ## combining characters are correctly interpreted as well: + ## Returns the reverse of ``s``, interpreting it as Unicode characters. + ## Unicode combining characters are correctly interpreted as well: ## ## .. code-block:: nim ## @@ -1322,3 +1330,4 @@ when isMainModule: assert reversed("先秦兩漢") == "漢兩秦先" assert reversed("as⃝df̅") == "f̅ds⃝a" assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" + assert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 70b314e63..c459023a9 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -105,10 +105,10 @@ template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} = ## [OK] 2 + 2 = 4 ## [OK] (2 + -2) != 4 block: - template setup*(setupBody: stmt): stmt {.immediate, dirty.} = + template setup(setupBody: stmt): stmt {.immediate, dirty.} = template testSetupIMPL: stmt {.immediate, dirty.} = setupBody - template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} = + template teardown(teardownBody: stmt): stmt {.immediate, dirty.} = template testTeardownIMPL: stmt {.immediate, dirty.} = teardownBody body |