Bump upstreams
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / protocol / serialization / match / AbstractMatchEntrySerializer.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.impl.protocol.serialization.match;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import io.netty.buffer.ByteBuf;
14 import io.netty.buffer.Unpooled;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
18 import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer;
19 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.IpConversionUtil;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.IetfYangUtil;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
28
29 /**
30  * Abstract base class for conveniently implementing {@link MatchEntrySerializer}.
31  *
32  * @param <E> Match entry type
33  * @param <M> Match entry mask type, use Void a mask is not applicable
34  */
35 public abstract class AbstractMatchEntrySerializer<E, M> implements MatchEntrySerializer {
36     /**
37      * Base class supporting writing out a particular match entry's header. This class should be subclassed only
38      * in case the header contents depends on the match entry content in a more dynamic fashion than the presence
39      * or absence of the mask. In all other cases using {@link ConstantHeaderWriter} is preferable.
40      *
41      * @param <E> Match entry type
42      * @param <M> Match entry mask type, use Void a mask is not applicable
43      */
44     protected abstract static class HeaderWriter<E, M> {
45
46         /**
47          * Write out the header for a particular entry, containing specified mask, to the provided output buffer.
48          *
49          * @param entry match entry for which to write the header
50          * @param mask mask as extracted from the match entry, may be null
51          * @param outBuffer output buffer
52          */
53         protected abstract void writeHeader(@NonNull E entry, @Nullable M mask, @NonNull ByteBuf outBuffer);
54
55         protected static final void writeHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
56                 final boolean hasMask, final ByteBuf outBuffer) {
57             writeHeader(oxmClassCode, oxmFieldCode, valueLength, hasMask, 0, outBuffer);
58         }
59
60         protected static final void writeHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
61                 final boolean hasMask, final int extraLength, final ByteBuf outBuffer) {
62             outBuffer.writeShort(oxmClassCode);
63
64             int fieldAndMask = oxmFieldCode << 1;
65             int length = valueLength;
66
67             if (hasMask) {
68                 fieldAndMask |= 1;
69                 length *= 2;
70             }
71
72             outBuffer.writeByte(fieldAndMask);
73             outBuffer.writeByte(length + extraLength);
74         }
75     }
76
77     /**
78      * Utility {@link HeaderWriter} optimized for cases where the header does not depend on the actual entry content
79      * beyond presence/absence of a mask. This class pre-computes the possible header values for masked/unmasked cases
80      * and stores them internally for reuse.
81      *
82      * @param <E> Match entry type
83      * @param <M> Match entry mask type, use Void a mask is not applicable
84      */
85     /*
86      * Implementation note:
87      *
88      * While it looks like we could save some memory by refactoring this class into two instances for the non-mask
89      * and mask values, that actually would result in increased memory footprint because the JVM object header is
90      * larger than the state we keep. We would also require another reference field in the serializer itself, which
91      * would expand the size of that object, negating the benefit.
92      *
93      * Another refactor would see the case where an entry cannot have a mask split out into a separate class, making
94      * that specialized object smaller and not contain the null check in writeHeader(). This can be considered, but
95      * has to be thoroughly benchmarked with representative data, because introducing another implementation means
96      * calls to writeHeader() are no longer monomorphic and are not as readily devirtualizable -- which has performance
97      * implications which can easily negate any behefits from the specialization.
98      */
99     protected static final class ConstantHeaderWriter<E, M> extends HeaderWriter<E, M> {
100         private final int withMask;
101         private final int withoutMask;
102
103         protected ConstantHeaderWriter(final int withMask, final int withoutMask) {
104             this.withMask = withMask;
105             this.withoutMask = withoutMask;
106         }
107
108         protected ConstantHeaderWriter(final int oxmClassCode, final int oxmFieldCode, final int valueLength) {
109             this(oxmClassCode, oxmFieldCode, valueLength, 0);
110         }
111
112         protected ConstantHeaderWriter(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
113                 final int extraLength) {
114             this(constructHeader(oxmClassCode, oxmFieldCode, valueLength, extraLength, true),
115                 constructHeader(oxmClassCode, oxmFieldCode, valueLength, extraLength, false));
116         }
117
118         @Override
119         protected void writeHeader(final E entry, final M mask, final ByteBuf outBuffer) {
120             outBuffer.writeInt(mask != null ? withMask : withoutMask);
121         }
122
123         private static int constructHeader(final int oxmClassCode, final int oxmFieldCode, final int valueLength,
124                 final int extraLength, final boolean withMask) {
125             final ByteBuf buf = Unpooled.buffer();
126             writeHeader(oxmClassCode, oxmFieldCode, valueLength, withMask, extraLength, buf);
127             final int header = buf.readInt();
128             verify(buf.readableBytes() == 0);
129             return header;
130         }
131     }
132
133     private final HeaderWriter<E, M> headerWriter;
134
135     protected AbstractMatchEntrySerializer(final HeaderWriter<E, M> headerWriter) {
136         this.headerWriter = requireNonNull(headerWriter);
137     }
138
139     protected AbstractMatchEntrySerializer(final int oxmClassCode, final int oxmFieldCode, final int valueLength) {
140         this(new ConstantHeaderWriter<>(oxmClassCode, oxmFieldCode, valueLength));
141     }
142
143     @Override
144     public final void serializeIfPresent(final Match match, final ByteBuf outBuffer) {
145         final E entry = extractEntry(match);
146         if (entry != null) {
147             final M mask = extractEntryMask(entry);
148             headerWriter.writeHeader(entry, mask, outBuffer);
149             serializeEntry(entry, mask, outBuffer);
150         }
151     }
152
153     /**
154      * Serialize byte mask to bytes. checking for mask length.
155      *
156      * @param mask byte mask
157      * @param outBuffer output buffer
158      * @param length mask length
159      */
160     protected static void writeMask(final byte[] mask, final ByteBuf outBuffer, final int length) {
161         if (mask != null) {
162             if (mask.length != length) {
163                 throw new IllegalArgumentException("incorrect length of mask: " + mask.length + ", expected: "
164                         + length);
165             }
166             outBuffer.writeBytes(mask);
167         }
168     }
169
170     /**
171      * Serialize Ipv4 address to bytes.
172      *
173      * @param address Ipv4 address
174      * @param outBuffer output buffer
175      */
176     protected static void writeIpv4Address(final Ipv4Address address, final ByteBuf outBuffer) {
177         outBuffer.writeBytes(IetfInetUtil.ipv4AddressBytes(address));
178     }
179
180     /**
181      * Serialize Ipv6 address to bytes.
182      *
183      * @param address Ipv6 address
184      * @param outBuffer output buffer
185      */
186     protected static void writeIpv6Address(final Ipv6Address address, final ByteBuf outBuffer) {
187         outBuffer.writeBytes(IetfInetUtil.ipv6AddressBytes(address));
188     }
189
190     /**
191      * Serialize Mac address to bytes.
192      *
193      * @param address Mac address
194      * @param outBuffer output buffer
195      */
196     protected static void writeMacAddress(final MacAddress address, final ByteBuf outBuffer) {
197         // 48 b + mask [OF 1.3.2 spec]
198         outBuffer.writeBytes(IetfYangUtil.macAddressBytes(address));
199     }
200
201     /**
202      * Serialize Ipv4 prefix (address and mask).
203      *
204      * @param prefix Ipv4 prefix
205      * @param outBuffer output buffer
206      */
207     protected static void writeIpv4Prefix(final @NonNull Ipv4Prefix prefix, final @Nullable Integer mask,
208             final @NonNull ByteBuf outBuffer) {
209         // Write address part of prefix
210         writeIpv4Address(IetfInetUtil.ipv4AddressFrom(prefix), outBuffer);
211
212         // If prefix had mask, also write prefix
213         if (mask != null) {
214             outBuffer.writeInt(IpConversionUtil.maskForIpv4Prefix(mask));
215         }
216     }
217
218     /**
219      * Serialize Ipv6 prefix (address and mask).
220      *
221      * @param prefix Ipv6 prefix
222      * @param outBuffer output buffer
223      */
224     protected static void writeIpv6Prefix(final @NonNull Ipv6Prefix prefix, final @Nullable Integer mask,
225             final @NonNull ByteBuf outBuffer) {
226         // Write address part of prefix
227         writeIpv6Address(IpConversionUtil.extractIpv6Address(prefix), outBuffer);
228
229         // If prefix had mask, also write prefix
230         if (mask != null) {
231             writeMask(IpConversionUtil.convertIpv6PrefixToByteArray(mask), outBuffer,
232                 EncodeConstants.SIZE_OF_IPV6_ADDRESS_IN_BYTES);
233         }
234     }
235
236     /**
237      * Extract the corresponding entry from a match.
238      *
239      * @param match Openflow match
240      * @return Entry, null if not present
241      */
242     protected abstract @Nullable E extractEntry(Match match);
243
244     /**
245      * Extract the mask contained in an entry.
246      *
247      * @param entry entry to examine
248      * @return Mask, null if not present
249      */
250     protected abstract @Nullable M extractEntryMask(@NonNull E entry);
251
252     /**
253      * Extract the corresponding entry from a match.
254      *
255      * @param entry entry to serialize
256      * @param mask mask as extracted from entry
257      * @param outBuffer output buffer
258      */
259     protected abstract void serializeEntry(@NonNull E entry, @Nullable M mask, @NonNull ByteBuf outBuffer);
260 }