From 54b6cfa9a9e5b861a9930af873580d6dc20f773c Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: Initial Contribution --- core/jni/android_database_CursorWindow.cpp | 674 +++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 core/jni/android_database_CursorWindow.cpp (limited to 'core/jni/android_database_CursorWindow.cpp') diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp new file mode 100644 index 0000000..f19fbbf --- /dev/null +++ b/core/jni/android_database_CursorWindow.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CursorWindow" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "CursorWindow.h" +#include "sqlite3_exception.h" +#include "android_util_Binder.h" + + +namespace android { + +static jfieldID gWindowField; +static jfieldID gBufferField; +static jfieldID gSizeCopiedField; + +#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField)) +#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) +#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf)) +#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size)) + +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow) +{ + return GET_WINDOW(env, javaWindow); +} + +static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly) +{ + uint8_t * data; + size_t size; + CursorWindow * window; + + window = new CursorWindow(MAX_WINDOW_SIZE); + if (!window) { + jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object"); + return; + } + + if (!window->initBuffer(localOnly)) { + jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window"); + delete window; + return; + } + +LOG_WINDOW("native_init_empty: window = %p", window); + SET_WINDOW(env, object, window); +} + +static void native_init_memory(JNIEnv * env, jobject object, jobject memObj) +{ + sp memory = interface_cast(ibinderForJavaObject(env, memObj)); + if (memory == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); + return; + } + + CursorWindow * window = new CursorWindow(); + if (!window) { + jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object"); + return; + } + if (!window->setMemory(memory)) { + jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj"); + delete window; + return; + } + +LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window); + SET_WINDOW(env, object, window); +} + +static jobject native_getBinder(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (window) { + sp memory = window->getMemory(); + if (memory != NULL) { + sp binder = memory->asBinder(); + return javaObjectForIBinder(env, binder); + } + } + return NULL; +} + +static void native_clear(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Clearing window %p", window); + if (window == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); + return; + } + window->clear(); +} + +static void native_close(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (window) { +LOG_WINDOW("Closing window %p", window); + delete window; + SET_WINDOW(env, object, 0); + } +} + +static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column) +{ + char buf[100]; + snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column); + jniThrowException(env, "java/lang/IllegalStateException", buf); +} + +static void throwUnknowTypeException(JNIEnv * env, jint type) +{ + char buf[80]; + snprintf(buf, sizeof(buf), "UNKNOWN type %d", type); + jniThrowException(env, "java/lang/IllegalStateException", buf); +} + +static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return 0; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0); +#else + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtoll(str, NULL, 0); +#endif + } else { + return 0; + } + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_NULL) { + return 0; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to long"); + return 0; + } else { + throwUnknowTypeException(env, type); + return 0; + } +} + +static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { + jbyteArray byteArray = env->NewByteArray(field.data.buffer.size); + LOG_ASSERT(byteArray, "Native could not create new byte[]"); + env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size, + (const jbyte*)window->offsetToPtr(field.data.buffer.offset)); + return byteArray; + } else if (type == FIELD_TYPE_INTEGER) { + throw_sqlite3_exception(env, "INTEGER data in getBlob_native "); + } else if (type == FIELD_TYPE_FLOAT) { + throw_sqlite3_exception(env, "FLOAT data in getBlob_native "); + } else if (type == FIELD_TYPE_NULL) { + // do nothing + } else { + throwUnknowTypeException(env, type); + } + return NULL; +} + +static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL; +} + +static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + return env->NewString((jchar const *)utf16.string(), utf16.size()); +#else + return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); +#endif + } else { + return env->NewStringUTF(""); + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", value); + return env->NewStringUTF(buf); + } + return NULL; + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%g", value); + return env->NewStringUTF(buf); + } + return NULL; + } else if (type == FIELD_TYPE_NULL) { + return NULL; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to string"); + return NULL; + } else { + throwUnknowTypeException(env, type); + return NULL; + } +} + +/** + * Use this only to convert characters that are known to be within the + * 0-127 range for direct conversion to UTF-16 + */ +static jint charToJchar(const char* src, jchar* dst, jint bufferSize) +{ + int32_t len = strlen(src); + + if (bufferSize < len) { + len = bufferSize; + } + + for (int i = 0; i < len; i++) { + *dst++ = (*src++ & 0x7F); + } + return len; +} + +static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row, + jint column, jint bufferSize, jobject buf) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); + return NULL; + } + + jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); + return NULL; + } + jchar* dst = env->GetCharArrayElements(buffer, NULL); + uint8_t type = field.type; + uint32_t sizeCopied = 0; + jcharArray newArray = NULL; + if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + int32_t strSize = utf16.size(); + if (strSize > bufferSize || dst == NULL) { + newArray = env->NewCharArray(strSize); + env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); + } else { + memcpy(dst, (jchar const *)utf16.string(), strSize * 2); + } + sizeCopied = strSize; +#else + sizeCopied = size/2 + size % 2; + if (size > bufferSize * 2 || dst == NULL) { + newArray = env->NewCharArray(sizeCopied); + memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } else { + memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } +#endif + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + int len; + snprintf(buf, sizeof(buf), "%lld", value); + jchar* dst = env->GetCharArrayElements(buffer, NULL); + sizeCopied = charToJchar(buf, dst, bufferSize); + } + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + char tempbuf[32]; + snprintf(tempbuf, sizeof(tempbuf), "%g", value); + jchar* dst = env->GetCharArrayElements(buffer, NULL); + sizeCopied = charToJchar(tempbuf, dst, bufferSize); + } + } else if (type == FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to string"); + } else { + LOGE("Unknown field type %d", type); + throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()"); + } + SET_SIZE_COPIED(env, buf, sizeCopied); + env->ReleaseCharArrayElements(buffer, dst, JNI_OK); + return newArray; +} + +static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return 0.0; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0.0; + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL); +#else + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtod(str, NULL); +#endif + } else { + return 0.0; + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + return (double) value; + } + return 0.0; + } else if (type == FIELD_TYPE_NULL) { + return 0.0; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to double"); + return 0.0; + } else { + throwUnknowTypeException(env, type); + return 0.0; + } +} + +static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column) +{ + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); + + bool isNull; + if (window->getNull(row, column, &isNull)) { + return isNull; + } + + //TODO throw execption? + return true; +} + +static jint getNumRows(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->getNumRows(); +} + +static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->setNumColumns(columnNum); +} + +static jboolean allocRow(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->allocRow() != NULL; +} + +static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + jint len = env->GetArrayLength(value); + int offset = window->alloc(len); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", len); + return false; + } + jbyte * bytes = env->GetByteArrayElements(value, NULL); + window->copyIn(offset, (uint8_t const *)bytes, len); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); + LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset); + return true; +} + +static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + +#if WINDOW_STORAGE_UTF8 + int len = env->GetStringUTFLength(value) + 1; + char const * valStr = env->GetStringUTFChars(value, NULL); +#else + int len = env->GetStringLength(value); + // GetStringLength return number of chars and one char takes 2 bytes + len *= 2; + const jchar* valStr = env->GetStringChars(value, NULL); +#endif + if (!valStr) { + LOG_WINDOW("value can't be transfer to UTFChars"); + return false; + } + + int offset = window->alloc(len); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", len); +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(value, valStr); +#else + env->ReleaseStringChars(value, valStr); +#endif + return false; + } + + window->copyIn(offset, (uint8_t const *)valStr, len); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + + LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset); +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(value, valStr); +#else + env->ReleaseStringChars(value, valStr); +#endif + + return true; +} + +static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putLong(row, col, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value); + + return true; +} + +static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putDouble(row, col, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value); + + return true; +} + +static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putNull(row, col)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is NULL", row, col); + + return true; +} + +// free the last row +static void freeLastRow(JNIEnv * env, jobject object) { + CursorWindow * window = GET_WINDOW(env, object); + window->freeLastRow(); +} + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_init", "(Z)V", (void *)native_init_empty}, + {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory}, + {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder}, + {"native_clear", "()V", (void *)native_clear}, + {"close_native", "()V", (void *)native_close}, + {"getLong_native", "(II)J", (void *)getLong_native}, + {"getBlob_native", "(II)[B", (void *)getBlob_native}, + {"isBlob_native", "(II)Z", (void *)isBlob_native}, + {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native}, + {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native}, + {"getDouble_native", "(II)D", (void *)getDouble_native}, + {"isNull_native", "(II)Z", (void *)isNull_native}, + {"getNumRows_native", "()I", (void *)getNumRows}, + {"setNumColumns_native", "(I)Z", (void *)setNumColumns}, + {"allocRow_native", "()Z", (void *)allocRow}, + {"putBlob_native", "([BII)Z", (void *)putBlob_native}, + {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native}, + {"putLong_native", "(JII)Z", (void *)putLong_native}, + {"putDouble_native", "(DII)Z", (void *)putDouble_native}, + {"freeLastRow_native", "()V", (void *)freeLastRow}, + {"putNull_native", "(II)Z", (void *)putNull_native}, +}; + +int register_android_database_CursorWindow(JNIEnv * env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/CursorWindow"); + if (clazz == NULL) { + LOGE("Can't find android/database/CursorWindow"); + return -1; + } + + gWindowField = env->GetFieldID(clazz, "nWindow", "I"); + + if (gWindowField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + clazz = env->FindClass("android/database/CharArrayBuffer"); + if (clazz == NULL) { + LOGE("Can't find android/database/CharArrayBuffer"); + return -1; + } + + gBufferField = env->GetFieldID(clazz, "data", "[C"); + + if (gBufferField == NULL) { + LOGE("Error locating fields data in CharArrayBuffer"); + return -1; + } + + gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I"); + + if (gSizeCopiedField == NULL) { + LOGE("Error locating fields sizeCopied in CharArrayBuffer"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", + sMethods, NELEM(sMethods)); +} + +} // namespace android -- cgit v1.1