Added peeking procedures to streams

Adds peeking to streams, which is just like reading, but at the end, the stream position hasn't changed.
This commit is contained in:
apense 2015-04-12 20:38:35 -04:00
commit 9a629d72e8

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -31,6 +31,8 @@ type
getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.} getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
readDataImpl*: proc (s: Stream, buffer: pointer, readDataImpl*: proc (s: Stream, buffer: pointer,
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.} 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, writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
tags: [WriteIOEffect], gcsafe.} tags: [WriteIOEffect], gcsafe.}
flushImpl*: proc (s: Stream) {.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. ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
result = s.readDataImpl(s, buffer, bufLen) 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) = proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
## low level proc that writes an untyped `buffer` of `bufLen` size ## low level proc that writes an untyped `buffer` of `bufLen` size
## to the stream `s`. ## 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): if readData(s, addr(result), sizeof(T)) != sizeof(T):
raise newEIO("cannot read from stream") 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 = proc readChar*(s: Stream): char =
## reads a char from the stream `s`. Raises `EIO` if an error occurred. ## reads a char from the stream `s`. Raises `EIO` if an error occurred.
## Returns '\0' as an EOF marker. ## Returns '\0' as an EOF marker.
if readData(s, addr(result), sizeof(result)) != 1: result = '\0' 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 = proc readBool*(s: Stream): bool =
## reads a bool from the stream `s`. Raises `EIO` if an error occurred. ## reads a bool from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readInt8*(s: Stream): int8 =
## reads an int8 from the stream `s`. Raises `EIO` if an error occurred. ## reads an int8 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readInt16*(s: Stream): int16 =
## reads an int16 from the stream `s`. Raises `EIO` if an error occurred. ## reads an int16 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readInt32*(s: Stream): int32 =
## reads an int32 from the stream `s`. Raises `EIO` if an error occurred. ## reads an int32 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readInt64*(s: Stream): int64 =
## reads an int64 from the stream `s`. Raises `EIO` if an error occurred. ## reads an int64 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readFloat32*(s: Stream): float32 =
## reads a float32 from the stream `s`. Raises `EIO` if an error occurred. ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readFloat64*(s: Stream): float64 =
## reads a float64 from the stream `s`. Raises `EIO` if an error occurred. ## reads a float64 from the stream `s`. Raises `EIO` if an error occurred.
read(s, result) 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 = proc readStr*(s: Stream, length: int): TaintedString =
## reads a string of length `length` from the stream `s`. Raises `EIO` if ## reads a string of length `length` from the stream `s`. Raises `EIO` if
## an error occurred. ## an error occurred.
@ -161,6 +206,13 @@ proc readStr*(s: Stream, length: int): TaintedString =
var L = readData(s, addr(string(result)[0]), length) var L = readData(s, addr(string(result)[0]), length)
if L != length: setLen(result.string, L) 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 = proc readLine*(s: Stream, line: var TaintedString): bool =
## reads a line of text from the stream `s` into `line`. `line` must not be ## reads a line of text from the stream `s` into `line`. `line` must not be
## ``nil``! May throw an IO exception. ## ``nil``! May throw an IO exception.
@ -181,6 +233,30 @@ proc readLine*(s: Stream, line: var TaintedString): bool =
line.string.add(c) line.string.add(c)
result = true 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 = 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. ## `EIO` if an error occurred.
@ -195,6 +271,23 @@ proc readLine*(s: Stream): TaintedString =
else: else:
result.string.add(c) 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 type
StringStream* = ref StringStreamObj ## a stream that encapsulates a string StringStream* = ref StringStreamObj ## a stream that encapsulates a string
StringStreamObj* = object of StreamObj 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) copyMem(buffer, addr(s.data[s.pos]), result)
inc(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) = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s) var s = StringStream(s)
if bufLen <= 0: if bufLen <= 0:
@ -245,6 +344,7 @@ proc newStringStream*(s: string = ""): StringStream =
result.setPositionImpl = ssSetPosition result.setPositionImpl = ssSetPosition
result.getPositionImpl = ssGetPosition result.getPositionImpl = ssGetPosition
result.readDataImpl = ssReadData result.readDataImpl = ssReadData
result.peekDataImpl = ssPeekData
result.writeDataImpl = ssWriteData result.writeDataImpl = ssWriteData
when not defined(js): when not defined(js):
@ -267,6 +367,11 @@ when not defined(js):
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = readBuffer(FileStream(s).f, buffer, bufLen) 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) = proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
raise newEIO("cannot write to stream") raise newEIO("cannot write to stream")
@ -280,6 +385,7 @@ when not defined(js):
result.setPositionImpl = fsSetPosition result.setPositionImpl = fsSetPosition
result.getPositionImpl = fsGetPosition result.getPositionImpl = fsGetPosition
result.readDataImpl = fsReadData result.readDataImpl = fsReadData
result.peekDataImpl = fsPeekData
result.writeDataImpl = fsWriteData result.writeDataImpl = fsWriteData
result.flushImpl = fsFlush result.flushImpl = fsFlush
@ -330,6 +436,9 @@ else:
result = posix.read(s.handle, buffer, bufLen) result = posix.read(s.handle, buffer, bufLen)
inc(s.pos, result) 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) = proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
if posix.write(s.handle, buffer, bufLen) != bufLen: if posix.write(s.handle, buffer, bufLen) != bufLen:
raise newEIO("cannot write to stream") raise newEIO("cannot write to stream")
@ -344,6 +453,7 @@ else:
result.setPosition = hsSetPosition result.setPosition = hsSetPosition
result.getPosition = hsGetPosition result.getPosition = hsGetPosition
result.readData = hsReadData result.readData = hsReadData
result.peekData = hsPeekData
result.writeData = hsWriteData result.writeData = hsWriteData
proc newFileHandleStream*(filename: string, proc newFileHandleStream*(filename: string,