diff --git a/bar.cr b/bar.cr new file mode 100644 index 000000000000..ef9edd1a1144 --- /dev/null +++ b/bar.cr @@ -0,0 +1,31 @@ +require "logger" + +module Migrate::Logging + @@logger : Logger = Logger.new(STDOUT).tap do |l| + l.formatter = Logger::Formatter.new do |severity, datetime, progname, message, io| + io << severity[0] << ", [" << datetime << " #" << Process.pid << "] " + io << severity.rjust(5) << " -- " << progname << ": " << message + io << "My additional suff" + end + end + + def logger + @@logger + end + + def self.logger + @@logger + end +end + +class Foo + include Migrate::Logging + def foo + logger.warn("Hello!") + 42 + end +end + +Foo.new.foo + + diff --git a/foo b/foo new file mode 100755 index 000000000000..eaf3d7ce0470 Binary files /dev/null and b/foo differ diff --git a/foo.cr b/foo.cr new file mode 100644 index 000000000000..7785637b6171 --- /dev/null +++ b/foo.cr @@ -0,0 +1,5 @@ +# puts "A" +# spawn do +# puts "B" +# end +# puts "C" diff --git a/io_deferred b/io_deferred new file mode 100755 index 000000000000..a119b1baff5b Binary files /dev/null and b/io_deferred differ diff --git a/io_deferred.cr b/io_deferred.cr new file mode 100644 index 000000000000..9c7b564d2318 --- /dev/null +++ b/io_deferred.cr @@ -0,0 +1,175 @@ +module IO + def self.deferred(&block : IO -> Void) + IO::Deferred.new(block).reader + end +end + +class IO::Deferred + include IO + + @target : Bytes? + @raw : Bytes + @buffer : Bytes + + @chan_reader : Channel(Int32) + @chan_writer : Channel(Int32) + @chan_block : Channel(Nil) + + @started : Bool + @finished : Bool + + @reader : Reader? + @writer : Writer? + + def initialize(@block : IO -> Void) + @raw = Bytes.new(4096) + @buffer = Bytes.new(@raw.to_unsafe, 0) + @chan_reader = Channel(Int32).new + @chan_writer = Channel(Int32).new + @chan_block = Channel(Nil).new + @started = false + @finished = false + end + + def read(target : Bytes) + puts "READING 1" + + try_start + written = 0 + + puts "READING 2" + + # Store the requested target buffer to be written in the `write` method + @target = target + + # Grab what's leftover in the buffer, if any + if @buffer.size > 0 + writeable_size = [@buffer.size, target.size].min + target.copy_from(@buffer.to_unsafe, writeable_size) + @buffer += writeable_size + target += writeable_size + written += writeable_size + end + + # Unblock the writing fiber only if the buffer did not cover the read request, + # and block until the write method signals us that the write was completed + while target.size > 0 && !@finished + @chan_writer.send(target.size) + select + when bytes = @chan_reader.receive + written += bytes + when @chan_block.receive + @finished = true + end + end + + @target = nil + return written + end + + def write(source : Bytes) + # Block until the main reader fiber signals us for a write request + requested = @chan_writer.receive + + # Write to the target slice + target = @target.not_nil! + writeable_size = [target.size, source.size].min + target.copy_from(source.to_unsafe, writeable_size) + source += writeable_size + + # Store leftover (if any) in buffer, resizing underlying raw buffer if needed + if source.size > 0 + @raw = Bytes.new(source.size) if source.size > @raw.size + @raw.copy_from(source.to_unsafe, source.size) + end + + # Update buffer view of raw underlying buffer + @buffer = Bytes.new(@raw.to_unsafe, source.size) + + # Signal the reader fiber that this write operation is finished, + # sending the number of bytes written to target + @chan_reader.send(writeable_size) + + return nil + end + + def reader + @reader ||= Reader.new(self) + puts "Yielding reader" + @reader.not_nil! + end + + def writer + @writer ||= Writer.new(self) + puts "Yielding writer" + @writer.not_nil! + end + + def try_start + unless @started + @started = true + spawn do + @block.call(writer) + @chan_block.send(nil) + end + end + end +end + +class IO::Deferred::Reader + include IO + + def initialize(@io : IO::Deferred) + end + + def read(slice : Bytes) + @io.read(slice) + end + + def write(slice : Bytes) + raise IO::Error.new("can't write to IO::Deferred::Reader") + end +end + +class IO::Deferred::Writer + include IO + + def initialize(@io : IO::Deferred) + end + + def read(slice : Bytes) + raise IO::Error.new("can't read from IO::Deferred::Writer") + end + + def write(slice : Bytes) + @io.write(slice) + end +end + +puts "Before Starting" + +reader = IO.deferred do |writer| + puts "BLOCK | Initialising writer" + 4.times do + puts "BLOCK | Writing 6 bytes" + writer.write("foobar".to_slice) + end +end + +puts "Starting" +# puts reader.read_string(6) +# puts "1" +# puts reader.read_string(6) +# puts "2" +# puts reader.read_string(6) +# puts "3" +# puts reader.read_string(6) +# puts "4" +# puts reader.read_string(6) +# puts "5" +# puts reader.read_string(6) +# puts "6" +# # puts reader.read_string(1) +# # puts reader.read_string(1) +# # puts reader.read_string(16) +# # puts reader.gets_to_end diff --git a/spec/std/openssl/digest_io_spec.cr b/spec/std/openssl/digest_io_spec.cr index 6b3fb3a450d8..e57035a96365 100644 --- a/spec/std/openssl/digest_io_spec.cr +++ b/spec/std/openssl/digest_io_spec.cr @@ -31,7 +31,7 @@ describe OpenSSL::DigestIO do base_io = IO::Memory.new("foo") base_digest = OpenSSL::Digest.new("SHA256") empty_digest = OpenSSL::Digest.new("SHA256").digest - io = OpenSSL::DigestIO.new(base_io, base_digest, digest_on_read: false) + io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write) slice = Bytes.new(256) io.read(slice).should eq(3) slice[0, 3].should eq("foo".to_slice) @@ -41,7 +41,7 @@ describe OpenSSL::DigestIO do it "calculates digest from writing" do base_io = IO::Memory.new base_digest = OpenSSL::Digest.new("SHA256") - io = OpenSSL::DigestIO.new(base_io, base_digest) + io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write) io.write("foo".to_slice) base_io.to_slice[0, 3].should eq("foo".to_slice) @@ -51,7 +51,7 @@ describe OpenSSL::DigestIO do it "calculates digest from writing a string" do base_io = IO::Memory.new base_digest = OpenSSL::Digest.new("SHA256") - io = OpenSSL::DigestIO.new(base_io, base_digest) + io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write) io.print("foo") base_io.to_slice[0, 3].should eq("foo".to_slice) @@ -61,7 +61,7 @@ describe OpenSSL::DigestIO do it "calculates digest from multiple writes" do base_io = IO::Memory.new base_digest = OpenSSL::Digest.new("SHA256") - io = OpenSSL::DigestIO.new(base_io, base_digest) + io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write) io.write("fo".to_slice) io.write("o".to_slice) base_io.to_slice[0, 3].should eq("foo".to_slice) @@ -73,7 +73,7 @@ describe OpenSSL::DigestIO do base_io = IO::Memory.new base_digest = OpenSSL::Digest.new("SHA256") empty_digest = OpenSSL::Digest.new("SHA256").digest - io = OpenSSL::DigestIO.new(base_io, base_digest, digest_on_write: false) + io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Read) io.write("foo".to_slice) base_io.to_slice[0, 3].should eq("foo".to_slice) diff --git a/src/openssl/digest/digest_io.cr b/src/openssl/digest/digest_io.cr index e464581aa7eb..743b36dd752e 100644 --- a/src/openssl/digest/digest_io.cr +++ b/src/openssl/digest/digest_io.cr @@ -1,6 +1,6 @@ require "./digest_base" -# Wraps an IO by calculating a specified digest on read and/or write operations +# Wraps an IO by calculating a specified digest on read or write operations # # ### Example # @@ -20,30 +20,33 @@ module OpenSSL getter io : IO getter digest_algorithm : OpenSSL::Digest - getter digest_on_read : Bool - getter digest_on_write : Bool + getter mode : DigestMode delegate close, closed?, flush, peek, tty?, rewind, to: @io - delegate digest, to: @digest_algorithm delegate digest, hexdigest, base64digest, to: @digest_algorithm - def initialize(@io : IO, @digest_algorithm : OpenSSL::Digest, *, @digest_on_read = true, @digest_on_write = true) + enum DigestMode + Read + Write end - def initialize(@io : IO, algorithm : String, *, @digest_on_read = true, @digest_on_write = true) + def initialize(@io : IO, @digest_algorithm : OpenSSL::Digest, @mode = DigestMode::Read) + end + + def initialize(@io : IO, algorithm : String, @mode = DigestMode::Read) @digest_algorithm = OpenSSL::Digest.new(algorithm) end def read(slice : Bytes) read_bytes = io.read(slice) - if @digest_on_read + if @mode == DigestMode::Read digest_algorithm.update(slice[0, read_bytes]) end read_bytes end def write(slice : Bytes) - if @digest_on_write + if @mode == DigestMode::Write digest_algorithm.update(slice) end io.write(slice)