Support arbitrary bit width action parameter and match field

This commit goes into the direction of supporting user-defined types in P4Runtime.
The modification is focusing on supporting fields and params with arbitrary bit width, that is the
case of using a String with the p4runtime_translation annotation on the user-defined type.

Change-Id: I7db7a6d97211378ff78ab4f1b3734a0bec4558e6
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
index d4a6c4d..467e729 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
@@ -27,6 +27,7 @@
  * Implementation of PiActionParamModel for P4Runtime.
  */
 final class P4ActionParamModel implements PiActionParamModel {
+    static final int BIT_WIDTH_UNDEFINED = -1;
 
     private final PiActionParamId id;
     private final int bitWidth;
@@ -47,6 +48,11 @@
     }
 
     @Override
+    public boolean hasBitWidth() {
+        return bitWidth != BIT_WIDTH_UNDEFINED;
+    }
+
+    @Override
     public int hashCode() {
         return Objects.hash(id, bitWidth);
     }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
index 7c1fd1a..6558d80 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
@@ -64,6 +64,7 @@
 import p4.config.v1.P4InfoOuterClass.MeterSpec;
 import p4.config.v1.P4InfoOuterClass.P4Info;
 import p4.config.v1.P4InfoOuterClass.Table;
+import p4.config.v1.P4Types;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -189,7 +190,9 @@
                 tableFieldMapBuilder.put(
                         fieldId,
                         new P4MatchFieldModel(fieldId,
-                                              fieldMsg.getBitwidth(),
+                                              isFieldString(p4info, fieldMsg.getTypeName().getName()) ?
+                                                      P4MatchFieldModel.BIT_WIDTH_UNDEFINED :
+                                                      fieldMsg.getBitwidth(),
                                               mapMatchFieldType(fieldMsg.getMatchType())));
 
             }
@@ -385,8 +388,11 @@
             actionMsg.getParamsList().forEach(paramMsg -> {
                 final PiActionParamId paramId = PiActionParamId.of(paramMsg.getName());
                 paramMapBuilder.put(paramId,
-                                    new P4ActionParamModel(PiActionParamId.of(paramMsg.getName()),
-                                                           paramMsg.getBitwidth()));
+                                    new P4ActionParamModel(
+                                            PiActionParamId.of(paramMsg.getName()),
+                                            isFieldString(p4info, paramMsg.getTypeName().getName()) ?
+                                                    P4ActionParamModel.BIT_WIDTH_UNDEFINED :
+                                                    paramMsg.getBitwidth()));
             });
             actionMap.put(
                     actionMsg.getPreamble().getId(),
@@ -480,4 +486,11 @@
                 .findFirst()
                 .orElse(null);
     }
+
+    private static boolean isFieldString(P4Info p4info, String fieldTypeName) {
+        P4Types.P4TypeInfo p4TypeInfo = p4info.getTypeInfo();
+        return p4TypeInfo.containsNewTypes(fieldTypeName) &&
+                p4TypeInfo.getNewTypesOrThrow(fieldTypeName).hasTranslatedType() &&
+                p4TypeInfo.getNewTypesOrThrow(fieldTypeName).getTranslatedType().hasSdnString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
index f311acf..73bc4aa 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
@@ -28,6 +28,7 @@
  * Implementation of PiMatchFieldModel for P4Runtime.
  */
 final class P4MatchFieldModel implements PiMatchFieldModel {
+    static final int BIT_WIDTH_UNDEFINED = -1;
 
     private final PiMatchFieldId id;
     private final int bitWidth;
@@ -50,6 +51,11 @@
     }
 
     @Override
+    public boolean hasBitWidth() {
+        return bitWidth != BIT_WIDTH_UNDEFINED;
+    }
+
+    @Override
     public PiMatchType matchType() {
         return matchType;
     }
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionParamModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionParamModelTest.java
index 035465c..c98d401 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionParamModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionParamModelTest.java
@@ -41,6 +41,15 @@
 
     private final P4ActionParamModel actionParamModel3 = new P4ActionParamModel(piActionParamId, BIT_WIDTH_64);
 
+    private final P4ActionParamModel actionParamModel4 = new
+            P4ActionParamModel(piActionParamId, P4ActionParamModel.BIT_WIDTH_UNDEFINED);
+
+    private final P4ActionParamModel sameAsActionParamModel4 =
+            new P4ActionParamModel(sameAsPiActionParamId, P4ActionParamModel.BIT_WIDTH_UNDEFINED);
+
+    private final P4ActionParamModel actionParamModel5 =
+            new P4ActionParamModel(piActionParamId2, P4ActionParamModel.BIT_WIDTH_UNDEFINED);
+
 
 
     /**
@@ -58,8 +67,10 @@
     public void testEquals() {
         new EqualsTester()
                 .addEqualityGroup(actionParamModel, sameAsActionParamModel)
+                .addEqualityGroup(actionParamModel4, sameAsActionParamModel4)
                 .addEqualityGroup(actionParamModel2)
                 .addEqualityGroup(actionParamModel3)
+                .addEqualityGroup(actionParamModel5)
                 .testEquals();
     }
 }
\ No newline at end of file
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4MatchFieldModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4MatchFieldModelTest.java
index 448dcc5..86a0e66 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4MatchFieldModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4MatchFieldModelTest.java
@@ -42,6 +42,14 @@
     private static final P4MatchFieldModel P4_MATCH_FIELD_MODEL_2 =
         new P4MatchFieldModel(PI_MATCH_FIELD_ID_2, BIT_WIDTH_2, PI_MATCH_TYPE_2);
 
+    private static final P4MatchFieldModel P4_MATCH_FIELD_MODEL_3 =
+            new P4MatchFieldModel(PI_MATCH_FIELD_ID_1, P4MatchFieldModel.BIT_WIDTH_UNDEFINED, PI_MATCH_TYPE_1);
+    private static final P4MatchFieldModel SAME_AS_P4_MATCH_FIELD_MODEL_3 =
+            new P4MatchFieldModel(PI_MATCH_FIELD_ID_1, P4MatchFieldModel.BIT_WIDTH_UNDEFINED, PI_MATCH_TYPE_1);
+
+    private static final P4MatchFieldModel P4_MATCH_FIELD_MODEL_4 =
+            new P4MatchFieldModel(PI_MATCH_FIELD_ID_2, P4MatchFieldModel.BIT_WIDTH_UNDEFINED, PI_MATCH_TYPE_2);
+
     /**
      * Checks that the P4MatchFieldModel class is immutable.
      */
@@ -57,7 +65,9 @@
     public void testEquals() {
         new EqualsTester()
             .addEqualityGroup(P4_MATCH_FIELD_MODEL_1, SAME_AS_P4_MATCH_FIELD_MODEL_1)
+            .addEqualityGroup(P4_MATCH_FIELD_MODEL_3, SAME_AS_P4_MATCH_FIELD_MODEL_3)
             .addEqualityGroup(P4_MATCH_FIELD_MODEL_2)
+            .addEqualityGroup(P4_MATCH_FIELD_MODEL_4)
             .testEquals();
     }
 }
