blob: 137acc612cbf3a428bb3725b67c0c6d97c6958bd [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#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;
Joseph Wenf1f48bc2010-07-19 16:59:51 +08008static jmethodID gInputStream_markMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009static 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
Gilles Debunne8cd48572010-07-15 18:06:36 -070056 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 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;
Gilles Debunne8cd48572010-07-15 18:06:36 -070080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 jlong skipped = env->CallLongMethod(fJavaInputStream,
82 gInputStream_skipMethodID, (jlong)size);
83 if (env->ExceptionCheck()) {
84 env->ExceptionDescribe();
85 env->ExceptionClear();
Gilles Debunne8cd48572010-07-15 18:06:36 -070086 SkDebugf("------- skip threw an exception\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 return 0;
88 }
89 if (skipped < 0) {
90 skipped = 0;
91 }
Gilles Debunne8cd48572010-07-15 18:06:36 -070092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 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 {
Gilles Debunne8cd48572010-07-15 18:06:36 -0700121 size_t amount = this->doSkip(size - amountSkipped);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 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,
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800147 jbyteArray storage, int markSize) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 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");
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800157 gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
158 "mark", "(I)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 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);
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800167 RETURN_NULL_IF_NULL(gInputStream_markMethodID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 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
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800175 if (markSize) {
176 env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
177 }
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 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