VPP-86: fix array copy in generated JNI code

Change-Id: Ic67b3c0623d98c5ee3f1ffa1e1bd9cfb96b233bd
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
diff --git a/vpp-api/java/jvpp/gen/jvpp_c_gen.py b/vpp-api/java/jvpp/gen/jvpp_c_gen.py
index e277976..c02c826 100644
--- a/vpp-api/java/jvpp/gen/jvpp_c_gen.py
+++ b/vpp-api/java/jvpp/gen/jvpp_c_gen.py
@@ -89,7 +89,8 @@
 u8_array_struct_setter_template = Template("""
     {
         jsize cnt = (*env)->GetArrayLength (env, ${java_name});
-        if (cnt > sizeof(mp->${c_name})) cnt = sizeof(mp->${c_name});
+        size_t max_size = ${field_length};
+        if (max_size != 0 && cnt > max_size) cnt = max_size;
         (*env)->GetByteArrayRegion(env, ${java_name}, 0, cnt, (jbyte *)mp->${c_name});
     }
 """)
@@ -97,8 +98,11 @@
 u32_array_struct_setter_template = Template("""
     jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL);
     {
-        int _i;
-        for (_i = 0; _i < 0; _i++) {
+        size_t _i;
+        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
+        size_t max_size = ${field_length};
+        if (max_size != 0 && cnt > max_size) cnt = max_size;
+        for (_i = 0; _i < cnt; _i++) {
             mp->${c_name}[_i] = clib_host_to_net_u32(${java_name}ArrayElements[_i]);
         }
     }
@@ -180,16 +184,18 @@
                         jni_getter=jni_getter)
 
             # field setters
-            for t in zip(f['c_types'], f['args']):
+            for t in zip(f['c_types'], f['args'], f['lengths']):
                 c_type = t[0]
                 c_name = t[1]
+                field_length = t[2]
                 java_field_name = util.underscore_to_camelcase(c_name)
 
                 struct_setter_template = struct_setter_templates[c_type]
 
                 struct_setters += struct_setter_template.substitute(
                         c_name=c_name,
-                        java_name=java_field_name)
+                        java_name=java_field_name,
+                        field_length=field_length)
 
         jni_impl.append(jni_impl_template.substitute(
                 inputfile=inputfile,
@@ -221,23 +227,23 @@
 """)
 
 u8_array_dto_field_setter_template = Template("""
-    jbyteArray ${java_name} = (*env)->NewByteArray(env, sizeof(mp->${c_name}));
-    (*env)->SetByteArrayRegion(env, ${java_name}, 0, sizeof(mp->${c_name}), (const jbyte*)mp->${c_name});
+    jbyteArray ${java_name} = (*env)->NewByteArray(env, ${field_length});
+    (*env)->SetByteArrayRegion(env, ${java_name}, 0, ${field_length}, (const jbyte*)mp->${c_name});
     (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
 """)
 
 # For each u64 array we get its elements. Then we convert values to host byte order.
 # All changes to  jint* buffer are written to jlongArray (isCopy is set to NULL)
 u64_array_dto_field_setter_template = Template("""
-    jlongArray ${java_name} = (*env)->NewLongArray(env, sizeof(mp->${c_name}));
     {
+        jlongArray ${java_name} = (*env)->NewLongArray(env, ${field_length});
         jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL);
-        int _i;
-        for (_i = 0; _i < 0; _i++) {
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
             ${java_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]);
         }
+        (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
     }
-    (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
 """)
 
 dto_field_setter_templates = {'u8': default_dto_field_setter_template,
@@ -282,10 +288,11 @@
 
         dto_setters = ''
         # dto setters
-        for t in zip(f['c_types'], f['types'], f['args']):
+        for t in zip(f['c_types'], f['types'], f['args'], f['lengths']):
             c_type = t[0]
             jni_type = t[1]
             c_name = t[2]
+            field_length = t[3]
 
             java_field_name = util.underscore_to_camelcase(c_name)
             jni_signature = util.jni_2_signature_mapping[jni_type]
@@ -302,7 +309,8 @@
                     java_name=java_field_name,
                     jni_signature=jni_signature,
                     c_name=c_name,
-                    jni_setter=jni_setter)
+                    jni_setter=jni_setter,
+                    field_length=field_length)
 
         handlers.append(msg_handler_template.substitute(
                 inputfile=inputfile,
diff --git a/vpp-api/java/jvpp/gen/jvpp_gen.py b/vpp-api/java/jvpp/gen/jvpp_gen.py
index 931141e..e2ff2ad 100755
--- a/vpp-api/java/jvpp/gen/jvpp_gen.py
+++ b/vpp-api/java/jvpp/gen/jvpp_gen.py
@@ -72,16 +72,19 @@
 def get_types(t, filter):
     types_list = []
     c_types_list = []
+    lengths_list = []
     for i in t:
         if not filter(i[1]):
             continue
         if len(i) is 3:  # array type
             types_list.append(vpp_2_jni_type_mapping[i[0]] + 'Array')
             c_types_list.append(i[0] + '[]')
+            lengths_list.append(i[2])
         else:  # primitive type
             types_list.append(vpp_2_jni_type_mapping[i[0]])
             c_types_list.append(i[0])
-    return types_list, c_types_list
+            lengths_list.append(0)
+    return types_list, c_types_list, lengths_list
 
 
 def get_definitions():
@@ -96,18 +99,18 @@
 
         # For replies include all the arguments except message_id
         if util.is_reply(java_name):
-            types, c_types = get_types(a[1:], is_response_field)
+            types, c_types, lengths = get_types(a[1:], is_response_field)
             func_name[a[0]] = dict(
                 [('name', a[0]), ('java_name', java_name),
                  ('args', get_args(a[1:], is_response_field)), ('full_args', get_args(a[1:], lambda x: True)),
-                 ('types', types), ('c_types', c_types)])
+                 ('types', types), ('c_types', c_types), ('lengths', lengths)])
         # For requests skip message_id, client_id and context
         else:
