streams.nim 47 KB

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