CreateJavaOutputStreamAdaptor.cpp revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1#include "CreateJavaOutputStreamAdaptor.h" 2 3#define RETURN_NULL_IF_NULL(value) \ 4 do { if (!(value)) { SkASSERT(0); return NULL; } } while (false) 5 6static jclass gInputStream_Clazz; 7static jmethodID gInputStream_resetMethodID; 8static jmethodID gInputStream_availableMethodID; 9static jmethodID gInputStream_readMethodID; 10static jmethodID gInputStream_skipMethodID; 11 12class JavaInputStreamAdaptor : public SkStream { 13public: 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) { 56 break; // eof 57 } 58 59 const jbyte* array = env->GetByteArrayElements(fJavaByteArray, 60 NULL); 61 memcpy(buffer, array, n); 62 env->ReleaseByteArrayElements(fJavaByteArray, 63 const_cast<jbyte*>(array), JNI_ABORT); 64 65 buffer = (void*)((char*)buffer + n); 66 bytesRead += n; 67 size -= n; 68 fBytesRead += n; 69 } while (size != 0); 70 71 return bytesRead; 72 } 73 74 size_t doSkip(size_t size) { 75 JNIEnv* env = fEnv; 76 jlong skipped = env->CallLongMethod(fJavaInputStream, 77 gInputStream_skipMethodID, (jlong)size); 78 if (env->ExceptionCheck()) { 79 env->ExceptionDescribe(); 80 env->ExceptionClear(); 81 SkDebugf("------- available threw an exception\n"); 82 return 0; 83 } 84 if (skipped < 0) { 85 skipped = 0; 86 } 87 return (size_t)skipped; 88 } 89 90 size_t doSize() { 91 JNIEnv* env = fEnv; 92 jint avail = env->CallIntMethod(fJavaInputStream, 93 gInputStream_availableMethodID); 94 if (env->ExceptionCheck()) { 95 env->ExceptionDescribe(); 96 env->ExceptionClear(); 97 SkDebugf("------- available threw an exception\n"); 98 avail = 0; 99 } 100 return avail; 101 } 102 103 virtual size_t read(void* buffer, size_t size) { 104 JNIEnv* env = fEnv; 105 if (NULL == buffer) { 106 if (0 == size) { 107 return this->doSize(); 108 } else { 109 /* InputStream.skip(n) can return <=0 but still not be at EOF 110 If we see that value, we need to call read(), which will 111 block if waiting for more data, or return -1 at EOF 112 */ 113 size_t amountSkipped = 0; 114 do { 115 size_t amount = this->doSkip(size); 116 if (0 == amount) { 117 char tmp; 118 amount = this->doRead(&tmp, 1); 119 if (0 == amount) { 120 // if read returned 0, we're at EOF 121 break; 122 } 123 } 124 amountSkipped += amount; 125 } while (amountSkipped < size); 126 return amountSkipped; 127 } 128 } 129 return this->doRead(buffer, size); 130 } 131 132private: 133 JNIEnv* fEnv; 134 jobject fJavaInputStream; // the caller owns this object 135 jbyteArray fJavaByteArray; // the caller owns this object 136 size_t fCapacity; 137 size_t fBytesRead; 138}; 139 140SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 141 jbyteArray storage) { 142 static bool gInited; 143 144 if (!gInited) { 145 gInputStream_Clazz = env->FindClass("java/io/InputStream"); 146 RETURN_NULL_IF_NULL(gInputStream_Clazz); 147 gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz); 148 149 gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz, 150 "reset", "()V"); 151 gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz, 152 "available", "()I"); 153 gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz, 154 "read", "([BII)I"); 155 gInputStream_skipMethodID = env->GetMethodID(gInputStream_Clazz, 156 "skip", "(J)J"); 157 158 RETURN_NULL_IF_NULL(gInputStream_resetMethodID); 159 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 160 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 161 RETURN_NULL_IF_NULL(gInputStream_skipMethodID); 162 163 gInited = true; 164 } 165 166 return new JavaInputStreamAdaptor(env, stream, storage); 167} 168 169/////////////////////////////////////////////////////////////////////////////// 170 171static jclass gOutputStream_Clazz; 172static jmethodID gOutputStream_writeMethodID; 173static jmethodID gOutputStream_flushMethodID; 174 175class SkJavaOutputStream : public SkWStream { 176public: 177 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) 178 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { 179 fCapacity = env->GetArrayLength(storage); 180 } 181 182 virtual bool write(const void* buffer, size_t size) { 183 JNIEnv* env = fEnv; 184 jbyteArray storage = fJavaByteArray; 185 186 while (size > 0) { 187 size_t requested = size; 188 if (requested > fCapacity) { 189 requested = fCapacity; 190 } 191 192 jbyte* array = env->GetByteArrayElements(storage, NULL); 193 memcpy(array, buffer, requested); 194 env->ReleaseByteArrayElements(storage, array, 0); 195 196 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, 197 storage, 0, requested); 198 if (env->ExceptionCheck()) { 199 env->ExceptionDescribe(); 200 env->ExceptionClear(); 201 SkDebugf("------- write threw an exception\n"); 202 return false; 203 } 204 205 buffer = (void*)((char*)buffer + requested); 206 size -= requested; 207 } 208 return true; 209 } 210 211 virtual void flush() { 212 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); 213 } 214 215private: 216 JNIEnv* fEnv; 217 jobject fJavaOutputStream; // the caller owns this object 218 jbyteArray fJavaByteArray; // the caller owns this object 219 size_t fCapacity; 220}; 221 222SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, 223 jbyteArray storage) { 224 static bool gInited; 225 226 if (!gInited) { 227 gOutputStream_Clazz = env->FindClass("java/io/OutputStream"); 228 RETURN_NULL_IF_NULL(gOutputStream_Clazz); 229 gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz); 230 231 gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz, 232 "write", "([BII)V"); 233 RETURN_NULL_IF_NULL(gOutputStream_writeMethodID); 234 gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz, 235 "flush", "()V"); 236 RETURN_NULL_IF_NULL(gOutputStream_flushMethodID); 237 238 gInited = true; 239 } 240 241 return new SkJavaOutputStream(env, stream, storage); 242} 243 244