@@ -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
0 commit comments