/* * Copyright 2015-2016 Cel Skeggs * * This file is part of the CCRE, the Common Chicken Runtime Engine. * * The CCRE is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with the CCRE. If not, see <http://www.gnu.org/licenses/>. */ package ccre.drivers; import ccre.log.Logger; /** * A collection of useful byte-level fiddling utilities. * * @author skeggsc */ public class ByteFiddling { /** * Finds the first index of the byte b in the byte range, or -1 if it cannot * be found in that range. * * @param bytes the byte array. * @param from the start of the range. * @param to the end of the range. * @param b the byte to look for. * @return the index, or -1 if not found. */ public static int indexOf(byte[] bytes, int from, int to, byte b) { for (int i = from; i < to; i++) { if (bytes[i] == b) { return i; } } return -1; } /** * Extracts a subsequence of the bytes. * * @param bytes the byte array to copy from. * @param from the start of the range. * @param to the end of the range. * @return the bytes from the range. */ public static byte[] sub(byte[] bytes, int from, int to) { byte[] out = new byte[to - from]; System.arraycopy(bytes, from, out, 0, to - from); return out; } /** * Parses the ASCII characters in the byte array into an integer. * * @param bytes the characters to parse. * @return the integer, or null if it cannot be parsed. */ public static Integer parseInt(byte[] bytes) { return parseInt(bytes, 0, bytes.length); } /** * Parses the ASCII characters in the byte section into an integer. * * @param bytes the byte array to parse. * @param from the start of the byte section. * @param to the end of the byte section. * @return the integer, or null if it cannot be parsed. */ public static Integer parseInt(byte[] bytes, int from, int to) { if (to <= from) { return null; } boolean neg = bytes[from] == '-'; if (neg) { from++; if (to <= from) { return null; } } int num = 0; for (int i = from; i < to; i++) { int digit = bytes[i] - '0'; if (digit < 0 || digit > 9) { return null; } num = (num * 10) + digit; } return neg ? -num : num; } /** * Counts the number of occurrences of the byte b in the byte array. * * @param bytes the byte array to search. * @param b the byte to look for. * @return the number of instances of the byte. */ public static int count(byte[] bytes, byte b) { return count(bytes, 0, bytes.length, b); } /** * Counts the number of occurrences of the byte b in the byte section. * * @param bytes the byte array to search. * @param from the start of the byte section. * @param to the end of the byte section. * @param b the byte to look for. * @return the number of instances of the byte. */ public static int count(byte[] bytes, int from, int to, byte b) { int count = 0; for (int i = from; i < to; i++) { if (bytes[i] == b) { count++; } } return count; } /** * Splits the byte section into multiple byte arrays with b as the * delimiter. * * Will always return 1 + n byte arrays, where n is the number of instances * of the byte in the byte section. * * @param bytes the byte array to search. * @param from the start of the byte section. * @param to the end of the byte section. * @param b the byte to split on. * @return the byte arrays. */ public static byte[][] split(byte[] bytes, int from, int to, byte b) { byte[][] out = new byte[count(bytes, b) + 1][]; for (int i = 0; i < out.length; i++) { int next = indexOf(bytes, from, to, b); out[i] = sub(bytes, from, next); from = next + 1; } return out; } /** * Checks if the byte array (interpreted as ASCII) is the same as the given * string. * * @param a the byte array to compare. * @param b the string to compare. * @return if the sequences contain the same character data. */ public static boolean streq(byte[] a, String b) { int len = a.length; if (len != b.length()) { return false; } for (int i = 0; i < len; i++) { if (a[i] != (byte) b.charAt(i)) { return false; } } return true; } /** * Parses the ASCII characters in the byte array into a double. * * @param bytes the byte array to parse. * @return the double, or null if it cannot be parsed. */ public static Double parseDouble(byte[] bytes) { return parseDouble(bytes, 0, bytes.length); } /** * Parses the ASCII characters in the byte section into a double. * * @param bytes the byte array to parse. * @param from the start of the byte section. * @param to the end of the byte section. * @return the double, or null if it cannot be parsed. */ public static Double parseDouble(byte[] bytes, int from, int to) { try { return Double.parseDouble(parseASCII(bytes, from, to)); } catch (NumberFormatException ex) { return null; } } /** * Parse the ASCII characters in the byte array into a string. * * @param bytes the byte array to parse. * @return the string. */ public static String parseASCII(byte[] bytes) { return parseASCII(bytes, 0, bytes.length); } /** * Parses the ASCII characters in the byte section into a string. * * @param bytes the byte array to parse. * @param from the start of the byte section. * @param to the end of the byte section. * @return the string. */ public static String parseASCII(byte[] bytes, int from, int to) { char[] conv = new char[to - from]; for (int i = from, j = 0; i < to; i++, j++) { conv[j] = (char) (bytes[i] & 0xFF); } return new String(conv); } /** * Encodes the byte section into a hexadecimal string. * * @param bytes the byte array to encode. * @param from the start of the byte section. * @param to the end of the byte section. * @return the hexadecimal version. */ public static String toHex(byte[] bytes, int from, int to) { if (to > bytes.length || to < from || from < 0) { throw new ArrayIndexOutOfBoundsException("Bad toHex arguments: " + from + ";" + to); } char[] out = new char[2 * (to - from)]; for (int i = from, j = 0; i < to; i++) { try { out[j++] = hex[(bytes[i] >> 4) & 0xF]; out[j++] = hex[bytes[i] & 0xF]; } catch (ArrayIndexOutOfBoundsException ex) { Logger.warning("Offending indexes: " + j + "," + i + ": " + from + "," + to + "," + bytes.length + "," + out.length); throw ex; } } return new String(out); } private static final char[] hex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * Encodes a byte into a two-character hexadecimal string. * * @param b the byte to encode. * @return the hexadecimal version. */ public static String toHex(byte b) { return new String(new char[] { toHexNibble((b >> 4) & 0xF), toHexNibble(b & 0xF) }); } /** * Encodes a nibble into a hexadecimal character. * * @param i the nibble to encode. * @return the hexadecimal version. */ public static char toHexNibble(int i) { if (i < 0 || i >= 16) { throw new IllegalArgumentException("Not a valid hex nibble!"); } return hex[i]; } /** * Converts four little-endian bytes from data into an integer. * * @param data the data to extract the bytes from * @param from the index of the first byte * @return the extracted integer. */ public static int asInt32LE(byte[] data, int from) { return (data[from] & 0xFF) | ((data[from + 1] & 0xFF) << 8) | ((data[from + 2] & 0xFF) << 16) | ((data[from + 3] & 0xFF) << 24); } /** * Converts four big-endian bytes from data into an integer. * * @param data the data to extract the bytes from * @param from the index of the first byte * @return the extracted integer. */ public static int asInt32BE(byte[] data, int from) { return ((data[from] & 0xFF) << 24) | ((data[from + 1] & 0xFF) << 16) | ((data[from + 2] & 0xFF) << 8) | (data[from + 3] & 0xFF); } /** * Converts two little-endian bytes from data into an unsigned integer. * * @param data the data to extract the bytes from * @param from the index of the first byte * @return the extracted integer. */ public static int asInt16LE(byte[] data, int from) { return (data[from] & 0xFF) | ((data[from + 1] & 0xFF) << 8); } /** * Converts two big-endian bytes from data into an unsigned integer. * * @param data the data to extract the bytes from * @param from the index of the first byte * @return the extracted integer. */ public static int asInt16BE(byte[] data, int from) { return ((data[from] & 0xFF) << 8) | (data[from + 1] & 0xFF); } }