-
-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into truffle-head
- 9.4.12.0
- 9.4.11.0
- 9.4.10.0
- 9.4.9.0
- 9.4.8.0
- 9.4.7.0
- 9.4.6.0
- 9.4.5.0
- 9.4.4.0
- 9.4.3.0
- 9.4.2.0
- 9.4.1.0
- 9.4.0.0
- 9.3.15.0
- 9.3.14.0
- 9.3.13.0
- 9.3.12.0
- 9.3.11.0
- 9.3.10.0
- 9.3.9.0
- 9.3.8.0
- 9.3.7.0
- 9.3.6.0
- 9.3.5.0
- 9.3.4.0
- 9.3.3.0
- 9.3.2.0
- 9.3.1.0
- 9.3.0.0
- 9.2.21.0
- 9.2.20.1
- 9.2.20.0
- 9.2.19.0
- 9.2.18.0
- 9.2.17.0
- 9.2.16.0
- 9.2.15.0
- 9.2.14.0
- 9.2.13.0
- 9.2.12.0
- 9.2.11.1
- 9.2.11.0
- 9.2.10.0
- 9.2.9.0
- 9.2.8.0
- 9.2.7.0
- 9.2.6.0
- 9.2.5.0
- 9.2.4.1
- 9.2.4.0
- 9.2.3.0
- 9.2.2.0
- 9.2.1.0
- 9.2.0.0
- 9.1.17.0
- 9.1.16.0
- 9.1.15.0
- 9.1.14.0
- 9.1.13.0
- 9.1.12.0
- 9.1.11.0
- 9.1.10.0
- 9.1.9.0
- 9.1.8.0
- 9.1.7.0
- 9.1.6.0
- 9.1.5.0
- 9.1.4.0
- 9.1.3.0
Showing
5 changed files
with
608 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
exclude :test_dump, "tests MRI-specific exception" | ||
exclude :test_queue_close_multi_multi, "flakey on Travis: no threads runnning" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
562 changes: 562 additions & 0 deletions
562
truffle/src/main/java/org/jruby/truffle/core/string/ConvertBytes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,562 @@ | ||
/* | ||
* 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 | ||
* | ||
* This is a port of org.jruby.util.ConvertBytes to work with the JRuby+Truffle backend. | ||
* The original class is licensed under the same EPL1.0/GPL 2.0/LGPL 2.1 used throughout. | ||
*/ | ||
|
||
package org.jruby.truffle.core.string; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | ||
import com.oracle.truffle.api.nodes.Node; | ||
import com.oracle.truffle.api.object.DynamicObject; | ||
import org.jruby.truffle.RubyContext; | ||
import org.jruby.truffle.core.CoreLibrary; | ||
import org.jruby.truffle.core.numeric.FixnumOrBignumNode; | ||
import org.jruby.truffle.core.rope.Rope; | ||
import org.jruby.truffle.language.control.RaiseException; | ||
import org.jruby.util.ByteList; | ||
|
||
import java.math.BigInteger; | ||
import java.util.Arrays; | ||
|
||
public class ConvertBytes { | ||
private final RubyContext context; | ||
private final Node node; | ||
private final FixnumOrBignumNode fixnumOrBignumNode; | ||
private final DynamicObject _str; | ||
private int str; | ||
private int end; | ||
private byte[] data; | ||
private int base; | ||
private final boolean badcheck; | ||
|
||
public ConvertBytes(RubyContext context, Node node, FixnumOrBignumNode fixnumOrBignumNode, DynamicObject _str, int base, boolean badcheck) { | ||
final Rope rope = StringOperations.rope(_str); | ||
|
||
this.context = context; | ||
this.node = node; | ||
this.fixnumOrBignumNode = fixnumOrBignumNode; | ||
this._str = _str; | ||
this.str = 0; | ||
this.data = rope.getBytes(); | ||
this.end = str + rope.byteLength(); | ||
this.badcheck = badcheck; | ||
this.base = base; | ||
} | ||
|
||
private static final byte[][] MIN_VALUE_BYTES; | ||
static { | ||
MIN_VALUE_BYTES = new byte[37][]; | ||
for (int i = 2; i <= 36; i++) { | ||
MIN_VALUE_BYTES[i] = ByteList.plain(Long.toString(Long.MIN_VALUE, i)); | ||
} | ||
} | ||
|
||
/** rb_cstr_to_inum | ||
* | ||
*/ | ||
|
||
public static Object byteListToInum19(RubyContext context, Node node, FixnumOrBignumNode fixnumOrBignumNode, DynamicObject str, int base, boolean badcheck) { | ||
return new ConvertBytes(context, node, fixnumOrBignumNode, str, base, badcheck).byteListToInum(); | ||
} | ||
|
||
private final static byte[] conv_digit = new byte[128]; | ||
private final static boolean[] digit = new boolean[128]; | ||
private final static boolean[] space = new boolean[128]; | ||
private final static boolean[] spaceOrUnderscore = new boolean[128]; | ||
|
||
static { | ||
Arrays.fill(conv_digit, (byte)-1); | ||
Arrays.fill(digit, false); | ||
for(char c = '0'; c <= '9'; c++) { | ||
conv_digit[c] = (byte)(c - '0'); | ||
digit[c] = true; | ||
} | ||
|
||
for(char c = 'a'; c <= 'z'; c++) { | ||
conv_digit[c] = (byte)(c - 'a' + 10); | ||
} | ||
|
||
for(char c = 'A'; c <= 'Z'; c++) { | ||
conv_digit[c] = (byte)(c - 'A' + 10); | ||
} | ||
|
||
Arrays.fill(space, false); | ||
space['\t'] = true; | ||
space['\n'] = true; | ||
space[11] = true; // \v | ||
space['\f'] = true; | ||
space['\r'] = true; | ||
space[' '] = true; | ||
|
||
Arrays.fill(spaceOrUnderscore, false); | ||
spaceOrUnderscore['\t'] = true; | ||
spaceOrUnderscore['\n'] = true; | ||
spaceOrUnderscore[11] = true; // \v | ||
spaceOrUnderscore['\f'] = true; | ||
spaceOrUnderscore['\r'] = true; | ||
spaceOrUnderscore[' '] = true; | ||
spaceOrUnderscore['_'] = true; | ||
} | ||
|
||
/** conv_digit | ||
* | ||
*/ | ||
private byte convertDigit(byte c) { | ||
if(c < 0) { | ||
return -1; | ||
} | ||
return conv_digit[c]; | ||
} | ||
|
||
/** ISSPACE | ||
* | ||
*/ | ||
private boolean isSpace(int str) { | ||
byte c; | ||
if(str == end || (c = data[str]) < 0) { | ||
return false; | ||
} | ||
return space[c]; | ||
} | ||
|
||
/** ISSPACE || *str == '_' | ||
* | ||
*/ | ||
private boolean isSpaceOrUnderscore(int str) { | ||
byte c; | ||
if(str == end || (c = data[str]) < 0) { | ||
return false; | ||
} | ||
return spaceOrUnderscore[c]; | ||
} | ||
|
||
private boolean getSign() { | ||
boolean sign = true; | ||
if(str < end) { | ||
if(data[str] == '+') { | ||
str++; | ||
} else if(data[str] == '-') { | ||
str++; | ||
sign = false; | ||
} | ||
} | ||
|
||
return sign; | ||
} | ||
|
||
private void ignoreLeadingWhitespace() { | ||
while(isSpace(str)) { | ||
str++; | ||
} | ||
} | ||
|
||
private void figureOutBase() { | ||
if(base <= 0) { | ||
if(str < end && data[str] == '0') { | ||
if(str + 1 < end) { | ||
switch(data[str+1]) { | ||
case 'x': | ||
case 'X': | ||
base = 16; | ||
break; | ||
case 'b': | ||
case 'B': | ||
base = 2; | ||
break; | ||
case 'o': | ||
case 'O': | ||
base = 8; | ||
break; | ||
case 'd': | ||
case 'D': | ||
base = 10; | ||
break; | ||
default: | ||
base = 8; | ||
} | ||
} else { | ||
base = 8; | ||
} | ||
} else if(base < -1) { | ||
base = -base; | ||
} else { | ||
base = 10; | ||
} | ||
} | ||
} | ||
|
||
private int calculateLength() { | ||
int len = 0; | ||
byte second = ((str+1 < end) && data[str] == '0') ? data[str+1] : (byte)0; | ||
|
||
switch(base) { | ||
case 2: | ||
len = 1; | ||
if(second == 'b' || second == 'B') { | ||
str+=2; | ||
} | ||
break; | ||
case 3: | ||
len = 2; | ||
break; | ||
case 8: | ||
if(second == 'o' || second == 'O') { | ||
str+=2; | ||
} | ||
case 4: case 5: case 6: case 7: | ||
len = 3; | ||
break; | ||
case 10: | ||
if(second == 'd' || second == 'D') { | ||
str+=2; | ||
} | ||
case 9: case 11: case 12: | ||
case 13: case 14: case 15: | ||
len = 4; | ||
break; | ||
case 16: | ||
len = 4; | ||
if(second == 'x' || second == 'X') { | ||
str+=2; | ||
} | ||
break; | ||
default: | ||
if(base < 2 || 36 < base) { | ||
throw new RaiseException( | ||
context.getCoreExceptions().argumentErrorInvalidRadix(base, node)); | ||
} | ||
if(base <= 32) { | ||
len = 5; | ||
} else { | ||
len = 6; | ||
} | ||
break; | ||
} | ||
|
||
return len; | ||
} | ||
|
||
private void squeezeZeroes() { | ||
byte c; | ||
if(str < end && data[str] == '0') { | ||
str++; | ||
int us = 0; | ||
while((str < end) && ((c = data[str]) == '0' || c == '_')) { | ||
if(c == '_') { | ||
if(++us >= 2) { | ||
break; | ||
} | ||
} else { | ||
us += 0; | ||
} | ||
str++; | ||
} | ||
if(str == end || isSpace(str)) { | ||
str--; | ||
} | ||
} | ||
} | ||
|
||
private long stringToLong(int nptr, int[] endptr, int base) { | ||
if(base < 0 || base == 1 || base > 36) { | ||
return 0; | ||
} | ||
int save = nptr; | ||
int s = nptr; | ||
boolean overflow = false; | ||
|
||
while(isSpace(s)) { | ||
s++; | ||
} | ||
|
||
if(s != end) { | ||
boolean negative = false; | ||
if(data[s] == '-') { | ||
negative = true; | ||
s++; | ||
} else if(data[s] == '+') { | ||
negative = false; | ||
s++; | ||
} | ||
|
||
save = s; | ||
byte c; | ||
long i = 0; | ||
|
||
final long cutoff = Long.MAX_VALUE / (long)base; | ||
final long cutlim = Long.MAX_VALUE % (long)base; | ||
|
||
while(s < end) { | ||
c = convertDigit(data[s]); | ||
|
||
if(c == -1 || c >= base) { | ||
break; | ||
} | ||
s++; | ||
|
||
if(i > cutoff || (i == cutoff && c > cutlim)) { | ||
overflow = true; | ||
} else { | ||
i *= base; | ||
i += c; | ||
} | ||
} | ||
|
||
if(s != save) { | ||
if(endptr != null) { | ||
endptr[0] = s; | ||
} | ||
|
||
if(overflow) { | ||
throw new ConvertBytes.ERange(negative ? ConvertBytes.ERange.Kind.Underflow : ConvertBytes.ERange.Kind.Overflow); | ||
} | ||
|
||
if(negative) { | ||
return -i; | ||
} else { | ||
return i; | ||
} | ||
} | ||
} | ||
|
||
if(endptr != null) { | ||
if(save - nptr >= 2 && (data[save-1] == 'x' || data[save-1] == 'X') && data[save-2] == '0') { | ||
endptr[0] = save-1; | ||
} else { | ||
endptr[0] = nptr; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
@TruffleBoundary | ||
public Object byteListToInum() { | ||
if(_str == null) { | ||
if(badcheck) { | ||
invalidString("Integer"); | ||
} | ||
return 0; | ||
} | ||
|
||
ignoreLeadingWhitespace(); | ||
|
||
boolean sign = getSign(); | ||
|
||
if(str < end) { | ||
if(data[str] == '+' || data[str] == '-') { | ||
if(badcheck) { | ||
invalidString("Integer"); | ||
} | ||
return 0; | ||
} | ||
} | ||
|
||
figureOutBase(); | ||
|
||
int len = calculateLength(); | ||
|
||
squeezeZeroes(); | ||
|
||
byte c = 0; | ||
if(str < end) { | ||
c = data[str]; | ||
} | ||
c = convertDigit(c); | ||
if(c < 0 || c >= base) { | ||
if(badcheck) { | ||
invalidString("Integer"); | ||
} | ||
return 0; | ||
} | ||
|
||
if (base <= 10) { | ||
len *= (trailingLength()); | ||
} else { | ||
len *= (end-str); | ||
} | ||
|
||
if(len < Long.SIZE-1) { | ||
int[] endPlace = new int[]{str}; | ||
long val = stringToLong(str, endPlace, base); | ||
|
||
if(endPlace[0] < end && data[endPlace[0]] == '_') { | ||
return bigParse(len, sign); | ||
} | ||
if(badcheck) { | ||
if(endPlace[0] == str) { | ||
invalidString("Integer"); // no number | ||
} | ||
|
||
while(isSpace(endPlace[0])) { | ||
endPlace[0]++; | ||
} | ||
|
||
if(endPlace[0] < end) { | ||
invalidString("Integer"); // trailing garbage | ||
} | ||
} | ||
|
||
if(sign) { | ||
if (CoreLibrary.fitsIntoInteger(val)) { | ||
return (int) val; | ||
} else { | ||
return val; | ||
} | ||
} else { | ||
if (CoreLibrary.fitsIntoInteger(-val)) { | ||
return (int) -val; | ||
} else { | ||
return -val; | ||
} | ||
} | ||
} | ||
return bigParse(len, sign); | ||
} | ||
|
||
private int trailingLength() { | ||
int newLen = 0; | ||
for (int i=str; i < end; i++) { | ||
if (Character.isDigit(data[i])) newLen++; | ||
else return newLen; | ||
} | ||
return newLen; | ||
} | ||
|
||
private Object bigParse(int len, boolean sign) { | ||
if(badcheck && str < end && data[str] == '_') { | ||
invalidString("Integer"); | ||
} | ||
|
||
char[] result = new char[end-str]; | ||
int resultIndex = 0; | ||
|
||
byte nondigit = -1; | ||
|
||
// str2big_scan_digits | ||
{ | ||
while(str < end) { | ||
byte c = data[str++]; | ||
byte cx = c; | ||
if(c == '_') { | ||
if(nondigit != -1) { | ||
if(badcheck) { | ||
invalidString("Integer"); | ||
} | ||
break; | ||
} | ||
nondigit = c; | ||
continue; | ||
} else if((c = convertDigit(c)) < 0) { | ||
break; | ||
} | ||
if(c >= base) { | ||
break; | ||
} | ||
nondigit = -1; | ||
result[resultIndex++] = (char)cx; | ||
} | ||
|
||
if(resultIndex == 0) { return 0; } | ||
|
||
int tmpStr = str; | ||
if (badcheck) { | ||
// no str-- here because we don't null-terminate strings | ||
if (1 < tmpStr && data[tmpStr-1] == '_') invalidString("Integer"); | ||
while (tmpStr < end && Character.isWhitespace(data[tmpStr])) tmpStr++; | ||
if (tmpStr < end) { | ||
invalidString("Integer"); | ||
} | ||
|
||
} | ||
} | ||
|
||
String s = new String(result, 0, resultIndex); | ||
BigInteger z = (base == 10) ? stringToBig(s) : new BigInteger(s, base); | ||
if(!sign) { z = z.negate(); } | ||
|
||
if(badcheck) { | ||
if(1 < str && data[str-1] == '_') { | ||
invalidString("Integer"); | ||
} | ||
while(str < end && isSpace(str)) { | ||
str++; | ||
} | ||
if(str < end) { | ||
invalidString("Integer"); | ||
} | ||
} | ||
|
||
return fixnumOrBignumNode.fixnumOrBignum(z); | ||
} | ||
|
||
private BigInteger stringToBig(String str) { | ||
str = str.replaceAll("_", ""); | ||
int size = str.length(); | ||
int nDigits = 512; | ||
if (size < nDigits) { nDigits = size; } | ||
|
||
int j = size - 1; | ||
int i = j - nDigits + 1; | ||
|
||
BigInteger digits[] = new BigInteger[j / nDigits + 1]; | ||
|
||
for(int z = 0; j >= 0; z++) { | ||
digits[z] = new BigInteger(str.substring(i, j + 1).trim()); | ||
j = i - 1; | ||
i = j - nDigits + 1; | ||
if(i < 0) { i = 0; } | ||
} | ||
|
||
BigInteger b10x = BigInteger.TEN.pow(nDigits); | ||
int n = digits.length; | ||
while(n > 1) { | ||
i = 0; | ||
j = 0; | ||
while(i < n / 2) { | ||
digits[i] = digits[j].add(digits[j + 1].multiply(b10x)); | ||
i += 1; | ||
j += 2; | ||
} | ||
if(j == n-1) { | ||
digits[i] = digits[j]; | ||
i += 1; | ||
} | ||
n = i; | ||
b10x = b10x.multiply(b10x); | ||
} | ||
|
||
return digits[0]; | ||
} | ||
|
||
public static class ERange extends RuntimeException { | ||
public static enum Kind {Overflow, Underflow}; | ||
private ConvertBytes.ERange.Kind kind; | ||
public ERange() { | ||
super(); | ||
} | ||
public ERange(ConvertBytes.ERange.Kind kind) { | ||
super(); | ||
this.kind = kind; | ||
} | ||
public ConvertBytes.ERange.Kind getKind() { | ||
return kind; | ||
} | ||
} | ||
|
||
/** rb_invalid_str | ||
* | ||
*/ | ||
@TruffleBoundary | ||
private void invalidString(String type) { | ||
throw new RaiseException( | ||
context.getCoreExceptions().argumentErrorInvalidValue(_str, type, node)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters