Loading common/java/com/android/common/Base64InputStream.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.common; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * An OutputStream that does either Base64 encoding or decoding on the * data written to it, writing the resulting data to another * OutputStream. */ public class Base64InputStream extends FilterInputStream { private final boolean encode; private final Base64.EncoderState estate; private final Base64.DecoderState dstate; private static byte[] EMPTY = new byte[0]; private static final int BUFFER_SIZE = 2048; private boolean eof; private byte[] inputBuffer; private byte[] outputBuffer; private int outputStart; private int outputEnd; /** * An InputStream that performs Base64 decoding on the data read * from the wrapped stream. * * @param in the InputStream to read the source data from * @param flags bit flags for controlling the decoder; see the * constants in {@link Base64} */ public Base64InputStream(InputStream out, int flags) { this(out, flags, false); } /** * Performs Base64 encoding or decoding on the data read from the * wrapped InputStream. * * @param in the InputStream to read the source data from * @param flags bit flags for controlling the decoder; see the * constants in {@link Base64} * @param encode true to encode, false to decode */ public Base64InputStream(InputStream out, int flags, boolean encode) { super(out); this.encode = encode; eof = false; inputBuffer = new byte[BUFFER_SIZE]; if (encode) { // len*8/5+10 is an overestimate of the most bytes the // encoder can produce for len bytes of input. outputBuffer = new byte[BUFFER_SIZE * 8/5 + 10]; estate = new Base64.EncoderState(flags, outputBuffer); dstate = null; } else { // len*3/4+10 is an overestimate of the most bytes the // decoder can produce for len bytes of input. outputBuffer = new byte[BUFFER_SIZE * 3/4 + 10]; estate = null; dstate = new Base64.DecoderState(flags, outputBuffer); } outputStart = 0; outputEnd = 0; } public boolean markSupported() { return false; } public void mark(int readlimit) { throw new UnsupportedOperationException(); } public void reset() { throw new UnsupportedOperationException(); } public void close() throws IOException { in.close(); inputBuffer = null; } public int available() { return outputEnd - outputStart; } public long skip(long n) throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return 0; } long bytes = Math.min(n, outputEnd-outputStart); outputStart += bytes; return bytes; } public int read() throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return -1; } else { return outputBuffer[outputStart++]; } } public int read(byte[] b, int off, int len) throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return -1; } int bytes = Math.min(len, outputEnd-outputStart); System.arraycopy(outputBuffer, outputStart, b, off, bytes); outputStart += bytes; return bytes; } /** * Read data from the input stream into inputBuffer, then * decode/encode it into the empty outputBuffer, and reset the * outputStart and outputEnd pointers. */ private void refill() throws IOException { if (eof) return; int bytesRead = in.read(inputBuffer); if (encode) { if (bytesRead == -1) { eof = true; Base64.encodeInternal(EMPTY, 0, 0, estate, true); } else { Base64.encodeInternal(inputBuffer, 0, bytesRead, estate, false); } outputEnd = estate.op; } else { if (bytesRead == -1) { eof = true; Base64.decodeInternal(EMPTY, 0, 0, dstate, true); } else { Base64.decodeInternal(inputBuffer, 0, bytesRead, dstate, false); } outputEnd = dstate.op; } outputStart = 0; } } common/tests/src/com/android/common/Base64Test.java +163 −53 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.common; import junit.framework.TestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Random; Loading Loading @@ -228,8 +229,13 @@ public class Base64Test extends TestCase { /** * Tests that Base64.encodeInternal does correct handling of the * tail for each call. * * This test is disabled because while it passes if you can get it * to run, android's test infrastructure currently doesn't allow * us to get at package-private members (Base64.EncoderState in * this case). */ public void testEncodeInternal() throws Exception { public void XXXtestEncodeInternal() throws Exception { byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 }; byte[] output = new byte[100]; Loading Loading @@ -272,23 +278,8 @@ public class Base64Test extends TestCase { assertEquals("YQ".getBytes(), 2, state.output, state.op); } /** * Tests that Base64OutputStream produces exactly the same results * as calling Base64.encode/.decode on an in-memory array. */ public void testOutputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // input needs to be at least 1024 bytes to test filling up // the write(int) buffer. byte[] input = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + private static final String lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Quisque congue eleifend odio, eu ornare nulla facilisis eget. " + "Integer eget elit diam, sit amet laoreet nibh. Quisque enim " + "urna, pharetra vitae consequat eget, adipiscing eu ante. " + Loading @@ -308,103 +299,222 @@ public class Base64Test extends TestCase { "Phasellus posuere, leo at ultricies vehicula, massa risus " + "volutpat sapien, eu tincidunt diam ipsum eget nulla. Cras " + "molestie dapibus commodo. Ut vel tellus at massa gravida " + "semper non sed orci.").getBytes(); "semper non sed orci."; public void testInputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // Test input needs to be at least 2048 bytes to fill up the // read buffer of Base64InputStream. byte[] plain = (lipsum + lipsum + lipsum + lipsum + lipsum).getBytes(); for (int flags: flagses) { byte[] encoded = Base64.encode(plain, flags); ByteArrayInputStream bais; Base64InputStream b64is; byte[] actual = new byte[plain.length * 2]; int ap; int b; // ----- test decoding ("encoded" -> "plain") ----- // read as much as it will give us in one chunk bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } assertEquals(actual, ap, plain); // read individual bytes bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } assertEquals(actual, ap, plain); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; readloop: while (true) { int l = writeLengths[rng.nextInt(writeLengths.length)]; if (l >= 0) { b = b64is.read(actual, ap, l); if (b == -1) break readloop; ap += b; } else { for (int i = 0; i < -l; ++i) { if ((b = b64is.read()) == -1) break readloop; actual[ap++] = (byte) b; } } } assertEquals(actual, ap, plain); for (int f = 0; f < flagses.length; ++f) { int flags = flagses[f]; // ----- test encoding ("plain" -> "encoded") ----- byte[] expected = Base64.encode(input, flags); // read as much as it will give us in one chunk bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } assertEquals(actual, ap, encoded); // read individual bytes bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } assertEquals(actual, ap, encoded); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; readloop: while (true) { int l = writeLengths[rng.nextInt(writeLengths.length)]; if (l >= 0) { b = b64is.read(actual, ap, l); if (b == -1) break readloop; ap += b; } else { for (int i = 0; i < -l; ++i) { if ((b = b64is.read()) == -1) break readloop; actual[ap++] = (byte) b; } } } assertEquals(actual, ap, encoded); } } /** * Tests that Base64OutputStream produces exactly the same results * as calling Base64.encode/.decode on an in-memory array. */ public void testOutputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // Test input needs to be at least 1024 bytes to test filling // up the write(int) buffer of Base64OutputStream. byte[] plain = (lipsum + lipsum).getBytes(); for (int flags: flagses) { byte[] encoded = Base64.encode(plain, flags); ByteArrayOutputStream baos; Base64OutputStream b64os; byte[] actual; int p; // ----- test encoding ("input" -> "expected") ----- // ----- test encoding ("plain" -> "encoded") ----- // one large write(byte[]) of the whole input baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); b64os.write(input); b64os.write(plain); b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); for (int i = 0; i < input.length; ++i) { b64os.write(input[i]); for (int i = 0; i < plain.length; ++i) { b64os.write(plain[i]); } b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); p = 0; while (p < input.length) { while (p < plain.length) { int l = writeLengths[rng.nextInt(writeLengths.length)]; l = Math.min(l, input.length-p); l = Math.min(l, plain.length-p); if (l >= 0) { b64os.write(input, p, l); b64os.write(plain, p, l); p += l; } else { l = Math.min(-l, input.length-p); l = Math.min(-l, plain.length-p); for (int i = 0; i < l; ++i) { b64os.write(input[p+i]); b64os.write(plain[p+i]); } p += l; } } b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // ----- test decoding ("expected" -> "input") ----- // ----- test decoding ("encoded" -> "plain") ----- // one large write(byte[]) of the whole input baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); b64os.write(expected); b64os.write(encoded); b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); for (int i = 0; i < expected.length; ++i) { b64os.write(expected[i]); for (int i = 0; i < encoded.length; ++i) { b64os.write(encoded[i]); } b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); p = 0; while (p < expected.length) { while (p < encoded.length) { int l = writeLengths[rng.nextInt(writeLengths.length)]; l = Math.min(l, expected.length-p); l = Math.min(l, encoded.length-p); if (l >= 0) { b64os.write(expected, p, l); b64os.write(encoded, p, l); p += l; } else { l = Math.min(-l, expected.length-p); l = Math.min(-l, encoded.length-p); for (int i = 0; i < l; ++i) { b64os.write(expected[p+i]); b64os.write(encoded[p+i]); } p += l; } } b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); } } } Loading
common/java/com/android/common/Base64InputStream.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.common; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * An OutputStream that does either Base64 encoding or decoding on the * data written to it, writing the resulting data to another * OutputStream. */ public class Base64InputStream extends FilterInputStream { private final boolean encode; private final Base64.EncoderState estate; private final Base64.DecoderState dstate; private static byte[] EMPTY = new byte[0]; private static final int BUFFER_SIZE = 2048; private boolean eof; private byte[] inputBuffer; private byte[] outputBuffer; private int outputStart; private int outputEnd; /** * An InputStream that performs Base64 decoding on the data read * from the wrapped stream. * * @param in the InputStream to read the source data from * @param flags bit flags for controlling the decoder; see the * constants in {@link Base64} */ public Base64InputStream(InputStream out, int flags) { this(out, flags, false); } /** * Performs Base64 encoding or decoding on the data read from the * wrapped InputStream. * * @param in the InputStream to read the source data from * @param flags bit flags for controlling the decoder; see the * constants in {@link Base64} * @param encode true to encode, false to decode */ public Base64InputStream(InputStream out, int flags, boolean encode) { super(out); this.encode = encode; eof = false; inputBuffer = new byte[BUFFER_SIZE]; if (encode) { // len*8/5+10 is an overestimate of the most bytes the // encoder can produce for len bytes of input. outputBuffer = new byte[BUFFER_SIZE * 8/5 + 10]; estate = new Base64.EncoderState(flags, outputBuffer); dstate = null; } else { // len*3/4+10 is an overestimate of the most bytes the // decoder can produce for len bytes of input. outputBuffer = new byte[BUFFER_SIZE * 3/4 + 10]; estate = null; dstate = new Base64.DecoderState(flags, outputBuffer); } outputStart = 0; outputEnd = 0; } public boolean markSupported() { return false; } public void mark(int readlimit) { throw new UnsupportedOperationException(); } public void reset() { throw new UnsupportedOperationException(); } public void close() throws IOException { in.close(); inputBuffer = null; } public int available() { return outputEnd - outputStart; } public long skip(long n) throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return 0; } long bytes = Math.min(n, outputEnd-outputStart); outputStart += bytes; return bytes; } public int read() throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return -1; } else { return outputBuffer[outputStart++]; } } public int read(byte[] b, int off, int len) throws IOException { if (outputStart >= outputEnd) { refill(); } if (outputStart >= outputEnd) { return -1; } int bytes = Math.min(len, outputEnd-outputStart); System.arraycopy(outputBuffer, outputStart, b, off, bytes); outputStart += bytes; return bytes; } /** * Read data from the input stream into inputBuffer, then * decode/encode it into the empty outputBuffer, and reset the * outputStart and outputEnd pointers. */ private void refill() throws IOException { if (eof) return; int bytesRead = in.read(inputBuffer); if (encode) { if (bytesRead == -1) { eof = true; Base64.encodeInternal(EMPTY, 0, 0, estate, true); } else { Base64.encodeInternal(inputBuffer, 0, bytesRead, estate, false); } outputEnd = estate.op; } else { if (bytesRead == -1) { eof = true; Base64.decodeInternal(EMPTY, 0, 0, dstate, true); } else { Base64.decodeInternal(inputBuffer, 0, bytesRead, dstate, false); } outputEnd = dstate.op; } outputStart = 0; } }
common/tests/src/com/android/common/Base64Test.java +163 −53 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.common; import junit.framework.TestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Random; Loading Loading @@ -228,8 +229,13 @@ public class Base64Test extends TestCase { /** * Tests that Base64.encodeInternal does correct handling of the * tail for each call. * * This test is disabled because while it passes if you can get it * to run, android's test infrastructure currently doesn't allow * us to get at package-private members (Base64.EncoderState in * this case). */ public void testEncodeInternal() throws Exception { public void XXXtestEncodeInternal() throws Exception { byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 }; byte[] output = new byte[100]; Loading Loading @@ -272,23 +278,8 @@ public class Base64Test extends TestCase { assertEquals("YQ".getBytes(), 2, state.output, state.op); } /** * Tests that Base64OutputStream produces exactly the same results * as calling Base64.encode/.decode on an in-memory array. */ public void testOutputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // input needs to be at least 1024 bytes to test filling up // the write(int) buffer. byte[] input = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + private static final String lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Quisque congue eleifend odio, eu ornare nulla facilisis eget. " + "Integer eget elit diam, sit amet laoreet nibh. Quisque enim " + "urna, pharetra vitae consequat eget, adipiscing eu ante. " + Loading @@ -308,103 +299,222 @@ public class Base64Test extends TestCase { "Phasellus posuere, leo at ultricies vehicula, massa risus " + "volutpat sapien, eu tincidunt diam ipsum eget nulla. Cras " + "molestie dapibus commodo. Ut vel tellus at massa gravida " + "semper non sed orci.").getBytes(); "semper non sed orci."; public void testInputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // Test input needs to be at least 2048 bytes to fill up the // read buffer of Base64InputStream. byte[] plain = (lipsum + lipsum + lipsum + lipsum + lipsum).getBytes(); for (int flags: flagses) { byte[] encoded = Base64.encode(plain, flags); ByteArrayInputStream bais; Base64InputStream b64is; byte[] actual = new byte[plain.length * 2]; int ap; int b; // ----- test decoding ("encoded" -> "plain") ----- // read as much as it will give us in one chunk bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } assertEquals(actual, ap, plain); // read individual bytes bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } assertEquals(actual, ap, plain); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(encoded); b64is = new Base64InputStream(bais, flags); ap = 0; readloop: while (true) { int l = writeLengths[rng.nextInt(writeLengths.length)]; if (l >= 0) { b = b64is.read(actual, ap, l); if (b == -1) break readloop; ap += b; } else { for (int i = 0; i < -l; ++i) { if ((b = b64is.read()) == -1) break readloop; actual[ap++] = (byte) b; } } } assertEquals(actual, ap, plain); for (int f = 0; f < flagses.length; ++f) { int flags = flagses[f]; // ----- test encoding ("plain" -> "encoded") ----- byte[] expected = Base64.encode(input, flags); // read as much as it will give us in one chunk bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } assertEquals(actual, ap, encoded); // read individual bytes bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } assertEquals(actual, ap, encoded); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(plain); b64is = new Base64InputStream(bais, flags, true); ap = 0; readloop: while (true) { int l = writeLengths[rng.nextInt(writeLengths.length)]; if (l >= 0) { b = b64is.read(actual, ap, l); if (b == -1) break readloop; ap += b; } else { for (int i = 0; i < -l; ++i) { if ((b = b64is.read()) == -1) break readloop; actual[ap++] = (byte) b; } } } assertEquals(actual, ap, encoded); } } /** * Tests that Base64OutputStream produces exactly the same results * as calling Base64.encode/.decode on an in-memory array. */ public void testOutputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, Base64.NO_WRAP, Base64.NO_PADDING | Base64.NO_WRAP, Base64.CRLF, Base64.WEB_SAFE }; int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 }; Random rng = new Random(32176L); // Test input needs to be at least 1024 bytes to test filling // up the write(int) buffer of Base64OutputStream. byte[] plain = (lipsum + lipsum).getBytes(); for (int flags: flagses) { byte[] encoded = Base64.encode(plain, flags); ByteArrayOutputStream baos; Base64OutputStream b64os; byte[] actual; int p; // ----- test encoding ("input" -> "expected") ----- // ----- test encoding ("plain" -> "encoded") ----- // one large write(byte[]) of the whole input baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); b64os.write(input); b64os.write(plain); b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); for (int i = 0; i < input.length; ++i) { b64os.write(input[i]); for (int i = 0; i < plain.length; ++i) { b64os.write(plain[i]); } b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags); p = 0; while (p < input.length) { while (p < plain.length) { int l = writeLengths[rng.nextInt(writeLengths.length)]; l = Math.min(l, input.length-p); l = Math.min(l, plain.length-p); if (l >= 0) { b64os.write(input, p, l); b64os.write(plain, p, l); p += l; } else { l = Math.min(-l, input.length-p); l = Math.min(-l, plain.length-p); for (int i = 0; i < l; ++i) { b64os.write(input[p+i]); b64os.write(plain[p+i]); } p += l; } } b64os.close(); actual = baos.toByteArray(); assertEquals(expected, actual); assertEquals(encoded, actual); // ----- test decoding ("expected" -> "input") ----- // ----- test decoding ("encoded" -> "plain") ----- // one large write(byte[]) of the whole input baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); b64os.write(expected); b64os.write(encoded); b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); for (int i = 0; i < expected.length; ++i) { b64os.write(expected[i]); for (int i = 0; i < encoded.length; ++i) { b64os.write(encoded[i]); } b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. baos = new ByteArrayOutputStream(); b64os = new Base64OutputStream(baos, flags, false); p = 0; while (p < expected.length) { while (p < encoded.length) { int l = writeLengths[rng.nextInt(writeLengths.length)]; l = Math.min(l, expected.length-p); l = Math.min(l, encoded.length-p); if (l >= 0) { b64os.write(expected, p, l); b64os.write(encoded, p, l); p += l; } else { l = Math.min(-l, expected.length-p); l = Math.min(-l, encoded.length-p); for (int i = 0; i < l; ++i) { b64os.write(expected[p+i]); b64os.write(encoded[p+i]); } p += l; } } b64os.close(); actual = baos.toByteArray(); assertEquals(input, actual); assertEquals(plain, actual); } } }