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