You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by ve...@apache.org on 2015/05/02 21:33:21 UTC
svn commit: r1677340 - in /webservices/axiom/trunk/modules/axiom-api/src:
main/java/org/apache/axiom/util/base64/
test/java/org/apache/axiom/util/base64/
Author: veithen
Date: Sat May 2 19:33:21 2015
New Revision: 1677340
URL: http://svn.apache.org/r1677340
Log:
Rewrite the Base64Utils#decode(String) method to minimize the number of array allocations and to throw an exception for data that is not correctly encoded (AXIOM-434).
Modified:
webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/AbstractBase64DecodingWriter.java
webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Constants.java
webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Utils.java
webservices/axiom/trunk/modules/axiom-api/src/test/java/org/apache/axiom/util/base64/Base64UtilsTest.java
Modified: webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/AbstractBase64DecodingWriter.java
URL: http://svn.apache.org/viewvc/webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/AbstractBase64DecodingWriter.java?rev=1677340&r1=1677339&r2=1677340&view=diff
==============================================================================
--- webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/AbstractBase64DecodingWriter.java (original)
+++ webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/AbstractBase64DecodingWriter.java Sat May 2 19:33:21 2015
@@ -65,7 +65,7 @@ public abstract class AbstractBase64Deco
return -1;
} else if (c < Base64Constants.S_DECODETABLE.length) {
int result = Base64Constants.S_DECODETABLE[c];
- if (result != Byte.MAX_VALUE) {
+ if (result >= 0) {
return result;
}
}
Modified: webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Constants.java
URL: http://svn.apache.org/viewvc/webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Constants.java?rev=1677340&r1=1677339&r2=1677340&view=diff
==============================================================================
--- webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Constants.java (original)
+++ webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Constants.java Sat May 2 19:33:21 2015
@@ -30,15 +30,36 @@ class Base64Constants {
static final byte S_BASE64PAD = '=';
+ /**
+ * Used in {@link #S_DECODETABLE} to indicate that a character is the padding character.
+ */
+ static final byte PADDING = -1;
+
+ /**
+ * Used in {@link #S_DECODETABLE} to indicate that a character is white space.
+ */
+ static final byte WHITE_SPACE = -2;
+
+ /**
+ * Used in {@link #S_DECODETABLE} to indicate that a character is invalid.
+ */
+ static final byte INVALID = -3;
+
static final byte[] S_DECODETABLE = new byte[128];
static {
for (int i = 0; i < S_DECODETABLE.length; i++) {
- S_DECODETABLE[i] = Byte.MAX_VALUE; // 127
+ S_DECODETABLE[i] = INVALID;
}
for (int i = 0; i < S_BASE64CHAR.length; i++) {
// 0 to 63
S_DECODETABLE[S_BASE64CHAR[i]] = (byte) i;
}
+ S_DECODETABLE[S_BASE64PAD] = PADDING;
+ // See http://www.w3.org/TR/2008/REC-xml-20081126/#white
+ S_DECODETABLE[' '] = WHITE_SPACE;
+ S_DECODETABLE['\t'] = WHITE_SPACE;
+ S_DECODETABLE['\r'] = WHITE_SPACE;
+ S_DECODETABLE['\n'] = WHITE_SPACE;
}
}
Modified: webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Utils.java
URL: http://svn.apache.org/viewvc/webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Utils.java?rev=1677340&r1=1677339&r2=1677340&view=diff
==============================================================================
--- webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Utils.java (original)
+++ webservices/axiom/trunk/modules/axiom-api/src/main/java/org/apache/axiom/util/base64/Base64Utils.java Sat May 2 19:33:21 2015
@@ -120,7 +120,7 @@ public class Base64Utils {
for (int i = off; i < off + len; i++) {
char ch = data[i];
if (ch == Base64Constants.S_BASE64PAD || ch < Base64Constants.S_DECODETABLE.length
- && Base64Constants.S_DECODETABLE[ch] != Byte.MAX_VALUE) {
+ && Base64Constants.S_DECODETABLE[ch] >= 0) {
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
@@ -136,29 +136,69 @@ public class Base64Utils {
}
/**
- *
+ * Decodes a base64 encoded string into a byte array. This method is designed to conform to the
+ * <a href="http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#base64Binary">XML Schema</a>
+ * specification. It can be used to decode the text content of an element (or the value of an
+ * attribute) of type <code>base64Binary</code>.
+ *
+ * @param data
+ * the base64 encoded data
+ * @return the decoded data
*/
public static byte[] decode(String data) {
- char[] ibuf = new char[4];
- int ibufcount = 0;
- byte[] obuf = new byte[data.length() / 4 * 3 + 3];
- int obufcount = 0;
+ int symbols = 0;
+ int padding = 0;
for (int i = 0; i < data.length(); i++) {
- char ch = data.charAt(i);
- if (ch == Base64Constants.S_BASE64PAD || ch < Base64Constants.S_DECODETABLE.length
- && Base64Constants.S_DECODETABLE[ch] != Byte.MAX_VALUE) {
- ibuf[ibufcount++] = ch;
- if (ibufcount == ibuf.length) {
- ibufcount = 0;
- obufcount += decode0(ibuf, obuf, obufcount);
- }
+ switch (Base64Constants.S_DECODETABLE[data.charAt(i)]) {
+ case Base64Constants.PADDING:
+ if (padding == 2) {
+ throw new IllegalArgumentException("Too much padding");
+ }
+ padding++;
+ break;
+ case Base64Constants.WHITE_SPACE:
+ break;
+ case Base64Constants.INVALID:
+ throw new IllegalArgumentException("Invalid character encountered");
+ default:
+ // Padding can only occur at the end
+ if (padding > 0) {
+ throw new IllegalArgumentException("Unexpected padding character");
+ }
+ symbols++;
}
}
- if (obufcount == obuf.length)
- return obuf;
- byte[] ret = new byte[obufcount];
- System.arraycopy(obuf, 0, ret, 0, obufcount);
- return ret;
+ if ((symbols + padding) % 4 != 0) {
+ throw new IllegalArgumentException("Missing padding");
+ }
+ byte[] result = new byte[(symbols + padding) / 4 * 3 - padding];
+ int pos = 0;
+ int resultPos = 0;
+ byte accumulator = 0;
+ int bits = 0;
+ while (symbols > 0) {
+ byte b = Base64Constants.S_DECODETABLE[data.charAt(pos++)];
+ if (b == Base64Constants.WHITE_SPACE) {
+ continue;
+ }
+ if (bits == 0) {
+ accumulator = (byte)(b << 2);
+ bits = 6;
+ } else {
+ accumulator |= b >> (bits - 2);
+ result[resultPos++] = accumulator;
+ accumulator = (byte)(b << (10 - bits));
+ bits -= 2;
+ }
+ symbols--;
+ }
+ if (accumulator != 0) {
+ throw new IllegalArgumentException("Invalid base64 value");
+ }
+ if (resultPos != result.length) {
+ throw new Error("Oops. This is a bug.");
+ }
+ return result;
}
/**
@@ -169,7 +209,7 @@ public class Base64Utils {
char ch = data.charAt(i);
if (ch == Base64Constants.S_BASE64PAD || ch < Base64Constants.S_DECODETABLE.length
- && Base64Constants.S_DECODETABLE[ch] != Byte.MAX_VALUE) {
+ && Base64Constants.S_DECODETABLE[ch] >= 0) {
//valid character.Do nothing
} else if (ch == '\r' || ch == '\n') {
//do nothing
@@ -192,7 +232,7 @@ public class Base64Utils {
for (int i = off; i < off + len; i++) {
char ch = data[i];
if (ch == Base64Constants.S_BASE64PAD || ch < Base64Constants.S_DECODETABLE.length
- && Base64Constants.S_DECODETABLE[ch] != Byte.MAX_VALUE) {
+ && Base64Constants.S_DECODETABLE[ch] >= 0) {
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
@@ -214,7 +254,7 @@ public class Base64Utils {
for (int i = 0; i < data.length(); i++) {
char ch = data.charAt(i);
if (ch == Base64Constants.S_BASE64PAD || ch < Base64Constants.S_DECODETABLE.length
- && Base64Constants.S_DECODETABLE[ch] != Byte.MAX_VALUE) {
+ && Base64Constants.S_DECODETABLE[ch] >= 0) {
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
Modified: webservices/axiom/trunk/modules/axiom-api/src/test/java/org/apache/axiom/util/base64/Base64UtilsTest.java
URL: http://svn.apache.org/viewvc/webservices/axiom/trunk/modules/axiom-api/src/test/java/org/apache/axiom/util/base64/Base64UtilsTest.java?rev=1677340&r1=1677339&r2=1677340&view=diff
==============================================================================
--- webservices/axiom/trunk/modules/axiom-api/src/test/java/org/apache/axiom/util/base64/Base64UtilsTest.java (original)
+++ webservices/axiom/trunk/modules/axiom-api/src/test/java/org/apache/axiom/util/base64/Base64UtilsTest.java Sat May 2 19:33:21 2015
@@ -19,33 +19,74 @@
package org.apache.axiom.util.base64;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import org.apache.commons.codec.binary.Base64;
+import java.util.Random;
import junit.framework.TestCase;
+import org.apache.commons.codec.binary.Base64;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+
public class Base64UtilsTest extends TestCase {
+ public void testDecode() {
+ Random random = new Random(43219876);
+ for (int len=0; len<20; len++) {
+ byte[] data = new byte[len];
+ random.nextBytes(data);
+ Assert.assertThat(
+ Base64Utils.decode(Base64.encodeBase64String(data)),
+ CoreMatchers.equalTo(data));
+ }
+ }
+
+ public void testMissingPadding() {
+ try {
+ Base64Utils.decode("cw");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
- Object expectedObject;
+ public void testTooMuchPadding() {
+ try {
+ Base64Utils.decode("cw===");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testNonZeroRemainder() {
+ try {
+ Base64Utils.decode("//==");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testSpace() throws Exception{
+ assertEquals(
+ "any carnal pleasure.",
+ new String(Base64Utils.decode(" YW55IG\tNhcm5hbC\r\nBwb GVhc3VyZS4 = "), "utf-8"));
+ }
- ByteArrayInputStream byteStream;
+ public void testInvalidCharacter() {
+ try {
+ Base64Utils.decode("//-/");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
- public void testDecode() throws Exception {
- Object actualObject;
- String expectedBase64;
- expectedObject = new String("Lanka Software Foundation");
- ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
- ObjectOutputStream objectOutStream = new ObjectOutputStream(byteStream);
- objectOutStream.writeObject(expectedObject);
- expectedBase64 = Base64.encodeBase64String(byteStream.toByteArray());
- byte[] tempa = Base64Utils.decode(expectedBase64);
- ObjectInputStream objectInStream = new ObjectInputStream(
- new ByteArrayInputStream(tempa));
- actualObject = objectInStream.readObject();
- assertEquals("Base64 Encoding Check", expectedObject, actualObject);
+ public void testInvalidPadding() {
+ try {
+ Base64Utils.decode("//=/");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
}
}
\ No newline at end of file