Use StandardCharsets.UTF_8
[bgpcep.git] / util / src / main / java / org / opendaylight / protocol / util / ByteArray.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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 package org.opendaylight.protocol.util;
9
10 import com.google.common.base.Preconditions;
11 import io.netty.buffer.ByteBuf;
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.nio.ByteBuffer;
16 import java.nio.charset.CharacterCodingException;
17 import java.nio.charset.StandardCharsets;
18 import java.util.Arrays;
19 import java.util.BitSet;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Util class for methods working with byte array.
25  */
26 public final class ByteArray {
27
28     private static final Logger LOG = LoggerFactory.getLogger(ByteArray.class);
29
30     private ByteArray() {
31         throw new UnsupportedOperationException();
32     }
33
34     /**
35      * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
36      * at reader index, with given length. Increases reader index of the buffer by 'length'.
37      *
38      * @param buffer ByteBuf from which the bytes are going to be taken
39      * @param length length of the returned byte array
40      * @return byte array
41      */
42     public static byte[] readBytes(final ByteBuf buffer, final int length) {
43         Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
44             "Buffer cannot be read for %s bytes.", length);
45         final byte[] result = new byte[length];
46         buffer.readBytes(result);
47         return result;
48     }
49
50     /**
51      * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
52      * Adjusts reader index of the buffer by length of readable bytes in the buffer.
53      *
54      * @param buffer byteBuf from which the bytes are going to be taken
55      * @return byte array
56      */
57     public static byte[] readAllBytes(final ByteBuf buffer) {
58         return readBytes(buffer, buffer.readableBytes());
59     }
60
61     /**
62      * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
63      * at reader index, with given length. Does not modify reader or writer index of the buffer.
64      *
65      * @param buffer ByteBuf from which the bytes are going to be taken
66      * @param length length of the returned byte array
67      * @return byte array
68      */
69     public static byte[] getBytes(final ByteBuf buffer, final int length) {
70         Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
71             "Buffer cannot be read for %s bytes.", length);
72         final byte[] result = new byte[length];
73         buffer.getBytes(buffer.readerIndex(), result);
74         return result;
75     }
76
77     /**
78      * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
79      * Does not modify writer or reader index of the buffer.
80      *
81      * @param buffer byteBuf from which the bytes are going to be taken
82      * @return byte array
83      */
84     public static byte[] getAllBytes(final ByteBuf buffer) {
85         return getBytes(buffer, buffer.readableBytes());
86     }
87
88     /**
89      * Returns a new byte array from given byte array, starting at start index with the size of the length parameter.
90      * Byte array given as parameter stays untouched.
91      *
92      * @param bytes original byte array
93      * @param startIndex beginning index, inclusive
94      * @param length how many bytes should be in the sub-array
95      * @return a new byte array that is a sub-array of the original
96      */
97     public static byte[] subByte(final byte[] bytes, final int startIndex, final int length) {
98         Preconditions.checkArgument(checkLength(bytes, length) && checkStartIndex(bytes, startIndex, length), "Cannot create subByte, invalid arguments: Length: %s startIndex: %s", length, startIndex);
99         final byte[] res = new byte[length];
100         System.arraycopy(bytes, startIndex, res, 0, length);
101         return res;
102     }
103
104     private static boolean checkLength(final byte[] bytes, final int length) {
105         return length > 0 && bytes.length > 0 && length <= bytes.length;
106     }
107
108     private static boolean checkStartIndex(final byte[] bytes, final int startIndex, final int length) {
109         return startIndex >= 0 && startIndex < bytes.length && (startIndex + length <= bytes.length);
110     }
111
112     /**
113      * Converts byte array to Integer. If there are less bytes in the array as required (4), the method will push
114      * adequate number of zero bytes prepending given byte array.
115      *
116      * @param bytes array to be converted to int
117      * @return int
118      */
119     public static int bytesToInt(final byte[] bytes) {
120         Preconditions.checkArgument(bytes.length <= Integer.SIZE / Byte.SIZE, "Cannot convert bytes to integer. Byte array too big.");
121         byte[] res = new byte[Integer.SIZE / Byte.SIZE];
122         if (bytes.length != Integer.SIZE / Byte.SIZE) {
123             System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
124         } else {
125             res = bytes;
126         }
127         final ByteBuffer buff = ByteBuffer.wrap(res);
128         return buff.getInt();
129     }
130
131     /**
132      * Converts byte array to long. If there are less bytes in the array as required (Long.Size), the method will push
133      * adequate number of zero bytes prepending given byte array.
134      *
135      * @param bytes array to be converted to long
136      * @return long
137      */
138     public static long bytesToLong(final byte[] bytes) {
139         Preconditions.checkArgument(bytes.length <= Long.SIZE / Byte.SIZE, "Cannot convert bytes to long.Byte array too big.");
140         byte[] res = new byte[Long.SIZE / Byte.SIZE];
141         if (bytes.length != Long.SIZE / Byte.SIZE) {
142             System.arraycopy(bytes, 0, res, Long.SIZE / Byte.SIZE - bytes.length, bytes.length);
143         } else {
144             res = bytes;
145         }
146         final ByteBuffer buff = ByteBuffer.wrap(res);
147         return buff.getLong();
148     }
149
150     /**
151      * Cuts 'count' number of bytes from the beginning of given byte array.
152      *
153      * @param bytes array to be cut, cannot be null
154      * @param count how many bytes needed to be cut, needs to be greater than 0
155      * @return bytes array without first 'count' bytes
156      */
157     public static byte[] cutBytes(final byte[] bytes, final int count) {
158         Preconditions.checkArgument(bytes.length != 0 && count <= bytes.length && count > 0, "Cannot cut bytes, invalid arguments: Count: %s bytes.length: %s", count, bytes.length);
159         return Arrays.copyOfRange(bytes, count, bytes.length);
160     }
161
162     /**
163      * Parse byte to bits, from the leftmost bit.
164      *
165      * @param b byte to be parsed
166      * @return array of booleans with size of 8
167      */
168     @Deprecated
169     public static boolean[] parseBits(final byte b) {
170         final boolean[] bits = new boolean[Byte.SIZE];
171         int j = 0;
172         for (int i = Byte.SIZE - 1; i >= 0; i--) {
173             bits[j] = ((b & (1 << i)) != 0);
174             j++;
175         }
176         return bits;
177     }
178
179     /**
180      * Parses array of bytes to BitSet, from left most bit.
181      *
182      * @param bytes array of bytes to be parsed
183      * @return BitSet with length = bytes.length * Byte.SIZE
184      */
185     @Deprecated
186     public static BitSet bytesToBitSet(final byte[] bytes) {
187         final BitSet bitSet = new BitSet(bytes.length * Byte.SIZE);
188         for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
189             final int offset = bytesIter * Byte.SIZE;
190             for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
191                 bitSet.set(offset + (Byte.SIZE - byteIter - 1), (bytes[bytesIter] & 1 << (byteIter)) != 0);
192             }
193         }
194         return bitSet;
195     }
196
197     /**
198      * Parses BitSet to bytes, from most left bit.
199      *
200      * @param bitSet BitSet to be parsed
201      * @param returnedLength Length of returned array. Overlapping flags are truncated.
202      * @return parsed array of bytes with length of bitSet.length / Byte.SIZE
203      */
204     @Deprecated
205     public static byte[] bitSetToBytes(final BitSet bitSet, final int returnedLength) {
206         final byte[] bytes = new byte[returnedLength];
207
208         for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
209             final int offset = bytesIter * Byte.SIZE;
210
211             for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
212                 bytes[bytesIter] |= (bitSet.get(offset + (Byte.SIZE - byteIter - 1)) ? 1 << byteIter : 0);
213             }
214         }
215         return bytes;
216     }
217
218     /**
219      * Parses file to array of bytes
220      *
221      * @param name path to file to by parsed
222      * @return parsed array of bytes
223      */
224     public static byte[] fileToBytes(final String name) throws IOException {
225         final File file = new File(name);
226         int offset = 0;
227         int numRead = 0;
228
229         if (file.length() > Integer.MAX_VALUE) {
230             throw new IOException("Too large file to load in byte array.");
231         }
232         final byte[] byteArray = new byte[(int) file.length()];
233         try (final FileInputStream fin = new FileInputStream(file)) {
234             while (offset < byteArray.length) {
235                 numRead = fin.read(byteArray, offset, byteArray.length - offset);
236                 if (numRead >= 0) {
237                     offset += numRead;
238                 }
239             }
240             fin.close();
241         }
242         return byteArray;
243     }
244
245     /**
246      * Copies range of bits from passed byte and align to right.<br>
247      *
248      * @param src source byte to copy from
249      * @param fromBit bit from which will copy (inclusive) - numbered from 0
250      * @param length of bits to by copied, valid values are 1 through 8
251      * @return copied value aligned to right
252      */
253     public static byte copyBitsRange(final byte src, final int fromBit, final int length) {
254         Preconditions.checkArgument(fromBit >= 0 && fromBit <= Byte.SIZE - 1 && length >= 1 && length <= Byte.SIZE, "fromBit or toBit is out of range.");
255         Preconditions.checkArgument(fromBit + length <= Byte.SIZE, "Out of range.");
256
257         byte retByte = 0;
258         int retI = 0;
259
260         for (int i = fromBit + length - 1; i >= fromBit; i--) {
261
262             if ((src & 1 << (Byte.SIZE - i - 1)) != 0) {
263                 retByte |= 1 << retI;
264             }
265
266             retI++;
267         }
268
269         return retByte;
270     }
271
272     /**
273      * Decodes bytes to human readable UTF-8 string. If bytes are not valid UTF-8, they are represented as raw binary.
274      *
275      * @param bytes bytes to be decoded to string
276      * @return String representation of passed bytes
277      */
278     public static String bytesToHRString(final byte[] bytes) {
279         try {
280             return StandardCharsets.UTF_8.newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
281         } catch (final CharacterCodingException e) {
282             LOG.debug("Could not apply UTF-8 encoding.", e);
283             return Arrays.toString(bytes);
284         }
285     }
286 }