streams.nim 47 KB

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