Merge pull request #2543 from apense/patch-1

Added peeking procedures to streams
This commit is contained in:
Andreas Rumpf
2015-05-25 00:52:01 +02:00

View File

@@ -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,17 @@ 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)
readLine(s, line)
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 +258,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)
readLine(s)
type
StringStream* = ref StringStreamObj ## a stream that encapsulates a string
StringStreamObj* = object of StreamObj
@@ -222,6 +292,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 +321,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 +343,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:
@@ -280,6 +362,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 +412,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 +430,7 @@ else:
result.setPosition = hsSetPosition
result.getPosition = hsGetPosition
result.readData = hsReadData
result.peekData = hsPeekData
result.writeData = hsWriteData
proc newFileHandleStream*(filename: string,
@@ -361,3 +448,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