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