Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9df2ffd4 authored by Doug Zongker's avatar Doug Zongker
Browse files

tweak the Base64 implementation

- move the encodeInternal/decodeInternal methods into the inner
  "state" classes

- tighten up the inner loop of the encoder and decoder a bit, saving
  about 5% of time in both cases

- improve javadoc

- other little fixes

Change-Id: I72e0ce8502c664a32418cea04636ccdbf4fec17c
parent 5b31fdae
Loading
Loading
Loading
Loading
+401 −366

File changed.

Preview size limit exceeded, changes collapsed.

+16 −33
Original line number Diff line number Diff line
@@ -25,16 +25,13 @@ import java.io.InputStream;
 * it.
 */
public class Base64InputStream extends FilterInputStream {
    private final boolean encode;
    private final Base64.EncoderState estate;
    private final Base64.DecoderState dstate;
    private final Base64.Coder coder;

    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;

@@ -63,22 +60,14 @@ public class Base64InputStream extends FilterInputStream {
     */
    public Base64InputStream(InputStream in, int flags, boolean encode) {
        super(in);
        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;
            coder = new Base64.Encoder(flags, 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);
            coder = new Base64.Decoder(flags, null);
        }
        coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)];
        outputStart = 0;
        outputEnd = 0;
    }
@@ -123,7 +112,7 @@ public class Base64InputStream extends FilterInputStream {
        if (outputStart >= outputEnd) {
            return -1;
        } else {
            return outputBuffer[outputStart++];
            return coder.output[outputStart++];
        }
    }

@@ -135,36 +124,30 @@ public class Base64InputStream extends FilterInputStream {
            return -1;
        }
        int bytes = Math.min(len, outputEnd-outputStart);
        System.arraycopy(outputBuffer, outputStart, b, off, bytes);
        System.arraycopy(coder.output, 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
     * decode/encode it into the empty coder.output, 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 {
        boolean success;
        if (bytesRead == -1) {
            eof = true;
                Base64.decodeInternal(EMPTY, 0, 0, dstate, true);
            success = coder.process(EMPTY, 0, 0, true);
        } else {
                Base64.decodeInternal(inputBuffer, 0, bytesRead, dstate, false);
            success = coder.process(inputBuffer, 0, bytesRead, false);
        }
            outputEnd = dstate.op;
        if (!success) {
            throw new IOException("bad base-64");
        }
        outputEnd = coder.op;
        outputStart = 0;
    }
}
+29 −28
Original line number Diff line number Diff line
@@ -25,9 +25,7 @@ import java.io.OutputStream;
 * it, writing the resulting data to another OutputStream.
 */
public class Base64OutputStream extends FilterOutputStream {
    private final boolean encode;
    private final Base64.EncoderState estate;
    private final Base64.DecoderState dstate;
    private final Base64.Coder coder;
    private final int flags;

