CreateJavaOutputStreamAdaptor.cpp revision dd66bcbf9d6ef0c50a18d9c4b1b39ce7ef7afcc4
1#include "CreateJavaOutputStreamAdaptor.h"
2
3#define RETURN_NULL_IF_NULL(value) \
4    do { if (!(value)) { SkASSERT(0); return NULL; } } while (false)
5
6static jmethodID    gInputStream_resetMethodID;
7static jmethodID    gInputStream_markMethodID;
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) { // 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
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
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
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
137private:
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
145SkStream* 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_availableMethodID);
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
182static jmethodID    gOutputStream_writeMethodID;
183static jmethodID    gOutputStream_flushMethodID;
184
185class SkJavaOutputStream : public SkWStream {
186public:
187    SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
188        : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
189        fCapacity = env->GetArrayLength(storage);
190    }
191
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
226    virtual void flush() {
227        fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
228    }
229
230private:
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
237SkWStream* 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