This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push:
new 34e7e21 Update Commons Codec to latest
34e7e21 is described below
commit 34e7e2162925aa1f948135ed044dc799947772a9
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Aug 25 19:15:06 2020 +0100
Update Commons Codec to latest
---
MERGE.txt | 2 +-
.../apache/tomcat/util/codec/binary/Base64.java | 1013 ++++++++++----------
.../tomcat/util/codec/binary/BaseNCodec.java | 523 +++++-----
.../tomcat/util/codec/binary/StringUtils.java | 42 +-
webapps/docs/changelog.xml | 4 +
5 files changed, 802 insertions(+), 782 deletions(-)
diff --git a/MERGE.txt b/MERGE.txt
index d63a3ca..fd084e5 100644
--- a/MERGE.txt
+++ b/MERGE.txt
@@ -43,7 +43,7 @@ Codec
Sub-tree:
src/main/java/org/apache/commons/codec
The SHA1 ID for the most recent commit to be merged to Tomcat is:
-9637dd44fa0e2d5a6ddb45791e3cd78298842d95 (2019-12-06)
+53c93d0ffccb65d182306c74d1230ce814889dc1 (2020-08-18)
Note: Only classes required for Base64 encoding/decoding. The rest are removed.
FileUpload
diff --git a/java/org/apache/tomcat/util/codec/binary/Base64.java
b/java/org/apache/tomcat/util/codec/binary/Base64.java
index 72cfd54..2b5c7d4 100644
--- a/java/org/apache/tomcat/util/codec/binary/Base64.java
+++ b/java/org/apache/tomcat/util/codec/binary/Base64.java
@@ -61,17 +61,6 @@ public class Base64 extends BaseNCodec {
private static final int BYTES_PER_ENCODED_BLOCK = 4;
/**
- * Chunk separator per RFC 2045 section 2.1.
- *
- * <p>
- * N.B. The next major release may break compatibility and make this field
private.
- * </p>
- *
- * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section
2.1</a>
- */
- static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
-
- /**
* This array is a lookup table that translates 6-bit positive integer
index values into their "Base64 Alphabet"
* equivalents as specified in Table 1 of RFC 2045.
*
@@ -149,367 +138,211 @@ public class Base64 extends BaseNCodec {
// some state be preserved between calls of encode() and decode().
/**
- * Encode table to use: either STANDARD or URL_SAFE. Note: the
DECODE_TABLE above remains static because it is able
- * to decode both STANDARD and URL_SAFE streams, but the encodeTable must
be a member variable so we can switch
- * between the two modes.
+ * Decodes Base64 data into octets.
+ * <p>
+ * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or
normal mode.
+ * </p>
+ *
+ * @param base64Data
+ * Byte array containing Base64 data
+ * @return Array containing decoded data.
*/
- private final byte[] encodeTable;
-
- // Only one decode table currently; keep for consistency with Base32 code
- private final byte[] decodeTable;
+ public static byte[] decodeBase64(final byte[] base64Data) {
+ return decodeBase64(base64Data, 0, base64Data.length);
+ }
- /**
- * Line separator for encoding. Not used when decoding. Only used if
lineLength > 0.
- */
- private final byte[] lineSeparator;
+ public static byte[] decodeBase64(
+ final byte[] base64Data, final int off, final int len) {
+ return new Base64().decode(base64Data, off, len);
+ }
/**
- * Convenience variable to help us determine when our buffer is going to
run out of room and needs resizing.
- * <code>decodeSize = 3 + lineSeparator.length;</code>
+ * Decodes a Base64 String into octets.
+ * <p>
+ * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or
normal mode.
+ * </p>
+ *
+ * @param base64String
+ * String containing Base64 data
+ * @return Array containing decoded data.
+ * @since 1.4
*/
- private final int decodeSize;
+ public static byte[] decodeBase64(final String base64String) {
+ return new Base64().decode(base64String);
+ }
- /**
- * Convenience variable to help us determine when our buffer is going to
run out of room and needs resizing.
- * <code>encodeSize = 4 + lineSeparator.length;</code>
- */
- private final int encodeSize;
+ public static byte[] decodeBase64URLSafe(final String base64String) {
+ return new Base64(true).decode(base64String);
+ }
+ // Implementation of integer encoding used for crypto
/**
- * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
- * <p>
- * When encoding the line length is 0 (no chunking), and the encoding
table is STANDARD_ENCODE_TABLE.
- * </p>
+ * Decodes a byte64-encoded integer according to crypto standards such as
W3C's XML-Signature.
*
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * @param pArray
+ * a byte array containing base64 character data
+ * @return A BigInteger
+ * @since 1.4
*/
- public Base64() {
- this(0);
+ public static BigInteger decodeInteger(final byte[] pArray) {
+ return new BigInteger(1, decodeBase64(pArray));
}
/**
- * Creates a Base64 codec used for decoding (all modes) and encoding in
the given URL-safe mode.
- * <p>
- * When encoding the line length is 76, the line separator is CRLF, and
the encoding table is STANDARD_ENCODE_TABLE.
- * </p>
- *
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * Encodes binary data using the base64 algorithm but does not chunk the
output.
*
- * @param urlSafe
- * if <code>true</code>, URL-safe encoding is used. In most
cases this should be set to
- * <code>false</code>.
- * @since 1.4
+ * @param binaryData
+ * binary data to encode
+ * @return byte[] containing Base64 characters in their UTF-8
representation.
*/
- public Base64(final boolean urlSafe) {
- this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
+ public static byte[] encodeBase64(final byte[] binaryData) {
+ return encodeBase64(binaryData, false);
}
/**
- * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
- * <p>
- * When encoding the line length is given in the constructor, the line
separator is CRLF, and the encoding table is
- * STANDARD_ENCODE_TABLE.
- * </p>
- * <p>
- * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
- * </p>
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
*
- * @param lineLength
- * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
- * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
- * decoding.
- * @since 1.4
+ * @param binaryData
+ * Array containing binary data to encode.
+ * @param isChunked
+ * if {@code true} this encoder will chunk the base64 output
into 76 character blocks
+ * @return Base64-encoded data.
+ * @throws IllegalArgumentException
+ * Thrown when the input array needs an output array bigger
than {@link Integer#MAX_VALUE}
*/
- public Base64(final int lineLength) {
- this(lineLength, CHUNK_SEPARATOR);
+ public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked) {
+ return encodeBase64(binaryData, isChunked, false);
}
/**
- * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
- * <p>
- * When encoding the line length and line separator are given in the
constructor, and the encoding table is
- * STANDARD_ENCODE_TABLE.
- * </p>
- * <p>
- * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
- * </p>
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
*
- * @param lineLength
- * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
- * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
- * decoding.
- * @param lineSeparator
- * Each line of encoded data will end with this sequence of
bytes.
+ * @param binaryData
+ * Array containing binary data to encode.
+ * @param isChunked
+ * if {@code true} this encoder will chunk the base64 output
into 76 character blocks
+ * @param urlSafe
+ * if {@code true} this encoder will emit - and _ instead of
the usual + and / characters.
+ * <b>Note: no padding is added when encoding using the
URL-safe alphabet.</b>
+ * @return Base64-encoded data.
* @throws IllegalArgumentException
- * Thrown when the provided lineSeparator included some base64
characters.
+ * Thrown when the input array needs an output array bigger
than {@link Integer#MAX_VALUE}
* @since 1.4
*/
- public Base64(final int lineLength, final byte[] lineSeparator) {
- this(lineLength, lineSeparator, false);
+ public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked, final boolean urlSafe) {
+ return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
}
/**
- * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
- * <p>
- * When encoding the line length and line separator are given in the
constructor, and the encoding table is
- * STANDARD_ENCODE_TABLE.
- * </p>
- * <p>
- * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
- * </p>
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
*
- * @param lineLength
- * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
- * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
- * decoding.
- * @param lineSeparator
- * Each line of encoded data will end with this sequence of
bytes.
+ * @param binaryData
+ * Array containing binary data to encode.
+ * @param isChunked
+ * if {@code true} this encoder will chunk the base64 output
into 76 character blocks
* @param urlSafe
- * Instead of emitting '+' and '/' we emit '-' and '_'
respectively. urlSafe is only applied to encode
- * operations. Decoding seamlessly handles both modes.
- * <b>Note: no padding is added when using the URL-safe
alphabet.</b>
+ * if {@code true} this encoder will emit - and _ instead of
the usual + and / characters.
+ * <b>Note: no padding is added when encoding using the
URL-safe alphabet.</b>
+ * @param maxResultSize
+ * The maximum result size to accept.
+ * @return Base64-encoded data.
* @throws IllegalArgumentException
- * The provided lineSeparator included some base64 characters.
That's not going to work!
+ * Thrown when the input array needs an output array bigger
than maxResultSize
* @since 1.4
*/
- public Base64(final int lineLength, final byte[] lineSeparator, final
boolean urlSafe) {
- super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
- lineLength,
- lineSeparator == null ? 0 : lineSeparator.length);
- // Needs to be set early to avoid NPE during call to
containsAlphabetOrPad() below
- this.decodeTable = urlSafe ? URL_SAFE_DECODE_TABLE :
STANDARD_DECODE_TABLE;
- // TODO could be simplified if there is no requirement to reject
invalid line sep when length <=0
- // @see test case Base64Test.testConstructors()
- if (lineSeparator != null) {
- if (containsAlphabetOrPad(lineSeparator)) {
- final String sep = StringUtils.newStringUtf8(lineSeparator);
- throw new
IllegalArgumentException(sm.getString("base64.lineSeparator", sep));
- }
- if (lineLength > 0){ // null line-sep forces no chunking rather
than throwing IAE
- this.encodeSize = BYTES_PER_ENCODED_BLOCK +
lineSeparator.length;
- this.lineSeparator = new byte[lineSeparator.length];
- System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
lineSeparator.length);
- } else {
- this.encodeSize = BYTES_PER_ENCODED_BLOCK;
- this.lineSeparator = null;
- }
- } else {
- this.encodeSize = BYTES_PER_ENCODED_BLOCK;
- this.lineSeparator = null;
+ public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked,
+ final boolean urlSafe, final int
maxResultSize) {
+ if (binaryData == null || binaryData.length == 0) {
+ return binaryData;
}
- this.decodeSize = this.encodeSize - 1;
- this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE :
STANDARD_ENCODE_TABLE;
+
+ // Create this so can use the super-class method
+ // Also ensures that the same roundings are performed by the ctor and
the code
+ final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0,
CHUNK_SEPARATOR, urlSafe);
+ final long len = b64.getEncodedLength(binaryData);
+ if (len > maxResultSize) {
+ throw new IllegalArgumentException(sm.getString(
+ "base64.inputTooLarge", Long.valueOf(len),
Integer.valueOf(maxResultSize)));
+ }
+
+ return b64.encode(binaryData);
}
/**
- * Returns our current encode mode. True if we're URL-SAFE, false
otherwise.
+ * Encodes binary data using the base64 algorithm and chunks the encoded
output into 76 character blocks
*
- * @return true if we're in URL-SAFE mode, false otherwise.
- * @since 1.4
+ * @param binaryData
+ * binary data to encode
+ * @return Base64 characters chunked in 76 character blocks
*/
- public boolean isUrlSafe() {
- return this.encodeTable == URL_SAFE_ENCODE_TABLE;
+ public static byte[] encodeBase64Chunked(final byte[] binaryData) {
+ return encodeBase64(binaryData, true);
}
/**
- * <p>
- * Encodes all of the provided data, starting at inPos, for inAvail bytes.
Must be called at least twice: once with
- * the data to encode, and once with inAvail set to "-1" to alert encoder
that EOF has been reached, to flush last
- * remaining bytes (if not multiple of 3).
- * </p>
- * <p><b>Note: no padding is added when encoding using the URL-safe
alphabet.</b></p>
- * <p>
- * Thanks to "commons" project in ws.apache.org for the bitwise
operations, and general approach.
- * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
- * </p>
+ * Encodes binary data using the base64 algorithm but does not chunk the
output.
*
- * @param in
- * byte[] array of binary data to base64 encode.
- * @param inPos
- * Position to start reading data from.
- * @param inAvail
- * Amount of bytes available from input for encoding.
- * @param context
- * the context to be used
+ * NOTE: We changed the behavior of this method from multi-line chunking
(commons-codec-1.4) to
+ * single-line non-chunking (commons-codec-1.5).
+ *
+ * @param binaryData
+ * binary data to encode
+ * @return String containing Base64 characters.
+ * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not).
*/
- @Override
- void encode(final byte[] in, int inPos, final int inAvail, final Context
context) {
- if (context.eof) {
- return;
- }
- // inAvail < 0 is how we're informed of EOF in the underlying data
we're
- // encoding.
- if (inAvail < 0) {
- context.eof = true;
- if (0 == context.modulus && lineLength == 0) {
- return; // no leftovers to process and not using chunking
- }
- final byte[] buffer = ensureBufferSize(encodeSize, context);
- final int savedPos = context.pos;
- switch (context.modulus) { // 0-2
- case 0 : // nothing to do here
- break;
- case 1 : // 8 bits = 6 + 2
- // top 6 bits:
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 2) & MASK_6BITS];
- // remaining 2:
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
<< 4) & MASK_6BITS];
- // URL-SAFE skips the padding to further reduce size.
- if (encodeTable == STANDARD_ENCODE_TABLE) {
- buffer[context.pos++] = pad;
- buffer[context.pos++] = pad;
- }
- break;
-
- case 2 : // 16 bits = 6 + 6 + 4
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 10) & MASK_6BITS];
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 4) & MASK_6BITS];
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
<< 2) & MASK_6BITS];
- // URL-SAFE skips the padding to further reduce size.
- if (encodeTable == STANDARD_ENCODE_TABLE) {
- buffer[context.pos++] = pad;
- }
- break;
- default:
- throw new IllegalStateException(sm.getString(
- "base64.impossibleModulus",
Integer.valueOf(context.modulus)));
- }
- context.currentLinePos += context.pos - savedPos; // keep track of
current line position
- // if currentPos == 0 we are at the start of a line, so don't add
CRLF
- if (lineLength > 0 && context.currentLinePos > 0) {
- System.arraycopy(lineSeparator, 0, buffer, context.pos,
lineSeparator.length);
- context.pos += lineSeparator.length;
- }
- } else {
- for (int i = 0; i < inAvail; i++) {
- final byte[] buffer = ensureBufferSize(encodeSize, context);
- context.modulus = (context.modulus+1) %
BYTES_PER_UNENCODED_BLOCK;
- int b = in[inPos++];
- if (b < 0) {
- b += 256;
- }
- context.ibitWorkArea = (context.ibitWorkArea << 8) + b; //
BITS_PER_BYTE
- if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits
to extract
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 18) & MASK_6BITS];
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 12) & MASK_6BITS];
- buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 6) & MASK_6BITS];
- buffer[context.pos++] = encodeTable[context.ibitWorkArea &
MASK_6BITS];
- context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
- if (lineLength > 0 && lineLength <=
context.currentLinePos) {
- System.arraycopy(lineSeparator, 0, buffer,
context.pos, lineSeparator.length);
- context.pos += lineSeparator.length;
- context.currentLinePos = 0;
- }
- }
- }
- }
+ public static String encodeBase64String(final byte[] binaryData) {
+ return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
}
/**
- * <p>
- * Decodes all of the provided data, starting at inPos, for inAvail bytes.
Should be called at least twice: once
- * with the data to decode, and once with inAvail set to "-1" to alert
decoder that EOF has been reached. The "-1"
- * call is not necessary when decoding, but it doesn't hurt, either.
- * </p>
- * <p>
- * Ignores all non-base64 characters. This is how chunked (e.g. 76
character) data is handled, since CR and LF are
- * silently ignored, but has implications for other bytes, too. This
method subscribes to the garbage-in,
- * garbage-out philosophy: it will not check the provided data for
validity.
- * </p>
- * <p>
- * Thanks to "commons" project in ws.apache.org for the bitwise
operations, and general approach.
- * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
- * </p>
- *
- * @param in
- * byte[] array of ascii data to base64 decode.
- * @param inPos
- * Position to start reading data from.
- * @param inAvail
- * Amount of bytes available from input for decoding.
- * @param context
- * the context to be used
+ * Encodes binary data using a URL-safe variation of the base64 algorithm
but does not chunk the output. The
+ * url-safe variation emits - and _ instead of + and / characters.
+ * <b>Note: no padding is added.</b>
+ * @param binaryData
+ * binary data to encode
+ * @return byte[] containing Base64 characters in their UTF-8
representation.
+ * @since 1.4
*/
- @Override
- void decode(final byte[] in, int inPos, final int inAvail, final Context
context) {
- if (context.eof) {
- return;
- }
- if (inAvail < 0) {
- context.eof = true;
- }
- for (int i = 0; i < inAvail; i++) {
- final byte[] buffer = ensureBufferSize(decodeSize, context);
- final byte b = in[inPos++];
- if (b == pad) {
- // We're done.
- context.eof = true;
- break;
- }
- if (b >= 0 && b < decodeTable.length) {
- final int result = decodeTable[b];
- if (result >= 0) {
- context.modulus = (context.modulus+1) %
BYTES_PER_ENCODED_BLOCK;
- context.ibitWorkArea = (context.ibitWorkArea <<
BITS_PER_ENCODED_BYTE) + result;
- if (context.modulus == 0) {
- buffer[context.pos++] = (byte) ((context.ibitWorkArea
>> 16) & MASK_8BITS);
- buffer[context.pos++] = (byte) ((context.ibitWorkArea
>> 8) & MASK_8BITS);
- buffer[context.pos++] = (byte) (context.ibitWorkArea &
MASK_8BITS);
- }
- }
- }
- }
+ public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
+ return encodeBase64(binaryData, false, true);
+ }
- // Two forms of EOF as far as base64 decoder is concerned: actual
- // EOF (-1) and first time '=' character is encountered in stream.
- // This approach makes the '=' padding characters completely optional.
- if (context.eof && context.modulus != 0) {
- final byte[] buffer = ensureBufferSize(decodeSize, context);
+ /**
+ * Encodes binary data using a URL-safe variation of the base64 algorithm
but does not chunk the output. The
+ * url-safe variation emits - and _ instead of + and / characters.
+ * <b>Note: no padding is added.</b>
+ * @param binaryData
+ * binary data to encode
+ * @return String containing Base64 characters
+ * @since 1.4
+ */
+ public static String encodeBase64URLSafeString(final byte[] binaryData) {
+ return StringUtils.newStringUsAscii(encodeBase64(binaryData, false,
true));
+ }
- // We have some spare bits remaining
- // Output all whole multiples of 8 bits and ignore the rest
- switch (context.modulus) {
-// case 0 : // impossible, as excluded above
- case 1 : // 6 bits - ignore entirely
- // TODO not currently tested; perhaps it is impossible?
- break;
- case 2 : // 12 bits = 8 + 4
- validateCharacter(MASK_4BITS, context);
- context.ibitWorkArea = context.ibitWorkArea >> 4; // dump
the extra 4 bits
- buffer[context.pos++] = (byte) ((context.ibitWorkArea) &
MASK_8BITS);
- break;
- case 3 : // 18 bits = 8 + 8 + 2
- validateCharacter(MASK_2BITS, context);
- context.ibitWorkArea = context.ibitWorkArea >> 2; // dump
2 bits
- buffer[context.pos++] = (byte) ((context.ibitWorkArea >>
8) & MASK_8BITS);
- buffer[context.pos++] = (byte) ((context.ibitWorkArea) &
MASK_8BITS);
- break;
- default:
- throw new IllegalStateException(sm.getString(
- "base64.impossibleModulus",
Integer.valueOf(context.modulus)));
- }
+ /**
+ * Encodes to a byte64-encoded integer according to crypto standards such
as W3C's XML-Signature.
+ *
+ * @param bigInteger
+ * a BigInteger
+ * @return A byte array containing base64 character data
+ * @throws NullPointerException
+ * if null is passed in
+ * @since 1.4
+ */
+ public static byte[] encodeInteger(final BigInteger bigInteger) {
+ if (bigInteger == null) {
+ throw new
NullPointerException(sm.getString("base64.nullEncodeParameter"));
}
+ return encodeBase64(toIntegerBytes(bigInteger), false);
}
/**
- * Returns whether or not the <code>octet</code> is in the base 64
alphabet.
+ * Returns whether or not the {@code octet} is in the base 64 alphabet.
*
* @param octet
* The value to test
- * @return <code>true</code> if the value is defined in the the base 64
alphabet, <code>false</code> otherwise.
+ * @return {@code true} if the value is defined in the the base 64
alphabet, {@code false} otherwise.
* @since 1.4
*/
public static boolean isBase64(final byte octet) {
@@ -517,27 +350,13 @@ public class Base64 extends BaseNCodec {
}
/**
- * Tests a given String to see if it contains only valid characters within
the Base64 alphabet. Currently the
- * method treats whitespace as valid.
- *
- * @param base64
- * String to test
- * @return <code>true</code> if all characters in the String are valid
characters in the Base64 alphabet or if
- * the String is empty; <code>false</code>, otherwise
- * @since 1.5
- */
- public static boolean isBase64(final String base64) {
- return isBase64(StringUtils.getBytesUtf8(base64));
- }
-
- /**
* Tests a given byte array to see if it contains only valid characters
within the Base64 alphabet. Currently the
* method treats whitespace as valid.
*
* @param arrayOctet
* byte array to test
- * @return <code>true</code> if all bytes are valid characters in the
Base64 alphabet or if the byte array is empty;
- * <code>false</code>, otherwise
+ * @return {@code true} if all bytes are valid characters in the Base64
alphabet or if the byte array is empty;
+ * {@code false}, otherwise
* @since 1.5
*/
public static boolean isBase64(final byte[] arrayOctet) {
@@ -550,268 +369,436 @@ public class Base64 extends BaseNCodec {
}
/**
- * Encodes binary data using the base64 algorithm but does not chunk the
output.
+ * Tests a given String to see if it contains only valid characters within
the Base64 alphabet. Currently the
+ * method treats whitespace as valid.
*
- * @param binaryData
- * binary data to encode
- * @return byte[] containing Base64 characters in their UTF-8
representation.
+ * @param base64
+ * String to test
+ * @return {@code true} if all characters in the String are valid
characters in the Base64 alphabet or if
+ * the String is empty; {@code false}, otherwise
+ * @since 1.5
*/
- public static byte[] encodeBase64(final byte[] binaryData) {
- return encodeBase64(binaryData, false);
+ public static boolean isBase64(final String base64) {
+ return isBase64(StringUtils.getBytesUtf8(base64));
}
/**
- * Encodes binary data using the base64 algorithm but does not chunk the
output.
+ * Returns a byte-array representation of a {@code BigInteger} without
sign bit.
*
- * NOTE: We changed the behaviour of this method from multi-line chunking
(commons-codec-1.4) to
- * single-line non-chunking (commons-codec-1.5).
- *
- * @param binaryData
- * binary data to encode
- * @return String containing Base64 characters.
- * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not).
+ * @param bigInt
+ * {@code BigInteger} to be converted
+ * @return a byte array representation of the BigInteger parameter
*/
- public static String encodeBase64String(final byte[] binaryData) {
- return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
- }
+ static byte[] toIntegerBytes(final BigInteger bigInt) {
+ int bitlen = bigInt.bitLength();
+ // round bitlen
+ bitlen = ((bitlen + 7) >> 3) << 3;
+ final byte[] bigBytes = bigInt.toByteArray();
- /**
- * Encodes binary data using a URL-safe variation of the base64 algorithm
but does not chunk the output. The
- * url-safe variation emits - and _ instead of + and / characters.
- * <b>Note: no padding is added.</b>
- * @param binaryData
- * binary data to encode
- * @return byte[] containing Base64 characters in their UTF-8
representation.
- * @since 1.4
- */
- public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
- return encodeBase64(binaryData, false, true);
+ if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1)
== (bitlen / 8))) {
+ return bigBytes;
+ }
+ // set up params for copying everything but sign bit
+ int startSrc = 0;
+ int len = bigBytes.length;
+
+ // if bigInt is exactly byte-aligned, just skip signbit in copy
+ if ((bigInt.bitLength() % 8) == 0) {
+ startSrc = 1;
+ len--;
+ }
+ final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
+ final byte[] resizedBytes = new byte[bitlen / 8];
+ System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
+ return resizedBytes;
}
/**
- * Encodes binary data using a URL-safe variation of the base64 algorithm
but does not chunk the output. The
- * url-safe variation emits - and _ instead of + and / characters.
- * <b>Note: no padding is added.</b>
- * @param binaryData
- * binary data to encode
- * @return String containing Base64 characters
- * @since 1.4
- */
- public static String encodeBase64URLSafeString(final byte[] binaryData) {
- return StringUtils.newStringUsAscii(encodeBase64(binaryData, false,
true));
+ * Validates whether decoding the final trailing character is possible in
the context
+ * of the set of possible base 64 values.
+ *
+ * <p>The character is valid if the lower bits within the provided mask
are zero. This
+ * is used to test the final trailing base-64 digit is zero in the bits
that will be discarded.
+ *
+ * @param emptyBitsMask The mask of the lower bits that should be empty
+ * @param context the context to be used
+ *
+ * @throws IllegalArgumentException if the bits being checked contain any
non-zero value
+ */
+ private static void validateCharacter(final int emptyBitsMask, final
Context context) {
+ if ((context.ibitWorkArea & emptyBitsMask) != 0) {
+ throw new IllegalArgumentException(
+ "Last encoded character (before the paddings if any) is a
valid base 64 alphabet but not a possible value. " +
+ "Expected the discarded bits to be zero.");
+ }
}
+
/**
- * Encodes binary data using the base64 algorithm and chunks the encoded
output into 76 character blocks
- *
- * @param binaryData
- * binary data to encode
- * @return Base64 characters chunked in 76 character blocks
+ * Encode table to use: either STANDARD or URL_SAFE. Note: the
DECODE_TABLE above remains static because it is able
+ * to decode both STANDARD and URL_SAFE streams, but the encodeTable must
be a member variable so we can switch
+ * between the two modes.
*/
- public static byte[] encodeBase64Chunked(final byte[] binaryData) {
- return encodeBase64(binaryData, true);
- }
+ private final byte[] encodeTable;
+
+ // Only one decode table currently; keep for consistency with Base32 code
+ private final byte[] decodeTable;
/**
- * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
- *
- * @param binaryData
- * Array containing binary data to encode.
- * @param isChunked
- * if <code>true</code> this encoder will chunk the base64
output into 76 character blocks
- * @return Base64-encoded data.
- * @throws IllegalArgumentException
- * Thrown when the input array needs an output array bigger
than {@link Integer#MAX_VALUE}
+ * Line separator for encoding. Not used when decoding. Only used if
lineLength > 0.
*/
- public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked) {
- return encodeBase64(binaryData, isChunked, false);
- }
+ private final byte[] lineSeparator;
/**
- * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
+ * Convenience variable to help us determine when our buffer is going to
run out of room and needs resizing.
+ * {@code decodeSize = 3 + lineSeparator.length;}
+ */
+ private final int decodeSize;
+
+ /**
+ * Convenience variable to help us determine when our buffer is going to
run out of room and needs resizing.
+ * {@code encodeSize = 4 + lineSeparator.length;}
+ */
+ private final int encodeSize;
+
+ /**
+ * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
+ * <p>
+ * When encoding the line length is 0 (no chunking), and the encoding
table is STANDARD_ENCODE_TABLE.
+ * </p>
*
- * @param binaryData
- * Array containing binary data to encode.
- * @param isChunked
- * if <code>true</code> this encoder will chunk the base64
output into 76 character blocks
- * @param urlSafe
- * if <code>true</code> this encoder will emit - and _ instead
of the usual + and / characters.
- * <b>Note: no padding is added when encoding using the
URL-safe alphabet.</b>
- * @return Base64-encoded data.
- * @throws IllegalArgumentException
- * Thrown when the input array needs an output array bigger
than {@link Integer#MAX_VALUE}
- * @since 1.4
+ * <p>
+ * When decoding all variants are supported.
+ * </p>
*/
- public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked, final boolean urlSafe) {
- return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
+ public Base64() {
+ this(0);
}
/**
- * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
+ * Creates a Base64 codec used for decoding (all modes) and encoding in
the given URL-safe mode.
+ * <p>
+ * When encoding the line length is 76, the line separator is CRLF, and
the encoding table is STANDARD_ENCODE_TABLE.
+ * </p>
+ *
+ * <p>
+ * When decoding all variants are supported.
+ * </p>
*
- * @param binaryData
- * Array containing binary data to encode.
- * @param isChunked
- * if <code>true</code> this encoder will chunk the base64
output into 76 character blocks
* @param urlSafe
- * if <code>true</code> this encoder will emit - and _ instead
of the usual + and / characters.
- * <b>Note: no padding is added when encoding using the
URL-safe alphabet.</b>
- * @param maxResultSize
- * The maximum result size to accept.
- * @return Base64-encoded data.
- * @throws IllegalArgumentException
- * Thrown when the input array needs an output array bigger
than maxResultSize
+ * if {@code true}, URL-safe encoding is used. In most cases
this should be set to
+ * {@code false}.
* @since 1.4
*/
- public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked,
- final boolean urlSafe, final int
maxResultSize) {
- if (binaryData == null || binaryData.length == 0) {
- return binaryData;
- }
-
- // Create this so can use the super-class method
- // Also ensures that the same roundings are performed by the ctor and
the code
- final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0,
CHUNK_SEPARATOR, urlSafe);
- final long len = b64.getEncodedLength(binaryData);
- if (len > maxResultSize) {
- throw new IllegalArgumentException(sm.getString(
- "base64.inputTooLarge", Long.valueOf(len),
Integer.valueOf(maxResultSize)));
- }
-
- return b64.encode(binaryData);
+ public Base64(final boolean urlSafe) {
+ this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
}
/**
- * Decodes a Base64 String into octets.
+ * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
* <p>
- * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or
normal mode.
+ * When encoding the line length is given in the constructor, the line
separator is CRLF, and the encoding table is
+ * STANDARD_ENCODE_TABLE.
+ * </p>
+ * <p>
+ * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
+ * </p>
+ * <p>
+ * When decoding all variants are supported.
* </p>
*
- * @param base64String
- * String containing Base64 data
- * @return Array containing decoded data.
+ * @param lineLength
+ * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
+ * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
+ * decoding.
* @since 1.4
*/
- public static byte[] decodeBase64(final String base64String) {
- return new Base64().decode(base64String);
- }
-
- public static byte[] decodeBase64URLSafe(final String base64String) {
- return new Base64(true).decode(base64String);
+ public Base64(final int lineLength) {
+ this(lineLength, CHUNK_SEPARATOR);
}
/**
- * Decodes Base64 data into octets.
+ * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
* <p>
- * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or
normal mode.
+ * When encoding the line length and line separator are given in the
constructor, and the encoding table is
+ * STANDARD_ENCODE_TABLE.
+ * </p>
+ * <p>
+ * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
+ * </p>
+ * <p>
+ * When decoding all variants are supported.
* </p>
*
- * @param base64Data
- * Byte array containing Base64 data
- * @return Array containing decoded data.
+ * @param lineLength
+ * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
+ * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
+ * decoding.
+ * @param lineSeparator
+ * Each line of encoded data will end with this sequence of
bytes.
+ * @throws IllegalArgumentException
+ * Thrown when the provided lineSeparator included some base64
characters.
+ * @since 1.4
*/
- public static byte[] decodeBase64(final byte[] base64Data) {
- return decodeBase64(base64Data, 0, base64Data.length);
- }
-
- public static byte[] decodeBase64(
- final byte[] base64Data, final int off, final int len) {
- return new Base64().decode(base64Data, off, len);
+ public Base64(final int lineLength, final byte[] lineSeparator) {
+ this(lineLength, lineSeparator, false);
}
- // Implementation of the Encoder Interface
-
- // Implementation of integer encoding used for crypto
/**
- * Decodes a byte64-encoded integer according to crypto standards such as
W3C's XML-Signature.
+ * Creates a Base64 codec used for decoding (all modes) and encoding in
URL-unsafe mode.
+ * <p>
+ * When encoding the line length and line separator are given in the
constructor, and the encoding table is
+ * STANDARD_ENCODE_TABLE.
+ * </p>
+ * <p>
+ * Line lengths that aren't multiples of 4 will still essentially end up
being multiples of 4 in the encoded data.
+ * </p>
+ * <p>
+ * When decoding all variants are supported.
+ * </p>
*
- * @param pArray
- * a byte array containing base64 character data
- * @return A BigInteger
+ * @param lineLength
+ * Each line of encoded data will be at most of the given
length (rounded down to nearest multiple of
+ * 4). If lineLength <= 0, then the output will not be
divided into lines (chunks). Ignored when
+ * decoding.
+ * @param lineSeparator
+ * Each line of encoded data will end with this sequence of
bytes.
+ * @param urlSafe
+ * Instead of emitting '+' and '/' we emit '-' and '_'
respectively. urlSafe is only applied to encode
+ * operations. Decoding seamlessly handles both modes.
+ * <b>Note: no padding is added when using the URL-safe
alphabet.</b>
+ * @throws IllegalArgumentException
+ * Thrown when the {@code lineSeparator} contains Base64
characters.
* @since 1.4
*/
- public static BigInteger decodeInteger(final byte[] pArray) {
- return new BigInteger(1, decodeBase64(pArray));
+ public Base64(final int lineLength, final byte[] lineSeparator, final
boolean urlSafe) {
+ super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
+ lineLength,
+ lineSeparator == null ? 0 : lineSeparator.length);
+ // Needs to be set early to avoid NPE during call to
containsAlphabetOrPad() below
+ this.decodeTable = urlSafe ? URL_SAFE_DECODE_TABLE :
STANDARD_DECODE_TABLE;
+ // TODO could be simplified if there is no requirement to reject
invalid line sep when length <=0
+ // @see test case Base64Test.testConstructors()
+ if (lineSeparator != null) {
+ if (containsAlphabetOrPad(lineSeparator)) {
+ final String sep = StringUtils.newStringUtf8(lineSeparator);
+ throw new
IllegalArgumentException(sm.getString("base64.lineSeparator", sep));
+ }
+ if (lineLength > 0){ // null line-sep forces no chunking rather
than throwing IAE
+ this.encodeSize = BYTES_PER_ENCODED_BLOCK +
lineSeparator.length;
+ this.lineSeparator = new byte[lineSeparator.length];
+ System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
lineSeparator.length);
+ } else {
+ this.encodeSize = BYTES_PER_ENCODED_BLOCK;
+ this.lineSeparator = null;
+ }
+ } else {
+ this.encodeSize = BYTES_PER_ENCODED_BLOCK;
+ this.lineSeparator = null;
+ }
+ this.decodeSize = this.encodeSize - 1;
+ this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE :
STANDARD_ENCODE_TABLE;
}
+ // Implementation of the Encoder Interface
+
/**
- * Encodes to a byte64-encoded integer according to crypto standards such
as W3C's XML-Signature.
+ * <p>
+ * Decodes all of the provided data, starting at inPos, for inAvail bytes.
Should be called at least twice: once
+ * with the data to decode, and once with inAvail set to "-1" to alert
decoder that EOF has been reached. The "-1"
+ * call is not necessary when decoding, but it doesn't hurt, either.
+ * </p>
+ * <p>
+ * Ignores all non-base64 characters. This is how chunked (e.g. 76
character) data is handled, since CR and LF are
+ * silently ignored, but has implications for other bytes, too. This
method subscribes to the garbage-in,
+ * garbage-out philosophy: it will not check the provided data for
validity.
+ * </p>
+ * <p>
+ * Thanks to "commons" project in ws.apache.org for the bitwise
operations, and general approach.
+ * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+ * </p>
*
- * @param bigInt
- * a BigInteger
- * @return A byte array containing base64 character data
- * @throws NullPointerException
- * if null is passed in
- * @since 1.4
+ * @param in
+ * byte[] array of ascii data to base64 decode.
+ * @param inPos
+ * Position to start reading data from.
+ * @param inAvail
+ * Amount of bytes available from input for decoding.
+ * @param context
+ * the context to be used
*/
- public static byte[] encodeInteger(final BigInteger bigInt) {
- if (bigInt == null) {
- throw new
NullPointerException(sm.getString("base64.nullEncodeParameter"));
+ @Override
+ void decode(final byte[] in, int inPos, final int inAvail, final Context
context) {
+ if (context.eof) {
+ return;
+ }
+ if (inAvail < 0) {
+ context.eof = true;
+ }
+ for (int i = 0; i < inAvail; i++) {
+ final byte[] buffer = ensureBufferSize(decodeSize, context);
+ final byte b = in[inPos++];
+ if (b == pad) {
+ // We're done.
+ context.eof = true;
+ break;
+ }
+ if (b >= 0 && b < decodeTable.length) {
+ final int result = decodeTable[b];
+ if (result >= 0) {
+ context.modulus = (context.modulus+1) %
BYTES_PER_ENCODED_BLOCK;
+ context.ibitWorkArea = (context.ibitWorkArea <<
BITS_PER_ENCODED_BYTE) + result;
+ if (context.modulus == 0) {
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea
>> 16) & MASK_8BITS);
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea
>> 8) & MASK_8BITS);
+ buffer[context.pos++] = (byte) (context.ibitWorkArea &
MASK_8BITS);
+ }
+ }
+ }
+ }
+
+ // Two forms of EOF as far as base64 decoder is concerned: actual
+ // EOF (-1) and first time '=' character is encountered in stream.
+ // This approach makes the '=' padding characters completely optional.
+ if (context.eof && context.modulus != 0) {
+ final byte[] buffer = ensureBufferSize(decodeSize, context);
+
+ // We have some spare bits remaining
+ // Output all whole multiples of 8 bits and ignore the rest
+ switch (context.modulus) {
+// case 0 : // impossible, as excluded above
+// case 1 : // 6 bits - invalid - use default below
+ case 2 : // 12 bits = 8 + 4
+ validateCharacter(MASK_4BITS, context);
+ context.ibitWorkArea = context.ibitWorkArea >> 4; // dump
the extra 4 bits
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea) &
MASK_8BITS);
+ break;
+ case 3 : // 18 bits = 8 + 8 + 2
+ validateCharacter(MASK_2BITS, context);
+ context.ibitWorkArea = context.ibitWorkArea >> 2; // dump
2 bits
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea >>
8) & MASK_8BITS);
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea) &
MASK_8BITS);
+ break;
+ default:
+ throw new IllegalStateException(sm.getString(
+ "base64.impossibleModulus",
Integer.valueOf(context.modulus)));
+ }
}
- return encodeBase64(toIntegerBytes(bigInt), false);
}
/**
- * Returns a byte-array representation of a <code>BigInteger</code>
without sign bit.
+ * <p>
+ * Encodes all of the provided data, starting at inPos, for inAvail bytes.
Must be called at least twice: once with
+ * the data to encode, and once with inAvail set to "-1" to alert encoder
that EOF has been reached, to flush last
+ * remaining bytes (if not multiple of 3).
+ * </p>
+ * <p><b>Note: no padding is added when encoding using the URL-safe
alphabet.</b></p>
+ * <p>
+ * Thanks to "commons" project in ws.apache.org for the bitwise
operations, and general approach.
+ * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+ * </p>
*
- * @param bigInt
- * <code>BigInteger</code> to be converted
- * @return a byte array representation of the BigInteger parameter
+ * @param in
+ * byte[] array of binary data to base64 encode.
+ * @param inPos
+ * Position to start reading data from.
+ * @param inAvail
+ * Amount of bytes available from input for encoding.
+ * @param context
+ * the context to be used
*/
- static byte[] toIntegerBytes(final BigInteger bigInt) {
- int bitlen = bigInt.bitLength();
- // round bitlen
- bitlen = ((bitlen + 7) >> 3) << 3;
- final byte[] bigBytes = bigInt.toByteArray();
-
- if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1)
== (bitlen / 8))) {
- return bigBytes;
+ @Override
+ void encode(final byte[] in, int inPos, final int inAvail, final Context
context) {
+ if (context.eof) {
+ return;
}
- // set up params for copying everything but sign bit
- int startSrc = 0;
- int len = bigBytes.length;
+ // inAvail < 0 is how we're informed of EOF in the underlying data
we're
+ // encoding.
+ if (inAvail < 0) {
+ context.eof = true;
+ if (0 == context.modulus && lineLength == 0) {
+ return; // no leftovers to process and not using chunking
+ }
+ final byte[] buffer = ensureBufferSize(encodeSize, context);
+ final int savedPos = context.pos;
+ switch (context.modulus) { // 0-2
+ case 0 : // nothing to do here
+ break;
+ case 1 : // 8 bits = 6 + 2
+ // top 6 bits:
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 2) & MASK_6BITS];
+ // remaining 2:
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
<< 4) & MASK_6BITS];
+ // URL-SAFE skips the padding to further reduce size.
+ if (encodeTable == STANDARD_ENCODE_TABLE) {
+ buffer[context.pos++] = pad;
+ buffer[context.pos++] = pad;
+ }
+ break;
- // if bigInt is exactly byte-aligned, just skip signbit in copy
- if ((bigInt.bitLength() % 8) == 0) {
- startSrc = 1;
- len--;
+ case 2 : // 16 bits = 6 + 6 + 4
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 10) & MASK_6BITS];
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 4) & MASK_6BITS];
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
<< 2) & MASK_6BITS];
+ // URL-SAFE skips the padding to further reduce size.
+ if (encodeTable == STANDARD_ENCODE_TABLE) {
+ buffer[context.pos++] = pad;
+ }
+ break;
+ default:
+ throw new IllegalStateException(sm.getString(
+ "base64.impossibleModulus",
Integer.valueOf(context.modulus)));
+ }
+ context.currentLinePos += context.pos - savedPos; // keep track of
current line position
+ // if currentPos == 0 we are at the start of a line, so don't add
CRLF
+ if (lineLength > 0 && context.currentLinePos > 0) {
+ System.arraycopy(lineSeparator, 0, buffer, context.pos,
lineSeparator.length);
+ context.pos += lineSeparator.length;
+ }
+ } else {
+ for (int i = 0; i < inAvail; i++) {
+ final byte[] buffer = ensureBufferSize(encodeSize, context);
+ context.modulus = (context.modulus+1) %
BYTES_PER_UNENCODED_BLOCK;
+ int b = in[inPos++];
+ if (b < 0) {
+ b += 256;
+ }
+ context.ibitWorkArea = (context.ibitWorkArea << 8) + b; //
BITS_PER_BYTE
+ if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits
to extract
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 18) & MASK_6BITS];
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 12) & MASK_6BITS];
+ buffer[context.pos++] = encodeTable[(context.ibitWorkArea
>> 6) & MASK_6BITS];
+ buffer[context.pos++] = encodeTable[context.ibitWorkArea &
MASK_6BITS];
+ context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
+ if (lineLength > 0 && lineLength <=
context.currentLinePos) {
+ System.arraycopy(lineSeparator, 0, buffer,
context.pos, lineSeparator.length);
+ context.pos += lineSeparator.length;
+ context.currentLinePos = 0;
+ }
+ }
+ }
}
- final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
- final byte[] resizedBytes = new byte[bitlen / 8];
- System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
- return resizedBytes;
}
/**
- * Returns whether or not the <code>octet</code> is in the Base64 alphabet.
+ * Returns whether or not the {@code octet} is in the Base64 alphabet.
*
* @param octet
* The value to test
- * @return <code>true</code> if the value is defined in the the Base64
alphabet <code>false</code> otherwise.
+ * @return {@code true} if the value is defined in the the Base64 alphabet
{@code false} otherwise.
*/
@Override
protected boolean isInAlphabet(final byte octet) {
return octet >= 0 && octet < decodeTable.length && decodeTable[octet]
!= -1;
}
-
/**
- * Validates whether decoding the final trailing character is possible in
the context
- * of the set of possible base 64 values.
- *
- * <p>The character is valid if the lower bits within the provided mask
are zero. This
- * is used to test the final trailing base-64 digit is zero in the bits
that will be discarded.
- *
- * @param emptyBitsMask The mask of the lower bits that should be empty
- * @param context the context to be used
+ * Returns our current encode mode. True if we're URL-SAFE, false
otherwise.
*
- * @throws IllegalArgumentException if the bits being checked contain any
non-zero value
+ * @return true if we're in URL-SAFE mode, false otherwise.
+ * @since 1.4
*/
- private static void validateCharacter(final int emptyBitsMask, final
Context context) {
- if ((context.ibitWorkArea & emptyBitsMask) != 0) {
- throw new IllegalArgumentException(
- "Last encoded character (before the paddings if any) is a
valid base 64 alphabet but not a possible value. " +
- "Expected the discarded bits to be zero.");
- }
+ public boolean isUrlSafe() {
+ return this.encodeTable == URL_SAFE_ENCODE_TABLE;
}
}
diff --git a/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
b/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
index 0e2d1ad..0d29096 100644
--- a/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
+++ b/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
@@ -94,11 +94,9 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
@SuppressWarnings("boxing") // OK to ignore boxing here
@Override
public String toString() {
- return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, " +
- "ibitWorkArea=%s, modulus=%s, pos=%s, " +
- "readPos=%s]", this.getClass().getSimpleName(),
- HexUtils.toHexString(buffer), currentLinePos, eof,
- ibitWorkArea, modulus, pos, readPos);
+ return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s,
ibitWorkArea=%s, " +
+ "modulus=%s, pos=%s, readPos=%s]",
this.getClass().getSimpleName(), HexUtils.toHexString(buffer),
+ currentLinePos, eof, ibitWorkArea, modulus, pos, readPos);
}
}
@@ -161,111 +159,12 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
*/
protected static final byte PAD_DEFAULT = '='; // Allow static access to
default
- protected final byte pad; // instance variable just in case it needs to
vary later
-
- /** Number of bytes in each full block of unencoded data, e.g. 4 for
Base64 and 5 for Base32 */
- private final int unencodedBlockSize;
-
- /** Number of bytes in each full block of encoded data, e.g. 3 for Base64
and 8 for Base32 */
- private final int encodedBlockSize;
-
- /**
- * Chunksize for encoding. Not used when decoding.
- * A value of zero or less implies no chunking of the encoded data.
- * Rounded down to nearest multiple of encodedBlockSize.
- */
- protected final int lineLength;
-
- /**
- * Size of chunk separator. Not used unless {@link #lineLength} > 0.
- */
- private final int chunkSeparatorLength;
-
- /**
- * Note <code>lineLength</code> is rounded down to the nearest multiple of
the encoded block size.
- * If <code>chunkSeparatorLength</code> is zero, then chunking is disabled.
- * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 =
3)
- * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
- * @param lineLength if > 0, use chunking with a length
<code>lineLength</code>
- * @param chunkSeparatorLength the chunk separator length, if relevant
- */
- protected BaseNCodec(final int unencodedBlockSize, final int
encodedBlockSize,
- final int lineLength, final int chunkSeparatorLength)
{
- this(unencodedBlockSize, encodedBlockSize, lineLength,
chunkSeparatorLength, PAD_DEFAULT);
- }
-
- /**
- * Note <code>lineLength</code> is rounded down to the nearest multiple of
the encoded block size.
- * If <code>chunkSeparatorLength</code> is zero, then chunking is disabled.
- * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 =
3)
- * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
- * @param lineLength if > 0, use chunking with a length
<code>lineLength</code>
- * @param chunkSeparatorLength the chunk separator length, if relevant
- * @param pad byte used as padding byte.
- */
- protected BaseNCodec(final int unencodedBlockSize, final int
encodedBlockSize,
- final int lineLength, final int chunkSeparatorLength,
final byte pad) {
- this.unencodedBlockSize = unencodedBlockSize;
- this.encodedBlockSize = encodedBlockSize;
- final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;
- this.lineLength = useChunking ? (lineLength / encodedBlockSize) *
encodedBlockSize : 0;
- this.chunkSeparatorLength = chunkSeparatorLength;
-
- this.pad = pad;
- }
-
- /**
- * Returns true if this object has buffered data for reading.
- *
- * @param context the context to be used
- * @return true if there is data still available for reading.
- */
- boolean hasData(final Context context) { // package protected for access
from I/O streams
- return context.buffer != null;
- }
-
- /**
- * Returns the amount of buffered data available for reading.
- *
- * @param context the context to be used
- * @return The amount of buffered data available for reading.
- */
- int available(final Context context) { // package protected for access
from I/O streams
- return context.buffer != null ? context.pos - context.readPos : 0;
- }
-
/**
- * Get the default buffer size. Can be overridden.
+ * Chunk separator per RFC 2045 section 2.1.
*
- * @return the default buffer size.
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section
2.1</a>
*/
- protected int getDefaultBufferSize() {
- return DEFAULT_BUFFER_SIZE;
- }
-
- /**
- * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
- * @param context the context to be used
- * @param minCapacity the minimum required capacity
- * @return the resized byte[] buffer
- * @throws OutOfMemoryError if the {@code minCapacity} is negative
- */
- private static byte[] resizeBuffer(final Context context, final int
minCapacity) {
- // Overflow-conscious code treats the min and new capacity as unsigned.
- final int oldCapacity = context.buffer.length;
- int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
- if (compareUnsigned(newCapacity, minCapacity) < 0) {
- newCapacity = minCapacity;
- }
- if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
- newCapacity = createPositiveCapacity(minCapacity);
- }
-
- final byte[] b = new byte[newCapacity];
- System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
- context.buffer = b;
- return b;
- }
+ static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
/**
* Compares two {@code int} values numerically treating the values
@@ -280,7 +179,7 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
* a value greater than {@code 0} if {@code x > y} as
* unsigned values
*/
- private static int compareUnsigned(int x, int y) {
+ private static int compareUnsigned(final int x, final int y) {
return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
}
@@ -293,7 +192,7 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
* @return the capacity
* @throws OutOfMemoryError if the {@code minCapacity} is negative
*/
- private static int createPositiveCapacity(int minCapacity) {
+ private static int createPositiveCapacity(final int minCapacity) {
if (minCapacity < 0) {
// overflow
throw new OutOfMemoryError("Unable to allocate array size: " +
(minCapacity & 0xffffffffL));
@@ -312,53 +211,14 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * Ensure that the buffer has room for <code>size</code> bytes
+ * Gets a copy of the chunk separator per RFC 2045 section 2.1.
*
- * @param size minimum spare space required
- * @param context the context to be used
- * @return the buffer
+ * @return the chunk separator
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section
2.1</a>
+ * @since 1.15
*/
- protected byte[] ensureBufferSize(final int size, final Context context){
- if (context.buffer == null) {
- context.buffer = new byte[getDefaultBufferSize()];
- context.pos = 0;
- context.readPos = 0;
-
- // Overflow-conscious:
- // x + y > z == x + y - z > 0
- } else if (context.pos + size - context.buffer.length > 0) {
- return resizeBuffer(context, context.pos + size);
- }
- return context.buffer;
- }
-
- /**
- * Extracts buffered data into the provided byte[] array, starting at
position bPos, up to a maximum of bAvail
- * bytes. Returns how many bytes were actually extracted.
- * <p>
- * Package protected for access from I/O streams.
- *
- * @param b
- * byte[] array to extract the buffered data into.
- * @param bPos
- * position in byte[] array to start extraction at.
- * @param bAvail
- * amount of bytes we're allowed to extract. We may extract
fewer (if fewer are available).
- * @param context
- * the context to be used
- * @return The number of bytes successfully extracted into the provided
byte[] array.
- */
- int readResults(final byte[] b, final int bPos, final int bAvail, final
Context context) {
- if (context.buffer != null) {
- final int len = Math.min(available(context), bAvail);
- System.arraycopy(context.buffer, context.readPos, b, bPos, len);
- context.readPos += len;
- if (context.readPos >= context.pos) {
- context.buffer = null; // so hasData() will return false, and
this method can return -1
- }
- return len;
- }
- return context.eof ? EOF : 0;
+ public static byte[] getChunkSeparator() {
+ return CHUNK_SEPARATOR.clone();
}
/**
@@ -381,83 +241,110 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * Encodes an Object using the Base-N algorithm. This method is provided
in order to satisfy the requirements of
- * the Encoder interface, and will throw an EncoderException if the
supplied object is not of type byte[].
- *
- * @param obj
- * Object to encode
- * @return An object (of type byte[]) containing the Base-N encoded data
which corresponds to the byte[] supplied.
- * @throws EncoderException
- * if the parameter supplied is not of type byte[]
- * @deprecated This unused method will be removed in Tomcat 9
+ * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
+ * @param context the context to be used
+ * @param minCapacity the minimum required capacity
+ * @return the resized byte[] buffer
+ * @throws OutOfMemoryError if the {@code minCapacity} is negative
*/
- @Override
- @Deprecated
- public Object encode(final Object obj) throws EncoderException {
- if (!(obj instanceof byte[])) {
- throw new EncoderException("Parameter supplied to Base-N encode is
not a byte[]");
+ private static byte[] resizeBuffer(final Context context, final int
minCapacity) {
+ // Overflow-conscious code treats the min and new capacity as unsigned.
+ final int oldCapacity = context.buffer.length;
+ int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
+ if (compareUnsigned(newCapacity, minCapacity) < 0) {
+ newCapacity = minCapacity;
}
- return encode((byte[]) obj);
+ if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
+ newCapacity = createPositiveCapacity(minCapacity);
+ }
+
+ final byte[] b = new byte[newCapacity];
+ System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
+ context.buffer = b;
+ return b;
}
+ protected final byte pad; // instance variable just in case it needs to
vary later
+
+ /** Number of bytes in each full block of unencoded data, e.g. 4 for
Base64 and 5 for Base32 */
+ private final int unencodedBlockSize;
+
+ /** Number of bytes in each full block of encoded data, e.g. 3 for Base64
and 8 for Base32 */
+ private final int encodedBlockSize;
+
/**
- * Encodes a byte[] containing binary data, into a String containing
characters in the Base-N alphabet.
- * Uses UTF8 encoding.
- *
- * @param pArray
- * a byte array containing binary data
- * @return A String containing only Base-N character data
+ * Chunksize for encoding. Not used when decoding.
+ * A value of zero or less implies no chunking of the encoded data.
+ * Rounded down to nearest multiple of encodedBlockSize.
*/
- public String encodeToString(final byte[] pArray) {
- return StringUtils.newStringUtf8(encode(pArray));
+ protected final int lineLength;
+
+ /**
+ * Size of chunk separator. Not used unless {@link #lineLength} > 0.
+ */
+ private final int chunkSeparatorLength;
+
+ /**
+ * Note {@code lineLength} is rounded down to the nearest multiple of the
encoded block size.
+ * If {@code chunkSeparatorLength} is zero, then chunking is disabled.
+ * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 =
3)
+ * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
+ * @param lineLength if > 0, use chunking with a length {@code
lineLength}
+ * @param chunkSeparatorLength the chunk separator length, if relevant
+ */
+ protected BaseNCodec(final int unencodedBlockSize, final int
encodedBlockSize,
+ final int lineLength, final int chunkSeparatorLength)
{
+ this(unencodedBlockSize, encodedBlockSize, lineLength,
chunkSeparatorLength, PAD_DEFAULT);
}
/**
- * Encodes a byte[] containing binary data, into a String containing
characters in the appropriate alphabet.
- * Uses UTF8 encoding.
- *
- * @param pArray a byte array containing binary data
- * @return String containing only character data in the appropriate
alphabet.
- * @since 1.5
- * This is a duplicate of {@link #encodeToString(byte[])}; it was merged
during refactoring.
- */
- public String encodeAsString(final byte[] pArray){
- return StringUtils.newStringUtf8(encode(pArray));
+ * Note {@code lineLength} is rounded down to the nearest multiple of the
encoded block size.
+ * If {@code chunkSeparatorLength} is zero, then chunking is disabled.
+ * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 =
3)
+ * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
+ * @param lineLength if > 0, use chunking with a length {@code
lineLength}
+ * @param chunkSeparatorLength the chunk separator length, if relevant
+ * @param pad byte used as padding byte.
+ */
+ protected BaseNCodec(final int unencodedBlockSize, final int
encodedBlockSize,
+ final int lineLength, final int chunkSeparatorLength,
final byte pad) {
+ this.unencodedBlockSize = unencodedBlockSize;
+ this.encodedBlockSize = encodedBlockSize;
+ final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;
+ this.lineLength = useChunking ? (lineLength / encodedBlockSize) *
encodedBlockSize : 0;
+ this.chunkSeparatorLength = chunkSeparatorLength;
+ this.pad = pad;
}
/**
- * Decodes an Object using the Base-N algorithm. This method is provided
in order to satisfy the requirements of
- * the Decoder interface, and will throw a DecoderException if the
supplied object is not of type byte[] or String.
+ * Returns the amount of buffered data available for reading.
*
- * @param obj
- * Object to decode
- * @return An object (of type byte[]) containing the binary data which
corresponds to the byte[] or String
- * supplied.
- * @throws DecoderException
- * if the parameter supplied is not of type byte[]
- * @deprecated This unused method will be removed in Tomcat 9
+ * @param context the context to be used
+ * @return The amount of buffered data available for reading.
*/
- @Override
- @Deprecated
- public Object decode(final Object obj) throws DecoderException {
- if (obj instanceof byte[]) {
- return decode((byte[]) obj);
- } else if (obj instanceof String) {
- return decode((String) obj);
- } else {
- throw new DecoderException("Parameter supplied to Base-N decode is
not a byte[] or a String");
- }
+ int available(final Context context) { // package protected for access
from I/O streams
+ return context.buffer != null ? context.pos - context.readPos : 0;
}
/**
- * Decodes a String containing characters in the Base-N alphabet.
+ * Tests a given byte array to see if it contains any characters within
the alphabet or PAD.
*
- * @param pArray
- * A String containing Base-N character data
- * @return a byte array containing binary data
+ * Intended for use in checking line-ending arrays
+ *
+ * @param arrayOctet
+ * byte array to test
+ * @return {@code true} if any byte is a valid character in the alphabet
or PAD; {@code false} otherwise
*/
- public byte[] decode(final String pArray) {
- return decode(StringUtils.getBytesUtf8(pArray));
+ protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
+ if (arrayOctet == null) {
+ return false;
+ }
+ for (final byte element : arrayOctet) {
+ if (pad == element || isInAlphabet(element)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -484,6 +371,44 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
return result;
}
+ // package protected for access from I/O streams
+ abstract void decode(byte[] pArray, int i, int length, Context context);
+
+ /**
+ * Decodes a String containing characters in the Base-N alphabet.
+ *
+ * @param pArray
+ * A String containing Base-N character data
+ * @return a byte array containing binary data
+ */
+ public byte[] decode(final String pArray) {
+ return decode(StringUtils.getBytesUtf8(pArray));
+ }
+
+ /**
+ * Decodes an Object using the Base-N algorithm. This method is provided
in order to satisfy the requirements of
+ * the Decoder interface, and will throw a DecoderException if the
supplied object is not of type byte[] or String.
+ *
+ * @param obj
+ * Object to decode
+ * @return An object (of type byte[]) containing the binary data which
corresponds to the byte[] or String
+ * supplied.
+ * @throws DecoderException
+ * if the parameter supplied is not of type byte[]
+ * @deprecated This unused method will be removed in Tomcat 9
+ */
+ @Override
+ @Deprecated
+ public Object decode(final Object obj) throws DecoderException {
+ if (obj instanceof byte[]) {
+ return decode((byte[]) obj);
+ } else if (obj instanceof String) {
+ return decode((String) obj);
+ } else {
+ throw new DecoderException("Parameter supplied to Base-N decode is
not a byte[] or a String");
+ }
+ }
+
/**
* Encodes a byte[] containing binary data, into a byte[] containing
characters in the alphabet.
*
@@ -527,16 +452,117 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
// package protected for access from I/O streams
abstract void encode(byte[] pArray, int i, int length, Context context);
- // package protected for access from I/O streams
- abstract void decode(byte[] pArray, int i, int length, Context context);
+ /**
+ * Encodes a byte[] containing binary data, into a String containing
characters in the appropriate alphabet.
+ * Uses UTF8 encoding.
+ *
+ * @param pArray a byte array containing binary data
+ * @return String containing only character data in the appropriate
alphabet.
+ * @since 1.5
+ * This is a duplicate of {@link #encodeToString(byte[])}; it was merged
during refactoring.
+ */
+ public String encodeAsString(final byte[] pArray){
+ return StringUtils.newStringUtf8(encode(pArray));
+ }
/**
- * Returns whether or not the <code>octet</code> is in the current
alphabet.
+ * Encodes an Object using the Base-N algorithm. This method is provided
in order to satisfy the requirements of
+ * the Encoder interface, and will throw an EncoderException if the
supplied object is not of type byte[].
+ *
+ * @param obj
+ * Object to encode
+ * @return An object (of type byte[]) containing the Base-N encoded data
which corresponds to the byte[] supplied.
+ * @throws EncoderException
+ * if the parameter supplied is not of type byte[]
+ * @deprecated This unused method will be removed in Tomcat 9
+ */
+ @Override
+ @Deprecated
+ public Object encode(final Object obj) throws EncoderException {
+ if (!(obj instanceof byte[])) {
+ throw new EncoderException("Parameter supplied to Base-N encode is
not a byte[]");
+ }
+ return encode((byte[]) obj);
+ }
+
+ /**
+ * Encodes a byte[] containing binary data, into a String containing
characters in the Base-N alphabet.
+ * Uses UTF8 encoding.
+ *
+ * @param pArray
+ * a byte array containing binary data
+ * @return A String containing only Base-N character data
+ */
+ public String encodeToString(final byte[] pArray) {
+ return StringUtils.newStringUtf8(encode(pArray));
+ }
+
+ /**
+ * Ensure that the buffer has room for {@code size} bytes
+ *
+ * @param size minimum spare space required
+ * @param context the context to be used
+ * @return the buffer
+ */
+ protected byte[] ensureBufferSize(final int size, final Context context){
+ if (context.buffer == null) {
+ context.buffer = new byte[Math.max(size, getDefaultBufferSize())];
+ context.pos = 0;
+ context.readPos = 0;
+
+ // Overflow-conscious:
+ // x + y > z == x + y - z > 0
+ } else if (context.pos + size - context.buffer.length > 0) {
+ return resizeBuffer(context, context.pos + size);
+ }
+ return context.buffer;
+ }
+
+ /**
+ * Get the default buffer size. Can be overridden.
+ *
+ * @return the default buffer size.
+ */
+ protected int getDefaultBufferSize() {
+ return DEFAULT_BUFFER_SIZE;
+ }
+
+ /**
+ * Calculates the amount of space needed to encode the supplied array.
+ *
+ * @param pArray byte[] array which will later be encoded
+ *
+ * @return amount of space needed to encoded the supplied array.
+ * Returns a long since a max-len array will require > Integer.MAX_VALUE
+ */
+ public long getEncodedLength(final byte[] pArray) {
+ // Calculate non-chunked size - rounded up to allow for padding
+ // cast to long is needed to avoid possibility of overflow
+ long len = ((pArray.length + unencodedBlockSize-1) /
unencodedBlockSize) * (long) encodedBlockSize;
+ if (lineLength > 0) { // We're using chunking
+ // Round up to nearest multiple
+ len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength;
+ }
+ return len;
+ }
+
+ /**
+ * Returns true if this object has buffered data for reading.
+ *
+ * @param context the context to be used
+ * @return true if there is data still available for reading.
+ */
+ boolean hasData(final Context context) { // package protected for access
from I/O streams
+ return context.buffer != null;
+ }
+
+ /**
+ * Returns whether or not the {@code octet} is in the current alphabet.
* Does not allow whitespace or pad.
*
* @param value The value to test
*
- * @return <code>true</code> if the value is defined in the current
alphabet, <code>false</code> otherwise.
+ * @return {@code true} if the value is defined in the current alphabet,
{@code false} otherwise.
*/
protected abstract boolean isInAlphabet(byte value);
@@ -545,10 +571,10 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
* The method optionally treats whitespace and pad as valid.
*
* @param arrayOctet byte array to test
- * @param allowWSPad if <code>true</code>, then whitespace and PAD are
also allowed
+ * @param allowWSPad if {@code true}, then whitespace and PAD are also
allowed
*
- * @return <code>true</code> if all bytes are valid characters in the
alphabet or if the byte array is empty;
- * <code>false</code>, otherwise
+ * @return {@code true} if all bytes are valid characters in the alphabet
or if the byte array is empty;
+ * {@code false}, otherwise
*/
public boolean isInAlphabet(final byte[] arrayOctet, final boolean
allowWSPad) {
for (final byte octet : arrayOctet) {
@@ -565,8 +591,8 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
* The method treats whitespace and PAD as valid.
*
* @param basen String to test
- * @return <code>true</code> if all characters in the String are valid
characters in the alphabet or if
- * the String is empty; <code>false</code>, otherwise
+ * @return {@code true} if all characters in the String are valid
characters in the alphabet or if
+ * the String is empty; {@code false}, otherwise
* @see #isInAlphabet(byte[], boolean)
*/
public boolean isInAlphabet(final String basen) {
@@ -574,42 +600,31 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * Tests a given byte array to see if it contains any characters within
the alphabet or PAD.
- *
- * Intended for use in checking line-ending arrays
+ * Extracts buffered data into the provided byte[] array, starting at
position bPos, up to a maximum of bAvail
+ * bytes. Returns how many bytes were actually extracted.
+ * <p>
+ * Package protected for access from I/O streams.
*
- * @param arrayOctet
- * byte array to test
- * @return <code>true</code> if any byte is a valid character in the
alphabet or PAD; <code>false</code> otherwise
+ * @param b
+ * byte[] array to extract the buffered data into.
+ * @param bPos
+ * position in byte[] array to start extraction at.
+ * @param bAvail
+ * amount of bytes we're allowed to extract. We may extract
fewer (if fewer are available).
+ * @param context
+ * the context to be used
+ * @return The number of bytes successfully extracted into the provided
byte[] array.
*/
- protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
- if (arrayOctet == null) {
- return false;
- }
- for (final byte element : arrayOctet) {
- if (pad == element || isInAlphabet(element)) {
- return true;
+ int readResults(final byte[] b, final int bPos, final int bAvail, final
Context context) {
+ if (context.buffer != null) {
+ final int len = Math.min(available(context), bAvail);
+ System.arraycopy(context.buffer, context.readPos, b, bPos, len);
+ context.readPos += len;
+ if (context.readPos >= context.pos) {
+ context.buffer = null; // so hasData() will return false, and
this method can return -1
}
+ return len;
}
- return false;
- }
-
- /**
- * Calculates the amount of space needed to encode the supplied array.
- *
- * @param pArray byte[] array which will later be encoded
- *
- * @return amount of space needed to encoded the supplied array.
- * Returns a long since a max-len array will require > Integer.MAX_VALUE
- */
- public long getEncodedLength(final byte[] pArray) {
- // Calculate non-chunked size - rounded up to allow for padding
- // cast to long is needed to avoid possibility of overflow
- long len = ((pArray.length + unencodedBlockSize-1) /
unencodedBlockSize) * (long) encodedBlockSize;
- if (lineLength > 0) { // We're using chunking
- // Round up to nearest multiple
- len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength;
- }
- return len;
+ return context.eof ? EOF : 0;
}
}
diff --git a/java/org/apache/tomcat/util/codec/binary/StringUtils.java
b/java/org/apache/tomcat/util/codec/binary/StringUtils.java
index f2a92b4..673dd0f 100644
--- a/java/org/apache/tomcat/util/codec/binary/StringUtils.java
+++ b/java/org/apache/tomcat/util/codec/binary/StringUtils.java
@@ -37,7 +37,7 @@ public class StringUtils {
* @param string
* The string to encode (if null, return null).
* @param charset
- * The {@link Charset} to encode the <code>String</code>
+ * The {@link Charset} to encode the {@code String}
* @return the encoded bytes
*/
private static byte[] getBytes(final String string, final Charset charset)
{
@@ -52,8 +52,12 @@ public class StringUtils {
* array.
*
* @param string
- * the String to encode, may be <code>null</code>
- * @return encoded bytes, or <code>null</code> if the input string was
<code>null</code>
+ * the String to encode, may be {@code null}
+ * @return encoded bytes, or {@code null} if the input string was {@code
null}
+ * @throws NullPointerException
+ * Thrown if {@link StandardCharsets#UTF_8} is not
initialized, which should never happen since it is
+ * required by the Java platform specification.
+ * @since As of 1.7, throws {@link NullPointerException} instead of
UnsupportedEncodingException
* @see <a
href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
charsets</a>
*/
public static byte[] getBytesUtf8(final String string) {
@@ -61,40 +65,50 @@ public class StringUtils {
}
/**
- * Constructs a new <code>String</code> by decoding the specified array of
bytes using the given charset.
+ * Constructs a new {@code String} by decoding the specified array of
bytes using the given charset.
*
* @param bytes
* The bytes to be decoded into characters
* @param charset
- * The {@link Charset} to encode the <code>String</code>
- * @return A new <code>String</code> decoded from the specified array of
bytes using the given charset,
- * or <code>null</code> if the input byte array was
<code>null</code>.
+ * The {@link Charset} to encode the {@code String}; not {@code
null}
+ * @return A new {@code String} decoded from the specified array of bytes
using the given charset,
+ * or {@code null} if the input byte array was {@code null}.
+ * @throws NullPointerException
+ * Thrown if charset is {@code null}
*/
private static String newString(final byte[] bytes, final Charset charset)
{
return bytes == null ? null : new String(bytes, charset);
}
/**
- * Constructs a new <code>String</code> by decoding the specified array of
bytes using the US-ASCII charset.
+ * Constructs a new {@code String} by decoding the specified array of
bytes using the US-ASCII charset.
*
* @param bytes
* The bytes to be decoded into characters
- * @return A new <code>String</code> decoded from the specified array of
bytes using the US-ASCII charset,
- * or <code>null</code> if the input byte array was
<code>null</code>.
+ * @return A new {@code String} decoded from the specified array of bytes
using the US-ASCII charset,
+ * or {@code null} if the input byte array was {@code null}.
+ * @throws NullPointerException
+ * Thrown if {@link StandardCharsets#US_ASCII} is not
initialized, which should never happen since it is
+ * required by the Java platform specification.
+ * @since As of 1.7, throws {@link NullPointerException} instead of
UnsupportedEncodingException
*/
public static String newStringUsAscii(final byte[] bytes) {
return newString(bytes, StandardCharsets.US_ASCII);
}
/**
- * Constructs a new <code>String</code> by decoding the specified array of
bytes using the UTF-8 charset.
+ * Constructs a new {@code String} by decoding the specified array of
bytes using the UTF-8 charset.
*
* @param bytes
* The bytes to be decoded into characters
- * @return A new <code>String</code> decoded from the specified array of
bytes using the UTF-8 charset,
- * or <code>null</code> if the input byte array was
<code>null</code>.
+ * @return A new {@code String} decoded from the specified array of bytes
using the UTF-8 charset,
+ * or {@code null} if the input byte array was {@code null}.
+ * @throws NullPointerException
+ * Thrown if {@link StandardCharsets#UTF_8} is not
initialized, which should never happen since it is
+ * required by the Java platform specification.
+ * @since As of 1.7, throws {@link NullPointerException} instead of
UnsupportedEncodingException
*/
public static String newStringUtf8(final byte[] bytes) {
return newString(bytes, StandardCharsets.UTF_8);
}
-}
+}
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index eb08eb5..3fa7bff 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -176,6 +176,10 @@
Update the internal fork of Apache Commons BCEL to 6.5.0. Code clean-up
only. (markt)
</add>
+ <add>
+ Update the internal fork of Apache Commons Codec to 53c93d0
(2020-08-18,
+ 1.15-SNAPSHOT). Code clean-up. (markt)
+ </add>
</changelog>
</subsection>
</section>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]