1#
2#
3#            Nim's Runtime Library
4#        (c) Copyright 2015 Andreas Rumpf
5#
6#    See the file "copying.txt", included in this
7#    distribution, for details about the copyright.
8#
9
10## This module provides a stream interface and two implementations thereof:
11## the `FileStream <#FileStream>`_ and the `StringStream <#StringStream>`_
12## which implement the stream interface for Nim file objects (`File`) and
13## strings.
14##
15## Other modules may provide other implementations for this standard
16## stream interface.
17##
18## Basic usage
19## ===========
20##
21## The basic flow of using this module is:
22##
23## 1. Open input stream
24## 2. Read or write stream
25## 3. Close stream
26##
27## StringStream example
28## --------------------
29##
30## .. code-block:: Nim
31##
32##  import std/streams
33##
34##  var strm = newStringStream("""The first line
35##  the second line
36##  the third line""")
37##
38##  var line = ""
39##
40##  while strm.readLine(line):
41##    echo line
42##
43##  # Output:
44##  # The first line
45##  # the second line
46##  # the third line
47##
48##  strm.close()
49##
50## FileStream example
51## ------------------
52##
53## Write file stream example:
54##
55## .. code-block:: Nim
56##
57##  import std/streams
58##
59##  var strm = newFileStream("somefile.txt", fmWrite)
60##  var line = ""
61##
62##  if not isNil(strm):
63##    strm.writeLine("The first line")
64##    strm.writeLine("the second line")
65##    strm.writeLine("the third line")
66##    strm.close()
67##
68##  # Output (somefile.txt):
69##  # The first line
70##  # the second line
71##  # the third line
72##
73## Read file stream example:
74##
75## .. code-block:: Nim
76##
77##  import std/streams
78##
79##  var strm = newFileStream("somefile.txt", fmRead)
80##  var line = ""
81##
82##  if not isNil(strm):
83##    while strm.readLine(line):
84##      echo line
85##    strm.close()
86##
87##  # Output:
88##  # The first line
89##  # the second line
90##  # the third line
91##
92## See also
93## ========
94## * `asyncstreams module <asyncstreams.html>`_
95## * `io module <io.html>`_ for `FileMode enum <io.html#FileMode>`_
96
97import std/private/since
98
99proc newEIO(msg: string): owned(ref IOError) =
100  new(result)
101  result.msg = msg
102
103type
104  Stream* = ref StreamObj
105    ## All procedures of this module use this type.
106    ## Procedures don't directly use `StreamObj <#StreamObj>`_.
107  StreamObj* = object of RootObj
108    ## Stream interface that supports writing or reading.
109    ##
110    ## **Note:**
111    ## * That these fields here shouldn't be used directly.
112    ##   They are accessible so that a stream implementation can override them.
113    closeImpl*: proc (s: Stream)
114      {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
115    atEndImpl*: proc (s: Stream): bool
116      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
117    setPositionImpl*: proc (s: Stream, pos: int)
118      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
119    getPositionImpl*: proc (s: Stream): int
120      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
121
122    readDataStrImpl*: proc (s: Stream, buffer: var string, slice: Slice[int]): int
123      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
124
125    readLineImpl*: proc(s: Stream, line: var string): bool
126      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
127
128    readDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
129      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
130    peekDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
131      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
132    writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int)
133      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
134
135    flushImpl*: proc (s: Stream)
136      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
137
138proc flush*(s: Stream) =
139  ## Flushes the buffers that the stream `s` might use.
140  ##
141  ## This procedure causes any unwritten data for that stream to be delivered
142  ## to the host environment to be written to the file.
143  ##
144  ## See also:
145  ## * `close proc <#close,Stream>`_
146  runnableExamples:
147    from std/os import removeFile
148
149    var strm = newFileStream("somefile.txt", fmWrite)
150
151    doAssert "Before write:" & readFile("somefile.txt") == "Before write:"
152    strm.write("hello")
153    doAssert "After  write:" & readFile("somefile.txt") == "After  write:"
154
155    strm.flush()
156    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:hello"
157    strm.write("HELLO")
158    strm.flush()
159    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:helloHELLO"
160
161    strm.close()
162    doAssert "After  close:" & readFile("somefile.txt") == "After  close:helloHELLO"
163    removeFile("somefile.txt")
164
165  if not isNil(s.flushImpl): s.flushImpl(s)
166
167proc close*(s: Stream) =
168  ## Closes the stream `s`.
169  ##
170  ## See also:
171  ## * `flush proc <#flush,Stream>`_
172  runnableExamples:
173    var strm = newStringStream("The first line\nthe second line\nthe third line")
174    ## do something...
175    strm.close()
176  if not isNil(s.closeImpl): s.closeImpl(s)
177
178proc atEnd*(s: Stream): bool =
179  ## Checks if more data can be read from `s`. Returns ``true`` if all data has
180  ## been read.
181  runnableExamples:
182    var strm = newStringStream("The first line\nthe second line\nthe third line")
183    var line = ""
184    doAssert strm.atEnd() == false
185    while strm.readLine(line):
186      discard
187    doAssert strm.atEnd() == true
188    strm.close()
189
190  result = s.atEndImpl(s)
191
192proc setPosition*(s: Stream, pos: int) =
193  ## Sets the position `pos` of the stream `s`.
194  runnableExamples:
195    var strm = newStringStream("The first line\nthe second line\nthe third line")
196    strm.setPosition(4)
197    doAssert strm.readLine() == "first line"
198    strm.setPosition(0)
199    doAssert strm.readLine() == "The first line"
200    strm.close()
201
202  s.setPositionImpl(s, pos)
203
204proc getPosition*(s: Stream): int =
205  ## Retrieves the current position in the stream `s`.
206  runnableExamples:
207    var strm = newStringStream("The first line\nthe second line\nthe third line")
208    doAssert strm.getPosition() == 0
209    discard strm.readLine()
210    doAssert strm.getPosition() == 15
211    strm.close()
212
213  result = s.getPositionImpl(s)
214
215proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
216  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size.
217  ##
218  ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between
219  ## ``0..<bufLen``.
220  runnableExamples:
221    var strm = newStringStream("abcde")
222    var buffer: array[6, char]
223    doAssert strm.readData(addr(buffer), 1024) == 5
224    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
225    doAssert strm.atEnd() == true
226    strm.close()
227
228  result = s.readDataImpl(s, buffer, bufLen)
229
230proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
231  ## Low level proc that reads data into a string ``buffer`` at ``slice``.
232  runnableExamples:
233    var strm = newStringStream("abcde")
234    var buffer = "12345"
235    doAssert strm.readDataStr(buffer, 0..3) == 4
236    doAssert buffer == "abcd5"
237    strm.close()
238
239  if s.readDataStrImpl != nil:
240    result = s.readDataStrImpl(s, buffer, slice)
241  else:
242    # fallback
243    result = s.readData(addr buffer[slice.a], slice.b + 1 - slice.a)
244
245template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
246  when nimvm:
247    block:
248      caseJsOrVm
249  else:
250    block:
251      when defined(js) or defined(nimscript):
252        # nimscript has to be here to avoid semantic checking of caseElse
253        caseJsOrVm
254      else:
255        caseElse
256
257when (NimMajor, NimMinor) >= (1, 3) or not defined(js):
258  proc readAll*(s: Stream): string =
259    ## Reads all available data.
260    runnableExamples:
261      var strm = newStringStream("The first line\nthe second line\nthe third line")
262      doAssert strm.readAll() == "The first line\nthe second line\nthe third line"
263      doAssert strm.atEnd() == true
264      strm.close()
265
266    const bufferSize = 1024
267    jsOrVmBlock:
268      var buffer2: string
269      buffer2.setLen(bufferSize)
270      while true:
271        let readBytes = readDataStr(s, buffer2, 0..<bufferSize)
272        if readBytes == 0:
273          break
274        let prevLen = result.len
275        result.setLen(prevLen + readBytes)
276        result[prevLen..<prevLen+readBytes] = buffer2[0..<readBytes]
277        if readBytes < bufferSize:
278          break
279    do: # not JS or VM
280      var buffer {.noinit.}: array[bufferSize, char]
281      while true:
282        let readBytes = readData(s, addr(buffer[0]), bufferSize)
283        if readBytes == 0:
284          break
285        let prevLen = result.len
286        result.setLen(prevLen + readBytes)
287        copyMem(addr(result[prevLen]), addr(buffer[0]), readBytes)
288        if readBytes < bufferSize:
289          break
290
291proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
292  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size
293  ## without moving stream position.
294  ##
295  ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between
296  ## ``0..<bufLen``.
297  runnableExamples:
298    var strm = newStringStream("abcde")
299    var buffer: array[6, char]
300    doAssert strm.peekData(addr(buffer), 1024) == 5
301    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
302    doAssert strm.atEnd() == false
303    strm.close()
304
305  result = s.peekDataImpl(s, buffer, bufLen)
306
307proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
308  ## Low level proc that writes an untyped `buffer` of `bufLen` size
309  ## to the stream `s`.
310  ##
311  ## **JS note:** `buffer` is treated as a ``ptr string`` and read between
312  ## ``0..<bufLen``.
313  runnableExamples:
314    ## writeData
315    var strm = newStringStream("")
316    var buffer = ['a', 'b', 'c', 'd', 'e']
317    strm.writeData(addr(buffer), sizeof(buffer))
318    doAssert strm.atEnd() == true
319    ## readData
320    strm.setPosition(0)
321    var buffer2: array[6, char]
322    doAssert strm.readData(addr(buffer2), sizeof(buffer2)) == 5
323    doAssert buffer2 == ['a', 'b', 'c', 'd', 'e', '\x00']
324    strm.close()
325
326  s.writeDataImpl(s, buffer, bufLen)
327
328proc write*[T](s: Stream, x: T) =
329  ## Generic write procedure. Writes `x` to the stream `s`. Implementation:
330  ##
331  ## **Note:** Not available for JS backend. Use `write(Stream, string)
332  ## <#write,Stream,string>`_ for now.
333  ##
334  ## .. code-block:: Nim
335  ##
336  ##     s.writeData(s, unsafeAddr(x), sizeof(x))
337  runnableExamples:
338    var strm = newStringStream("")
339    strm.write("abcde")
340    strm.setPosition(0)
341    doAssert strm.readAll() == "abcde"
342    strm.close()
343
344  writeData(s, unsafeAddr(x), sizeof(x))
345
346proc write*(s: Stream, x: string) =
347  ## Writes the string `x` to the stream `s`. No length field or
348  ## terminating zero is written.
349  runnableExamples:
350    var strm = newStringStream("")
351    strm.write("THE FIRST LINE")
352    strm.setPosition(0)
353    doAssert strm.readLine() == "THE FIRST LINE"
354    strm.close()
355
356  when nimvm:
357    writeData(s, cstring(x), x.len)
358  else:
359    if x.len > 0:
360      when defined(js):
361        var x = x
362        writeData(s, addr(x), x.len)
363      else:
364        writeData(s, cstring(x), x.len)
365
366proc write*(s: Stream, args: varargs[string, `$`]) =
367  ## Writes one or more strings to the the stream. No length fields or
368  ## terminating zeros are written.
369  runnableExamples:
370    var strm = newStringStream("")
371    strm.write(1, 2, 3, 4)
372    strm.setPosition(0)
373    doAssert strm.readLine() == "1234"
374    strm.close()
375
376  for str in args: write(s, str)
377
378proc writeLine*(s: Stream, args: varargs[string, `$`]) =
379  ## Writes one or more strings to the the stream `s` followed
380  ## by a new line. No length field or terminating zero is written.
381  runnableExamples:
382    var strm = newStringStream("")
383    strm.writeLine(1, 2)
384    strm.writeLine(3, 4)
385    strm.setPosition(0)
386    doAssert strm.readAll() == "12\n34\n"
387    strm.close()
388
389  for str in args: write(s, str)
390  write(s, "\n")
391
392proc read*[T](s: Stream, result: var T) =
393  ## Generic read procedure. Reads `result` from the stream `s`.
394  ##
395  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
396  runnableExamples:
397    var strm = newStringStream("012")
398    ## readInt
399    var i: int8
400    strm.read(i)
401    doAssert i == 48
402    ## readData
403    var buffer: array[2, char]
404    strm.read(buffer)
405    doAssert buffer == ['1', '2']
406    strm.close()
407
408  if readData(s, addr(result), sizeof(T)) != sizeof(T):
409    raise newEIO("cannot read from stream")
410
411proc peek*[T](s: Stream, result: var T) =
412  ## Generic peek procedure. Peeks `result` from the stream `s`.
413  ##
414  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
415  runnableExamples:
416    var strm = newStringStream("012")
417    ## peekInt
418    var i: int8
419    strm.peek(i)
420    doAssert i == 48
421    ## peekData
422    var buffer: array[2, char]
423    strm.peek(buffer)
424    doAssert buffer == ['0', '1']
425    strm.close()
426
427  if peekData(s, addr(result), sizeof(T)) != sizeof(T):
428    raise newEIO("cannot read from stream")
429
430proc readChar*(s: Stream): char =
431  ## Reads a char from the stream `s`.
432  ##
433  ## Raises `IOError` if an error occurred.
434  ## Returns '\\0' as an EOF marker.
435  runnableExamples:
436    var strm = newStringStream("12\n3")
437    doAssert strm.readChar() == '1'
438    doAssert strm.readChar() == '2'
439    doAssert strm.readChar() == '\n'
440    doAssert strm.readChar() == '3'
441    doAssert strm.readChar() == '\x00'
442    strm.close()
443
444  jsOrVmBlock:
445    var str = " "
446    if readDataStr(s, str, 0..0) != 1: result = '\0'
447    else: result = str[0]
448  do:
449    if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
450
451proc peekChar*(s: Stream): char =
452  ## Peeks a char from the stream `s`. Raises `IOError` if an error occurred.
453  ## Returns '\\0' as an EOF marker.
454  runnableExamples:
455    var strm = newStringStream("12\n3")
456    doAssert strm.peekChar() == '1'
457    doAssert strm.peekChar() == '1'
458    discard strm.readAll()
459    doAssert strm.peekChar() == '\x00'
460    strm.close()
461
462  when defined(js):
463    var str = " "
464    if peekData(s, addr(str), sizeof(result)) != 1: result = '\0'
465    else: result = str[0]
466  else:
467    if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
468
469proc readBool*(s: Stream): bool =
470  ## Reads a bool from the stream `s`.
471  ##
472  ## A bool is one byte long and it is `true` for every non-zero
473  ## (`0000_0000`) value.
474  ## Raises `IOError` if an error occurred.
475  ##
476  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
477  runnableExamples:
478    var strm = newStringStream()
479    ## setup for reading data
480    strm.write(true)
481    strm.write(false)
482    strm.flush()
483    strm.setPosition(0)
484    ## get data
485    doAssert strm.readBool() == true
486    doAssert strm.readBool() == false
487    doAssertRaises(IOError): discard strm.readBool()
488    strm.close()
489
490  var t: byte
491  read(s, t)
492  result = t != 0.byte
493
494proc peekBool*(s: Stream): bool =
495  ## Peeks a bool from the stream `s`.
496  ##
497  ## A bool is one byte long and it is `true` for every non-zero
498  ## (`0000_0000`) value.
499  ## Raises `IOError` if an error occurred.
500  ##
501  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
502  runnableExamples:
503    var strm = newStringStream()
504    ## setup for reading data
505    strm.write(true)
506    strm.write(false)
507    strm.flush()
508    strm.setPosition(0)
509    ## get data
510    doAssert strm.peekBool() == true
511    ## not false
512    doAssert strm.peekBool() == true
513    doAssert strm.readBool() == true
514    doAssert strm.peekBool() == false
515    strm.close()
516
517  var t: byte
518  peek(s, t)
519  result = t != 0.byte
520
521proc readInt8*(s: Stream): int8 =
522  ## Reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
523  ##
524  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
525  runnableExamples:
526    var strm = newStringStream()
527    ## setup for reading data
528    strm.write(1'i8)
529    strm.write(2'i8)
530    strm.flush()
531    strm.setPosition(0)
532    ## get data
533    doAssert strm.readInt8() == 1'i8
534    doAssert strm.readInt8() == 2'i8
535    doAssertRaises(IOError): discard strm.readInt8()
536    strm.close()
537
538  read(s, result)
539
540proc peekInt8*(s: Stream): int8 =
541  ## Peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
542  ##
543  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
544  runnableExamples:
545    var strm = newStringStream()
546    ## setup for reading data
547    strm.write(1'i8)
548    strm.write(2'i8)
549    strm.flush()
550    strm.setPosition(0)
551    ## get data
552    doAssert strm.peekInt8() == 1'i8
553    ## not 2'i8
554    doAssert strm.peekInt8() == 1'i8
555    doAssert strm.readInt8() == 1'i8
556    doAssert strm.peekInt8() == 2'i8
557    strm.close()
558
559  peek(s, result)
560
561proc readInt16*(s: Stream): int16 =
562  ## Reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
563  ##
564  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
565  runnableExamples:
566    var strm = newStringStream()
567    ## setup for reading data
568    strm.write(1'i16)
569    strm.write(2'i16)
570    strm.flush()
571    strm.setPosition(0)
572    ## get data
573    doAssert strm.readInt16() == 1'i16
574    doAssert strm.readInt16() == 2'i16
575    doAssertRaises(IOError): discard strm.readInt16()
576    strm.close()
577
578  read(s, result)
579
580proc peekInt16*(s: Stream): int16 =
581  ## Peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
582  ##
583  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
584  runnableExamples:
585    var strm = newStringStream()
586    ## setup for reading data
587    strm.write(1'i16)
588    strm.write(2'i16)
589    strm.flush()
590    strm.setPosition(0)
591    ## get data
592    doAssert strm.peekInt16() == 1'i16
593    ## not 2'i16
594    doAssert strm.peekInt16() == 1'i16
595    doAssert strm.readInt16() == 1'i16
596    doAssert strm.peekInt16() == 2'i16
597    strm.close()
598
599  peek(s, result)
600
601proc readInt32*(s: Stream): int32 =
602  ## Reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
603  ##
604  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
605  runnableExamples:
606    var strm = newStringStream()
607    ## setup for reading data
608    strm.write(1'i32)
609    strm.write(2'i32)
610    strm.flush()
611    strm.setPosition(0)
612    ## get data
613    doAssert strm.readInt32() == 1'i32
614    doAssert strm.readInt32() == 2'i32
615    doAssertRaises(IOError): discard strm.readInt32()
616    strm.close()
617
618  read(s, result)
619
620proc peekInt32*(s: Stream): int32 =
621  ## Peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
622  ##
623  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
624  runnableExamples:
625    var strm = newStringStream()
626    ## setup for reading data
627    strm.write(1'i32)
628    strm.write(2'i32)
629    strm.flush()
630    strm.setPosition(0)
631    ## get data
632    doAssert strm.peekInt32() == 1'i32
633    ## not 2'i32
634    doAssert strm.peekInt32() == 1'i32
635    doAssert strm.readInt32() == 1'i32
636    doAssert strm.peekInt32() == 2'i32
637    strm.close()
638
639  peek(s, result)
640
641proc readInt64*(s: Stream): int64 =
642  ## Reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
643  ##
644  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
645  runnableExamples:
646    var strm = newStringStream()
647    ## setup for reading data
648    strm.write(1'i64)
649    strm.write(2'i64)
650    strm.flush()
651    strm.setPosition(0)
652    ## get data
653    doAssert strm.readInt64() == 1'i64
654    doAssert strm.readInt64() == 2'i64
655    doAssertRaises(IOError): discard strm.readInt64()
656    strm.close()
657
658  read(s, result)
659
660proc peekInt64*(s: Stream): int64 =
661  ## Peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
662  ##
663  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
664  runnableExamples:
665    var strm = newStringStream()
666    ## setup for reading data
667    strm.write(1'i64)
668    strm.write(2'i64)
669    strm.flush()
670    strm.setPosition(0)
671    ## get data
672    doAssert strm.peekInt64() == 1'i64
673    ## not 2'i64
674    doAssert strm.peekInt64() == 1'i64
675    doAssert strm.readInt64() == 1'i64
676    doAssert strm.peekInt64() == 2'i64
677    strm.close()
678
679  peek(s, result)
680
681proc readUint8*(s: Stream): uint8 =
682  ## Reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
683  ##
684  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
685  runnableExamples:
686    var strm = newStringStream()
687    ## setup for reading data
688    strm.write(1'u8)
689    strm.write(2'u8)
690    strm.flush()
691    strm.setPosition(0)
692    ## get data
693    doAssert strm.readUint8() == 1'u8
694    doAssert strm.readUint8() == 2'u8
695    doAssertRaises(IOError): discard strm.readUint8()
696    strm.close()
697
698  read(s, result)
699
700proc peekUint8*(s: Stream): uint8 =
701  ## Peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
702  ##
703  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
704  runnableExamples:
705    var strm = newStringStream()
706    ## setup for reading data
707    strm.write(1'u8)
708    strm.write(2'u8)
709    strm.flush()
710    strm.setPosition(0)
711    ## get data
712    doAssert strm.peekUint8() == 1'u8
713    ## not 2'u8
714    doAssert strm.peekUint8() == 1'u8
715    doAssert strm.readUint8() == 1'u8
716    doAssert strm.peekUint8() == 2'u8
717    strm.close()
718
719  peek(s, result)
720
721proc readUint16*(s: Stream): uint16 =
722  ## Reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
723  ##
724  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
725  runnableExamples:
726    var strm = newStringStream()
727    ## setup for reading data
728    strm.write(1'u16)
729    strm.write(2'u16)
730    strm.flush()
731    strm.setPosition(0)
732    ## get data
733    doAssert strm.readUint16() == 1'u16
734    doAssert strm.readUint16() == 2'u16
735    doAssertRaises(IOError): discard strm.readUint16()
736    strm.close()
737
738  read(s, result)
739
740proc peekUint16*(s: Stream): uint16 =
741  ## Peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
742  ##
743  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
744  runnableExamples:
745    var strm = newStringStream()
746    ## setup for reading data
747    strm.write(1'u16)
748    strm.write(2'u16)
749    strm.flush()
750    strm.setPosition(0)
751    ## get data
752    doAssert strm.peekUint16() == 1'u16
753    ## not 2'u16
754    doAssert strm.peekUint16() == 1'u16
755    doAssert strm.readUint16() == 1'u16
756    doAssert strm.peekUint16() == 2'u16
757    strm.close()
758
759  peek(s, result)
760
761proc readUint32*(s: Stream): uint32 =
762  ## Reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
763  ##
764  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
765  runnableExamples:
766    var strm = newStringStream()
767    ## setup for reading data
768    strm.write(1'u32)
769    strm.write(2'u32)
770    strm.flush()
771    strm.setPosition(0)
772
773    ## get data
774    doAssert strm.readUint32() == 1'u32
775    doAssert strm.readUint32() == 2'u32
776    doAssertRaises(IOError): discard strm.readUint32()
777    strm.close()
778
779  read(s, result)
780
781proc peekUint32*(s: Stream): uint32 =
782  ## Peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
783  ##
784  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
785  runnableExamples:
786    var strm = newStringStream()
787    ## setup for reading data
788    strm.write(1'u32)
789    strm.write(2'u32)
790    strm.flush()
791    strm.setPosition(0)
792    ## get data
793    doAssert strm.peekUint32() == 1'u32
794    ## not 2'u32
795    doAssert strm.peekUint32() == 1'u32
796    doAssert strm.readUint32() == 1'u32
797    doAssert strm.peekUint32() == 2'u32
798    strm.close()
799
800  peek(s, result)
801
802proc readUint64*(s: Stream): uint64 =
803  ## Reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
804  ##
805  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
806  runnableExamples:
807    var strm = newStringStream()
808    ## setup for reading data
809    strm.write(1'u64)
810    strm.write(2'u64)
811    strm.flush()
812    strm.setPosition(0)
813    ## get data
814    doAssert strm.readUint64() == 1'u64
815    doAssert strm.readUint64() == 2'u64
816    doAssertRaises(IOError): discard strm.readUint64()
817    strm.close()
818
819  read(s, result)
820
821proc peekUint64*(s: Stream): uint64 =
822  ## Peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
823  ##
824  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
825  runnableExamples:
826    var strm = newStringStream()
827    ## setup for reading data
828    strm.write(1'u64)
829    strm.write(2'u64)
830    strm.flush()
831    strm.setPosition(0)
832    ## get data
833    doAssert strm.peekUint64() == 1'u64
834    ## not 2'u64
835    doAssert strm.peekUint64() == 1'u64
836    doAssert strm.readUint64() == 1'u64
837    doAssert strm.peekUint64() == 2'u64
838    strm.close()
839
840  peek(s, result)
841
842proc readFloat32*(s: Stream): float32 =
843  ## Reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
844  ##
845  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
846  runnableExamples:
847    var strm = newStringStream()
848    ## setup for reading data
849    strm.write(1'f32)
850    strm.write(2'f32)
851    strm.flush()
852    strm.setPosition(0)
853    ## get data
854    doAssert strm.readFloat32() == 1'f32
855    doAssert strm.readFloat32() == 2'f32
856    doAssertRaises(IOError): discard strm.readFloat32()
857    strm.close()
858
859  read(s, result)
860
861proc peekFloat32*(s: Stream): float32 =
862  ## Peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
863  ##
864  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
865  runnableExamples:
866    var strm = newStringStream()
867    ## setup for reading data
868    strm.write(1'f32)
869    strm.write(2'f32)
870    strm.flush()
871    strm.setPosition(0)
872    ## get data
873    doAssert strm.peekFloat32() == 1'f32
874    ## not 2'f32
875    doAssert strm.peekFloat32() == 1'f32
876    doAssert strm.readFloat32() == 1'f32
877    doAssert strm.peekFloat32() == 2'f32
878    strm.close()
879
880  peek(s, result)
881
882proc readFloat64*(s: Stream): float64 =
883  ## Reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
884  ##
885  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
886  runnableExamples:
887    var strm = newStringStream()
888    ## setup for reading data
889    strm.write(1'f64)
890    strm.write(2'f64)
891    strm.flush()
892    strm.setPosition(0)
893    ## get data
894    doAssert strm.readFloat64() == 1'f64
895    doAssert strm.readFloat64() == 2'f64
896    doAssertRaises(IOError): discard strm.readFloat64()
897    strm.close()
898
899  read(s, result)
900
901proc peekFloat64*(s: Stream): float64 =
902  ## Peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
903  ##
904  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
905  runnableExamples:
906    var strm = newStringStream()
907    ## setup for reading data
908    strm.write(1'f64)
909    strm.write(2'f64)
910    strm.flush()
911    strm.setPosition(0)
912    ## get data
913    doAssert strm.peekFloat64() == 1'f64
914    ## not 2'f64
915    doAssert strm.peekFloat64() == 1'f64
916    doAssert strm.readFloat64() == 1'f64
917    doAssert strm.peekFloat64() == 2'f64
918    strm.close()
919
920  peek(s, result)
921
922proc readStrPrivate(s: Stream, length: int, str: var string) =
923  if length > len(str): setLen(str, length)
924  when defined(js):
925    let L = readData(s, addr(str), length)
926  else:
927    let L = readData(s, cstring(str), length)
928  if L != len(str): setLen(str, L)
929
930proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
931  ## Reads a string of length `length` from the stream `s`. Raises `IOError` if
932  ## an error occurred.
933  readStrPrivate(s, length, str)
934
935proc readStr*(s: Stream, length: int): string =
936  ## Reads a string of length `length` from the stream `s`. Raises `IOError` if
937  ## an error occurred.
938  runnableExamples:
939    var strm = newStringStream("abcde")
940    doAssert strm.readStr(2) == "ab"
941    doAssert strm.readStr(2) == "cd"
942    doAssert strm.readStr(2) == "e"
943    doAssert strm.readStr(2) == ""
944    strm.close()
945  result = newString(length)
946  readStrPrivate(s, length, result)
947
948proc peekStrPrivate(s: Stream, length: int, str: var string) =
949  if length > len(str): setLen(str, length)
950  when defined(js):
951    let L = peekData(s, addr(str), length)
952  else:
953    let L = peekData(s, cstring(str), length)
954  if L != len(str): setLen(str, L)
955
956proc peekStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
957  ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if
958  ## an error occurred.
959  peekStrPrivate(s, length, str)
960
961proc peekStr*(s: Stream, length: int): string =
962  ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if
963  ## an error occurred.
964  runnableExamples:
965    var strm = newStringStream("abcde")
966    doAssert strm.peekStr(2) == "ab"
967    ## not "cd
968    doAssert strm.peekStr(2) == "ab"
969    doAssert strm.readStr(2) == "ab"
970    doAssert strm.peekStr(2) == "cd"
971    strm.close()
972  result = newString(length)
973  peekStrPrivate(s, length, result)
974
975proc readLine*(s: Stream, line: var string): bool =
976  ## Reads a line of text from the stream `s` into `line`. `line` must not be
977  ## ``nil``! May throw an IO exception.
978  ##
979  ## A line of text may be delimited by ``LF`` or ``CRLF``.
980  ## The newline character(s) are not part of the returned string.
981  ## Returns ``false`` if the end of the file has been reached, ``true``
982  ## otherwise. If ``false`` is returned `line` contains no new data.
983  ##
984  ## See also:
985  ## * `readLine(Stream) proc <#readLine,Stream>`_
986  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
987  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
988  runnableExamples:
989    var strm = newStringStream("The first line\nthe second line\nthe third line")
990    var line = ""
991    doAssert strm.readLine(line) == true
992    doAssert line == "The first line"
993    doAssert strm.readLine(line) == true
994    doAssert line == "the second line"
995    doAssert strm.readLine(line) == true
996    doAssert line == "the third line"
997    doAssert strm.readLine(line) == false
998    doAssert line == ""
999    strm.close()
1000
1001  if s.readLineImpl != nil:
1002    result = s.readLineImpl(s, line)
1003  else:
1004    # fallback
1005    line.setLen(0)
1006    while true:
1007      var c = readChar(s)
1008      if c == '\c':
1009        c = readChar(s)
1010        break
1011      elif c == '\L': break
1012      elif c == '\0':
1013        if line.len > 0: break
1014        else: return false
1015      line.add(c)
1016    result = true
1017
1018proc peekLine*(s: Stream, line: var string): bool =
1019  ## Peeks a line of text from the stream `s` into `line`. `line` must not be
1020  ## ``nil``! May throw an IO exception.
1021  ##
1022  ## A line of text may be delimited by ``CR``, ``LF`` or
1023  ## ``CRLF``. The newline character(s) are not part of the returned string.
1024  ## Returns ``false`` if the end of the file has been reached, ``true``
1025  ## otherwise. If ``false`` is returned `line` contains no new data.
1026  ##
1027  ## See also:
1028  ## * `readLine(Stream) proc <#readLine,Stream>`_
1029  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
1030  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
1031  runnableExamples:
1032    var strm = newStringStream("The first line\nthe second line\nthe third line")
1033    var line = ""
1034    doAssert strm.peekLine(line) == true
1035    doAssert line == "The first line"
1036    doAssert strm.peekLine(line) == true
1037    ## not "the second line"
1038    doAssert line == "The first line"
1039    doAssert strm.readLine(line) == true
1040    doAssert line == "The first line"
1041    doAssert strm.peekLine(line) == true
1042    doAssert line == "the second line"
1043    strm.close()
1044
1045  let pos = getPosition(s)
1046  defer: setPosition(s, pos)
1047  result = readLine(s, line)
1048
1049proc readLine*(s: Stream): string =
1050  ## Reads a line from a stream `s`. Raises `IOError` if an error occurred.
1051  ##
1052  ## **Note:** This is not very efficient.
1053  ##
1054  ## See also:
1055  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
1056  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
1057  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
1058  runnableExamples:
1059    var strm = newStringStream("The first line\nthe second line\nthe third line")
1060    doAssert strm.readLine() == "The first line"
1061    doAssert strm.readLine() == "the second line"
1062    doAssert strm.readLine() == "the third line"
1063    doAssertRaises(IOError): discard strm.readLine()
1064    strm.close()
1065
1066  result = ""
1067  if s.atEnd:
1068    raise newEIO("cannot read from stream")
1069  while true:
1070    var c = readChar(s)
1071    if c == '\c':
1072      c = readChar(s)
1073      break
1074    if c == '\L' or c == '\0':
1075      break
1076    else:
1077      result.add(c)
1078
1079proc peekLine*(s: Stream): string =
1080  ## Peeks a line from a stream `s`. Raises `IOError` if an error occurred.
1081  ##
1082  ## **Note:** This is not very efficient.
1083  ##
1084  ## See also:
1085  ## * `readLine(Stream) proc <#readLine,Stream>`_
1086  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
1087  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
1088  runnableExamples:
1089    var strm = newStringStream("The first line\nthe second line\nthe third line")
1090    doAssert strm.peekLine() == "The first line"
1091    ## not "the second line"
1092    doAssert strm.peekLine() == "The first line"
1093    doAssert strm.readLine() == "The first line"
1094    doAssert strm.peekLine() == "the second line"
1095    strm.close()
1096
1097  let pos = getPosition(s)
1098  defer: setPosition(s, pos)
1099  result = readLine(s)
1100
1101iterator lines*(s: Stream): string =
1102  ## Iterates over every line in the stream.
1103  ## The iteration is based on ``readLine``.
1104  ##
1105  ## See also:
1106  ## * `readLine(Stream) proc <#readLine,Stream>`_
1107  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
1108  runnableExamples:
1109    var strm = newStringStream("The first line\nthe second line\nthe third line")
1110    var lines: seq[string]
1111    for line in strm.lines():
1112      lines.add line
1113    doAssert lines == @["The first line", "the second line", "the third line"]
1114    strm.close()
1115
1116  var line: string
1117  while s.readLine(line):
1118    yield line
1119
1120type
1121  StringStream* = ref StringStreamObj
1122    ## A stream that encapsulates a string.
1123  StringStreamObj* = object of StreamObj
1124    ## A string stream object.
1125    data*: string ## A string data.
1126                  ## This is updated when called `writeLine` etc.
1127    pos: int
1128
1129when (NimMajor, NimMinor) < (1, 3) and defined(js):
1130  proc ssAtEnd(s: Stream): bool {.compileTime.} =
1131    var s = StringStream(s)
1132    return s.pos >= s.data.len
1133
1134  proc ssSetPosition(s: Stream, pos: int) {.compileTime.} =
1135    var s = StringStream(s)
1136    s.pos = clamp(pos, 0, s.data.len)
1137
1138  proc ssGetPosition(s: Stream): int {.compileTime.} =
1139    var s = StringStream(s)
1140    return s.pos
1141
1142  proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int {.compileTime.} =
1143    var s = StringStream(s)
1144    result = min(slice.b + 1 - slice.a, s.data.len - s.pos)
1145    if result > 0:
1146      buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
1147      inc(s.pos, result)
1148    else:
1149      result = 0
1150
1151  proc ssClose(s: Stream) {.compileTime.} =
1152    var s = StringStream(s)
1153    s.data = ""
1154
1155  proc newStringStream*(s: string = ""): owned StringStream {.compileTime.} =
1156    new(result)
1157    result.data = s
1158    result.pos = 0
1159    result.closeImpl = ssClose
1160    result.atEndImpl = ssAtEnd
1161    result.setPositionImpl = ssSetPosition
1162    result.getPositionImpl = ssGetPosition
1163    result.readDataStrImpl = ssReadDataStr
1164
1165  proc readAll*(s: Stream): string {.compileTime.} =
1166    const bufferSize = 1024
1167    var bufferr: string
1168    bufferr.setLen(bufferSize)
1169    while true:
1170      let readBytes = readDataStr(s, bufferr, 0..<bufferSize)
1171      if readBytes == 0:
1172        break
1173      let prevLen = result.len
1174      result.setLen(prevLen + readBytes)
1175      result[prevLen..<prevLen+readBytes] = bufferr[0..<readBytes]
1176      if readBytes < bufferSize:
1177        break
1178
1179else: # after 1.3 or JS not defined
1180  proc ssAtEnd(s: Stream): bool =
1181    var s = StringStream(s)
1182    return s.pos >= s.data.len
1183
1184  proc ssSetPosition(s: Stream, pos: int) =
1185    var s = StringStream(s)
1186    s.pos = clamp(pos, 0, s.data.len)
1187
1188  proc ssGetPosition(s: Stream): int =
1189    var s = StringStream(s)
1190    return s.pos
1191
1192  proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
1193    var s = StringStream(s)
1194    result = min(slice.b + 1 - slice.a, s.data.len - s.pos)
1195    if result > 0:
1196      jsOrVmBlock:
1197        buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
1198      do:
1199        copyMem(unsafeAddr buffer[slice.a], addr s.data[s.pos], result)
1200      inc(s.pos, result)
1201    else:
1202      result = 0
1203
1204  proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
1205    var s = StringStream(s)
1206    result = min(bufLen, s.data.len - s.pos)
1207    if result > 0:
1208      when defined(js):
1209        try:
1210          cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
1211        except:
1212          raise newException(Defect, "could not read string stream, " &
1213            "did you use a non-string buffer pointer?", getCurrentException())
1214      elif not defined(nimscript):
1215        copyMem(buffer, addr(s.data[s.pos]), result)
1216      inc(s.pos, result)
1217    else:
1218      result = 0
1219
1220  proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int =
1221    var s = StringStream(s)
1222    result = min(bufLen, s.data.len - s.pos)
1223    if result > 0:
1224      when defined(js):
1225        try:
1226          cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
1227        except:
1228          raise newException(Defect, "could not peek string stream, " &
1229            "did you use a non-string buffer pointer?", getCurrentException())
1230      elif not defined(nimscript):
1231        copyMem(buffer, addr(s.data[s.pos]), result)
1232    else:
1233      result = 0
1234
1235  proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
1236    var s = StringStream(s)
1237    if bufLen <= 0:
1238      return
1239    if s.pos + bufLen > s.data.len:
1240      setLen(s.data, s.pos + bufLen)
1241    when defined(js):
1242      try:
1243        s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
1244      except:
1245        raise newException(Defect, "could not write to string stream, " &
1246          "did you use a non-string buffer pointer?", getCurrentException())
1247    elif not defined(nimscript):
1248      copyMem(addr(s.data[s.pos]), buffer, bufLen)
1249    inc(s.pos, bufLen)
1250
1251  proc ssClose(s: Stream) =
1252    var s = StringStream(s)
1253    s.data = ""
1254
1255  proc newStringStream*(s: sink string = ""): owned StringStream =
1256    ## Creates a new stream from the string `s`.
1257    ##
1258    ## See also:
1259    ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
1260    ##   opened File.
1261    ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
1262    ##   file stream from the file name and the mode.
1263    ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
1264    ##   file stream from the file name and the mode.
1265    runnableExamples:
1266      var strm = newStringStream("The first line\nthe second line\nthe third line")
1267      doAssert strm.readLine() == "The first line"
1268      doAssert strm.readLine() == "the second line"
1269      doAssert strm.readLine() == "the third line"
1270      strm.close()
1271
1272    new(result)
1273    result.data = s
1274    result.pos = 0
1275    result.closeImpl = ssClose
1276    result.atEndImpl = ssAtEnd
1277    result.setPositionImpl = ssSetPosition
1278    result.getPositionImpl = ssGetPosition
1279    result.readDataStrImpl = ssReadDataStr
1280    when nimvm:
1281      discard
1282    else:
1283      result.readDataImpl = ssReadData
1284      result.peekDataImpl = ssPeekData
1285      result.writeDataImpl = ssWriteData
1286
1287type
1288  FileStream* = ref FileStreamObj
1289    ## A stream that encapsulates a `File`.
1290    ##
1291    ## **Note:** Not available for JS backend.
1292  FileStreamObj* = object of Stream
1293    ## A file stream object.
1294    ##
1295    ## **Note:** Not available for JS backend.
1296    f: File
1297
1298proc fsClose(s: Stream) =
1299  if FileStream(s).f != nil:
1300    close(FileStream(s).f)
1301    FileStream(s).f = nil
1302proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
1303proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
1304proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
1305proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
1306
1307proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
1308  result = readBuffer(FileStream(s).f, buffer, bufLen)
1309
1310proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
1311  result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a)
1312
1313proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
1314  let pos = fsGetPosition(s)
1315  defer: fsSetPosition(s, pos)
1316  result = readBuffer(FileStream(s).f, buffer, bufLen)
1317
1318proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
1319  if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
1320    raise newEIO("cannot write to stream")
1321
1322proc fsReadLine(s: Stream, line: var string): bool =
1323  result = readLine(FileStream(s).f, line)
1324
1325proc newFileStream*(f: File): owned FileStream =
1326  ## Creates a new stream from the file `f`.
1327  ##
1328  ## **Note:** Not available for JS backend.
1329  ##
1330  ## See also:
1331  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
1332  ##   from string.
1333  ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same
1334  ##   as using `open proc <io.html#open,File,string,FileMode,int>`_
1335  ##   on Examples.
1336  ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
1337  ##   file stream from the file name and the mode.
1338  runnableExamples:
1339    ## Input (somefile.txt):
1340    ## The first line
1341    ## the second line
1342    ## the third line
1343    var f: File
1344    if open(f, "somefile.txt", fmRead, -1):
1345      var strm = newFileStream(f)
1346      var line = ""
1347      while strm.readLine(line):
1348        echo line
1349      ## Output:
1350      ## The first line
1351      ## the second line
1352      ## the third line
1353      strm.close()
1354
1355  new(result)
1356  result.f = f
1357  result.closeImpl = fsClose
1358  result.atEndImpl = fsAtEnd
1359  result.setPositionImpl = fsSetPosition
1360  result.getPositionImpl = fsGetPosition
1361  result.readDataStrImpl = fsReadDataStr
1362  result.readDataImpl = fsReadData
1363  result.readLineImpl = fsReadLine
1364  result.peekDataImpl = fsPeekData
1365  result.writeDataImpl = fsWriteData
1366  result.flushImpl = fsFlush
1367
1368proc newFileStream*(filename: string, mode: FileMode = fmRead,
1369    bufSize: int = -1): owned FileStream =
1370  ## Creates a new stream from the file named `filename` with the mode `mode`.
1371  ##
1372  ## If the file cannot be opened, `nil` is returned. See the `io module
1373  ## <io.html>`_ for a list of available `FileMode enums <io.html#FileMode>`_.
1374  ##
1375  ## **Note:**
1376  ## * **This function returns nil in case of failure.**
1377  ##   To prevent unexpected behavior and ensure proper error handling,
1378  ##   use `openFileStream proc <#openFileStream,string,FileMode,int>`_
1379  ##   instead.
1380  ## * Not available for JS backend.
1381  ##
1382  ## See also:
1383  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
1384  ##   from string.
1385  ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
1386  ##   opened File.
1387  ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
1388  ##   file stream from the file name and the mode.
1389  runnableExamples:
1390    from std/os import removeFile
1391    var strm = newFileStream("somefile.txt", fmWrite)
1392    if not isNil(strm):
1393      strm.writeLine("The first line")
1394      strm.writeLine("the second line")
1395      strm.writeLine("the third line")
1396      strm.close()
1397      ## Output (somefile.txt)
1398      ## The first line
1399      ## the second line
1400      ## the third line
1401      removeFile("somefile.txt")
1402
1403  var f: File
1404  if open(f, filename, mode, bufSize): result = newFileStream(f)
1405
1406proc openFileStream*(filename: string, mode: FileMode = fmRead,
1407    bufSize: int = -1): owned FileStream =
1408  ## Creates a new stream from the file named `filename` with the mode `mode`.
1409  ## If the file cannot be opened, an IO exception is raised.
1410  ##
1411  ## **Note:** Not available for JS backend.
1412  ##
1413  ## See also:
1414  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
1415  ##   from string.
1416  ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
1417  ##   opened File.
1418  ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
1419  ##   file stream from the file name and the mode.
1420  runnableExamples:
1421    try:
1422      ## Input (somefile.txt):
1423      ## The first line
1424      ## the second line
1425      ## the third line
1426      var strm = openFileStream("somefile.txt")
1427      echo strm.readLine()
1428      ## Output:
1429      ## The first line
1430      strm.close()
1431    except:
1432      stderr.write getCurrentExceptionMsg()
1433
1434  var f: File
1435  if open(f, filename, mode, bufSize):
1436    return newFileStream(f)
1437  else:
1438    raise newEIO("cannot open file stream: " & filename)
1439
1440when false:
1441  type
1442    FileHandleStream* = ref FileHandleStreamObj
1443    FileHandleStreamObj* = object of Stream
1444      handle*: FileHandle
1445      pos: int
1446
1447  proc newEOS(msg: string): ref OSError =
1448    new(result)
1449    result.msg = msg
1450
1451  proc hsGetPosition(s: FileHandleStream): int =
1452    return s.pos
1453
1454  when defined(windows):
1455    # do not import windows as this increases compile times:
1456    discard
1457  else:
1458    import posix
1459
1460    proc hsSetPosition(s: FileHandleStream, pos: int) =
1461      discard lseek(s.handle, pos, SEEK_SET)
1462
1463    proc hsClose(s: FileHandleStream) = discard close(s.handle)
1464    proc hsAtEnd(s: FileHandleStream): bool =
1465      var pos = hsGetPosition(s)
1466      var theEnd = lseek(s.handle, 0, SEEK_END)
1467      result = pos >= theEnd
1468      hsSetPosition(s, pos) # set position back
1469
1470    proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
1471      result = posix.read(s.handle, buffer, bufLen)
1472      inc(s.pos, result)
1473
1474    proc hsPeekData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
1475      result = posix.read(s.handle, buffer, bufLen)
1476
1477    proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
1478      if posix.write(s.handle, buffer, bufLen) != bufLen:
1479        raise newEIO("cannot write to stream")
1480      inc(s.pos, bufLen)
1481
1482  proc newFileHandleStream*(handle: FileHandle): owned FileHandleStream =
1483    new(result)
1484    result.handle = handle
1485    result.pos = 0
1486    result.close = hsClose
1487    result.atEnd = hsAtEnd
1488    result.setPosition = hsSetPosition
1489    result.getPosition = hsGetPosition
1490    result.readData = hsReadData
1491    result.peekData = hsPeekData
1492    result.writeData = hsWriteData
1493
1494  proc newFileHandleStream*(filename: string,
1495                            mode: FileMode): owned FileHandleStream =
1496    when defined(windows):
1497      discard
1498    else:
1499      var flags: cint
1500      case mode
1501      of fmRead: flags = posix.O_RDONLY
1502      of fmWrite: flags = O_WRONLY or int(O_CREAT)
1503      of fmReadWrite: flags = O_RDWR or int(O_CREAT)
1504      of fmReadWriteExisting: flags = O_RDWR
1505      of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND
1506      static: doAssert false # handle bug #17888
1507      var handle = open(filename, flags)
1508      if handle < 0: raise newEOS("posix.open() call failed")
1509    result = newFileHandleStream(handle)
1510