Fix findbugs violations in openflowjava
[openflowplugin.git] / openflowjava / openflowjava-util / src / main / java / org / opendaylight / openflowjava / util / ByteBufUtils.java
1 /*
2  * Copyright (c) 2013 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
9 package org.opendaylight.openflowjava.util;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Splitter;
13 import com.google.common.collect.Lists;
14 import com.google.common.primitives.UnsignedBytes;
15 import io.netty.buffer.ByteBuf;
16 import io.netty.buffer.UnpooledByteBufAllocator;
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.nio.charset.StandardCharsets;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.IetfYangUtil;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
30
31 /**
32  *  Class for common operations on ByteBuf.
33  *
34  * @author michal.polkorab
35  * @author timotej.kubas
36  */
37 public abstract class ByteBufUtils {
38     public static final Splitter DOT_SPLITTER = Splitter.on('.');
39     public static final Splitter COLON_SPLITTER = Splitter.on(':');
40     private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
41     private static final Splitter HEXSTRING_SPLITTER =  Splitter.onPattern("\\s+").omitEmptyStrings();
42     private static final Splitter HEXSTRING_NOSPACE_SPLITTER = Splitter.onPattern("(?<=\\G.{2})").omitEmptyStrings();
43
44     private ByteBufUtils() {
45         //not called
46     }
47
48     /**
49      * Converts ByteBuf into String.
50      *
51      * @param bb input ByteBuf
52      * @return String
53      */
54     public static String byteBufToHexString(final ByteBuf bb) {
55         StringBuilder sb = new StringBuilder();
56         for (int i = bb.readerIndex(); i < bb.readerIndex() + bb.readableBytes(); i++) {
57             sb.append(String.format(" %02x", bb.getUnsignedByte(i)));
58         }
59         return sb.toString().trim();
60     }
61
62     /**
63      * Converts String into byte[].
64      *
65      * @param hexSrc input String
66      * @return byte[] filled with input data
67      */
68     public static byte[] hexStringToBytes(final String hexSrc) {
69         return hexStringToBytes(hexSrc, true);
70     }
71
72     /**
73      * Converts String into byte[].
74      *
75      * @param hexSrc input String
76      * @param withSpaces if there are spaces in string
77      * @return byte[] filled with input data
78      */
79     public static byte[] hexStringToBytes(final String hexSrc, final boolean withSpaces) {
80         final Splitter splitter = withSpaces ? HEXSTRING_SPLITTER : HEXSTRING_NOSPACE_SPLITTER;
81         List<String> byteChips = Lists.newArrayList(splitter.split(hexSrc));
82         byte[] result = new byte[byteChips.size()];
83         int index = 0;
84         for (String chip : byteChips) {
85             result[index] = (byte) Short.parseShort(chip, 16);
86             index++;
87         }
88         return result;
89     }
90
91     /**
92      * Creates ByteBuf filled with specified data.
93      *
94      * @param hexSrc input String of bytes in hex format
95      * @return ByteBuf with specified hexString converted
96      */
97     public static ByteBuf hexStringToByteBuf(final String hexSrc) {
98         ByteBuf out = UnpooledByteBufAllocator.DEFAULT.buffer();
99         hexStringToByteBuf(hexSrc, out);
100         return out;
101     }
102
103     /**
104      * Creates ByteBuf filled with specified data.
105      *
106      * @param hexSrc input String of bytes in hex format
107      * @param out ByteBuf with specified hexString converted
108      */
109     public static void hexStringToByteBuf(final String hexSrc, final ByteBuf out) {
110         out.writeBytes(hexStringToBytes(hexSrc));
111     }
112
113     /**
114      * Fills specified ByteBuf with 0 (zeros) of desired length, used for padding.
115      *
116      * @param length the desired length
117      * @param out ByteBuf to be padded
118      * @deprecated Use {@link ByteBuf#writeZero(int)} directly.
119      */
120     @Deprecated
121     public static void padBuffer(final int length, final ByteBuf out) {
122         out.writeZero(length);
123     }
124
125     /**
126      * Create standard OF header.
127      *
128      * @param msgType message code
129      * @param message POJO
130      * @param out writing buffer
131      * @param length ofheader length
132      */
133     public static <E extends OfHeader> void writeOFHeader(final byte msgType, final E message, final ByteBuf out,
134             final int length) {
135         out.writeByte(message.getVersion());
136         out.writeByte(msgType);
137         out.writeShort(length);
138         out.writeInt(message.getXid().intValue());
139     }
140
141     /**
142      * Write length standard OF header.
143      *
144      * @param out writing buffer
145      */
146     public static void updateOFHeaderLength(final ByteBuf out) {
147         out.setShort(EncodeConstants.OFHEADER_LENGTH_INDEX, out.readableBytes());
148     }
149
150     /**
151      * Write length OF header.
152      *
153      * @param out writing buffer
154      * @param index writing index
155      */
156     public static void updateOFHeaderLength(final ByteBuf out, int index) {
157         out.setShort(index + EncodeConstants.OFHEADER_LENGTH_INDEX, out.writerIndex() - index);
158     }
159
160     /**
161      * Fills the bitmask from boolean map where key is bit position.
162      *
163      * @param booleanMap bit to boolean mapping
164      * @return bit mask
165      */
166     public static int fillBitMaskFromMap(final Map<Integer, Boolean> booleanMap) {
167         int bitmask = 0;
168
169         for (Entry<Integer, Boolean> iterator : booleanMap.entrySet()) {
170             if (iterator.getValue() != null && iterator.getValue().booleanValue()) {
171                 bitmask |= 1 << iterator.getKey();
172             }
173         }
174         return bitmask;
175     }
176
177     /**
178      * Fills the bitmask from a set of bit values, starting at specified offset.
179      *
180      * @param offset Bit offset to start at
181      * @param values boolean bit values to fill
182      * @return Filled-in bitmask
183      */
184     public static int fillBitMask(final int offset, final boolean... values) {
185         int bitmask = 0;
186
187         int index = offset;
188         for (boolean v : values) {
189             if (v) {
190                 bitmask |= 1 << index;
191             }
192             ++index;
193         }
194
195         return bitmask;
196     }
197
198     /**
199      * Fills the bitmask from boolean list where key is bit position.
200      *
201      * @param booleanList bit to boolean mapping
202      * @return bit mask
203      */
204     public static int[] fillBitMaskFromList(final List<Boolean> booleanList) {
205         int[] bitmask;
206         int index = 0;
207         int arrayIndex = 0;
208         if (booleanList.size() % Integer.SIZE != 0) {
209             bitmask = new int[booleanList.size() / Integer.SIZE + 1];
210         } else {
211             bitmask = new int[booleanList.size() / Integer.SIZE];
212         }
213         for (Boolean currElement : booleanList) {
214             if (currElement != null && currElement.booleanValue()) {
215                 bitmask[arrayIndex] |= 1 << index;
216             }
217             index++;
218             arrayIndex = index / Integer.SIZE;
219         }
220         return bitmask;
221     }
222
223     /**
224      * Converts byte array into String.
225      *
226      * @param array input byte array
227      * @return String
228      */
229     public static String bytesToHexString(final byte[] array) {
230         StringBuilder sb = new StringBuilder();
231         for (byte element : array) {
232             sb.append(String.format(" %02x", element));
233         }
234         return sb.toString().trim();
235     }
236
237     private static int hexValue(final char ch) {
238         if (ch >= '0' && ch <= '9') {
239             return ch - '0';
240         }
241         if (ch >= 'a' && ch <= 'f') {
242             return ch - 'a' + 10;
243         }
244         if (ch >= 'A' && ch <= 'F') {
245             return ch - 'A' + 10;
246         }
247
248         throw new IllegalArgumentException(String.format("Invalid character '%s' encountered", ch));
249     }
250
251     /**
252      * Converts macAddress to byte array.
253      * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}.
254      *
255      * @param macAddress the mac address to convert
256      * @return byte representation of mac address
257      */
258     @SuppressWarnings("checkstyle:IllegalCatch")
259     public static byte[] macAddressToBytes(final String macAddress) {
260         final byte[] result = new byte[EncodeConstants.MAC_ADDRESS_LENGTH];
261         final char[] mac = macAddress.toCharArray();
262
263         try {
264             int offset = 0;
265             for (int i = 0; i < EncodeConstants.MAC_ADDRESS_LENGTH - 1; ++i) {
266                 if (mac[offset + EncodeConstants.SIZE_OF_BYTE_IN_BYTES] == ':') {
267                     result[i] = UnsignedBytes.checkedCast(hexValue(mac[offset]));
268                     offset++;
269                 } else {
270                     result[i] = UnsignedBytes.checkedCast(
271                             hexValue(mac[offset]) << 4 | hexValue(mac[offset + 1]));
272                     offset += 2;
273                 }
274                 Preconditions.checkArgument(mac[offset] == ':', "Invalid value: %s", macAddress);
275                 offset++;
276             }
277
278             if (offset == mac.length - 1) {
279                 result[EncodeConstants.MAC_ADDRESS_LENGTH - 1] = UnsignedBytes.checkedCast(hexValue(mac[offset]));
280             } else {
281                 result[EncodeConstants.MAC_ADDRESS_LENGTH - 1] =
282                         UnsignedBytes.checkedCast(hexValue(mac[offset]) << 4 | hexValue(mac[offset + 1]));
283                 offset++;
284             }
285             if (offset != mac.length - 1) {
286                 throw new IllegalArgumentException("Incorrect MAC address length");
287             }
288         } catch (RuntimeException e) {
289             throw new IllegalArgumentException("Unable to serialize MAC address for input: " + macAddress
290                     + ". \n" + e);
291         }
292         return result;
293     }
294
295     private static void appendHexByte(final StringBuilder sb, final byte value) {
296         final int v = UnsignedBytes.toInt(value);
297         sb.append(HEX_CHARS[v >>> 4]);
298         sb.append(HEX_CHARS[v &  15]);
299     }
300
301     private static void appendHexUnsignedShort(final StringBuilder sb, final int val) {
302         sb.append(ByteBufUtils.HEX_CHARS[val >>> 12 & 15]);
303         sb.append(ByteBufUtils.HEX_CHARS[val >>>  8 & 15]);
304         sb.append(ByteBufUtils.HEX_CHARS[val >>>  4 & 15]);
305         sb.append(ByteBufUtils.HEX_CHARS[ val         & 15]);
306     }
307
308     /**
309      * Converts a MAC address represented in bytes to String.
310      * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}.
311      *
312      * @param address the MAC address to convert
313      * @return String representation of a MAC address
314      */
315     public static String macAddressToString(final byte[] address) {
316         Preconditions.checkArgument(address.length == EncodeConstants.MAC_ADDRESS_LENGTH);
317
318         final StringBuilder sb = new StringBuilder(17);
319
320         appendHexByte(sb, address[0]);
321         for (int i = 1; i < EncodeConstants.MAC_ADDRESS_LENGTH; i++) {
322             sb.append(':');
323             appendHexByte(sb, address[i]);
324         }
325
326         return sb.toString();
327     }
328
329     /**
330      * Reads and parses null-terminated string from ByteBuf.
331      *
332      * @param rawMessage the message to parse
333      * @param length maximal length of String
334      * @return String with name of port
335      */
336     public static String decodeNullTerminatedString(final ByteBuf rawMessage, final int length) {
337         byte[] name = new byte[length];
338         rawMessage.readBytes(name);
339         return new String(name, StandardCharsets.UTF_8).trim();
340     }
341
342     /**
343      * Read an IPv4 address from a buffer and format it into dotted-quad string.
344      *
345      * @param buf Input buffer
346      * @return Dotted-quad string
347      */
348     public static String readIpv4Address(final ByteBuf buf) {
349         final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV4_ADDRESS * 4 - 1);
350
351         sb.append(buf.readUnsignedByte());
352         for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV4_ADDRESS; i++) {
353             sb.append('.');
354             sb.append(buf.readUnsignedByte());
355         }
356
357         return sb.toString();
358     }
359
360
361     /**
362      * Read an IPv6 address from a buffer and format it into a string of eight groups of four
363      * hexadecimal digits separated by colons.
364      *
365      * @param buf Input buffer
366      * @return IPv6 address in string format
367      */
368     public static String readIpv6Address(final ByteBuf buf) {
369         final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV6_ADDRESS * 5 - 1);
370
371         appendHexUnsignedShort(sb, buf.readUnsignedShort());
372         for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV6_ADDRESS; i++) {
373             sb.append(':');
374             appendHexUnsignedShort(sb, buf.readUnsignedShort());
375         }
376
377         return sb.toString();
378     }
379
380     public static Ipv4Address readIetfIpv4Address(final ByteBuf buf) {
381         final byte[] tmp = new byte[4];
382         buf.readBytes(tmp);
383         return IetfInetUtil.INSTANCE.ipv4AddressFor(tmp);
384     }
385
386     public static Ipv6Address readIetfIpv6Address(final ByteBuf buf) {
387         final byte[] tmp = new byte[16];
388         buf.readBytes(tmp);
389         return IetfInetUtil.INSTANCE.ipv6AddressFor(tmp);
390     }
391
392     public static MacAddress readIetfMacAddress(final ByteBuf buf) {
393         final byte[] tmp = new byte[EncodeConstants.MAC_ADDRESS_LENGTH];
394         buf.readBytes(tmp);
395         return IetfYangUtil.INSTANCE.macAddressFor(tmp);
396     }
397
398     public static byte[] serializeList(final List<Short> list) throws IOException {
399         ByteBuffer byteBuffer = ByteBuffer.allocate(list.size() * 2);
400         for (Short shortValue : list) {
401             byteBuffer.putShort(shortValue);
402         }
403         return byteBuffer.array();
404     }
405 }