OutputStreamWrapper.java
// Copyright (c) ZeroC, Inc.
package com.zeroc.Ice;
import java.io.IOException;
import java.io.OutputStream;
/**
* Decorates an {@link com.zeroc.Ice.OutputStream} to marshal serializable Java classes into a sequence of bytes.
*
* <p>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 maintains a private byte-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;
}
// Dummy size, until we know how big the stream really is and can patch the size.
_s.writeSize(255);
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;
}
// Dummy size, until we know how big the stream really is and can patch the size.
_s.writeSize(255);
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;
}