ruby-tee/tee.rb
2012-10-30 17:43:21 +01:00

108 lines
2 KiB
Ruby

require 'digest/sha1'
require 'digest/sha2'
module Enumerable
def tee(*procs)
each { |i| procs.map { |p| p.call i } }
end
end
class IO
def chunks(chunk_size=1024)
Enumerator.new { |y| y << read(chunk_size) until eof? }
end
def each_chunk(chunk_size=nil)
chunks.each { |*args| yield *args }
end
def digest_with(digest, chunk_size=nil)
chunks(chunk_size).each { |chunk| digest << chunk }
digest
end
def sha256(chunk_size=nil)
digest_with Digest::SHA2.new(256), chunk_size
end
def tee(*procs)
ios = procs.map do |proc|
FiberChunkedIO.new &proc
end
chunks.each do |chunk|
# for each proc
# - copy chunk into its dedicated IO stream
# - resume
ios.each.with_index do |io, i|
$stdout.puts "#{i}:#{tell}"
io.write chunk
io.resume
end
end
ios.map { |io| io.resume }
end
end
class FiberChunkedIO
def initialize(chunk_size=1024, &block)
@chunk_size = chunk_size
@chunks = []
@fiber = Fiber.new do
@result = block.call self
end
end
def resume
@fiber.resume
@result
end
def write(chunk)
if chunk.size > @chunk_size
raise ArgumentError.new("chunk size mismatch: expected #{@chunk_size}, got #{chunk.size}")
end
@chunks << chunk
chunk.size
end
def read(chunk_size)
unless chunk_size == @chunk_size
raise ArgumentError.new("chunk size mismatch: expected #{@chunk_size}, got #{chunk_size}")
end
@chunks.shift
end
def chunks(chunk_size=1024)
Enumerator.new do |y|
while chunk = read(chunk_size)
y << chunk
Fiber.yield
end
end
end
end
File.open("test") do |f|
sha1_proc = lambda do |f|
digest = Digest::SHA1.new
f.chunks.each { |chunk| digest << chunk }
digest
end
sha2_proc = lambda do |f|
digest = Digest::SHA2.new(256)
f.chunks.each { |chunk| digest << chunk }
digest
end
puts_proc = lambda do |f|
f.chunks.each { |chunk| puts chunk.length }
end
p f.tee(sha1_proc, sha2_proc, puts_proc)
end