From 9a629d72e8560011d1f9351d22ffee8b7ed5bd43 Mon Sep 17 00:00:00 2001 From: apense Date: Sun, 12 Apr 2015 20:38:35 -0400 Subject: [PATCH 1/4] Added peeking procedures to streams Adds peeking to streams, which is just like reading, but at the end, the stream position hasn't changed. --- lib/pure/streams.nim | 112 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index e706f20160..798c587a1d 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.} @@ -84,6 +86,11 @@ proc readData*(s, unused: Stream, buffer: pointer, ## 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`. @@ -121,39 +128,77 @@ proc read[T](s: Stream, result: var T) = 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 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 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 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 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 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 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 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 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. @@ -161,6 +206,13 @@ proc readStr*(s: Stream, length: int): 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. @@ -181,6 +233,30 @@ 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. + line.string.setLen(0) + let pos = getPosition(s) + while true: + var c = readChar(s) + if c == '\c': + c = readChar(s) + break + elif c == '\L': break + elif c == '\0': + if line.len > 0: break + else: + setPosition(s, pos) + return false + line.string.add(c) + setPosition(s, pos) + result = true + proc readLine*(s: Stream): TaintedString = ## Reads a line from a stream `s`. Note: This is not very efficient. Raises ## `EIO` if an error occurred. @@ -195,6 +271,23 @@ 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. + result = TaintedString"" + let pos = getPosition(s) + while true: + var c = readChar(s) + if c == '\c': + c = readChar(s) + setPosition(s, pos) + break + if c == '\L' or c == '\0': + setPosition(s, pos) + break + else: + result.string.add(c) + type StringStream* = ref StringStreamObj ## a stream that encapsulates a string StringStreamObj* = object of StreamObj @@ -222,6 +315,12 @@ proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int = copyMem(buffer, addr(s.data[s.pos]), result) inc(s.pos, result) +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: @@ -245,6 +344,7 @@ proc newStringStream*(s: string = ""): StringStream = result.setPositionImpl = ssSetPosition result.getPositionImpl = ssGetPosition result.readDataImpl = ssReadData + result.peekDataImpl = ssPeekData result.writeDataImpl = ssWriteData when not defined(js): @@ -266,6 +366,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) + result = readBuffer(FileStream(s).f, buffer, bufLen) + fsSetPosition(s, pos) proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: @@ -280,6 +385,7 @@ when not defined(js): result.setPositionImpl = fsSetPosition result.getPositionImpl = fsGetPosition result.readDataImpl = fsReadData + result.peekDataImpl = fsPeekData result.writeDataImpl = fsWriteData result.flushImpl = fsFlush @@ -329,6 +435,9 @@ else: proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int = result = posix.read(s.handle, buffer, bufLen) inc(s.pos, result) + + 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: @@ -344,6 +453,7 @@ else: result.setPosition = hsSetPosition result.getPosition = hsGetPosition result.readData = hsReadData + result.peekData = hsPeekData result.writeData = hsWriteData proc newFileHandleStream*(filename: string, From 80b5e612c0f92f2bae6e148317a682deb86821f0 Mon Sep 17 00:00:00 2001 From: apense Date: Mon, 4 May 2015 08:49:41 -0400 Subject: [PATCH 2/4] Update streams.nim --- lib/pure/streams.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 798c587a1d..ce33a96254 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -471,3 +471,13 @@ else: var handle = open(filename, flags) if handle < 0: raise newEOS("posix.open() call failed") result = newFileHandleStream(handle) + +when 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 From d48bcb9873e426ece794d517290f262f6345f3b2 Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 21 May 2015 17:51:41 -0400 Subject: [PATCH 3/4] Rewrote some peeking procedures Use `defer`s and `read...` procs that are already in place. Someone should check that the `defer`s are written correctly. I'm new to using them. --- lib/pure/streams.nim | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index ce33a96254..9054031e0e 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -240,22 +240,9 @@ proc peekLine*(s: Stream, line: var TaintedString): bool = ## ``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. - line.string.setLen(0) let pos = getPosition(s) - while true: - var c = readChar(s) - if c == '\c': - c = readChar(s) - break - elif c == '\L': break - elif c == '\0': - if line.len > 0: break - else: - setPosition(s, pos) - return false - line.string.add(c) - setPosition(s, pos) - result = true + defer: setPosition(s, pos) + readLine(s, line) proc readLine*(s: Stream): TaintedString = ## Reads a line from a stream `s`. Note: This is not very efficient. Raises @@ -274,19 +261,9 @@ proc readLine*(s: Stream): TaintedString = proc peekLine*(s: Stream): TaintedString = ## Peeks a line from a stream `s`. Note: This is not very efficient. Raises ## `EIO` if an error occurred. - result = TaintedString"" let pos = getPosition(s) - while true: - var c = readChar(s) - if c == '\c': - c = readChar(s) - setPosition(s, pos) - break - if c == '\L' or c == '\0': - setPosition(s, pos) - break - else: - result.string.add(c) + defer: setPosition(s, pos) + readLine(s) type StringStream* = ref StringStreamObj ## a stream that encapsulates a string From f610f8c5f049940aa5f6140b66830a1b2581a64c Mon Sep 17 00:00:00 2001 From: apense Date: Sun, 24 May 2015 18:36:52 -0400 Subject: [PATCH 4/4] Added defer statement All peeks should be covered by defer now, I think --- lib/pure/streams.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 9054031e0e..77698d3292 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -346,8 +346,8 @@ when not defined(js): proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int = let pos = fsGetPosition(s) + defer: fsSetPosition(s, pos) result = readBuffer(FileStream(s).f, buffer, bufLen) - fsSetPosition(s, pos) proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: