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