Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

711 lines (561 sloc) 13.395 kB
class StringIO
include Enumerable
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
def initialize(string)
@string = string
@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="", mode=nil)
string = Rubinius::Type.coerce_to string, String, :to_str
@__data__ = Data.new string
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)
@__data__.string.force_encoding(external || Encoding.default_external)
end
def external_encoding
@__data__.string.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?
if $KCODE == "UTF8"
lookup = 7.downto(4)
while c = read(1) do
n = c[0].ord
leftmost_zero_bit = lookup.find{|i| n[i].zero? }
case leftmost_zero_bit
when 7 # ASCII
yield c
when 6 # UTF 8 complementary characters
next # Encoding error, ignore
else
more = read(6-leftmost_zero_bit)
break unless more
yield c+more
end
end
else
while s = read(1)
yield s
end
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 #{string.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.length
string << str
d.pos = string.length
elsif pos > string.bytesize
string[string.bytesize..pos] = "\000" * (pos - string.bytesize)
string << str
d.pos = string.bytesize
else
string[pos, str.length] = str
d.pos += str.length
string.taint if str.tainted?
end
return str.length
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__
char = d.string[d.pos]
d.pos += 1 unless eof?
char
end
def getbyte
char = getc
char && char.ord
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.length
string << char
d.pos = string.length
elsif pos > string.length
string[string.length..pos] = "\000" * (pos - string.length)
string << char
d.pos = string.length
else
string[pos] = char
d.pos += 1
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
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
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...pos + limit)
else
line = string.byteslice(pos..-1)
end
d.pos += line.bytesize
elsif sep.empty?
if stop = string.index("\n\n", pos)
stop += 2
line = string.byteslice(pos...stop)
while string[stop] == ?\n
stop += 1
end
d.pos = stop
else
line = string.byteslice(pos..-1)
d.pos = string.bytesize
end
else
if stop = string.index(sep, pos)
if limit && stop - pos >= limit
stop = pos + limit
else
stop += sep.length
end
line = string.byteslice(pos...stop)
d.pos = stop
else
if limit
line = string.byteslice(pos...pos + limit)
else
line = string.byteslice(pos..-1)
end
d.pos += line.bytesize
end
end
d.lineno += 1
return line
end
end
Jump to Line
Something went wrong with that request. Please try again.