-            types, c_types = get_types(a[1:], is_request_field)
+            types, c_types, lengths = get_types(a[1:], is_request_field)
             func_name[a[0]] = dict(
                 [('name', a[0]), ('java_name', java_name),
                  ('args', get_args(a[1:], is_request_field)), ('full_args', get_args(a[1:], lambda x: True)),
-                 ('types', types), ('c_types', c_types)])
+                 ('types', types), ('c_types', c_types), ('lengths', lengths)])
 
         # Indexed by name
         func_list.append(func_name[a[0]])
diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/L2AclTest.java b/vpp-api/java/jvpp/org/openvpp/jvpp/test/L2AclTest.java
new file mode 100644
index 0000000..e92b4f5
--- /dev/null
+++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/L2AclTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package org.openvpp.jvpp.test;
+
+import org.openvpp.jvpp.JVppImpl;
+import org.openvpp.jvpp.VppJNIConnection;
+import org.openvpp.jvpp.dto.ClassifyAddDelSession;
+import org.openvpp.jvpp.dto.ClassifyAddDelSessionReply;
+import org.openvpp.jvpp.dto.ClassifyAddDelTable;
+import org.openvpp.jvpp.dto.ClassifyAddDelTableReply;
+import org.openvpp.jvpp.dto.InputAclSetInterface;
+import org.openvpp.jvpp.dto.InputAclSetInterfaceReply;
+import org.openvpp.jvpp.future.FutureJVppFacade;
+
+/**
+ * <p>Tests L2 ACL creation.<br>
+ * Equivalent to the following vppctl commands:<br>
+ *
+ * <pre>{@code
+ * vppctl classify table mask l2 src
+ * vppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06
+ * vppctl vppctl set int input acl intfc local0 l2-table 0
+ * }
+ * </pre>
+ *
+ * To verify invoke:<br>
+ * {@code vppctl sh class table verbose}
+ */
+public class L2AclTest {
+
+    private static ClassifyAddDelTable createClassifyTable() {
+        ClassifyAddDelTable request = new ClassifyAddDelTable();
+        request.isAdd = 1;
+        request.tableIndex = ~0; // default
+        request.nbuckets = 2;
+        request.memorySize = 2 << 20;
+        request.nextTableIndex = ~0; // default
+        request.missNextIndex = ~0; // default
+        request.skipNVectors = 0;
+        request.matchNVectors = 1;
+        request.mask =
+                new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                        (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    private static ClassifyAddDelSession createClassifySession(final int tableIndex) {
+        ClassifyAddDelSession request = new ClassifyAddDelSession();
+        request.isAdd = 1;
+        request.tableIndex = tableIndex;
+        request.hitNextIndex = 0; // deny
+        request.opaqueIndex = 0;
+        request.advance = 0; // default
+        // match 01:02:03:04:05:06 mac address
+        request.match =
+                new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                        (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    private static InputAclSetInterface aclSetInterface() {
+        InputAclSetInterface request = new InputAclSetInterface();
+        request.isAdd = 1;
+        request.swIfIndex = 0;
+        request.ip4TableIndex = ~0; // skip
+        request.ip6TableIndex = ~0; // skip
+        request.l2TableIndex = 0;
+        return request;
+    }
+
+    private static void print(ClassifyAddDelTableReply reply) {
+        System.out.printf("ClassifyAddDelTableReply: context=%d, retval=%d, " +
+                        "newTableIndex=%d, skipNVectors=%d, matchNVectors=%d\n",
+                reply.context,
+                reply.retval,
+                reply.newTableIndex,
+                reply.skipNVectors,
+                reply.matchNVectors);
+    }
+
+    private static void print(ClassifyAddDelSessionReply reply) {
+        System.out.printf("ClassifyAddDelSessionReply: context=%d, retval=%d\n",
+                reply.context,
+                reply.retval);
+    }
+
+    private static void print(final InputAclSetInterfaceReply reply) {
+        System.out.printf("InputAclSetInterfaceReply: context=%d, retval=%d\n",
+                reply.context,
+                reply.retval);
+
+    }
+
+    private static void testL2Acl() throws Exception {
+        System.out.println("Testing L2 ACLs using Java callback API");
+        final JVppImpl jvpp = new JVppImpl(new VppJNIConnection("L2AclTest"));
+        final FutureJVppFacade jvppFacade = new FutureJVppFacade(jvpp);
+
+        System.out.println("Successfully connected to VPP");
+        Thread.sleep(1000);
+
+        final ClassifyAddDelTableReply classifyAddDelTableReply =
+                jvppFacade.classifyAddDelTable(createClassifyTable()).toCompletableFuture().get();
+        print(classifyAddDelTableReply);
+
+        final ClassifyAddDelSessionReply classifyAddDelSessionReply =
+                jvppFacade.classifyAddDelSession(createClassifySession(classifyAddDelTableReply.newTableIndex))
+                        .toCompletableFuture().get();
+        print(classifyAddDelSessionReply);
+
+        final InputAclSetInterfaceReply inputAclSetInterfaceReply =
+                jvppFacade.inputAclSetInterface(aclSetInterface()).toCompletableFuture().get();
+        print(inputAclSetInterfaceReply);
+
+        System.out.println("Disconnecting...");
+        jvpp.close();
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testL2Acl();
+    }
+}
diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt b/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt
index df4100d..79486ac 100644
--- a/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt
+++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt
@@ -2,12 +2,11 @@
 
 - Make sure VPP is running
 - From VPP's build-root/ folder execute:
-  - sudo java -cp .:build-vpp-native/vpp-api/java/jvpp-1.0.0.jar org.openvpp.jvpp.test.ControlPingTest
-  - sudo java -cp .:build-vpp-native/vpp-api/java/jvpp-1.0.0.jar org.openvpp.jvpp.test.FutureApiTest
-  - sudo java -cp .:build-vpp-native/vpp-api/java/jvpp-1.0.0.jar org.openvpp.jvpp.test.CallbackApiTest
+  - sudo java -cp build-vpp-native/vpp-api/java/jvpp-16.09.jar org.openvpp.jvpp.test.[test name]
 
 Available tests:
 ControlPingTest - Simple test executing a single control ping using low level JVpp APIs
 CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs
 FutureApiTest - Execution of more complex calls using Future based JVpp facade
-CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade
\ No newline at end of file
+CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade
+L2AclTest - Tests L2 ACL creation
\ No newline at end of file