summary refs log tree commit diff stats
path: root/lib/pure/streams.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/streams.nim')
-rw-r--r--lib/pure/streams.nim184
1 files changed, 145 insertions, 39 deletions
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index e706f2016..8aa8d35d8 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -31,6 +31,8 @@ type
     getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
     readDataImpl*: proc (s: Stream, buffer: pointer,
                          bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
+    peekDataImpl*: proc (s: Stream, buffer: pointer,
+                         bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
     writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
       tags: [WriteIOEffect], gcsafe.}
     flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
@@ -79,23 +81,28 @@ proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
   ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
   result = s.readDataImpl(s, buffer, bufLen)
 
-proc readData*(s, unused: Stream, buffer: pointer, 
+proc readData*(s, unused: Stream, buffer: pointer,
                bufLen: int): int {.deprecated.} =
   ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
   result = s.readDataImpl(s, buffer, bufLen)
 
+proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
+  ## low level proc that reads data into an untyped `buffer` of `bufLen` size
+  ## without moving stream position
+  result = s.peekDataImpl(s, buffer, bufLen)
+
 proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
   ## low level proc that writes an untyped `buffer` of `bufLen` size
   ## to the stream `s`.
   s.writeDataImpl(s, buffer, bufLen)
 
-proc writeData*(s, unused: Stream, buffer: pointer, 
+proc writeData*(s, unused: Stream, buffer: pointer,
                 bufLen: int) {.deprecated.} =
   ## low level proc that writes an untyped `buffer` of `bufLen` size
   ## to the stream `s`.
   s.writeDataImpl(s, buffer, bufLen)
 
-proc write*[T](s: Stream, x: T) = 
+proc write*[T](s: Stream, x: T) =
   ## generic write procedure. Writes `x` to the stream `s`. Implementation:
   ##
   ## .. code-block:: Nim
@@ -105,62 +112,112 @@ proc write*[T](s: Stream, x: T) =
   shallowCopy(y, x)
   writeData(s, addr(y), sizeof(y))
 
-proc write*(s: Stream, x: string) = 
-  ## writes the string `x` to the the stream `s`. No length field or 
+proc write*(s: Stream, x: string) =
+  ## writes the string `x` to the the stream `s`. No length field or
   ## terminating zero is written.
   writeData(s, cstring(x), x.len)
 
-proc writeln*(s: Stream, args: varargs[string, `$`]) =
+proc writeLn*(s: Stream, args: varargs[string, `$`]) {.deprecated.} =
+  ## **Deprecated since version 0.11.4:** Use **writeLine** instead.
+  for str in args: write(s, str)
+  write(s, "\n")
+
+proc writeLine*(s: Stream, args: varargs[string, `$`]) =
   ## writes one or more strings to the the stream `s` followed
   ## by a new line. No length field or terminating zero is written.
   for str in args: write(s, str)
   write(s, "\n")
 
-proc read[T](s: Stream, result: var T) = 
+proc read[T](s: Stream, result: var T) =
   ## generic read procedure. Reads `result` from the stream `s`.
   if readData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
+proc peek[T](s: Stream, result: var T) =
+  ## generic peek procedure. Peeks `result` from the stream `s`.
+  if peekData(s, addr(result), sizeof(T)) != sizeof(T):
+    raise newEIO("cannot read from stream")
+
 proc readChar*(s: Stream): char =
   ## reads a char from the stream `s`. Raises `EIO` if an error occurred.
   ## Returns '\0' as an EOF marker.
   if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
 
-proc readBool*(s: Stream): bool = 
+proc peekChar*(s: Stream): char =
+  ## peeks a char from the stream `s`. Raises `EIO` if an error occurred.
+  ## Returns '\0' as an EOF marker.
+  if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
+
+proc readBool*(s: Stream): bool =
   ## reads a bool from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readInt8*(s: Stream): int8 = 
+proc peekBool*(s: Stream): bool =
+  ## peeks a bool from the stream `s`. Raises `EIO` if an error occured.
+  peek(s, result)
+
+proc readInt8*(s: Stream): int8 =
   ## reads an int8 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readInt16*(s: Stream): int16 = 
+proc peekInt8*(s: Stream): int8 =
+  ## peeks an int8 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readInt16*(s: Stream): int16 =
   ## reads an int16 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readInt32*(s: Stream): int32 = 
+proc peekInt16*(s: Stream): int16 =
+  ## peeks an int16 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readInt32*(s: Stream): int32 =
   ## reads an int32 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readInt64*(s: Stream): int64 = 
+proc peekInt32*(s: Stream): int32 =
+  ## peeks an int32 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readInt64*(s: Stream): int64 =
   ## reads an int64 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readFloat32*(s: Stream): float32 = 
+proc peekInt64*(s: Stream): int64 =
+  ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readFloat32*(s: Stream): float32 =
   ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readFloat64*(s: Stream): float64 = 
+proc peekFloat32*(s: Stream): float32 =
+  ## peeks a float32 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readFloat64*(s: Stream): float64 =
   ## reads a float64 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
 
-proc readStr*(s: Stream, length: int): TaintedString = 
-  ## reads a string of length `length` from the stream `s`. Raises `EIO` if 
+proc peekFloat64*(s: Stream): float64 =
+  ## peeks a float64 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readStr*(s: Stream, length: int): TaintedString =
+  ## reads a string of length `length` from the stream `s`. Raises `EIO` if
   ## an error occurred.
   result = newString(length).TaintedString
   var L = readData(s, addr(string(result)[0]), length)
   if L != length: setLen(result.string, L)
 
+proc peekStr*(s: Stream, length: int): TaintedString =
+  ## peeks a string of length `length` from the stream `s`. Raises `EIO` if
+  ## an error occurred.
+  result = newString(length).TaintedString
+  var L = peekData(s, addr(string(result)[0]), length)
+  if L != length: setLen(result.string, L)
+
 proc readLine*(s: Stream, line: var TaintedString): bool =
   ## reads a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
@@ -171,7 +228,7 @@ proc readLine*(s: Stream, line: var TaintedString): bool =
   line.string.setLen(0)
   while true:
     var c = readChar(s)
-    if c == '\c': 
+    if c == '\c':
       c = readChar(s)
       break
     elif c == '\L': break
@@ -181,13 +238,24 @@ proc readLine*(s: Stream, line: var TaintedString): bool =
     line.string.add(c)
   result = true
 
+proc peekLine*(s: Stream, line: var TaintedString): bool =
+  ## peeks a line of text from the stream `s` 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.
+  let pos = getPosition(s)
+  defer: setPosition(s, pos)
+  result = readLine(s, line)
+
 proc readLine*(s: Stream): TaintedString =
-  ## Reads a line from a stream `s`. Note: This is not very efficient. Raises 
+  ## Reads a line from a stream `s`. Note: This is not very efficient. Raises
   ## `EIO` if an error occurred.
   result = TaintedString""
   while true:
     var c = readChar(s)
-    if c == '\c': 
+    if c == '\c':
       c = readChar(s)
       break
     if c == '\L' or c == '\0':
@@ -195,6 +263,13 @@ proc readLine*(s: Stream): TaintedString =
     else:
       result.string.add(c)
 
+proc peekLine*(s: Stream): TaintedString =
+  ## Peeks a line from a stream `s`. Note: This is not very efficient. Raises
+  ## `EIO` if an error occurred.
+  let pos = getPosition(s)
+  defer: setPosition(s, pos)
+  result = readLine(s)
+
 type
   StringStream* = ref StringStreamObj ## a stream that encapsulates a string
   StringStreamObj* = object of StreamObj
@@ -206,10 +281,10 @@ type
 proc ssAtEnd(s: Stream): bool =
   var s = StringStream(s)
   return s.pos >= s.data.len
-    
-proc ssSetPosition(s: Stream, pos: int) = 
+
+proc ssSetPosition(s: Stream, pos: int) =
   var s = StringStream(s)
-  s.pos = clamp(pos, 0, s.data.high)
+  s.pos = clamp(pos, 0, s.data.len)
 
 proc ssGetPosition(s: Stream): int =
   var s = StringStream(s)
@@ -218,11 +293,17 @@ proc ssGetPosition(s: Stream): int =
 proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
   var s = StringStream(s)
   result = min(bufLen, s.data.len - s.pos)
-  if result > 0: 
+  if result > 0:
     copyMem(buffer, addr(s.data[s.pos]), result)
     inc(s.pos, result)
 
-proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = 
+proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  var s = StringStream(s)
+  result = min(bufLen, s.data.len - s.pos)
+  if result > 0:
+    copyMem(buffer, addr(s.data[s.pos]), result)
+
+proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
   var s = StringStream(s)
   if bufLen <= 0:
     return
@@ -235,7 +316,7 @@ proc ssClose(s: Stream) =
   var s = StringStream(s)
   s.data = nil
 
-proc newStringStream*(s: string = ""): StringStream = 
+proc newStringStream*(s: string = ""): StringStream =
   ## creates a new stream from the string `s`.
   new(result)
   result.data = s
@@ -245,12 +326,13 @@ proc newStringStream*(s: string = ""): StringStream =
   result.setPositionImpl = ssSetPosition
   result.getPositionImpl = ssGetPosition
   result.readDataImpl = ssReadData
+  result.peekDataImpl = ssPeekData
   result.writeDataImpl = ssWriteData
 
 when not defined(js):
 
   type
-    FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
+    FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
     FileStreamObj* = object of Stream
       f: File
   {.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
@@ -267,6 +349,11 @@ when not defined(js):
   proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
     result = readBuffer(FileStream(s).f, buffer, bufLen)
 
+  proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+    let pos = fsGetPosition(s)
+    defer: fsSetPosition(s, pos)
+    result = readBuffer(FileStream(s).f, buffer, bufLen)
+
   proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
     if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
       raise newEIO("cannot write to stream")
@@ -280,6 +367,7 @@ when not defined(js):
     result.setPositionImpl = fsSetPosition
     result.getPositionImpl = fsGetPosition
     result.readDataImpl = fsReadData
+    result.peekDataImpl = fsPeekData
     result.writeDataImpl = fsWriteData
     result.flushImpl = fsFlush
 
@@ -300,14 +388,14 @@ else:
       handle*: FileHandle
       pos: int
 
-  {.deprecated: [PFileHandleStream: FileHandleStream, 
+  {.deprecated: [PFileHandleStream: FileHandleStream,
      TFileHandleStream: FileHandleStreamObj].}
 
   proc newEOS(msg: string): ref OSError =
     new(result)
     result.msg = msg
 
-  proc hsGetPosition(s: FileHandleStream): int = 
+  proc hsGetPosition(s: FileHandleStream): int =
     return s.pos
 
   when defined(windows):
@@ -315,27 +403,30 @@ else:
     discard
   else:
     import posix
-    
-    proc hsSetPosition(s: FileHandleStream, pos: int) = 
+
+    proc hsSetPosition(s: FileHandleStream, pos: int) =
       discard lseek(s.handle, pos, SEEK_SET)
 
     proc hsClose(s: FileHandleStream) = discard close(s.handle)
-    proc hsAtEnd(s: FileHandleStream): bool = 
+    proc hsAtEnd(s: FileHandleStream): bool =
       var pos = hsGetPosition(s)
       var theEnd = lseek(s.handle, 0, SEEK_END)
       result = pos >= theEnd
       hsSetPosition(s, pos) # set position back
 
-    proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int = 
+    proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
       result = posix.read(s.handle, buffer, bufLen)
       inc(s.pos, result)
-      
-    proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) = 
-      if posix.write(s.handle, buffer, bufLen) != bufLen: 
+
+    proc hsPeekData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
+      result = posix.read(s.handle, buffer, bufLen)
+
+    proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
+      if posix.write(s.handle, buffer, bufLen) != bufLen:
         raise newEIO("cannot write to stream")
       inc(s.pos, bufLen)
 
-  proc newFileHandleStream*(handle: FileHandle): FileHandleStream = 
+  proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
     new(result)
     result.handle = handle
     result.pos = 0
@@ -344,10 +435,11 @@ else:
     result.setPosition = hsSetPosition
     result.getPosition = hsGetPosition
     result.readData = hsReadData
+    result.peekData = hsPeekData
     result.writeData = hsWriteData
 
-  proc newFileHandleStream*(filename: string, 
-                            mode: FileMode): FileHandleStream = 
+  proc newFileHandleStream*(filename: string,
+                            mode: FileMode): FileHandleStream =
     when defined(windows):
       discard
     else:
@@ -361,3 +453,17 @@ else:
       var handle = open(filename, flags)
       if handle < 0: raise newEOS("posix.open() call failed")
     result = newFileHandleStream(handle)
+
+when isMainModule and defined(testing):
+  var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran")
+  assert(ss.getPosition == 0)
+  assert(ss.peekStr(5) == "The q")
+  assert(ss.getPosition == 0) # haven't moved
+  assert(ss.readStr(5) == "The q")
+  assert(ss.getPosition == 5) # did move
+  assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.")
+  assert(ss.getPosition == 5) # haven't moved
+  var str = newString(100)
+  assert(ss.peekLine(str))
+  assert(str == "uick brown fox jumped over the lazy dog.")
+  assert(ss.getPosition == 5) # haven't moved