| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package org.apache.impala.common; |
| |
| import static org.apache.impala.common.ByteUnits.GIGABYTE; |
| import static org.apache.impala.common.ByteUnits.KILOBYTE; |
| import static org.apache.impala.common.ByteUnits.MEGABYTE; |
| import static org.apache.impala.common.ByteUnits.PETABYTE; |
| import static org.apache.impala.common.ByteUnits.TERABYTE; |
| |
| import java.text.DecimalFormat; |
| import java.util.Iterator; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.text.WordUtils; |
| |
| import com.google.common.base.Joiner; |
| |
| /** |
| * Utility functions for pretty printing. |
| */ |
| public class PrintUtils { |
| /** |
| * Prints the given number of bytes in PB, TB, GB, MB, KB with 2 decimal points. |
| * For example 5000 will be returned as 4.88KB. |
| */ |
| public static String printBytes(long bytes) { |
| double result = bytes; |
| // Avoid String.format() due to IMPALA-1572 which happens on JDK7 but not JDK6. |
| // IMPALA-6759: Please update tests/stress/concurrent_select.py MEM_ESTIMATE_PATTERN |
| // if you add additional unit prefixes. |
| if (bytes >= PETABYTE) return new DecimalFormat(".00PB").format(result / PETABYTE); |
| if (bytes >= TERABYTE) return new DecimalFormat(".00TB").format(result / TERABYTE); |
| if (bytes >= GIGABYTE) return new DecimalFormat(".00GB").format(result / GIGABYTE); |
| if (bytes >= MEGABYTE) return new DecimalFormat(".00MB").format(result / MEGABYTE); |
| if (bytes >= KILOBYTE) return new DecimalFormat(".00KB").format(result / KILOBYTE); |
| return bytes + "B"; |
| } |
| |
| public static final long KILO = 1000; |
| public static final long MEGA = KILO * 1000; |
| public static final long GIGA = MEGA * 1000; |
| public static final long TERA = GIGA * 1000; |
| |
| /** |
| * Print a value using simple metric (power of 1000) units. Units are |
| * (none), K, M, G or T. Value has two digits past the decimal point. |
| */ |
| public static String printMetric(long value) { |
| double result = value; |
| if (value >= TERA) return new DecimalFormat(".00T").format(result / TERA); |
| if (value >= GIGA) return new DecimalFormat(".00G").format(result / GIGA); |
| if (value >= MEGA) return new DecimalFormat(".00M").format(result / MEGA); |
| if (value >= KILO) return new DecimalFormat(".00K").format(result / KILO); |
| return Long.toString(value); |
| } |
| |
| /** |
| * Pattern to use when searching for a metric-encoded value. |
| */ |
| public static final String METRIC_REGEX = "(\\d+(?:.\\d+)?)([TGMK]?)"; |
| |
| /** |
| * Pattern to use when searching for or parsing a metric-encoded value. |
| */ |
| public static final Pattern METRIC_PATTERN = |
| Pattern.compile(METRIC_REGEX, Pattern.CASE_INSENSITIVE); |
| |
| /** |
| * Decode a value metric-encoded using {@link #printMetric(long)}. |
| * @param value metric-encoded string |
| * @return approximate numeric value, or -1 if the value is invalid |
| * (metric encoded strings can never be negative normally) |
| */ |
| public static double decodeMetric(String value) { |
| Matcher m = METRIC_PATTERN.matcher(value); |
| if (! m.matches()) return -1; |
| return decodeMetric(m.group(1), m.group(2)); |
| } |
| |
| /** |
| * Decode a metric-encoded string already parsed into parts. |
| * @param valueStr numeric part of the value |
| * @param units units part of the value |
| * @return approximate numeric value |
| */ |
| // Yes, "PrintUtils" is an odd place for a parse function, but |
| // best to keep the formatter and parser together. |
| public static double decodeMetric(String valueStr, String units) { |
| double value = Double.parseDouble(valueStr); |
| switch (units.toUpperCase()) { |
| case "": |
| return value; |
| case "K": |
| return value * KILO; |
| case "M": |
| return value * MEGA; |
| case "G": |
| return value * GIGA; |
| case "T": |
| return value * TERA; |
| default: |
| return -1; |
| } |
| } |
| |
| /** |
| * Same as printBytes() except 0 decimal points are shown for MB and KB. |
| */ |
| public static String printBytesRoundedToMb(long bytes) { |
| double result = bytes; |
| // Avoid String.format() due to IMPALA-1572 which happens on JDK7 but not JDK6. |
| // IMPALA-6759: Please update tests/stress/concurrent_select.py MEM_ESTIMATE_PATTERN |
| // if you add additional unit prefixes. |
| if (bytes >= PETABYTE) return new DecimalFormat(".00PB").format(result / PETABYTE); |
| if (bytes >= TERABYTE) return new DecimalFormat(".00TB").format(result / TERABYTE); |
| if (bytes >= GIGABYTE) return new DecimalFormat(".00GB").format(result / GIGABYTE); |
| if (bytes >= MEGABYTE) return new DecimalFormat("0MB").format(result / MEGABYTE); |
| if (bytes >= KILOBYTE) return new DecimalFormat("0KB").format(result / KILOBYTE); |
| return bytes + "B"; |
| } |
| |
| /** |
| * Print an estimated cardinality. No need to print the exact value |
| * because estimates are not super-precise. |
| */ |
| public static String printEstCardinality(long cardinality) { |
| return (cardinality != -1) ? printMetric(cardinality) : "unavailable"; |
| } |
| |
| /** |
| * Print an exact cardinality (such as a row count) as one of three formats: |
| * |
| * * "unavailable" (if value < 0) |
| * * number (if value is small) |
| * * xx.xxU (dd,ddd) (if the value is large) |
| */ |
| public static String printExactCardinality(long value) { |
| if (value == -1) return "unavailable"; |
| String result = printMetric(value); |
| if (value < KILO) return result; |
| return String.format("%s (%,d)", result, value); |
| } |
| |
| public static String printNumHosts(String prefix, long numHosts) { |
| return prefix + "hosts=" + ((numHosts != -1) ? numHosts : "unavailable"); |
| } |
| |
| public static String printNumInstances(String prefix, long numInstances) { |
| return prefix + "instances=" + ((numInstances != -1) ? numInstances : "unavailable"); |
| } |
| |
| /** |
| * Prints the given square matrix into matrixStr. Separates cells by cellSpacing. |
| */ |
| public static void printMatrix( |
| boolean[][] matrix, int cellSpacing, StringBuilder matrixStr) { |
| // Print labels. |
| matrixStr.append(StringUtils.repeat(' ', cellSpacing)); |
| String formatStr = "%Xd".replace("X", String.valueOf(cellSpacing)); |
| for (int i = 0; i < matrix.length; ++i) { |
| matrixStr.append(String.format(formatStr, i)); |
| } |
| matrixStr.append("\n"); |
| |
| // Print matrix. |
| for (int i = 0; i < matrix.length; ++i) { |
| matrixStr.append(String.format(formatStr, i)); |
| for (int j = 0; j < matrix.length; ++j) { |
| int cell = (matrix[i][j]) ? 1 : 0; |
| matrixStr.append(String.format(formatStr, cell)); |
| } |
| matrixStr.append("\n"); |
| } |
| } |
| |
| /** |
| * Wrap a string by inserting newlines so that no line exceeds a given length. |
| * Any newlines in the input are maintained. |
| */ |
| public static String wrapString(String s, int wrapLength) { |
| String wrapped = WordUtils.wrap(s, wrapLength, null, true); |
| // Remove any trailing blanks from a line. |
| wrapped = wrapped.replaceAll(" +$", ""); |
| return wrapped; |
| } |
| |
| /** |
| * Join the objects in 'objects' in a human-readable form, such as |
| * "'obj 1', 'obj 2', 'obj 3'". No escaping of quotes is performed. |
| */ |
| public static String joinQuoted(Iterable<?> objs) { |
| Iterator<?> it = objs.iterator(); |
| if (!it.hasNext()) return ""; |
| return "'" + Joiner.on("', '").join(it) + "'"; |
| } |
| } |