Class: Rubino::Tools::ShellTool::CappedCapture
- Inherits:
-
Object
- Object
- Rubino::Tools::ShellTool::CappedCapture
- Defined in:
- lib/rubino/tools/shell_tool.rb
Overview
Bounded head+tail accumulator for a subprocess's merged output (#539).
Keeps at most cap bytes in memory no matter how much is appended: a
~10% HEAD slice (filled first) plus a sliding TAIL window (the rest of
the budget), with the middle elided. Tail-biased because the bytes that
matter on overflow — exit suffix, error, "N failures" — are at the end.
capped? flips true once the producer has emitted MORE than the cap, so
the reader can kill it; to_s renders the retained slice with a marker.
Instance Method Summary collapse
-
#append(bytes, raw_bytes: bytes.bytesize) ⇒ Object
Append already-UTF-8-scrubbed bytes, retaining only head+tail.
- #capped? ⇒ Boolean
-
#initialize(cap) ⇒ CappedCapture
constructor
Marker is a fixed worst-case width so it always fits inside the cap.
-
#to_s(capped: @capped) ⇒ Object
Render the retained output.
Constructor Details
#initialize(cap) ⇒ CappedCapture
Marker is a fixed worst-case width so it always fits inside the cap.
777 778 779 780 781 782 783 784 785 |
# File 'lib/rubino/tools/shell_tool.rb', line 777 def initialize(cap) @cap = [cap.to_i, 1_024].max @head_limit = [(@cap * 0.1).to_i, 1].max @tail_limit = @cap - @head_limit @head = +"" @tail = +"" @total = 0 @capped = false end |
Instance Method Details
#append(bytes, raw_bytes: bytes.bytesize) ⇒ Object
Append already-UTF-8-scrubbed bytes, retaining only head+tail. The cap
is charged against raw_bytes (the bytes the pipe actually delivered)
so a producer whose output scrubs to empty (cat /dev/zero → pure NUL,
deleted) is still capped on volume read, not on retained size (#539).
791 792 793 794 795 796 797 798 799 800 801 802 803 |
# File 'lib/rubino/tools/shell_tool.rb', line 791 def append(bytes, raw_bytes: bytes.bytesize) @total += raw_bytes if @head.bytesize < @head_limit take = @head_limit - @head.bytesize @head << bytes.byteslice(0, take) rest = bytes.byteslice(take, bytes.bytesize - take) push_tail(rest) if rest && !rest.empty? else push_tail(bytes) end @capped ||= @total > @cap self end |
#capped? ⇒ Boolean
805 806 807 |
# File 'lib/rubino/tools/shell_tool.rb', line 805 def capped? @capped end |
#to_s(capped: @capped) ⇒ Object
Render the retained output. When capped, splice in a marker that names the cap and that the producer was terminated, mirroring the elision note Util::Output.truncate uses so the model knows output was cut.
812 813 814 815 816 817 818 819 820 821 |
# File 'lib/rubino/tools/shell_tool.rb', line 812 def to_s(capped: @capped) head = scrub(@head) tail = scrub(@tail) return head + tail unless capped || @capped elided = [@total - head.bytesize - tail.bytesize, 0].max marker = "\n... [#{elided} bytes elided · output capped at #{@cap} bytes " \ "— command terminated] ...\n" head + marker + tail end |