BUG-2825: use provided Ipv4/MacAddress factories
[openflowjava.git] / 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
10 package org.opendaylight.openflowjava.util;
11
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.collect.Lists;
15 import com.google.common.primitives.UnsignedBytes;
16 import io.netty.buffer.ByteBuf;
17 import io.netty.buffer.UnpooledByteBufAllocator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IetfInetUtil;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.IetfYangUtil;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
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     private static final Splitter HEXSTRING_SPLITTER =  Splitter.onPattern("\\s+").omitEmptyStrings();
38     private static final Splitter HEXSTRING_NOSPACE_SPLITTER = Splitter.onPattern("(?<=\\G.{2})").omitEmptyStrings();
39
40     private ByteBufUtils() {
41         //not called
42     }
43
44     /**
45      * Converts ByteBuf into String
46      * @param bb input ByteBuf
47      * @return String
48      */
49     public static String byteBufToHexString(final ByteBuf bb) {
50         StringBuilder sb = new StringBuilder();
51         for (int i = bb.readerIndex(); i < (bb.readerIndex() + bb.readableBytes()); i++) {
52             sb.append(String.format(" %02x", bb.getUnsignedByte(i)));
53         }
54         return sb.toString().trim();
55     }
56
57     /**
58      * Converts String into byte[]
59      * @param hexSrc input String
60      * @return byte[] filled with input data
61      */
62     public static byte[] hexStringToBytes(final String hexSrc) {
63         return hexStringToBytes(hexSrc, true);
64     }
65
66     /**
67      * Converts String into byte[]
68      * @param hexSrc input String
69      * @param withSpaces if there are spaces in string
70      * @return byte[] filled with input data
71      */
72     public static byte[] hexStringToBytes(final String hexSrc, final boolean withSpaces) {
73         final Splitter splitter = withSpaces ? HEXSTRING_SPLITTER : HEXSTRING_NOSPACE_SPLITTER;
74         List<String> byteChips = Lists.newArrayList(splitter.split(hexSrc));
75         byte[] result = new byte[byteChips.size()];
76         int i = 0;
77         for (String chip : byteChips) {
78             result[i] = (byte) Short.parseShort(chip, 16);
79             i++;
80         }
81         return result;
82     }
83
84     /**
85      * Creates ByteBuf filled with specified data
86      * @param hexSrc input String of bytes in hex format
87      * @return ByteBuf with specified hexString converted
88      */
89     public static ByteBuf hexStringToByteBuf(final String hexSrc) {
90         ByteBuf out = UnpooledByteBufAllocator.DEFAULT.buffer();
91         hexStringToByteBuf(hexSrc, out);
92         return out;
93     }
94
95     /**
96      * Creates ByteBuf filled with specified data
97      * @param hexSrc input String of bytes in hex format
98      * @param out ByteBuf with specified hexString converted
99      */
100     public static void hexStringToByteBuf(final String hexSrc, final ByteBuf out) {
101         out.writeBytes(hexStringToBytes(hexSrc));
102     }
103
104     /**
105      * Fills specified ByteBuf with 0 (zeros) of desired length, used for padding
106      * @param length
107      * @param out ByteBuf to be padded
108      * @deprecated Use {@link ByteBuf#writeZero(int)} directly.
109      */
110     @Deprecated
111     public static void padBuffer(final int length, final ByteBuf out) {
112         out.writeZero(length);
113     }
114
115     /**
116      * Create standard OF header
117      * @param msgType message code
118      * @param message POJO
119      * @param out writing buffer
120      * @param length ofheader length
121      */
122     public static <E extends OfHeader> void writeOFHeader(final byte msgType, final E message, final ByteBuf out, final int length) {
123         out.writeByte(message.getVersion());
124         out.writeByte(msgType);
125         out.writeShort(length);
126         out.writeInt(message.getXid().intValue());
127     }
128
129     /**
130      * Write length standard OF header
131      * @param out writing buffer
132      */
133     public static void updateOFHeaderLength(final ByteBuf out) {
134         out.setShort(EncodeConstants.OFHEADER_LENGTH_INDEX, out.readableBytes());
135     }
136
137     /**
138      * Fills the bitmask from boolean map where key is bit position
139      * @param booleanMap bit to boolean mapping
140      * @return bit mask
141      */
142     public static int fillBitMaskFromMap(final Map<Integer, Boolean> booleanMap) {
143         int bitmask = 0;
144
145         for (Entry<Integer, Boolean> iterator : booleanMap.entrySet()) {
146             if (iterator.getValue() != null && iterator.getValue().booleanValue()) {
147                 bitmask |= 1 << iterator.getKey();
148             }
149         }
150         return bitmask;
151     }
152
153     /**
154      * Fills the bitmask from a set of bit values, starting at specified offset.
155      *
156      * @param offset Bit offset to start at
157      * @param values boolean bit values to fill
158      * @return Filled-in bitmask
159      */
160     public static int fillBitMask(final int offset, final boolean... values) {
161         int bitmask = 0;
162
163         int i = offset;
164         for (boolean v : values) {
165             if (v) {
166                 bitmask |= 1 << i;
167             }
168             ++i;
169         }
170
171         return bitmask;
172     }
173
174     /**
175      * Fills the bitmask from boolean list where key is bit position
176      * @param booleanList bit to boolean mapping
177      * @return bit mask
178      */
179     public static int[] fillBitMaskFromList(final List<Boolean> booleanList) {
180         int[] bitmask;
181         int index = 0;
182         int arrayIndex = 0;
183         if ((booleanList.size() % Integer.SIZE) != 0) {
184             bitmask = new int[booleanList.size() / Integer.SIZE + 1];
185         } else {
186             bitmask = new int[booleanList.size() / Integer.SIZE];
187         }
188         for (Boolean currElement : booleanList) {
189             if (currElement != null && currElement.booleanValue()) {
190                 bitmask[arrayIndex] |= 1 << index;
191             }
192             index++;
193             arrayIndex = index / Integer.SIZE;
194         }
195         return bitmask;
196     }
197
198     /**
199      * Converts byte array into String
200      * @param array input byte array
201      * @return String
202      */
203     public static String bytesToHexString(final byte[] array) {
204         StringBuilder sb = new StringBuilder();
205         for (byte element : array) {
206             sb.append(String.format(" %02x", element));
207         }
208         return sb.toString().trim();
209     }
210
211     private static int hexValue(final char c) {
212         if (c >= '0' && c <= '9') {
213             return c - '0';
214         }
215         if (c >= 'a' && c <= 'f') {
216             return c - 'a' + 10;
217         }
218         if (c >= 'A' && c <= 'F') {
219             return c - 'A' + 10;
220         }
221
222         throw new IllegalArgumentException(String.format("Invalid character '%s' encountered", c));
223     }
224
225     /**
226      * Converts macAddress to byte array.
227      * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}.
228      *
229      * @param macAddress
230      * @return byte representation of mac address
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      * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}.
284      *
285      * @param address
286      * @return String representation of a MAC address
287      */
288     public static String macAddressToString(final byte[] address) {
289         Preconditions.checkArgument(address.length == EncodeConstants.MAC_ADDRESS_LENGTH);
290
291         final StringBuilder sb = new StringBuilder(17);
292
293         appendHexByte(sb, address[0]);
294         for (int i = 1; i < EncodeConstants.MAC_ADDRESS_LENGTH; i++) {
295             sb.append(':');
296             appendHexByte(sb, address[i]);
297         }
298
299         return sb.toString();
300     }
301
302     /**
303      * Reads and parses null-terminated string from ByteBuf
304      * @param rawMessage
305      * @param length maximal length of String
306      * @return String with name of port
307      */
308     public static String decodeNullTerminatedString(final ByteBuf rawMessage, final int length) {
309         byte[] name = new byte[length];
310         rawMessage.readBytes(name);
311         return new String(name).trim();
312     }
313
314     /**
315      * Read an IPv4 address from a buffer and format it into dotted-quad string.
316      *
317      * @param buf Input buffer
318      * @return Dotted-quad string
319      */
320     public static String readIpv4Address(final ByteBuf buf) {
321         final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV4_ADDRESS * 4 - 1);
322
323         sb.append(buf.readUnsignedByte());
324         for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV4_ADDRESS; i++) {
325             sb.append('.');
326             sb.append(buf.readUnsignedByte());
327         }
328
329         return sb.toString();
330     }
331
332
333     /**
334      * Read an IPv6 address from a buffer and format it into a string of eight groups of four
335      * hexadecimal digits separated by colons.
336      *
337      * @param buf Input buffer
338      * @return IPv6 address in string format
339      */
340     public static String readIpv6Address(final ByteBuf buf) {
341         final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV6_ADDRESS * 5 - 1);
342
343         appendHexUnsignedShort(sb, buf.readUnsignedShort());
344         for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV6_ADDRESS; i++) {
345             sb.append(':');
346             appendHexUnsignedShort(sb, buf.readUnsignedShort());
347         }
348
349         return sb.toString();
350     }
351
352     public static Ipv4Address readIetfIpv4Address(final ByteBuf buf) {
353         final byte[] tmp = new byte[4];
354         buf.readBytes(tmp);
355         return IetfInetUtil.INSTANCE.ipv4AddressFor(tmp);
356     }
357
358     public static Ipv6Address readIetfIpv6Address(final ByteBuf buf) {
359         final byte[] tmp = new byte[16];
360         buf.readBytes(tmp);
361         return IetfInetUtil.INSTANCE.ipv6AddressFor(tmp);
362     }
363
364     public static MacAddress readIetfMacAddress(final ByteBuf buf) {
365         final byte[] tmp = new byte[EncodeConstants.MAC_ADDRESS_LENGTH];
366         buf.readBytes(tmp);
367         return IetfYangUtil.INSTANCE.macAddressFor(tmp);
368     }
369 }