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