Skip to content

Commit 20a88d9

Browse files
committed
- Applied patch for [CODEC-81] production pretty much unchanged.
- Applied patch for [CODEC-81] tests and split one new unit test method into two tests. One for URL-safe and another for normal processing. - Renamed StringBytesUtils to StringUtils - Added missing @return tags to StringUtils git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@798333 13f79535-47bb-0310-9956-ffa450edef68
1 parent 8423569 commit 20a88d9

12 files changed

Lines changed: 345 additions & 268 deletions

File tree

src/java/org/apache/commons/codec/binary/Base64.java

Lines changed: 79 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ public Base64(boolean urlSafe) {
254254
* </p>
255255
*
256256
* @param lineLength
257-
* Each line of encoded data will be at most of the given length (rounded up to nearest multiple of 4).
257+
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
258258
* If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding.
259259
* @since 1.4
260260
*/
@@ -276,7 +276,7 @@ public Base64(int lineLength) {
276276
* </p>
277277
*
278278
* @param lineLength
279-
* Each line of encoded data will be at most of the given length (rounded up to nearest multiple of 4).
279+
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
280280
* If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding.
281281
* @param lineSeparator
282282
* Each line of encoded data will end with this sequence of bytes.
@@ -302,7 +302,7 @@ public Base64(int lineLength, byte[] lineSeparator) {
302302
* </p>
303303
*
304304
* @param lineLength
305-
* Each line of encoded data will be at most of the given length (rounded up to nearest multiple of 4).
305+
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4).
306306
* If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding.
307307
* @param lineSeparator
308308
* Each line of encoded data will end with this sequence of bytes.
@@ -314,7 +314,7 @@ public Base64(int lineLength, byte[] lineSeparator) {
314314
* @since 1.4
315315
*/
316316
public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) {
317-
this.lineLength = lineLength;
317+
this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0;
318318
this.lineSeparator = new byte[lineSeparator.length];
319319
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
320320
if (lineLength > 0) {
@@ -324,7 +324,7 @@ public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) {
324324
}
325325
this.decodeSize = this.encodeSize - 1;
326326
if (containsBase64Byte(lineSeparator)) {
327-
String sep = StringBytesUtils.newStringUtf8(lineSeparator);
327+
String sep = StringUtils.newStringUtf8(lineSeparator);
328328
throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
329329
}
330330
this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
@@ -669,10 +669,11 @@ public static byte[] encodeBase64Chunked(byte[] binaryData) {
669669
* if the parameter supplied is not of type byte[]
670670
*/
671671
public Object decode(Object pObject) throws DecoderException {
672-
if (!(pObject instanceof byte[])) {
672+
if (pObject instanceof byte[]) {
673+
return decode((byte[]) pObject);
674+
} else {
673675
throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
674676
}
675-
return decode((byte[]) pObject);
676677
}
677678

678679
/**
@@ -683,7 +684,24 @@ public Object decode(Object pObject) throws DecoderException {
683684
* @return a byte array containing binary data
684685
*/
685686
public byte[] decode(byte[] pArray) {
686-
return decodeBase64(pArray);
687+
if (pArray == null || pArray.length == 0) {
688+
return pArray;
689+
}
690+
long len = (pArray.length * 3) / 4;
691+
byte[] buf = new byte[(int) len];
692+
setInitialBuffer(buf, 0, buf.length);
693+
decode(pArray, 0, pArray.length);
694+
decode(pArray, 0, -1); // Notify decoder of EOF.
695+
696+
// Would be nice to just return buf (like we sometimes do in the encode
697+
// logic), but we have no idea what the line-length was (could even be
698+
// variable). So we cannot determine ahead of time exactly how big an
699+
// array is necessary. Hence the need to construct a 2nd byte array to
700+
// hold the final result:
701+
702+
byte[] result = new byte[pos];
703+
readResults(result, 0, result.length);
704+
return result;
687705
}
688706

689707
/**
@@ -739,41 +757,17 @@ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean
739757
if (binaryData == null || binaryData.length == 0) {
740758
return binaryData;
741759
}
742-
Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
743-
long len = (binaryData.length * 4) / 3;
744-
long mod = len % 4;
745-
if (mod != 0) {
746-
len += 4 - mod;
747-
}
748-
if (isChunked) {
749-
boolean lenChunksPerfectly = len % CHUNK_SIZE == 0;
750-
len += (len / CHUNK_SIZE) * CHUNK_SEPARATOR.length;
751-
if (!lenChunksPerfectly) {
752-
len += CHUNK_SEPARATOR.length;
753-
}
754-
}
760+
761+
long len = getEncodeLength(binaryData, CHUNK_SIZE, CHUNK_SEPARATOR);
755762
if (len > maxResultSize) {
756763
throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
757764
len +
758765
") than the specified maxium size of " +
759766
maxResultSize);
760767
}
761-
byte[] buf = new byte[(int) len];
762-
b64.setInitialBuffer(buf, 0, buf.length);
763-
b64.encode(binaryData, 0, binaryData.length);
764-
b64.encode(binaryData, 0, -1); // Notify encoder of EOF.
765-
// Encoder might have resized, even though it was unnecessary.
766-
if (b64.buffer != buf) {
767-
b64.readResults(buf, 0, buf.length);
768-
}
769-
// In URL-SAFE mode we skip the padding characters, so sometimes our
770-
// final length is a bit smaller.
771-
if (urlSafe && b64.pos < buf.length) {
772-
byte[] smallerBuf = new byte[b64.pos];
773-
System.arraycopy(buf, 0, smallerBuf, 0, b64.pos);
774-
buf = smallerBuf;
775-
}
776-
return buf;
768+
769+
Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
770+
return b64.encode(binaryData);
777771
}
778772

779773
/**
@@ -784,20 +778,8 @@ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean
784778
* @return Array containing decoded data.
785779
*/
786780
public static byte[] decodeBase64(byte[] base64Data) {
787-
if (base64Data == null || base64Data.length == 0) {
788-
return base64Data;
789-
}
790781
Base64 b64 = new Base64();
791-
long len = (base64Data.length * 3) / 4;
792-
byte[] buf = new byte[(int) len];
793-
b64.setInitialBuffer(buf, 0, buf.length);
794-
b64.decode(base64Data, 0, base64Data.length);
795-
b64.decode(base64Data, 0, -1); // Notify decoder of EOF.
796-
// We have no idea what the line-length was, so we
797-
// cannot know how much of our array wasn't used.
798-
byte[] result = new byte[b64.pos];
799-
b64.readResults(result, 0, result.length);
800-
return result;
782+
return b64.decode(base64Data);
801783
}
802784

803785
/**
@@ -873,7 +855,53 @@ public Object encode(Object pObject) throws EncoderException {
873855
* @return A byte array containing only Base64 character data
874856
*/
875857
public byte[] encode(byte[] pArray) {
876-
return encodeBase64(pArray, false, isUrlSafe());
858+
long len = getEncodeLength(pArray, lineLength, lineSeparator);
859+
byte[] buf = new byte[(int) len];
860+
setInitialBuffer(buf, 0, buf.length);
861+
encode(pArray, 0, pArray.length);
862+
encode(pArray, 0, -1); // Notify encoder of EOF.
863+
// Encoder might have resized, even though it was unnecessary.
864+
if (buffer != buf) {
865+
readResults(buf, 0, buf.length);
866+
}
867+
// In URL-SAFE mode we skip the padding characters, so sometimes our
868+
// final length is a bit smaller.
869+
if (isUrlSafe() && pos < buf.length) {
870+
byte[] smallerBuf = new byte[pos];
871+
System.arraycopy(buf, 0, smallerBuf, 0, pos);
872+
buf = smallerBuf;
873+
}
874+
return buf;
875+
}
876+
877+
/**
878+
* Pre-calculates the amount of space needed to base64-encode the supplied array.
879+
*
880+
* @param pArray byte[] array which will later be encoded
881+
* @param chunkSize line-length of the output (<= 0 means no chunking) between each
882+
* chunkSeparator (e.g. CRLF).
883+
* @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF).
884+
*
885+
* @return amount of space needed to encoded the supplied array. Returns
886+
* a long since a max-len array will require Integer.MAX_VALUE + 33%.
887+
*/
888+
private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) {
889+
// base64 always encodes to multiples of 4.
890+
chunkSize = (chunkSize / 4) * 4;
891+
892+
long len = (pArray.length * 4) / 3;
893+
long mod = len % 4;
894+
if (mod != 0) {
895+
len += 4 - mod;
896+
}
897+
if (chunkSize > 0 && chunkSeparator != null) {
898+
boolean lenChunksPerfectly = len % chunkSize == 0;
899+
len += (len / chunkSize) * chunkSeparator.length;
900+
if (!lenChunksPerfectly) {
901+
len += chunkSeparator.length;
902+
}
903+
}
904+
return len;
877905
}
878906

879907
// Implementation of integer encoding used for crypto

src/java/org/apache/commons/codec/binary/Base64InputStream.java

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,22 @@
2222
import java.io.InputStream;
2323

2424
/**
25-
* Provides Base64 encoding and decoding in a streaming fashion (unlimited size).
26-
* When encoding the default lineLength is 76 characters and the default
27-
* lineEnding is CRLF, but these can be overridden by using the appropriate
25+
* Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength
26+
* is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate
2827
* constructor.
2928
* <p>
30-
* The default behaviour of the Base64InputStream is to DECODE, whereas the
31-
* default behaviour of the Base64OutputStream is to ENCODE, but this
32-
* behaviour can be overridden by using a different constructor.
33-
* </p><p>
29+
* The default behaviour of the Base64InputStream is to DECODE, whereas the default behaviour of the Base64OutputStream
30+
* is to ENCODE, but this behaviour can be overridden by using a different constructor.
31+
* </p>
32+
* <p>
3433
* This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
3534
* Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
36-
* </p><p>
35+
* </p>
36+
* <p>
3737
* Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode
38-
* character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).
38+
* character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).
3939
* </p>
40+
*
4041
* @author Apache Software Foundation
4142
* @version $Id $
4243
* @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
@@ -45,26 +46,29 @@
4546
public class Base64InputStream extends FilterInputStream {
4647

4748
private final boolean doEncode;
49+
4850
private final Base64 base64;
51+
4952
private final byte[] singleByte = new byte[1];
5053

5154
/**
52-
* Creates a Base64InputStream such that all data read is Base64-decoded
53-
* from the original provided InputStream.
54-
*
55-
* @param in InputStream to wrap.
55+
* Creates a Base64InputStream such that all data read is Base64-decoded from the original provided InputStream.
56+
*
57+
* @param in
58+
* InputStream to wrap.
5659
*/
5760
public Base64InputStream(InputStream in) {
5861
this(in, false);
5962
}
6063

6164
/**
62-
* Creates a Base64InputStream such that all data read is either
63-
* Base64-encoded or Base64-decoded from the original provided InputStream.
64-
*
65-
* @param in InputStream to wrap.
66-
* @param doEncode true if we should encode all data read from us,
67-
* false if we should decode.
65+
* Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original
66+
* provided InputStream.
67+
*
68+
* @param in
69+
* InputStream to wrap.
70+
* @param doEncode
71+
* true if we should encode all data read from us, false if we should decode.
6872
*/
6973
public Base64InputStream(InputStream in, boolean doEncode) {
7074
super(in);
@@ -73,20 +77,20 @@ public Base64InputStream(InputStream in, boolean doEncode) {
7377
}
7478

7579
/**
76-
* Creates a Base64InputStream such that all data read is either
77-
* Base64-encoded or Base64-decoded from the original provided InputStream.
78-
*
79-
* @param in InputStream to wrap.
80-
* @param doEncode true if we should encode all data read from us,
81-
* false if we should decode.
82-
* @param lineLength If doEncode is true, each line of encoded
83-
* data will contain lineLength characters.
84-
* If lineLength <=0, the encoded data is not divided into lines.
85-
* If doEncode is false, lineLength is ignored.
86-
* @param lineSeparator If doEncode is true, each line of encoded
87-
* data will be terminated with this byte sequence (e.g. \r\n).
88-
* If lineLength <= 0, the lineSeparator is not used.
89-
* If doEncode is false lineSeparator is ignored.
80+
* Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original
81+
* provided InputStream.
82+
*
83+
* @param in
84+
* InputStream to wrap.
85+
* @param doEncode
86+
* true if we should encode all data read from us, false if we should decode.
87+
* @param lineLength
88+
* If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to
89+
* nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is
90+
* false, lineLength is ignored.
91+
* @param lineSeparator
92+
* If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n).
93+
* If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.
9094
*/
9195
public Base64InputStream(InputStream in, boolean doEncode, int lineLength, byte[] lineSeparator) {
9296
super(in);
@@ -97,8 +101,9 @@ public Base64InputStream(InputStream in, boolean doEncode, int lineLength, byte[
97101
/**
98102
* Reads one <code>byte</code> from this input stream.
99103
*
100-
* @return the byte as an integer in the range 0 to 255
101-
* Returns -1 if EOF has been reached.
104+
* @return the byte as an integer in the range 0 to 255. Returns -1 if EOF has been reached.
105+
* @throws IOException
106+
* if an I/O error occurs.
102107
*/
103108
public int read() throws IOException {
104109
int r = read(singleByte, 0, 1);
@@ -112,18 +117,23 @@ public int read() throws IOException {
112117
}
113118

114119
/**
115-
* Attempts to read <code>len</code> bytes into the specified
116-
* <code>b</code> array starting at <code>offset</code> from
117-
* this InputStream.
120+
* Attempts to read <code>len</code> bytes into the specified <code>b</code> array starting at <code>offset</code>
121+
* from this InputStream.
118122
*
119-
* @param b destination byte array
120-
* @param offset where to start writing the bytes
121-
* @param len maximum number of bytes to read
123+
* @param b
124+
* destination byte array
125+
* @param offset
126+
* where to start writing the bytes
127+
* @param len
128+
* maximum number of bytes to read
122129
*
123130
* @return number of bytes read
124-
* @throws IOException if an I/O error occurs.
125-
* @throws NullPointerException if the byte array parameter is null
126-
* @throws IndexOutOfBoundsException if offset, len or buffer size are invalid
131+
* @throws IOException
132+
* if an I/O error occurs.
133+
* @throws NullPointerException
134+
* if the byte array parameter is null
135+
* @throws IndexOutOfBoundsException
136+
* if offset, len or buffer size are invalid
127137
*/
128138
public int read(byte b[], int offset, int len) throws IOException {
129139
if (b == null) {
@@ -138,13 +148,11 @@ public int read(byte b[], int offset, int len) throws IOException {
138148
if (!base64.hasData()) {
139149
byte[] buf = new byte[doEncode ? 4096 : 8192];
140150
int c = in.read(buf);
141-
142151
// A little optimization to avoid System.arraycopy()
143152
// when possible.
144153
if (c > 0 && b.length == len) {
145154
base64.setInitialBuffer(b, offset, len);
146155
}
147-
148156
if (doEncode) {
149157
base64.encode(buf, 0, c);
150158
} else {
@@ -157,6 +165,7 @@ public int read(byte b[], int offset, int len) throws IOException {
157165

158166
/**
159167
* {@inheritDoc}
168+
*
160169
* @return false
161170
*/
162171
public boolean markSupported() {

0 commit comments

Comments
 (0)