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