001/**
002 * Copyright (c) 2014-2015 Digi International Inc.,
003 * All rights not expressly granted are reserved.
004 *
005 * This Source Code Form is subject to the terms of the Mozilla Public
006 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
007 * You can obtain one at http://mozilla.org/MPL/2.0/.
008 *
009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
010 * =======================================================================
011 */
012package com.digi.xbee.api.utils;
013
014import java.io.ByteArrayInputStream;
015import java.io.ByteArrayOutputStream;
016
017/**
018 * Utility class containing methods to work with bytes and byte arrays and 
019 * several data type conversions.
020 */
021public class ByteUtils {
022
023        /**
024         * Reads the given amount of bytes from the given byte array input stream.
025         * 
026         * @param numBytes Number of bytes to read.
027         * @param inputStream Byte array input stream to read bytes from.
028         * 
029         * @return An array with the read bytes.
030         * 
031         * @throws IllegalArgumentException if {@code numBytes < 0}.
032         * @throws NullPointerException if {@code inputStream == null}.
033         * 
034         * @see #readString(ByteArrayInputStream)
035         * @see #readUntilCR(ByteArrayInputStream)
036         */
037        public static byte[] readBytes(int numBytes, ByteArrayInputStream inputStream) {
038                if (inputStream == null)
039                        throw new NullPointerException("Input stream cannot be null.");
040                if (numBytes < 0)
041                        throw new IllegalArgumentException("Number of bytes to read must be greater than 0.");
042                
043                byte[] data = new byte[numBytes];
044                int len = inputStream.read(data, 0, numBytes);
045                if (len < numBytes) {
046                        byte[] d = new byte[len];
047                        System.arraycopy(data, 0, d, 0, len);
048                        return d;
049                }
050                return data;
051        }
052        
053        /**
054         * Reads a null-terminated string from the given byte array input stream.
055         * 
056         * @param inputStream Byte array input stream to read string  from.
057         * 
058         * @return The read string from the given {@code ByteArrayInputStream}.
059         * 
060         * @throws NullPointerException if {@code inputStream == null}.
061         * 
062         * @see #readBytes(int, ByteArrayInputStream)
063         * @see #readUntilCR(ByteArrayInputStream)
064         */
065        public static String readString(ByteArrayInputStream inputStream) {
066                if (inputStream == null)
067                        throw new NullPointerException("Input stream cannot be null.");
068                
069                StringBuilder sb = new StringBuilder();
070                byte readByte;
071                while (((readByte = (byte)inputStream.read()) != 0x00) && readByte != -1)
072                        sb.append((char)readByte);
073                return sb.toString();
074        }
075        
076        /**
077         * Converts the given long value into a byte array.
078         * 
079         * @param value Long value to convert to byte array.
080         * 
081         * @return Byte array of the given long value (8 bytes length).
082         * 
083         * @see #byteArrayToLong(byte[])
084         */
085        public static byte[] longToByteArray(long value) {
086                return new byte[] {
087                                (byte)((value >>> 56) & 0xFF),
088                                (byte)((value >>> 48) & 0xFF),
089                                (byte)((value >>> 40) & 0xFF),
090                                (byte)((value >>> 32) & 0xFF),
091                                (byte)((value >>> 24) & 0xFF),
092                                (byte)((value >>> 16) & 0xFF),
093                                (byte)((value >>> 8) & 0xFF),
094                                (byte)(value & 0xFF)
095                };
096        }
097        
098        /**
099         * Converts the given byte array (8 bytes length max) into a long.
100         * 
101         * @param byteArray Byte array to convert to long (8 bytes length max).
102         * 
103         * @return Converted long value.
104         * 
105         * @throws NullPointerException if {@code b == null}.
106         * 
107         * @see #longToByteArray(long)
108         */
109        public static long byteArrayToLong(byte[] byteArray) {
110                if (byteArray == null)
111                        throw new NullPointerException("Byte array cannot be null.");
112                
113                byte[] values = byteArray;
114                if (byteArray.length < 8) {
115                        values = new byte[8];
116                        int diff = 8 - byteArray.length;
117                        for (int i = 0; i < diff; i++)
118                                values[i] = 0;
119                        for (int i = diff; i < 8; i++)
120                                values[i] = byteArray[i - diff];
121                }
122                return ((long)values[0] << 56) 
123                                + ((long)(values[1] & 0xFF) << 48) 
124                                + ((long)(values[2] & 0xFF) << 40) 
125                                + ((long)(values[3] & 0xFF) << 32) 
126                                + ((long)(values[4] & 0xFF) << 24) 
127                                + ((values[5] & 0xFF) << 16) 
128                                + ((values[6] & 0xFF) <<  8) 
129                                + (values[7] & 0xFF);
130        }
131        
132        /**
133         * Converts the given integer value into a byte array.
134         * 
135         * @param value Integer value to convert to byte array.
136         * 
137         * @return Byte array of the given integer (4 bytes length).
138         * 
139         * @see #byteArrayToInt(byte[])
140         */
141        public static byte[] intToByteArray(int value) {
142                return new byte[] {
143                                (byte)((value >>> 24) & 0xFF),
144                                (byte)((value >>> 16) & 0xFF),
145                                (byte)((value >>> 8) & 0xFF),
146                                (byte)(value & 0xFF)
147                };
148        }
149        
150        /**
151         * Converts the given byte array (4 bytes length max) into an integer.
152         * 
153         * @param byteArray Byte array to convert to integer (4 bytes length max).
154         * 
155         * @return Converted integer value.
156         * 
157         * @throws NullPointerException if {@code byteArray == null}.
158         * 
159         * @see #intToByteArray(int)
160         */
161        public static int byteArrayToInt(byte[] byteArray) {
162                if (byteArray == null)
163                        throw new NullPointerException("Byte array cannot be null.");
164                
165                byte[] values = byteArray;
166                if (byteArray.length < 4) {
167                        values = new byte[4];
168                        int diff = 4 - byteArray.length;
169                        for (int i = 0; i < diff; i++)
170                                values[i] = 0;
171                        for (int i = diff; i < 4; i++)
172                                values[i] = byteArray[i - diff];
173                }
174                return ((values[0] & 0xFF) << 24)
175                                | ((values[1] & 0xFF) << 16)
176                                | ((values[2] & 0xFF) << 8)
177                                | (values[3] & 0xFF);
178        }
179        
180        /**
181         * Converts the given short value into a byte array.
182         * 
183         * @param value Short value to convert to byte array.
184         * 
185         * @return Byte array of the given short (2 bytes length).
186         * 
187         * @see #byteArrayToShort(byte[])
188         */
189        public static byte[] shortToByteArray(short value) {
190                byte[] b = new byte[2];
191                b[0] = (byte)((value >> 8) & 0xFF);
192                b[1] = (byte)(value & 0xFF);
193                return b;
194        }
195        
196        /**
197         * Converts the given byte array (2 bytes length max) to short.
198         * 
199         * @param byteArray Byte array to convert to short (2 bytes length max).
200         * 
201         * @return Converted short value.
202         * 
203         * @throws NullPointerException if {@code byteArray == null}.
204         * 
205         * @see #shortToByteArray(short)
206         */
207        public static short byteArrayToShort(byte[] byteArray) {
208                if (byteArray == null)
209                        throw new NullPointerException("Byte array cannot be null.");
210                
211                return (short) (((byteArray[0] << 8) & 0xFF00) 
212                                                | byteArray[1] & 0x00FF);
213        }
214        
215        /**
216         * Converts the given string into a byte array.
217         * 
218         * @param value String to convert to byte array.
219         * 
220         * @return Byte array of the given string.
221         * 
222         * @throws NullPointerException if {@code value == null}.
223         * 
224         * @see #byteArrayToString(byte[])
225         */
226        public static byte[] stringToByteArray(String value) {
227                if (value == null)
228                        throw new NullPointerException("Value cannot be null.");
229                
230                return value.getBytes();
231        }
232        
233        /**
234         * Converts the given byte array into a string.
235         * 
236         * @param value Byte array to convert to string.
237         * 
238         * @return Converted String.
239         * 
240         * @throws NullPointerException if {@code value == null}.
241         */
242        public static String byteArrayToString(byte[] value) {
243                if (value == null)
244                        throw new NullPointerException("Byte array cannot be null.");
245                
246                return new String(value);
247        }
248        
249        /**
250         * Converts the given byte into an integer.
251         * 
252         * @param b Byte to convert to integer.
253         * 
254         * @return Converted byte into integer.
255         */
256        public static int byteToInt(byte b) {
257                return (int) b & 0xFF;
258        }
259        
260        /**
261         * Returns whether the specified bit of the given integer is set to 1
262         * or not.
263         * 
264         * @param containerInteger Integer to check the given bit position
265         *                         enablement state.
266         * @param bitPosition Position of the bit to check its enablement state.
267         * 
268         * @return {@code true} if the given bit position is set to {@code 1} 
269         *         in the {@code containerInteger}, {@code false} otherwise.
270         */
271        public static boolean isBitEnabled(int containerInteger, int bitPosition) {
272                return (((containerInteger & 0xFFFFFFFF) >> bitPosition) & 0x01) == 0x01;
273        }
274        
275        /**
276         * Reads an integer value from the given byte using the given bit offset 
277         * and the given bit size.
278         * 
279         * @param containerByte Byte to read the integer from.
280         * @param bitOffset Offset inside the byte to start reading integer value.
281         * @param bitLength Size in bits of the integer value to read.
282         * 
283         * @return The integer read value.
284         */
285        public static int readIntegerFromByte(byte containerByte, int bitOffset, int bitLength) {
286                int readInteger = 0;
287                for (int i = 0; i < bitLength; i++) {
288                        if (isBitEnabled(containerByte, bitOffset + i))
289                                readInteger = readInteger | (int)Math.pow(2, i);
290                }
291                return readInteger;
292        }
293        
294        /**
295         * Reads a boolean value from the given byte at the given bit position.
296         * 
297         * @param containerByte Byte to read boolean value from.
298         * @param bitOffset Offset inside the byte to read the boolean value.
299         * 
300         * @return The read boolean value.
301         */
302        public static boolean readBooleanFromByte(byte containerByte, int bitOffset) {
303                return isBitEnabled(containerByte, bitOffset);
304        }
305        
306        /**
307         * Reads from the given byte array input stream until a CR character is
308         * found or the end of stream is reached. Read bytes are returned.
309         * 
310         * @param inputStream Byte array input stream to read from.
311         * 
312         * @return An array with the read bytes.
313         * 
314         * @throws NullPointerException if {@code inputStream == null}.
315         * 
316         * @see #readBytes(int, ByteArrayInputStream)
317         * @see #readString(ByteArrayInputStream)
318         */
319        public static byte[] readUntilCR(ByteArrayInputStream inputStream) {
320                if (inputStream == null)
321                        throw new NullPointerException("Input stream cannot be null.");
322                
323                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
324                byte readByte;
325                while (((readByte = (byte)inputStream.read()) != 0x0D) && readByte != -1)
326                        outputStream.write(readByte);
327                return outputStream.toByteArray();
328        }
329        
330        /**
331         * Generates a new byte array of the given size using the given data and 
332         * filling with ASCII zeros (0x48) the remaining space.
333         * 
334         * <p>If new size is lower than current, array is truncated.</p>
335         * 
336         * @param data Data to use in the new array.
337         * @param finalSize Final size of the array.
338         * 
339         * @return Final byte array of the given size containing the given data and
340         *         replacing with zeros the remaining space.
341         * 
342         * @throws NullPointerException if {@code data == null}.
343         */
344        public static byte[] newByteArray(byte[] data, int finalSize) {
345                if (data == null)
346                        throw new NullPointerException("Data cannot be null.");
347                
348                byte[] filledArray = new byte[finalSize];
349                int diff = finalSize - data.length;
350                if (diff >= 0) {
351                        for (int i = 0; i < diff; i++)
352                                filledArray[i] = '0';
353                        System.arraycopy(data, 0, filledArray, diff, data.length);
354                } else 
355                        System.arraycopy(data, 0, filledArray, 0, finalSize);
356                return filledArray;
357        }
358        
359        /**
360         * Swaps the given byte array order.
361         * 
362         * @param source Byte array to swap.
363         * 
364         * @return The swapped byte array.
365         */
366        public static byte[] swapByteArray(byte[] source) {
367                byte[] swapped = new byte[source.length];
368                for (int i = 0; i < source.length; i++)
369                        swapped[source.length - i - 1] = source[i];
370                return swapped;
371        }
372}