streams.nim 46 KB

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