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