Optimization of TableEnumerator/MultiWrite

Change-Id: Iefc6cb15d6ee2a16b288a867499d79802320df09
diff --git a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
index fe61007..4694c4e 100644
--- a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
+++ b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
@@ -340,12 +340,11 @@
 
     const static jmethodID methodId = env->GetMethodID(cls,
                                           "<init>",
-                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                          "([B[BJ)V");
     check_null(methodId, "GetMethodID failed");
 
     return env->NewObject(cls,
                           methodId,
-                          jRamCloud,
                           jKey,
                           jValue,
                           static_cast<jlong>(version));
@@ -377,7 +376,7 @@
                                                         jobject jRamCloud,
 							jlongArray jTableId,
 							jobjectArray jKeyData,
-							jshortArray jKeyDataSize,
+							jshortArray jKeyLength,
 							jint jrequestNum){
 
     RamCloud* ramcloud = getRamCloud(env, jRamCloud);
@@ -386,22 +385,22 @@
     jbyteArray jKey[jrequestNum];
     MultiReadObject* requests[jrequestNum];
 
-    jlong TableId;
-    jshort KeyDataSize;
-    jbyte* data[jrequestNum];
+    jlong tableId;
+    jshort keyLength;
+    jbyte* keyData[jrequestNum];
 
     for (int i = 0 ; i < jrequestNum ; i++){
         jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i); 
 
-        env->GetShortArrayRegion(jKeyDataSize, i, 1, &KeyDataSize);
+        env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
 
-        data[i] = (jbyte *) malloc(KeyDataSize);
-        env->GetByteArrayRegion(jKey[i], 0, KeyDataSize, data[i]);
+        keyData[i] = (jbyte *) malloc(keyLength);
+        env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
 
-        env->GetLongArrayRegion(jTableId, i, 1, &TableId);
-        objects[i].tableId = TableId;
-        objects[i].key = data[i];
-	objects[i].keyLength = KeyDataSize;
+        env->GetLongArrayRegion(jTableId, i, 1, &tableId);
+        objects[i].tableId = tableId;
+        objects[i].key = keyData[i];
+	objects[i].keyLength = keyLength;
         objects[i].value = &values[i];
         requests[i] = &objects[i];
     }
@@ -414,7 +413,7 @@
     check_null(jc_RcObject, "FindClass failed");
     const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
                                         "<init>",
-                                        "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                        "([B[BJ)V");
 
     jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
@@ -425,11 +424,11 @@
 	    check_null(jValue, "NewByteArray failed");
 	    JByteArrayGetter value(env, jValue);
 	    values[i].get()->copy(0, values[i].get()->getTotalLength(), value.pointer);
-	    jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, jKey[i], jValue);
+	    jobject obj = env->NewObject(jc_RcObject, jm_init, jKey[i], jValue);
 	    check_null(obj, "NewObject failed");
 	    env->SetObjectArrayElement(outJNIArray, i, obj);
 	}
-	free(data[i]);
+	free(keyData[i]);
     }
     return outJNIArray;
 }
@@ -672,11 +671,10 @@
         check_null(cls, "FindClass failed");
         const static jmethodID methodId = env->GetMethodID(cls,
                                           "<init>",
-                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                          "([B[BJ)V");
         check_null(methodId, "GetMethodID failed");
         return env->NewObject(cls,
                               methodId,
-                              jTableEnumerator,
                               jKey,
                               jValue,
                               static_cast<jlong>(version));        
