--- /dev/null
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jni.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#undef sun
+
+union align {
+ int i;
+ long l;
+ double d;
+ void *p;
+ void (*f)(void *);
+ struct notexist *s;
+};
+
+struct native_type {
+ const char *name;
+ size_t sz;
+ uint32_t tag;
+};
+
+typedef jbyteArray wrapped;
+
+struct open {
+ wrapped obj;
+ jbyte *arr;
+};
+
+struct base {
+ uint32_t tag;
+};
+
+static void except(JNIEnv *jni, const char *clsname, const char *msg)
+{
+ jclass cls;
+ int rc;
+
+ cls = (*jni)->FindClass(jni, clsname); assert(cls);
+ rc = (*jni)->ThrowNew(jni, cls, msg); assert(!rc);
+}
+
+static void except_errno(JNIEnv *jni, const char *clsname, int err)
+ { except(jni, clsname, strerror(err)); }
+
+static void *open_struct_unchecked(JNIEnv *jni, wrapped obj, struct open *op)
+{
+ jboolean copyp;
+ uintptr_t p, q;
+
+ op->obj = obj;
+ op->arr = (*jni)->GetByteArrayElements(jni, obj, ©p);
+ if (!op->arr) return (0);
+ p = (uintptr_t)op->arr;
+ q = p + sizeof(union align) - 1;
+ q -= q%sizeof(union align);
+ fprintf(stderr, ";; offset = %"PRIuPTR"\n", q - p);
+ return (op->arr + (q - p));
+}
+
+static void *open_struct(JNIEnv *jni, wrapped obj,
+ const struct native_type *ty, struct open *op)
+{
+ struct base *p;
+ jsize n;
+
+ if (!obj) { except(jni, "java/lang/NullPointerException", 0); return (0); }
+ n = (*jni)->GetArrayLength(jni, obj);
+ if ((*jni)->ExceptionOccurred(jni)) return (0);
+ p = open_struct_unchecked(jni, obj, op);
+ if (!p) return (0);
+ if (n < ty->sz + sizeof(union align) - 1 || p->tag != ty->tag)
+ {
+ (*jni)->ReleaseByteArrayElements(jni, obj, op->arr, JNI_ABORT);
+ except(jni, "uk/org/distorted/tripe/JNI$NativeObjectTypeException", 0);
+ return (0);
+ }
+ return (p);
+}
+
+static wrapped close_struct(JNIEnv *jni, struct open *op)
+{
+ (*jni)->ReleaseByteArrayElements(jni, op->obj, op->arr, 0);
+ return (op->obj);
+}
+
+static void *alloc_struct(JNIEnv *jni, const struct native_type *ty,
+ struct open *op)
+{
+ wrapped obj;
+ struct base *p;
+
+ obj = (*jni)->NewByteArray(jni, ty->sz + sizeof(union align) - 1);
+ if (!obj) return (0);
+ p = open_struct_unchecked(jni, obj, op);
+ if (!p) { (*jni)->DeleteLocalRef(jni, obj); return (0); }
+ p->tag = ty->tag;
+ return (p);
+}
+
+JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_test
+ (JNIEnv *jni, jobject cls)
+ { printf("Hello from C!\n"); }
+
+struct toy {
+ struct base _base;
+ const char *p;
+};
+static const struct native_type toy_type =
+ { "toy", sizeof(struct toy), 0x58008918 };
+
+JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_make
+ (JNIEnv *jni, jobject cls)
+{
+ struct open op_toy;
+ struct toy *toy;
+
+ toy = alloc_struct(jni, &toy_type, &op_toy);
+ if (!toy) return (0);
+ toy->p = "A working thing";
+ return (close_struct(jni, &op_toy));
+}
+
+JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_check
+ (JNIEnv *jni, jobject cls, wrapped wtoy)
+{
+ struct toy *toy;
+ struct open op_toy;
+
+ toy = open_struct(jni, wtoy, &toy_type, &op_toy);
+ if (!toy) return;
+ printf("Toy says: %s\n", toy->p);
+ close_struct(jni, &op_toy);
+}
+
+struct conn {
+ struct base _base;
+ int fd;
+ unsigned f;
+#define CF_CLOSERD 1u
+#define CF_CLOSEWR 2u
+#define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
+};
+static const struct native_type conn_type
+ = { "conn", sizeof(struct conn), 0xed030167 };
+
+JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_connect
+ (JNIEnv *jni, jobject cls)
+{
+ struct conn *conn;
+ struct open op;
+ struct sockaddr_un sun;
+ int fd = -1;
+
+ conn = alloc_struct(jni, &conn_type, &op);
+ if (!conn) goto err;
+
+ fd = socket(SOCK_STREAM, PF_UNIX, 0);
+ if (!fd) goto err_except;
+
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path, "/tmp/mdw/sk");
+ if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err_except;
+
+ conn->fd = fd;
+ return (close_struct(jni, &op));
+
+err_except:
+ except_errno(jni, "java/io/IOException", errno);
+err:
+ if (fd) close(fd);
+ return (0);
+}
+
+JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_send
+ (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
+ jint start, jint len)
+{
+ struct conn *conn = 0;
+ struct open op;
+ jboolean copyp;
+ jsize bufsz;
+ ssize_t n;
+ jbyte *p = 0;
+
+ conn = open_struct(jni, wconn, &conn_type, &op);
+ if (!conn) goto end;
+
+ bufsz = (*jni)->GetArrayLength(jni, buf);
+ if ((*jni)->ExceptionOccurred(jni)) goto end;
+ if (bufsz < start || bufsz - start < len) {
+ except(jni, "java/lang/IndexOutOfBoundsException",
+ "bad send-buffer bounds");
+ goto end;
+ }
+
+ p = (*jni)->GetByteArrayElements(jni, buf, ©p);
+ if (!p) goto end;
+
+ while (len) {
+ n = send(conn->fd, p + start, len, 0);
+ if (n < 0) {
+ except_errno(jni, "java/io/IOException", errno);
+ goto end;
+ }
+ start += n; len -= n;
+ }
+
+end:
+ if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
+ if (conn) close_struct(jni, &op);
+ return;
+}
+
+JNIEXPORT jint JNICALL Java_uk_org_distorted_tripe_JNI_recv
+ (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
+ jint start, jint len)
+{
+ struct conn *conn = 0;
+ struct open op;
+ jboolean copyp;
+ jsize bufsz;
+ jbyte *p = 0;
+ jint rc = -1;
+
+ conn = open_struct(jni, wconn, &conn_type, &op);
+ if (!conn) goto end;
+
+ bufsz = (*jni)->GetArrayLength(jni, buf);
+ if ((*jni)->ExceptionOccurred(jni)) goto end;
+ if (bufsz < start || bufsz - start < len) {
+ except(jni, "java/lang/IndexOutOfBoundsException",
+ "bad receive-buffer bounds");
+ goto end;
+ }
+
+ p = (*jni)->GetByteArrayElements(jni, buf, ©p);
+ if (!p) goto end;
+
+ rc = recv(conn->fd, p + start, len, 0);
+ if (rc < 0) {
+ except_errno(jni, "java/io/IOException", errno);
+ goto end;
+ }
+ if (!rc) rc = -1;
+
+end:
+ if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
+ if (conn) close_struct(jni, &op);
+ return (rc);
+}
+
+JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_close
+ (JNIEnv *jni, jobject cls, wrapped wconn, jint how)
+{
+ struct conn *conn = 0;
+ struct open op;
+
+ conn = open_struct(jni, wconn, &conn_type, &op);
+ if (!conn || conn->fd == -1) goto end;
+
+ how &= CF_CLOSEMASK&~conn->f;
+ conn->f |= how;
+fprintf(stderr, ";; closing %u\n", how);
+ if ((conn->f&CF_CLOSEMASK) == CF_CLOSEMASK) {
+ close(conn->fd);
+ conn->fd = -1;
+ } else {
+ if (how&CF_CLOSERD) shutdown(conn->fd, SHUT_RD);
+ if (how&CF_CLOSEWR) shutdown(conn->fd, SHUT_WR);
+ }
+
+end:
+ if (conn) close_struct(jni, &op);
+ return;
+}
--- /dev/null
+package uk.org.distorted.tripe;
+
+import java.io.{InputStream, OutputStream};
+
+class Connection {
+ val conn = JNI.connect();
+ def close() { JNI.close(conn, JNI.CF_CLOSEMASK); }
+ override protected def finalize() { close(); }
+}
+
+class ConnectionInputStream(val conn: Connection) extends InputStream {
+ override def read(): Int = {
+ val buf = new Array[Byte](1);
+ val n = read(buf, 0, 1);
+ if (n < 0) -1 else buf(0)&0xff;
+ }
+ override def read(buf: Array[Byte]): Int =
+ read(buf, 0, buf.length);
+ override def read(buf: Array[Byte], start: Int, len: Int) =
+ JNI.recv(conn.conn, buf, start, len);
+ override def close() { JNI.close(conn.conn, JNI.CF_CLOSERD); }
+}
+
+class ConnectionOutputStream(val conn: Connection) extends OutputStream {
+ override def write(b: Int) = {
+ val buf = Array[Byte](b.toByte);
+ write(buf, 0, 1);
+ }
+ override def write(buf: Array[Byte]) { write(buf, 0, buf.length); }
+ override def write(buf: Array[Byte], start: Int, len: Int) =
+ JNI.send(conn.conn, buf, start, len);
+ override def close() { JNI.close(conn.conn, JNI.CF_CLOSEWR); }
+}