lib/lightio/library/io.rb in lightio-0.2.2 vs lib/lightio/library/io.rb in lightio-0.3.0

- old
+ new

@@ -2,49 +2,124 @@ module LightIO::Library class IO include LightIO::Wrap::IOWrapper wrap ::IO - extend Forwardable - def_delegators :@io_watcher, :wait, :wait_readable, :wait_writable + wrap_blocking_methods :read, :write - wrap_blocking_methods :read, :write, exception_symbol: false - alias_method :<<, :write def read(length=nil, outbuf=nil) raise ArgumentError, "negative length #{length} given" if length && length < 0 (outbuf ||= "").clear loop do readlen = length.nil? ? 4096 : length - outbuf.size - begin - outbuf << wait_nonblock(:read_nonblock, readlen, exception_symbol: false) + if (data = wait_nonblock(:read_nonblock, readlen)) + outbuf << data if length == outbuf.size return outbuf end - rescue EOFError - return outbuf + else + return length.nil? ? '' : nil end end end def readpartial(maxlen, outbuf=nil) (outbuf ||= "").clear - outbuf << wait_nonblock(:read_nonblock, maxlen, exception_symbol: false) + if (data = wait_nonblock(:read_nonblock, maxlen)) + outbuf << data + else + raise EOFError, 'end of file reached' + end outbuf end + def getbyte + read(1) + end + + def getc + wait_readable + @io.getc + end + + def readline(*args) + line = gets(*args) + raise EOFError, 'end of file reached' if line.nil? + line + end + + def readlines(*args) + result = [] + until eof? + result << readline(*args) + end + result + end + + def readchar + c = getc + raise EOFError, 'end of file reached' if c.nil? + c + end + + def readbyte + b = getbyte + raise EOFError, 'end of file reached' if b.nil? + b + end + + def eof + wait_readable + @io.eof? + end + + alias eof? eof + + def gets(*args) + raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2 + return nil if eof? + sep = $/ + if args[0].is_a?(Numeric) + limit = args.shift + else + sep = args.shift if args.size > 0 + limit = args.shift if args.first.is_a?(Numeric) + end + s = '' + while (c = getc) + s << c + break if limit && s.size == limit + break if c == sep + end + $_ = s + end + def close(*args) # close watcher before io closed @io_watcher.close @io.close(*args) end def to_io self end + private + def wait_readable + # if IO is already readable, continue wait_readable may block it forever + # so use getbyte detect this situation + # Maybe move getc and gets to thread pool is a good idea + b = getbyte + if b + ungetbyte(b) + return + end + @io_watcher.wait_readable + end + class << self def open(*args) io = self.new(*args) yield io ensure @@ -63,21 +138,19 @@ end [IO._wrap(r), IO._wrap(w)] end def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil) - timer = 0 + timer = timeout && Time.now # run once ioloop LightIO.sleep 0 loop do r_fds = (read_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).readable?} w_fds = (write_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).writable?} e_fds = [] if r_fds.empty? && w_fds.empty? - interval = 0.1 - LightIO.sleep interval - timer += interval - if timeout && timer > timeout + LightIO.sleep 0 + if timeout && Time.now - timer > timeout return nil end else return [r_fds, w_fds, e_fds] end