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