diff options
-rw-r--r-- | lib/pure/irc.nim | 6 | ||||
-rwxr-xr-x | lib/pure/sockets.nim | 87 | ||||
-rw-r--r-- | tests/benchmark.nim | 47 | ||||
-rw-r--r-- | tests/benchmarks/fannkuch.nim | 69 | ||||
-rw-r--r-- | tests/benchmarks/quicksort.nim | 54 |
5 files changed, 257 insertions, 6 deletions
diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim index 8946a9d8f..81dff024e 100644 --- a/lib/pure/irc.nim +++ b/lib/pure/irc.nim @@ -142,6 +142,8 @@ proc parseMessage(msg: string): TIRCEvent = inc(i) # Skip `:` var nick = "" i.inc msg.parseUntil(nick, {'!', ' '}, i) + result.nick = "" + result.serverName = "" if msg[i] == '!': result.nick = nick inc(i) # Skip `!` @@ -237,7 +239,8 @@ proc processLine(irc: var TIRC, line: string): TIRCEvent = result = parseMessage(line) # Get the origin result.origin = result.params[0] - if result.origin == irc.nick: result.origin = result.nick + if result.origin == irc.nick and + result.nick != "": result.origin = result.nick if result.cmd == MError: irc.close() @@ -386,6 +389,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort, result.messageBuffer = @[] result.handleEvent = ircEvent result.userArg = userArg + result.lineBuffer = "" proc register*(d: PDispatcher, irc: PAsyncIRC) = ## Registers ``irc`` with dispatcher ``d``. diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 5721d79fe..31973c6ce 100755 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -8,8 +8,12 @@ # ## This module implements a simple portable type-safe sockets layer. +## +## Most procedures raise EOS on error. + import os, parseutils +from times import epochTime when defined(Windows): import winlean @@ -59,6 +63,8 @@ type TRecvLineResult* = enum ## result for recvLineAsync RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail + ETimeout* = object of ESynch + const InvalidSocket* = TSocket(-1'i32) ## invalid socket number @@ -377,12 +383,14 @@ proc connect*(socket: TSocket, name: string, port = TPort(0), ## host name. If ``name`` is a host name, this function will try each IP ## of that host name. ``htons`` is already performed on ``port`` so you must ## not do it. + 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) gaiNim(name, port, hints, aiList) + # try all possibilities: var success = false var it = aiList @@ -445,7 +453,6 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), freeaddrinfo(aiList) if not success: OSError() - proc timeValFromMilliseconds(timeout = 500): TTimeVal = if timeout != -1: var seconds = timeout div 1000 @@ -545,7 +552,33 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int = result = int(select(cint(m+1), addr(rd), nil, nil, nil)) pruneSocketSet(readfds, (rd)) + +proc recv*(socket: TSocket, data: pointer, size: int): int = + ## receives data from a socket + result = recv(cint(socket), data, size, 0'i32) +template waitFor(): stmt = + if timeout - int(waited * 1000.0) < 1: + raise newException(ETimeout, "Call to recv() timed out.") + var s = @[socket] + var startTime = epochTime() + if select(s, timeout - int(waited * 1000.0)) != 1: + raise newException(ETimeout, "Call to recv() timed out.") + waited += (epochTime() - startTime) + +proc recv*(socket: TSocket, data: var string, size: int, timeout: int): int = + ## overload with a ``timeout`` parameter in miliseconds. + var waited = 0.0 # number of seconds already waited + + var read = 0 + while read < size: + waitFor() + result = recv(cint(socket), addr(data[read]), 1, 0'i32) + if result < 0: + return + inc(read) + + result = read proc recvLine*(socket: TSocket, line: var TaintedString): bool = ## returns false if no further data is available. `Line` must be initialized @@ -567,6 +600,29 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool = elif c == '\L': return true add(line.string, c) +proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool = + ## variant with a ``timeout`` parameter, the timeout parameter specifies + ## how many miliseconds to wait for data. + + var waited = 0.0 # number of seconds already waited + + setLen(line.string, 0) + while true: + var c: char + waitFor() + var n = recv(cint(socket), addr(c), 1, 0'i32) + if n < 0: return + elif n == 0: return true + if c == '\r': + waitFor() + n = recv(cint(socket), addr(c), 1, MSG_PEEK) + if n > 0 and c == '\L': + discard recv(cint(socket), addr(c), 1, 0'i32) + elif n <= 0: return false + return true + elif c == '\L': return true + add(line.string, c) + proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult = ## similar to ``recvLine`` but for non-blocking sockets. ## The values of the returned enum should be pretty self explanatory: @@ -592,10 +648,6 @@ proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult = elif c == '\L': return RecvFullLine add(line.string, c) -proc recv*(socket: TSocket, data: pointer, size: int): int = - ## receives data from a socket - result = recv(cint(socket), data, size, 0'i32) - proc recv*(socket: TSocket): TaintedString = ## receives all the data from the socket. ## Socket errors will result in an ``EOS`` error. @@ -625,6 +677,16 @@ proc recv*(socket: TSocket): TaintedString = add(result.string, buf) if bytesRead != bufSize-1: break +proc recvTimeout*(socket: TSocket, timeout: int): TaintedString = + ## overloaded variant to support a ``timeout`` parameter, the ``timeout`` + ## parameter specifies the amount of miliseconds to wait for data on the + ## socket. + var s = @[socket] + if s.select(timeout) != 1: + raise newException(ETimeout, "Call to recv() timed out.") + + return socket.recv + proc recvAsync*(socket: TSocket, s: var TaintedString): bool = ## receives all the data from a non-blocking socket. If socket is non-blocking ## and there are no messages available, `False` will be returned. @@ -723,6 +785,21 @@ proc setBlocking*(s: TSocket, blocking: bool) = if fcntl(cint(s), F_SETFL, mode) == -1: OSError() +proc connect*(socket: TSocket, timeout: int, name: string, port = TPort(0), + af: TDomain = AF_INET) = + ## Overload for ``connect`` to support timeouts. The ``timeout`` parameter + ## specifies the time in miliseconds of how long to wait for a connection + ## to be made. + ## + ## **Warning:** If ``socket`` is non-blocking and timeout is not ``-1`` then + ## this function may set blocking mode on ``socket`` to true. + socket.setBlocking(true) + + socket.connectAsync(name, port, af) + var s: seq[TSocket] = @[socket] + if selectWrite(s, timeout) != 1: + raise newException(ETimeout, "Call to connect() timed out.") + when defined(Windows): var wsa: TWSADATA if WSAStartup(0x0101'i16, wsa) != 0: OSError() diff --git a/tests/benchmark.nim b/tests/benchmark.nim new file mode 100644 index 000000000..8dd9cc2e2 --- /dev/null +++ b/tests/benchmark.nim @@ -0,0 +1,47 @@ +# +# +# Nimrod Benchmark tool +# (c) Copyright 2012 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This program runs benchmarks +import osproc, os, times, json + +type + TBenchResult = tuple[file: string, success: bool, time: float] + +proc compileBench(file: string) = + ## Compiles ``file``. + doAssert(execCmdEx("nimrod c -d:release " & file).exitCode == QuitSuccess) + +proc runBench(file: string): TBenchResult = + ## Runs ``file`` and returns info on how long it took to run. + var start = epochTime() + if execCmdEx(file.addFileExt(ExeExt)).exitCode == QuitSuccess: + var t = epochTime() - start + result = (file, true, t) + else: result = (file, false, -1.0) + +proc genOutput(benches: seq[TBenchResult]): PJsonNode = + result = newJObject() + for i in benches: + if i.success: + result[i.file.extractFilename] = newJFloat(i.time) + else: + result[i.file.extractFilename] = newJNull() + +proc doBench(): seq[TBenchResult] = + result = @[] + for i in walkFiles("tests/benchmarks/*.nim"): + echo(i.extractFilename) + compileBench(i) + result.add(runBench(i)) + +when isMainModule: + var b = doBench() + var output = genOutput(b) + writeFile("benchmarkResults.json", pretty(output)) + \ No newline at end of file diff --git a/tests/benchmarks/fannkuch.nim b/tests/benchmarks/fannkuch.nim new file mode 100644 index 000000000..15f78f50c --- /dev/null +++ b/tests/benchmarks/fannkuch.nim @@ -0,0 +1,69 @@ +import os +import strutils + +proc fannkuch (n: int): int = + var + count: seq[int] + maxFlips = 0 + m = n-1 + r = n + check = 0 + perm1: seq[int] + perm: seq[int] + + newSeq (count, n+1) + newSeq (perm1, n) + newSeq (perm, n) + for i in 0 .. n-1: + count[i] = i+1 + perm1[i] = i + perm[i] = i + count[n] = n+1 + + while True: + if check < 30: + for i in items (perm1): + write (stdout, $(i+1)) + echo ("") + inc (check) + + while r != 1: + count[r-1] = r + dec (r) + + if perm1[0] != 0 and perm1[m] != m: + # perm = perm1 + # The above line is between 3 and 4 times slower than the loop below! + for i in 0 .. n-1: + perm[i] = perm1[i] + var flipsCount = 0 + var k = perm[0] + while k != 0: + for i in 0 .. (k div 2): + swap (perm[i], perm[k-i]) + inc (flipsCount) + k = perm[0] + + if flipsCount > maxFlips: + maxFlips = flipsCount + + block makePerm: + while r != n: + var tmp = perm1[0] + # # perm1.delete (0) + # # perm1.insert (tmp, r) + # # The above is about twice as slow as the following: + # moveMem (addr (perm1[0]), addr (perm1[1]), r * sizeof (int)) + # The call to moveMem is about 50% slower than the loop below! + for i in 0 .. r-1: + perm1[i] = perm1[i+1] + perm1[r] = tmp + + dec (count[r]) + if count[r] > 0: + break makePerm + inc (r) + return maxFlips + +var n = 10 +echo ("Pfannkuchen(" & $n & ") = " & $fannkuch (n)) \ No newline at end of file diff --git a/tests/benchmarks/quicksort.nim b/tests/benchmarks/quicksort.nim new file mode 100644 index 000000000..599e3674c --- /dev/null +++ b/tests/benchmarks/quicksort.nim @@ -0,0 +1,54 @@ +import os +import strutils + +# Generate some pseudo-random data +var seed: tuple[s1, s2, s3: int32] = (2'i32, 8'i32, 16'i32) + +proc random(): int32 = + seed = (((((((seed[0] and 0x0007_FFFF'i32) shl 13'i32) xor seed[0]) shr + 19'i32) and 0x0000_1FFF'i32) xor + ((seed[0] and 0x000F_FFFE'i32) shl 12'i32)), + + ((((((seed[1] and 0x3FFF_FFFF'i32) shl 2'i32) xor seed[1]) shr + 25'i32) and 0x0000_007F'i32) xor + ((seed[1] and 0x0FFF_FFF8'i32) shl 4'i32)), + + ((((((seed[2] and 0x1FFF_FFFF'i32) shl 3'i32) xor seed[2]) shr + 11'i32) and 0x001F_FFFF'i32) xor + ((seed[2] and 0x0000_7FF0'i32) shl 17'i32))) + return seed[0] xor seed[1] xor seed[2] + +var n = 9999999 + +var data: seq[int32] +newSeq (data, n) +for i in 0 .. data.high(): + data[i] = random() + + +proc `$` (d: seq[int32]): string = + result = "[ " + for i in items (d): + result.addSep (", ", 2) + result.add ($(i and 0xFFFF_FFFF'i64)) + result.add (" ]") + +# Sort the data +proc sort (start, stop: int) = + if stop <= start+1: + return + + var j = start + for i in start..stop-2: + if data[i] <% data[stop-1]: + swap (data[i], data[j]) + inc (j) + swap (data[j], data[stop-1]) + + sort (start, j) + sort (j+1, stop) + +sort (0, data.len) +echo (data[n div 2 - 1] and 0xFFFF_FFFF'i64, ", ", + data[n div 2] and 0xFFFF_FFFF'i64, ", ", + data[n div 2 + 1] and 0xFFFF_FFFF'i64) \ No newline at end of file |