package org.corfudb.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.ByteBuffer; import java.text.DecimalFormat; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; import jdk.internal.org.objectweb.asm.tree.ClassNode; import jdk.internal.org.objectweb.asm.tree.InsnList; import jdk.internal.org.objectweb.asm.tree.MethodNode; import jdk.internal.org.objectweb.asm.util.Printer; import jdk.internal.org.objectweb.asm.util.Textifier; import jdk.internal.org.objectweb.asm.util.TraceMethodVisitor; import lombok.extern.slf4j.Slf4j; /** * Created by crossbach on 5/22/15. */ @Slf4j public class Utils { private static Printer printer = new Textifier(); private static TraceMethodVisitor mp = new TraceMethodVisitor(printer); public static String printByteCode(byte[] bytes) { ClassReader cr = new ClassReader(bytes); ClassNode cn = new ClassNode(); cr.accept(cn, 0); final List<MethodNode> methods = cn.methods; StringBuilder sb = new StringBuilder(); for (MethodNode m : methods) { InsnList inList = m.instructions; sb.append(m.name); for (int i = 0; i < inList.size(); i++) { sb.append(insnToString(inList.get(i))); } } return sb.toString(); } public static String insnToString(AbstractInsnNode insn) { insn.accept(mp); StringWriter sw = new StringWriter(); printer.print(new PrintWriter(sw)); printer.getText().clear(); return sw.toString(); } /** * A fancy parser which parses suffixes. * * @param toParseObj * @return */ public static long parseLong(final Object toParseObj) { if (toParseObj == null) { return 0; } if (toParseObj instanceof Long) { return (Long) toParseObj; } if (toParseObj instanceof Integer) { return (Integer) toParseObj; } String toParse = (String) toParseObj; if (toParse.matches("[0-9]*[A-Za-z]$")) { long multiplier; char suffix = toParse.toUpperCase().charAt(toParse.length() - 1); switch (suffix) { case 'E': multiplier = 1_000_000_000_000_000_000L; break; case 'P': multiplier = 1_000_000_000_000_000L; break; case 'T': multiplier = 1_000_000_000_000L; break; case 'G': multiplier = 1_000_000_000L; break; case 'M': multiplier = 1_000_000L; break; case 'K': multiplier = 1_000L; break; default: throw new NumberFormatException("Unknown suffix: '" + suffix + "'!"); } return Long.parseLong(toParse.substring(0, toParse.length() - 2)) * multiplier; } else { return Long.parseLong(toParse); } } @SuppressWarnings("unchecked") public static <T> T getOption(Map<String, Object> optionsMap, String option, Class<T> type, T defaultValue) { T obj = (T) optionsMap.get(option); if (type == Long.class) { if (obj == null && defaultValue != null) { return defaultValue; } return (T) (Long) parseLong(obj); } else if (type == Integer.class) { if (obj == null && defaultValue != null) { return defaultValue; } return (T) (Integer) ((Long) parseLong(obj)).intValue(); } if (obj == null) { return defaultValue; } return obj; } public static <T> T getOption(Map<String, Object> optionsMap, String option, Class<T> type) { return getOption(optionsMap, option, type, null); } /** * Turn a range into a set of discrete longs. * * @param range The range to discretize. * @return A set containing all the longs in that range. */ public static Set<Long> discretizeRange(Range<Long> range) { Set<Long> s = new HashSet<>(); for (long l = range.lowerEndpoint(); l <= range.upperEndpoint(); l++) { if (range.contains(l)) { s.add(l); } } return s; } /** * Turn a set of ranges into a discrete set. * * @param ranges A set of ranges to discretize. * @return A set containing all the longs in that rangeset. */ public static Set<Long> discretizeRangeSet(RangeSet<Long> ranges) { Set<Long> total = Collections.newSetFromMap(new ConcurrentHashMap<>()); for (Range<Long> r : ranges.asRanges()) { total.addAll(Utils.discretizeRange(r)); } return total; } /** * Convert to byte string representation. * from http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java * * @param value The value to convert. * @return A string for bytes (i.e, 10GB). */ public static String convertToByteStringRepresentation(final long value) { final long[] dividers = new long[]{1_000_000_000_000L, 1_000_000_000, 1_000_000, 1_000, 1}; final String[] units = new String[]{"TB", "GB", "MB", "KB", "B"}; if (value < 1) throw new IllegalArgumentException("Invalid file size: " + value); String result = null; for (int i = 0; i < dividers.length; i++) { final long divider = dividers[i]; if (value >= divider) { final double cresult = divider > 1 ? (double) value / (double) divider : (double) value; result = new DecimalFormat("#,##0.#").format(cresult) + " " + units[i]; break; } } return result; } public static ByteBuffer serialize(Object obj) { try { //todo: make serialization less clunky! ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); byte b[] = baos.toByteArray(); oos.close(); return ByteBuffer.wrap(b); } catch (IOException e) { throw new RuntimeException(e); } } public static Object deserialize(ByteBuffer b) { try { //todo: make serialization less clunky! ByteArrayInputStream bais = new ByteArrayInputStream(b.array()); ObjectInputStream ois = new ObjectInputStream(bais); Object obj = ois.readObject(); return obj; } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException ce) { throw new RuntimeException(ce); } } static long rotl64(long x, int r) { return (x << r) | (x >> (64 - r)); } static long fmix64(long k) { k ^= k >> 33; k *= 0xff51afd7ed558ccdl; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53l; k ^= k >> 33; return k; } /** * murmer hash 3 implementation specialized for UUIDs, * based on googlecode C implementation from: * http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp * * @param key * @param seed * @return */ public static UUID murmerhash3( UUID key, long seed ) { long msb = key.getMostSignificantBits(); long lsb = key.getLeastSignificantBits(); byte[] data = new byte[8]; data[7] = (byte) (lsb & 0xFF); data[6] = (byte) ((lsb >> 8) & 0xFF); data[5] = (byte) ((lsb >> 16) & 0xFF); data[4] = (byte) ((lsb >> 24) & 0xFF); data[3] = (byte) (msb & 0xFF); data[2] = (byte) ((msb >> 8) & 0xFF); data[1] = (byte) ((msb >> 16) & 0xFF); data[0] = (byte) ((msb >> 24) & 0xFF); int nblocks = 2; long h1 = seed; long h2 = seed; long c1 = 0x87c37b91114253d5l; long c2 = 0x4cf5ad432745937fl; long[] blocks = new long[nblocks]; blocks[0] = msb; blocks[1] = lsb; for (int i = 0; i < nblocks; i++) { long k1 = blocks[i * 2 + 0]; long k2 = blocks[i * 2 + 1]; k1 *= c1; k1 = rotl64(k1, 31); k1 *= c2; h1 ^= k1; h1 = rotl64(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; k2 *= c2; k2 = rotl64(k2, 33); k2 *= c1; h2 ^= k2; h2 = rotl64(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } h1 ^= 2; h2 ^= 2; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; return new UUID(h2, h1); } /** * simple UUID hashing, which is *not* hashing, and is effectively * customized to the task of deterministically allocating new UUIDs * based on a given UUID (which is necessary in the assignment of stream * IDs in ICOrfuDBObjects that contain others, since syncing the log in * multiple clients needs allocators to produce the same streamID/object * every time). * * @param key * @param seed * @return */ public static UUID simpleUUIDHash(UUID key, long seed) { return new UUID(key.getMostSignificantBits(), key.getLeastSignificantBits() + seed); } public static UUID nextDeterministicUUID(UUID uuid, long seed) { return simpleUUIDHash(uuid, seed); } /** restart the JVM - borrowed from https://dzone.com/articles/programmatically-restart-java */ /** * Sun property pointing the main class and its arguments. * Might not be defined on non Hotspot VM implementations. */ public static final String SUN_JAVA_COMMAND = "sun.java.command"; public static void sleepUninterruptibly(long millis) { while (true) { try { Thread.sleep(millis); return; } catch (InterruptedException ie) { } } } /** Generates a human readable UUID string (4 hex chars) * using time_mid * @param id The UUID to parse * @return A human readable UUID string */ public static String toReadableID(UUID id) { return Long.toHexString((id.getLeastSignificantBits()) & 0xFFFF); } }