CreateJavaOutputStreamAdaptor.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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            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        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
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
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
135private:
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
143SkStream* 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
174static jclass       gOutputStream_Clazz;
175static jmethodID    gOutputStream_writeMethodID;
176static jmethodID    gOutputStream_flushMethodID;
177
178class SkJavaOutputStream : public SkWStream {
179public:
180    SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
181        : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
182        fCapacity = env->GetArrayLength(storage);
183    }
184
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
219    virtual void flush() {
220        fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
221    }
222
223private:
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
230SkWStream* 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