Skip to content

Commit fdc4686

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 fdc4686

12 files changed

Lines changed: 763 additions & 31 deletions

File tree

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: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package java.lang;
1717

18+
import static javaemul.internal.Coercions.ensureInt;
19+
import static javaemul.internal.Coercions.toUnsignedInt;
20+
1821
import javaemul.internal.JsUtils;
1922
import javaemul.internal.annotations.HasNoSideEffects;
2023

@@ -34,7 +37,7 @@ public final class Integer extends Number implements Comparable<Integer> {
3437
*/
3538
private static class BoxedValues {
3639
// Box values according to JLS - between -128 and 127
37-
private static Integer[] boxedValues = new Integer[256];
40+
private static final Integer[] boxedValues = new Integer[256];
3841

3942
@HasNoSideEffects
4043
private static Integer get(int i) {
@@ -81,10 +84,18 @@ public static int compare(int x, int y) {
8184
}
8285
}
8386

87+
public static int compareUnsigned(int a, int b) {
88+
return compare(a ^ MIN_VALUE, b ^ MIN_VALUE);
89+
}
90+
8491
public static Integer decode(String s) throws NumberFormatException {
8592
return Integer.valueOf(__decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
8693
}
8794

95+
public static int divideUnsigned(int dividend, int divisor) {
96+
return ensureInt(toUnsignedInt(dividend) / toUnsignedInt(divisor));
97+
}
98+
8899
public static int hashCode(int i) {
89100
return i;
90101
}
@@ -170,6 +181,37 @@ public static int parseInt(String s, int radix) throws NumberFormatException {
170181
return __parseAndValidateInt(s, radix, MIN_VALUE, MAX_VALUE);
171182
}
172183

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

294+
public static long toUnsignedLong(int x) {
295+
return x & 0xffff_ffffL;
296+
}
297+
298+
public static String toUnsignedString(int x) {
299+
return toUnsignedString(x, 10);
300+
}
301+
252302
public static Integer valueOf(String s) throws NumberFormatException {
253303
return valueOf(s, 10);
254304
}
255305

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

261310
private final transient int value;

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

Lines changed: 142 additions & 9 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,17 +289,59 @@ 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

227-
public static Long valueOf(String s) throws NumberFormatException {
228-
return valueOf(s, 10);
297+
public static String toUnsignedString(long value) {
298+
return toUnsignedString(value, 10);
229299
}
230300

231-
public static Long valueOf(String s, int radix) throws NumberFormatException {
232-
return valueOf(parseLong(s, radix));
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);
233345
}
234346

235347
private static String toPowerOfTwoUnsignedString(long value, int shift) {
@@ -252,6 +364,27 @@ private static String toPowerOfTwoUnsignedString(long value, int shift) {
252364
return String.valueOf(buf, pos, bufSize - pos);
253365
}
254366

367+
private static boolean isPowerOfTwo(int x) {
368+
return (x & (x - 1)) == 0;
369+
}
370+
371+
private static int log2(int x) {
372+
return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(x);
373+
}
374+
375+
static boolean fitsInUint(long value) {
376+
int highBits = (int) (value >> 32);
377+
return highBits == 0;
378+
}
379+
380+
public static Long valueOf(String s) throws NumberFormatException {
381+
return valueOf(s, 10);
382+
}
383+
384+
public static Long valueOf(String s, int radix) throws NumberFormatException {
385+
return valueOf(parseLong(s, radix));
386+
}
387+
255388
private final transient long value;
256389

257390
public Long(long value) {

0 commit comments

Comments
 (0)