streamwrapper.nim 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements stream wrapper.
  10. ##
  11. ## **Since** version 1.2.
  12. import deques, streams
  13. when defined(nimPreviewSlimSystem):
  14. import std/assertions
  15. type
  16. PipeOutStream*[T] = ref object of T
  17. # When stream peek operation is called, it reads from base stream
  18. # type using `baseReadDataImpl` and stores the content to this buffer.
  19. # Next stream read operation returns data in the buffer so that previus peek
  20. # operation looks like didn't changed read positon.
  21. # When stream read operation that returns N byte data is called and the size is smaller than buffer size,
  22. # first N elements are removed from buffer.
  23. # Deque type can do such operation more efficiently than seq type.
  24. buffer: Deque[char]
  25. baseReadLineImpl: typeof(StreamObj.readLineImpl)
  26. baseReadDataImpl: typeof(StreamObj.readDataImpl)
  27. proc posReadLine[T](s: Stream, line: var string): bool =
  28. var s = PipeOutStream[T](s)
  29. assert s.baseReadLineImpl != nil
  30. let n = s.buffer.len
  31. line.setLen(0)
  32. for i in 0..<n:
  33. var c = s.buffer.popFirst
  34. if c == '\c':
  35. c = readChar(s)
  36. return true
  37. elif c == '\L': return true
  38. elif c == '\0':
  39. return line.len > 0
  40. line.add(c)
  41. var line2: string
  42. result = s.baseReadLineImpl(s, line2)
  43. line.add line2
  44. proc posReadData[T](s: Stream, buffer: pointer, bufLen: int): int =
  45. var s = PipeOutStream[T](s)
  46. assert s.baseReadDataImpl != nil
  47. let
  48. dest = cast[ptr UncheckedArray[char]](buffer)
  49. n = min(s.buffer.len, bufLen)
  50. result = n
  51. for i in 0..<n:
  52. dest[i] = s.buffer.popFirst
  53. if bufLen > n:
  54. result += s.baseReadDataImpl(s, addr dest[n], bufLen - n)
  55. proc posReadDataStr[T](s: Stream, buffer: var string, slice: Slice[int]): int =
  56. posReadData[T](s, addr buffer[slice.a], slice.len)
  57. proc posPeekData[T](s: Stream, buffer: pointer, bufLen: int): int =
  58. var s = PipeOutStream[T](s)
  59. assert s.baseReadDataImpl != nil
  60. let
  61. dest = cast[ptr UncheckedArray[char]](buffer)
  62. n = min(s.buffer.len, bufLen)
  63. result = n
  64. for i in 0..<n:
  65. dest[i] = s.buffer[i]
  66. if bufLen > n:
  67. let
  68. newDataNeeded = bufLen - n
  69. numRead = s.baseReadDataImpl(s, addr dest[n], newDataNeeded)
  70. result += numRead
  71. for i in 0..<numRead:
  72. s.buffer.addLast dest[n + i]
  73. proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] =
  74. ## Wrap pipe for reading with PipeOutStream so that you can use peek* procs and generate runtime error
  75. ## when setPosition/getPosition is called or write operation is performed.
  76. ##
  77. ## Example:
  78. ## ```Nim
  79. ## import std/[osproc, streamwrapper]
  80. ## var
  81. ## p = startProcess(exePath)
  82. ## outStream = p.outputStream().newPipeOutStream()
  83. ## echo outStream.peekChar
  84. ## p.close()
  85. ## ```
  86. assert s.readDataImpl != nil
  87. new(result)
  88. for dest, src in fields((ref T)(result)[], s[]):
  89. dest = src
  90. wasMoved(s[])
  91. if result.readLineImpl != nil:
  92. result.baseReadLineImpl = result.readLineImpl
  93. result.readLineImpl = posReadLine[T]
  94. result.baseReadDataImpl = result.readDataImpl
  95. result.readDataImpl = posReadData[T]
  96. result.readDataStrImpl = posReadDataStr[T]
  97. result.peekDataImpl = posPeekData[T]
  98. # Set nil to anything you may not call.
  99. result.setPositionImpl = nil
  100. result.getPositionImpl = nil
  101. result.writeDataImpl = nil
  102. result.flushImpl = nil