/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.util; import nxt.Constants; import nxt.NxtException; import nxt.crypto.Crypto; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public final class Convert { private static final char[] hexChars = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; private static final long[] multipliers = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; public static final BigInteger two64 = new BigInteger("18446744073709551616"); public static final long[] EMPTY_LONG = new long[0]; public static final byte[] EMPTY_BYTE = new byte[0]; public static final byte[][] EMPTY_BYTES = new byte[0][]; public static final String[] EMPTY_STRING = new String[0]; private Convert() {} //never public static byte[] parseHexString(String hex) { if (hex == null) { return null; } byte[] bytes = new byte[hex.length() / 2]; for (int i = 0; i < bytes.length; i++) { int char1 = hex.charAt(i * 2); char1 = char1 > 0x60 ? char1 - 0x57 : char1 - 0x30; int char2 = hex.charAt(i * 2 + 1); char2 = char2 > 0x60 ? char2 - 0x57 : char2 - 0x30; if (char1 < 0 || char2 < 0 || char1 > 15 || char2 > 15) { throw new NumberFormatException("Invalid hex number: " + hex); } bytes[i] = (byte)((char1 << 4) + char2); } return bytes; } public static String toHexString(byte[] bytes) { if (bytes == null) { return null; } char[] chars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { chars[i * 2] = hexChars[((bytes[i] >> 4) & 0xF)]; chars[i * 2 + 1] = hexChars[(bytes[i] & 0xF)]; } return String.valueOf(chars); } public static long parseUnsignedLong(String number) { if (number == null) { return 0; } return Long.parseUnsignedLong(number); } public static long parseLong(Object o) { if (o == null) { return 0; } else if (o instanceof Long) { return ((Long)o); } else if (o instanceof String) { return Long.parseLong((String)o); } else { throw new IllegalArgumentException("Not a long: " + o); } } public static long parseAccountId(String account) { if (account == null || (account = account.trim()).isEmpty()) { return 0; } account = account.toUpperCase(); if (account.startsWith("NXT-")) { return Crypto.rsDecode(account.substring(4)); } else { return Long.parseUnsignedLong(account); } } public static String rsAccount(long accountId) { return "NXT-" + Crypto.rsEncode(accountId); } public static long fullHashToId(byte[] hash) { if (hash == null || hash.length < 8) { throw new IllegalArgumentException("Invalid hash: " + Arrays.toString(hash)); } BigInteger bigInteger = new BigInteger(1, new byte[] {hash[7], hash[6], hash[5], hash[4], hash[3], hash[2], hash[1], hash[0]}); return bigInteger.longValue(); } public static long fromEpochTime(int epochTime) { return epochTime * 1000L + Constants.EPOCH_BEGINNING - 500L; } public static int toEpochTime(long currentTime) { return (int)((currentTime - Constants.EPOCH_BEGINNING + 500) / 1000); } public static String emptyToNull(String s) { return s == null || s.length() == 0 ? null : s; } public static String nullToEmpty(String s) { return s == null ? "" : s; } public static byte[] emptyToNull(byte[] bytes) { if (bytes == null) { return null; } for (byte b : bytes) { if (b != 0) { return bytes; } } return null; } public static byte[][] nullToEmpty(byte[][] bytes) { return bytes == null ? EMPTY_BYTES : bytes; } public static long[] nullToEmpty(long[] array) { return array == null ? EMPTY_LONG : array; } public static long nullToZero(Long l) { return l == null ? 0 : l; } public static long[] toArray(List<Long> list) { long[] result = new long[list.size()]; for (int i = 0; i < list.size(); i++) { result[i] = list.get(i); } return result; } public static List<Long> toList(long[] array) { List<Long> result = new ArrayList<>(array.length); for (long elem : array) { result.add(elem); } return result; } public static Long[] toArray(long[] array) { Long[] result = new Long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } return result; } public static long[] toArray(Long[] array) { long[] result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } return result; } public static Set<Long> toSet(long[] array) { if (array == null || array.length ==0) { return Collections.emptySet(); } Set<Long> set = new HashSet<>(array.length); for (long elem : array) { set.add(elem); } return set; } public static byte[] toBytes(String s) { try { return s.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString(), e); } } public static byte[] toBytes(String s, boolean isText) { return isText ? toBytes(s) : parseHexString(s); } public static String toString(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString(), e); } } public static String toString(byte[] bytes, boolean isText) { return isText ? toString(bytes) : toHexString(bytes); } public static byte[] toBytes(long n) { byte[] bytes = new byte[8]; for (int i = 0; i < 8; i++) { bytes[i] = (byte)(n >> (8 * i)); } return bytes; } public static String readString(ByteBuffer buffer, int numBytes, int maxLength) throws NxtException.NotValidException { if (numBytes > 3 * maxLength) { throw new NxtException.NotValidException("Max parameter length exceeded"); } byte[] bytes = new byte[numBytes]; buffer.get(bytes); return Convert.toString(bytes); } public static String truncate(String s, String replaceNull, int limit, boolean dots) { return s == null ? replaceNull : s.length() > limit ? (s.substring(0, dots ? limit - 3 : limit) + (dots ? "..." : "")) : s; } public static long parseNXT(String nxt) { return parseStringFraction(nxt, 8, Constants.MAX_BALANCE_NXT); } private static long parseStringFraction(String value, int decimals, long maxValue) { String[] s = value.trim().split("\\."); if (s.length == 0 || s.length > 2) { throw new NumberFormatException("Invalid number: " + value); } long wholePart = Long.parseLong(s[0]); if (wholePart > maxValue) { throw new IllegalArgumentException("Whole part of value exceeds maximum possible"); } if (s.length == 1) { return wholePart * multipliers[decimals]; } long fractionalPart = Long.parseLong(s[1]); if (fractionalPart >= multipliers[decimals] || s[1].length() > decimals) { throw new IllegalArgumentException("Fractional part exceeds maximum allowed divisibility"); } for (int i = s[1].length(); i < decimals; i++) { fractionalPart *= 10; } return wholePart * multipliers[decimals] + fractionalPart; } public static byte[] compress(byte[] bytes) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(bos)) { gzip.write(bytes); gzip.flush(); gzip.close(); return bos.toByteArray(); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } public static byte[] uncompress(byte[] bytes) { try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); GZIPInputStream gzip = new GZIPInputStream(bis); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int nRead; while ((nRead = gzip.read(buffer, 0, buffer.length)) > 0) { bos.write(buffer, 0, nRead); } bos.flush(); return bos.toByteArray(); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } public static final Comparator<byte[]> byteArrayComparator = (o1, o2) -> { int minLength = Math.min(o1.length, o2.length); for (int i = 0; i < minLength; i++) { int result = Byte.compare(o1[i], o2[i]); if (result != 0) { return result; } } return o1.length - o2.length; }; }