Skip to content
OtpOutputStream.java 31.5 KiB
Newer Older
Carlos Galindo's avatar
Carlos Galindo committed
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2000-2017. All Rights Reserved.
 *
 * 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.
 *
 * %CopyrightEnd%
 */
package com.ericsson.otp.erlang;

// import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.zip.Deflater;

/**
 * Provides a stream for encoding Erlang terms to external format, for
 * transmission or storage.
 *
 * <p>
 * Note that this class is not synchronized, if you need synchronization you
 * must provide it yourself.
 *
 */
public class OtpOutputStream extends ByteArrayOutputStream {
    /** The default initial size of the stream. * */
    public static final int defaultInitialSize = 2048;

    /**
     * The default increment used when growing the stream (increment at least
     * this much). *
     */
    public static final int defaultIncrement = 2048;

    // static formats, used to encode floats and doubles
    @SuppressWarnings("unused")
    private static final DecimalFormat eform = new DecimalFormat("e+00;e-00");
    @SuppressWarnings("unused")
    private static final BigDecimal ten = new BigDecimal(10.0);
    @SuppressWarnings("unused")
    private static final BigDecimal one = new BigDecimal(1.0);

    private int fixedSize = Integer.MAX_VALUE;

    /**
     * Create a stream with the default initial size (2048 bytes).
     */
    public OtpOutputStream() {
        this(defaultInitialSize);
    }

    /**
     * Create a stream with the specified initial size.
     */
    public OtpOutputStream(final int size) {
        super(size);
    }

    /**
     * Create a stream containing the encoded version of the given Erlang term.
     */
    public OtpOutputStream(final OtpErlangObject o) {
        this();
        write_any(o);
    }

    // package scope
    /*
     * Get the contents of the output stream as an input stream instead. This is
     * used internally in {@link OtpCconnection} for tracing outgoing packages.
     *
     * @param offset where in the output stream to read data from when creating
     * the input stream. The offset is necessary because header contents start 5
     * bytes into the header buffer, whereas payload contents start at the
     * beginning
     *
     * @return an input stream containing the same raw data.
     */
    OtpInputStream getOtpInputStream(final int offset) {
        return new OtpInputStream(super.buf, offset, super.count - offset, 0);
    }

    /**
     * Get the current position in the stream.
     *
     * @return the current position in the stream.
     */
    public int getPos() {
        return super.count;
    }

    /**
Carlos Galindo's avatar
Carlos Galindo committed
     * Trims the capacity of this <code>OtpOutputStream</code> instance to be the
Carlos Galindo's avatar
Carlos Galindo committed
     * buffer's current size. An application can use this operation to minimize
Carlos Galindo's avatar
Carlos Galindo committed
     * the storage of an <code>OtpOutputStream</code> instance.
Carlos Galindo's avatar
Carlos Galindo committed
     */
    public void trimToSize() {
        resize(super.count);
    }

    private void resize(final int size) {
        if (size < super.buf.length) {
            final byte[] tmp = new byte[size];
            System.arraycopy(super.buf, 0, tmp, 0, size);
            super.buf = tmp;
        } else if (size > super.buf.length) {
            ensureCapacity(size);
        }
    }

    /**
Carlos Galindo's avatar
Carlos Galindo committed
     * Increases the capacity of this <code>OtpOutputStream</code> instance, if
Carlos Galindo's avatar
Carlos Galindo committed
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param minCapacity
     *            the desired minimum capacity
     */
    public void ensureCapacity(final int minCapacity) {
        if (minCapacity > fixedSize) {
            throw new IllegalArgumentException(
                    "Trying to increase fixed-size buffer");
        }
        final int oldCapacity = super.buf.length;
        if (minCapacity > oldCapacity) {
            int newCapacity = oldCapacity * 3 / 2 + 1;
            if (newCapacity < oldCapacity + defaultIncrement) {
                newCapacity = oldCapacity + defaultIncrement;
            }
            if (newCapacity < minCapacity) {
                newCapacity = minCapacity;
            }
            newCapacity = Math.min(fixedSize, newCapacity);
            // minCapacity is usually close to size, so this is a win:
            final byte[] tmp = new byte[newCapacity];
            System.arraycopy(super.buf, 0, tmp, 0, super.count);
            super.buf = tmp;
        }
    }

    /**
     * Write one byte to the stream.
     *
     * @param b
     *            the byte to write.
     *
     */
    public void write(final byte b) {
        ensureCapacity(super.count + 1);
        super.buf[super.count++] = b;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.ByteArrayOutputStream#write(byte[])
     */
    @Override
    public void write(final byte[] abuf) {
        // don't assume that super.write(byte[]) calls write(buf, 0, buf.length)
        write(abuf, 0, abuf.length);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.ByteArrayOutputStream#write(int)
     */
    @Override
    public synchronized void write(final int b) {
        ensureCapacity(super.count + 1);
        super.buf[super.count] = (byte) b;
        count += 1;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.io.ByteArrayOutputStream#write(byte[], int, int)
     */
    @Override
    public synchronized void write(final byte[] b, final int off, final int len) {
        if (off < 0 || off > b.length || len < 0 || off + len - b.length > 0) {
            throw new IndexOutOfBoundsException();
Loading
Loading full blame...