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