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