1 #include "CreateJavaOutputStreamAdaptor.h" 2 3 #define RETURN_NULL_IF_NULL(value) \ 4 do { if (!(value)) { SkASSERT(0); return NULL; } } while (false) 5 6 static jmethodID gInputStream_resetMethodID; 7 static jmethodID gInputStream_markMethodID; 8 static jmethodID gInputStream_availableMethodID; 9 static jmethodID gInputStream_readMethodID; 10 static jmethodID gInputStream_skipMethodID; 11 12 class JavaInputStreamAdaptor : public SkStream { 13 public: 14 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) 15 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { 16 SkASSERT(ar); 17 fCapacity = env->GetArrayLength(ar); 18 SkASSERT(fCapacity > 0); 19 fBytesRead = 0; 20 } 21 22 virtual bool rewind() { 23 JNIEnv* env = fEnv; 24 25 fBytesRead = 0; 26 27 env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID); 28 if (env->ExceptionCheck()) { 29 env->ExceptionDescribe(); 30 env->ExceptionClear(); 31 SkDebugf("------- reset threw an exception\n"); 32 return false; 33 } 34 return true; 35 } 36 37 size_t doRead(void* buffer, size_t size) { 38 JNIEnv* env = fEnv; 39 size_t bytesRead = 0; 40 // read the bytes 41 do { 42 size_t requested = size; 43 if (requested > fCapacity) 44 requested = fCapacity; 45 46 jint n = env->CallIntMethod(fJavaInputStream, 47 gInputStream_readMethodID, fJavaByteArray, 0, requested); 48 if (env->ExceptionCheck()) { 49 env->ExceptionDescribe(); 50 env->ExceptionClear(); 51 SkDebugf("---- read threw an exception\n"); 52 return 0; 53 } 54 55 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. 56 break; // eof 57 } 58 59 env->GetByteArrayRegion(fJavaByteArray, 0, n, 60 reinterpret_cast<jbyte*>(buffer)); 61 if (env->ExceptionCheck()) { 62 env->ExceptionDescribe(); 63 env->ExceptionClear(); 64 SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); 65 return 0; 66 } 67 68 buffer = (void*)((char*)buffer + n); 69 bytesRead += n; 70 size -= n; 71 fBytesRead += n; 72 } while (size != 0); 73 74 return bytesRead; 75 } 76 77 size_t doSkip(size_t size) { 78 JNIEnv* env = fEnv; 79 80 jlong skipped = env->CallLongMethod(fJavaInputStream, 81 gInputStream_skipMethodID, (jlong)size); 82 if (env->ExceptionCheck()) { 83 env->ExceptionDescribe(); 84 env->ExceptionClear(); 85 SkDebugf("------- skip threw an exception\n"); 86 return 0; 87 } 88 if (skipped < 0) { 89 skipped = 0; 90 } 91 92 return (size_t)skipped; 93 } 94 95 size_t doSize() { 96 JNIEnv* env = fEnv; 97 jint avail = env->CallIntMethod(fJavaInputStream, 98 gInputStream_availableMethodID); 99 if (env->ExceptionCheck()) { 100 env->ExceptionDescribe(); 101 env->ExceptionClear(); 102 SkDebugf("------- available threw an exception\n"); 103 avail = 0; 104 } 105 return avail; 106 } 107 108 virtual size_t read(void* buffer, size_t size) { 109 JNIEnv* env = fEnv; 110 if (NULL == buffer) { 111 if (0 == size) { 112 return this->doSize(); 113 } else { 114 /* InputStream.skip(n) can return <=0 but still not be at EOF 115 If we see that value, we need to call read(), which will 116 block if waiting for more data, or return -1 at EOF 117 */ 118 size_t amountSkipped = 0; 119 do { 120 size_t amount = this->doSkip(size - amountSkipped); 121 if (0 == amount) { 122 char tmp; 123 amount = this->doRead(&tmp, 1); 124 if (0 == amount) { 125 // if read returned 0, we're at EOF 126 break; 127 } 128 } 129 amountSkipped += amount; 130 } while (amountSkipped < size); 131 return amountSkipped; 132 } 133 } 134 return this->doRead(buffer, size); 135 } 136 137 private: 138 JNIEnv* fEnv; 139 jobject fJavaInputStream; // the caller owns this object 140 jbyteArray fJavaByteArray; // the caller owns this object 141 size_t fCapacity; 142 size_t fBytesRead; 143 }; 144 145 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 146 jbyteArray storage, int markSize) { 147 static bool gInited; 148 149 if (!gInited) { 150 jclass inputStream_Clazz = env->FindClass("java/io/InputStream"); 151 RETURN_NULL_IF_NULL(inputStream_Clazz); 152 153 gInputStream_resetMethodID = env->GetMethodID(inputStream_Clazz, 154 "reset", "()V"); 155 gInputStream_markMethodID = env->GetMethodID(inputStream_Clazz, 156 "mark", "(I)V"); 157 gInputStream_availableMethodID = env->GetMethodID(inputStream_Clazz, 158 "available", "()I"); 159 gInputStream_readMethodID = env->GetMethodID(inputStream_Clazz, 160 "read", "([BII)I"); 161 gInputStream_skipMethodID = env->GetMethodID(inputStream_Clazz, 162 "skip", "(J)J"); 163 164 RETURN_NULL_IF_NULL(gInputStream_resetMethodID); 165 RETURN_NULL_IF_NULL(gInputStream_markMethodID); 166 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 167 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 168 RETURN_NULL_IF_NULL(gInputStream_skipMethodID); 169 170 gInited = true; 171 } 172 173 if (markSize) { 174 env->CallVoidMethod(stream, gInputStream_markMethodID, markSize); 175 } 176 177 return new JavaInputStreamAdaptor(env, stream, storage); 178 } 179 180 /////////////////////////////////////////////////////////////////////////////// 181 182 static jmethodID gOutputStream_writeMethodID; 183 static jmethodID gOutputStream_flushMethodID; 184 185 class SkJavaOutputStream : public SkWStream { 186 public: 187 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) 188 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { 189 fCapacity = env->GetArrayLength(storage); 190 } 191 192 virtual bool write(const void* buffer, size_t size) { 193 JNIEnv* env = fEnv; 194 jbyteArray storage = fJavaByteArray; 195 196 while (size > 0) { 197 size_t requested = size; 198 if (requested > fCapacity) { 199 requested = fCapacity; 200 } 201 202 env->SetByteArrayRegion(storage, 0, requested, 203 reinterpret_cast<const jbyte*>(buffer)); 204 if (env->ExceptionCheck()) { 205 env->ExceptionDescribe(); 206 env->ExceptionClear(); 207 SkDebugf("--- write:SetByteArrayElements threw an exception\n"); 208 return false; 209 } 210 211 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, 212 storage, 0, requested); 213 if (env->ExceptionCheck()) { 214 env->ExceptionDescribe(); 215 env->ExceptionClear(); 216 SkDebugf("------- write threw an exception\n"); 217 return false; 218 } 219 220 buffer = (void*)((char*)buffer + requested); 221 size -= requested; 222 } 223 return true; 224 } 225 226 virtual void flush() { 227 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); 228 } 229 230 private: 231 JNIEnv* fEnv; 232 jobject fJavaOutputStream; // the caller owns this object 233 jbyteArray fJavaByteArray; // the caller owns this object 234 size_t fCapacity; 235 }; 236 237 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, 238 jbyteArray storage) { 239 static bool gInited; 240 241 if (!gInited) { 242 jclass outputStream_Clazz = env->FindClass("java/io/OutputStream"); 243 RETURN_NULL_IF_NULL(outputStream_Clazz); 244 245 gOutputStream_writeMethodID = env->GetMethodID(outputStream_Clazz, 246 "write", "([BII)V"); 247 RETURN_NULL_IF_NULL(gOutputStream_writeMethodID); 248 gOutputStream_flushMethodID = env->GetMethodID(outputStream_Clazz, 249 "flush", "()V"); 250 RETURN_NULL_IF_NULL(gOutputStream_flushMethodID); 251 252 gInited = true; 253 } 254 255 return new SkJavaOutputStream(env, stream, storage); 256 } 257