Skip to content

Commit 13db596

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 13db596

File tree

12 files changed

+774
-27
lines changed

12 files changed

+774
-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: 154 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
}
@@ -115,12 +148,50 @@ public static long parseLong(String s, int radix) throws NumberFormatException {
115148
return __parseAndValidateLong(s, radix);
116149
}
117150

151+
HEAD
118152
public static long reverse(long l) {
119153
int high = LongUtils.getHighBits(l);
120154
int low = (int) l;
121155
return LongUtils.fromBits(Integer.reverse(high), Integer.reverse(low));
122156
}
123157

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

165236
public static String toBinaryString(long value) {
166-
return toPowerOfTwoUnsignedString(value, 1);
237+
return toUnsignedString(value, 2);
167238
}
168239

169240
public static String toHexString(long value) {
170-
return toPowerOfTwoUnsignedString(value, 4);
241+
return toUnsignedString(value, 16);
171242
}
172243

173244
public static String toOctalString(long value) {
174-
return toPowerOfTwoUnsignedString(value, 3);
245+
return toUnsignedString(value, 8);
175246
}
176247

177248
public static String toString(long value) {
@@ -219,11 +290,89 @@ public static String toString(long value, int intRadix) {
219290

220291
public static Long valueOf(long l) {
221292
if (l > -129 && l < 128) {
222-
return BoxedValues.get(l);
293+
return BoxedValues.get(l);
223294
}
224295
return new Long(l);
225296
}
226297

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

0 commit comments

Comments
 (0)