Skip to content

Commit 5f21ffe

Browse files
korzhazbynek
authored andcommitted
Emulate unsigned numbers Java 8 API.
Some code and tests adapted from google guava libraries. Change-Id: Ibcc1a9253f734a11e08c964b5fc067c234ddf8ac
1 parent 7a93f15 commit 5f21ffe

File tree

12 files changed

+773
-27
lines changed

12 files changed

+773
-27
lines changed

user/super/com/google/gwt/emul/java/lang/Byte.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public final class Byte extends Number implements Comparable<Byte> {
3131
*/
3232
private static class BoxedValues {
3333
// Box all values according to JLS
34-
private static Byte[] boxedValues = new Byte[256];
34+
private static final Byte[] boxedValues = new Byte[256];
3535

3636
// This method should be marked with @HasNoSideEffects but it seems to trigger a bug
3737
// in the optimizing pipeling and breaks one test.
@@ -70,6 +70,14 @@ public static String toString(byte b) {
7070
return String.valueOf(b);
7171
}
7272

73+
public static int toUnsignedInt(byte b) {
74+
return b & 0xff;
75+
}
76+
77+
public static long toUnsignedLong(byte b) {
78+
return toUnsignedInt(b);
79+
}
80+
7381
public static Byte valueOf(byte b) {
7482
return BoxedValues.get(b);
7583
}

user/super/com/google/gwt/emul/java/lang/Character.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public java.lang.CharSequence subSequence(int start, int end) {
104104
*/
105105
private static class BoxedValues {
106106
// Box values according to JLS - from \u0000 to \u007f
107-
private static Character[] boxedValues = new Character[128];
107+
private static final Character[] boxedValues = new Character[128];
108108

109109
@HasNoSideEffects
110110
private static Character get(char c) {

user/super/com/google/gwt/emul/java/lang/Integer.java

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import javaemul.internal.JsUtils;
1919
import javaemul.internal.annotations.HasNoSideEffects;
20+
import static javaemul.internal.Coercions.ensureInt;
21+
import static javaemul.internal.Coercions.toUnsignedInt;
2022

2123
/**
2224
* Wraps a primitive <code>int</code> as an object.
@@ -34,7 +36,7 @@ public final class Integer extends Number implements Comparable<Integer> {
3436
*/
3537
private static class BoxedValues {
3638
// Box values according to JLS - between -128 and 127
37-
private static Integer[] boxedValues = new Integer[256];
39+
private final static Integer[] boxedValues = new Integer[256];
3840

3941
@HasNoSideEffects
4042
private static Integer get(int i) {
@@ -81,10 +83,18 @@ public static int compare(int x, int y) {
8183
}
8284
}
8385

86+
public static int compareUnsigned(int a, int b) {
87+
return compare(a ^ MIN_VALUE, b ^ MIN_VALUE);
88+
}
89+
8490
public static Integer decode(String s) throws NumberFormatException {
8591
return Integer.valueOf(__decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
8692
}
8793

94+
public static int divideUnsigned(int dividend, int divisor) {
95+
return ensureInt(toUnsignedInt(dividend) / toUnsignedInt(divisor));
96+
}
97+
8898
public static int hashCode(int i) {
8999
return i;
90100
}
@@ -170,6 +180,37 @@ public static int parseInt(String s, int radix) throws NumberFormatException {
170180
return __parseAndValidateInt(s, radix, MIN_VALUE, MAX_VALUE);
171181
}
172182

183+
public static int parseUnsignedInt(String s) throws NumberFormatException {
184+
return parseUnsignedInt(s, 10);
185+
}
186+
187+
public static int parseUnsignedInt(String s, int radix) throws NumberFormatException {
188+
if (s == null) {
189+
throw NumberFormatException.forNullInputString();
190+
}
191+
192+
int len = s.length();
193+
if (len == 0 || s.charAt(0) == '-') {
194+
throw NumberFormatException.forInputString(s);
195+
}
196+
197+
// Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
198+
// Integer.MAX_VALUE in base 10 is 10 digits
199+
if (len <= 5 || (radix == 10 && len <= 9)) {
200+
return parseInt(s, radix);
201+
}
202+
203+
long value = Long.parseLong(s, radix);
204+
if (!Long.fitsInUint(value)) {
205+
throw NumberFormatException.forInputString(s);
206+
}
207+
return (int) value;
208+
}
209+
210+
public static int remainderUnsigned(int dividend, int divisor) {
211+
return ensureInt(toUnsignedInt(dividend) % toUnsignedInt(divisor));
212+
}
213+
173214
public static int reverse(int i) {
174215
int[] nibbles = ReverseNibbles.reverseNibbles;
175216
return (nibbles[i >>> 28]) | (nibbles[(i >> 24) & 0xf] << 4)
@@ -249,13 +290,20 @@ public static Integer valueOf(int i) {
249290
return new Integer(i);
250291
}
251292

293+
public static long toUnsignedLong(int x) {
294+
return x & 0xffff_ffffL;
295+
}
296+
297+
public static String toUnsignedString(int x) {
298+
return toUnsignedString(x, 10);
299+
}
300+
252301
public static Integer valueOf(String s) throws NumberFormatException {
253302
return valueOf(s, 10);
254303
}
255304

256-
public static Integer valueOf(String s, int radix)
257-
throws NumberFormatException {
258-
return Integer.valueOf(Integer.parseInt(s, radix));
305+
public static Integer valueOf(String s, int radix) throws NumberFormatException {
306+
return valueOf(parseInt(s, radix));
259307
}
260308

261309
private final transient int value;

user/super/com/google/gwt/emul/java/lang/Long.java

Lines changed: 153 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public final class Long extends Number implements Comparable<Long> {
2424
/** Use nested class to avoid clinit on outer. */
2525
static class BoxedValues {
2626
// Box values according to JLS - between -128 and 127
27-
static Long[] boxedValues = new Long[256];
27+
static final Long[] boxedValues = new Long[256];
2828

2929
@HasNoSideEffects
3030
private static Long get(long l) {
@@ -59,11 +59,44 @@ public static int compare(long x, long y) {
5959
}
6060
}
6161

62+
public static int compareUnsigned(long a, long b) {
63+
return compare(a ^ MIN_VALUE, b ^ MIN_VALUE);
64+
}
65+
6266
public static Long decode(String s) throws NumberFormatException {
6367
__Decode decode = __decodeNumberString(s);
6468
return valueOf(decode.payload, decode.radix);
6569
}
6670

71+
public static long divideUnsigned(long dividend, long divisor) {
72+
if (divisor < 0) { // i.e., divisor >= 2^63:
73+
if (compare(dividend, divisor) < 0) {
74+
return 0; // dividend < divisor
75+
} else {
76+
return 1; // dividend >= divisor
77+
}
78+
}
79+
80+
// Optimization - use signed division if dividend < 2^63
81+
if (dividend >= 0) {
82+
return dividend / divisor;
83+
}
84+
85+
/*
86+
* Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
87+
* guaranteed to be either exact or one less than the correct value. This follows from fact
88+
* that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not
89+
* quite trivial.
90+
*/
91+
long quotient = ((dividend >>> 1) / divisor) << 1;
92+
long rem = dividend - quotient * divisor;
93+
if (compare(rem, divisor) >= 0) {
94+
return quotient + 1;
95+
} else {
96+
return quotient;
97+
}
98+
}
99+
67100
public static int hashCode(long l) {
68101
return LongUtils.getHighBits(l) ^ (int) l;
69102
}
@@ -121,6 +154,43 @@ public static long reverse(long l) {
121154
return LongUtils.fromBits(Integer.reverse(high), Integer.reverse(low));
122155
}
123156

157+
public static long parseUnsignedLong(String s) throws NumberFormatException {
158+
return parseUnsignedLong(s, 10);
159+
}
160+
161+
public static long parseUnsignedLong(String s, int radix) throws NumberFormatException {
162+
return __parseAndValidateUnsignedLong(s, radix);
163+
}
164+
165+
public static long remainderUnsigned(long dividend, long divisor) {
166+
if (divisor < 0) { // i.e., divisor >= 2^63:
167+
if (compare(dividend, divisor) < 0) {
168+
return dividend; // dividend < divisor
169+
} else {
170+
return dividend - divisor; // dividend >= divisor
171+
}
172+
}
173+
174+
// Optimization - use signed modulus if dividend < 2^63
175+
if (dividend >= 0) {
176+
return dividend % divisor;
177+
}
178+
179+
/*
180+
* Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
181+
* guaranteed to be either exact or one less than the correct value. This follows from fact
182+
* that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not
183+
* quite trivial.
184+
*/
185+
long quotient = ((dividend >>> 1) / divisor) << 1;
186+
long rem = dividend - quotient * divisor;
187+
if (compare(rem, divisor) >= 0) {
188+
return rem - divisor;
189+
} else {
190+
return rem;
191+
}
192+
}
193+
124194
public static long reverseBytes(long l) {
125195
int high = LongUtils.getHighBits(l);
126196
int low = (int) l;
@@ -163,15 +233,15 @@ public static long sum(long a, long b) {
163233
}
164234

165235
public static String toBinaryString(long value) {
166-
return toPowerOfTwoUnsignedString(value, 1);
236+
return toUnsignedString(value, 2);
167237
}
168238

169239
public static String toHexString(long value) {
170-
return toPowerOfTwoUnsignedString(value, 4);
240+
return toUnsignedString(value, 16);
171241
}
172242

173243
public static String toOctalString(long value) {
174-
return toPowerOfTwoUnsignedString(value, 3);
244+
return toUnsignedString(value, 8);
175245
}
176246

177247
public static String toString(long value) {
@@ -219,11 +289,89 @@ public static String toString(long value, int intRadix) {
219289

220290
public static Long valueOf(long l) {
221291
if (l > -129 && l < 128) {
222-
return BoxedValues.get(l);
292+
return BoxedValues.get(l);
223293
}
224294
return new Long(l);
225295
}
226296

297+
public static String toUnsignedString(long value) {
298+
return toUnsignedString(value, 10);
299+
}
300+
301+
public static String toUnsignedString(long value, int intRadix) {
302+
if (fitsInUint(value)) {
303+
return Integer.toUnsignedString((int) value, intRadix);
304+
}
305+
306+
if (intRadix < Character.MIN_RADIX || intRadix > Character.MAX_RADIX) {
307+
intRadix = 10;
308+
}
309+
310+
if (isPowerOfTwo(intRadix)) {
311+
return toPowerOfTwoUnsignedString(value, intRadix);
312+
}
313+
314+
if (value >= 0) {
315+
return toString(value, intRadix);
316+
}
317+
318+
// Convert radix to long before hand to avoid costly conversion on each iteration.
319+
long radix = intRadix;
320+
if (intRadix == 10) {
321+
long quotient = divideUnsigned(value, radix);
322+
int rem = (int) (value - quotient * radix);
323+
return toString(quotient) + rem;
324+
}
325+
326+
int bufLen = intRadix < 8 ? 65 : 23; // Max chars in result (conservative)
327+
char[] buf = new char[bufLen];
328+
int cursor = bufLen;
329+
if (value < 0) {
330+
// Separate off the last digit using unsigned division. That will leave
331+
// a number that is nonnegative as a signed integer.
332+
long quotient = divideUnsigned(value, radix);
333+
int rem = (int) (value - quotient * radix);
334+
buf[--cursor] = Character.forDigit(rem, intRadix);
335+
value = quotient;
336+
}
337+
338+
// Simple modulo/division approach
339+
while (value > 0) {
340+
buf[--cursor] = Character.forDigit((int) (value % radix), intRadix);
341+
value /= radix;
342+
}
343+
344+
return new String(buf, cursor, buf.length - cursor);
345+
}
346+
347+
private static String toPowerOfTwoUnsignedString(long value, int radix) {
348+
final int shift = log2(radix);
349+
350+
final int mask = radix - 1;
351+
final int bufSize = 64 / shift + 1;
352+
char[] buf = new char[bufSize];
353+
int pos = bufSize;
354+
do {
355+
buf[--pos] = Character.forDigit(((int) value) & mask);
356+
value >>>= shift;
357+
} while (value != 0);
358+
359+
return String.valueOf(buf, pos, bufSize - pos);
360+
}
361+
362+
private static boolean isPowerOfTwo(int x) {
363+
return (x & (x - 1)) == 0;
364+
}
365+
366+
private static int log2(int x) {
367+
return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(x);
368+
}
369+
370+
static boolean fitsInUint(long value) {
371+
int highBits = (int) (value >> 32);
372+
return highBits == 0;
373+
}
374+
227375
public static Long valueOf(String s) throws NumberFormatException {
228376
return valueOf(s, 10);
229377
}

0 commit comments

Comments
 (0)