    private byte[] buffer = null;
@@ -62,13 +60,10 @@ public class Base64OutputStream extends FilterOutputStream {
    public Base64OutputStream(OutputStream out, int flags, boolean encode) {
        super(out);
        this.flags = flags;
        this.encode = encode;
        if (encode) {
            estate = new Base64.EncoderState(flags, null);
            dstate = null;
            coder = new Base64.Encoder(flags, null);
        } else {
            estate = null;
            dstate = new Base64.DecoderState(flags, null);
            coder = new Base64.Decoder(flags, null);
        }
    }

@@ -107,13 +102,29 @@ public class Base64OutputStream extends FilterOutputStream {
    }

    public void close() throws IOException {
        IOException thrown = null;
        try {
            flushBuffer();
            internalWrite(EMPTY, 0, 0, true);
        } catch (IOException e) {
            thrown = e;
        }

        try {
            if ((flags & Base64.NO_CLOSE) == 0) {
                out.close();
            } else {
                out.flush();
            }
        } catch (IOException e) {
            if (thrown != null) {
                thrown = e;
            }
        }

        if (thrown != null) {
            throw thrown;
        }
    }

    /**
@@ -123,21 +134,11 @@ public class Base64OutputStream extends FilterOutputStream {
     *        encoder/decoder state to be finalized.
     */
    private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
        if (encode) {
            // len*8/5+10 is an overestimate of the most bytes the
            // encoder can produce for len bytes of input.
            estate.output = embiggen(estate.output, len*8/5+10);
            Base64.encodeInternal(b, off, len, estate, finish);
            out.write(estate.output, 0, estate.op);
        } else {
            // len*3/4+10 is an overestimate of the most bytes the
            // decoder can produce for len bytes of input.
            dstate.output = embiggen(dstate.output, len*3/4+10);
            if (!Base64.decodeInternal(b, off, len, dstate, finish)) {
        coder.output = embiggen(coder.output, coder.maxOutputSize(len));
        if (!coder.process(b, off, len, finish)) {
            throw new IOException("bad base-64");
        }
            out.write(dstate.output, 0, dstate.op);
        }
        out.write(coder.output, 0, coder.op);
    }

    /**
+51 −51
Original line number Diff line number Diff line
@@ -134,25 +134,25 @@ public class Base64Test extends TestCase {
    }

    public void testWebSafe() throws Exception {
        assertEquals(BYTES, 0, Base64.decode("", Base64.WEB_SAFE));
        assertEquals(BYTES, 1, Base64.decode("_w==", Base64.WEB_SAFE));
        assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.WEB_SAFE));
        assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.WEB_SAFE));
        assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.WEB_SAFE));
        assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.WEB_SAFE));
        assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.WEB_SAFE));
        assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.WEB_SAFE));
        assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.WEB_SAFE));

        assertEquals("", Base64.encodeToString(BYTES, 0, 0, Base64.WEB_SAFE));
        assertEquals("_w==\n", Base64.encodeToString(BYTES, 0, 1, Base64.WEB_SAFE));
        assertEquals("_-4=\n", Base64.encodeToString(BYTES, 0, 2, Base64.WEB_SAFE));
        assertEquals("_-7d\n", Base64.encodeToString(BYTES, 0, 3, Base64.WEB_SAFE));
        assertEquals("_-7dzA==\n", Base64.encodeToString(BYTES, 0, 4, Base64.WEB_SAFE));
        assertEquals("_-7dzLs=\n", Base64.encodeToString(BYTES, 0, 5, Base64.WEB_SAFE));
        assertEquals("_-7dzLuq\n", Base64.encodeToString(BYTES, 0, 6, Base64.WEB_SAFE));
        assertEquals("_-7dzLuqmQ==\n", Base64.encodeToString(BYTES, 0, 7, Base64.WEB_SAFE));
        assertEquals("_-7dzLuqmYg=\n", Base64.encodeToString(BYTES, 0, 8, Base64.WEB_SAFE));
        assertEquals(BYTES, 0, Base64.decode("", Base64.URL_SAFE));
        assertEquals(BYTES, 1, Base64.decode("_w==", Base64.URL_SAFE));
        assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.URL_SAFE));
        assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.URL_SAFE));
        assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.URL_SAFE));
        assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.URL_SAFE));
        assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.URL_SAFE));
        assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.URL_SAFE));
        assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.URL_SAFE));

        assertEquals("", Base64.encodeToString(BYTES, 0, 0, Base64.URL_SAFE));
        assertEquals("_w==\n", Base64.encodeToString(BYTES, 0, 1, Base64.URL_SAFE));
        assertEquals("_-4=\n", Base64.encodeToString(BYTES, 0, 2, Base64.URL_SAFE));
        assertEquals("_-7d\n", Base64.encodeToString(BYTES, 0, 3, Base64.URL_SAFE));
        assertEquals("_-7dzA==\n", Base64.encodeToString(BYTES, 0, 4, Base64.URL_SAFE));
        assertEquals("_-7dzLs=\n", Base64.encodeToString(BYTES, 0, 5, Base64.URL_SAFE));
        assertEquals("_-7dzLuq\n", Base64.encodeToString(BYTES, 0, 6, Base64.URL_SAFE));
        assertEquals("_-7dzLuqmQ==\n", Base64.encodeToString(BYTES, 0, 7, Base64.URL_SAFE));
        assertEquals("_-7dzLuqmYg=\n", Base64.encodeToString(BYTES, 0, 8, Base64.URL_SAFE));
    }

    public void testFlags() throws Exception {
@@ -227,55 +227,55 @@ public class Base64Test extends TestCase {
    }

    /**
     * Tests that Base64.encodeInternal does correct handling of the
     * Tests that Base64.Encoder.encode() 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
     * us to get at package-private members (Base64.Encoder in
     * this case).
     */
    public void XXXtestEncodeInternal() throws Exception {
        byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 };
        byte[] output = new byte[100];

        Base64.EncoderState state = new Base64.EncoderState(Base64.NO_PADDING | Base64.NO_WRAP,
        Base64.Encoder encoder = new Base64.Encoder(Base64.NO_PADDING | Base64.NO_WRAP,
                                                    output);

        Base64.encodeInternal(input, 0, 3, state, false);
        assertEquals("YWJj".getBytes(), 4, state.output, state.op);
        assertEquals(0, state.tailLen);
        encoder.process(input, 0, 3, false);
        assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op);
        assertEquals(0, encoder.tailLen);

        Base64.encodeInternal(input, 0, 3, state, false);
        assertEquals("YWJj".getBytes(), 4, state.output, state.op);
        assertEquals(0, state.tailLen);
        encoder.process(input, 0, 3, false);
        assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op);
        assertEquals(0, encoder.tailLen);

