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