Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruby 2.7: Better support of Integer#[] with range arguments #2182

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions spec/tags/core/integer/element_reference_tags.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1 @@
fails:Integer#[] fixnum when index and length passed returns specified number of bits from specified position
fails:Integer#[] fixnum when index and length passed ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)
fails:Integer#[] fixnum when index and length passed moves start position to the most significant bits when negative index passed
fails:Integer#[] fixnum when index and length passed ignores negative length
fails:Integer#[] fixnum when range passed returns bits specified by range
fails:Integer#[] fixnum when range passed ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)
fails:Integer#[] fixnum when range passed ensures n[i..] equals to (n >> i)
fails:Integer#[] fixnum when range passed moves lower boundary to the most significant bits when negative value passed
fails:Integer#[] fixnum when range passed ignores negative upper boundary
fails:Integer#[] fixnum when range passed ignores upper boundary smaller than lower boundary
fails:Integer#[] fixnum when range passed raises FloatDomainError if any boundary is infinity
fails:Integer#[] fixnum when range passed when passed (..i) returns 0 if all i bits equal 0
fails:Integer#[] fixnum when range passed when passed (..i) raises ArgumentError if any of i bit equals 1
72 changes: 69 additions & 3 deletions src/main/ruby/truffleruby/core/integer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
Object.deprecate_constant :Fixnum, :Bignum

class Integer < Numeric
FIXNUM_MAX = 0x3fffffff
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eregon not sure that it's safe to hardcode this value, but I did't find better solution, mb you know?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to me we even need this value to implement Integer#[], do you know which arguments return it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this anymore.


# Have a copy in Integer of the Numeric version, as MRI does
alias_method :remainder, :remainder
Expand All @@ -59,9 +60,74 @@ def **(o)
redo_coerced :**, o
end

def [](index)
index = Primitive.rb_to_int(index)
index < 0 ? 0 : (self >> index) & 1
def [](index, len = undefined)
cmp = -> (a, b) {
return FIXNUM_MAX if Primitive.nil?(b)
return a - b
}
fix_aref = -> (num, idx) {
val = Primitive.rb_num2long(num)
idx = Primitive.rb_to_int idx
if !Truffle::Type.fits_into_long?(idx)
if idx < 0 || val >= 0
return 0
else
return 1
end
end
return 0 if idx < 0
return 1 if (val & (1 << idx) > 0)
return 0
}

if index.kind_of?(Range)
exclude_end = index.exclude_end?
lm = index.begin
rm = index.end
if Primitive.nil?(lm)
if !Primitive.nil?(rm) && rm >= 0
rm += 1 if !exclude_end
mask = (1 << rm) - 1
if (self & mask) == 0
return 0
else
raise ArgumentError,
'The beginless range for Integer#[] results in infinity'
end
else
return 0
end
end
num = self >> lm
cmp = cmp.call(lm, rm)

if !Primitive.nil?(rm) && cmp < 0
len = rm - lm

len += 1 if !exclude_end

mask = (1 << len) - 1
num = num & mask
elsif cmp == 0
return 0 if exclude_end
num = self
arg = lm
return Truffle::Type.fits_into_long?(num) ? fix_aref.call(num, arg) : self[lm]

end
num
else
#not range
index = Primitive.rb_to_int(index)
if Primitive.undefined?(len)
return index < 0 ? 0 : (self >> index) & 1
else
num = self >> index
mask = (1 << len) - 1

return num & mask
end
end
end

def allbits?(mask)
Expand Down
1 change: 0 additions & 1 deletion test/mri/excludes/TestInteger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@
exclude :test_pow, "needs investigation"
exclude :test_truncate, "needs investigation"
exclude :test_Integer_with_invalid_exception, "needs investigation"
exclude :test_aref, "needs investigation"