mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-02 11:12:37 +00:00
Note that contrary to what docgen.rst currently says, the ids have
to match exactly or else most web browsers will not jump to the
intended symbol.
(cherry picked from commit 93461aee34)
1342 lines
40 KiB
Nim
1342 lines
40 KiB
Nim
#
|
|
#
|
|
# Nim's Runtime Library
|
|
# (c) Copyright 2015 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module provides a stream interface and two implementations thereof:
|
|
## the `FileStream <#FileStream>`_ and the `StringStream <#StringStream>`_
|
|
## which implement the stream interface for Nim file objects (`File`) and
|
|
## strings.
|
|
##
|
|
## Other modules may provide other implementations for this standard
|
|
## stream interface.
|
|
##
|
|
## Basic usage
|
|
## ===========
|
|
##
|
|
## The basic flow of using this module is:
|
|
##
|
|
## 1. Open input stream
|
|
## 2. Read or write stream
|
|
## 3. Close stream
|
|
##
|
|
## StringStream example
|
|
## --------------------
|
|
##
|
|
## .. code-block:: Nim
|
|
##
|
|
## import streams
|
|
##
|
|
## var strm = newStringStream("""The first line
|
|
## the second line
|
|
## the third line""")
|
|
##
|
|
## var line = ""
|
|
##
|
|
## while strm.readLine(line):
|
|
## echo line
|
|
##
|
|
## # Output:
|
|
## # The first line
|
|
## # the second line
|
|
## # the third line
|
|
##
|
|
## strm.close()
|
|
##
|
|
## FileStream example
|
|
## ------------------
|
|
##
|
|
## Write file stream example:
|
|
##
|
|
## .. code-block:: Nim
|
|
##
|
|
## import streams
|
|
##
|
|
## var strm = newFileStream("somefile.txt", fmWrite)
|
|
## var line = ""
|
|
##
|
|
## if not isNil(strm):
|
|
## strm.writeLine("The first line")
|
|
## strm.writeLine("the second line")
|
|
## strm.writeLine("the third line")
|
|
## strm.close()
|
|
##
|
|
## # Output (somefile.txt):
|
|
## # The first line
|
|
## # the second line
|
|
## # the third line
|
|
##
|
|
## Read file stream example:
|
|
##
|
|
## .. code-block:: Nim
|
|
##
|
|
## import streams
|
|
##
|
|
## var strm = newFileStream("somefile.txt", fmRead)
|
|
## var line = ""
|
|
##
|
|
## if not isNil(strm):
|
|
## while strm.readLine(line):
|
|
## echo line
|
|
## strm.close()
|
|
##
|
|
## # Output:
|
|
## # The first line
|
|
## # the second line
|
|
## # the third line
|
|
##
|
|
## See also
|
|
## ========
|
|
## * `asyncstreams module <asyncstreams.html>`_
|
|
## * `io module <io.html>`_ for `FileMode enum <io.html#FileMode>`_
|
|
|
|
include "system/inclrtl"
|
|
|
|
proc newEIO(msg: string): owned(ref IOError) =
|
|
new(result)
|
|
result.msg = msg
|
|
|
|
type
|
|
Stream* = ref StreamObj
|
|
## All procedures of this module use this type.
|
|
## Procedures don't directly use `StreamObj <#StreamObj>`_.
|
|
StreamObj* = object of RootObj
|
|
## Stream interface that supports writing or reading.
|
|
##
|
|
## **Note:**
|
|
## * That these fields here shouldn't be used directly.
|
|
## They are accessible so that a stream implementation can override them.
|
|
closeImpl*: proc (s: Stream)
|
|
{.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
|
|
atEndImpl*: proc (s: Stream): bool
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
|
|
setPositionImpl*: proc (s: Stream, pos: int)
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
|
|
getPositionImpl*: proc (s: Stream): int
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
|
|
|
|
readDataStrImpl*: proc (s: Stream, buffer: var string, slice: Slice[int]): int
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
|
|
|
|
readLineImpl*: proc(s: Stream, line: var TaintedString): bool
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
|
|
|
|
readDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
|
|
peekDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
|
|
writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int)
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
|
|
|
|
flushImpl*: proc (s: Stream)
|
|
{.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
|
|
|
|
proc flush*(s: Stream) =
|
|
## Flushes the buffers that the stream `s` might use.
|
|
##
|
|
## This procedure causes any unwritten data for that stream to be delivered
|
|
## to the host environment to be written to the file.
|
|
##
|
|
## See also:
|
|
## * `close proc <#close,Stream>`_
|
|
runnableExamples:
|
|
from os import removeFile
|
|
|
|
var strm = newFileStream("somefile.txt", fmWrite)
|
|
|
|
doAssert "Before write:" & readFile("somefile.txt") == "Before write:"
|
|
strm.write("hello")
|
|
doAssert "After write:" & readFile("somefile.txt") == "After write:"
|
|
|
|
strm.flush()
|
|
doAssert "After flush:" & readFile("somefile.txt") == "After flush:hello"
|
|
strm.write("HELLO")
|
|
strm.flush()
|
|
doAssert "After flush:" & readFile("somefile.txt") == "After flush:helloHELLO"
|
|
|
|
strm.close()
|
|
doAssert "After close:" & readFile("somefile.txt") == "After close:helloHELLO"
|
|
removeFile("somefile.txt")
|
|
|
|
if not isNil(s.flushImpl): s.flushImpl(s)
|
|
|
|
proc close*(s: Stream) =
|
|
## Closes the stream `s`.
|
|
##
|
|
## See also:
|
|
## * `flush proc <#flush,Stream>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
## do something...
|
|
strm.close()
|
|
if not isNil(s.closeImpl): s.closeImpl(s)
|
|
|
|
proc atEnd*(s: Stream): bool =
|
|
## Checks if more data can be read from `s`. Returns ``true`` if all data has
|
|
## been read.
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
var line = ""
|
|
doAssert strm.atEnd() == false
|
|
while strm.readLine(line):
|
|
discard
|
|
doAssert strm.atEnd() == true
|
|
strm.close()
|
|
|
|
result = s.atEndImpl(s)
|
|
|
|
proc setPosition*(s: Stream, pos: int) =
|
|
## Sets the position `pos` of the stream `s`.
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
strm.setPosition(4)
|
|
doAssert strm.readLine() == "first line"
|
|
strm.setPosition(0)
|
|
doAssert strm.readLine() == "The first line"
|
|
strm.close()
|
|
|
|
s.setPositionImpl(s, pos)
|
|
|
|
proc getPosition*(s: Stream): int =
|
|
## Retrieves the current position in the stream `s`.
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
doAssert strm.getPosition() == 0
|
|
discard strm.readLine()
|
|
doAssert strm.getPosition() == 15
|
|
strm.close()
|
|
|
|
result = s.getPositionImpl(s)
|
|
|
|
proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
|
|
## Low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
|
runnableExamples:
|
|
var strm = newStringStream("abcde")
|
|
var buffer: array[6, char]
|
|
doAssert strm.readData(addr(buffer), 1024) == 5
|
|
doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
|
|
doAssert strm.atEnd() == true
|
|
strm.close()
|
|
|
|
result = s.readDataImpl(s, buffer, bufLen)
|
|
|
|
proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
|
|
## Low level proc that reads data into a string ``buffer`` at ``slice``.
|
|
runnableExamples:
|
|
var strm = newStringStream("abcde")
|
|
var buffer = "12345"
|
|
doAssert strm.readDataStr(buffer, 0..3) == 4
|
|
doAssert buffer == "abcd5"
|
|
strm.close()
|
|
|
|
if s.readDataStrImpl != nil:
|
|
result = s.readDataStrImpl(s, buffer, slice)
|
|
else:
|
|
# fallback
|
|
result = s.readData(addr buffer[0], buffer.len)
|
|
|
|
when not defined(js):
|
|
proc readAll*(s: Stream): string =
|
|
## Reads all available data.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
doAssert strm.readAll() == "The first line\nthe second line\nthe third line"
|
|
doAssert strm.atEnd() == true
|
|
strm.close()
|
|
|
|
const bufferSize = 1024
|
|
var buffer {.noinit.}: array[bufferSize, char]
|
|
while true:
|
|
let readBytes = readData(s, addr(buffer[0]), bufferSize)
|
|
if readBytes == 0:
|
|
break
|
|
let prevLen = result.len
|
|
result.setLen(prevLen + readBytes)
|
|
copyMem(addr(result[prevLen]), addr(buffer[0]), readBytes)
|
|
if readBytes < bufferSize:
|
|
break
|
|
|
|
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.
|
|
runnableExamples:
|
|
var strm = newStringStream("abcde")
|
|
var buffer: array[6, char]
|
|
doAssert strm.peekData(addr(buffer), 1024) == 5
|
|
doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
|
|
doAssert strm.atEnd() == false
|
|
strm.close()
|
|
|
|
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`.
|
|
runnableExamples:
|
|
## writeData
|
|
var strm = newStringStream("")
|
|
var buffer = ['a', 'b', 'c', 'd', 'e']
|
|
strm.writeData(addr(buffer), sizeof(buffer))
|
|
doAssert strm.atEnd() == true
|
|
## readData
|
|
strm.setPosition(0)
|
|
var buffer2: array[6, char]
|
|
doAssert strm.readData(addr(buffer2), sizeof(buffer2)) == 5
|
|
doAssert buffer2 == ['a', 'b', 'c', 'd', 'e', '\x00']
|
|
strm.close()
|
|
|
|
s.writeDataImpl(s, buffer, bufLen)
|
|
|
|
proc write*[T](s: Stream, x: T) =
|
|
## Generic write procedure. Writes `x` to the stream `s`. Implementation:
|
|
##
|
|
## .. code-block:: Nim
|
|
##
|
|
## s.writeData(s, addr(x), sizeof(x))
|
|
runnableExamples:
|
|
var strm = newStringStream("")
|
|
strm.write("abcde")
|
|
strm.setPosition(0)
|
|
doAssert strm.readAll() == "abcde"
|
|
strm.close()
|
|
|
|
var y: 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
|
|
## terminating zero is written.
|
|
runnableExamples:
|
|
var strm = newStringStream("")
|
|
strm.write("THE FIRST LINE")
|
|
strm.setPosition(0)
|
|
doAssert strm.readLine() == "THE FIRST LINE"
|
|
strm.close()
|
|
|
|
when nimvm:
|
|
writeData(s, cstring(x), x.len)
|
|
else:
|
|
if x.len > 0: writeData(s, cstring(x), x.len)
|
|
|
|
proc write*(s: Stream, args: varargs[string, `$`]) =
|
|
## Writes one or more strings to the the stream. No length fields or
|
|
## terminating zeros are written.
|
|
runnableExamples:
|
|
var strm = newStringStream("")
|
|
strm.write(1, 2, 3, 4)
|
|
strm.setPosition(0)
|
|
doAssert strm.readLine() == "1234"
|
|
strm.close()
|
|
|
|
for str in args: write(s, str)
|
|
|
|
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.
|
|
runnableExamples:
|
|
var strm = newStringStream("")
|
|
strm.writeLine(1, 2)
|
|
strm.writeLine(3, 4)
|
|
strm.setPosition(0)
|
|
doAssert strm.readAll() == "12\n34\n"
|
|
strm.close()
|
|
|
|
for str in args: write(s, str)
|
|
write(s, "\n")
|
|
|
|
proc read*[T](s: Stream, result: var T) =
|
|
## Generic read procedure. Reads `result` from the stream `s`.
|
|
runnableExamples:
|
|
var strm = newStringStream("012")
|
|
## readInt
|
|
var i: int8
|
|
strm.read(i)
|
|
doAssert i == 48
|
|
## readData
|
|
var buffer: array[2, char]
|
|
strm.read(buffer)
|
|
doAssert buffer == ['1', '2']
|
|
strm.close()
|
|
|
|
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`.
|
|
runnableExamples:
|
|
var strm = newStringStream("012")
|
|
## peekInt
|
|
var i: int8
|
|
strm.peek(i)
|
|
doAssert i == 48
|
|
## peekData
|
|
var buffer: array[2, char]
|
|
strm.peek(buffer)
|
|
doAssert buffer == ['0', '1']
|
|
strm.close()
|
|
|
|
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 `IOError` if an error occurred.
|
|
## Returns '\\0' as an EOF marker.
|
|
runnableExamples:
|
|
var strm = newStringStream("12\n3")
|
|
doAssert strm.readChar() == '1'
|
|
doAssert strm.readChar() == '2'
|
|
doAssert strm.readChar() == '\n'
|
|
doAssert strm.readChar() == '3'
|
|
doAssert strm.readChar() == '\x00'
|
|
strm.close()
|
|
|
|
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
|
|
|
proc peekChar*(s: Stream): char =
|
|
## Peeks a char from the stream `s`. Raises `IOError` if an error occurred.
|
|
## Returns '\\0' as an EOF marker.
|
|
runnableExamples:
|
|
var strm = newStringStream("12\n3")
|
|
doAssert strm.peekChar() == '1'
|
|
doAssert strm.peekChar() == '1'
|
|
discard strm.readAll()
|
|
doAssert strm.peekChar() == '\x00'
|
|
strm.close()
|
|
|
|
if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
|
|
|
proc readBool*(s: Stream): bool =
|
|
## Reads a bool from the stream `s`.
|
|
##
|
|
## A bool is one byte long and it is `true` for every non-zero
|
|
## (`0000_0000`) value.
|
|
## Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(true)
|
|
strm.write(false)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readBool() == true
|
|
doAssert strm.readBool() == false
|
|
doAssertRaises(IOError): discard strm.readBool()
|
|
strm.close()
|
|
|
|
var t: byte
|
|
read(s, t)
|
|
result = t != 0.byte
|
|
|
|
proc peekBool*(s: Stream): bool =
|
|
## Peeks a bool from the stream `s`.
|
|
##
|
|
## A bool is one byte long and it is `true` for every non-zero
|
|
## (`0000_0000`) value.
|
|
## Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(true)
|
|
strm.write(false)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekBool() == true
|
|
## not false
|
|
doAssert strm.peekBool() == true
|
|
doAssert strm.readBool() == true
|
|
doAssert strm.peekBool() == false
|
|
strm.close()
|
|
|
|
var t: byte
|
|
peek(s, t)
|
|
result = t != 0.byte
|
|
|
|
proc readInt8*(s: Stream): int8 =
|
|
## Reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i8)
|
|
strm.write(2'i8)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readInt8() == 1'i8
|
|
doAssert strm.readInt8() == 2'i8
|
|
doAssertRaises(IOError): discard strm.readInt8()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekInt8*(s: Stream): int8 =
|
|
## Peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i8)
|
|
strm.write(2'i8)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekInt8() == 1'i8
|
|
## not 2'i8
|
|
doAssert strm.peekInt8() == 1'i8
|
|
doAssert strm.readInt8() == 1'i8
|
|
doAssert strm.peekInt8() == 2'i8
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readInt16*(s: Stream): int16 =
|
|
## Reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i16)
|
|
strm.write(2'i16)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readInt16() == 1'i16
|
|
doAssert strm.readInt16() == 2'i16
|
|
doAssertRaises(IOError): discard strm.readInt16()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekInt16*(s: Stream): int16 =
|
|
## Peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i16)
|
|
strm.write(2'i16)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekInt16() == 1'i16
|
|
## not 2'i16
|
|
doAssert strm.peekInt16() == 1'i16
|
|
doAssert strm.readInt16() == 1'i16
|
|
doAssert strm.peekInt16() == 2'i16
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readInt32*(s: Stream): int32 =
|
|
## Reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i32)
|
|
strm.write(2'i32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readInt32() == 1'i32
|
|
doAssert strm.readInt32() == 2'i32
|
|
doAssertRaises(IOError): discard strm.readInt32()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekInt32*(s: Stream): int32 =
|
|
## Peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i32)
|
|
strm.write(2'i32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekInt32() == 1'i32
|
|
## not 2'i32
|
|
doAssert strm.peekInt32() == 1'i32
|
|
doAssert strm.readInt32() == 1'i32
|
|
doAssert strm.peekInt32() == 2'i32
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readInt64*(s: Stream): int64 =
|
|
## Reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i64)
|
|
strm.write(2'i64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readInt64() == 1'i64
|
|
doAssert strm.readInt64() == 2'i64
|
|
doAssertRaises(IOError): discard strm.readInt64()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekInt64*(s: Stream): int64 =
|
|
## Peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'i64)
|
|
strm.write(2'i64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekInt64() == 1'i64
|
|
## not 2'i64
|
|
doAssert strm.peekInt64() == 1'i64
|
|
doAssert strm.readInt64() == 1'i64
|
|
doAssert strm.peekInt64() == 2'i64
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readUint8*(s: Stream): uint8 =
|
|
## Reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u8)
|
|
strm.write(2'u8)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readUint8() == 1'u8
|
|
doAssert strm.readUint8() == 2'u8
|
|
doAssertRaises(IOError): discard strm.readUint8()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekUint8*(s: Stream): uint8 =
|
|
## Peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u8)
|
|
strm.write(2'u8)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekUint8() == 1'u8
|
|
## not 2'u8
|
|
doAssert strm.peekUint8() == 1'u8
|
|
doAssert strm.readUint8() == 1'u8
|
|
doAssert strm.peekUint8() == 2'u8
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readUint16*(s: Stream): uint16 =
|
|
## Reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u16)
|
|
strm.write(2'u16)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readUint16() == 1'u16
|
|
doAssert strm.readUint16() == 2'u16
|
|
doAssertRaises(IOError): discard strm.readUint16()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekUint16*(s: Stream): uint16 =
|
|
## Peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u16)
|
|
strm.write(2'u16)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekUint16() == 1'u16
|
|
## not 2'u16
|
|
doAssert strm.peekUint16() == 1'u16
|
|
doAssert strm.readUint16() == 1'u16
|
|
doAssert strm.peekUint16() == 2'u16
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readUint32*(s: Stream): uint32 =
|
|
## Reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u32)
|
|
strm.write(2'u32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
|
|
## get data
|
|
doAssert strm.readUint32() == 1'u32
|
|
doAssert strm.readUint32() == 2'u32
|
|
doAssertRaises(IOError): discard strm.readUint32()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekUint32*(s: Stream): uint32 =
|
|
## Peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u32)
|
|
strm.write(2'u32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekUint32() == 1'u32
|
|
## not 2'u32
|
|
doAssert strm.peekUint32() == 1'u32
|
|
doAssert strm.readUint32() == 1'u32
|
|
doAssert strm.peekUint32() == 2'u32
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readUint64*(s: Stream): uint64 =
|
|
## Reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u64)
|
|
strm.write(2'u64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readUint64() == 1'u64
|
|
doAssert strm.readUint64() == 2'u64
|
|
doAssertRaises(IOError): discard strm.readUint64()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekUint64*(s: Stream): uint64 =
|
|
## Peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'u64)
|
|
strm.write(2'u64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekUint64() == 1'u64
|
|
## not 2'u64
|
|
doAssert strm.peekUint64() == 1'u64
|
|
doAssert strm.readUint64() == 1'u64
|
|
doAssert strm.peekUint64() == 2'u64
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readFloat32*(s: Stream): float32 =
|
|
## Reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'f32)
|
|
strm.write(2'f32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readFloat32() == 1'f32
|
|
doAssert strm.readFloat32() == 2'f32
|
|
doAssertRaises(IOError): discard strm.readFloat32()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekFloat32*(s: Stream): float32 =
|
|
## Peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'f32)
|
|
strm.write(2'f32)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekFloat32() == 1'f32
|
|
## not 2'f32
|
|
doAssert strm.peekFloat32() == 1'f32
|
|
doAssert strm.readFloat32() == 1'f32
|
|
doAssert strm.peekFloat32() == 2'f32
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readFloat64*(s: Stream): float64 =
|
|
## Reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'f64)
|
|
strm.write(2'f64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.readFloat64() == 1'f64
|
|
doAssert strm.readFloat64() == 2'f64
|
|
doAssertRaises(IOError): discard strm.readFloat64()
|
|
strm.close()
|
|
|
|
read(s, result)
|
|
|
|
proc peekFloat64*(s: Stream): float64 =
|
|
## Peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream()
|
|
## setup for reading data
|
|
strm.write(1'f64)
|
|
strm.write(2'f64)
|
|
strm.flush()
|
|
strm.setPosition(0)
|
|
## get data
|
|
doAssert strm.peekFloat64() == 1'f64
|
|
## not 2'f64
|
|
doAssert strm.peekFloat64() == 1'f64
|
|
doAssert strm.readFloat64() == 1'f64
|
|
doAssert strm.peekFloat64() == 2'f64
|
|
strm.close()
|
|
|
|
peek(s, result)
|
|
|
|
proc readStr*(s: Stream, length: int): TaintedString =
|
|
## Reads a string of length `length` from the stream `s`. Raises `IOError` if
|
|
## an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream("abcde")
|
|
doAssert strm.readStr(2) == "ab"
|
|
doAssert strm.readStr(2) == "cd"
|
|
doAssert strm.readStr(2) == "e"
|
|
doAssert strm.readStr(2) == ""
|
|
strm.close()
|
|
|
|
result = newString(length).TaintedString
|
|
var L = readData(s, cstring(result), 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 `IOError` if
|
|
## an error occurred.
|
|
runnableExamples:
|
|
var strm = newStringStream("abcde")
|
|
doAssert strm.peekStr(2) == "ab"
|
|
## not "cd
|
|
doAssert strm.peekStr(2) == "ab"
|
|
doAssert strm.readStr(2) == "ab"
|
|
doAssert strm.peekStr(2) == "cd"
|
|
strm.close()
|
|
|
|
result = newString(length).TaintedString
|
|
var L = peekData(s, cstring(result), 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.
|
|
##
|
|
## A line of text may be delimited by ``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.
|
|
##
|
|
## See also:
|
|
## * `readLine(Stream) proc <#readLine,Stream>`_
|
|
## * `peekLine(Stream) proc <#peekLine,Stream>`_
|
|
## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
var line = ""
|
|
doAssert strm.readLine(line) == true
|
|
doAssert line == "The first line"
|
|
doAssert strm.readLine(line) == true
|
|
doAssert line == "the second line"
|
|
doAssert strm.readLine(line) == true
|
|
doAssert line == "the third line"
|
|
doAssert strm.readLine(line) == false
|
|
doAssert line == ""
|
|
strm.close()
|
|
|
|
if s.readLineImpl != nil:
|
|
result = s.readLineImpl(s, line)
|
|
else:
|
|
# fallback
|
|
line.string.setLen(0)
|
|
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: return false
|
|
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.
|
|
##
|
|
## See also:
|
|
## * `readLine(Stream) proc <#readLine,Stream>`_
|
|
## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
|
|
## * `peekLine(Stream) proc <#peekLine,Stream>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
var line = ""
|
|
doAssert strm.peekLine(line) == true
|
|
doAssert line == "The first line"
|
|
doAssert strm.peekLine(line) == true
|
|
## not "the second line"
|
|
doAssert line == "The first line"
|
|
doAssert strm.readLine(line) == true
|
|
doAssert line == "The first line"
|
|
doAssert strm.peekLine(line) == true
|
|
doAssert line == "the second line"
|
|
strm.close()
|
|
|
|
let pos = getPosition(s)
|
|
defer: setPosition(s, pos)
|
|
result = readLine(s, line)
|
|
|
|
proc readLine*(s: Stream): TaintedString =
|
|
## Reads a line from a stream `s`. Raises `IOError` if an error occurred.
|
|
##
|
|
## **Note:** This is not very efficient.
|
|
##
|
|
## See also:
|
|
## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
|
|
## * `peekLine(Stream) proc <#peekLine,Stream>`_
|
|
## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
doAssert strm.readLine() == "The first line"
|
|
doAssert strm.readLine() == "the second line"
|
|
doAssert strm.readLine() == "the third line"
|
|
doAssertRaises(IOError): discard strm.readLine()
|
|
strm.close()
|
|
|
|
result = TaintedString""
|
|
if s.atEnd:
|
|
raise newEIO("cannot read from stream")
|
|
while true:
|
|
var c = readChar(s)
|
|
if c == '\c':
|
|
c = readChar(s)
|
|
break
|
|
if c == '\L' or c == '\0':
|
|
break
|
|
else:
|
|
result.string.add(c)
|
|
|
|
proc peekLine*(s: Stream): TaintedString =
|
|
## Peeks a line from a stream `s`. Raises `IOError` if an error occurred.
|
|
##
|
|
## **Note:** This is not very efficient.
|
|
##
|
|
## See also:
|
|
## * `readLine(Stream) proc <#readLine,Stream>`_
|
|
## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
|
|
## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
doAssert strm.peekLine() == "The first line"
|
|
## not "the second line"
|
|
doAssert strm.peekLine() == "The first line"
|
|
doAssert strm.readLine() == "The first line"
|
|
doAssert strm.peekLine() == "the second line"
|
|
strm.close()
|
|
|
|
let pos = getPosition(s)
|
|
defer: setPosition(s, pos)
|
|
result = readLine(s)
|
|
|
|
iterator lines*(s: Stream): TaintedString =
|
|
## Iterates over every line in the stream.
|
|
## The iteration is based on ``readLine``.
|
|
##
|
|
## See also:
|
|
## * `readLine(Stream) proc <#readLine,Stream>`_
|
|
## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
var lines: seq[string]
|
|
for line in strm.lines():
|
|
lines.add line
|
|
doAssert lines == @["The first line", "the second line", "the third line"]
|
|
strm.close()
|
|
|
|
var line: TaintedString
|
|
while s.readLine(line):
|
|
yield line
|
|
|
|
when not defined(js):
|
|
|
|
type
|
|
StringStream* = ref StringStreamObj
|
|
## A stream that encapsulates a string.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
StringStreamObj* = object of StreamObj
|
|
## A string stream object.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
data*: string ## A string data.
|
|
## This is updated when called `writeLine` etc.
|
|
pos: int
|
|
|
|
proc ssAtEnd(s: Stream): bool =
|
|
var s = StringStream(s)
|
|
return s.pos >= s.data.len
|
|
|
|
proc ssSetPosition(s: Stream, pos: int) =
|
|
var s = StringStream(s)
|
|
s.pos = clamp(pos, 0, s.data.len)
|
|
|
|
proc ssGetPosition(s: Stream): int =
|
|
var s = StringStream(s)
|
|
return s.pos
|
|
|
|
proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
|
|
var s = StringStream(s)
|
|
result = min(slice.b + 1 - slice.a, s.data.len - s.pos)
|
|
if result > 0:
|
|
when nimvm:
|
|
for i in 0 ..< result: # sorry, but no fast string splicing on the vm.
|
|
buffer[slice.a + i] = s.data[s.pos + i]
|
|
else:
|
|
copyMem(unsafeAddr buffer[slice.a], addr s.data[s.pos], result)
|
|
inc(s.pos, result)
|
|
else:
|
|
result = 0
|
|
|
|
proc ssReadData(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)
|
|
inc(s.pos, result)
|
|
else:
|
|
result = 0
|
|
|
|
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)
|
|
else:
|
|
result = 0
|
|
|
|
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
|
var s = StringStream(s)
|
|
if bufLen <= 0:
|
|
return
|
|
if s.pos + bufLen > s.data.len:
|
|
setLen(s.data, s.pos + bufLen)
|
|
copyMem(addr(s.data[s.pos]), buffer, bufLen)
|
|
inc(s.pos, bufLen)
|
|
|
|
proc ssClose(s: Stream) =
|
|
var s = StringStream(s)
|
|
when defined(nimNoNilSeqs):
|
|
s.data = ""
|
|
else:
|
|
s.data = nil
|
|
|
|
proc newStringStream*(s: string = ""): owned StringStream =
|
|
## Creates a new stream from the string `s`.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
##
|
|
## See also:
|
|
## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
|
|
## opened File.
|
|
## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ creates a
|
|
## file stream from the file name and the mode.
|
|
## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
|
|
## file stream from the file name and the mode.
|
|
runnableExamples:
|
|
var strm = newStringStream("The first line\nthe second line\nthe third line")
|
|
doAssert strm.readLine() == "The first line"
|
|
doAssert strm.readLine() == "the second line"
|
|
doAssert strm.readLine() == "the third line"
|
|
strm.close()
|
|
|
|
new(result)
|
|
result.data = s
|
|
result.pos = 0
|
|
result.closeImpl = ssClose
|
|
result.atEndImpl = ssAtEnd
|
|
result.setPositionImpl = ssSetPosition
|
|
result.getPositionImpl = ssGetPosition
|
|
result.readDataImpl = ssReadData
|
|
result.peekDataImpl = ssPeekData
|
|
result.writeDataImpl = ssWriteData
|
|
result.readDataStrImpl = ssReadDataStr
|
|
|
|
type
|
|
FileStream* = ref FileStreamObj
|
|
## A stream that encapsulates a `File`.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
FileStreamObj* = object of Stream
|
|
## A file stream object.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
f: File
|
|
|
|
proc fsClose(s: Stream) =
|
|
if FileStream(s).f != nil:
|
|
close(FileStream(s).f)
|
|
FileStream(s).f = nil
|
|
proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
|
|
proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
|
|
proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
|
|
proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
|
|
|
|
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
|
result = readBuffer(FileStream(s).f, buffer, bufLen)
|
|
|
|
proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
|
|
result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a)
|
|
|
|
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")
|
|
|
|
proc fsReadLine(s: Stream, line: var TaintedString): bool =
|
|
result = readLine(FileStream(s).f, line)
|
|
|
|
proc newFileStream*(f: File): owned FileStream =
|
|
## Creates a new stream from the file `f`.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
##
|
|
## See also:
|
|
## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
|
|
## from string.
|
|
## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same
|
|
## as using `open proc <io.html#open,File,string,FileMode,int>`_
|
|
## on Examples.
|
|
## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
|
|
## file stream from the file name and the mode.
|
|
runnableExamples:
|
|
## Input (somefile.txt):
|
|
## The first line
|
|
## the second line
|
|
## the third line
|
|
var f: File
|
|
if open(f, "somefile.txt", fmRead, -1):
|
|
var strm = newFileStream(f)
|
|
var line = ""
|
|
while strm.readLine(line):
|
|
echo line
|
|
## Output:
|
|
## The first line
|
|
## the second line
|
|
## the third line
|
|
strm.close()
|
|
|
|
new(result)
|
|
result.f = f
|
|
result.closeImpl = fsClose
|
|
result.atEndImpl = fsAtEnd
|
|
result.setPositionImpl = fsSetPosition
|
|
result.getPositionImpl = fsGetPosition
|
|
result.readDataStrImpl = fsReadDataStr
|
|
result.readDataImpl = fsReadData
|
|
result.readLineImpl = fsReadLine
|
|
result.peekDataImpl = fsPeekData
|
|
result.writeDataImpl = fsWriteData
|
|
result.flushImpl = fsFlush
|
|
|
|
proc newFileStream*(filename: string, mode: FileMode = fmRead,
|
|
bufSize: int = -1): owned FileStream =
|
|
## Creates a new stream from the file named `filename` with the mode `mode`.
|
|
##
|
|
## If the file cannot be opened, `nil` is returned. See the `io module
|
|
## <io.html>`_ for a list of available `FileMode enums <io.html#FileMode>`_.
|
|
##
|
|
## **Note:**
|
|
## * **This function returns nil in case of failure.**
|
|
## To prevent unexpected behavior and ensure proper error handling,
|
|
## use `openFileStream proc <#openFileStream,string,FileMode,int>`_
|
|
## instead.
|
|
## * Not available for JS backend.
|
|
##
|
|
## See also:
|
|
## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
|
|
## from string.
|
|
## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
|
|
## opened File.
|
|
## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
|
|
## file stream from the file name and the mode.
|
|
runnableExamples:
|
|
from os import removeFile
|
|
var strm = newFileStream("somefile.txt", fmWrite)
|
|
if not isNil(strm):
|
|
strm.writeLine("The first line")
|
|
strm.writeLine("the second line")
|
|
strm.writeLine("the third line")
|
|
strm.close()
|
|
## Output (somefile.txt)
|
|
## The first line
|
|
## the second line
|
|
## the third line
|
|
removeFile("somefile.txt")
|
|
|
|
var f: File
|
|
if open(f, filename, mode, bufSize): result = newFileStream(f)
|
|
|
|
proc openFileStream*(filename: string, mode: FileMode = fmRead,
|
|
bufSize: int = -1): owned FileStream =
|
|
## Creates a new stream from the file named `filename` with the mode `mode`.
|
|
## If the file cannot be opened, an IO exception is raised.
|
|
##
|
|
## **Note:** Not available for JS backend.
|
|
##
|
|
## See also:
|
|
## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
|
|
## from string.
|
|
## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
|
|
## opened File.
|
|
## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ creates a
|
|
## file stream from the file name and the mode.
|
|
runnableExamples:
|
|
try:
|
|
## Input (somefile.txt):
|
|
## The first line
|
|
## the second line
|
|
## the third line
|
|
var strm = openFileStream("somefile.txt")
|
|
echo strm.readLine()
|
|
## Output:
|
|
## The first line
|
|
strm.close()
|
|
except:
|
|
stderr.write getCurrentExceptionMsg()
|
|
|
|
var f: File
|
|
if open(f, filename, mode, bufSize):
|
|
return newFileStream(f)
|
|
else:
|
|
raise newEIO("cannot open file stream: " & filename)
|
|
|
|
when false:
|
|
type
|
|
FileHandleStream* = ref FileHandleStreamObj
|
|
FileHandleStreamObj* = object of Stream
|
|
handle*: FileHandle
|
|
pos: int
|
|
|
|
proc newEOS(msg: string): ref OSError =
|
|
new(result)
|
|
result.msg = msg
|
|
|
|
proc hsGetPosition(s: FileHandleStream): int =
|
|
return s.pos
|
|
|
|
when defined(windows):
|
|
# do not import windows as this increases compile times:
|
|
discard
|
|
else:
|
|
import posix
|
|
|
|
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 =
|
|
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 =
|
|
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:
|
|
raise newEIO("cannot write to stream")
|
|
inc(s.pos, bufLen)
|
|
|
|
proc newFileHandleStream*(handle: FileHandle): owned FileHandleStream =
|
|
new(result)
|
|
result.handle = handle
|
|
result.pos = 0
|
|
result.close = hsClose
|
|
result.atEnd = hsAtEnd
|
|
result.setPosition = hsSetPosition
|
|
result.getPosition = hsGetPosition
|
|
result.readData = hsReadData
|
|
result.peekData = hsPeekData
|
|
result.writeData = hsWriteData
|
|
|
|
proc newFileHandleStream*(filename: string,
|
|
mode: FileMode): owned FileHandleStream =
|
|
when defined(windows):
|
|
discard
|
|
else:
|
|
var flags: cint
|
|
case mode
|
|
of fmRead: flags = posix.O_RDONLY
|
|
of fmWrite: flags = O_WRONLY or int(O_CREAT)
|
|
of fmReadWrite: flags = O_RDWR or int(O_CREAT)
|
|
of fmReadWriteExisting: flags = O_RDWR
|
|
of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND
|
|
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
|