Files
Nim/lib/pure/streamwrapper.nim
ringabout 40a1ec21d7 overhaul hook injections (#24841)
ref https://github.com/nim-lang/Nim/issues/24764 

To keep destructors injected consistently, we need to transform `mAsgn`
properly into `nkSinkAsgn` and `nkAsgn`. This PR is the first step
towards overhauling hook injections.

In this PR, hooks (except mAsgn) are treated consistently whether it is
resolved in matching or instantiated by sempass2. It also fixes a
spelling `=wasMoved` to its normalized version, which caused no
replacing generic hook calls with lifted hook calls.
2025-04-10 09:24:19 +02:00

123 lines
3.5 KiB
Nim

#
#
# Nim's Runtime Library
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements stream wrapper.
##
## **Since** version 1.2.
import std/[deques, streams]
when defined(nimPreviewSlimSystem):
import std/assertions
type
PipeOutStream*[T] = ref object of T
# When stream peek operation is called, it reads from base stream
# type using `baseReadDataImpl` and stores the content to this buffer.
# Next stream read operation returns data in the buffer so that previus peek
# operation looks like didn't changed read positon.
# When stream read operation that returns N byte data is called and the size is smaller than buffer size,
# first N elements are removed from buffer.
# Deque type can do such operation more efficiently than seq type.
buffer: Deque[char]
baseReadLineImpl: typeof(StreamObj.readLineImpl)
baseReadDataImpl: typeof(StreamObj.readDataImpl)
proc posReadLine[T](s: Stream, line: var string): bool =
var s = PipeOutStream[T](s)
assert s.baseReadLineImpl != nil
let n = s.buffer.len
line.setLen(0)
for i in 0..<n:
var c = s.buffer.popFirst
if c == '\c':
c = readChar(s)
return true
elif c == '\L': return true
elif c == '\0':
return line.len > 0
line.add(c)
var line2: string
result = s.baseReadLineImpl(s, line2)
line.add line2
proc posReadData[T](s: Stream, buffer: pointer, bufLen: int): int =
var s = PipeOutStream[T](s)
assert s.baseReadDataImpl != nil
let
dest = cast[ptr UncheckedArray[char]](buffer)
n = min(s.buffer.len, bufLen)
result = n
for i in 0..<n:
dest[i] = s.buffer.popFirst
if bufLen > n:
result += s.baseReadDataImpl(s, addr dest[n], bufLen - n)
proc posReadDataStr[T](s: Stream, buffer: var string, slice: Slice[int]): int =
posReadData[T](s, addr buffer[slice.a], slice.len)
proc posPeekData[T](s: Stream, buffer: pointer, bufLen: int): int =
var s = PipeOutStream[T](s)
assert s.baseReadDataImpl != nil
let
dest = cast[ptr UncheckedArray[char]](buffer)
n = min(s.buffer.len, bufLen)
result = n
for i in 0..<n:
dest[i] = s.buffer[i]
if bufLen > n:
let
newDataNeeded = bufLen - n
numRead = s.baseReadDataImpl(s, addr dest[n], newDataNeeded)
result += numRead
for i in 0..<numRead:
s.buffer.addLast dest[n + i]
proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] =
## Wrap pipe for reading with PipeOutStream so that you can use peek* procs and generate runtime error
## when setPosition/getPosition is called or write operation is performed.
##
## Example:
## ```Nim
## import std/[osproc, streamwrapper]
## var
## p = startProcess(exePath)
## outStream = p.outputStream().newPipeOutStream()
## echo outStream.peekChar
## p.close()
## ```
assert s.readDataImpl != nil
new(result)
for dest, src in fields((ref T)(result)[], s[]):
dest = src
{.cast(raises: []), cast(tags: []).}:
wasMoved(s[])
if result.readLineImpl != nil:
result.baseReadLineImpl = result.readLineImpl
result.readLineImpl = posReadLine[T]
result.baseReadDataImpl = result.readDataImpl
result.readDataImpl = posReadData[T]
result.readDataStrImpl = posReadDataStr[T]
result.peekDataImpl = posPeekData[T]
# Set nil to anything you may not call.
result.setPositionImpl = nil
result.getPositionImpl = nil
result.writeDataImpl = nil
result.flushImpl = nil