blob: 35ba04fdb89303e5c7006e09b0a933edb26d54d2 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
package android.filterfw.core;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
import android.filterfw.core.FrameManager;
import android.filterfw.format.ObjectFormat;
import android.graphics.Bitmap;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* A frame that serializes any assigned values. Such a frame is used when passing data objects
* between threads.
*
* @hide
*/
public class SerializedFrame extends Frame {
/**
* The initial capacity of the serialized data stream.
*/
private final static int INITIAL_CAPACITY = 64;
/**
* The internal data streams.
*/
private DirectByteOutputStream mByteOutputStream;
private ObjectOutputStream mObjectOut;
/**
* An unsynchronized output stream that writes data to an accessible byte array. Callers are
* responsible for synchronization. This is more efficient than a ByteArrayOutputStream, as
* there are no array copies or synchronization involved to read back written data.
*/
private class DirectByteOutputStream extends OutputStream {
private byte[] mBuffer = null;
private int mOffset = 0;
private int mDataOffset = 0;
public DirectByteOutputStream(int size) {
mBuffer = new byte[size];
}
private final void ensureFit(int bytesToWrite) {
if (mOffset + bytesToWrite > mBuffer.length) {
byte[] oldBuffer = mBuffer;
mBuffer = new byte[Math.max(mOffset + bytesToWrite, mBuffer.length * 2)];
System.arraycopy(oldBuffer, 0, mBuffer, 0, mOffset);
oldBuffer = null;
}
}
public final void markHeaderEnd() {
mDataOffset = mOffset;
}
public final int getSize() {
return mOffset;
}
public byte[] getByteArray() {
return mBuffer;
}
@Override
public final void write(byte b[]) {
write(b, 0, b.length);
}
@Override
public final void write(byte b[], int off, int len) {
ensureFit(len);
System.arraycopy(b, off, mBuffer, mOffset, len);
mOffset += len;
}
@Override
public final void write(int b) {
ensureFit(1);
mBuffer[mOffset++] = (byte)b;
}
public final void reset() {
mOffset = mDataOffset;
}
public final DirectByteInputStream getInputStream() {
return new DirectByteInputStream(mBuffer, mOffset);
}
}
/**
* An unsynchronized input stream that reads data directly from a provided byte array. Callers
* are responsible for synchronization and ensuring that the byte buffer is valid.
*/
private class DirectByteInputStream extends InputStream {
private byte[] mBuffer;
private int mPos = 0;
private int mSize;
public DirectByteInputStream(byte[] buffer, int size) {
mBuffer = buffer;
mSize = size;
}
@Override
public final int available() {
return mSize - mPos;
}
@Override
public final int read() {
return (mPos < mSize) ? (mBuffer[mPos++] & 0xFF) : -1;
}
@Override
public final int read(byte[] b, int off, int len) {
if (mPos >= mSize) {
return -1;
}
if ((mPos + len) > mSize) {
len = mSize - mPos;
}
System.arraycopy(mBuffer, mPos, b, off, len);
mPos += len;
return len;
}
@Override
public final long skip(long n) {
if ((mPos + n) > mSize) {
n = mSize - mPos;
}
if (n < 0) {
return 0;
}
mPos += n;
return n;
}
}
SerializedFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
setReusable(false);
// Setup streams
try {
mByteOutputStream = new DirectByteOutputStream(INITIAL_CAPACITY);
mObjectOut = new ObjectOutputStream(mByteOutputStream);
mByteOutputStream.markHeaderEnd();
} catch (IOException e) {
throw new RuntimeException("Could not create serialization streams for "
+ "SerializedFrame!", e);
}
}
static SerializedFrame wrapObject(Object object, FrameManager frameManager) {
FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
SerializedFrame result = new SerializedFrame(format, frameManager);
result.setObjectValue(object);
return result;
}
@Override
protected boolean hasNativeAllocation() {
return false;
}
@Override
protected void releaseNativeAllocation() {
}
@Override
public Object getObjectValue() {
return deserializeObjectValue();
}
@Override
public void setInts(int[] ints) {
assertFrameMutable();
setGenericObjectValue(ints);
}
@Override
public int[] getInts() {
Object result = deserializeObjectValue();
return (result instanceof int[]) ? (int[])result : null;
}
@Override
public void setFloats(float[] floats) {
assertFrameMutable();
setGenericObjectValue(floats);
}
@Override
public float[] getFloats() {
Object result = deserializeObjectValue();
return (result instanceof float[]) ? (float[])result : null;
}
@Override
public void setData(ByteBuffer buffer, int offset, int length) {
assertFrameMutable();
setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
}
@Override
public ByteBuffer getData() {
Object result = deserializeObjectValue();
return (result instanceof ByteBuffer) ? (ByteBuffer)result : null;
}
@Override
public void setBitmap(Bitmap bitmap) {
assertFrameMutable();
setGenericObjectValue(bitmap);
}
@Override
public Bitmap getBitmap() {
Object result = deserializeObjectValue();
return (result instanceof Bitmap) ? (Bitmap)result : null;
}
@Override
protected void setGenericObjectValue(Object object) {
serializeObjectValue(object);
}
private final void serializeObjectValue(Object object) {
try {
mByteOutputStream.reset();
mObjectOut.writeObject(object);
mObjectOut.flush();
mObjectOut.close();
} catch (IOException e) {
throw new RuntimeException("Could not serialize object " + object + " in "
+ this + "!", e);
}
}
private final Object deserializeObjectValue() {
try {
InputStream inputStream = mByteOutputStream.getInputStream();
ObjectInputStream objectStream = new ObjectInputStream(inputStream);
return objectStream.readObject();
} catch (IOException e) {
throw new RuntimeException("Could not deserialize object in " + this + "!", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to deserialize object of unknown class in "
+ this + "!", e);
}
}
@Override
public String toString() {
return "SerializedFrame (" + getFormat() + ")";
}
}