streams.nim 47 KB

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