chiark / gitweb /
Initial commit.
[tripe-android] / jni.c
1 #include <assert.h>
2 #include <errno.h>
3 #include <inttypes.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include <jni.h>
10
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 #include <unistd.h>
15
16 #undef sun
17
18 union align {
19   int i;
20   long l;
21   double d;
22   void *p;
23   void (*f)(void *);
24   struct notexist *s;
25 };
26
27 struct native_type {
28   const char *name;
29   size_t sz;
30   uint32_t tag;
31 };
32
33 typedef jbyteArray wrapped;
34
35 struct open {
36   wrapped obj;
37   jbyte *arr;
38 };
39
40 struct base {
41   uint32_t tag;
42 };
43
44 static void except(JNIEnv *jni, const char *clsname, const char *msg)
45 {
46   jclass cls;
47   int rc;
48
49   cls = (*jni)->FindClass(jni, clsname); assert(cls);
50   rc = (*jni)->ThrowNew(jni, cls, msg); assert(!rc);
51 }
52
53 static void except_errno(JNIEnv *jni, const char *clsname, int err)
54   { except(jni, clsname, strerror(err)); }
55
56 static void *open_struct_unchecked(JNIEnv *jni, wrapped obj, struct open *op)
57 {
58   jboolean copyp;
59   uintptr_t p, q;
60
61   op->obj = obj;
62   op->arr = (*jni)->GetByteArrayElements(jni, obj, &copyp);
63   if (!op->arr) return (0);
64   p = (uintptr_t)op->arr;
65   q = p + sizeof(union align) - 1;
66   q -= q%sizeof(union align);
67   fprintf(stderr, ";; offset = %"PRIuPTR"\n", q - p);
68   return (op->arr + (q - p));
69 }
70
71 static void *open_struct(JNIEnv *jni, wrapped obj,
72                          const struct native_type *ty, struct open *op)
73 {
74   struct base *p;
75   jsize n;
76
77   if (!obj) { except(jni, "java/lang/NullPointerException", 0); return (0); }
78   n = (*jni)->GetArrayLength(jni, obj);
79   if ((*jni)->ExceptionOccurred(jni)) return (0);
80   p = open_struct_unchecked(jni, obj, op);
81   if (!p) return (0);
82   if (n < ty->sz + sizeof(union align) - 1 || p->tag != ty->tag)
83   {
84     (*jni)->ReleaseByteArrayElements(jni, obj, op->arr, JNI_ABORT);
85     except(jni, "uk/org/distorted/tripe/JNI$NativeObjectTypeException", 0);
86     return (0);
87   }
88   return (p);
89 }
90
91 static wrapped close_struct(JNIEnv *jni, struct open *op)
92 {
93   (*jni)->ReleaseByteArrayElements(jni, op->obj, op->arr, 0);
94   return (op->obj);
95 }
96
97 static void *alloc_struct(JNIEnv *jni, const struct native_type *ty,
98                           struct open *op)
99 {
100   wrapped obj;
101   struct base *p;
102
103   obj = (*jni)->NewByteArray(jni, ty->sz + sizeof(union align) - 1);
104   if (!obj) return (0);
105   p = open_struct_unchecked(jni, obj, op);
106   if (!p) { (*jni)->DeleteLocalRef(jni, obj); return (0); }
107   p->tag = ty->tag;
108   return (p);
109 }
110
111 JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_test
112         (JNIEnv *jni, jobject cls)
113   { printf("Hello from C!\n"); }
114
115 struct toy {
116   struct base _base;
117   const char *p;
118 };
119 static const struct native_type toy_type =
120         { "toy", sizeof(struct toy), 0x58008918 };
121
122 JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_make
123         (JNIEnv *jni, jobject cls)
124 {
125   struct open op_toy;
126   struct toy *toy;
127
128   toy = alloc_struct(jni, &toy_type, &op_toy);
129   if (!toy) return (0);
130   toy->p = "A working thing";
131   return (close_struct(jni, &op_toy));
132 }
133
134 JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_check
135         (JNIEnv *jni, jobject cls, wrapped wtoy)
136 {
137   struct toy *toy;
138   struct open op_toy;
139
140   toy = open_struct(jni, wtoy, &toy_type, &op_toy);
141   if (!toy) return;
142   printf("Toy says: %s\n", toy->p);
143   close_struct(jni, &op_toy);
144 }
145
146 struct conn {
147   struct base _base;
148   int fd;
149   unsigned f;
150 #define CF_CLOSERD 1u
151 #define CF_CLOSEWR 2u
152 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
153 };
154 static const struct native_type conn_type
155         = { "conn", sizeof(struct conn), 0xed030167 };
156
157 JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_connect
158         (JNIEnv *jni, jobject cls)
159 {
160   struct conn *conn;
161   struct open op;
162   struct sockaddr_un sun;
163   int fd = -1;
164
165   conn = alloc_struct(jni, &conn_type, &op);
166   if (!conn) goto err;
167
168   fd = socket(SOCK_STREAM, PF_UNIX, 0);
169   if (!fd) goto err_except;
170
171   sun.sun_family = AF_UNIX;
172   strcpy(sun.sun_path, "/tmp/mdw/sk");
173   if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err_except;
174
175   conn->fd = fd;
176   return (close_struct(jni, &op));
177
178 err_except:
179   except_errno(jni, "java/io/IOException", errno);
180 err:
181   if (fd) close(fd);
182   return (0);
183 }
184
185 JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_send
186         (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
187          jint start, jint len)
188 {
189   struct conn *conn = 0;
190   struct open op;
191   jboolean copyp;
192   jsize bufsz;
193   ssize_t n;
194   jbyte *p = 0;
195
196   conn = open_struct(jni, wconn, &conn_type, &op);
197   if (!conn) goto end;
198
199   bufsz = (*jni)->GetArrayLength(jni, buf);
200   if ((*jni)->ExceptionOccurred(jni)) goto end;
201   if (bufsz < start || bufsz - start < len) {
202     except(jni, "java/lang/IndexOutOfBoundsException",
203            "bad send-buffer bounds");
204     goto end;
205   }
206
207   p = (*jni)->GetByteArrayElements(jni, buf, &copyp);
208   if (!p) goto end;
209
210   while (len) {
211     n = send(conn->fd, p + start, len, 0);
212     if (n < 0) {
213       except_errno(jni, "java/io/IOException", errno);
214       goto end;
215     }
216     start += n; len -= n;
217   }
218
219 end:
220   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
221   if (conn) close_struct(jni, &op);
222   return;
223 }
224
225 JNIEXPORT jint JNICALL Java_uk_org_distorted_tripe_JNI_recv
226         (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
227          jint start, jint len)
228 {
229   struct conn *conn = 0;
230   struct open op;
231   jboolean copyp;
232   jsize bufsz;
233   jbyte *p = 0;
234   jint rc = -1;
235
236   conn = open_struct(jni, wconn, &conn_type, &op);
237   if (!conn) goto end;
238
239   bufsz = (*jni)->GetArrayLength(jni, buf);
240   if ((*jni)->ExceptionOccurred(jni)) goto end;
241   if (bufsz < start || bufsz - start < len) {
242     except(jni, "java/lang/IndexOutOfBoundsException",
243            "bad receive-buffer bounds");
244     goto end;
245   }
246
247   p = (*jni)->GetByteArrayElements(jni, buf, &copyp);
248   if (!p) goto end;
249
250   rc = recv(conn->fd, p + start, len, 0);
251   if (rc < 0) {
252     except_errno(jni, "java/io/IOException", errno);
253     goto end;
254   }
255   if (!rc) rc = -1;
256
257 end:
258   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
259   if (conn) close_struct(jni, &op);
260   return (rc);
261 }
262
263 JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_close
264         (JNIEnv *jni, jobject cls, wrapped wconn, jint how)
265 {
266   struct conn *conn = 0;
267   struct open op;
268
269   conn = open_struct(jni, wconn, &conn_type, &op);
270   if (!conn || conn->fd == -1) goto end;
271
272   how &= CF_CLOSEMASK&~conn->f;
273   conn->f |= how;
274 fprintf(stderr, ";; closing %u\n", how);
275   if ((conn->f&CF_CLOSEMASK) == CF_CLOSEMASK) {
276     close(conn->fd);
277     conn->fd = -1;
278   } else {
279     if (how&CF_CLOSERD) shutdown(conn->fd, SHUT_RD);
280     if (how&CF_CLOSEWR) shutdown(conn->fd, SHUT_WR);
281   }
282
283 end:
284   if (conn) close_struct(jni, &op);
285   return;
286 }