Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ac82a4f0cde4^
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5dcfb9fb9289
Choose a head ref
  • 2 commits
  • 12 files changed
  • 1 contributor

Commits on Dec 18, 2016

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ac82a4f View commit details
  2. [Truffle] Updated przlib to use Rubinius::ByteArray.

    Prior to this change, przlib used Strings as byte buffers. This owes a lot to the legacy of Ruby Strings being litle more than dumb byte arrays in Ruby < 1.9. However, these usage patterns do not work terribly well with alternative String representations, such as ropes. By switching to a dedicated byte buffer type we can achive much better performance than with rope-backed Strings and use a more suitable API.
    nirvdrum committed Dec 18, 2016
    Copy the full SHA
    5dcfb9f View commit details
89 changes: 44 additions & 45 deletions lib/ruby/truffle/pr-zlib/lib/pr/rbzlib.rb
Original file line number Diff line number Diff line change
@@ -160,10 +160,9 @@ class Bytef_str
attr_accessor :buffer, :offset

def initialize(buffer, offset=0)
if buffer.class == String
if buffer.class == Rubinius::ByteArray
@buffer = buffer
@offset = offset
@buffer.force_encoding('ASCII-8BIT')
else
@buffer = buffer.buffer
@offset = offset
@@ -201,7 +200,7 @@ def set(val)
end

def current
@buffer[@offset..-1]
@buffer[@offset, @buffer.length - @offset]
end
end

@@ -245,15 +244,15 @@ def [](idx)
end

def []=(idx, val)
@buffer[(idx * 2) + @offset, 2] = [val].pack('v')
@buffer[(idx * 2) + @offset, 2] = Rubinius::ByteArray.from_string([val].pack('v'))
end

def get()
@buffer[@offset, 2].unpack('v').first
end

def set(val)
@buffer[@offset, 2] = [val].pack('v')
@buffer[@offset, 2] = Rubinius::ByteArray.from_string([val].pack('v'))
end
end

@@ -525,7 +524,7 @@ def gz_open(path, mode, fd)
elsif c == ?R.ord
strategy = Z_RLE
else
fmode += c.chr
fmode += c
end
end

@@ -544,15 +543,15 @@ def gz_open(path, mode, fd)
strategy
)

s.outbuf = 0.chr * Z_BUFSIZE
s.outbuf = Rubinius::ByteArray.new(Z_BUFSIZE, 0)
s.stream.next_out = Bytef.new(s.outbuf)

if err != Z_OK || s.outbuf.nil?
destroy(s)
return nil
end
else
s.inbuf = 0.chr * Z_BUFSIZE
s.inbuf = Rubinius::ByteArray.new(Z_BUFSIZE, 0)
s.stream.next_in = Bytef.new(s.inbuf)

err = inflateInit2_(s.stream, -MAX_WBITS, ZLIB_VERSION, s.stream.size)
@@ -568,17 +567,17 @@ def gz_open(path, mode, fd)
s.file = fd < 0 ? File.new(path, fmode) : IO.new(fd, fmode)

if s.mode == 'w'
gzheader = 0.chr * 10
gzheader = Rubinius::ByteArray.new(10, 0)
gzheader[0] = @@gz_magic[0]
gzheader[1] = @@gz_magic[1]
gzheader[2] = Z_DEFLATED.chr
gzheader[3] = 0.chr
gzheader[4] = 0.chr
gzheader[5] = 0.chr
gzheader[6] = 0.chr
gzheader[7] = 0.chr
gzheader[8] = 0.chr
gzheader[9] = OS_CODE.chr
gzheader[2] = Z_DEFLATED
gzheader[3] = 0
gzheader[4] = 0
gzheader[5] = 0
gzheader[6] = 0
gzheader[7] = 0
gzheader[8] = 0
gzheader[9] = OS_CODE
s.file.write(gzheader)
s.start = 10
else
@@ -660,12 +659,12 @@ def get_byte(s)
# Reads a long in LSB order from the given gz_stream. Sets z_err in case
# of error.
def getLong(s)
x = 0.chr * 4
x[0] = (get_byte(s)).chr
x[1] = (get_byte(s)).chr
x[2] = (get_byte(s)).chr
x = Rubinius::ByteArray.new(4, 0)
x[0] = get_byte(s)
x[1] = get_byte(s)
x[2] = get_byte(s)
c = get_byte(s)
x[3] = (c).chr
x[3] = c

s.z_err = Z_DATA_ERROR if (c == Z_EOF)

@@ -915,7 +914,7 @@ def gzread(file,buf,len)
# or -1 in case of end of file or error.
#
def gzgetc(file)
c = 0.chr
c = 0
if (gzread(file,c,1) == 1)
return c
else
@@ -947,18 +946,18 @@ def gzgets(file,buf,len)
return nil if buf.nil? || (len <= 0)

