Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package android.filterfw.core;
     19 
     20 import android.filterfw.core.Frame;
     21 import android.filterfw.core.FrameFormat;
     22 import android.filterfw.core.FrameManager;
     23 import android.filterfw.core.NativeBuffer;
     24 import android.filterfw.format.ObjectFormat;
     25 import android.graphics.Bitmap;
     26 
     27 import java.io.InputStream;
     28 import java.io.IOException;
     29 import java.io.ObjectInputStream;
     30 import java.io.ObjectOutputStream;
     31 import java.io.OptionalDataException;
     32 import java.io.OutputStream;
     33 import java.io.StreamCorruptedException;
     34 import java.lang.reflect.Constructor;
     35 import java.nio.ByteBuffer;
     36 
     37 /**
     38  * A frame that serializes any assigned values. Such a frame is used when passing data objects
     39  * between threads.
     40  *
     41  * @hide
     42  */
     43 public class SerializedFrame extends Frame {
     44 
     45     /**
     46      * The initial capacity of the serialized data stream.
     47      */
     48     private final static int INITIAL_CAPACITY = 64;
     49 
     50     /**
     51      * The internal data streams.
     52      */
     53     private DirectByteOutputStream mByteOutputStream;
     54     private ObjectOutputStream mObjectOut;
     55 
     56     /**
     57      * An unsynchronized output stream that writes data to an accessible byte array. Callers are
     58      * responsible for synchronization. This is more efficient than a ByteArrayOutputStream, as
     59      * there are no array copies or synchronization involved to read back written data.
     60      */
     61     private class DirectByteOutputStream extends OutputStream {
     62         private byte[] mBuffer = null;
     63         private int mOffset = 0;
     64         private int mDataOffset = 0;
     65 
     66         public DirectByteOutputStream(int size) {
     67             mBuffer = new byte[size];
     68         }
     69 
     70         private final void ensureFit(int bytesToWrite) {
     71             if (mOffset + bytesToWrite > mBuffer.length) {
     72                 byte[] oldBuffer = mBuffer;
     73                 mBuffer = new byte[Math.max(mOffset + bytesToWrite, mBuffer.length * 2)];
     74                 System.arraycopy(oldBuffer, 0, mBuffer, 0, mOffset);
     75                 oldBuffer = null;
     76             }
     77         }
     78 
     79         public final void markHeaderEnd() {
     80             mDataOffset = mOffset;
     81         }
     82 
     83         public final int getSize() {
     84             return mOffset;
     85         }
     86 
     87         public byte[] getByteArray() {
     88             return mBuffer;
     89         }
     90 
     91         @Override
     92         public final void write(byte b[]) {
     93             write(b, 0, b.length);
     94         }
     95 
     96         @Override
     97         public final void write(byte b[], int off, int len) {
     98             ensureFit(len);
     99             System.arraycopy(b, off, mBuffer, mOffset, len);
    100             mOffset += len;
    101         }
    102 
    103         @Override
    104         public final void write(int b) {
    105             ensureFit(1);
    106             mBuffer[mOffset++] = (byte)b;
    107         }
    108 
    109         public final void reset() {
    110             mOffset = mDataOffset;
    111         }
    112 
    113         public final DirectByteInputStream getInputStream() {
    114             return new DirectByteInputStream(mBuffer, mOffset);
    115         }
    116     }
    117 
    118     /**
    119      * An unsynchronized input stream that reads data directly from a provided byte array. Callers
    120      * are responsible for synchronization and ensuring that the byte buffer is valid.
    121      */
    122     private class DirectByteInputStream extends InputStream {
    123 
    124         private byte[] mBuffer;
    125         private int mPos = 0;
    126         private int mSize;
    127 
    128         public DirectByteInputStream(byte[] buffer, int size) {
    129             mBuffer = buffer;
    130             mSize = size;
    131         }
    132 
    133         @Override
    134         public final int available() {
    135             return mSize - mPos;
    136         }
    137 
    138         @Override
    139         public final int read() {
    140             return (mPos < mSize) ? (mBuffer[mPos++] & 0xFF) : -1;
    141         }
    142 
    143         @Override
    144         public final int read(byte[] b, int off, int len) {
    145             if (mPos >= mSize) {
    146                 return -1;
    147             }
    148             if ((mPos + len) > mSize) {
    149                 len = mSize - mPos;
    150             }
    151             System.arraycopy(mBuffer, mPos, b, off, len);
    152             mPos += len;
    153             return len;
    154         }
    155 
    156         @Override
    157         public final long skip(long n) {
    158             if ((mPos + n) > mSize) {
    159                 n = mSize - mPos;
    160             }
    161             if (n < 0) {
    162                 return 0;
    163             }
    164             mPos += n;
    165             return n;
    166         }
    167     }
    168 
    169     SerializedFrame(FrameFormat format, FrameManager frameManager) {
    170         super(format, frameManager);
    171         setReusable(false);
    172 
    173         // Setup streams
    174         try {
    175             mByteOutputStream = new DirectByteOutputStream(INITIAL_CAPACITY);
    176             mObjectOut = new ObjectOutputStream(mByteOutputStream);
    177             mByteOutputStream.markHeaderEnd();
    178         } catch (IOException e) {
    179             throw new RuntimeException("Could not create serialization streams for "
    180                 + "SerializedFrame!", e);
    181         }
    182     }
    183 
    184     static SerializedFrame wrapObject(Object object, FrameManager frameManager) {
    185         FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
    186         SerializedFrame result = new SerializedFrame(format, frameManager);
    187         result.setObjectValue(object);
    188         return result;
    189     }
    190 
    191     @Override
    192     protected boolean hasNativeAllocation() {
    193         return false;
    194     }
    195 
    196     @Override
    197     protected void releaseNativeAllocation() {
    198     }
    199 
    200     @Override
    201     public Object getObjectValue() {
    202         return deserializeObjectValue();
    203     }
    204 
    205     @Override
    206     public void setInts(int[] ints) {
    207         assertFrameMutable();
    208         setGenericObjectValue(ints);
    209     }
    210 
    211     @Override
    212     public int[] getInts() {
    213         Object result = deserializeObjectValue();
    214         return (result instanceof int[]) ? (int[])result : null;
    215     }
    216 
    217     @Override
    218     public void setFloats(float[] floats) {
    219         assertFrameMutable();
    220         setGenericObjectValue(floats);
    221     }
    222 
    223     @Override
    224     public float[] getFloats() {
    225         Object result = deserializeObjectValue();
    226         return (result instanceof float[]) ? (float[])result : null;
    227     }
    228 
    229     @Override
    230     public void setData(ByteBuffer buffer, int offset, int length) {
    231         assertFrameMutable();
    232         setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
    233     }
    234 
    235     @Override
    236     public ByteBuffer getData() {
    237         Object result = deserializeObjectValue();
    238         return (result instanceof ByteBuffer) ? (ByteBuffer)result : null;
    239     }
    240 
    241     @Override
    242     public void setBitmap(Bitmap bitmap) {
    243         assertFrameMutable();
    244         setGenericObjectValue(bitmap);
    245     }
    246 
    247     @Override
    248     public Bitmap getBitmap() {
    249         Object result = deserializeObjectValue();
    250         return (result instanceof Bitmap) ? (Bitmap)result : null;
    251     }
    252 
    253     @Override
    254     protected void setGenericObjectValue(Object object) {
    255         serializeObjectValue(object);
    256     }
    257 
    258     private final void serializeObjectValue(Object object) {
    259         try {
    260             mByteOutputStream.reset();
    261             mObjectOut.writeObject(object);
    262             mObjectOut.flush();
    263             mObjectOut.close();
    264         } catch (IOException e) {
    265             throw new RuntimeException("Could not serialize object " + object + " in "
    266                 + this + "!", e);
    267         }
    268     }
    269 
    270     private final Object deserializeObjectValue() {
    271         try {
    272             InputStream inputStream = mByteOutputStream.getInputStream();
    273             ObjectInputStream objectStream = new ObjectInputStream(inputStream);
    274             return objectStream.readObject();
    275         } catch (IOException e) {
    276             throw new RuntimeException("Could not deserialize object in " + this + "!", e);
    277         } catch (ClassNotFoundException e) {
    278             throw new RuntimeException("Unable to deserialize object of unknown class in "
    279                 + this + "!", e);
    280         }
    281     }
    282 
    283     @Override
    284     public String toString() {
    285         return "SerializedFrame (" + getFormat() + ")";
    286     }
    287 }
    288