OutputStreamWrapper.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.Ice;

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

//
// Class to provide a java.io.OutputStream on top of our stream.
// We use this to serialize arbitrary Java serializable classes into
//
// Slice sequences are encoded on the wire as a count of elements, followed
// by the sequence contents. For arbitrary Java classes, we do not know how
// big the sequence that is eventually written will be. To avoid excessive
// data copying, this class mantains a private _bytes array of 254 bytes and,
// initially, writes data into that array. If more than 254 bytes end up being
// written, we write a dummy sequence size of 255 (which occupies five bytes
// on the wire) into the stream and, once this stream is closed, patch
// that size to match the actual size. Otherwise, if the _bytes buffer contains
// fewer than 255 bytes when this stream is closed, we write the sequence size
// as a single byte, followed by the contents of the _bytes buffer.
//

class OutputStreamWrapper extends OutputStream {
    public OutputStreamWrapper(com.zeroc.Ice.OutputStream s) {
        _s = s;
        _spos = s.pos();
        _bytes = new byte[254];
        _pos = 0;
    }

    @Override
    public void write(int b) throws IOException {
        try {
            if (_bytes != null) {
                //
                // If we can fit the data into the first 254 bytes, write it to _bytes.
                //
                if (_pos < _bytes.length) {
                    _bytes[_pos++] = (byte) b;
                    return;
                }

                _s.writeSize(255); // Dummy size, until we know how big the stream
                // really is and can patch the size.

                if (_pos > 0) {
                    //
                    // Write the current contents of _bytes.
                    //
                    _s.expand(_pos);
                    _s.getBuffer().b.put(_bytes, 0, _pos);
                }
                _bytes = null;
            }

            //
            // Write data passed by caller.
            //
            _s.expand(1);
            _s.getBuffer().b.put((byte) b);
            _pos += 1;
        } catch (Exception ex) {
            throw new IOException(ex.toString());
        }
    }

    @Override
    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }

    @Override
    public void write(byte[] bytes, int offset, int count) throws IOException {
        try {
            if (_bytes != null) {
                //
                // If we can fit the data into the first 254 bytes, write it to _bytes.
                //
                if (count <= _bytes.length - _pos) {
                    System.arraycopy(bytes, offset, _bytes, _pos, count);
                    _pos += count;
                    return;
                }

                _s.writeSize(255); // Dummy size, until we know how big the stream
                // really is and can patch the size.

                if (_pos > 0) {
                    //
                    // Write the current contents of _bytes.
                    //
                    _s.expand(_pos);
                    _s.getBuffer().b.put(_bytes, 0, _pos);
                }
                _bytes = null;
            }

            //
            // Write data passed by caller.
            //
            _s.expand(count);
            _s.getBuffer().b.put(bytes, offset, count);
            _pos += count;
        } catch (Exception ex) {
            throw new IOException(ex.toString());
        }
    }

    @Override
    public void flush() throws IOException {
        // This does nothing because we do not know the final size of a writable stream until it is
        // closed, and we cannot write to the stream until we know whether the final size is < 255
        // or not.
    }

    @Override
    public void close() throws IOException {
        try {
            if (_bytes != null) {
                assert (_pos <= _bytes.length);
                _s.pos(_spos);
                _s.writeSize(_pos);
                _s.expand(_pos);
                _s.getBuffer().b.put(_bytes, 0, _pos);
                _bytes = null;
            } else {
                int currentPos = _s.pos();
                _s.pos(_spos);
                _s.writeSize(_pos); // Patch previously-written dummy value.
                _s.pos(currentPos);
            }
        } catch (Exception ex) {
            throw new IOException(ex.toString());
        }
    }

    private final com.zeroc.Ice.OutputStream _s;
    private final int _spos;
    private byte[] _bytes;
    private int _pos;
}