• 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 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:
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) { // 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 
doSkip(size_t size)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 
doSize()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 
read(void * buffer,size_t size)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 
CreateJavaInputStreamAdaptor(JNIEnv * env,jobject stream,jbyteArray storage,int markSize)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_readMethodID);
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:
SkJavaOutputStream(JNIEnv * env,jobject stream,jbyteArray storage)187     SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
188         : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
189         fCapacity = env->GetArrayLength(storage);
190     }
191 
write(const void * buffer,size_t size)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 
flush()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 
CreateJavaOutputStreamAdaptor(JNIEnv * env,jobject stream,jbyteArray storage)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