[Android/Api] invalid data conversion
authorJaeyun <jy1210.jung@samsung.com>
Fri, 26 Jul 2019 11:14:29 +0000 (20:14 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 1 Aug 2019 05:40:25 +0000 (14:40 +0900)
1. Fix wrong conversion of byte buffer in native.
2. Add methods to allocate buffer with native byte order.
3. Fix invalid condition to close handle.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
api/android/api/jni/nnstreamer-native-api.c
api/android/api/src/com/samsung/android/nnstreamer/Pipeline.java
api/android/api/src/com/samsung/android/nnstreamer/SingleShot.java
api/android/api/src/com/samsung/android/nnstreamer/TensorsData.java
api/android/sample/src/main/java/com/samsung/android/nnstreamer/sample/MainActivity.java

index 95e18f8..2cbe4d7 100644 (file)
@@ -138,20 +138,21 @@ nns_destroy_pipe_info (pipeline_info_s * pipe_info, JNIEnv * env)
 {
   g_assert (pipe_info);
 
+  g_mutex_lock (&pipe_info->lock);
+  g_hash_table_destroy (pipe_info->element_handles);
+  pipe_info->element_handles = NULL;
+  g_mutex_unlock (&pipe_info->lock);
+
   if (g_str_equal (pipe_info->pipeline_type, NNS_PIPE_TYPE_PIPELINE)) {
     ml_pipeline_destroy (pipe_info->pipeline_handle);
   } else if (g_str_equal (pipe_info->pipeline_type, NNS_PIPE_TYPE_SINGLE)) {
     ml_single_close (pipe_info->pipeline_handle);
   } else {
     nns_logw ("Given pipe type %s is unknown.", pipe_info->pipeline_type);
-    g_free (pipe_info->pipeline_handle);
+    if (pipe_info->pipeline_handle)
+      g_free (pipe_info->pipeline_handle);
   }
 
-  g_mutex_lock (&pipe_info->lock);
-  g_hash_table_destroy (pipe_info->element_handles);
-  pipe_info->element_handles = NULL;
-  g_mutex_unlock (&pipe_info->lock);
-
   g_mutex_clear (&pipe_info->lock);
 
   (*env)->DeleteGlobalRef (env, pipe_info->instance);
@@ -232,7 +233,7 @@ nns_convert_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
 
   /* method to generate tensors data */
   jmethodID mid_init = (*env)->GetMethodID (env, pipe_info->cls_tensors_data, "<init>", "()V");
-  jmethodID mid_add = (*env)->GetMethodID (env, pipe_info->cls_tensors_data, "addTensorData", "(Ljava/lang/Object;)V");
+  jmethodID mid_add = (*env)->GetMethodID (env, pipe_info->cls_tensors_data, "addTensorData", "([B)V");
 
   jobject obj_data = (*env)->NewObject (env, pipe_info->cls_tensors_data, mid_init);
   if (!obj_data) {
@@ -241,11 +242,13 @@ nns_convert_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
   }
 
   for (i = 0; i < data->num_tensors; i++) {
-    jobject item = (*env)->NewDirectByteBuffer (env, data->tensors[i].tensor,
-        (jlong) data->tensors[i].size);
+    jsize buffer_size = (jsize) data->tensors[i].size;
+    jbyteArray buffer = (*env)->NewByteArray (env, buffer_size);
 
-    (*env)->CallVoidMethod (env, obj_data, mid_add, item);
-    (*env)->DeleteLocalRef (env, item);
+    (*env)->SetByteArrayRegion (env, buffer, 0, buffer_size, (jbyte *) data->tensors[i].tensor);
+
+    (*env)->CallVoidMethod (env, obj_data, mid_add, buffer);
+    (*env)->DeleteLocalRef (env, buffer);
   }
 
 done:
@@ -287,6 +290,12 @@ nns_parse_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
       gpointer data_ptr = (*env)->GetDirectBufferAddress (env, tensor_data);
 
       data->tensors[i].tensor = g_malloc (data_size);
+      if (!data->tensors[i].tensor) {
+        nns_loge ("Failed to allocate memory %zd, data index %d.", data_size, i);
+        (*env)->DeleteLocalRef (env, tensor_data);
+        goto failed;
+      }
+
       memcpy (data->tensors[i].tensor, data_ptr, data_size);
       data->tensors[i].size = data_size;
 
@@ -300,6 +309,19 @@ nns_parse_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
   (*env)->DeleteLocalRef (env, cls_arraylist);
   (*env)->DeleteLocalRef (env, obj_arraylist);
   return TRUE;
+
+failed:
+  for (i = 0; i < data->num_tensors; i++) {
+    if (data->tensors[i].tensor) {
+      g_free (data->tensors[i].tensor);
+      data->tensors[i].tensor = NULL;
+    }
+
+    data->tensors[i].size = 0;
+  }
+
+  data->num_tensors = 0;
+  return FALSE;
 }
 
 /**
index f820c93..6acfffa 100644 (file)
@@ -376,7 +376,7 @@ public final class Pipeline implements AutoCloseable {
             mStateCallback = null;
         }
 
-        if (mHandle > 0) {
+        if (mHandle != 0) {
             nativeDestroy(mHandle);
             mHandle = 0;
         }
index a0ff012..ef1e476 100644 (file)
@@ -165,7 +165,7 @@ public final class SingleShot implements AutoCloseable {
 
     @Override
     public void close() {
-        if (mHandle > 0) {
+        if (mHandle != 0) {
             nativeClose(mHandle);
             mHandle = 0;
         }
index bf8049e..b3f2972 100644 (file)
@@ -28,6 +28,78 @@ public final class TensorsData implements AutoCloseable {
     private ArrayList<ByteBuffer> mDataList = new ArrayList<>();
 
     /**
+     * Allocates a new direct byte buffer with the native byte order.
+     *
+     * @param size The byte size of the buffer
+     *
+     * @return The new byte buffer
+     */
+    public static ByteBuffer allocateByteBuffer(int size) {
+        ByteBuffer buffer = ByteBuffer.allocateDirect(size);
+
+        buffer.order(ByteOrder.nativeOrder());
+
+        return buffer;
+    }
+
+    /**
+     * Allocates a new <code>TensorsData</code> instance with the given tensors information.
+     *
+     * @param info The tensors information
+     *
+     * @return The allocated tensors data instance
+     *
+     * @throws IllegalArgumentException if given param is invalid
+     */
+    public static TensorsData allocate(@NonNull TensorsInfo info) {
+        if (info == null) {
+            throw new IllegalArgumentException("The param info is null");
+        }
+
+        TensorsData data = new TensorsData();
+        int count = info.getTensorsCount();
+
+        for (int i = 0; i < count; i++) {
+            int type = info.getTesorType(i);
+            int[] dimension = info.getTesorDimension(i);
+
+            int size = 0;
+
+            switch (type) {
+                case NNStreamer.TENSOR_TYPE_INT32:
+                case NNStreamer.TENSOR_TYPE_UINT32:
+                case NNStreamer.TENSOR_TYPE_FLOAT32:
+                    size = 4;
+                    break;
+                case NNStreamer.TENSOR_TYPE_INT16:
+                case NNStreamer.TENSOR_TYPE_UINT16:
+                    size = 2;
+                    break;
+                case NNStreamer.TENSOR_TYPE_INT8:
+                case NNStreamer.TENSOR_TYPE_UINT8:
+                    size = 1;
+                    break;
+                case NNStreamer.TENSOR_TYPE_FLOAT64:
+                case NNStreamer.TENSOR_TYPE_INT64:
+                case NNStreamer.TENSOR_TYPE_UINT64:
+                    size = 8;
+                    break;
+                default:
+                    /* unknown type */
+                    break;
+            }
+
+            for (int j = 0; j < NNStreamer.TENSOR_RANK_LIMIT; j++) {
+                size *= dimension[j];
+            }
+
+            data.addTensorData(allocateByteBuffer(size));
+        }
+
+        return data;
+    }
+
+    /**
      * Gets the number of tensors in tensors data.
      *
      * @return The number of tensors
@@ -41,7 +113,7 @@ public final class TensorsData implements AutoCloseable {
      *
      * @param data The data object to be added
      *
-     * @throws IllegalArgumentException if the data is not a byte buffer
+     * @throws IllegalArgumentException if the data is not a byte buffer or the buffer is invalid
      * @throws IndexOutOfBoundsException when the maximum number of tensors in the list
      */
     public void addTensorData(@NonNull Object data) {
@@ -55,18 +127,40 @@ public final class TensorsData implements AutoCloseable {
     /**
      * Adds a new tensor data.
      *
+     * @param data The byte array to be added
+     *
+     * @throws IllegalArgumentException if given data is invalid
+     * @throws IndexOutOfBoundsException when the maximum number of tensors in the list
+     */
+    public void addTensorData(@NonNull byte[] data) {
+        if (data == null) {
+            throw new IllegalArgumentException("Given data is null");
+        }
+
+        ByteBuffer buffer = allocateByteBuffer(data.length);
+        buffer.put(data);
+
+        addTensorData(buffer);
+    }
+
+    /**
+     * Adds a new tensor data.
+     *
      * @param data The tensor data to be added
      *
+     * @throws IllegalArgumentException if given data is invalid
      * @throws IndexOutOfBoundsException when the maximum number of tensors in the list
      */
     public void addTensorData(@NonNull ByteBuffer data) {
+        checkByteBuffer(data);
+
         int index = getTensorsCount();
 
         if (index >= NNStreamer.TENSOR_SIZE_LIMIT) {
             throw new IndexOutOfBoundsException("Max size of the tensors is " + NNStreamer.TENSOR_SIZE_LIMIT);
         }
 
-        mDataList.add(convertBuffer(data));
+        mDataList.add(data);
     }
 
     /**
@@ -90,10 +184,13 @@ public final class TensorsData implements AutoCloseable {
      * @param data  The tensor data
      *
      * @throws IndexOutOfBoundsException if the given index is invalid
+     * @throws IllegalArgumentException if given data is invalid
      */
     public void setTensorData(int index, @NonNull ByteBuffer data) {
         checkIndexBounds(index);
-        mDataList.set(index, convertBuffer(data));
+        checkByteBuffer(data);
+
+        mDataList.set(index, data);
     }
 
     /**
@@ -108,29 +205,23 @@ public final class TensorsData implements AutoCloseable {
     }
 
     /**
-     * Internal method to convert the given data to direct buffer.
-     *
-     * @param data The tensor data
+     * Internal method to check byte buffer.
      *
-     * @return The converted buffer
-     *
-     * @throws IllegalArgumentException if given data is null
+     * @throws IllegalArgumentException if given data is invalid
      */
-    private ByteBuffer convertBuffer(ByteBuffer data) {
+    private void checkByteBuffer(ByteBuffer data) {
         if (data == null) {
             throw new IllegalArgumentException("Given data is null");
         }
 
-        if (data.isDirect() && data.order() == ByteOrder.nativeOrder()) {
-            return data;
+        if (!data.isDirect()) {
+            throw new IllegalArgumentException("Given data is not a direct buffer");
         }
 
-        ByteBuffer allocated = ByteBuffer.allocateDirect(data.capacity());
-
-        allocated.order(ByteOrder.nativeOrder());
-        allocated.put(data);
-
-        return allocated;
+        if (data.order() != ByteOrder.nativeOrder()) {
+            /* Default byte order of ByteBuffer in java is big-endian, it should be a little-endian. */
+            throw new IllegalArgumentException("Given data has invalid byte order");
+        }
     }
 
     @Override
index fc6a40c..21f681e 100644 (file)
@@ -250,11 +250,18 @@ public class MainActivity extends Activity {
         try {
             SingleShot single = new SingleShot(model);
 
+            Log.d(TAG, "Get input tensors info");
+            TensorsInfo inInfo = single.getInputInfo();
+            printTensorsInfo(inInfo);
+
+            Log.d(TAG, "Get output tensors info");
+            TensorsInfo outInfo = single.getOutputInfo();
+            printTensorsInfo(outInfo);
+
             /* single-shot invoke */
             for (int i = 0; i < 15; i++) {
                 /* dummy input */
-                TensorsData in = new TensorsData();
-                in.addTensorData(ByteBuffer.allocateDirect(3 * 224 * 224));
+                TensorsData in = TensorsData.allocate(inInfo);
 
                 Log.d(TAG, "Try to invoke data " + (i + 1));
 
@@ -264,12 +271,6 @@ public class MainActivity extends Activity {
                 Thread.sleep(50);
             }
 
-            Log.d(TAG, "Get input tensors info");
-            printTensorsInfo(single.getInputInfo());
-
-            Log.d(TAG, "Get output tensors info");
-            printTensorsInfo(single.getOutputInfo());
-
             single.close();
         } catch (Exception e) {
             e.printStackTrace();
@@ -339,7 +340,7 @@ public class MainActivity extends Activity {
             for (int i = 0; i < 15; i++) {
                 /* dummy input */
                 TensorsData in = new TensorsData();
-                in.addTensorData(ByteBuffer.allocateDirect(3 * 224 * 224));
+                in.addTensorData(TensorsData.allocateByteBuffer(3 * 224 * 224));
 
                 Log.d(TAG, "Push input data " + (i + 1));
 
@@ -401,7 +402,7 @@ public class MainActivity extends Activity {
             for (int i = 0; i < 15; i++) {
                 /* dummy input */
                 TensorsData in = new TensorsData();
-                in.addTensorData(ByteBuffer.allocateDirect(3 * 100 * 100));
+                in.addTensorData(TensorsData.allocateByteBuffer(3 * 100 * 100));
 
                 Log.d(TAG, "Push input data " + (i + 1));
 
@@ -472,7 +473,7 @@ public class MainActivity extends Activity {
             for (int i = 0; i < 15; i++) {
                 /* dummy input */
                 TensorsData in = new TensorsData();
-                in.addTensorData(ByteBuffer.allocateDirect(3 * 100 * 100));
+                in.addTensorData(TensorsData.allocateByteBuffer(3 * 100 * 100));
 
                 Log.d(TAG, "Push input data " + (i + 1));