/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server; import com.foundationdb.server.rowdata.FieldDef; import com.foundationdb.util.AkibanAppender; import com.foundationdb.util.ByteSource; import com.foundationdb.util.WrappingByteSource; import java.io.File; import java.io.UnsupportedEncodingException; import java.lang.management.RuntimeMXBean; import java.math.BigInteger; import java.nio.ByteBuffer; public class AkServerUtil { private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public final static String NEW_LINE = System.getProperty("line.separator"); private static String UNEXPECTED_SIGNED_WIDTH_MSG = "Width must be 0,1,2,3,4 or 8 but was: "; private static String UNEXPECTED_UNSIGNED_WIDTH_MSG = "Width must be 0,1,2,3, or 4 but was: "; public static long getSignedIntegerByWidth(final byte[] bytes, final int index, final int width) { switch (width) { case 0: return 0; case 1: return bytes[index]; case 2: return getShort(bytes, index); case 3: return getMediumInt(bytes, index); case 4: return getInt(bytes, index); case 8: return getLong(bytes, index); } throw new IllegalArgumentException(UNEXPECTED_SIGNED_WIDTH_MSG + width); } public static long getUnsignedIntegerByWidth(final byte[] bytes, final int index, final int width) { switch (width) { case 0: return 0; case 1: return getUByte(bytes, index); case 2: return getUShort(bytes, index); case 3: return getUMediumInt(bytes, index); case 4: return getUInt(bytes, index); //case 8: return getULong(bytes, index); // Returns BigInteger, must call directly } throw new IllegalArgumentException(UNEXPECTED_UNSIGNED_WIDTH_MSG + width); } /** * Write an integer into a byte array. * @param destination Byte buffer to write into * @param destinationIndex Position in destination to write at * @param width Width of integer (= number of bytes to write) * @param value Value to write * @return The number of bytes written */ public static int putIntegerByWidth(byte[] destination, int destinationIndex, int width, long value) { switch (width) { case 0: break; case 1: putByte(destination, destinationIndex, (byte)value); break; case 2: putShort(destination, destinationIndex, (short)value); break; case 3: putMediumInt(destination, destinationIndex, (int)value); break; case 4: putInt(destination, destinationIndex, (int)value); break; case 8: putLong(destination, destinationIndex, value); break; default: throw new IllegalArgumentException(UNEXPECTED_SIGNED_WIDTH_MSG + width); } return width; } public static byte getByte(byte[] bytes, int index) { return bytes[index]; } public static short getUByte(byte[] bytes, int index) { return (short) (bytes[index] & 0xFF); } public static short getShort(byte[] bytes, int index) { return (short) ((bytes[index] & 0xFF) | (bytes[index+1] & 0xFF) << 8); } public static int getUShort(byte[] bytes, int index) { return getShort(bytes, index) & 0xFFFF; } public static int getMediumInt(byte[] bytes, int index) { final int value = getUMediumInt(bytes, index); // Negative values have bit 23 set so the sign extension promotes to 32bit representation return (value << 8) >> 8; } public static int getUMediumInt(byte[] bytes, int index) { return (bytes[index] & 0xFF) | (bytes[index + 1] & 0xFF) << 8 | (bytes[index + 2] & 0xFF) << 16; } public static int getInt(byte[] bytes, int index) { return (bytes[index] & 0xFF) | (bytes[index + 1] & 0xFF) << 8 | (bytes[index + 2] & 0xFF) << 16 | (bytes[index + 3] & 0xFF) << 24; } public static long getUInt(byte[] bytes, int index) { return getInt(bytes, index) & 0xFFFFFFFFL; } public static long getLong(byte[] bytes, int index) { return (bytes[index] & 0xFFL) | (bytes[index + 1] & 0xFFL) << 8 | (bytes[index + 2] & 0xFFL) << 16 | (bytes[index + 3] & 0xFFL) << 24 | (bytes[index + 4] & 0xFFL) << 32 | (bytes[index + 5] & 0xFFL) << 40 | (bytes[index + 6] & 0xFFL) << 48 | (bytes[index + 7] & 0xFFL) << 56; } public static BigInteger getULong(byte[] bytes, int index) { byte longBytes[] = {bytes[index+7], bytes[index+6], bytes[index+5], bytes[index+4], bytes[index+3], bytes[index+2], bytes[index+1], bytes[index]}; return new BigInteger(1, longBytes); } public static void putByte(byte[] bytes, int index, int value) { bytes[index] = (byte) (value); } public static void putShort(byte[] bytes, int index, int value) { bytes[index] = (byte) (value); bytes[index + 1] = (byte) (value >>> 8); } public static void putMediumInt(byte[] bytes, int index, int value) { bytes[index] = (byte) (value); bytes[index + 1] = (byte) (value >>> 8); bytes[index + 2] = (byte) (value >>> 16); } public static void putInt(byte[] bytes, int index, int value) { bytes[index] = (byte) (value); bytes[index + 1] = (byte) (value >>> 8); bytes[index + 2] = (byte) (value >>> 16); bytes[index + 3] = (byte) (value >>> 24); } public static void putLong(byte[] bytes, int index, long value) { bytes[index] = (byte) (value); bytes[index + 1] = (byte) (value >>> 8); bytes[index + 2] = (byte) (value >>> 16); bytes[index + 3] = (byte) (value >>> 24); bytes[index + 4] = (byte) (value >>> 32); bytes[index + 5] = (byte) (value >>> 40); bytes[index + 6] = (byte) (value >>> 48); bytes[index + 7] = (byte) (value >>> 56); } public static String dump(byte[] b, int offset, int size) { StringBuilder sb1 = new StringBuilder(); StringBuilder sb2 = new StringBuilder(); for (int m = 0; m < size; m += 16) { sb2.setLength(0); hex(sb1, m, 4); sb1.append(":"); for (int i = 0; i < 16; i++) { sb1.append(" "); if (i % 8 == 0) sb1.append(" "); int j = m + i; if (j < size) { hex(sb1, b[j + offset], 2); final char c = (char) (b[j + offset] & 0xFF); sb2.append(c > 32 && c < 127 ? c : '.'); } else sb1.append(" "); } sb1.append(" "); sb1.append(sb2.toString()); sb1.append(NEW_LINE); } return sb1.toString(); } public static String hex(byte[] bytes, int start, int length) { final StringBuilder sb = new StringBuilder(length * 2); hex(AkibanAppender.of(sb), bytes, start, length); return sb.toString(); } public static StringBuilder hex(StringBuilder sb, long value, int length) { for (int i = length - 1; i >= 0; i--) { sb.append(HEX_DIGITS[(int) (value >> (i * 4)) & 0xF]); } return sb; } public static void hex(AkibanAppender sb, byte[] bytes, int start, int length) { for (int i = start; i < start + length; i++) { sb.append(HEX_DIGITS[(bytes[i] & 0xF0) >>> 4]); sb.append(HEX_DIGITS[(bytes[i] & 0x0F)]); } } public static void printRuntimeInfo() { System.out.println(); RuntimeMXBean m = java.lang.management.ManagementFactory .getRuntimeMXBean(); System.out.println("BootClassPath = " + m.getBootClassPath()); System.out.println("ClassPath = " + m.getClassPath()); System.out.println("LibraryPath = " + m.getLibraryPath()); System.out.println("ManagementSpecVersion = " + m.getManagementSpecVersion()); System.out.println("Name = " + m.getName()); System.out.println("SpecName = " + m.getSpecName()); System.out.println("SpecVendor = " + m.getSpecVendor()); System.out.println("SpecVersion = " + m.getSpecVersion()); System.out.println("UpTime = " + m.getUptime()); System.out.println("VmName = " + m.getVmName()); System.out.println("VmVendor = " + m.getVmVendor()); System.out.println("VmVersion = " + m.getVmVersion()); System.out.println("InputArguments = " + m.getInputArguments()); System.out.println("BootClassPathSupported = " + m.isBootClassPathSupported()); System.out.println("---all properties--"); System.out.println("SystemProperties = " + m.getSystemProperties()); System.out.println("---"); System.out.println(); } public final static boolean cleanUpDirectory(final File file) { if (!file.exists()) { return file.mkdirs(); } else if (file.isFile()) { return false; } else { boolean success = true; final File[] files = file.listFiles(); if (files != null) { if (!cleanUpFiles(files)) { success = false; } } return success; } } public final static boolean cleanUpFiles(final File[] files) { boolean success = true; for (final File file : files) { boolean success1 = true; if (file.isDirectory()) { success1 = cleanUpDirectory(file); } if (success1) { success1 = file.delete(); } if (!success1) { file.deleteOnExit(); success = false; } } return success; } /** * Cracks the MySQL variable-length format. Interprets 0, 1, 2 or 3 prefix * bytes as a little-endian string size and constructs a string from the * remaining bytes. * * @param bytes Byte array to read string from * @param offset Position within bytes to start at * @param width Number of available bytes * @param fieldDef Corresponding field * @return The decoded string. */ public static String decodeMySQLString(byte[] bytes, final int offset, final int width, final FieldDef fieldDef) { ByteBuffer buff = byteBufferForMySQLString(bytes, offset, width, fieldDef); return decodeString(buff, fieldDef.column().getCharsetName()); } /** * Convert the bytes in a given buffer to a string, using a given charset. * @param buffer array of bytes encoded using the given charset * @param charset name of valid and supported character set * @return string representation of the buffer * @throws RuntimeException if the charset is not supported */ static String decodeString(ByteBuffer buffer, String charset) { if (buffer == null) { return null; } if (charset == null) { throw new IllegalArgumentException("charset"); } // Note: String(.., Charset) has *very* different behavior than String(.., "charset") // Think carefully, and read the String docs, before changing. try { return new String(buffer.array(), buffer.position(), buffer.limit() - buffer.position(), charset); } catch(UnsupportedEncodingException e) { throw new RuntimeException(e); } } public static ByteSource byteSourceForMySQLString(byte[] bytes, final int offset, final int width, final FieldDef fieldDef) { if (width == 0) { return null; } final int prefixSize = fieldDef.getPrefixSize(); int length = (int) getUnsignedIntegerByWidth(bytes, offset, prefixSize); if (length > width) { throw new IllegalArgumentException(String.format( "String is wider than available bytes: %d > %d", length, width)); } byte[] result = new byte[length]; System.arraycopy(bytes, offset+prefixSize, result, 0, length); return new WrappingByteSource(result); } public static ByteBuffer byteBufferForMySQLString(byte[] bytes, final int offset, final int width, final FieldDef fieldDef) { if (width == 0) { return null; } final int prefixSize = fieldDef.getPrefixSize(); int length = (int) getUnsignedIntegerByWidth(bytes, offset, prefixSize); if (length > width) { throw new IllegalArgumentException(String.format( "String is wider than available bytes: %d > %d", length, width)); } return ByteBuffer.wrap(bytes, offset + prefixSize, length); } public static int varWidth(final int length) { return length == 0 ? 0 : length < 0x100 ? 1 : length < 0x10000 ? 2 : length < 0x1000000 ? 3 : 4; } public static boolean equals(final Object a, final Object b) { return a == null ? b == null : a.equals(b); } public static int hashCode(final Object o) { return o == null ? Integer.MIN_VALUE : o.hashCode(); } }