All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.io.Base64OutputStream Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.io;


import com.tangosol.util.Base;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;


/**
* Writes binary data into a Writer using IETF RFC 2045 Base64 Content
* Transfer Encoding.
*
* If the Base64OutputStream is not the first to write data into the
* Writer, it may be desired to write a line feed before Base64 data.
* According to the specification, Base64 data cannot exceed 76
* characters per line.
*
* Be careful to avoid calling flush() except when a stream of Base64
* content is complete.
*
* @author cp  2000.09.06
*/
public class Base64OutputStream
        extends OutputStream
        implements OutputStreaming
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Construct a Base64OutputStream on a Writer object.
    *
    * @param writer  the Writer to write the Base64 encoded data to
    */
    public Base64OutputStream(Writer writer)
        {
        this(writer, true);
        }

    /**
    * Construct a Base64OutputStream on a Writer object and specifying
    * a line-break option.
    *
    * @param writer       the Writer to write the Base64 encoded data to
    * @param fBreakLines  true to break the output into 76-character lines
    */
    public Base64OutputStream(Writer writer, boolean fBreakLines)
        {
        Base.azzert(writer != null);

        m_writer      = writer;
        m_fBreakLines = fBreakLines;
        }

    // ----- OutputStream implementation ------------------------------------

    /**
    * Writes the specified byte to this output stream.
    *
    * @param      b   the byte.
    *
    * @exception  IOException  if an I/O error occurs. In particular,
    *             an IOException may be thrown if the
    *             output stream has been closed.
    */
    public void write(int b) throws IOException
        {
        if (m_fClosed)
            {
            throw new IOException("Base64OutputStream is closed");
            }

        m_abAccum[m_cAccum++] = (byte) (b & 0xFF);
        if (m_cAccum == 3)
            {
            flushAccumulator();
            }
        }

    /**
    * Writes len bytes from the specified byte array
    * starting at offset off to this output stream.
    * 

* If b is null, a * NullPointerException is thrown. *

* If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is thrown. * * @param ab the data * @param ofb the start offset in the data * @param cb the number of bytes to write * * @exception IOException if an I/O error occurs. In particular, * an IOException is thrown if the output * stream is closed. */ public void write(byte[] ab, int ofb, int cb) throws IOException { // there is a point below which it does not make sense to // perform any optimizations if (cb > 256) { // if nothing has been written or line feeds are off and the // accumulator is empty then: // m_cLineGroups == GROUPS_PER_LINE && m_cAccum == 0 // otherwise write until: // m_cLineGroups == 0 && m_cAccum == 0 // or (in the case of line feeds being off) // m_cLineGroups == GROUPS_PER_LINE && m_cAccum == 0 // empty the accumulator and complete the current line while (!(m_cAccum == 0 && (m_cLineGroups == GROUPS_PER_LINE || m_cLineGroups == 0))) { write(ab[ofb++]); --cb; } // write a new line if necessary (note that the line groups counter // is not reset since the block to be written will terminate on a // line boundary as well) if (m_cLineGroups == 0) { m_writer.write(BASE64_LF); } // determine the binary block size to format; if not chunking into // lines, then the contents can be written in 3-byte groups, other- // wise the contents must break on a line boundary int cbChunk = m_fBreakLines ? GROUPS_PER_LINE * 3 : 3; int cChunks = cb / cbChunk; int cbBlock = cChunks * cbChunk; m_writer.write(encode(ab, ofb, cbBlock, m_fBreakLines)); ofb += cbBlock; cb -= cbBlock; if (m_fBreakLines) { m_cLineGroups = 0; } } // write remainder int ofbEnd = ofb + cb; while (ofb < ofbEnd) { write(ab[ofb++]); } } /** * Close the stream, flushing any accumulated bytes. The underlying * writer is not closed. * * @exception IOException if an I/O error occurs */ public void flush() throws IOException { if (m_fClosed) { throw new IOException("Base64OutputStream is closed"); } flushAccumulator(); m_writer.flush(); } /** * Close the stream, flushing any accumulated bytes. The underlying * writer is not closed. * * @exception IOException if an I/O error occurs */ public void close() throws IOException { flush(); m_fClosed = true; } /** * Flushes the bytes accumulated by the write(int) method. * * @exception IOException if an I/O error occurs */ protected void flushAccumulator() throws IOException { int cAccum = m_cAccum; if (cAccum == 0) { return; } int cLinesGroups = m_cLineGroups; if (cLinesGroups == 0) { m_writer.write(BASE64_LF); cLinesGroups = GROUPS_PER_LINE; } byte[] ab = m_abAccum; char[] ach = m_achGroup; char[] alpha = BASE64_ALPHABET; switch (cAccum) { case 1: { int n = (ab[0] & 0xFF); ach[0] = alpha[n >> 2 ]; // 1111 1100 ach[1] = alpha[n << 4 & 0x3F]; // 0000 0011 1111 ach[2] = BASE64_PAD; ach[3] = BASE64_PAD; } break; case 2: { int n = (ab[0] & 0xFF) << 8 | (ab[1] & 0xFF); ach[0] = alpha[n >> 10 ]; // 1111 1100 0000 0000 ach[1] = alpha[n >> 4 & 0x3F]; // 0000 0011 1111 0000 ach[2] = alpha[n << 2 & 0x3F]; // 0000 0000 0000 1111 11 ach[3] = BASE64_PAD; } break; case 3: { int n = (ab[0] & 0xFF) << 16 | (ab[1] & 0xFF) << 8 | (ab[2] & 0xFF); ach[0] = alpha[n >> 18 ]; // 1111 1100 0000 0000 0000 0000 ach[1] = alpha[n >> 12 & 0x3F]; // 0000 0011 1111 0000 0000 0000 ach[2] = alpha[n >> 6 & 0x3F]; // 0000 0000 0000 1111 1100 0000 ach[3] = alpha[n & 0x3F]; // 0000 0000 0000 0000 0011 1111 } break; default: Base.azzert(); break; } m_writer.write(ach); if (m_fBreakLines) { m_cLineGroups = cLinesGroups - 1; } m_cAccum = 0; } // ----- static helpers ------------------------------------------------- /** * Encode the passed binary data using Base64 encoding. * * @param ab the array containing the bytes to encode * * @return the encoded data as a char array */ public static char[] encode(byte[] ab) { return encode(ab, true); } /** * Encode the passed binary data using Base64 encoding. * * @param ab the array containing the bytes to encode * @param fBreakLines true to break the output into 76-character lines * * @return the encoded data as a char array */ public static char[] encode(byte[] ab, boolean fBreakLines) { return encode(ab, 0, ab.length, fBreakLines); } /** * Encode the passed binary data using Base64 encoding. * * @param ab the array containing the bytes to encode * @param ofb the start offset in the byte array * @param cb the number of bytes to encode * @param fBreakLines true to break the output into 76-character lines * * @return the encoded data as a char array */ public static char[] encode(byte[] ab, int ofb, int cb, boolean fBreakLines) { final char[] alpha = BASE64_ALPHABET; // examine the input int cGroups = cb / 3; // the number of full 24-bit groups int cbRemain = (cb * 4) % 3; // the number of leftover bytes // calculate the size of the result assuming: // (1) A 24-bit input group is encoded into 4 base64-alphabet // characters // (2) each output line is composed of groups of 4 base64-alphabet // characters // (3) each output line except the last has 19 groups of 4 // base64-alphabet characterss (length=76) // (4) each output line except the last is terminated with a new // line character (length=76+1=77) // (5) the last output line, even if full (19 groups), is not // terminated with a new line character int cchRaw = (cb + 2) / 3 * 4; int cLines = fBreakLines ? (cchRaw - 1) / 76 : 0; int cch = cchRaw + cLines; // allocate result char[] ach = new char[cch]; int ofch = 0; // offset into output if (cGroups > 0) { int cLineGroups = fBreakLines ? GROUPS_PER_LINE : -1; while (true) { // process next three bytes int n = (ab[ofb++] & 0xFF) << 16 | (ab[ofb++] & 0xFF) << 8 | (ab[ofb++] & 0xFF); ach[ofch++] = alpha[n >> 18 ]; // 1111 1100 0000 0000 0000 0000 ach[ofch++] = alpha[n >> 12 & 0x3F]; // 0000 0011 1111 0000 0000 0000 ach[ofch++] = alpha[n >> 6 & 0x3F]; // 0000 0000 0000 1111 1100 0000 ach[ofch++] = alpha[n & 0x3F]; // 0000 0000 0000 0000 0011 1111 // check for end of input if (--cGroups == 0) { break; } // check for new line if (--cLineGroups == 0) { ach[ofch++] = BASE64_LF; cLineGroups = GROUPS_PER_LINE; } } } switch (cbRemain) { default: case 0: break; case 1: { int n = (ab[ofb] & 0xFF); ach[ofch++] = alpha[n >> 2 ]; // 1111 1100 ach[ofch++] = alpha[n << 4 & 0x3F]; // 0000 0011 1111 ach[ofch++] = BASE64_PAD; ach[ofch++] = BASE64_PAD; } break; case 2: { int n = (ab[ofb++] & 0xFF) << 8 | (ab[ofb] & 0xFF); ach[ofch++] = alpha[n >> 10 ]; // 1111 1100 0000 0000 ach[ofch++] = alpha[n >> 4 & 0x3F]; // 0000 0011 1111 0000 ach[ofch++] = alpha[n << 2 & 0x3F]; // 0000 0000 0000 1111 11 ach[ofch++] = BASE64_PAD; } break; } Base.azzert(ofch == cch); return ach; } // ----- constants ------------------------------------------------------ /** * Base64 encodes into this "alphabet" of 64 characters. */ protected static final char[] BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" .toCharArray(); /** * The Base64 padding character. Base64 is encoded into 4-character * chunks; if the last chunk does not contain 4 characters, it is * filled with this padding character. */ protected static final char BASE64_PAD = '='; /** * The Base64 line feed character. Base64 is encoded into 76-character * lines unless . */ protected static final char BASE64_LF = '\n'; /** * The number of Base64 character groups in one line. This number * prevents a line from exceeding 76 characters. */ protected static final int GROUPS_PER_LINE = 19; // ----- data members --------------------------------------------------- /** * True after close is invoked. */ protected boolean m_fClosed; /** * The Writer object to which the Base64 encoded data is written. */ protected Writer m_writer; /** * True if lines are to be broken by BASE64_LF; */ protected boolean m_fBreakLines; /** * The number of groups left to write in the current line. */ protected int m_cLineGroups = GROUPS_PER_LINE; /** * Accumulated bytes. */ protected byte[] m_abAccum = new byte[3]; /** * The number of bytes accumulated (0, 1, 2 or 3). */ protected int m_cAccum; /** * An array that is used to send 4 characters at a time to the underlying * Writer object. */ protected char[] m_achGroup = new char[4]; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy