This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/7.0.x by this push:
new df20351 Update Commons Codec to latest
df20351 is described below
commit df20351955650581c8ddd852a27fbba62cbdaa60
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Aug 25 19:57:05 2020 +0100
Update Commons Codec to latest
---
.../apache/tomcat/util/codec/binary/Base64.java | 1031 ++++++++++----------
.../tomcat/util/codec/binary/BaseNCodec.java | 546 ++++++-----
.../util/codec/binary/LocalStrings.properties | 19 +
.../util/codec/binary/LocalStrings_fr.properties | 19 +
.../util/codec/binary/LocalStrings_ja.properties | 19 +
.../util/codec/binary/LocalStrings_ko.properties | 19 +
.../codec/binary/LocalStrings_zh_CN.properties | 19 +
.../tomcat/util/codec/binary/StringUtils.java | 28 +-
webapps/docs/changelog.xml | 4 +
9 files changed, 924 insertions(+), 780 deletions(-)
diff --git a/java/org/apache/tomcat/util/codec/binary/Base64.java
b/java/org/apache/tomcat/util/codec/binary/Base64.java
index 0543826..581d7ea 100644
--- a/java/org/apache/tomcat/util/codec/binary/Base64.java
+++ b/java/org/apache/tomcat/util/codec/binary/Base64.java
@@ -35,7 +35,7 @@ import java.math.BigInteger;
* <li>Line separator: Default is CRLF ("\r\n")</li>
* </ul>
* <p>
- * The URL-safe parameter is only applied to encode operations. Decoding only
handles standard mode.
+ * The URL-safe parameter is only applied to encode operations. Decoding
seamlessly handles both modes.
* </p>
* <p>
* Since this class operates directly on byte streams, and not character
streams, it is hard-coded to only
@@ -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.
*
@@ -104,12 +93,13 @@ public class Base64 extends BaseNCodec {
* in Table 1 of RFC 2045) into their 6-bit positive integer equivalents.
Characters that are not in the Base64
* alphabet but fall within the bounds of the array are translated to -1.
*
- * Note: The seamless decoding of URL safe values has been disabled
because Tomcat doesn't use it.
+ * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63.
This means decoder seamlessly handles both
+ * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to
know ahead of time what to emit).
*
* Thanks to "commons" project in ws.apache.org for this code.
* https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
*/
- private static final byte[] DECODE_TABLE = {
+ private static final byte[] STANDARD_DECODE_TABLE = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
00-0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
10-1f
@@ -121,6 +111,18 @@ public class Base64 extends BaseNCodec {
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 //
70-7a p-z
};
+ private static final byte[] URL_SAFE_DECODE_TABLE = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, // 00-0f
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, // 10-1f
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
-1, // 20-2f -
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
-1, // 30-3f 0-9
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, // 40-4f A-O
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1,
63, // 50-5f P-Z _
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, // 60-6f a-o
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
// 70-7a p-z
+ };
+
/**
* Base64 uses 6-bit fields.
*/
@@ -136,356 +138,203 @@ 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;
+ public static byte[] decodeBase64(final byte[] base64Data) {
+ return decodeBase64(base64Data, 0, base64Data.length);
+ }
- // Only one decode table currently; keep for consistency with Base32 code
- private final byte[] decodeTable = DECODE_TABLE;
+ public static byte[] decodeBase64(
+ final byte[] base64Data, final int off, final int len) {
+ return new Base64().decode(base64Data, off, len);
+ }
/**
- * Line separator for encoding. Not used when decoding. Only used if
lineLength > 0.
+ * 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 byte[] lineSeparator;
+ 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);
+ }
+
+ // Implementation of integer encoding used for crypto
/**
- * 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 byte64-encoded integer according to crypto standards such as
W3C's XML-Signature.
+ *
+ * @param pArray
+ * a byte array containing base64 character data
+ * @return A BigInteger
+ * @since 1.4
*/
- private final int decodeSize;
+ public static BigInteger decodeInteger(final byte[] pArray) {
+ return new BigInteger(1, decodeBase64(pArray));
+ }
/**
- * 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>
+ * Encodes binary data using the base64 algorithm but does not chunk the
output.
+ *
+ * @param binaryData
+ * binary data to encode
+ * @return byte[] containing Base64 characters in their UTF-8
representation.
*/
- private final int encodeSize;
+ 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 0 (no chunking), and the encoding
table is STANDARD_ENCODE_TABLE.
- * </p>
+ * Encodes binary data using the base64 algorithm, optionally chunking the
output into 76 character blocks.
*
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * @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() {
- this(0);
+ 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
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, optionally chunking the
output into 76 character blocks.
*
+ * @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</code>, URL-safe encoding is used. In most
cases this should be set to
- * <code>false</code>.
+ * 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 input array needs an output array bigger
than {@link Integer#MAX_VALUE}
* @since 1.4
*/
- public Base64(final boolean urlSafe) {
- this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
+ 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 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.
+ * @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>
+ * @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
* @since 1.4
*/
- public Base64(final int lineLength) {
- this(lineLength, CHUNK_SEPARATOR);
+ 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);
}
/**
- * 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 and chunks the encoded
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.
- * @throws IllegalArgumentException
- * Thrown when the provided lineSeparator included some base64
characters.
- * @since 1.4
+ * @param binaryData
+ * binary data to encode
+ * @return Base64 characters chunked in 76 character blocks
*/
- public Base64(final int lineLength, final byte[] lineSeparator) {
- this(lineLength, lineSeparator, false);
+ public static byte[] encodeBase64Chunked(final byte[] binaryData) {
+ return encodeBase64(binaryData, true);
}
/**
- * 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 but does not chunk the
output.
*
- * @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 only handles standard mode.
- * <b>Note: no padding is added when using the URL-safe
alphabet.</b>
- * @throws IllegalArgumentException
- * The provided lineSeparator included some base64 characters.
That's not going to work!
+ * 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).
+ */
+ public static String encodeBase64String(final byte[] binaryData) {
+ return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
+ }
+
+ /**
+ * 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 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);
- // 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("lineSeparator must not
contain base64 characters: [" + 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;
+ public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
+ return encodeBase64(binaryData, false, true);
}
/**
- * Returns our current encode mode. True if we're URL-SAFE, false
otherwise.
- *
- * @return true if we're in URL-SAFE mode, false otherwise.
+ * 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 boolean isUrlSafe() {
- return this.encodeTable == URL_SAFE_ENCODE_TABLE;
+ public static String encodeBase64URLSafeString(final byte[] binaryData) {
+ return StringUtils.newStringUsAscii(encodeBase64(binaryData, false,
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>
- *
- * @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
- */
- @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("Impossible modulus
"+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;
- }
- }
- }
- }
- }
-
- /**
- * <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>
+ * Encodes to a byte64-encoded integer according to crypto standards such
as W3C's XML-Signature.
*
- * @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
+ * @param bigInteger
+ * a BigInteger
+ * @return A byte array containing base64 character data
+ * @throws NullPointerException
+ * if null is passed in
+ * @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;
- } else {
- if (b >= 0 && b < DECODE_TABLE.length) {
- final int result = DECODE_TABLE[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 - 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("Impossible modulus
"+context.modulus);
- }
+ public static byte[] encodeInteger(final BigInteger bigInteger) {
+ if (bigInteger == null) {
+ throw new
NullPointerException(sm.getString("base64.nullEncodeParameter"));
}
+ return encodeBase64(toIntegerBytes(bigInteger), false);
}
/**
@@ -496,7 +345,7 @@ public class Base64 extends BaseNCodec {
* byte array to test
* @return {@code true} if all bytes are valid characters in the Base64
alphabet or if the byte array is empty;
* {@code false}, otherwise
- * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0.
+ * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in
Tomcat 8.
*/
@Deprecated
public static boolean isArrayByteBase64(final byte[] arrayOctet) {
@@ -504,29 +353,15 @@ public class Base64 extends BaseNCodec {
}
/**
- * 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) {
- return octet == PAD_DEFAULT || (octet >= 0 && octet <
DECODE_TABLE.length && DECODE_TABLE[octet] != -1);
- }
-
- /**
- * 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));
+ return octet == PAD_DEFAULT || (octet >= 0 && octet <
STANDARD_DECODE_TABLE.length && STANDARD_DECODE_TABLE[octet] != -1);
}
/**
@@ -535,8 +370,8 @@ public class Base64 extends BaseNCodec {
*
* @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) {
@@ -549,266 +384,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();
+
+ 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 byte[] containing Base64 characters in their UTF-8
representation.
- * @since 1.4
+ * 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
*/
- public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
- return encodeBase64(binaryData, false, true);
+ 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 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
+ * 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 String encodeBase64URLSafeString(final byte[] binaryData) {
- return StringUtils.newStringUsAscii(encodeBase64(binaryData, false,
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 and chunks the encoded
output into 76 character blocks
- *
- * @param binaryData
- * binary data to encode
- * @return Base64 characters chunked in 76 character blocks
+ * Line separator for encoding. Not used when decoding. Only used if
lineLength > 0.
*/
- public static byte[] encodeBase64Chunked(final byte[] binaryData) {
- return encodeBase64(binaryData, true);
- }
+ private final byte[] lineSeparator;
/**
- * 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}
+ * Convenience variable to help us determine when our buffer is going to
run out of room and needs resizing.
+ * {@code decodeSize = 3 + lineSeparator.length;}
*/
- public static byte[] encodeBase64(final byte[] binaryData, final boolean
isChunked) {
- return encodeBase64(binaryData, isChunked, false);
- }
+ private final int decodeSize;
/**
- * 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 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("Input array too big, the
output array would be bigger (" +
- len +
- ") than the specified maximum size of " +
- 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>
+ * 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>
- * <b>Note:</b> this method only handles data encoded in standard mode.
+ * 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 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 only handles data encoded in standard 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("encodeInteger called with null
parameter");
+ @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 0245942..e3e8f46 100644
--- a/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
+++ b/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
@@ -21,6 +21,7 @@ import org.apache.tomcat.util.codec.BinaryDecoder;
import org.apache.tomcat.util.codec.BinaryEncoder;
import org.apache.tomcat.util.codec.DecoderException;
import org.apache.tomcat.util.codec.EncoderException;
+import org.apache.tomcat.util.res.StringManager;
/**
* Abstract superclass for Base-N encoders and decoders.
@@ -31,6 +32,8 @@ import org.apache.tomcat.util.codec.EncoderException;
*/
public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
+ protected static final StringManager sm =
StringManager.getManager(BaseNCodec.class);
+
/**
* Holds thread context so classes can be thread-safe.
*
@@ -47,12 +50,6 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
int ibitWorkArea;
/**
- * Place holder for the bytes we're dealing with for our based logic.
- * Bitwise operations store and extract the encoding or decoding from
this variable.
- */
- long lbitWorkArea;
-
- /**
* Buffer for streaming.
*/
byte[] buffer;
@@ -96,11 +93,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, lbitWorkArea=%s, modulus=%s, pos=%s, " +
- "readPos=%s]", this.getClass().getSimpleName(),
- HexUtils.toHexString(buffer), currentLinePos, eof,
- ibitWorkArea, lbitWorkArea, 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);
}
}
@@ -163,7 +158,112 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
*/
protected static final byte PAD_DEFAULT = '='; // Allow static access to
default
- protected final byte PAD = PAD_DEFAULT; // instance variable just in case
it needs to vary later
+ /**
+ * Chunk separator per RFC 2045 section 2.1.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section
2.1</a>
+ */
+ static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
+
+ /**
+ * Compares two {@code int} values numerically treating the values
+ * as unsigned. Taken from JDK 1.8.
+ *
+ * <p>TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).</p>
+ *
+ * @param x the first {@code int} to compare
+ * @param y the second {@code int} to compare
+ * @return the value {@code 0} if {@code x == y}; a value less
+ * than {@code 0} if {@code x < y} as unsigned values; and
+ * a value greater than {@code 0} if {@code x > y} as
+ * unsigned values
+ */
+ private static int compareUnsigned(int x, int y) {
+ return Integer.valueOf(x +
Integer.MIN_VALUE).compareTo(Integer.valueOf(y + Integer.MIN_VALUE));
+ }
+
+ /**
+ * Create a positive capacity at least as large the minimum required
capacity.
+ * If the minimum capacity is negative then this throws an
OutOfMemoryError as no array
+ * can be allocated.
+ *
+ * @param minCapacity the minimum capacity
+ * @return the capacity
+ * @throws OutOfMemoryError if the {@code minCapacity} is negative
+ */
+ private static int createPositiveCapacity(final int minCapacity) {
+ if (minCapacity < 0) {
+ // overflow
+ throw new OutOfMemoryError("Unable to allocate array size: " +
(minCapacity & 0xffffffffL));
+ }
+ // This is called when we require buffer expansion to a very big array.
+ // Use the conservative maximum buffer size if possible, otherwise the
biggest required.
+ //
+ // Note: In this situation JDK 1.8 java.util.ArrayList returns
Integer.MAX_VALUE.
+ // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not
allocate a full
+ // Integer.MAX_VALUE length array.
+ // The result is that we may have to allocate an array of this size
more than once if
+ // the capacity must be expanded again.
+ return (minCapacity > MAX_BUFFER_SIZE) ?
+ minCapacity :
+ MAX_BUFFER_SIZE;
+ }
+
+ /**
+ * Gets a copy of the chunk separator per RFC 2045 section 2.1.
+ *
+ * @return the chunk separator
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section
2.1</a>
+ * @since 1.15
+ */
+ public static byte[] getChunkSeparator() {
+ return CHUNK_SEPARATOR.clone();
+ }
+
+ /**
+ * Checks if a byte value is whitespace or not.
+ * Whitespace is taken to mean: space, tab, CR, LF
+ * @param byteToCheck
+ * the byte to check
+ * @return true if byte is whitespace, false otherwise
+ */
+ protected static boolean isWhiteSpace(final byte byteToCheck) {
+ switch (byteToCheck) {
+ case ' ' :
+ case '\n' :
+ case '\r' :
+ case '\t' :
+ return true;
+ default :
+ return false;
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ 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;
@@ -179,16 +279,16 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
protected final int lineLength;
/**
- * Size of chunk separator. Not used unless {@link #lineLength} > 0.
+ * 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.
+ * 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</code>
+ * @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,
@@ -197,11 +297,11 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * 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.
+ * 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</code>
+ * @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.
*/
@@ -212,16 +312,7 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;
this.lineLength = useChunking ? (lineLength / encodedBlockSize) *
encodedBlockSize : 0;
this.chunkSeparatorLength = chunkSeparatorLength;
- }
-
- /**
- * 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;
+ this.pad = pad;
}
/**
@@ -235,149 +326,142 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * Get the default buffer size. Can be overridden.
+ * Tests a given byte array to see if it contains any characters within
the alphabet or PAD.
*
- * @return the default buffer size.
+ * 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
*/
- protected int getDefaultBufferSize() {
- return DEFAULT_BUFFER_SIZE;
+ 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;
}
/**
- * 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
+ * Decodes a byte[] containing characters in the Base-N alphabet.
+ *
+ * @param pArray
+ * A byte array containing Base-N character data
+ * @return a byte array containing binary data
*/
- 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);
- }
+ @Override
+ public byte[] decode(final byte[] pArray) {
+ return decode(pArray, 0, pArray.length);
+ }
- final byte[] b = new byte[newCapacity];
- System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
- context.buffer = b;
- return b;
+ public byte[] decode(final byte[] pArray, final int off, final int len) {
+ if (pArray == null || len == 0) {
+ return new byte[0];
+ }
+ final Context context = new Context();
+ decode(pArray, off, len, context);
+ decode(pArray, off, EOF, context); // Notify decoder of EOF.
+ final byte[] result = new byte[context.pos];
+ readResults(result, 0, result.length, context);
+ return result;
}
+ // package protected for access from I/O streams
+ abstract void decode(byte[] pArray, int i, int length, Context context);
+
/**
- * Compares two {@code int} values numerically treating the values
- * as unsigned. Taken from JDK 1.8.
- *
- * <p>TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).</p>
+ * Decodes a String containing characters in the Base-N alphabet.
*
- * @param x the first {@code int} to compare
- * @param y the second {@code int} to compare
- * @return the value {@code 0} if {@code x == y}; a value less
- * than {@code 0} if {@code x < y} as unsigned values; and
- * a value greater than {@code 0} if {@code x > y} as
- * unsigned values
+ * @param pArray
+ * A String containing Base-N character data
+ * @return a byte array containing binary data
*/
- private static int compareUnsigned(int x, int y) {
- return Integer.valueOf(x +
Integer.MIN_VALUE).compareTo(Integer.valueOf(y + Integer.MIN_VALUE));
+ public byte[] decode(final String pArray) {
+ return decode(StringUtils.getBytesUtf8(pArray));
}
/**
- * Create a positive capacity at least as large the minimum required
capacity.
- * If the minimum capacity is negative then this throws an
OutOfMemoryError as no array
- * can be allocated.
+ * 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 minCapacity the minimum capacity
- * @return the capacity
- * @throws OutOfMemoryError if the {@code minCapacity} is negative
+ * @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
*/
- private static int createPositiveCapacity(int minCapacity) {
- if (minCapacity < 0) {
- // overflow
- throw new OutOfMemoryError("Unable to allocate array size: " +
(minCapacity & 0xffffffffL));
+ @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");
}
- // This is called when we require buffer expansion to a very big array.
- // Use the conservative maximum buffer size if possible, otherwise the
biggest required.
- //
- // Note: In this situation JDK 1.8 java.util.ArrayList returns
Integer.MAX_VALUE.
- // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not
allocate a full
- // Integer.MAX_VALUE length array.
- // The result is that we may have to allocate an array of this size
more than once if
- // the capacity must be expanded again.
- return (minCapacity > MAX_BUFFER_SIZE) ?
- minCapacity :
- MAX_BUFFER_SIZE;
}
/**
- * Ensure that the buffer has room for <code>size</code> bytes
+ * Encodes a byte[] containing binary data, into a byte[] containing
characters in the alphabet.
*
- * @param size minimum spare space required
- * @param context the context to be used
- * @return the buffer
+ * @param pArray
+ * a byte array containing binary data
+ * @return A byte array containing only the base N alphabetic character
data
*/
- 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);
+ @Override
+ public byte[] encode(final byte[] pArray) {
+ if (pArray == null || pArray.length == 0) {
+ return pArray;
}
- return context.buffer;
+ return encode(pArray, 0, pArray.length);
}
/**
- * 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.
+ * Encodes a byte[] containing binary data, into a byte[] containing
+ * characters in the alphabet.
*
- * @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.
+ * @param pArray
+ * a byte array containing binary data
+ * @param offset
+ * initial offset of the subarray.
+ * @param length
+ * length of the subarray.
+ * @return A byte array containing only the base N alphabetic character
data
+ * @since 1.11
*/
- 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;
+ public byte[] encode(final byte[] pArray, final int offset, final int
length) {
+ if (pArray == null || pArray.length == 0) {
+ return pArray;
}
- return context.eof ? EOF : 0;
+ final Context context = new Context();
+ encode(pArray, offset, length, context);
+ encode(pArray, offset, EOF, context); // Notify encoder of EOF.
+ final byte[] buf = new byte[context.pos - context.readPos];
+ readResults(buf, 0, buf.length, context);
+ return buf;
}
+ // package protected for access from I/O streams
+ abstract void encode(byte[] pArray, int i, int length, Context context);
+
/**
- * Checks if a byte value is whitespace or not.
- * Whitespace is taken to mean: space, tab, CR, LF
- * @param byteToCheck
- * the byte to check
- * @return true if byte is whitespace, false otherwise
- */
- protected static boolean isWhiteSpace(final byte byteToCheck) {
- switch (byteToCheck) {
- case ' ' :
- case '\n' :
- case '\r' :
- case '\t' :
- return true;
- default :
- return false;
- }
+ * 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));
}
/**
@@ -389,8 +473,10 @@ public abstract class BaseNCodec implements BinaryEncoder,
BinaryDecoder {
* @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[]");
@@ -411,108 +497,71 @@ public abstract class BaseNCodec implements
BinaryEncoder, BinaryDecoder {
}
/**
- * 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));
- }
-
- /**
- * 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.
+ * Ensure that the buffer has room for {@code size} bytes
*
- * @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[]
+ * @param size minimum spare space required
+ * @param context the context to be used
+ * @return the buffer
*/
- @Override
- 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");
+ 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;
}
/**
- * Decodes a String containing characters in the Base-N alphabet.
+ * Get the default buffer size. Can be overridden.
*
- * @param pArray
- * A String containing Base-N character data
- * @return a byte array containing binary data
+ * @return the default buffer size.
*/
- public byte[] decode(final String pArray) {
- return decode(StringUtils.getBytesUtf8(pArray));
+ protected int getDefaultBufferSize() {
+ return DEFAULT_BUFFER_SIZE;
}
/**
- * Decodes a byte[] containing characters in the Base-N alphabet.
+ * Calculates the amount of space needed to encode the supplied array.
*
- * @param pArray
- * A byte array containing Base-N character data
- * @return a byte array containing binary data
+ * @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
*/
- @Override
- public byte[] decode(final byte[] pArray) {
- return decode(pArray, 0, pArray.length);
- }
-
- public byte[] decode(final byte[] pArray, final int off, final int len) {
- if (pArray == null || len == 0) {
- return new byte[0];
+ 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;
}
- final Context context = new Context();
- decode(pArray, off, len, context);
- decode(pArray, off, EOF, context); // Notify decoder of EOF.
- final byte[] result = new byte[context.pos];
- readResults(result, 0, result.length, context);
- return result;
+ return len;
}
/**
- * Encodes a byte[] containing binary data, into a byte[] containing
characters in the alphabet.
+ * Returns true if this object has buffered data for reading.
*
- * @param pArray
- * a byte array containing binary data
- * @return A byte array containing only the base N alphabetic character
data
+ * @param context the context to be used
+ * @return true if there is data still available for reading.
*/
- @Override
- public byte[] encode(final byte[] pArray) {
- if (pArray == null || pArray.length == 0) {
- return pArray;
- }
- final Context context = new Context();
- encode(pArray, 0, pArray.length, context);
- encode(pArray, 0, EOF, context); // Notify encoder of EOF.
- final byte[] buf = new byte[context.pos - context.readPos];
- readResults(buf, 0, buf.length, context);
- return buf;
+ boolean hasData(final Context context) { // package protected for access
from I/O streams
+ return context.buffer != null;
}
- // 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);
-
/**
- * Returns whether or not the <code>octet</code> is in the current
alphabet.
+ * 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);
@@ -521,15 +570,15 @@ 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) {
if (!isInAlphabet(octet) &&
- (!allowWSPad || (octet != PAD) && !isWhiteSpace(octet))) {
+ (!allowWSPad || (octet != pad) && !isWhiteSpace(octet))) {
return false;
}
}
@@ -541,8 +590,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) {
@@ -550,42 +599,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/LocalStrings.properties
b/java/org/apache/tomcat/util/codec/binary/LocalStrings.properties
new file mode 100644
index 0000000..1ae86a3
--- /dev/null
+++ b/java/org/apache/tomcat/util/codec/binary/LocalStrings.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+base64.impossibleModulus=Impossible modulus [{0}]
+base64.inputTooLarge=Input array too large, the output array would be bigger
[{0}] than the specified maximum size of [{1}]
+base64.lineSeparator=Line separator must not contain base64 characters [{0}]
+base64.nullEncodeParameter=Cannot encode integer with null parameter
diff --git
a/java/org/apache/tomcat/util/codec/binary/LocalStrings_fr.properties
b/java/org/apache/tomcat/util/codec/binary/LocalStrings_fr.properties
new file mode 100644
index 0000000..a2d0a67
--- /dev/null
+++ b/java/org/apache/tomcat/util/codec/binary/LocalStrings_fr.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+base64.impossibleModulus=Modulo [{0}] invalide
+base64.inputTooLarge=Le tableau en entrée est trop grand, la taille du tableau
de sortie [{0}] serait plus grande que la taille maximale autorisée [{1}]
+base64.lineSeparator=Le séparateur de ligne ne doit pas contenir des
caractères base64 [{0}]
+base64.nullEncodeParameter=Impossible d'encoder unentier en utilisant un
paramètre null
diff --git
a/java/org/apache/tomcat/util/codec/binary/LocalStrings_ja.properties
b/java/org/apache/tomcat/util/codec/binary/LocalStrings_ja.properties
new file mode 100644
index 0000000..aaf4b51
--- /dev/null
+++ b/java/org/apache/tomcat/util/codec/binary/LocalStrings_ja.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+base64.impossibleModulus=計算できない剰余 [{0}] です。
+base64.inputTooLarge=入力配列が大きすぎます。出力配列は[{1}]の指定された最大サイズよりも大きくなります[{0}]。
+base64.lineSeparator=行区切り記号にはbase64文字を使用できません[{0}]
+base64.nullEncodeParameter=null 値を整数に符号化できませんでした。
diff --git
a/java/org/apache/tomcat/util/codec/binary/LocalStrings_ko.properties
b/java/org/apache/tomcat/util/codec/binary/LocalStrings_ko.properties
new file mode 100644
index 0000000..7bdc2bb
--- /dev/null
+++ b/java/org/apache/tomcat/util/codec/binary/LocalStrings_ko.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+base64.impossibleModulus=불가능한 계수 [{0}]
+base64.inputTooLarge=입력 배열이 너무 큽니다. 출력 배열의 크기 [{0}]이(가), 지정된 최대 크기 [{1}] 보다 큰
값입니다.
+base64.lineSeparator=행 구분문자 [{0}]은(는) Base64 문자들을 포함해서는 안됩니다.
+base64.nullEncodeParameter=정수를 위한 파라미터가 널이라서 인코딩할 수 없습니다.
diff --git
a/java/org/apache/tomcat/util/codec/binary/LocalStrings_zh_CN.properties
b/java/org/apache/tomcat/util/codec/binary/LocalStrings_zh_CN.properties
new file mode 100644
index 0000000..0624dec
--- /dev/null
+++ b/java/org/apache/tomcat/util/codec/binary/LocalStrings_zh_CN.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+base64.impossibleModulus=不可能的模[{0}]
+base64.inputTooLarge=输入数组太大,输出数组将比指定的最大大小[{1}]大[{0}]
+base64.lineSeparator=行分隔符不能包含base64个字符[{0}]
+base64.nullEncodeParameter=不能用空参数编码整数
diff --git a/java/org/apache/tomcat/util/codec/binary/StringUtils.java
b/java/org/apache/tomcat/util/codec/binary/StringUtils.java
index a7b189d..2d49d4e 100644
--- a/java/org/apache/tomcat/util/codec/binary/StringUtils.java
+++ b/java/org/apache/tomcat/util/codec/binary/StringUtils.java
@@ -38,7 +38,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)
{
@@ -53,8 +53,8 @@ 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}
* @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) {
@@ -62,38 +62,40 @@ 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}.
*/
public static String newStringUsAscii(final byte[] bytes) {
return newString(bytes, Charset.forName("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}.
*/
public static String newStringUtf8(final byte[] bytes) {
return newString(bytes, B2CConverter.UTF_8);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d3008c4..35fc374 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -132,6 +132,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]