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