        Base64.encodeInternal(input, 0, 1, state, false);
        assertEquals(0, state.op);
        assertEquals(1, state.tailLen);
        encoder.process(input, 0, 1, false);
        assertEquals(0, encoder.op);
        assertEquals(1, encoder.tailLen);

        Base64.encodeInternal(input, 0, 1, state, false);
        assertEquals(0, state.op);
        assertEquals(2, state.tailLen);
        encoder.process(input, 0, 1, false);
        assertEquals(0, encoder.op);
        assertEquals(2, encoder.tailLen);

        Base64.encodeInternal(input, 0, 1, state, false);
        assertEquals("YWFh".getBytes(), 4, state.output, state.op);
        assertEquals(0, state.tailLen);
        encoder.process(input, 0, 1, false);
        assertEquals("YWFh".getBytes(), 4, encoder.output, encoder.op);
        assertEquals(0, encoder.tailLen);

        Base64.encodeInternal(input, 0, 2, state, false);
        assertEquals(0, state.op);
        assertEquals(2, state.tailLen);
        encoder.process(input, 0, 2, false);
        assertEquals(0, encoder.op);
        assertEquals(2, encoder.tailLen);

        Base64.encodeInternal(input, 0, 2, state, false);
        assertEquals("YWJh".getBytes(), 4, state.output, state.op);
        assertEquals(1, state.tailLen);
        encoder.process(input, 0, 2, false);
        assertEquals("YWJh".getBytes(), 4, encoder.output, encoder.op);
        assertEquals(1, encoder.tailLen);

        Base64.encodeInternal(input, 0, 2, state, false);
        assertEquals("YmFi".getBytes(), 4, state.output, state.op);
        assertEquals(0, state.tailLen);
        encoder.process(input, 0, 2, false);
        assertEquals("YmFi".getBytes(), 4, encoder.output, encoder.op);
        assertEquals(0, encoder.tailLen);

        Base64.encodeInternal(input, 0, 1, state, true);
        assertEquals("YQ".getBytes(), 2, state.output, state.op);
        encoder.process(input, 0, 1, true);
        assertEquals("YQ".getBytes(), 2, encoder.output, encoder.op);
    }

    private static final String lipsum =
@@ -307,7 +307,7 @@ public class Base64Test extends TestCase {
                          Base64.NO_WRAP,
                          Base64.NO_PADDING | Base64.NO_WRAP,
                          Base64.CRLF,
                          Base64.WEB_SAFE };
                          Base64.URL_SAFE };
        int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 };
        Random rng = new Random(32176L);

@@ -414,7 +414,7 @@ public class Base64Test extends TestCase {
                          Base64.NO_WRAP,
                          Base64.NO_PADDING | Base64.NO_WRAP,
                          Base64.CRLF,
                          Base64.WEB_SAFE };
                          Base64.URL_SAFE };
        int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 };
        Random rng = new Random(32176L);