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