2
0
mirror of synced 2025-02-22 14:28:14 +00:00
mobile/bind/java/seq_android.c
Hyang-Ah (Hana) Kim c039090cf1 bind/java: fix misuse of mem_ensure.
mem_ensure is to ensure the remaining space in buffer
is large enough to hold extra size bytes. We've been passing
the target capacity instead of the number of extra bytes we
need.

Change-Id: Ic6f6ddb4ad22cbcdbc44eb4a58e6a415ae771fb2
Reviewed-on: https://go-review.googlesource.com/12578
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-07-23 22:34:55 +00:00

452 lines
12 KiB
C

// 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 <android/log.h>
#include <errno.h>
#include <jni.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#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;
}
uint32_t cap = m->cap;
if (m->cap > m->off+size) {
return m;
}
if (cap == 0) {
cap = 64;
}
// TODO(hyangah): consider less aggressive allocation such as
// cap += max(pow2round(size), 64)
while (cap < m->off+size) {
cap *= 2;
}
m->buf = (uint8_t*)realloc((void*)m->buf, cap);
if (m->buf == NULL) {
LOG_FATAL("mem_ensure realloc failed, off=%d, size=%d", m->off, size);
}
m->cap = cap;
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 uint32_t align(uint32_t offset, uint32_t alignment) {
uint32_t pad = offset % alignment;
if (pad > 0) {
pad = alignment-pad;
}
return pad+offset;
}
static uint8_t *mem_read(JNIEnv *env, jobject obj, uint32_t size, uint32_t alignment) {
if (size == 0) {
return NULL;
}
mem *m = mem_get(env, obj);
if (m == NULL) {
LOG_FATAL("mem_read on NULL mem");
}
uint32_t offset = align(m->off, alignment);
if (m->len-offset < size) {
LOG_FATAL("short read");
}
uint8_t *res = m->buf+offset;
m->off = offset+size;
return res;
}
uint8_t *mem_write(JNIEnv *env, jobject obj, uint32_t size, uint32_t alignment) {
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 offset = align(m->off, alignment);
m = mem_ensure(m, offset - m->off + size);
uint8_t *res = m->buf+offset;
m->off = offset+size;
m->len = offset+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 void describe_exception(JNIEnv* env) {
jthrowable exc = (*env)->ExceptionOccurred(env);
if (exc) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
}
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) {
describe_exception(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
jfieldID id = (*env)->GetFieldID(env, clazz, field_name , field_type);
if(id == NULL) {
describe_exception(env);
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) {
describe_exception(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return (*env)->NewGlobalRef(env, clazz);
}
JNIEXPORT void JNICALL
Java_go_Seq_initSeq(JNIEnv *env, jclass clazz) {
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");
jclass bclazz = find_class(env, "[B");
jbytearray_clazz = (*env)->NewGlobalRef(env, bclazz);
}
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), sizeof(ty)))
JNIEXPORT jboolean JNICALL
Java_go_Seq_readBool(JNIEnv *env, jobject obj) {
int8_t *v = MEM_READ(obj, int8_t);
if (v == NULL) {
return 0;
}
return *v != 0 ? 1 : 0;
}
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, 1), 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), sizeof(ty)))
JNIEXPORT void JNICALL
Java_go_Seq_writeBool(JNIEnv *env, jobject obj, jboolean v) {
MEM_WRITE(int8_t) = v ? 1 : 0;
}
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, 1));
}
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);
}
JNIEXPORT void JNICALL
Java_go_Seq_setContext(JNIEnv* env, jclass clazz, jobject ctx) {
JavaVM* vm;
if ((*env)->GetJavaVM(env, &vm) != 0) {
LOG_FATAL("failed to get JavaVM");
}
setContext(vm, (*env)->NewGlobalRef(env, ctx));
}