\ No newline at end of file
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java
index 3ae46c9..69b31a8 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionCodec.java
@@ -48,8 +48,11 @@
             final P4InfoOuterClass.Action.Param paramInfo = browser.actionParams(actionId)
                     .getByName(p.id().toString());
             final ByteString paramValue = ByteString.copyFrom(p.value().asReadOnlyBuffer());
-            assertSize(format("param '%s' of action '%s'", p.id(), piAction.id()),
-                       paramValue, paramInfo.getBitwidth());
+            if (!browser.isTypeString(paramInfo.getTypeName())) {
+                // Check size only if the param type is not a sdn_string
+                assertSize(format("param '%s' of action '%s'", p.id(), piAction.id()),
+                           paramValue, paramInfo.getBitwidth());
+            }
             actionMsgBuilder.addParams(P4RuntimeOuterClass.Action.Param.newBuilder()
                                                .setParamId(paramInfo.getId())
                                                .setValue(paramValue)
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java
index f289d5d..5e2ec43 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/FieldMatchCodec.java
@@ -71,7 +71,10 @@
             case EXACT:
                 PiExactFieldMatch fieldMatch = (PiExactFieldMatch) piFieldMatch;
                 ByteString exactValue = ByteString.copyFrom(fieldMatch.value().asReadOnlyBuffer());
-                assertSize(VALUE_OF_PREFIX + entityName, exactValue, fieldBitwidth);
+                // We support string only for EXACT match (via p4runtime_translation)
+                if (!browser.isTypeString(matchFieldInfo.getTypeName())) {
+                    assertSize(VALUE_OF_PREFIX + entityName, exactValue, fieldBitwidth);
+                }
                 return messageBuilder.setExact(
                         P4RuntimeOuterClass.FieldMatch.Exact
                                 .newBuilder()
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java
index 8504202..85405ca 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/utils/P4InfoBrowser.java
@@ -31,6 +31,7 @@
 import p4.config.v1.P4InfoOuterClass.Preamble;
 import p4.config.v1.P4InfoOuterClass.Table;
 import p4.config.v1.P4InfoOuterClass.Digest;
+import p4.config.v1.P4Types;
 
 import java.util.Map;
 
@@ -57,6 +58,7 @@
     private final Map<Integer, EntityBrowser<ControllerPacketMetadata.Metadata>> ctrlPktMetadatasMetadata =
             Maps.newHashMap();
     private final EntityBrowser<Digest> digests = new EntityBrowser<>("digest");
+    private final Map<String, Boolean> isTypeString = Maps.newHashMap();
 
     /**
      * Creates a new browser for the given P4Info.
@@ -121,6 +123,12 @@
 
         p4info.getDigestsList().forEach(
                 entity -> digests.addWithPreamble(entity.getPreamble(), entity));
+        p4info.getTypeInfo().getNewTypesMap().forEach(
+                (s, p4NewTypeSpec) ->
+                        isTypeString.put(s,
+                                         p4NewTypeSpec.hasTranslatedType()
+                                                 && p4NewTypeSpec.getTranslatedType().hasSdnString()
+                        ));
     }
 
     /**
@@ -245,6 +253,17 @@
     }
 
     /**
+     * Checks if the given type name is a sdn_string.
+     *
+     * @param typeName Type name to check
+     * @return True if the given type name is a sdn_string, false otherwise
+     */
+    public boolean isTypeString(P4Types.P4NamedType typeName) {
+        return isTypeString.containsKey(typeName.getName())
+                && isTypeString.get(typeName.getName());
+    }
+
+    /**
      * Browser of P4Info entities.
      *
      * @param <T> protobuf message type