Merge pull request #2799 from avsej/fix-streams-peekline

Fix streams peekline
This commit is contained in:
Andreas Rumpf 2015-05-25 23:59:52 +02:00
commit 77b010bd36

View file

@ -31,7 +31,7 @@ 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, peekDataImpl*: proc (s: Stream, buffer: pointer,
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.} 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.}
@ -81,7 +81,7 @@ proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
## 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 readData*(s, unused: Stream, buffer: pointer, proc readData*(s, unused: Stream, buffer: pointer,
bufLen: int): int {.deprecated.} = bufLen: int): int {.deprecated.} =
## 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)
@ -96,13 +96,13 @@ proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
## to the stream `s`. ## to the stream `s`.
s.writeDataImpl(s, buffer, bufLen) s.writeDataImpl(s, buffer, bufLen)
proc writeData*(s, unused: Stream, buffer: pointer, proc writeData*(s, unused: Stream, buffer: pointer,
bufLen: int) {.deprecated.} = bufLen: int) {.deprecated.} =
## 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`.
s.writeDataImpl(s, buffer, bufLen) 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: ## generic write procedure. Writes `x` to the stream `s`. Implementation:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
@ -112,8 +112,8 @@ proc write*[T](s: Stream, x: T) =
shallowCopy(y, x) shallowCopy(y, x)
writeData(s, addr(y), sizeof(y)) writeData(s, addr(y), sizeof(y))
proc write*(s: Stream, x: string) = proc write*(s: Stream, x: string) =
## writes the string `x` to the the stream `s`. No length field or ## writes the string `x` to the the stream `s`. No length field or
## terminating zero is written. ## terminating zero is written.
writeData(s, cstring(x), x.len) writeData(s, cstring(x), x.len)
@ -123,7 +123,7 @@ proc writeln*(s: Stream, args: varargs[string, `$`]) =
for str in args: write(s, str) for str in args: write(s, str)
write(s, "\n") 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`. ## generic read procedure. Reads `result` from the stream `s`.
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")
@ -143,7 +143,7 @@ proc peekChar*(s: Stream): char =
## Returns '\0' as an EOF marker. ## Returns '\0' as an EOF marker.
if peekData(s, addr(result), sizeof(result)) != 1: result = '\0' 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)
@ -151,7 +151,7 @@ proc peekBool*(s: Stream): bool =
## peeks a bool from the stream `s`. Raises `EIO` if an error occured. ## peeks a bool from the stream `s`. Raises `EIO` if an error occured.
peek(s, result) 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)
@ -159,55 +159,55 @@ proc peekInt8*(s: Stream): int8 =
## peeks an int8 from the stream `s`. Raises `EIO` if an error occurred. ## peeks an int8 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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 = proc peekInt16*(s: Stream): int16 =
## peeks an int16 from the stream `s`. Raises `EIO` if an error occurred. ## peeks an int16 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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 = proc peekInt32*(s: Stream): int32 =
## peeks an int32 from the stream `s`. Raises `EIO` if an error occurred. ## peeks an int32 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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 = proc peekInt64*(s: Stream): int64 =
## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred. ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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 = proc peekFloat32*(s: Stream): float32 =
## peeks a float32 from the stream `s`. Raises `EIO` if an error occurred. ## peeks a float32 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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 = proc peekFloat64*(s: Stream): float64 =
## peeks a float64 from the stream `s`. Raises `EIO` if an error occurred. ## peeks a float64 from the stream `s`. Raises `EIO` if an error occurred.
peek(s, result) 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.
result = newString(length).TaintedString result = newString(length).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 = proc peekStr*(s: Stream, length: int): TaintedString =
## peeks a string of length `length` from the stream `s`. Raises `EIO` if ## peeks a string of length `length` from the stream `s`. Raises `EIO` if
## an error occurred. ## an error occurred.
result = newString(length).TaintedString result = newString(length).TaintedString
var L = peekData(s, addr(string(result)[0]), length) var L = peekData(s, addr(string(result)[0]), length)
@ -223,7 +223,7 @@ proc readLine*(s: Stream, line: var TaintedString): bool =
line.string.setLen(0) line.string.setLen(0)
while true: while true:
var c = readChar(s) var c = readChar(s)
if c == '\c': if c == '\c':
c = readChar(s) c = readChar(s)
break break
elif c == '\L': break elif c == '\L': break
@ -242,15 +242,15 @@ proc peekLine*(s: Stream, line: var TaintedString): bool =
## otherwise. If ``false`` is returned `line` contains no new data. ## otherwise. If ``false`` is returned `line` contains no new data.
let pos = getPosition(s) let pos = getPosition(s)
defer: setPosition(s, pos) defer: setPosition(s, pos)
readLine(s, line) result = readLine(s, line)
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.
result = TaintedString"" result = TaintedString""
while true: while true:
var c = readChar(s) var c = readChar(s)
if c == '\c': if c == '\c':
c = readChar(s) c = readChar(s)
break break
if c == '\L' or c == '\0': if c == '\L' or c == '\0':
@ -259,11 +259,11 @@ proc readLine*(s: Stream): TaintedString =
result.string.add(c) result.string.add(c)
proc peekLine*(s: Stream): TaintedString = proc peekLine*(s: Stream): TaintedString =
## Peeks a line from a stream `s`. Note: This is not very efficient. Raises ## Peeks a line from a stream `s`. Note: This is not very efficient. Raises
## `EIO` if an error occurred. ## `EIO` if an error occurred.
let pos = getPosition(s) let pos = getPosition(s)
defer: setPosition(s, pos) defer: setPosition(s, pos)
readLine(s) result = readLine(s)
type type
StringStream* = ref StringStreamObj ## a stream that encapsulates a string StringStream* = ref StringStreamObj ## a stream that encapsulates a string
@ -276,8 +276,8 @@ type
proc ssAtEnd(s: Stream): bool = proc ssAtEnd(s: Stream): bool =
var s = StringStream(s) var s = StringStream(s)
return s.pos >= s.data.len return s.pos >= s.data.len
proc ssSetPosition(s: Stream, pos: int) = proc ssSetPosition(s: Stream, pos: int) =
var s = StringStream(s) var s = StringStream(s)
s.pos = clamp(pos, 0, s.data.high) s.pos = clamp(pos, 0, s.data.high)
@ -288,17 +288,17 @@ proc ssGetPosition(s: Stream): int =
proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int = proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
var s = StringStream(s) var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos) result = min(bufLen, s.data.len - s.pos)
if result > 0: if result > 0:
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 = proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int =
var s = StringStream(s) var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos) result = min(bufLen, s.data.len - s.pos)
if result > 0: if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result) 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:
return return
@ -311,7 +311,7 @@ proc ssClose(s: Stream) =
var s = StringStream(s) var s = StringStream(s)
s.data = nil s.data = nil
proc newStringStream*(s: string = ""): StringStream = proc newStringStream*(s: string = ""): StringStream =
## creates a new stream from the string `s`. ## creates a new stream from the string `s`.
new(result) new(result)
result.data = s result.data = s
@ -343,7 +343,7 @@ 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 = proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
let pos = fsGetPosition(s) let pos = fsGetPosition(s)
defer: fsSetPosition(s, pos) defer: fsSetPosition(s, pos)
@ -383,14 +383,14 @@ else:
handle*: FileHandle handle*: FileHandle
pos: int pos: int
{.deprecated: [PFileHandleStream: FileHandleStream, {.deprecated: [PFileHandleStream: FileHandleStream,
TFileHandleStream: FileHandleStreamObj].} TFileHandleStream: FileHandleStreamObj].}
proc newEOS(msg: string): ref OSError = proc newEOS(msg: string): ref OSError =
new(result) new(result)
result.msg = msg result.msg = msg
proc hsGetPosition(s: FileHandleStream): int = proc hsGetPosition(s: FileHandleStream): int =
return s.pos return s.pos
when defined(windows): when defined(windows):
@ -398,30 +398,30 @@ else:
discard discard
else: else:
import posix import posix
proc hsSetPosition(s: FileHandleStream, pos: int) = proc hsSetPosition(s: FileHandleStream, pos: int) =
discard lseek(s.handle, pos, SEEK_SET) discard lseek(s.handle, pos, SEEK_SET)
proc hsClose(s: FileHandleStream) = discard close(s.handle) proc hsClose(s: FileHandleStream) = discard close(s.handle)
proc hsAtEnd(s: FileHandleStream): bool = proc hsAtEnd(s: FileHandleStream): bool =
var pos = hsGetPosition(s) var pos = hsGetPosition(s)
var theEnd = lseek(s.handle, 0, SEEK_END) var theEnd = lseek(s.handle, 0, SEEK_END)
result = pos >= theEnd result = pos >= theEnd
hsSetPosition(s, pos) # set position back 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) result = posix.read(s.handle, buffer, bufLen)
inc(s.pos, result) inc(s.pos, result)
proc hsPeekData(s: FileHandleStream, buffer: pointer, bufLen: int): int = proc hsPeekData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
result = posix.read(s.handle, buffer, bufLen) 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")
inc(s.pos, bufLen) inc(s.pos, bufLen)
proc newFileHandleStream*(handle: FileHandle): FileHandleStream = proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
new(result) new(result)
result.handle = handle result.handle = handle
result.pos = 0 result.pos = 0
@ -433,8 +433,8 @@ else:
result.peekData = hsPeekData result.peekData = hsPeekData
result.writeData = hsWriteData result.writeData = hsWriteData
proc newFileHandleStream*(filename: string, proc newFileHandleStream*(filename: string,
mode: FileMode): FileHandleStream = mode: FileMode): FileHandleStream =
when defined(windows): when defined(windows):
discard discard
else: else:
@ -458,3 +458,7 @@ when defined(testing):
assert(ss.getPosition == 5) # did move assert(ss.getPosition == 5) # did move
assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.")
assert(ss.getPosition == 5) # haven't moved 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