Bump upstreams
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / protocol / serialization / match / AbstractMatchEntrySerializer.java
index 956f921b7c3a52efd5254627be23b22460b3b6ba..aeecb7f7ded3e2c21fc95a4772d872fc3196bb5b 100644 (file)
@@ -5,17 +5,18 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
 
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
 import io.netty.buffer.ByteBuf;
-import java.util.Iterator;
-import java.util.Optional;
-import org.opendaylight.openflowjava.protocol.api.extensibility.HeaderSerializer;
+import io.netty.buffer.Unpooled;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
 import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer;
 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.IpConversionUtil;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchConvertorUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
@@ -25,121 +26,235 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
 
-public abstract class AbstractMatchEntrySerializer implements HeaderSerializer<Match>, MatchEntrySerializer {
+/**
+ * Abstract base class for conveniently implementing {@link MatchEntrySerializer}.
+ *
+ * @param <E> Match entry type
+ * @param <M> Match entry mask type, use Void a mask is not applicable
+ */
+public abstract class AbstractMatchEntrySerializer<E, M> implements MatchEntrySerializer {
+    /**
+     * Base class supporting writing out a particular match entry's header. This class should be subclassed only
+     * in case the header contents depends on the match entry content in a more dynamic fashion than the presence
+     * or absence of the mask. In all other cases using {@link ConstantHeaderWriter} is preferable.
+     *
+     * @param <E> Match entry type
+     * @param <M> Match entry mask type, use Void a mask is not applicable
+     */
+    protected abstract static class HeaderWriter<E, M> {
 
-    @Override
-    public void serialize(Match match, ByteBuf outBuffer) {
-        serializeHeader(match, outBuffer);
+        /**
+         * Write out the header for a particular entry, containing specified mask, to the provided output buffer.
+         *
+         * @param entry match entry for which to write the header
+         * @param mask mask as extracted from the match entry, may be null
+         * @param outBuffer output buffer
+         */
+        protected abstract void writeHeader(@NonNull E entry, @Nullable M mask, @NonNull ByteBuf outBuffer);
+
+        protected static final void writeHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
+                final boolean hasMask, final ByteBuf outBuffer) {
+            writeHeader(oxmClassCode, oxmFieldCode, valueLength, hasMask, 0, outBuffer);
+        }
+
+        protected static final void writeHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
+                final boolean hasMask, final int extraLength, final ByteBuf outBuffer) {
+            outBuffer.writeShort(oxmClassCode);
+
+            int fieldAndMask = oxmFieldCode << 1;
+            int length = valueLength;
+
+            if (hasMask) {
+                fieldAndMask |= 1;
+                length *= 2;
+            }
+
+            outBuffer.writeByte(fieldAndMask);
+            outBuffer.writeByte(length + extraLength);
+        }
     }
 
-    @Override
-    public void serializeHeader(Match match, ByteBuf outBuffer) {
-        outBuffer.writeShort(getOxmClassCode());
+    /**
+     * Utility {@link HeaderWriter} optimized for cases where the header does not depend on the actual entry content
+     * beyond presence/absence of a mask. This class pre-computes the possible header values for masked/unmasked cases
+     * and stores them internally for reuse.
+     *
+     * @param <E> Match entry type
+     * @param <M> Match entry mask type, use Void a mask is not applicable
+     */
+    /*
+     * Implementation note:
+     *
+     * While it looks like we could save some memory by refactoring this class into two instances for the non-mask
+     * and mask values, that actually would result in increased memory footprint because the JVM object header is
+     * larger than the state we keep. We would also require another reference field in the serializer itself, which
+     * would expand the size of that object, negating the benefit.
+     *
+     * Another refactor would see the case where an entry cannot have a mask split out into a separate class, making
+     * that specialized object smaller and not contain the null check in writeHeader(). This can be considered, but
+     * has to be thoroughly benchmarked with representative data, because introducing another implementation means
+     * calls to writeHeader() are no longer monomorphic and are not as readily devirtualizable -- which has performance
+     * implications which can easily negate any behefits from the specialization.
+     */
+    protected static final class ConstantHeaderWriter<E, M> extends HeaderWriter<E, M> {
+        private final int withMask;
+        private final int withoutMask;
 
-        int fieldAndMask = getOxmFieldCode() << 1;
-        int length = getValueLength();
+        protected ConstantHeaderWriter(final int withMask, final int withoutMask) {
+            this.withMask = withMask;
+            this.withoutMask = withoutMask;
+        }
+
+        protected ConstantHeaderWriter(final int oxmClassCode, final int oxmFieldCode, final int valueLength) {
+            this(oxmClassCode, oxmFieldCode, valueLength, 0);
+        }
 
-        if (getHasMask(match)) {
-            fieldAndMask |= 1;
-            length *= 2;
+        protected ConstantHeaderWriter(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
+                final int extraLength) {
+            this(constructHeader(oxmClassCode, oxmFieldCode, valueLength, extraLength, true),
+                constructHeader(oxmClassCode, oxmFieldCode, valueLength, extraLength, false));
         }
 
-        outBuffer.writeByte(fieldAndMask);
-        outBuffer.writeByte(length);
+        @Override
+        protected void writeHeader(final E entry, final M mask, final ByteBuf outBuffer) {
+            outBuffer.writeInt(mask != null ? withMask : withoutMask);
+        }
+
+        private static int constructHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
+                final int extraLength, final boolean withMask) {
+            final ByteBuf buf = Unpooled.buffer();
+            writeHeader(oxmClassCode, oxmFieldCode, valueLength, withMask, extraLength, buf);
+            final int header = buf.readInt();
+            verify(buf.readableBytes() == 0);
+            return header;
+        }
+    }
+
+    private final HeaderWriter<E, M> headerWriter;
+
+    protected AbstractMatchEntrySerializer(final HeaderWriter<E, M> headerWriter) {
+        this.headerWriter = requireNonNull(headerWriter);
+    }
+
+    protected AbstractMatchEntrySerializer(final int oxmClassCode, final int oxmFieldCode, final int valueLength) {
+        this(new ConstantHeaderWriter<>(oxmClassCode, oxmFieldCode, valueLength));
+    }
+
+    @Override
+    public final void serializeIfPresent(final Match match, final ByteBuf outBuffer) {
+        final E entry = extractEntry(match);
+        if (entry != null) {
+            final M mask = extractEntryMask(entry);
+            headerWriter.writeHeader(entry, mask, outBuffer);
+            serializeEntry(entry, mask, outBuffer);
+        }
     }
 
     /**
-     * Serialize byte mask to bytes. checking for mask length
+     * Serialize byte mask to bytes. checking for mask length.
+     *
      * @param mask byte mask
      * @param outBuffer output buffer
      * @param length mask length
      */
-    protected static void writeMask(byte[] mask, ByteBuf outBuffer, int length) {
-        if (mask != null && mask.length != length) {
-            throw new IllegalArgumentException("incorrect length of mask: "+
-                    mask.length + ", expected: " + length);
+    protected static void writeMask(final byte[] mask, final ByteBuf outBuffer, final int length) {
+        if (mask != null) {
+            if (mask.length != length) {
+                throw new IllegalArgumentException("incorrect length of mask: " + mask.length + ", expected: "
+                        + length);
+            }
+            outBuffer.writeBytes(mask);
         }
-
-        outBuffer.writeBytes(mask);
     }
 
     /**
-     * Serialize Ipv4 address to bytes
+     * Serialize Ipv4 address to bytes.
+     *
      * @param address Ipv4 address
      * @param outBuffer output buffer
      */
     protected static void writeIpv4Address(final Ipv4Address address, final ByteBuf outBuffer) {
-        outBuffer.writeBytes(IetfInetUtil.INSTANCE.ipv4AddressBytes(address));
+        outBuffer.writeBytes(IetfInetUtil.ipv4AddressBytes(address));
     }
 
     /**
-     * Serialize Ipv6 address to bytes
+     * Serialize Ipv6 address to bytes.
+     *
      * @param address Ipv6 address
      * @param outBuffer output buffer
      */
     protected static void writeIpv6Address(final Ipv6Address address, final ByteBuf outBuffer) {
-        outBuffer.writeBytes(IetfInetUtil.INSTANCE.ipv6AddressBytes(address));
+        outBuffer.writeBytes(IetfInetUtil.ipv6AddressBytes(address));
     }
 
     /**
-     * Serialize Mac address to bytes
+     * Serialize Mac address to bytes.
+     *
      * @param address Mac address
      * @param outBuffer output buffer
      */
     protected static void writeMacAddress(final MacAddress address, final ByteBuf outBuffer) {
-        outBuffer.writeBytes(IetfYangUtil.INSTANCE.bytesFor(address)); // 48 b + mask [OF 1.3.2 spec]
+        // 48 b + mask [OF 1.3.2 spec]
+        outBuffer.writeBytes(IetfYangUtil.macAddressBytes(address));
     }
 
     /**
-     * Serialize Ipv4 prefix (address and mask)
+     * Serialize Ipv4 prefix (address and mask).
+     *
      * @param prefix Ipv4 prefix
      * @param outBuffer output buffer
      */
-    protected static void writeIpv4Prefix(final Ipv4Prefix prefix, final ByteBuf outBuffer) {
-        // Split address to IP and mask
-        final Iterator<String> addressParts = IpConversionUtil.splitToParts(prefix);
-
+    protected static void writeIpv4Prefix(final @NonNull Ipv4Prefix prefix, final @Nullable Integer mask,
+            final @NonNull ByteBuf outBuffer) {
         // Write address part of prefix
-        writeIpv4Address(new Ipv4Address(addressParts.next()), outBuffer);
+        writeIpv4Address(IetfInetUtil.ipv4AddressFrom(prefix), outBuffer);
 
         // If prefix had mask, also write prefix
-        Optional.ofNullable(MatchConvertorUtil.extractIpv4Mask(addressParts)).ifPresent(mask ->
-                writeMask(mask, outBuffer, EncodeConstants.GROUPS_IN_IPV4_ADDRESS));
+        if (mask != null) {
+            outBuffer.writeInt(IpConversionUtil.maskForIpv4Prefix(mask));
+        }
     }
 
     /**
-     * Serialize Ipv6 prefix (address and mask)
+     * Serialize Ipv6 prefix (address and mask).
+     *
      * @param prefix Ipv6 prefix
      * @param outBuffer output buffer
      */
-    protected static void writeIpv6Prefix(final Ipv6Prefix prefix, final ByteBuf outBuffer) {
+    protected static void writeIpv6Prefix(final @NonNull Ipv6Prefix prefix, final @Nullable Integer mask,
+            final @NonNull ByteBuf outBuffer) {
         // Write address part of prefix
         writeIpv6Address(IpConversionUtil.extractIpv6Address(prefix), outBuffer);
 
         // If prefix had mask, also write prefix
-        Optional.ofNullable(IpConversionUtil.hasIpv6Prefix(prefix)).ifPresent(mask ->
-                writeMask(IpConversionUtil.convertIpv6PrefixToByteArray(mask), outBuffer,
-                        EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES));
+        if (mask != null) {
+            writeMask(IpConversionUtil.convertIpv6PrefixToByteArray(mask), outBuffer,
+                EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
+        }
     }
 
     /**
+     * Extract the corresponding entry from a match.
+     *
      * @param match Openflow match
-     * @return if field has or has not mask
+     * @return Entry, null if not present
      */
-    protected abstract boolean getHasMask(final Match match);
+    protected abstract @Nullable E extractEntry(Match match);
 
     /**
-     * @return numeric representation of oxm_field
+     * Extract the mask contained in an entry.
+     *
+     * @param entry entry to examine
+     * @return Mask, null if not present
      */
-    protected abstract int getOxmFieldCode();
+    protected abstract @Nullable M extractEntryMask(@NonNull E entry);
 
     /**
-     * @return numeric representation of oxm_class
-     */
-    protected abstract int getOxmClassCode();
-
-    /**
-     * @return match entry value length (without mask length)
+     * Extract the corresponding entry from a match.
+     *
+     * @param entry entry to serialize
+     * @param mask mask as extracted from entry
+     * @param outBuffer output buffer
      */
-    protected abstract int getValueLength();
+    protected abstract void serializeEntry(@NonNull E entry, @Nullable M mask, @NonNull ByteBuf outBuffer);
 }