CreateJavaOutputStreamAdaptor.cpp revision f1f48bc7f200f54c76b22d845d8ba8419879b375
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_markMethodID;
9static jmethodID    gInputStream_availableMethodID;
10static jmethodID    gInputStream_readMethodID;
11static jmethodID    gInputStream_skipMethodID;
12
13class JavaInputStreamAdaptor : public SkStream {
14public:
15    JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar)
16        : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) {
17        SkASSERT(ar);
18        fCapacity   = env->GetArrayLength(ar);
19        SkASSERT(fCapacity > 0);
20        fBytesRead  = 0;
21    }
22
23	virtual bool rewind() {
24        JNIEnv* env = fEnv;
25
26        fBytesRead = 0;
27
28        env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);
29        if (env->ExceptionCheck()) {
30            env->ExceptionDescribe();
31            env->ExceptionClear();
32            SkDebugf("------- reset threw an exception\n");
33            return false;
34        }
35        return true;
36    }
37
38    size_t doRead(void* buffer, size_t size) {
39        JNIEnv* env = fEnv;
40        size_t bytesRead = 0;
41        // read the bytes
42        do {
43            size_t requested = size;
44            if (requested > fCapacity)
45                requested = fCapacity;
46
47            jint n = env->CallIntMethod(fJavaInputStream,
48                                        gInputStream_readMethodID, fJavaByteArray, 0, requested);
49            if (env->ExceptionCheck()) {
50                env->ExceptionDescribe();
51                env->ExceptionClear();
52                SkDebugf("---- read threw an exception\n");
53                return 0;
54            }
55
56            if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
57                break;  // eof
58            }
59
60            env->GetByteArrayRegion(fJavaByteArray, 0, n,
61                                    reinterpret_cast<jbyte*>(buffer));
62            if (env->ExceptionCheck()) {
63                env->ExceptionDescribe();
64                env->ExceptionClear();
65                SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
66                return 0;
67            }
68
69            buffer = (void*)((char*)buffer + n);
70            bytesRead += n;
71            size -= n;
72            fBytesRead += n;
73        } while (size != 0);
74
75        return bytesRead;
76    }
77
78    size_t doSkip(size_t size) {
79        JNIEnv* env = fEnv;
80
81        jlong skipped = env->CallLongMethod(fJavaInputStream,
82                                            gInputStream_skipMethodID, (jlong)size);
83        if (env->ExceptionCheck()) {
84            env->ExceptionDescribe();
85            env->ExceptionClear();
86            SkDebugf("------- skip threw an exception\n");
87            return 0;
88        }
89        if (skipped < 0) {
90            skipped = 0;
91        }
92
93        return (size_t)skipped;
94    }
95
96    size_t doSize() {
97        JNIEnv* env = fEnv;
98        jint avail = env->CallIntMethod(fJavaInputStream,
99                                        gInputStream_availableMethodID);
100        if (env->ExceptionCheck()) {
101            env->ExceptionDescribe();
102            env->ExceptionClear();
103            SkDebugf("------- available threw an exception\n");
104            avail = 0;
105        }
106        return avail;
107    }
108
109	virtual size_t read(void* buffer, size_t size) {
110        JNIEnv* env = fEnv;
111        if (NULL == buffer) {
112            if (0 == size) {
113                return this->doSize();
114            } else {
115                /*  InputStream.skip(n) can return <=0 but still not be at EOF
116                    If we see that value, we need to call read(), which will
117                    block if waiting for more data, or return -1 at EOF
118                 */
119                size_t amountSkipped = 0;
120                do {
121                    size_t amount = this->doSkip(size - amountSkipped);
122                    if (0 == amount) {
123                        char tmp;
124                        amount = this->doRead(&tmp, 1);
125                        if (0 == amount) {
126                            // if read returned 0, we're at EOF
127                            break;
128                        }
129                    }
130                    amountSkipped += amount;
131                } while (amountSkipped < size);
132                return amountSkipped;
133            }
134        }
135        return this->doRead(buffer, size);
136    }
137
138private:
139    JNIEnv*     fEnv;
140    jobject     fJavaInputStream;   // the caller owns this object
141    jbyteArray  fJavaByteArray;     // the caller owns this object
142    size_t      fCapacity;
143    size_t      fBytesRead;
144};
145
146SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
147                                       jbyteArray storage, int markSize) {
148    static bool gInited;
149
150    if (!gInited) {
151        gInputStream_Clazz = env->FindClass("java/io/InputStream");
152        RETURN_NULL_IF_NULL(gInputStream_Clazz);
153        gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz);
154
155        gInputStream_resetMethodID      = env->GetMethodID(gInputStream_Clazz,
156                                                           "reset", "()V");
157        gInputStream_markMethodID       = env->GetMethodID(gInputStream_Clazz,
158                                                           "mark", "(I)V");
159        gInputStream_availableMethodID  = env->GetMethodID(gInputStream_Clazz,
160                                                           "available", "()I");
161        gInputStream_readMethodID       = env->GetMethodID(gInputStream_Clazz,
162                                                           "read", "([BII)I");
163        gInputStream_skipMethodID       = env->GetMethodID(gInputStream_Clazz,
164                                                           "skip", "(J)J");
165
166        RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
167        RETURN_NULL_IF_NULL(gInputStream_markMethodID);
168        RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
169        RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
170        RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
171
172        gInited = true;
173    }
174
175    if (markSize) {
176        env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
177    }
178
179    return new JavaInputStreamAdaptor(env, stream, storage);
180}
181
182///////////////////////////////////////////////////////////////////////////////
183
184static jclass       gOutputStream_Clazz;
185static jmethodID    gOutputStream_writeMethodID;
186static jmethodID    gOutputStream_flushMethodID;
187
188class SkJavaOutputStream : public SkWStream {
189public:
190    SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
191        : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
192        fCapacity = env->GetArrayLength(storage);
193    }
194
195	virtual bool write(const void* buffer, size_t size) {
196        JNIEnv* env = fEnv;
197        jbyteArray storage = fJavaByteArray;
198
199        while (size > 0) {
200            size_t requested = size;
201            if (requested > fCapacity) {
202                requested = fCapacity;
203            }
204
205            env->SetByteArrayRegion(storage, 0, requested,
206                                    reinterpret_cast<const jbyte*>(buffer));
207            if (env->ExceptionCheck()) {
208                env->ExceptionDescribe();
209                env->ExceptionClear();
210                SkDebugf("--- write:SetByteArrayElements threw an exception\n");
211                return false;
212            }
213
214            fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID,
215                                 storage, 0, requested);
216            if (env->ExceptionCheck()) {
217                env->ExceptionDescribe();
218                env->ExceptionClear();
219                SkDebugf("------- write threw an exception\n");
220                return false;
221            }
222
223            buffer = (void*)((char*)buffer + requested);
224            size -= requested;
225        }
226        return true;
227    }
228
229    virtual void flush() {
230        fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
231    }
232
233private:
234    JNIEnv*     fEnv;
235    jobject     fJavaOutputStream;  // the caller owns this object
236    jbyteArray  fJavaByteArray;     // the caller owns this object
237    size_t      fCapacity;
238};
239
240SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
241                                         jbyteArray storage) {
242    static bool gInited;
243
244    if (!gInited) {
245        gOutputStream_Clazz = env->FindClass("java/io/OutputStream");
246        RETURN_NULL_IF_NULL(gOutputStream_Clazz);
247        gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz);
248
249        gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz,
250                                                       "write", "([BII)V");
251        RETURN_NULL_IF_NULL(gOutputStream_writeMethodID);
252        gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz,
253                                                       "flush", "()V");
254        RETURN_NULL_IF_NULL(gOutputStream_flushMethodID);
255
256        gInited = true;
257    }
258
259    return new SkJavaOutputStream(env, stream, storage);
260}
261
262