@@ -686,24 +684,123 @@
 
 /*
  * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    getTableObjects
+ * Signature: (JJ)Ledu/stanford/ramcloud/JRamCloud/TableEnumeratorObject;
+ */
+JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_getTableObjects(JNIEnv *env,
+                                                                               jobject jRamCloud,
+                                                                               jlong jTableId,
+                                                                               jlong jTabletNextHash){
+
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+
+    Buffer state;
+    Buffer objects;
+    bool done = false;
+    uint64_t version = 0;
+
+    uint32_t nextOffset = 0;
+
+    const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$Object"));
+    check_null(jc_RcObject, "FindClass failed");
+    const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
+                                        "<init>",
+                                        "([B[BJ)V");
+    check_null(jm_init, "GetMethodID failed");
+    
+    const static jclass jc_RcTableObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$TableEnumeratorObject"));
+    check_null(jc_RcTableObject, "FindClass failed");
+    const static jmethodID jm_TableEnumeratorObject_init = env->GetMethodID(jc_RcTableObject,
+                                        "<init>",
+                                        "([Ledu/stanford/ramcloud/JRamCloud$Object;J)V");
+    check_null(jm_TableEnumeratorObject_init, "GetMethodID failed");
+
+    
+    while (true) {
+        jTabletNextHash = ramcloud->enumerateTable(jTableId, jTabletNextHash, state, objects);
+        if (objects.getTotalLength() > 0) {
+            break;
+        }
+        if (objects.getTotalLength() == 0 && jTabletNextHash == 0) {
+            done = true;
+            break;
+        }    
+    }
+    
+    if (done) {
+        return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, env->NewObjectArray(0, jc_RcObject , NULL), 0);
+    }
+    
+    int numOfTable;
+    for (numOfTable = 0; nextOffset < objects.getTotalLength() ; numOfTable++) {
+        uint32_t objectSize = *objects.getOffset<uint32_t>(nextOffset);
+        nextOffset += downCast<uint32_t>(sizeof(uint32_t));
+        nextOffset += objectSize;
+    }
+    
+    jobjectArray outJNIArray = env->NewObjectArray(numOfTable, jc_RcObject , NULL);
+    check_null(outJNIArray, "NewObjectArray failed");
+
+    nextOffset = 0;
+    for (int i = 0; nextOffset < objects.getTotalLength() ;i++) {
+        uint32_t objectSize = *objects.getOffset<uint32_t>(nextOffset);
+        nextOffset += downCast<uint32_t>(sizeof(uint32_t));
+
+        const void* blob = objects.getRange(nextOffset, objectSize);
+        nextOffset += objectSize;
+
+        Object object(blob, objectSize);
+
+        jbyteArray jKey = env->NewByteArray(object.getKeyLength());
+        jbyteArray jValue = env->NewByteArray(object.getDataLength());
+
+        JByteArrayGetter key(env, jKey);
+        JByteArrayGetter value(env, jValue);
+
+        memcpy(key.pointer, object.getKey(), object.getKeyLength());
+        memcpy(value.pointer, object.getData(), object.getDataLength());
+
+        version = object.getVersion();
+
+        jobject obj = env->NewObject(jc_RcObject, jm_init, jKey, jValue, static_cast<jlong>(version));
+        check_null(obj, "NewObject failed");
+
+        env->SetObjectArrayElement(outJNIArray, i, obj);
+    }
+
+    return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, outJNIArray, jTabletNextHash);
+
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
  * Method:    multiWrite
  * Signature: ([Ledu/stanford/ramcloud/JRamCloud/MultiWriteObject;)[Ledu/stanford/ramcloud/JRamCloud/MultiWriteRspObject;
  */
-JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, jobject jRamCloud, jobjectArray jmultiWriteArray) {
-    jint requestNum = env->GetArrayLength(jmultiWriteArray);
-    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
-    Tub<MultiWriteObject> objects[requestNum];
-    MultiWriteObject *requests[requestNum];
-    RejectRules rules[requestNum];
-    jbyteArray jKey[requestNum];
-    jbyteArray jValue[requestNum];
+JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, 
+	                                                                       jobject jRamCloud,
+	                                                                       jlongArray jTableId,
+	                                                                       jobjectArray jKeyData,
+	                                                                       jshortArray jKeyLength,
+	                                                                       jobjectArray jValueData,
+	                                                                       jshortArray jValueLength,
+	                                                                       jint jrequestNum,
+	                                                                       jobjectArray jRules ) {
     
-    const static jclass jc_multiWriteObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteObject"));
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    Tub<MultiWriteObject> objects[jrequestNum];
+    MultiWriteObject *requests[jrequestNum];
+    RejectRules rules[jrequestNum];
+    jbyteArray jKey[jrequestNum];
+    jbyteArray jValue[jrequestNum];
+    
+    jlong tableId;
+    jshort keyLength;
+    jshort valueLength;
+    jbyte* keyData[jrequestNum];
+    jbyte* valueData[jrequestNum];
+    
     const static jclass jc_RejectRules = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$RejectRules"));
-    const static jfieldID jf_tableId = env->GetFieldID(jc_multiWriteObject, "tableId", "J");
-    const static jfieldID jf_key = env->GetFieldID(jc_multiWriteObject, "key", "[B");
-    const static jfieldID jf_value = env->GetFieldID(jc_multiWriteObject, "value", "[B");
-    const static jfieldID jf_reject_rules = env->GetFieldID(jc_multiWriteObject, "rules", "L" PACKAGE_PATH "JRamCloud$RejectRules;");
 
     const static jfieldID jf_doesntExist = env->GetFieldID(jc_RejectRules, "doesntExist", "Z");
     check_null(jf_doesntExist, "doesentExist field id is null");
@@ -716,21 +813,20 @@
     const static jfieldID jf_versionNeGiven = env->GetFieldID(jc_RejectRules, "versionNeGiven", "Z");
     check_null(jf_versionNeGiven, "versionNeGiven field id is null");
 
-    for (int i = 0; i < requestNum; i++) {
-        jobject obj = env->GetObjectArrayElement(jmultiWriteArray, i);
-        check_null(obj, "multi write GetObjectArrayElement failed");
+    for (int i = 0; i < jrequestNum; i++) {
+	env->GetLongArrayRegion(jTableId, i, 1, &tableId);
 
-        uint64_t tableId = env->GetLongField(obj, jf_tableId);
-
-        jKey[i] = (jbyteArray)env->GetObjectField(obj, jf_key);
-        jbyte* keyData = env->GetByteArrayElements(jKey[i], NULL);
-        uint16_t keyLength = env->GetArrayLength(jKey[i]);
-
-        jValue[i] = (jbyteArray)env->GetObjectField(obj, jf_value);
-        jbyte* valueData = env->GetByteArrayElements(jValue[i], NULL);
-        uint32_t valueLength = env->GetArrayLength(jValue[i]);
-
-        jobject jRejectRules = env->GetObjectField(obj, jf_reject_rules);
+	env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
+	jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i);
+	keyData[i] = (jbyte *) malloc(keyLength);
+	env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
+	
+	env->GetShortArrayRegion(jValueLength, i, 1, &valueLength);
+        jValue[i] = (jbyteArray)env->GetObjectArrayElement(jValueData, i);
+	valueData[i] = (jbyte *) malloc(valueLength);
+	env->GetByteArrayRegion(jValue[i], 0, valueLength, valueData[i]);
+	
+	jobject jRejectRules = (jbyteArray)env->GetObjectArrayElement(jRules, i);
         rules[i] = {};
 
         if (jRejectRules != NULL) {
@@ -750,28 +846,28 @@
             ruleBool = env->GetBooleanField(jRejectRules, jf_versionNeGiven);
             rules[i].versionNeGiven = ruleBool ? 1 : 0;
         }
-        objects[i].construct(tableId, keyData, keyLength, valueData, valueLength, &rules[i]);
+        objects[i].construct(tableId, keyData[i], keyLength, valueData[i], valueLength, &rules[i]);
         requests[i] = objects[i].get();
     }
     try {
-        ramcloud->multiWrite(requests, requestNum);
+        ramcloud->multiWrite(requests, jrequestNum);
     } EXCEPTION_CATCHER(NULL);
  
     const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteRspObject"));
     check_null(jc_RcObject, "FindClass failed");
     const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
                                         "<init>",
-                                        "(L" PACKAGE_PATH "JRamCloud;IJ)V");
+                                        "(IJ)V");
 
-    jobjectArray outJNIArray = env->NewObjectArray(requestNum, jc_RcObject , NULL);
+    jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
     
-    for (int i = 0 ; i < requestNum ; i++) {
-        jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, objects[i]->status, objects[i]->version);
+    for (int i = 0 ; i < jrequestNum ; i++) {
+        jobject obj = env->NewObject(jc_RcObject, jm_init, objects[i]->status, objects[i]->version);
         check_null(obj, "NewObject failed");
         env->SetObjectArrayElement(outJNIArray, i, obj);
-        env->ReleaseByteArrayElements(jKey[i], (jbyte *)requests[i]->key, JNI_ABORT);
-        env->ReleaseByteArrayElements(jValue[i], (jbyte *)requests[i]->value, JNI_ABORT);
+	free(keyData[i]);
+	free(valueData[i]);
         objects[i].destroy();
     }
     return outJNIArray;