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