github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/bind/java/seq_android.c (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 #include <android/log.h> 6 #include <errno.h> 7 #include <jni.h> 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <unistd.h> 11 #include "seq_android.h" 12 #include "_cgo_export.h" 13 14 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "go/Seq", __VA_ARGS__) 15 #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "go/Seq", __VA_ARGS__) 16 17 static jfieldID memptr_id; 18 static jfieldID receive_refnum_id; 19 static jfieldID receive_code_id; 20 static jfieldID receive_handle_id; 21 22 static jclass jbytearray_clazz; 23 24 // pinned represents a pinned array to be released at the end of Send call. 25 typedef struct pinned { 26 jobject ref; 27 void* ptr; 28 struct pinned* next; 29 } pinned; 30 31 // mem is a simple C equivalent of seq.Buffer. 32 // 33 // Many of the allocations around mem could be avoided to improve 34 // function call performance, but the goal is to start simple. 35 typedef struct mem { 36 uint8_t *buf; 37 uint32_t off; 38 uint32_t len; 39 uint32_t cap; 40 41 // TODO(hyangah): have it as a separate field outside mem? 42 pinned* pinned; 43 } mem; 44 45 // mem_ensure ensures that m has at least size bytes free. 46 // If m is NULL, it is created. 47 static mem *mem_ensure(mem *m, uint32_t size) { 48 if (m == NULL) { 49 m = (mem*)malloc(sizeof(mem)); 50 if (m == NULL) { 51 LOG_FATAL("mem_ensure malloc failed"); 52 } 53 m->cap = 0; 54 m->off = 0; 55 m->len = 0; 56 m->buf = NULL; 57 m->pinned = NULL; 58 } 59 uint32_t cap = m->cap; 60 if (m->cap > m->off+size) { 61 return m; 62 } 63 if (cap == 0) { 64 cap = 64; 65 } 66 // TODO(hyangah): consider less aggressive allocation such as 67 // cap += max(pow2round(size), 64) 68 while (cap < m->off+size) { 69 cap *= 2; 70 } 71 m->buf = (uint8_t*)realloc((void*)m->buf, cap); 72 if (m->buf == NULL) { 73 LOG_FATAL("mem_ensure realloc failed, off=%d, size=%d", m->off, size); 74 } 75 m->cap = cap; 76 return m; 77 } 78 79 static mem *mem_get(JNIEnv *env, jobject obj) { 80 // Storage space for pointer is always 64-bits, even on 32-bit 81 // machines. Cast to uintptr_t to avoid -Wint-to-pointer-cast. 82 return (mem*)(uintptr_t)(*env)->GetLongField(env, obj, memptr_id); 83 } 84 85 static uint32_t align(uint32_t offset, uint32_t alignment) { 86 uint32_t pad = offset % alignment; 87 if (pad > 0) { 88 pad = alignment-pad; 89 } 90 return pad+offset; 91 } 92 93 static uint8_t *mem_read(JNIEnv *env, jobject obj, uint32_t size, uint32_t alignment) { 94 if (size == 0) { 95 return NULL; 96 } 97 mem *m = mem_get(env, obj); 98 if (m == NULL) { 99 LOG_FATAL("mem_read on NULL mem"); 100 } 101 uint32_t offset = align(m->off, alignment); 102 103 if (m->len-offset < size) { 104 LOG_FATAL("short read"); 105 } 106 uint8_t *res = m->buf+offset; 107 m->off = offset+size; 108 return res; 109 } 110 111 uint8_t *mem_write(JNIEnv *env, jobject obj, uint32_t size, uint32_t alignment) { 112 mem *m = mem_get(env, obj); 113 if (m == NULL) { 114 LOG_FATAL("mem_write on NULL mem"); 115 } 116 if (m->off != m->len) { 117 LOG_FATAL("write can only append to seq, size: (off=%d, len=%d, size=%d", m->off, m->len, size); 118 } 119 uint32_t offset = align(m->off, alignment); 120 m = mem_ensure(m, offset - m->off + size); 121 uint8_t *res = m->buf+offset; 122 m->off = offset+size; 123 m->len = offset+size; 124 return res; 125 } 126 127 static void *pin_array(JNIEnv *env, jobject obj, jobject arr) { 128 mem *m = mem_get(env, obj); 129 if (m == NULL) { 130 m = mem_ensure(m, 64); 131 } 132 pinned *p = (pinned*) malloc(sizeof(pinned)); 133 if (p == NULL) { 134 LOG_FATAL("pin_array malloc failed"); 135 } 136 p->ref = (*env)->NewGlobalRef(env, arr); 137 138 if ((*env)->IsInstanceOf(env, p->ref, jbytearray_clazz)) { 139 p->ptr = (*env)->GetByteArrayElements(env, p->ref, NULL); 140 } else { 141 LOG_FATAL("unsupported array type"); 142 } 143 144 p->next = m->pinned; 145 m->pinned = p; 146 return p->ptr; 147 } 148 149 static void unpin_arrays(JNIEnv *env, mem *m) { 150 pinned* p = m->pinned; 151 while (p != NULL) { 152 if ((*env)->IsInstanceOf(env, p->ref, jbytearray_clazz)) { 153 (*env)->ReleaseByteArrayElements(env, p->ref, (jbyte*)p->ptr, JNI_ABORT); 154 } else { 155 LOG_FATAL("invalid array type"); 156 } 157 158 (*env)->DeleteGlobalRef(env, p->ref); 159 160 pinned* o = p; 161 p = p->next; 162 free(o); 163 } 164 m->pinned = NULL; 165 } 166 167 static void describe_exception(JNIEnv* env) { 168 jthrowable exc = (*env)->ExceptionOccurred(env); 169 if (exc) { 170 (*env)->ExceptionDescribe(env); 171 (*env)->ExceptionClear(env); 172 } 173 } 174 175 static jfieldID find_field(JNIEnv *env, const char *class_name, const char *field_name, const char *field_type) { 176 jclass clazz = (*env)->FindClass(env, class_name); 177 if (clazz == NULL) { 178 describe_exception(env); 179 LOG_FATAL("cannot find %s", class_name); 180 return NULL; 181 } 182 jfieldID id = (*env)->GetFieldID(env, clazz, field_name , field_type); 183 if(id == NULL) { 184 describe_exception(env); 185 LOG_FATAL("no %s/%s field", field_name, field_type); 186 return NULL; 187 } 188 return id; 189 } 190 191 static jclass find_class(JNIEnv *env, const char *class_name) { 192 jclass clazz = (*env)->FindClass(env, class_name); 193 if (clazz == NULL) { 194 describe_exception(env); 195 LOG_FATAL("cannot find %s", class_name); 196 return NULL; 197 } 198 return (*env)->NewGlobalRef(env, clazz); 199 } 200 201 JNIEXPORT void JNICALL 202 Java_go_Seq_initSeq(JNIEnv *env, jclass clazz) { 203 memptr_id = find_field(env, "go/Seq", "memptr", "J"); 204 receive_refnum_id = find_field(env, "go/Seq$Receive", "refnum", "I"); 205 receive_handle_id = find_field(env, "go/Seq$Receive", "handle", "I"); 206 receive_code_id = find_field(env, "go/Seq$Receive", "code", "I"); 207 208 jclass bclazz = find_class(env, "[B"); 209 jbytearray_clazz = (*env)->NewGlobalRef(env, bclazz); 210 } 211 212 JNIEXPORT void JNICALL 213 Java_go_Seq_ensure(JNIEnv *env, jobject obj, jint size) { 214 mem *m = mem_get(env, obj); 215 if (m == NULL || m->off+size > m->cap) { 216 m = mem_ensure(m, size); 217 (*env)->SetLongField(env, obj, memptr_id, (jlong)(uintptr_t)m); 218 } 219 } 220 221 JNIEXPORT void JNICALL 222 Java_go_Seq_free(JNIEnv *env, jobject obj) { 223 mem *m = mem_get(env, obj); 224 if (m != NULL) { 225 unpin_arrays(env, m); 226 free((void*)m->buf); 227 free((void*)m); 228 } 229 } 230 231 #define MEM_READ(obj, ty) ((ty*)mem_read(env, obj, sizeof(ty), sizeof(ty))) 232 233 JNIEXPORT jboolean JNICALL 234 Java_go_Seq_readBool(JNIEnv *env, jobject obj) { 235 int8_t *v = MEM_READ(obj, int8_t); 236 if (v == NULL) { 237 return 0; 238 } 239 return *v != 0 ? 1 : 0; 240 } 241 242 JNIEXPORT jbyte JNICALL 243 Java_go_Seq_readInt8(JNIEnv *env, jobject obj) { 244 uint8_t *v = MEM_READ(obj, uint8_t); 245 if (v == NULL) { 246 return 0; 247 } 248 return *v; 249 } 250 251 JNIEXPORT jshort JNICALL 252 Java_go_Seq_readInt16(JNIEnv *env, jobject obj) { 253 int16_t *v = MEM_READ(obj, int16_t); 254 return v == NULL ? 0 : *v; 255 } 256 257 JNIEXPORT jint JNICALL 258 Java_go_Seq_readInt32(JNIEnv *env, jobject obj) { 259 int32_t *v = MEM_READ(obj, int32_t); 260 return v == NULL ? 0 : *v; 261 } 262 263 JNIEXPORT jlong JNICALL 264 Java_go_Seq_readInt64(JNIEnv *env, jobject obj) { 265 int64_t *v = MEM_READ(obj, int64_t); 266 return v == NULL ? 0 : *v; 267 } 268 269 JNIEXPORT jfloat JNICALL 270 Java_go_Seq_readFloat32(JNIEnv *env, jobject obj) { 271 float *v = MEM_READ(obj, float); 272 return v == NULL ? 0 : *v; 273 } 274 275 JNIEXPORT jdouble JNICALL 276 Java_go_Seq_readFloat64(JNIEnv *env, jobject obj) { 277 double *v = MEM_READ(obj, double); 278 return v == NULL ? 0 : *v; 279 } 280 281 JNIEXPORT jstring JNICALL 282 Java_go_Seq_readUTF16(JNIEnv *env, jobject obj) { 283 int32_t size = *MEM_READ(obj, int32_t); 284 if (size == 0) { 285 return NULL; 286 } 287 return (*env)->NewString(env, (jchar*)mem_read(env, obj, 2*size, 1), size); 288 } 289 290 JNIEXPORT jbyteArray JNICALL 291 Java_go_Seq_readByteArray(JNIEnv *env, jobject obj) { 292 // Send the (array length, pointer) pair encoded as two int64. 293 // The pointer value is omitted if array length is 0. 294 jlong size = Java_go_Seq_readInt64(env, obj); 295 if (size == 0) { 296 return NULL; 297 } 298 jbyteArray res = (*env)->NewByteArray(env, size); 299 jlong ptr = Java_go_Seq_readInt64(env, obj); 300 (*env)->SetByteArrayRegion(env, res, 0, size, (jbyte*)(intptr_t)(ptr)); 301 return res; 302 } 303 304 #define MEM_WRITE(ty) (*(ty*)mem_write(env, obj, sizeof(ty), sizeof(ty))) 305 306 JNIEXPORT void JNICALL 307 Java_go_Seq_writeBool(JNIEnv *env, jobject obj, jboolean v) { 308 MEM_WRITE(int8_t) = v ? 1 : 0; 309 } 310 311 JNIEXPORT void JNICALL 312 Java_go_Seq_writeInt8(JNIEnv *env, jobject obj, jbyte v) { 313 MEM_WRITE(int8_t) = v; 314 } 315 316 JNIEXPORT void JNICALL 317 Java_go_Seq_writeInt16(JNIEnv *env, jobject obj, jshort v) { 318 MEM_WRITE(int16_t) = v; 319 } 320 321 JNIEXPORT void JNICALL 322 Java_go_Seq_writeInt32(JNIEnv *env, jobject obj, jint v) { 323 MEM_WRITE(int32_t) = v; 324 } 325 326 JNIEXPORT void JNICALL 327 Java_go_Seq_writeInt64(JNIEnv *env, jobject obj, jlong v) { 328 MEM_WRITE(int64_t) = v; 329 } 330 331 JNIEXPORT void JNICALL 332 Java_go_Seq_writeFloat32(JNIEnv *env, jobject obj, jfloat v) { 333 MEM_WRITE(float) = v; 334 } 335 336 JNIEXPORT void JNICALL 337 Java_go_Seq_writeFloat64(JNIEnv *env, jobject obj, jdouble v) { 338 MEM_WRITE(double) = v; 339 } 340 341 JNIEXPORT void JNICALL 342 Java_go_Seq_writeUTF16(JNIEnv *env, jobject obj, jstring v) { 343 if (v == NULL) { 344 MEM_WRITE(int32_t) = 0; 345 return; 346 } 347 int32_t size = (*env)->GetStringLength(env, v); 348 MEM_WRITE(int32_t) = size; 349 (*env)->GetStringRegion(env, v, 0, size, (jchar*)mem_write(env, obj, 2*size, 1)); 350 } 351 352 JNIEXPORT void JNICALL 353 Java_go_Seq_writeByteArray(JNIEnv *env, jobject obj, jbyteArray v) { 354 // For Byte array, we pass only the (array length, pointer) pair 355 // encoded as two int64 values. If the array length is 0, 356 // the pointer value is omitted. 357 if (v == NULL) { 358 MEM_WRITE(int64_t) = 0; 359 return; 360 } 361 362 jsize len = (*env)->GetArrayLength(env, v); 363 MEM_WRITE(int64_t) = len; 364 if (len == 0) { 365 return; 366 } 367 368 jbyte* b = pin_array(env, obj, v); 369 MEM_WRITE(int64_t) = (jlong)(uintptr_t)b; 370 } 371 372 JNIEXPORT void JNICALL 373 Java_go_Seq_resetOffset(JNIEnv *env, jobject obj) { 374 mem *m = mem_get(env, obj); 375 if (m == NULL) { 376 LOG_FATAL("resetOffset on NULL mem"); 377 } 378 m->off = 0; 379 } 380 381 JNIEXPORT void JNICALL 382 Java_go_Seq_log(JNIEnv *env, jobject obj, jstring v) { 383 mem *m = mem_get(env, obj); 384 const char *label = (*env)->GetStringUTFChars(env, v, NULL); 385 if (label == NULL) { 386 LOG_FATAL("log GetStringUTFChars failed"); 387 } 388 if (m == NULL) { 389 LOG_INFO("%s: mem=NULL", label); 390 } else { 391 LOG_INFO("%s: mem{off=%d, len=%d, cap=%d}", label, m->off, m->len, m->cap); 392 } 393 (*env)->ReleaseStringUTFChars(env, v, label); 394 } 395 396 JNIEXPORT void JNICALL 397 Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) { 398 DestroyRef(refnum); 399 } 400 401 JNIEXPORT void JNICALL 402 Java_go_Seq_send(JNIEnv *env, jclass clazz, jstring descriptor, jint code, jobject src_obj, jobject dst_obj) { 403 mem *src = mem_get(env, src_obj); 404 if (src == NULL) { 405 LOG_FATAL("send src is NULL"); 406 } 407 mem *dst = mem_get(env, dst_obj); 408 if (dst == NULL) { 409 LOG_FATAL("send dst is NULL"); 410 } 411 412 GoString desc; 413 desc.p = (char*)(*env)->GetStringUTFChars(env, descriptor, NULL); 414 if (desc.p == NULL) { 415 LOG_FATAL("send GetStringUTFChars failed"); 416 } 417 desc.n = (*env)->GetStringUTFLength(env, descriptor); 418 Send(desc, (GoInt)code, src->buf, src->len, &dst->buf, &dst->len); 419 (*env)->ReleaseStringUTFChars(env, descriptor, desc.p); 420 unpin_arrays(env, src); // assume 'src' is no longer needed. 421 } 422 423 JNIEXPORT void JNICALL 424 Java_go_Seq_recv(JNIEnv *env, jclass clazz, jobject in_obj, jobject receive) { 425 mem *in = mem_get(env, in_obj); 426 if (in == NULL) { 427 LOG_FATAL("recv in is NULL"); 428 } 429 struct Recv_return ret = Recv(&in->buf, &in->len); 430 (*env)->SetIntField(env, receive, receive_refnum_id, ret.r0); 431 (*env)->SetIntField(env, receive, receive_code_id, ret.r1); 432 (*env)->SetIntField(env, receive, receive_handle_id, ret.r2); 433 } 434 435 JNIEXPORT void JNICALL 436 Java_go_Seq_recvRes(JNIEnv *env, jclass clazz, jint handle, jobject out_obj) { 437 mem *out = mem_get(env, out_obj); 438 if (out == NULL) { 439 LOG_FATAL("recvRes out is NULL"); 440 } 441 RecvRes((int32_t)handle, out->buf, out->len); 442 } 443 444 JNIEXPORT void JNICALL 445 Java_go_Seq_setContext(JNIEnv* env, jclass clazz, jobject ctx) { 446 JavaVM* vm; 447 if ((*env)->GetJavaVM(env, &vm) != 0) { 448 LOG_FATAL("failed to get JavaVM"); 449 } 450 setContext(vm, (*env)->NewGlobalRef(env, ctx)); 451 }