streams.nim 40 KB

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