lib/stringio.rb in rubysl-stringio-2.1 vs lib/stringio.rb in rubysl-stringio-2.1.0

- old
+ new

@@ -1,742 +2 @@ -class IO - module Writable - end - module Readable - end -end - -class StringIO - - include Enumerable - include IO::Writable - include IO::Readable - - DEFAULT_RECORD_SEPARATOR = "\n" unless defined?(::DEFAULT_RECORD_SEPARATOR) - - # This is why we need undefined in Ruby - Undefined = Object.new - - class Data - attr_accessor :string, :pos, :lineno, :encoding - - def initialize(string) - @string = string - @encoding = string.encoding - @pos = @lineno = 0 - end - end - - def self.open(*args) - io = new(*args) - return io unless block_given? - - begin - yield io - ensure - io.close - io.__data__.string = nil - self - end - end - - attr_reader :__data__ - - def initialize(string=nil, mode=nil) - if string.nil? - @__data__ = Data.new "" - set_encoding(nil) - mode = IO::RDWR - else - string = Rubinius::Type.coerce_to string, String, :to_str - @__data__ = Data.new string - end - - if mode - if mode.is_a?(Integer) - mode_from_integer(mode) - else - mode = StringValue(mode) - mode_from_string(mode) - end - else - mode_from_string(string.frozen? ? "r" : "r+") - end - - self - end - - def initialize_copy(from) - from = Rubinius::Type.coerce_to(from, StringIO, :to_strio) - - taint if from.tainted? - - @append = from.instance_variable_get(:@append) - @readable = from.instance_variable_get(:@readable) - @writable = from.instance_variable_get(:@writable) - @__data__ = from.instance_variable_get(:@__data__) - - self - end - - def check_readable - raise IOError, "not opened for reading" unless @readable - end - - private :check_readable - - def check_writable - raise IOError, "not opened for writing" unless @writable - raise IOError, "unable to modify data" if @__data__.string.frozen? - end - - private :check_writable - - def set_encoding(external, internal=nil, options=nil) - encoding = external || Encoding.default_external - @__data__.encoding = encoding - @__data__.string.force_encoding(encoding) - self - end - - def external_encoding - @__data__.encoding - end - - def internal_encoding - nil - end - - def each_byte - return to_enum :each_byte unless block_given? - check_readable - - d = @__data__ - string = d.string - - while d.pos < string.length - byte = string.getbyte d.pos - d.pos += 1 - yield byte - end - - self - end - - alias_method :bytes, :each_byte - - def each_char - return to_enum :each_char unless block_given? - while s = getc - yield s - end - - self - end - - alias_method :chars, :each_char - - def each_codepoint(&block) - return to_enum :each_codepoint unless block_given? - check_readable - - d = @__data__ - string = d.string - - while d.pos < string.bytesize - char = string.chr_at d.pos - - unless char - raise ArgumentError, "invalid byte sequence in #{d.encoding}" - end - - d.pos += char.bytesize - yield char.ord - end - - self - end - - alias_method :codepoints, :each_codepoint - - def each(sep=$/, limit=Undefined) - return to_enum :each, sep, limit unless block_given? - check_readable - - while line = getline(true, sep, limit) - yield line - end - - self - end - - alias_method :each_line, :each - alias_method :lines, :each - - def <<(str) - write(str) - self - end - - def binmode - self - end - - def write(str) - check_writable - - str = String(str) - return 0 if str.empty? - - d = @__data__ - pos = d.pos - string = d.string - - if @append || pos == string.bytesize - string.byte_append str - d.pos = string.bytesize - elsif pos > string.bytesize - m = Rubinius::Mirror.reflect string - m.splice string.bytesize, 0, "\000" * (pos - string.bytesize) - string.byte_append str - d.pos = string.bytesize - else - stop = string.bytesize - pos - if str.bytesize < stop - stop = str.bytesize - end - m = Rubinius::Mirror.reflect string - m.splice pos, stop, str - d.pos += str.bytesize - string.taint if str.tainted? - end - - str.bytesize - end - alias_method :syswrite, :write - alias_method :write_nonblock, :write - - def close - raise IOError, "closed stream" if closed? - @readable = @writable = nil - end - - def closed? - !@readable && !@writable - end - - def close_read - check_readable - @readable = nil - end - - def closed_read? - !@readable - end - - def close_write - check_writable - @writable = nil - end - - def closed_write? - !@writable - end - - def eof? - d = @__data__ - d.pos >= d.string.bytesize - end - alias_method :eof, :eof? - - def fcntl - raise NotImplementedError, "StringIO#fcntl is not implemented" - end - - def fileno - nil - end - - def flush - self - end - - def fsync - 0 - end - - def getc - check_readable - d = @__data__ - - return nil if eof? - - char = d.string.find_character(d.pos) - d.pos += char.bytesize - char - end - - def getbyte - check_readable - d = @__data__ - - return nil if eof? - - byte = d.string.getbyte(d.pos) - d.pos += 1 - byte - end - - def gets(sep=$/, limit=Undefined) - check_readable - - $_ = getline(false, sep, limit) - end - - def isatty - false - end - alias_method :tty?, :isatty - - def lineno - @__data__.lineno - end - - def lineno=(line) - @__data__.lineno = line - end - - def pid - nil - end - - def pos - @__data__.pos - end - - def pos=(pos) - raise Errno::EINVAL if pos < 0 - @__data__.pos = pos - end - - def print(*args) - check_writable - args << $_ if args.empty? - write((args << $\).flatten.join) - nil - end - - def printf(*args) - check_writable - - if args.size > 1 - write(args.shift % args) - else - write(args.first) - end - - nil - end - - def putc(obj) - check_writable - - if obj.is_a?(String) - char = obj[0] - else - c = Rubinius::Type.coerce_to obj, Integer, :to_int - char = (c & 0xff).chr - end - - d = @__data__ - pos = d.pos - string = d.string - - if @append || pos == string.bytesize - string.byte_append char - d.pos = string.bytesize - elsif pos > string.bytesize - m = Rubinius::Mirror.reflect string - m.splice string.bytesize, 0, "\000" * (pos - string.bytesize) - string.byte_append char - d.pos = string.bytesize - else - m = Rubinius::Mirror.reflect string - m.splice pos, char.bytesize, char - d.pos += char.bytesize - end - - obj - end - - def puts(*args) - if args.empty? - write(DEFAULT_RECORD_SEPARATOR) - else - args.each do |arg| - if arg.nil? - line = "" - elsif Thread.guarding? arg - line = "[...]" - else - begin - arg = Rubinius::Type.coerce_to(arg, Array, :to_ary) - Thread.recursion_guard arg do - arg.each { |a| puts a } - end - next - rescue - line = arg.to_s - end - end - - write(line) - write(DEFAULT_RECORD_SEPARATOR) unless line[-1] == ?\n - end - end - - nil - end - - def read(length=nil, buffer=nil) - check_readable - d = @__data__ - pos = d.pos - string = d.string - - if length - length = Rubinius::Type.coerce_to length, Integer, :to_int - raise ArgumentError if length < 0 - - buffer = StringValue(buffer) if buffer - - if eof? - buffer.clear if buffer - if length == 0 - return "".force_encoding(Encoding::ASCII_8BIT) - else - return nil - end - end - - str = string.byteslice(pos, length) - str.force_encoding Encoding::ASCII_8BIT - - str = buffer.replace(str) if buffer - else - if eof? - buffer.clear if buffer - return "".force_encoding(Encoding::ASCII_8BIT) - end - - str = string.byteslice(pos..-1) - buffer.replace str if buffer - end - - d.pos += str.length - return str - end - - def readchar - raise IO::EOFError, "end of file reached" if eof? - getc - end - - def readbyte - readchar.getbyte(0) - end - - def readline(sep=$/, limit=Undefined) - check_readable - raise IO::EOFError, "end of file reached" if eof? - - $_ = getline(true, sep, limit) - end - - def readlines(sep=$/, limit=Undefined) - check_readable - - ary = [] - while line = getline(true, sep, limit) - ary << line - end - - ary - end - - def reopen(string=nil, mode=Undefined) - if string and not string.kind_of? String and mode.equal? Undefined - stringio = Rubinius::Type.coerce_to(string, StringIO, :to_strio) - - taint if stringio.tainted? - initialize_copy stringio - else - mode = nil if mode.equal? Undefined - string = "" unless string - - initialize string, mode - end - - self - end - - def rewind - d = @__data__ - d.pos = d.lineno = 0 - end - - def seek(to, whence = IO::SEEK_SET) - raise IOError, "closed stream" if self.closed? - to = Rubinius::Type.coerce_to to, Integer, :to_int - - case whence - when IO::SEEK_CUR - to += @__data__.pos - when IO::SEEK_END - to += @__data__.string.bytesize - when IO::SEEK_SET, nil - else - raise Errno::EINVAL, "invalid whence" - end - - raise Errno::EINVAL if to < 0 - - @__data__.pos = to - - return 0 - end - - def size - @__data__.string.bytesize - end - alias_method :length, :size - - def string - @__data__.string - end - - def string=(string) - d = @__data__ - d.string = StringValue(string) - d.pos = 0 - d.lineno = 0 - end - - def sync - true - end - - def sync=(val) - val - end - - def sysread(length=nil, buffer="") - str = read(length, buffer) - - if str.nil? - buffer.clear - raise IO::EOFError, "end of file reached" - end - - str - end - - alias_method :readpartial, :sysread - alias_method :read_nonblock, :sysread - - def tell - @__data__.pos - end - - def truncate(length) - check_writable - len = Rubinius::Type.coerce_to length, Integer, :to_int - raise Errno::EINVAL, "negative length" if len < 0 - string = @__data__.string - - if len < string.bytesize - string[len..string.bytesize] = "" - else - string << "\000" * (len - string.bytesize) - end - return length - end - - def ungetc(char) - check_readable - - d = @__data__ - pos = d.pos - string = d.string - - if char.kind_of? Integer - char = Rubinius::Type.coerce_to char, String, :chr - else - char = Rubinius::Type.coerce_to char, String, :to_str - end - - if pos > string.bytesize - string[string.bytesize..pos] = "\000" * (pos - string.bytesize) - d.pos -= 1 - string[d.pos] = char - elsif pos > 0 - d.pos -= 1 - string[d.pos] = char - end - - nil - end - - def ungetbyte(bytes) - check_readable - - return unless bytes - - if bytes.kind_of? Fixnum - bytes = "" << bytes - else - bytes = StringValue(bytes) - return if bytes.bytesize == 0 - end - - d = @__data__ - pos = d.pos - string = d.string - - enc = string.encoding - - if d.pos == 0 - d.string = "#{bytes}#{string}" - else - size = bytes.bytesize - a = string.byteslice(0, pos - size) if size < pos - b = string.byteslice(pos..-1) - d.string = "#{a}#{bytes}#{b}" - d.pos = pos > size ? pos - size : 0 - end - - d.string.force_encoding enc - nil - end - - def encode_with(coder) - end - - def init_with(coder) - @__data__ = Data.new("") - end - - def to_yaml_properties - [] - end - - def yaml_initialize(type, val) - @__data__ = Data.new("") - end - - protected - - def mode_from_string(mode) - @append = truncate = false - - if mode[0] == ?r - @readable = true - @writable = mode[-1] == ?+ ? true : false - end - - if mode[0] == ?w - @writable = truncate = true - @readable = mode[-1] == ?+ ? true : false - end - - if mode[0] == ?a - @append = @writable = true - @readable = mode[-1] == ?+ ? true : false - end - - d = @__data__ - raise Errno::EACCES, "Permission denied" if @writable && d.string.frozen? - d.string.replace("") if truncate - end - - def mode_from_integer(mode) - @readable = @writable = @append = false - d = @__data__ - - if mode == 0 or mode & IO::RDWR != 0 - @readable = true - end - - if mode & (IO::WRONLY | IO::RDWR) != 0 - raise Errno::EACCES, "Permission denied" if d.string.frozen? - @writable = true - end - - @append = true if (mode & IO::APPEND) != 0 - d.string.replace("") if (mode & IO::TRUNC) != 0 - end - - def getline(arg_error, sep, limit) - if limit != Undefined - limit = Rubinius::Type.coerce_to limit, Fixnum, :to_int if limit - sep = Rubinius::Type.coerce_to sep, String, :to_str if sep - else - limit = nil - - unless sep == $/ or sep.nil? - osep = sep - sep = Rubinius::Type.check_convert_type sep, String, :to_str - limit = Rubinius::Type.coerce_to osep, Fixnum, :to_int unless sep - end - end - - raise ArgumentError if arg_error and limit == 0 - - return nil if eof? - - d = @__data__ - pos = d.pos - string = d.string - - if sep.nil? - if limit - line = string.byteslice(pos, limit) - else - line = string.byteslice(pos, string.bytesize - pos) - end - d.pos += line.bytesize - elsif sep.empty? - if stop = string.find_string("\n\n", pos) - stop += 2 - line = string.byteslice(pos, stop - pos) - while string.getbyte(stop) == 10 - stop += 1 - end - d.pos = stop - else - line = string.byteslice(pos, string.bytesize - pos) - d.pos = string.bytesize - end - else - if stop = string.find_string(sep, pos) - if limit && stop - pos >= limit - stop = pos + limit - else - stop += sep.bytesize - end - line = string.byteslice(pos, stop - pos) - d.pos = stop - else - if limit - line = string.byteslice(pos, limit) - else - line = string.byteslice(pos, string.bytesize - pos) - end - d.pos += line.bytesize - end - end - - d.lineno += 1 - - return line - end -end +require "rubysl/stringio"