ruby-tee/tee.rb
2012-11-06 16:47:46 +01:00

121 lines
2.3 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
module Chunkable
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
end
module Digestable
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
end
module Utils
def tee(*procs)
ios = procs.map { |proc| FiberChunkedIO.new &proc }
chunks.each do |chunk|
ios.each do |io|
io.write chunk
end
end
ios.each { |io| io.close }
ios.map { |io| io.result }
end
end
include Chunkable
include Digestable
include Utils
end
class FiberChunkedIO
def initialize(chunk_size=1024, &block)
@chunk_size = chunk_size
@chunks = []
@eof = false
@fiber = Fiber.new do
@result = block.call self
end
@fiber.resume
end
# Being a stream, it behaves like IO#eof? and blocks until the other end sends some data or closes it.
def eof?
Fiber.yield
@eof
end
def close
@eof = true
@fiber.resume
end
def result
@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
@eof = false
@fiber.resume
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
include IO::Chunkable
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
results = f.tee(sha1_proc, sha2_proc, puts_proc)
p results
p File.read('sums').lines.map.with_index { |l, i| results[i] == l.split(' ')[0] }
end