// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include #include #include #include #include #include #include "seq_android.h" #include "_cgo_export.h" #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "go/Seq", __VA_ARGS__) #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "go/Seq", __VA_ARGS__) static jfieldID memptr_id; static jfieldID receive_refnum_id; static jfieldID receive_code_id; static jfieldID receive_handle_id; static jclass jbytearray_clazz; // pinned represents a pinned array to be released at the end of Send call. typedef struct pinned { jobject ref; void* ptr; struct pinned* next; } pinned; // mem is a simple C equivalent of seq.Buffer. // // Many of the allocations around mem could be avoided to improve // function call performance, but the goal is to start simple. typedef struct mem { uint8_t *buf; uint32_t off; uint32_t len; uint32_t cap; // TODO(hyangah): have it as a separate field outside mem? pinned* pinned; } mem; // mem_ensure ensures that m has at least size bytes free. // If m is NULL, it is created. static mem *mem_ensure(mem *m, uint32_t size) { if (m == NULL) { m = (mem*)malloc(sizeof(mem)); if (m == NULL) { LOG_FATAL("mem_ensure malloc failed"); } m->cap = 0; m->off = 0; m->len = 0; m->buf = NULL; m->pinned = NULL; } if (m->cap > m->off+size) { return m; } m->buf = (uint8_t*)realloc((void*)m->buf, m->off+size); if (m->buf == NULL) { LOG_FATAL("mem_ensure realloc failed, off=%d, size=%d", m->off, size); } m->cap = m->off+size; return m; } static mem *mem_get(JNIEnv *env, jobject obj) { // Storage space for pointer is always 64-bits, even on 32-bit // machines. Cast to uintptr_t to avoid -Wint-to-pointer-cast. return (mem*)(uintptr_t)(*env)->GetLongField(env, obj, memptr_id); } static uint8_t *mem_read(JNIEnv *env, jobject obj, uint32_t size) { if (size == 0) { return NULL; } mem *m = mem_get(env, obj); if (m == NULL) { LOG_FATAL("mem_read on NULL mem"); } if (m->len-m->off < size) { LOG_FATAL("short read, size: %d", size); } uint8_t *res = m->buf+m->off; m->off += size; return res; } uint8_t *mem_write(JNIEnv *env, jobject obj, uint32_t size) { mem *m = mem_get(env, obj); if (m == NULL) { LOG_FATAL("mem_write on NULL mem"); } if (m->off != m->len) { LOG_FATAL("write can only append to seq, size: (off=%d, len=%d, size=%d", m->off, m->len, size); } uint32_t cap = m->cap; while (m->off+size > cap) { cap *= 2; } m = mem_ensure(m, cap); uint8_t *res = m->buf+m->off; m->off += size; m->len += size; return res; } static void *pin_array(JNIEnv *env, jobject obj, jobject arr) { mem *m = mem_get(env, obj); if (m == NULL) { m = mem_ensure(m, 64); } pinned *p = (pinned*) malloc(sizeof(pinned)); if (p == NULL) { LOG_FATAL("pin_array malloc failed"); } p->ref = (*env)->NewGlobalRef(env, arr); if ((*env)->IsInstanceOf(env, p->ref, jbytearray_clazz)) { p->ptr = (*env)->GetByteArrayElements(env, p->ref, NULL); } else { LOG_FATAL("unsupported array type"); } p->next = m->pinned; m->pinned = p; return p->ptr; } static void unpin_arrays(JNIEnv *env, mem *m) { pinned* p = m->pinned; while (p != NULL) { if ((*env)->IsInstanceOf(env, p->ref, jbytearray_clazz)) { (*env)->ReleaseByteArrayElements(env, p->ref, (jbyte*)p->ptr, JNI_ABORT); } else { LOG_FATAL("invalid array type"); } (*env)->DeleteGlobalRef(env, p->ref); pinned* o = p; p = p->next; free(o); } m->pinned = NULL; } static jfieldID find_field(JNIEnv *env, const char *class_name, const char *field_name, const char *field_type) { jclass clazz = (*env)->FindClass(env, class_name); if (clazz == NULL) { LOG_FATAL("cannot find %s", class_name); return NULL; } jfieldID id = (*env)->GetFieldID(env, clazz, field_name , field_type); if(id == NULL) { LOG_FATAL("no %s/%s field", field_name, field_type); return NULL; } return id; } static jclass find_class(JNIEnv *env, const char *class_name) { jclass clazz = (*env)->FindClass(env, class_name); if (clazz == NULL) { LOG_FATAL("cannot find %s", class_name); return NULL; } return (*env)->NewGlobalRef(env, clazz); } void init_seq(void *javavm) { JavaVM *vm = (JavaVM*)javavm; JNIEnv *env; int res = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6); if (res == JNI_EDETACHED) { JavaVMAttachArgs args; args.version = JNI_VERSION_1_6; if ((*vm)->AttachCurrentThread(vm, &env, &args) != 0) { LOG_FATAL("cannot attach to current_vm"); } } else if (res != 0) { LOG_FATAL("bad vm env: %d", res); } memptr_id = find_field(env, "go/Seq", "memptr", "J"); receive_refnum_id = find_field(env, "go/Seq$Receive", "refnum", "I"); receive_handle_id = find_field(env, "go/Seq$Receive", "handle", "I"); receive_code_id = find_field(env, "go/Seq$Receive", "code", "I"); jbytearray_clazz = find_class(env, "[B"); LOG_INFO("loaded go/Seq"); if (res == JNI_EDETACHED) { (*vm)->DetachCurrentThread(vm); } } JNIEXPORT void JNICALL Java_go_Seq_ensure(JNIEnv *env, jobject obj, jint size) { mem *m = mem_get(env, obj); if (m == NULL || m->off+size > m->cap) { m = mem_ensure(m, size); (*env)->SetLongField(env, obj, memptr_id, (jlong)(uintptr_t)m); } } JNIEXPORT void JNICALL Java_go_Seq_free(JNIEnv *env, jobject obj) { mem *m = mem_get(env, obj); if (m != NULL) { unpin_arrays(env, m); free((void*)m->buf); free((void*)m); } } #define MEM_READ(obj, ty) ((ty*)mem_read(env, obj, sizeof(ty))) JNIEXPORT jbyte JNICALL Java_go_Seq_readInt8(JNIEnv *env, jobject obj) { uint8_t *v = MEM_READ(obj, uint8_t); if (v == NULL) { return 0; } return *v; } JNIEXPORT jshort JNICALL Java_go_Seq_readInt16(JNIEnv *env, jobject obj) { int16_t *v = MEM_READ(obj, int16_t); return v == NULL ? 0 : *v; } JNIEXPORT jint JNICALL Java_go_Seq_readInt32(JNIEnv *env, jobject obj) { int32_t *v = MEM_READ(obj, int32_t); return v == NULL ? 0 : *v; } JNIEXPORT jlong JNICALL Java_go_Seq_readInt64(JNIEnv *env, jobject obj) { int64_t *v = MEM_READ(obj, int64_t); return v == NULL ? 0 : *v; } JNIEXPORT jfloat JNICALL Java_go_Seq_readFloat32(JNIEnv *env, jobject obj) { float *v = MEM_READ(obj, float); return v == NULL ? 0 : *v; } JNIEXPORT jdouble JNICALL Java_go_Seq_readFloat64(JNIEnv *env, jobject obj) { double *v = MEM_READ(obj, double); return v == NULL ? 0 : *v; } JNIEXPORT jstring JNICALL Java_go_Seq_readUTF16(JNIEnv *env, jobject obj) { int32_t size = *MEM_READ(obj, int32_t); if (size == 0) { return NULL; } return (*env)->NewString(env, (jchar*)mem_read(env, obj, 2*size), size); } JNIEXPORT jbyteArray JNICALL Java_go_Seq_readByteArray(JNIEnv *env, jobject obj) { // Send the (array length, pointer) pair encoded as two int64. // The pointer value is omitted if array length is 0. jlong size = Java_go_Seq_readInt64(env, obj); if (size == 0) { return NULL; } jbyteArray res = (*env)->NewByteArray(env, size); jlong ptr = Java_go_Seq_readInt64(env, obj); (*env)->SetByteArrayRegion(env, res, 0, size, (jbyte*)(intptr_t)(ptr)); return res; } #define MEM_WRITE(ty) (*(ty*)mem_write(env, obj, sizeof(ty))) JNIEXPORT void JNICALL Java_go_Seq_writeInt8(JNIEnv *env, jobject obj, jbyte v) { MEM_WRITE(int8_t) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeInt16(JNIEnv *env, jobject obj, jshort v) { MEM_WRITE(int16_t) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeInt32(JNIEnv *env, jobject obj, jint v) { MEM_WRITE(int32_t) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeInt64(JNIEnv *env, jobject obj, jlong v) { MEM_WRITE(int64_t) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeFloat32(JNIEnv *env, jobject obj, jfloat v) { MEM_WRITE(float) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeFloat64(JNIEnv *env, jobject obj, jdouble v) { MEM_WRITE(double) = v; } JNIEXPORT void JNICALL Java_go_Seq_writeUTF16(JNIEnv *env, jobject obj, jstring v) { if (v == NULL) { MEM_WRITE(int32_t) = 0; return; } int32_t size = (*env)->GetStringLength(env, v); MEM_WRITE(int32_t) = size; (*env)->GetStringRegion(env, v, 0, size, (jchar*)mem_write(env, obj, 2*size)); } JNIEXPORT void JNICALL Java_go_Seq_writeByteArray(JNIEnv *env, jobject obj, jbyteArray v) { // For Byte array, we pass only the (array length, pointer) pair // encoded as two int64 values. If the array length is 0, // the pointer value is omitted. if (v == NULL) { MEM_WRITE(int64_t) = 0; return; } jsize len = (*env)->GetArrayLength(env, v); MEM_WRITE(int64_t) = len; if (len == 0) { return; } jbyte* b = pin_array(env, obj, v); MEM_WRITE(int64_t) = (jlong)(uintptr_t)b; } JNIEXPORT void JNICALL Java_go_Seq_resetOffset(JNIEnv *env, jobject obj) { mem *m = mem_get(env, obj); if (m == NULL) { LOG_FATAL("resetOffset on NULL mem"); } m->off = 0; } JNIEXPORT void JNICALL Java_go_Seq_log(JNIEnv *env, jobject obj, jstring v) { mem *m = mem_get(env, obj); const char *label = (*env)->GetStringUTFChars(env, v, NULL); if (label == NULL) { LOG_FATAL("log GetStringUTFChars failed"); } if (m == NULL) { LOG_INFO("%s: mem=NULL", label); } else { LOG_INFO("%s: mem{off=%d, len=%d, cap=%d}", label, m->off, m->len, m->cap); } (*env)->ReleaseStringUTFChars(env, v, label); } JNIEXPORT void JNICALL Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) { DestroyRef(refnum); } JNIEXPORT void JNICALL Java_go_Seq_send(JNIEnv *env, jclass clazz, jstring descriptor, jint code, jobject src_obj, jobject dst_obj) { mem *src = mem_get(env, src_obj); if (src == NULL) { LOG_FATAL("send src is NULL"); } mem *dst = mem_get(env, dst_obj); if (dst == NULL) { LOG_FATAL("send dst is NULL"); } GoString desc; desc.p = (char*)(*env)->GetStringUTFChars(env, descriptor, NULL); if (desc.p == NULL) { LOG_FATAL("send GetStringUTFChars failed"); } desc.n = (*env)->GetStringUTFLength(env, descriptor); Send(desc, (GoInt)code, src->buf, src->len, &dst->buf, &dst->len); (*env)->ReleaseStringUTFChars(env, descriptor, desc.p); unpin_arrays(env, src); // assume 'src' is no longer needed. } JNIEXPORT void JNICALL Java_go_Seq_recv(JNIEnv *env, jclass clazz, jobject in_obj, jobject receive) { mem *in = mem_get(env, in_obj); if (in == NULL) { LOG_FATAL("recv in is NULL"); } struct Recv_return ret = Recv(&in->buf, &in->len); (*env)->SetIntField(env, receive, receive_refnum_id, ret.r0); (*env)->SetIntField(env, receive, receive_code_id, ret.r1); (*env)->SetIntField(env, receive, receive_handle_id, ret.r2); } JNIEXPORT void JNICALL Java_go_Seq_recvRes(JNIEnv *env, jclass clazz, jint handle, jobject out_obj) { mem *out = mem_get(env, out_obj); if (out == NULL) { LOG_FATAL("recvRes out is NULL"); } RecvRes((int32_t)handle, out->buf, out->len); }