summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authordef <dennis@felsin9.de>2015-07-24 00:29:09 +0200
committerdef <dennis@felsin9.de>2015-07-24 00:31:35 +0200
commit3943fba34b317abf2c2421fdfd0f042dd34bbbc6 (patch)
tree0f8d87083faef27b7b98b2a33c3b521fd818308f /lib
parentcd42d38887d6c15be42c79b51d86e172f271d5f7 (diff)
downloadNim-3943fba34b317abf2c2421fdfd0f042dd34bbbc6.tar.gz
Improve performance of readLine by using fgets
This drops compatibility with pure CR line endings of old Mac systems
Diffstat (limited to 'lib')
-rw-r--r--lib/system.nim12
-rw-r--r--lib/system/sysio.nim77
2 files changed, 29 insertions, 60 deletions
diff --git a/lib/system.nim b/lib/system.nim
index c5b0e0cc7..2ebfc6d73 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2603,17 +2603,17 @@ when not defined(JS): #and not defined(NimrodVM):
 
     proc readLine*(f: File): TaintedString  {.tags: [ReadIOEffect], benign.}
       ## reads a line of text from the file `f`. May throw an IO exception.
-      ## A line of text may be delimited by ``CR``, ``LF`` or
-      ## ``CRLF``. The newline character(s) are not part of the returned string.
+      ## A line of text may be delimited by ``LF`` or ``CRLF``. The newline
+      ## character(s) are not part of the returned string.
 
     proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
                   benign.}
       ## reads a line of text from the file `f` into `line`. `line` must not be
       ## ``nil``! May throw an IO exception.
-      ## A line of text may be delimited by ``CR``, ``LF`` or
-      ## ``CRLF``. The newline character(s) are not part of the returned string.
-      ## Returns ``false`` if the end of the file has been reached, ``true``
-      ## otherwise. If ``false`` is returned `line` contains no new data.
+      ## A line of text may be delimited by ``LF`` or ``CRLF``. The newline
+      ## character(s) are not part of the returned string. Returns ``false``
+      ## if the end of the file has been reached, ``true`` otherwise. If
+      ## ``false`` is returned `line` contains no new data.
 
     proc writeLn*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
                              tags: [WriteIOEffect], benign, deprecated.}
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 5464ee126..dccb13303 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -31,26 +31,6 @@ proc fprintf(f: File, frmt: cstring) {.importc: "fprintf",
 proc strlen(c: cstring): int {.
   importc: "strlen", header: "<string.h>", tags: [].}
 
-when defined(posix):
-  proc getc_unlocked(stream: File): cint {.importc: "getc_unlocked",
-    header: "<stdio.h>", tags: [ReadIOEffect].}
-
-  proc flockfile(stream: File) {.importc: "flockfile", header: "<stdio.h>",
-    tags: [ReadIOEffect].}
-
-  proc funlockfile(stream: File) {.importc: "funlockfile", header: "<stdio.h>",
-    tags: [ReadIOEffect].}
-elif false:
-  # doesn't work on Windows yet:
-  proc getc_unlocked(stream: File): cint {.importc: "_fgetc_nolock",
-    header: "<stdio.h>", tags: [ReadIOEffect].}
-
-  proc flockfile(stream: File) {.importc: "_lock_file", header: "<stdio.h>",
-    tags: [ReadIOEffect].}
-
-  proc funlockfile(stream: File) {.importc: "_unlock_file", header: "<stdio.h>",
-    tags: [ReadIOEffect].}
-
 # C routine that is used here:
 proc fread(buf: pointer, size, n: int, f: File): int {.
   importc: "fread", header: "<stdio.h>", tags: [ReadIOEffect].}
@@ -86,40 +66,29 @@ const
 proc raiseEIO(msg: string) {.noinline, noreturn.} =
   sysFatal(IOError, msg)
 
-when declared(getc_unlocked):
-  proc readLine(f: File, line: var TaintedString): bool =
-    setLen(line.string, 0) # reuse the buffer!
-    flockfile(f)
-    while true:
-      var c = getc_unlocked(f)
-      if c < 0'i32:
-        if line.len > 0: break
-        else: return false
-      if c == 10'i32: break # LF
-      if c == 13'i32:  # CR
-        c = getc_unlocked(f) # is the next char LF?
-        if c != 10'i32: ungetc(c, f) # no, put the character back
-        break
-      add line.string, chr(int(c))
-    result = true
-    funlockfile(f)
-else:
-  proc readLine(f: File, line: var TaintedString): bool =
-    # of course this could be optimized a bit; but IO is slow anyway...
-    # and it was difficult to get this CORRECT with Ansi C's methods
-    setLen(line.string, 0) # reuse the buffer!
-    while true:
-      var c = fgetc(f)
-      if c < 0'i32:
-        if line.len > 0: break
-        else: return false
-      if c == 10'i32: break # LF
-      if c == 13'i32:  # CR
-        c = fgetc(f) # is the next char LF?
-        if c != 10'i32: ungetc(c, f) # no, put the character back
-        break
-      add line.string, chr(int(c))
-    result = true
+proc readLine(f: File, line: var TaintedString): bool =
+  template returnUntil(p: int) =
+    line.string[p] = '\0'
+    line.string.setLen(p)
+    return true
+
+  var pos = 0
+  # Use the currently reserved space for a first try
+  var space = cast[PGenericSeq](line.string).space
+
+  while true:
+    if fgets(addr line.string[pos], space, f) == nil:
+      line.string.setLen(0)
+      return false
+    # This will cut the string short when it contains \0
+    let last = pos + cstring(addr line.string[pos]).len-1
+    if line.string[last] == '\l':
+      if last > 0 and line.string[last-1] == '\c':
+        returnUntil(last-1)
+      returnUntil(last)
+    pos = last+1
+    space = 128 # Read in 128 bytes at a time
+    line.string.setLen(pos+space)
 
 proc readLine(f: File): TaintedString =
   result = TaintedString(newStringOfCap(80))