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