streams.nim 48 KB

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