i = 0
gzchar = 0.chr
gzchar = 0

loop do
len-=1
bytes = gzread(file, gzchar, 1)
buf[i] = gzchar[0]
i += 1
break if len == 0 || (bytes != 1) || (gzchar == (13).chr)
break if len == 0 || (bytes != 1) || (gzchar == 13)
end

buf[i..-1] = ''
buf.chomp!(0.chr)
buf[i, buf.length - i] = Rubinius::ByteArray.new(0, 0)
buf.chomp!(0)

if i == 0 && (len > 0)
return nil
@@ -1121,7 +1120,7 @@ def gzseek(file,offset,whence)
end

if (s.inbuf.nil?)
s.inbuf = 0.chr*Z_BUFSIZE
s.inbuf = Rubinius::ByteArray.new(Z_BUFSIZE, 0)
end

while (offset > 0)
@@ -1166,7 +1165,7 @@ def gzseek(file,offset,whence)
end

if offset != 0 && s.outbuf.nil?
s.outbuf = 0.chr * Z_BUFSIZE
s.outbuf = Rubinius::ByteArray.new(Z_BUFSIZE, 0)
end
if(offset != 0 && s.back != Z_EOF)
s.back = Z_EOF
@@ -1369,13 +1368,13 @@ def deflateInit2_(strm,level,method,windowBits,memLevel,strategy,version,stream_
s.hash_mask = s.hash_size - 1
s.hash_shift = ((s.hash_bits+MIN_MATCH-1) / MIN_MATCH)

s.window = 0.chr * (s.w_size * 2)
s.window = Rubinius::ByteArray.new(s.w_size * 2, 0)
s.prev = Array.new(s.w_size,0)
s.head = Array.new(s.hash_size,0)

s.lit_bufsize = 1 << (memLevel + 6)

overlay = 0.chr * (s.lit_bufsize * (2+2))
overlay = Rubinius::ByteArray.new(s.lit_bufsize * (2+2), 0)
s.pending_buf = Bytef.new(overlay)
s.pending_buf_size = (s.lit_bufsize) * (2+2)

@@ -2236,8 +2235,8 @@ def fill_window(s)
# Flush the current block, with given end-of-file flag.
# IN assertion: strstart is set to the end of the current match.
def FLUSH_BLOCK_ONLY(s,eof)
if (s.block_start >= (0))
_tr_flush_block(s, s.window[(s.block_start)..-1],
if (s.block_start >= 0)
_tr_flush_block(s, s.window[s.block_start, s.window.length - s.block_start],
((s.strstart) - s.block_start), eof)
else
_tr_flush_block(s, nil,
@@ -4084,7 +4083,7 @@ def updatewindow(strm, out)
state = strm.state

if state.window.nil?
state.window = 0.chr * (1 << state.wbits)
state.window = Rubinius::ByteArray.new(1 << state.wbits, 0)
return true if state.window.nil?
end

@@ -4127,19 +4126,19 @@ def UPDATE(state, check, buf)

# compute crc
def CRC2(check, word)
hbuf = 0.chr * 2
hbuf[0] = (word & 0xff).chr
hbuf[1] = ((word >> 8) & 0xff).chr
hbuf = Rubinius::ByteArray.new(2, 0)
hbuf[0] = (word & 0xff)
hbuf[1] = ((word >> 8) & 0xff)
check = crc32(check, hbuf)
end

# compute crc
def CRC4(check, word)
hbuf = 0.chr * 4
hbuf[0] = (word & 0xff).chr
hbuf[1] = ((word >> 8) & 0xff).chr
hbuf[2] = ((word >> 16) & 0xff).chr
hbuf[3] = ((word >> 24) & 0xff).chr
hbuf = Rubinius::ByteArray.new(4, 0)
hbuf[0] = (word & 0xff)
hbuf[1] = ((word >> 8) & 0xff)
hbuf[2] = ((word >> 16) & 0xff)
hbuf[3] = ((word >> 24) & 0xff)
check = crc32(check, hbuf)
end

@@ -4959,14 +4958,14 @@ def inflateSync(strm)
state = strm.state
return Z_BUF_ERROR if (strm.avail_in == 0 && state.bits < 8)

buf = 0.chr * 4
buf = Rubinius::ByteArray.new(4, 0)
if (state.mode != SYNC)
state.mode = SYNC
state.hold <<= state.bits & 7
state.bits -= state.bits & 7
len = 0
while (state.bits >= 8)
buf[len] = (state.hold).chr
buf[len] = state.hold
len+=1
state.hold >>= 8
state.bits -= 8
@@ -5012,7 +5011,7 @@ def inflateCopy(dest, source)
return Z_MEM_ERROR if copy.nil?
window = nil
if state.window
window = 0.chr * (1 << state.wbits)
window = Rubinius::ByteArray.new(1 << state.wbits, 0)
if window.nil?
copy = nil
return Z_MEM_ERROR
107 changes: 54 additions & 53 deletions lib/ruby/truffle/pr-zlib/lib/pr/zlib.rb
Original file line number Diff line number Diff line change
@@ -113,7 +113,7 @@ def raise_zlib_error(err, msg)

def zstream_expand_buffer()
if @buf.nil?
@buf = Bytef.new(0.chr * ZSTREAM_INITIAL_BUFSIZE)
@buf = Bytef.new(Rubinius::ByteArray.new(ZSTREAM_INITIAL_BUFSIZE, 0))
@stream.next_out = Bytef.new(@buf)
@stream.avail_out = ZSTREAM_INITIAL_BUFSIZE
return
@@ -127,7 +127,7 @@ def zstream_expand_buffer()
inc = ZSTREAM_AVAIL_OUT_STEP_MIN
end
if @buf.length < @buf.offset + inc
@buf.buffer << 0.chr * (@buf.offset + inc - @buf.length)
@buf.buffer << Rubinius::ByteArray.new(@buf.offset + inc - @buf.length, 0)
end
@stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ?
inc : ZSTREAM_AVAIL_OUT_STEP_MAX
@@ -143,7 +143,7 @@ def zstream_append_buffer(src, len)
return
end
if (@buf.length < @buf.offset + len)
@buf.buffer << (0.chr * (@buf.offset + len - @buf.length))
@buf.buffer << Rubinius::ByteArray.new(@buf.offset + len - @buf.length, 0)
@stream.avail_out = 0
else
if (@stream.avail_out >= len)
@@ -159,17 +159,17 @@ def zstream_append_buffer(src, len)

def zstream_detach_buffer()
if @buf.nil?
dst = ''
dst = Rubinius::ByteArray.new(0, 0)
else
dst = @buf.buffer[0,@buf.offset]
end

@buf = Bytef.new(0.chr * ZSTREAM_INITIAL_BUFSIZE)
@buf = Bytef.new(Rubinius::ByteArray.new(ZSTREAM_INITIAL_BUFSIZE, 0))
@stream.next_out = Bytef.new(@buf)
@stream.avail_out = ZSTREAM_INITIAL_BUFSIZE
@buf_filled = 0

return dst
return dst.to_str
end

def zstream_shift_buffer(len)
@@ -192,7 +192,7 @@ def zstream_buffer_ungetc(c)
if (@buf.nil? || (@buf.length - @buf.offset).zero?)
zstream_expand_buffer()
end
@buf.buffer[0,0] = c.chr
@buf.buffer[0,0] = c
@buf += 1
if (@stream.avail_out > 0)
@stream.next_out+=1
@@ -202,7 +202,7 @@ def zstream_buffer_ungetc(c)

def zstream_append_input(src, len)
return if (len <= 0)
src = src.current if src.class != String
src = src.current if src.class != Rubinius::ByteArray
if @input.nil?
@input = src[0,len]
else
@@ -214,7 +214,7 @@ def zstream_discard_input(len)
if (@input.nil? || @input.length <= len)
@input = nil
else
@input[0,len] = ''
@input[0,len] = Rubinius::ByteArray.new(0, 0)
end
end

@@ -406,12 +406,12 @@ def avail_out()

def avail_out=(size)
if @z.buf.nil?
@z.buf = Bytef.new(0.chr * size)
@z.buf = Bytef.new(Rubinius::ByteArray.new(size, 0))
@z.stream.next_out = Bytef.new(@z.buf)
@z.stream.avail_out = size
elsif @z.stream.avail_out != size
if @z.buf.offset + size > @z.buf.length
@z.buf.buffer << 0.chr * (@z.buf.offset + size - @z.buf.length)
@z.buf.buffer << Rubinius::ByteArray.new(@z.buf.offset + size - @z.buf.length, 0)
end
@z.stream.next_out = Bytef.new(@z.buf,@z.buf.offset)
@z.stream.avail_out = size
@@ -482,7 +482,7 @@ def reset()
end

def finish()
@z.zstream_run("", 0, Z_FINISH)
@z.zstream_run(Rubinius::ByteArray.new(0,0), 0, Z_FINISH)
@z.zstream_detach_buffer()
end

@@ -518,11 +518,11 @@ def self.deflate(src,level=Z_DEFAULT_COMPRESSION)
@z.ZSTREAM_READY()

begin
dst = deflate_run(src)
dst = deflate_run(Rubinius::ByteArray.from_string(src))
ensure
@z.zstream_end()
end
dst
dst.to_str
end

def initialize(level=Z_DEFAULT_COMPRESSION,wbits=MAX_WBITS,memlevel=DEF_MEM_LEVEL,strategy=Z_DEFAULT_STRATEGY)
@@ -547,11 +547,11 @@ def initialize_copy(orig)

def do_deflate(src,flush)
if src.nil?
@z.zstream_run('',0,Z_FINISH)
@z.zstream_run(Rubinius::ByteArray.new(0,0),0,Z_FINISH)
return
end
if (flush != Z_NO_FLUSH || (src && src.length>0))
@z.zstream_run(src,src.length,flush)
@z.zstream_run(Rubinius::ByteArray.from_string(src),src.length,flush)
end
end
private :do_deflate
@@ -568,7 +568,7 @@ def <<(src)

def flush(v_flush)
if(v_flush != Z_NO_FLUSH)
@z.zstream_run("", 0, flush)
@z.zstream_run(Rubinius::ByteArray.new(0, 0), 0, flush)
end
@z.zstream_detach_buffer()
end
@@ -588,7 +588,7 @@ def params(level=Z_DEFAULT_COMPRESSION,strategy=Z_DEFAULT_STRATEGY)
end

def set_dictionary(dic)
err = deflateSetDictionary(@z.stream,dic,dic.length)
err = deflateSetDictionary(@z.stream,Rubinius::ByteArray.from_string(dic),dic.length)
if (err != Z_OK)
raise_zlib_error(err, @z.stream.msg)
end
@@ -599,8 +599,8 @@ def set_dictionary(dic)
class Inflate < ZStream

def self.inflate_run(src)
@z.zstream_run(src,src.length,Z_SYNC_FLUSH)
@z.zstream_run('',0,Z_FINISH)
@z.zstream_run(Rubinius::ByteArray.from_string(src),src.length,Z_SYNC_FLUSH)
@z.zstream_run(Rubinius::ByteArray.new(0, 0),0,Z_FINISH)
@z.zstream_detach_buffer()
end

@@ -613,20 +613,20 @@ def self.inflate(src)
end
@z.ZSTREAM_READY()
begin
dst = inflate_run(src)
dst = inflate_run(Rubinius::ByteArray.from_string(src))
ensure
@z.zstream_end
end
dst
dst.to_str
end

def do_inflate(src)
if(src.nil?)
@z.zstream_run("", 0, Z_FINISH)
@z.zstream_run(Rubinius::ByteArray.new(0, 0), 0, Z_FINISH)
return
end
if (src.length>0)
@z.zstream_run(src,src.length,Z_SYNC_FLUSH)
@z.zstream_run(Rubinius::ByteArray.from_string(src),src.length,Z_SYNC_FLUSH)
end
end
private :do_inflate
@@ -647,7 +647,7 @@ def inflate(src)
dst = @z.zstream_detach_buffer()
else
@z.zstream_append_buffer(src,src.lenth)
dst = ''
dst = Rubinius::ByteArray.new(0, 0)
end
else
do_inflate(src)
@@ -657,16 +657,16 @@ def inflate(src)
end
end
if block_given?
yield dst
yield dst.to_str
else
dst
dst.to_str
end
end

def <<(src)
if @z.ZSTREAM_IS_FINISHED()
if src
@z.zstream_append_buffer(src,src.length)
@z.zstream_append_buffer(Rubinius::ByteArray.from_string(src),src.length)
end
else
do_inflate(src)
@@ -694,7 +694,7 @@ def sync_point?()
end

def set_dictionary(dic)
src = dic
src = Rubinius::ByteArray.from_string(dic)
err = inflateSetDictionary(@z.stream,src,src.length)

if err != Z_OK
@@ -885,11 +885,11 @@ def gzfile_get32(src)
end

def gzfile_set32(n)
[n].pack('V')
Rubinius::ByteArray.from_string([n].pack('V'))
end

def gzfile_make_header
buf = 0.chr * 10
buf = Rubinius::ByteArray.new(10, 0)
flags = 0
extraflags = 0
if @gz.orig_name
@@ -906,29 +906,28 @@ def gzfile_make_header
elsif (@gz.level == Z_BEST_COMPRESSION)
extraflags |= GZ_EXTRAFLAG_SLOW
end
buf[0] = GZ_MAGIC1.chr
buf[1] = GZ_MAGIC2.chr
buf[2] = GZ_METHOD_DEFLATE.chr
buf[3] = flags.chr
buf[0] = GZ_MAGIC1
buf[1] = GZ_MAGIC2
buf[2] = GZ_METHOD_DEFLATE
buf[3] = flags
buf[4,4] = gzfile_set32(@gz.mtime)
buf[8] = extraflags.chr
buf[9] = @gz.os_code.chr
buf[8] = extraflags
buf[9] = @gz.os_code
@gz.z.zstream_append_buffer(buf,buf.length)

if @gz.orig_name
@gz.z.zstream_append_buffer(@gz.orig_name,@gz.orig_name.length)
@gz.z.zstream_append_buffer("\0", 1)
@gz.z.zstream_append_buffer(Rubinius::ByteArray.from_string(@gz.orig_name),@gz.orig_name.length)
@gz.z.zstream_append_buffer(Rubinius::ByteArray.new(1, 0), 1)
end
if @gz.comment
@gz.z.zstream_append_buffer(@gz.comment,@gz.comment.length)
@gz.z.zstream_append_buffer("\0", 1)
@gz.z.zstream_append_buffer(Rubinius::ByteArray.from_string(@gz.comment),@gz.comment.length)
@gz.z.zstream_append_buffer(Rubinius::ByteArray.new(1, 0), 1)
end

@gz.z.flags |= GZFILE_FLAG_HEADER_FINISHED
end

def gzfile_make_footer()
buf = 0.chr * 8
buf = Rubinius::ByteArray.new(8, 0)
buf[0,4] = gzfile_set32(@gz.crc)
buf[4,4] = gzfile_set32(@gz.z.stream.total_in)
@gz.z.zstream_append_buffer(buf, buf.length)
@@ -1100,7 +1099,7 @@ def flush(v_flush=Z_SYNC_FLUSH)
raise GzipFile::Error, "closed gzip stream" unless @gz.z.ZSTREAM_IS_READY()

if v_flush != Z_NO_FLUSH
@gz.z.zstream_run("", 0, v_flush)
@gz.z.zstream_run(Rubinius::ByteArray.new(0, 0), 0, v_flush)
end

gzfile_write_raw()
@@ -1121,7 +1120,7 @@ def write(str)

def putc(ch)
raise GzipFile::Error, "closed gzip stream" unless @gz.z.ZSTREAM_IS_READY()
gzfile_write(ch.chr, 1)
gzfile_write(ch, 1)
ch
end

@@ -1154,10 +1153,11 @@ def gzfile_write_raw
private :gzfile_write_raw

def gzfile_write(str,len)
str = Rubinius::ByteArray.from_string(str)

if (@gz.z.flags & GZFILE_FLAG_HEADER_FINISHED).zero?
gzfile_make_header()
end

if (len > 0 || (@gz.z.flags & GZFILE_FLAG_SYNC))
@gz.crc = crc32(@gz.crc, str, len)
@gz.z.zstream_run(str, len, (@gz.z.flags & GZFILE_FLAG_SYNC).nonzero? ?
@@ -1172,7 +1172,7 @@ def gzfile_writer_end_run
if (@gz.z.flags & GZFILE_FLAG_HEADER_FINISHED).zero?
gzfile_make_header()
end
@gz.z.zstream_run("", 0, Z_FINISH)
@gz.z.zstream_run(Rubinius::ByteArray.new(0, 0), 0, Z_FINISH)
gzfile_make_footer()
gzfile_write_raw()

@@ -1266,7 +1266,7 @@ def readchar()
if dst.nil?
raise EOFError, "end of file reached"
end
dst
dst.to_str
end

def each_byte()
@@ -1283,6 +1283,7 @@ def ungetc(ch)

def gets(rs=$/)
dst = gzreader_gets(rs)
dst = dst.to_str if dst
$_ = dst if dst
dst
end
@@ -1412,7 +1413,7 @@ def gzreader_gets(rs=$/)
if (rspara)
gzreader_skip_linebreaks()
end
dst
dst.to_str
end

def gzfile_read(len)
@@ -1437,7 +1438,7 @@ def gzfile_read(len)

dst = @gz.z.zstream_shift_buffer(len)
gzfile_calc_crc(dst)
dst
dst.to_str
end

def gzfile_read_all()
@@ -1453,15 +1454,15 @@ def gzfile_read_all()

dst = @gz.z.zstream_detach_buffer()
gzfile_calc_crc(dst)
dst
dst.to_str
end

def gzfile_read_raw()
str = @gz.io.read(GZFILE_READ_SIZE)
if str && str.class != String
raise TypeError,"wrong argument type #{rs.class} (expected String)"
end
str
Rubinius::ByteArray.from_string(str)
end

def gzfile_read_raw_ensure(size)
@@ -1477,7 +1478,7 @@ def gzfile_read_raw_until_zero(offset)
ap = nil

loop do
ap = @gz.z.input[offset, @gz.z.input.length-offset].index(0.chr)
ap = @gz.z.input[offset, @gz.z.input.length-offset].index(0)
break if ap
str = gzfile_read_raw()

56 changes: 56 additions & 0 deletions spec/truffle/specs/truffle/byte_array/append_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray#append' do

before :each do
@byte_array = Rubinius::ByteArray.new(16, 0xff)
end

describe 'with string' do

before :each do
@string = "\u{6666}"
end

it "should add the string's bytes to the end of the byte array" do
original_size = @byte_array.size
@byte_array.append(@string)

@byte_array.size.should == original_size + @string.bytesize

@string.bytes.each_with_index do |value, index|
@byte_array[original_size + index].should == value
end
end

end

describe 'with byte array' do

before :each do
@other_byte_array = Rubinius::ByteArray.new(3, 0xaa)
end

it "should add the other byte array's bytes to the end of the byte array" do
original_size = @byte_array.size
@byte_array.append(@other_byte_array)

@byte_array.size.should == original_size + @other_byte_array.size

@other_byte_array.size.times do |index|
@byte_array[original_size + index].should == @other_byte_array[index]
end
end

end

end
41 changes: 41 additions & 0 deletions spec/truffle/specs/truffle/byte_array/chomp_bang_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray#chomp!' do

before :each do
@byte_array = Rubinius::ByteArray.new(16, 0xff)
end

describe 'with value present at end of byte array' do

it 'should truncate the portion of the byte array after the found byte' do
original_size = @byte_array.size

@byte_array.chomp!(0xff).should_not be_nil
@byte_array.size.should == original_size - 1
end

end

describe 'viwth value present, but not at end of byte array' do

it 'should truncate the portion of the byte array after the found byte' do
@byte_array[@byte_array.size - 1] = 0xcc
original_size = @byte_array.size

@byte_array.chomp!(0xff).should be_nil
@byte_array.size.should == original_size
end

end

end
50 changes: 50 additions & 0 deletions spec/truffle/specs/truffle/byte_array/element_reference_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray#[]' do

before :each do
@byte_array = Rubinius::ByteArray.new(16, 0xff)
end

describe 'with index' do

it 'should return return a single byte value if in range' do
@byte_array[0].should == 0xff
end

it 'should raise an error if out of range (negative indices do not wrap around)' do
lambda { @byte_array[-1] }.should raise_error(IndexError)
lambda { @byte_array[@byte_array.length + 1] }.should raise_error(IndexError)
end

end

describe 'with index and length' do

it 'should return a newly allocated Rubinius::ByteArray' do
@byte_array.size.times { |i| @byte_array[i] = i }

new_byte_array = @byte_array[1, 3]
new_byte_array.size.should == 3
new_byte_array[0].should == 0x01
new_byte_array[1].should == 0x02
new_byte_array[2].should == 0x03
end

it 'should raise an error if out of range (negative indices do not wrap around)' do
lambda { @byte_array[-1, 3] }.should raise_error(IndexError)
lambda { @byte_array[@byte_array.length + 1, 3] }.should raise_error(IndexError)
end

end

end
33 changes: 33 additions & 0 deletions spec/truffle/specs/truffle/byte_array/from_string_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray.from_string' do

before :each do
@string = "\u{6666}"
@byte_array = Rubinius::ByteArray.from_string(@string)
end

describe 'with variable-width character string' do

it 'should have the same byte length as the string' do
@byte_array.size.should == @string.bytesize
end

it 'should have the same bytes as the string' do
@string.bytes.each_with_index do |value, index|
@byte_array[index].should == value
end
end

end

end
40 changes: 40 additions & 0 deletions spec/truffle/specs/truffle/byte_array/index_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray#index' do

before :each do
@byte_array = Rubinius::ByteArray.new(16, 0xff)
end

describe 'with value not in string' do

it 'should return nil' do
@byte_array.index(0xbb).should be_nil
end

end

describe 'with index and length' do

it 'should return the index corresponding to the first occurrence of the value' do

@byte_array[5] = 0xcc
@byte_array.index(0xcc).should == 5

@byte_array[2] = 0xcc
@byte_array.index(0xcc).should == 2

end

end

end
30 changes: 30 additions & 0 deletions spec/truffle/specs/truffle/byte_array/to_str_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe 'Rubinius::ByteArray.to_str' do

before :each do
@byte_array = Rubinius::ByteArray.new(3, 0)
@byte_array[0] = 'a'.ord
@byte_array[1] = 'b'.ord
@byte_array[2] = 'c'.ord
end

describe 'with ASCII characters' do

it 'should be an ASCII-8BIT (binary) string' do
@byte_array.to_str.encoding.should == Encoding::ASCII_8BIT
@byte_array.to_str.should == 'abc'
end

end

end
Original file line number Diff line number Diff line change
@@ -1764,6 +1764,7 @@ public Object getTruffleKernelModule() {
"/core/truffle/cext.rb",
"/core/truffle/interop.rb",
"/core/rbconfig.rb",
"/core/byte_array.rb",
"/core/main.rb",
"/core/post.rb"
};
Original file line number Diff line number Diff line change
@@ -27,5 +27,6 @@ DynamicObject createByteArray(DynamicObjectFactory factory,
boolean isByteArray(DynamicObject object);

ByteList getBytes(DynamicObject object);
void setBytes(DynamicObject object, ByteList value);

}
Original file line number Diff line number Diff line change
@@ -16,15 +16,22 @@
import com.oracle.truffle.api.object.DynamicObjectFactory;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.builtins.CoreClass;
import org.jruby.truffle.builtins.CoreMethod;
import org.jruby.truffle.builtins.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.builtins.UnaryCoreMethodNode;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.string.ByteList;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.objects.AllocateObjectNode;

import java.util.Arrays;

@CoreClass("Rubinius::ByteArray")
public abstract class ByteArrayNodes {
@@ -33,14 +40,20 @@ public static DynamicObject createByteArray(DynamicObjectFactory factory, ByteLi
return Layouts.BYTE_ARRAY.createByteArray(factory, bytes);
}

@CoreMethod(names = {"get_byte", "[]"}, required = 1, lowerFixnum = 1)
@CoreMethod(names = {"get_byte", "getbyte", "[]"}, required = 1, optional = 1, lowerFixnum = { 1, 2 })
public abstract static class GetByteNode extends CoreMethodArrayArgumentsNode {

@Specialization
public int getByte(DynamicObject bytes, int index,
@Specialization(guards = "wasNotProvided(length)")
public int getByte(DynamicObject bytes, int index, Object length,
@Cached("create()") BranchProfile errorProfile,
@Cached("createBinaryProfile()") ConditionProfile nullByteIndexProfile) {
final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(bytes);

if (index < 0 || index >= byteList.length()) {
errorProfile.enter();
throw new RaiseException(coreExceptions().indexError("index out of bounds", this));
}

// Handling out-of-bounds issues like this is non-standard. In Rubinius, it would raise an exception instead.
// We're modifying the semantics to address a primary use case for this class: Rubinius's @data array
// in the String class. Rubinius Strings are NULL-terminated and their code working with Strings takes
@@ -54,6 +67,62 @@ public int getByte(DynamicObject bytes, int index,
return byteList.get(index) & 0xff;
}

@Specialization
public DynamicObject getBytes(DynamicObject bytes, int index, int length,
@Cached("create()") BranchProfile errorProfile,
@Cached("createBinaryProfile()") ConditionProfile nullByteIndexProfile) {
final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(bytes);

if (index < 0 || index >= byteList.length()) {
errorProfile.enter();
throw new RaiseException(coreExceptions().indexError("index out of bounds", this));
}

final byte[] newBytes = new byte[length];

System.arraycopy(byteList.unsafeBytes(), byteList.begin() + index, newBytes, 0, length);

return Layouts.BYTE_ARRAY.createByteArray(getContext().getCoreLibrary().getByteArrayFactory(), new ByteList(newBytes, ASCIIEncoding.INSTANCE, false));
}

}

@CoreMethod(names = "index", required = 1, lowerFixnum = 1)
public abstract static class IndexNode extends CoreMethodArrayArgumentsNode {

@Specialization
public Object index(DynamicObject byteArray, int value) {
final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(byteArray);
final byte[] bytes = byteList.unsafeBytes();

for (int i = byteList.begin(); i < byteList.begin() + byteList.length(); i++) {
if (bytes[i] == (byte) value) {
return i;
}
}

return nil();
}
}

@CoreMethod(names = {"append", "<<"}, required = 1)
public abstract static class AppendNode extends CoreMethodArrayArgumentsNode {

@Specialization(guards = "isRubyString(string)")
public DynamicObject appendString(DynamicObject bytes, DynamicObject string) {
final Rope rope = StringOperations.rope(string);
Layouts.BYTE_ARRAY.getBytes(bytes).append(rope.getBytes());

return bytes;
}

@Specialization(guards = "isRubiniusByteArray(otherBytes)")
public DynamicObject appendByteArray(DynamicObject bytes, DynamicObject otherBytes) {
Layouts.BYTE_ARRAY.getBytes(bytes).append(Layouts.BYTE_ARRAY.getBytes(otherBytes));

return bytes;
}

}

@CoreMethod(names = "prepend", required = 1)
@@ -73,11 +142,11 @@ public DynamicObject prepend(DynamicObject bytes, DynamicObject string) {

}

@CoreMethod(names = {"set_byte", "[]="}, required = 2, lowerFixnum = { 1, 2 })
@CoreMethod(names = {"set_byte", "setbyte", "[]="}, required = 2, optional = 1, lowerFixnum = { 1, 2 })
public abstract static class SetByteNode extends CoreMethodArrayArgumentsNode {

@Specialization
public Object setByte(DynamicObject bytes, int index, int value,
public Object setByte(DynamicObject bytes, int index, int value, NotProvided otherByteArray,
@Cached("create()") BranchProfile errorProfile) {
if (index < 0 || index >= Layouts.BYTE_ARRAY.getBytes(bytes).getRealSize()) {
errorProfile.enter();
@@ -88,9 +157,41 @@ public Object setByte(DynamicObject bytes, int index, int value,
return Layouts.BYTE_ARRAY.getBytes(bytes).get(index);
}

@Specialization(guards = "isRubiniusByteArray(otherByteArray)")
public Object setByte(DynamicObject bytes, int begin, int length, DynamicObject otherByteArray,
@Cached("create()") BranchProfile errorProfile) {
if (begin < 0 || begin >= Layouts.BYTE_ARRAY.getBytes(bytes).getRealSize()) {
errorProfile.enter();
throw new RaiseException(coreExceptions().indexError("index out of bounds", this));
}

final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(bytes);
final ByteList otherByteList = Layouts.BYTE_ARRAY.getBytes(otherByteArray);

if (byteList.length() < begin) {
errorProfile.enter();
throw new RaiseException(coreExceptions().indexError("index out of bounds", this));
}

if ((byteList.length() < length) || (byteList.length() < (begin + length))) {
length = byteList.length() - begin;
}

final int newArrayLength = (begin - byteList.begin()) + otherByteList.length() + (byteList.length() - (byteList.begin() + begin + length));
final byte[] newBytes = new byte[newArrayLength];

System.arraycopy(byteList.unsafeBytes(), byteList.begin(), newBytes, 0, byteList.begin() + begin);
System.arraycopy(otherByteList.unsafeBytes(), otherByteList.begin(), newBytes, begin, otherByteList.length());
System.arraycopy(byteList.unsafeBytes(), byteList.begin() + begin + length, newBytes, begin + otherByteList.length(), byteList.length() - (byteList.begin() + begin + length));

Layouts.BYTE_ARRAY.setBytes(bytes, new ByteList(newBytes, ASCIIEncoding.INSTANCE, false));

return otherByteArray;
}

}

@CoreMethod(names = "size")
@CoreMethod(names = {"size", "length"})
public abstract static class SizeNode extends CoreMethodArrayArgumentsNode {

@Specialization
@@ -116,13 +217,46 @@ public Object getByte(DynamicObject bytes, DynamicObject pattern, int start, int

}

@CoreMethod(names = "truncate", required = 1, lowerFixnum = 1)
public abstract static class TruncateNode extends CoreMethodArrayArgumentsNode {

@Specialization
public DynamicObject index(DynamicObject byteArray, int index) {
final ByteList byteList = Layouts.BYTE_ARRAY.getBytes(byteArray);
byteList.length(index);

return byteArray;
}
}

@CoreMethod(names = "allocate", constructor = true)
public abstract static class AllocateNode extends UnaryCoreMethodNode {

@TruffleBoundary
@Child private AllocateObjectNode allocateObjectNode;

public AllocateNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
allocateObjectNode = AllocateObjectNode.create();
}

@Specialization
public DynamicObject allocate(DynamicObject rubyClass) {
throw new RaiseException(coreExceptions().typeErrorAllocatorUndefinedFor(rubyClass, this));
return allocateObjectNode.allocate(rubyClass, ByteList.EMPTY_BYTELIST);
}

}

@CoreMethod(names = "initialize", required = 2, lowerFixnum = { 1, 2 })
public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode {

@Specialization
public DynamicObject initialize(DynamicObject byteArray, int size, int value) {
final byte[] bytes = new byte[size];
Arrays.fill(bytes, (byte) value);

Layouts.BYTE_ARRAY.setBytes(byteArray, new ByteList(bytes, ASCIIEncoding.INSTANCE, false));

return byteArray;
}

}
33 changes: 33 additions & 0 deletions truffle/src/main/ruby/core/byte_array.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1
# OTHER DEALINGS IN THE SOFTWARE.

module Rubinius
class ByteArray

def self.from_string(string)
new(0, 0).append(string)
end

def to_str
String.from_bytearray(self, 0, size)
end

def chomp!(value)
if self[size - 1] == value
return truncate(size - 1)
end

nil
end

def unpack(format)
to_str.unpack(format)
end
end
end