Copy long<->base64 code to Base64Utils for server RPC code
Review at http://gwt-code-reviews.appspot.com/639801
Review by: jat@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8274 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
index edffae2..205f547 100644
--- a/dev/core/super/com/google/gwt/lang/LongLib.java
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -31,8 +31,6 @@
private static LongEmul[] boxedValues;
- private static boolean haveNonZero;
-
public static LongEmul add(LongEmul a, LongEmul b) {
int sum0 = getL(a) + getL(b);
int sum1 = getM(a) + getM(b) + (sum0 >> BITS);
@@ -46,61 +44,6 @@
}
/**
- * Return an optionally single-quoted string containing a base-64 encoded
- * version of the given long value.
- */
- public static String base64Emit(long value, boolean quote) {
- // Convert to ints early to avoid need for long ops
- int low = (int) (value & 0xffffffff);
- int high = (int) (value >> 32);
-
- StringBuilder sb = new StringBuilder();
- if (quote) {
- sb.append('\'');
- }
- haveNonZero = false;
- base64Append(sb, (high >> 28) & 0xf); // bits 63 - 60
- base64Append(sb, (high >> 22) & 0x3f); // bits 59 - 54
- base64Append(sb, (high >> 16) & 0x3f); // bits 53 - 48
- base64Append(sb, (high >> 10) & 0x3f); // bits 47 - 42
- base64Append(sb, (high >> 4) & 0x3f); // bits 41 - 36
- int v = ((high & 0xf) << 2) | ((low >> 30) & 0x3);
- base64Append(sb, v); // bits 35 - 30
- base64Append(sb, (low >> 24) & 0x3f); // bits 29 - 24
- base64Append(sb, (low >> 18) & 0x3f); // bits 23 - 18
- base64Append(sb, (low >> 12) & 0x3f); // bits 17 - 12
- base64Append(sb, (low >> 6) & 0x3f); // bits 11 - 6
- haveNonZero = true; // always emit final digit
- base64Append(sb, low & 0x3f); // bits 5 - 0
- if (quote) {
- sb.append('\'');
- }
-
- return sb.toString();
- }
-
- /**
- * Parse a string containing a base-64 encoded version of a long value.
- */
- public static long base64Parse(String value) {
- int pos = 0;
- char first = value.charAt(pos++);
- int len = value.length();
-
- // Skip surrounding single quotes
- if (first == '\'') {
- first = value.charAt(pos++);
- len--;
- }
- long longVal = base64Value(first);
- while (pos < len) {
- longVal <<= 6;
- longVal |= base64Value(value.charAt(pos++));
- }
- return longVal;
- }
-
- /**
* Compare the receiver a to the argument b.
*
* @return 0 if they are the same, a positive value if the receiver is
@@ -244,6 +187,22 @@
}
}
+ /**
+ * Parse a string containing a base-64 encoded version of a long value.
+ *
+ * Keep this synchronized with the version in Base64Utils.
+ */
+ public static long longFromBase64(String value) {
+ int pos = 0;
+ long longVal = base64Value(value.charAt(pos++));
+ int len = value.length();
+ while (pos < len) {
+ longVal <<= 6;
+ longVal |= base64Value(value.charAt(pos++));
+ }
+ return longVal;
+ }
+
public static boolean lt(LongEmul a, LongEmul b) {
return !gte(a, b);
}
@@ -443,6 +402,34 @@
return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
}
+ /**
+ * Return an optionally single-quoted string containing a base-64 encoded
+ * version of the given long value.
+ *
+ * Keep this synchronized with the version in Base64Utils.
+ */
+ public static String toBase64(long value) {
+ // Convert to ints early to avoid need for long ops
+ int low = (int) (value & 0xffffffff);
+ int high = (int) (value >> 32);
+
+ StringBuilder sb = new StringBuilder();
+ boolean haveNonZero = base64Append(sb, (high >> 28) & 0xf, false);
+ haveNonZero = base64Append(sb, (high >> 22) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 16) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 10) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 4) & 0x3f, haveNonZero);
+ int v = ((high & 0xf) << 2) | ((low >> 30) & 0x3);
+ haveNonZero = base64Append(sb, v, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 24) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 18) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 12) & 0x3f, haveNonZero);
+ base64Append(sb, (low >> 6) & 0x3f, haveNonZero);
+ base64Append(sb, low & 0x3f, true);
+
+ return sb.toString();
+ }
+
public static double toDouble(LongEmul a) {
if (LongLib.eq(a, Const.MIN_VALUE)) {
return -9223372036854775808.0;
@@ -502,7 +489,8 @@
return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b));
}
- private static void base64Append(StringBuilder sb, int digit) {
+ private static boolean base64Append(StringBuilder sb, int digit,
+ boolean haveNonZero) {
if (digit > 0) {
haveNonZero = true;
}
@@ -521,6 +509,7 @@
}
sb.append((char) c);
}
+ return haveNonZero;
}
// Assume digit is one of [A-Za-z0-9$_]
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTest.java b/dev/core/test/com/google/gwt/lang/LongLibTest.java
index 7591238..b04ab5a 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTest.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTest.java
@@ -407,20 +407,20 @@
}
public static void testBase64() {
- assertEquals("A", LongLib.base64Emit(0x0L, false));
- assertEquals(0x0L, LongLib.base64Parse("A"));
+ assertEquals("A", LongLib.toBase64(0x0L));
+ assertEquals(0x0L, LongLib.longFromBase64("A"));
- assertEquals("'B'", LongLib.base64Emit(0x1L, true));
- assertEquals(0x1L, LongLib.base64Parse("B"));
+ assertEquals("B", LongLib.toBase64(0x1L));
+ assertEquals(0x1L, LongLib.longFromBase64("B"));
- assertEquals("'BA'", LongLib.base64Emit(0x40L, true));
- assertEquals(0x40L, LongLib.base64Parse("BA"));
+ assertEquals("BA", LongLib.toBase64(0x40L));
+ assertEquals(0x40L, LongLib.longFromBase64("BA"));
- assertEquals("'P_________A'", LongLib.base64Emit(-0x40L, true));
- assertEquals(-0x40L, LongLib.base64Parse("P_________A"));
+ assertEquals("P_________A", LongLib.toBase64(-0x40L));
+ assertEquals(-0x40L, LongLib.longFromBase64("P_________A"));
- assertEquals("'P__________'", LongLib.base64Emit(-1L, true));
- assertEquals(-1L, LongLib.base64Parse("P__________"));
+ assertEquals("P__________", LongLib.toBase64(-1L));
+ assertEquals(-1L, LongLib.longFromBase64("P__________"));
// Use all types of base 64 chars
long value = 0L;
@@ -437,8 +437,8 @@
value |= 63L; // '_'
String s = "Pjs$aJSZ05_";
- assertEquals(s, LongLib.base64Emit(value, false));
- assertEquals(value, LongLib.base64Parse(s));
+ assertEquals(s, LongLib.toBase64(value));
+ assertEquals(value, LongLib.longFromBase64(s));
}
public static void testCompare() {
diff --git a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
index 2085fbc..b2028a7 100644
--- a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
@@ -103,7 +103,7 @@
extractData(array, value);
toReturn = array;
- } else if (value instanceof Enum) {
+ } else if (value instanceof Enum<?>) {
EnumValueCommand e = new EnumValueCommand();
e.setValue((Enum<?>) value);
toReturn = e;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index c77e46b..db74b38 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.user.client.rpc.impl;
-import com.google.gwt.lang.LongLib;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
@@ -101,17 +100,7 @@
append(String.valueOf(fieldValue));
}
- public void writeLong(long value) {
- if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
- // Write longs as a pair of doubles for backwards compatibility
- double[] parts = getAsDoubleArray(value);
- assert parts != null && parts.length == 2;
- writeDouble(parts[0]);
- writeDouble(parts[1]);
- } else {
- append(LongLib.base64Emit(value, true));
- }
- }
+ public abstract void writeLong(long value);
public void writeObject(Object instance) throws SerializationException {
if (instance == null) {
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index eeb5f4c..83f2e61 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -88,7 +88,7 @@
@UnsafeNativeLong
public native long readLong() /*-{
var s = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::index];
- return @com.google.gwt.lang.LongLib::base64Parse(Ljava/lang/String;)(s);
+ return @com.google.gwt.lang.LongLib::longFromBase64(Ljava/lang/String;)(s);
}-*/;
public native short readShort() /*-{
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index 19ac418..571bdad 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -16,6 +16,7 @@
package com.google.gwt.user.client.rpc.impl;
import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.lang.LongLib;
import com.google.gwt.user.client.rpc.SerializationException;
import java.util.List;
@@ -147,6 +148,11 @@
return buffer.toString();
}
+ @Override
+ public void writeLong(long value) {
+ append(LongLib.toBase64(value));
+ }
+
/**
* Appends a token to the end of the buffer.
*/
diff --git a/user/src/com/google/gwt/user/server/Base64Utils.java b/user/src/com/google/gwt/user/server/Base64Utils.java
index 1909fef..c5db3f8 100644
--- a/user/src/com/google/gwt/user/server/Base64Utils.java
+++ b/user/src/com/google/gwt/user/server/Base64Utils.java
@@ -16,26 +16,28 @@
package com.google.gwt.user.server;
/**
- * A utility to decode and encode byte arrays as Strings, using only "safe" characters.
+ * A utility to decode and encode byte arrays as Strings, using only "safe"
+ * characters.
*/
public class Base64Utils {
/**
- * An array mapping size but values to the characters that will be used to represent them.
- * Note that this is not identical to the set of characters used by MIME-Base64.
+ * An array mapping size but values to the characters that will be used to
+ * represent them. Note that this is not identical to the set of characters
+ * used by MIME-Base64.
*/
private static final char[] base64Chars = new char[] {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '$', '_'
- };
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
+ 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '$', '_'};
/**
- * An array mapping legal base 64 characters [a-zA-Z0-9$_] to their associated 6-bit values.
- * The source indices will be given by 7-bit ASCII characters, thus the array size needs to
- * be 128 (actually 123 would suffice for the given set of characters in use).
+ * An array mapping legal base 64 characters [a-zA-Z0-9$_] to their associated
+ * 6-bit values. The source indices will be given by 7-bit ASCII characters,
+ * thus the array size needs to be 128 (actually 123 would suffice for the
+ * given set of characters in use).
*/
private static final byte[] base64Values = new byte[128];
@@ -60,17 +62,17 @@
if (data == null) {
return null;
}
-
+
int len = data.length();
assert (len % 4) == 0;
-
+
if (len == 0) {
return new byte[0];
}
-
+
char[] chars = new char[len];
data.getChars(0, len, chars, 0);
-
+
int olen = 3 * (len / 4);
if (chars[len - 2] == '=') {
--olen;
@@ -78,9 +80,9 @@
if (chars[len - 1] == '=') {
--olen;
}
-
+
byte[] bytes = new byte[olen];
-
+
int iidx = 0;
int oidx = 0;
while (iidx < len) {
@@ -89,28 +91,42 @@
int c2 = base64Values[chars[iidx++] & 0xff];
int c3 = base64Values[chars[iidx++] & 0xff];
int c24 = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
-
+
bytes[oidx++] = (byte) (c24 >> 16);
if (oidx == olen) {
break;
}
- bytes[oidx++] = (byte) (c24 >> 8);
+ bytes[oidx++] = (byte) (c24 >> 8);
if (oidx == olen) {
break;
}
- bytes[oidx++] = (byte) c24;
+ bytes[oidx++] = (byte) c24;
}
-
+
return bytes;
}
/**
+ * Decode a base64 string into a long value.
+ */
+ public static long longFromBase64(String value) {
+ int pos = 0;
+ long longVal = base64Values[value.charAt(pos++)];
+ int len = value.length();
+ while (pos < len) {
+ longVal <<= 6;
+ longVal |= base64Values[value.charAt(pos++)];
+ }
+ return longVal;
+ }
+
+ /**
* Converts a byte array into a base 64 encoded string. Null is encoded as
* null, and an empty array is encoded as an empty string. Otherwise, the byte
* data is read 3 bytes at a time, with bytes off the end of the array padded
* with zeros. Each 24-bit chunk is encoded as 4 characters from the sequence
- * [A-Za-z0-9$_]. If one of the size-bit source positions consists entirely of
- * padding zeros, an '=' character is used instead.
+ * [A-Za-z0-9$_]. If one of the source positions consists entirely of padding
+ * zeros, an '=' character is used instead.
*
* @param data a byte array, which may be null or empty
* @return a String
@@ -124,7 +140,7 @@
if (len == 0) {
return "";
}
-
+
int olen = 4 * ((len + 2) / 3);
char[] chars = new char[olen];
@@ -152,4 +168,41 @@
return new String(chars);
}
+
+ /**
+ * Return a string containing a base-64 encoded version of the given long
+ * value. Leading groups of all zero bits are omitted.
+ */
+ public static String toBase64(long value) {
+ // Convert to ints early to avoid need for long ops
+ int low = (int) (value & 0xffffffff);
+ int high = (int) (value >> 32);
+
+ StringBuilder sb = new StringBuilder();
+ boolean haveNonZero = base64Append(sb, (high >> 28) & 0xf, false);
+ haveNonZero = base64Append(sb, (high >> 22) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 16) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 10) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (high >> 4) & 0x3f, haveNonZero);
+ int v = ((high & 0xf) << 2) | ((low >> 30) & 0x3);
+ haveNonZero = base64Append(sb, v, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 24) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 18) & 0x3f, haveNonZero);
+ haveNonZero = base64Append(sb, (low >> 12) & 0x3f, haveNonZero);
+ base64Append(sb, (low >> 6) & 0x3f, haveNonZero);
+ base64Append(sb, low & 0x3f, true);
+
+ return sb.toString();
+ }
+
+ private static boolean base64Append(StringBuilder sb, int digit,
+ boolean haveNonZero) {
+ if (digit > 0) {
+ haveNonZero = true;
+ }
+ if (haveNonZero) {
+ sb.append(base64Chars[digit]);
+ }
+ return haveNonZero;
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 0be3812..4b642e1 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.user.server.rpc.impl;
-import com.google.gwt.lang.LongLib;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader;
@@ -498,7 +497,7 @@
if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
return (long) readDouble() + (long) readDouble();
} else {
- return LongLib.base64Parse(extract());
+ return Base64Utils.longFromBase64(extract());
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 9710012..b129c9f 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -555,6 +555,23 @@
return stream.toString();
}
+
+ @Override
+ public void writeLong(long value) {
+ if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
+ // Write longs as a pair of doubles for backwards compatibility
+ double[] parts = getAsDoubleArray(value);
+ assert parts != null && parts.length == 2;
+ writeDouble(parts[0]);
+ writeDouble(parts[1]);
+ } else {
+ StringBuilder sb = new StringBuilder();
+ sb.append('\'');
+ sb.append(Base64Utils.toBase64(value));
+ sb.append('\'');
+ append(sb.toString());
+ }
+ }
@Override
protected void append(String token) {