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