summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/irc.nim6
-rwxr-xr-xlib/pure/sockets.nim87
-rw-r--r--tests/benchmark.nim47
-rw-r--r--tests/benchmarks/fannkuch.nim69
-rw-r--r--tests/benchmarks/quicksort.nim54
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