You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/05/17 19:46:11 UTC
[commons-codec] branch master updated: Sort methods in AB order.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-codec.git
The following commit(s) were added to refs/heads/master by this push:
new e09b236 Sort methods in AB order.
e09b236 is described below
commit e09b236f8850133dd9687d4a0e345e2ffbf79cd1
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun May 17 15:46:07 2020 -0400
Sort methods in AB order.
---
.../org/apache/commons/codec/binary/Base32.java | 46 +-
.../org/apache/commons/codec/binary/Base64.java | 1016 ++++++++++----------
.../apache/commons/codec/binary/BaseNCodec.java | 554 +++++------
.../codec/binary/BaseNCodecInputStream.java | 28 +-
.../codec/binary/BaseNCodecOutputStream.java | 138 +--
.../apache/commons/codec/binary/BinaryCodec.java | 196 ++--
.../java/org/apache/commons/codec/binary/Hex.java | 94 +-
.../apache/commons/codec/binary/StringUtils.java | 32 +-
8 files changed, 1052 insertions(+), 1052 deletions(-)
diff --git a/src/main/java/org/apache/commons/codec/binary/Base32.java b/src/main/java/org/apache/commons/codec/binary/Base32.java
index 8d57861..845206f 100644
--- a/src/main/java/org/apache/commons/codec/binary/Base32.java
+++ b/src/main/java/org/apache/commons/codec/binary/Base32.java
@@ -172,10 +172,10 @@ public class Base32 extends BaseNCodec {
* <p>
* When encoding the line length is 0 (no chunking).
* </p>
- * @param pad byte used as padding byte.
+ * @param useHex if {@code true} then use Base32 Hex alphabet
*/
- public Base32(final byte pad) {
- this(false, pad);
+ public Base32(final boolean useHex) {
+ this(0, null, useHex, PAD_DEFAULT);
}
/**
@@ -184,9 +184,10 @@ public class Base32 extends BaseNCodec {
* When encoding the line length is 0 (no chunking).
* </p>
* @param useHex if {@code true} then use Base32 Hex alphabet
+ * @param padding byte used as padding byte.
*/
- public Base32(final boolean useHex) {
- this(0, null, useHex, PAD_DEFAULT);
+ public Base32(final boolean useHex, final byte padding) {
+ this(0, null, useHex, padding);
}
/**
@@ -194,11 +195,10 @@ public class Base32 extends BaseNCodec {
* <p>
* When encoding the line length is 0 (no chunking).
* </p>
- * @param useHex if {@code true} then use Base32 Hex alphabet
- * @param padding byte used as padding byte.
+ * @param pad byte used as padding byte.
*/
- public Base32(final boolean useHex, final byte padding) {
- this(0, null, useHex, padding);
+ public Base32(final byte pad) {
+ this(false, pad);
}
/**
@@ -588,20 +588,6 @@ public class Base32 extends BaseNCodec {
}
/**
- * Validates whether decoding allows final trailing characters that cannot be
- * created during encoding.
- *
- * @throws IllegalArgumentException if strict decoding is enabled
- */
- private void validateTrailingCharacters() {
- if (isStrictDecoding()) {
- throw new IllegalArgumentException(
- "Strict decoding: Last encoded character(s) (before the paddings if any) are valid base 32 alphabet but not a possible encoding. " +
- "Decoding requries either 2, 4, 5, or 7 trailing 5-bit characters to create bytes.");
- }
- }
-
- /**
* Validates whether decoding the final trailing character is possible in the context
* of the set of possible base 32 values.
*
@@ -621,4 +607,18 @@ public class Base32 extends BaseNCodec {
"Expected the discarded bits from the character to be zero.");
}
}
+
+ /**
+ * Validates whether decoding allows final trailing characters that cannot be
+ * created during encoding.
+ *
+ * @throws IllegalArgumentException if strict decoding is enabled
+ */
+ private void validateTrailingCharacters() {
+ if (isStrictDecoding()) {
+ throw new IllegalArgumentException(
+ "Strict decoding: Last encoded character(s) (before the paddings if any) are valid base 32 alphabet but not a possible encoding. " +
+ "Decoding requries either 2, 4, 5, or 7 trailing 5-bit characters to create bytes.");
+ }
+ }
}
diff --git a/src/main/java/org/apache/commons/codec/binary/Base64.java b/src/main/java/org/apache/commons/codec/binary/Base64.java
index 178633d..72cff5f 100644
--- a/src/main/java/org/apache/commons/codec/binary/Base64.java
+++ b/src/main/java/org/apache/commons/codec/binary/Base64.java
@@ -130,389 +130,194 @@ 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.
- */
- private final byte[] encodeTable;
-
- // Only one decode table currently; keep for consistency with Base32 code
- private final byte[] decodeTable = DECODE_TABLE;
-
- /**
- * Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
- */
- private final byte[] lineSeparator;
-
- /**
- * 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.
+ * Decodes Base64 data into octets.
* <p>
- * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.
+ * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
* </p>
*
- * <p>
- * When decoding all variants are supported.
- * </p>
+ * @param base64Data
+ * Byte array containing Base64 data
+ * @return Array containing decoded data.
*/
- public Base64() {
- this(0);
+ public static byte[] decodeBase64(final byte[] base64Data) {
+ return new Base64().decode(base64Data);
}
/**
- * 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>
- *
+ * Decodes a Base64 String into octets.
* <p>
- * When decoding all variants are supported.
+ * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
* </p>
*
- * @param urlSafe
- * if {@code true}, URL-safe encoding is used. In most cases this should be set to
- * {@code false}.
+ * @param base64String
+ * String containing Base64 data
+ * @return Array containing decoded data.
* @since 1.4
*/
- public Base64(final boolean urlSafe) {
- this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
+ public static byte[] decodeBase64(final String base64String) {
+ return new Base64().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 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>
+ * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
*
- * @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 pArray
+ * a byte array containing base64 character data
+ * @return A BigInteger
* @since 1.4
*/
- public Base64(final int lineLength) {
- this(lineLength, CHUNK_SEPARATOR);
+ 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 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 binaryData
+ * binary data to encode
+ * @return byte[] containing Base64 characters in their UTF-8 representation.
+ */
+ public static byte[] encodeBase64(final byte[] binaryData) {
+ return encodeBase64(binaryData, false);
+ }
+
+ /**
+ * 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
+ * @return Base64-encoded data.
* @throws IllegalArgumentException
- * Thrown when the provided lineSeparator included some base64 characters.
- * @since 1.4
+ * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
*/
- public Base64(final int lineLength, final byte[] lineSeparator) {
- this(lineLength, lineSeparator, false);
+ 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
- * 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>
+ * @return Base64-encoded data.
* @throws IllegalArgumentException
- * Thrown when the {@code lineSeparator} contains 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, final boolean urlSafe) {
- this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);
+ 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>
- * @param decodingPolicy The decoding policy.
+ * 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 {@code lineSeparator} contains Base64 characters.
- * @since 1.15
+ * 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, final CodecPolicy decodingPolicy) {
- super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
- lineLength,
- lineSeparator == null ? 0 : lineSeparator.length,
- PAD_DEFAULT,
- decodingPolicy);
- // 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;
+ 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("Input array too big, the output array would be bigger (" +
+ len +
+ ") than the specified maximum size of " +
+ 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.
- * http://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("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;
- }
- }
- }
- }
+ 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.
- * http://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 < 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);
- }
- }
- }
- }
+ 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 - either ignore entirely, or raise an exception
- validateTrailingCharacter();
- 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);
- }
- }
+ /**
+ * 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) {
+ Objects.requireNonNull(bigInteger, "bigInteger");
+ return encodeBase64(toIntegerBytes(bigInteger), false);
}
/**
@@ -543,20 +348,6 @@ 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} 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 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.
*
@@ -576,227 +367,426 @@ 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.
- *
- * 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).
+ * Returns a byte-array representation of a {@code BigInteger} without sign bit.
*
- * @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
+ * 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[] encodeBase64URLSafe(final byte[] binaryData) {
- return encodeBase64(binaryData, false, true);
- }
+ private final byte[] encodeTable;
+
+ // Only one decode table currently; keep for consistency with Base32 code
+ private final byte[] decodeTable = DECODE_TABLE;
/**
- * 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
+ * Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
*/
- public static String encodeBase64URLSafeString(final byte[] binaryData) {
- return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true));
+ private final byte[] lineSeparator;
+
+ /**
+ * 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>
+ *
+ * <p>
+ * When decoding all variants are supported.
+ * </p>
+ */
+ public Base64() {
+ this(0);
}
/**
- * Encodes binary data using the base64 algorithm and chunks the encoded 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>
*
- * @param binaryData
- * binary data to encode
- * @return Base64 characters chunked in 76 character blocks
+ * <p>
+ * When decoding all variants are supported.
+ * </p>
+ *
+ * @param urlSafe
+ * if {@code true}, URL-safe encoding is used. In most cases this should be set to
+ * {@code false}.
+ * @since 1.4
*/
- public static byte[] encodeBase64Chunked(final byte[] binaryData) {
- return encodeBase64(binaryData, true);
+ public Base64(final boolean urlSafe) {
+ this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
}
/**
- * 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 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>
*
- * @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}
+ * @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[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
- return encodeBase64(binaryData, isChunked, false);
+ public Base64(final int lineLength) {
+ this(lineLength, CHUNK_SEPARATOR);
}
/**
- * 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 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 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.
+ * @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 input array needs an output array bigger than {@link Integer#MAX_VALUE}
+ * Thrown when the provided lineSeparator included some base64 characters.
* @since 1.4
*/
- public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
- return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
+ public Base64(final int lineLength, final byte[] lineSeparator) {
+ this(lineLength, lineSeparator, false);
}
/**
- * 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 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 binaryData
- * Array containing binary data to encode.
- * @param isChunked
- * if {@code true} this encoder will chunk the base64 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 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.
+ * 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 input array needs an output array bigger than maxResultSize
+ * Thrown when the {@code lineSeparator} contains Base64 characters.
* @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 int lineLength, final byte[] lineSeparator, final boolean urlSafe) {
+ this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);
}
/**
- * 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 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 base64String
- * String containing Base64 data
- * @return Array containing decoded data.
- * @since 1.4
+ * @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>
+ * @param decodingPolicy The decoding policy.
+ * @throws IllegalArgumentException
+ * Thrown when the {@code lineSeparator} contains Base64 characters.
+ * @since 1.15
*/
- public static byte[] decodeBase64(final String base64String) {
- return new Base64().decode(base64String);
+ public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) {
+ super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
+ lineLength,
+ lineSeparator == null ? 0 : lineSeparator.length,
+ PAD_DEFAULT,
+ decodingPolicy);
+ // 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;
}
+ // Implementation of the Encoder Interface
+
/**
- * Decodes Base64 data into octets.
* <p>
- * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
+ * 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.
+ * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
* </p>
*
- * @param base64Data
- * Byte array containing Base64 data
- * @return Array containing decoded data.
+ * @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[] decodeBase64(final byte[] base64Data) {
- return new Base64().decode(base64Data);
- }
-
- // Implementation of the Encoder Interface
+ @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 < 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);
+ }
+ }
+ }
+ }
- // Implementation of integer encoding used for crypto
- /**
- * 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
- */
- public static BigInteger decodeInteger(final byte[] pArray) {
- return new BigInteger(1, decodeBase64(pArray));
- }
+ // 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 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) {
- Objects.requireNonNull(bigInteger, "bigInteger");
- return encodeBase64(toIntegerBytes(bigInteger), false);
+ // 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 - either ignore entirely, or raise an exception
+ validateTrailingCharacter();
+ 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);
+ }
+ }
}
/**
- * Returns a byte-array representation of a {@code BigInteger} 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.
+ * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+ * </p>
*
- * @param bigInt
- * {@code BigInteger} 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("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;
+ }
+ }
+ }
}
- 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;
}
/**
@@ -812,17 +802,13 @@ public class Base64 extends BaseNCodec {
}
/**
- * Validates whether decoding allows an entire final trailing character that cannot be
- * used for a complete byte.
+ * Returns our current encode mode. True if we're URL-SAFE, false otherwise.
*
- * @throws IllegalArgumentException if strict decoding is enabled
+ * @return true if we're in URL-SAFE mode, false otherwise.
+ * @since 1.4
*/
- private void validateTrailingCharacter() {
- if (isStrictDecoding()) {
- throw new IllegalArgumentException(
- "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
- "Decoding requries at least two trailing 6-bit characters to create bytes.");
- }
+ public boolean isUrlSafe() {
+ return this.encodeTable == URL_SAFE_ENCODE_TABLE;
}
/**
@@ -845,4 +831,18 @@ public class Base64 extends BaseNCodec {
}
}
+ /**
+ * Validates whether decoding allows an entire final trailing character that cannot be
+ * used for a complete byte.
+ *
+ * @throws IllegalArgumentException if strict decoding is enabled
+ */
+ private void validateTrailingCharacter() {
+ if (isStrictDecoding()) {
+ throw new IllegalArgumentException(
+ "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
+ "Decoding requries at least two trailing 6-bit characters to create bytes.");
+ }
+ }
+
}
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
index b45437d..1fe339c 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
@@ -193,6 +193,104 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
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(final int x, final int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, 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;
+ }
+
+ /**
* @deprecated Use {@link #pad}. Will be removed in 2.0.
*/
@Deprecated
@@ -288,47 +386,6 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
}
/**
- * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing
- * bits are not part of a valid encoding.
- *
- * <p>
- * The default is false for lenient encoding. Decoding will compose trailing bits into 8-bit bytes and discard the
- * remainder.
- * </p>
- *
- * @return true if using strict decoding
- * @since 1.15
- */
- public boolean isStrictDecoding() {
- return decodingPolicy == CodecPolicy.STRICT;
- }
-
- /**
- * Returns the decoding behavior policy. Decoding will raise an {@link IllegalArgumentException} if trailing bits
- * are not part of a valid encoding.
- *
- * <p>
- * The default is lenient. Decoding will compose trailing bits into 8-bit bytes and discard the remainder.
- * </p>
- *
- * @return true if using strict decoding
- * @since 1.15
- */
- public CodecPolicy getCodecPolicy() {
- return decodingPolicy;
- }
-
- /**
- * 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
@@ -339,204 +396,48 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
}
/**
- * Get the default buffer size. Can be overridden.
- *
- * @return the default buffer size.
- */
- 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;
- }
-
- /**
- * 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(final int x, final int y) {
- return Integer.compare(x + Integer.MIN_VALUE, 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;
- }
-
- /**
- * 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[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;
- }
-
- /**
- * Gets a copy of the chunk separator per RFC 2045 section 2.1.
+ * Tests a given byte array to see if it contains any characters within the alphabet or PAD.
*
- * @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;
- }
- }
-
- /**
- * 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[].
+ * Intended for use in checking line-ending arrays
*
- * @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[]
+ * @param arrayOctet
+ * byte array to test
+ * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise
*/
- @Override
- 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[]");
+ protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
+ if (arrayOctet == null) {
+ return false;
}
- return encode((byte[]) obj);
+ for (final byte element : arrayOctet) {
+ if (pad == element || isInAlphabet(element)) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.
- * Uses UTF8 encoding.
+ * Decodes a byte[] containing characters in the Base-N alphabet.
*
* @param pArray
- * a byte array containing binary data
- * @return A String containing only Base-N character data
+ * A byte array containing Base-N character data
+ * @return a byte array containing binary data
*/
- public String encodeToString(final byte[] pArray) {
- return StringUtils.newStringUtf8(encode(pArray));
+ @Override
+ public byte[] decode(final byte[] pArray) {
+ if (pArray == null || pArray.length == 0) {
+ return pArray;
+ }
+ final Context context = new Context();
+ decode(pArray, 0, pArray.length, context);
+ decode(pArray, 0, EOF, context); // Notify decoder of EOF.
+ final byte[] result = new byte[context.pos];
+ readResults(result, 0, result.length, context);
+ return result;
}
- /**
- * 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));
- }
+ // package protected for access from I/O streams
+ abstract void decode(byte[] pArray, int i, int length, Context context);
/**
* Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
@@ -572,26 +473,6 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
}
/**
- * 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
- */
- @Override
- public byte[] decode(final byte[] pArray) {
- if (pArray == null || pArray.length == 0) {
- return pArray;
- }
- final Context context = new Context();
- decode(pArray, 0, pArray.length, context);
- decode(pArray, 0, EOF, context); // Notify decoder of EOF.
- final byte[] result = new byte[context.pos];
- readResults(result, 0, result.length, context);
- return result;
- }
-
- /**
* Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet.
*
* @param pArray
@@ -634,8 +515,122 @@ 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 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[]
+ */
+ @Override
+ 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 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));
+ }
+
+ /**
+ * 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[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;
+ }
+
+ /**
+ * Returns the decoding behavior policy. Decoding will raise an {@link IllegalArgumentException} if trailing bits
+ * are not part of a valid encoding.
+ *
+ * <p>
+ * The default is lenient. Decoding will compose trailing bits into 8-bit bytes and discard the remainder.
+ * </p>
+ *
+ * @return true if using strict decoding
+ * @since 1.15
+ */
+ public CodecPolicy getCodecPolicy() {
+ return decodingPolicy;
+ }
+
+ /**
+ * 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.
@@ -681,42 +676,47 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
}
/**
- * Tests a given byte array to see if it contains any characters within the alphabet or PAD.
+ * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing
+ * bits are not part of a valid encoding.
*
- * Intended for use in checking line-ending arrays
+ * <p>
+ * The default is false for lenient encoding. Decoding will compose trailing bits into 8-bit bytes and discard the
+ * remainder.
+ * </p>
*
- * @param arrayOctet
- * byte array to test
- * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise
+ * @return true if using strict decoding
+ * @since 1.15
*/
- 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;
+ public boolean isStrictDecoding() {
+ return decodingPolicy == CodecPolicy.STRICT;
}
/**
- * Calculates the amount of space needed to encode the supplied array.
- *
- * @param pArray byte[] array which will later be encoded
+ * 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.
*
- * @return amount of space needed to encoded the supplied array.
- * Returns a long since a max-len array will require > Integer.MAX_VALUE
+ * @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.
*/
- 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;
+ 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 len;
+ return context.eof ? EOF : 0;
}
}
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
index 90792f1..ff99e5c 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
@@ -48,20 +48,6 @@ public class BaseNCodecInputStream extends FilterInputStream {
}
/**
- * Returns true if decoding behavior is strict. Decoding will raise an
- * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
- *
- * <p>The default is false for lenient encoding. Decoding will compose trailing bits
- * into 8-bit bytes and discard the remainder.
- *
- * @return true if using strict decoding
- * @since 1.15
- */
- public boolean isStrictDecoding() {
- return baseNCodec.isStrictDecoding();
- }
-
- /**
* {@inheritDoc}
*
* @return {@code 0} if the {@link InputStream} has reached {@code EOF},
@@ -79,6 +65,20 @@ public class BaseNCodecInputStream extends FilterInputStream {
}
/**
+ * Returns true if decoding behavior is strict. Decoding will raise an
+ * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
+ *
+ * <p>The default is false for lenient encoding. Decoding will compose trailing bits
+ * into 8-bit bytes and discard the remainder.
+ *
+ * @return true if using strict decoding
+ * @since 1.15
+ */
+ public boolean isStrictDecoding() {
+ return baseNCodec.isStrictDecoding();
+ }
+
+ /**
* Marks the current position in this input stream.
* <p>The {@link #mark} method of {@link BaseNCodecInputStream} does nothing.</p>
*
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
index bc27e07..074f96d 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
@@ -61,66 +61,49 @@ public class BaseNCodecOutputStream extends FilterOutputStream {
}
/**
- * Returns true if decoding behavior is strict. Decoding will raise an
- * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
- *
- * <p>The default is false for lenient encoding. Decoding will compose trailing bits
- * into 8-bit bytes and discard the remainder.
+ * Closes this output stream and releases any system resources associated with the stream.
+ * <p>
+ * To write the EOF marker without closing the stream, call {@link #eof()} or use an
+ * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
+ * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
+ * >CloseShieldOutputStream</a>.
+ * </p>
*
- * @return true if using strict decoding
- * @since 1.15
+ * @throws IOException
+ * if an I/O error occurs.
*/
- public boolean isStrictDecoding() {
- return baseNCodec.isStrictDecoding();
+ @Override
+ public void close() throws IOException {
+ eof();
+ flush();
+ out.close();
}
/**
- * Writes the specified {@code byte} to this output stream.
+ * Writes EOF.
*
- * @param i
- * source byte
* @throws IOException
* if an I/O error occurs.
+ * @since 1.11
*/
- @Override
- public void write(final int i) throws IOException {
- singleByte[0] = (byte) i;
- write(singleByte, 0, 1);
+ public void eof() throws IOException {
+ // Notify encoder of EOF (-1).
+ if (doEncode) {
+ baseNCodec.encode(singleByte, 0, EOF, context);
+ } else {
+ baseNCodec.decode(singleByte, 0, EOF, context);
+ }
}
/**
- * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this
- * output stream.
- *
- * @param array
- * source byte array
- * @param offset
- * where to start reading the bytes
- * @param len
- * maximum number of bytes to write
+ * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
*
* @throws IOException
* if an I/O error occurs.
- * @throws NullPointerException
- * if the byte array parameter is null
- * @throws IndexOutOfBoundsException
- * if offset, len or buffer size are invalid
*/
@Override
- public void write(final byte array[], final int offset, final int len) throws IOException {
- Objects.requireNonNull(array, "array");
- if (offset < 0 || len < 0) {
- throw new IndexOutOfBoundsException();
- } else if (offset > array.length || offset + len > array.length) {
- throw new IndexOutOfBoundsException();
- } else if (len > 0) {
- if (doEncode) {
- baseNCodec.encode(array, offset, len, context);
- } else {
- baseNCodec.decode(array, offset, len, context);
- }
- flush(false);
- }
+ public void flush() throws IOException {
+ flush(true);
}
/**
@@ -147,49 +130,66 @@ public class BaseNCodecOutputStream extends FilterOutputStream {
}
/**
- * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
+ * Returns true if decoding behavior is strict. Decoding will raise an
+ * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
*
- * @throws IOException
- * if an I/O error occurs.
+ * <p>The default is false for lenient encoding. Decoding will compose trailing bits
+ * into 8-bit bytes and discard the remainder.
+ *
+ * @return true if using strict decoding
+ * @since 1.15
*/
- @Override
- public void flush() throws IOException {
- flush(true);
+ public boolean isStrictDecoding() {
+ return baseNCodec.isStrictDecoding();
}
/**
- * Closes this output stream and releases any system resources associated with the stream.
- * <p>
- * To write the EOF marker without closing the stream, call {@link #eof()} or use an
- * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
- * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
- * >CloseShieldOutputStream</a>.
- * </p>
+ * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this
+ * output stream.
+ *
+ * @param array
+ * source byte array
+ * @param offset
+ * where to start reading the bytes
+ * @param len
+ * maximum number of bytes to write
*
* @throws IOException
* if an I/O error occurs.
+ * @throws NullPointerException
+ * if the byte array parameter is null
+ * @throws IndexOutOfBoundsException
+ * if offset, len or buffer size are invalid
*/
@Override
- public void close() throws IOException {
- eof();
- flush();
- out.close();
+ public void write(final byte array[], final int offset, final int len) throws IOException {
+ Objects.requireNonNull(array, "array");
+ if (offset < 0 || len < 0) {
+ throw new IndexOutOfBoundsException();
+ } else if (offset > array.length || offset + len > array.length) {
+ throw new IndexOutOfBoundsException();
+ } else if (len > 0) {
+ if (doEncode) {
+ baseNCodec.encode(array, offset, len, context);
+ } else {
+ baseNCodec.decode(array, offset, len, context);
+ }
+ flush(false);
+ }
}
/**
- * Writes EOF.
+ * Writes the specified {@code byte} to this output stream.
*
+ * @param i
+ * source byte
* @throws IOException
* if an I/O error occurs.
- * @since 1.11
*/
- public void eof() throws IOException {
- // Notify encoder of EOF (-1).
- if (doEncode) {
- baseNCodec.encode(singleByte, 0, EOF, context);
- } else {
- baseNCodec.decode(singleByte, 0, EOF, context);
- }
+ @Override
+ public void write(final int i) throws IOException {
+ singleByte[0] = (byte) i;
+ write(singleByte, 0, 1);
}
}
diff --git a/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java b/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
index 0c2389a..590dd3e 100644
--- a/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
+++ b/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
@@ -70,105 +70,14 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7};
/**
- * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
- *
- * @param raw
- * the raw binary data to convert
- * @return 0 and 1 ASCII character bytes one for each bit of the argument
- * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
- */
- @Override
- public byte[] encode(final byte[] raw) {
- return toAsciiBytes(raw);
- }
-
- /**
- * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
- *
- * @param raw
- * the raw binary data to convert
- * @return 0 and 1 ASCII character chars one for each bit of the argument
- * @throws EncoderException
- * if the argument is not a byte[]
- * @see org.apache.commons.codec.Encoder#encode(Object)
- */
- @Override
- public Object encode(final Object raw) throws EncoderException {
- if (!(raw instanceof byte[])) {
- throw new EncoderException("argument not a byte array");
- }
- return toAsciiChars((byte[]) raw);
- }
-
- /**
- * Decodes a byte array where each byte represents an ASCII '0' or '1'.
- *
- * @param ascii
- * each byte represents an ASCII '0' or '1'
- * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
- * @throws DecoderException
- * if argument is not a byte[], char[] or String
- * @see org.apache.commons.codec.Decoder#decode(Object)
- */
- @Override
- public Object decode(final Object ascii) throws DecoderException {
- if (ascii == null) {
- return EMPTY_BYTE_ARRAY;
- }
- if (ascii instanceof byte[]) {
- return fromAscii((byte[]) ascii);
- }
- if (ascii instanceof char[]) {
- return fromAscii((char[]) ascii);
- }
- if (ascii instanceof String) {
- return fromAscii(((String) ascii).toCharArray());
- }
- throw new DecoderException("argument not a byte array");
- }
-
- /**
* Decodes a byte array where each byte represents an ASCII '0' or '1'.
*
* @param ascii
* each byte represents an ASCII '0' or '1'
* @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
- * @see org.apache.commons.codec.Decoder#decode(Object)
*/
- @Override
- public byte[] decode(final byte[] ascii) {
- return fromAscii(ascii);
- }
-
- /**
- * Decodes a String where each char of the String represents an ASCII '0' or '1'.
- *
- * @param ascii
- * String of '0' and '1' characters
- * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
- * @see org.apache.commons.codec.Decoder#decode(Object)
- */
- public byte[] toByteArray(final String ascii) {
- if (ascii == null) {
- return EMPTY_BYTE_ARRAY;
- }
- return fromAscii(ascii.toCharArray());
- }
-
- // ------------------------------------------------------------------------
- //
- // static codec operations
- //
- // ------------------------------------------------------------------------
- /**
- * Decodes a char array where each char represents an ASCII '0' or '1'.
- *
- * @param ascii
- * each char represents an ASCII '0' or '1'
- * @return the raw encoded binary where each bit corresponds to a char in the char array argument
- */
- public static byte[] fromAscii(final char[] ascii) {
- if (ascii == null || ascii.length == 0) {
+ public static byte[] fromAscii(final byte[] ascii) {
+ if (isEmpty(ascii)) {
return EMPTY_BYTE_ARRAY;
}
// get length/8 times bytes with 3 bit shifts to the right of the length
@@ -187,15 +96,20 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
return l_raw;
}
+ // ------------------------------------------------------------------------
+ //
+ // static codec operations
+ //
+ // ------------------------------------------------------------------------
/**
- * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+ * Decodes a char array where each char represents an ASCII '0' or '1'.
*
* @param ascii
- * each byte represents an ASCII '0' or '1'
- * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+ * each char represents an ASCII '0' or '1'
+ * @return the raw encoded binary where each bit corresponds to a char in the char array argument
*/
- public static byte[] fromAscii(final byte[] ascii) {
- if (isEmpty(ascii)) {
+ public static byte[] fromAscii(final char[] ascii) {
+ if (ascii == null || ascii.length == 0) {
return EMPTY_BYTE_ARRAY;
}
// get length/8 times bytes with 3 bit shifts to the right of the length
@@ -297,4 +211,90 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
public static String toAsciiString(final byte[] raw) {
return new String(toAsciiChars(raw));
}
+
+ /**
+ * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+ *
+ * @param ascii
+ * each byte represents an ASCII '0' or '1'
+ * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+ * @see org.apache.commons.codec.Decoder#decode(Object)
+ */
+ @Override
+ public byte[] decode(final byte[] ascii) {
+ return fromAscii(ascii);
+ }
+
+ /**
+ * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+ *
+ * @param ascii
+ * each byte represents an ASCII '0' or '1'
+ * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+ * @throws DecoderException
+ * if argument is not a byte[], char[] or String
+ * @see org.apache.commons.codec.Decoder#decode(Object)
+ */
+ @Override
+ public Object decode(final Object ascii) throws DecoderException {
+ if (ascii == null) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ if (ascii instanceof byte[]) {
+ return fromAscii((byte[]) ascii);
+ }
+ if (ascii instanceof char[]) {
+ return fromAscii((char[]) ascii);
+ }
+ if (ascii instanceof String) {
+ return fromAscii(((String) ascii).toCharArray());
+ }
+ throw new DecoderException("argument not a byte array");
+ }
+
+ /**
+ * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
+ *
+ * @param raw
+ * the raw binary data to convert
+ * @return 0 and 1 ASCII character bytes one for each bit of the argument
+ * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+ */
+ @Override
+ public byte[] encode(final byte[] raw) {
+ return toAsciiBytes(raw);
+ }
+
+ /**
+ * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
+ *
+ * @param raw
+ * the raw binary data to convert
+ * @return 0 and 1 ASCII character chars one for each bit of the argument
+ * @throws EncoderException
+ * if the argument is not a byte[]
+ * @see org.apache.commons.codec.Encoder#encode(Object)
+ */
+ @Override
+ public Object encode(final Object raw) throws EncoderException {
+ if (!(raw instanceof byte[])) {
+ throw new EncoderException("argument not a byte array");
+ }
+ return toAsciiChars((byte[]) raw);
+ }
+
+ /**
+ * Decodes a String where each char of the String represents an ASCII '0' or '1'.
+ *
+ * @param ascii
+ * String of '0' and '1' characters
+ * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+ * @see org.apache.commons.codec.Decoder#decode(Object)
+ */
+ public byte[] toByteArray(final String ascii) {
+ if (ascii == null) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ return fromAscii(ascii.toCharArray());
+ }
}
diff --git a/src/main/java/org/apache/commons/codec/binary/Hex.java b/src/main/java/org/apache/commons/codec/binary/Hex.java
index 736fa1d..57c05bd 100644
--- a/src/main/java/org/apache/commons/codec/binary/Hex.java
+++ b/src/main/java/org/apache/commons/codec/binary/Hex.java
@@ -64,20 +64,6 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
'E', 'F' };
/**
- * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
- * will be half the length of the passed String, as it takes two characters to represent any given byte. An
- * exception is thrown if the passed String has an odd number of elements.
- *
- * @param data A String containing hexadecimal digits
- * @return A byte array containing binary data decoded from the supplied char array.
- * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
- * @since 1.11
- */
- public static byte[] decodeHex(final String data) throws DecoderException {
- return decodeHex(data.toCharArray());
- }
-
- /**
* Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
* returned array will be half the length of the passed array, as it takes two characters to represent any given
* byte. An exception is thrown if the passed char array has an odd number of elements.
@@ -109,30 +95,28 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
}
/**
- * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
- * The returned array will be double the length of the passed array, as it takes two characters to represent any
- * given byte.
+ * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
+ * will be half the length of the passed String, as it takes two characters to represent any given byte. An
+ * exception is thrown if the passed String has an odd number of elements.
*
- * @param data a byte[] to convert to Hex characters
- * @return A char[] containing lower-case hexadecimal characters
+ * @param data A String containing hexadecimal digits
+ * @return A byte array containing binary data decoded from the supplied char array.
+ * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
+ * @since 1.11
*/
- public static char[] encodeHex(final byte[] data) {
- return encodeHex(data, true);
+ public static byte[] decodeHex(final String data) throws DecoderException {
+ return decodeHex(data.toCharArray());
}
/**
- * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
- * returned array will be double the length of the passed array, as it takes two characters to represent any given
- * byte.
- *
- * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
- * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+ * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
+ * The returned array will be double the length of the passed array, as it takes two characters to represent any
+ * given byte.
*
- * @param data a byte buffer to convert to Hex characters
+ * @param data a byte[] to convert to Hex characters
* @return A char[] containing lower-case hexadecimal characters
- * @since 1.11
*/
- public static char[] encodeHex(final ByteBuffer data) {
+ public static char[] encodeHex(final byte[] data) {
return encodeHex(data, true);
}
@@ -151,23 +135,6 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
}
/**
- * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
- * returned array will be double the length of the passed array, as it takes two characters to represent any given
- * byte.
- *
- * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
- * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
- *
- * @param data a byte buffer to convert to Hex characters
- * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
- * @return A char[] containing hexadecimal characters in the selected case
- * @since 1.11
- */
- public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
- return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
- }
-
- /**
* Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
* The returned array will be double the length of the passed array, as it takes two characters to represent any
* given byte.
@@ -197,6 +164,39 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
* <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
* the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
*
+ * @param data a byte buffer to convert to Hex characters
+ * @return A char[] containing lower-case hexadecimal characters
+ * @since 1.11
+ */
+ public static char[] encodeHex(final ByteBuffer data) {
+ return encodeHex(data, true);
+ }
+
+ /**
+ * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
+ * returned array will be double the length of the passed array, as it takes two characters to represent any given
+ * byte.
+ *
+ * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
+ * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+ *
+ * @param data a byte buffer to convert to Hex characters
+ * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
+ * @return A char[] containing hexadecimal characters in the selected case
+ * @since 1.11
+ */
+ public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
+ * returned array will be double the length of the passed array, as it takes two characters to represent any given
+ * byte.
+ *
+ * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
+ * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+ *
* @param byteBuffer a byte buffer to convert to Hex characters
* @param toDigits the output alphabet (must be at least 16 characters)
* @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
diff --git a/src/main/java/org/apache/commons/codec/binary/StringUtils.java b/src/main/java/org/apache/commons/codec/binary/StringUtils.java
index 260c77a..176c3d6 100644
--- a/src/main/java/org/apache/commons/codec/binary/StringUtils.java
+++ b/src/main/java/org/apache/commons/codec/binary/StringUtils.java
@@ -89,22 +89,6 @@ public class StringUtils {
* The {@link Charset} to encode the {@code String}
* @return the encoded bytes
*/
- private static byte[] getBytes(final String string, final Charset charset) {
- if (string == null) {
- return null;
- }
- return string.getBytes(charset);
- }
-
- /**
- * Calls {@link String#getBytes(Charset)}
- *
- * @param string
- * The string to encode (if null, return null).
- * @param charset
- * The {@link Charset} to encode the {@code String}
- * @return the encoded bytes
- */
private static ByteBuffer getByteBuffer(final String string, final Charset charset) {
if (string == null) {
return null;
@@ -131,6 +115,22 @@ public class StringUtils {
}
/**
+ * Calls {@link String#getBytes(Charset)}
+ *
+ * @param string
+ * The string to encode (if null, return null).
+ * @param charset
+ * The {@link Charset} to encode the {@code String}
+ * @return the encoded bytes
+ */
+ private static byte[] getBytes(final String string, final Charset charset) {
+ if (string == null) {
+ return null;
+ }
+ return string.getBytes(charset);
+ }
+
+ /**
* Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new
* byte array.
*