1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.cts.statsd.atom; 18 19 import java.nio.charset.StandardCharsets; 20 import java.util.Formatter; 21 22 /** 23 * Print utility for byte[]. 24 */ 25 public class BufferDebug { 26 private static final int HALF_WIDTH = 8; 27 28 /** 29 * Number of bytes represented per row in hex output. 30 */ 31 public static final int WIDTH = HALF_WIDTH * 2; 32 33 /** 34 * Return a string suitable for debugging. 35 * - If the byte is printable as an ascii string, return that, in quotation marks, 36 * with a newline at the end. 37 * - Otherwise, return the hexdump -C style output. 38 * 39 * @param buf the buffer 40 * @param max print up to _max_ bytes, or the length of the string. If max is 0, 41 * print the whole contents of buf. 42 */ debugString(byte[] buf, int max)43 public static String debugString(byte[] buf, int max) { 44 if (buf == null) { 45 return "(null)"; 46 } 47 if (buf.length == 0) { 48 return "(length 0)"; 49 } 50 51 int len = max; 52 if (len <= 0 || len > buf.length) { 53 max = len = buf.length; 54 } 55 56 if (isPrintable(buf, len)) { 57 return "\"" + new String(buf, 0, len, StandardCharsets.UTF_8) + "\"\n"; 58 } else { 59 return toHex(buf, len, max); 60 } 61 } 62 toHex(byte[] buf, int len, int max)63 private static String toHex(byte[] buf, int len, int max) { 64 final StringBuilder str = new StringBuilder(); 65 66 // All but the last row 67 int rows = len / WIDTH; 68 for (int row = 0; row < rows; row++) { 69 writeRow(str, buf, row * WIDTH, WIDTH, max); 70 } 71 72 // Last row 73 if (len % WIDTH != 0) { 74 writeRow(str, buf, rows * WIDTH, max - (rows * WIDTH), max); 75 } 76 77 // Final len 78 str.append(String.format("%10d 0x%08x ", buf.length, buf.length)); 79 if (buf.length != max) { 80 str.append(String.format("truncated to %d 0x%08x", max, max)); 81 } 82 str.append('\n'); 83 84 return str.toString(); 85 } 86 writeRow(StringBuilder str, byte[] buf, int start, int len, int max)87 private static void writeRow(StringBuilder str, byte[] buf, int start, int len, int max) { 88 final Formatter f = new Formatter(str); 89 90 // Start index 91 f.format("%10d 0x%08x ", start, start); 92 93 // One past the last char we will print 94 int end = start + len; 95 // Number of missing caracters due to this being the last line. 96 int padding = 0; 97 if (start + WIDTH > max) { 98 padding = WIDTH - (end % WIDTH); 99 end = max; 100 } 101 102 // Hex 103 for (int i = start; i < end; i++) { 104 f.format("%02x ", buf[i]); 105 if (i == start + HALF_WIDTH - 1) { 106 str.append(" "); 107 } 108 } 109 for (int i = 0; i < padding; i++) { 110 str.append(" "); 111 } 112 if (padding >= HALF_WIDTH) { 113 str.append(" "); 114 } 115 116 str.append(" "); 117 for (int i = start; i < end; i++) { 118 byte b = buf[i]; 119 if (isPrintable(b)) { 120 str.append((char)b); 121 } else { 122 str.append('.'); 123 } 124 if (i == start + HALF_WIDTH - 1) { 125 str.append(" "); 126 } 127 } 128 129 str.append('\n'); 130 } 131 isPrintable(byte[] buf, int len)132 private static boolean isPrintable(byte[] buf, int len) { 133 for (int i=0; i<len; i++) { 134 if (!isPrintable(buf[i])) { 135 return false; 136 } 137 } 138 return true; 139 } 140 isPrintable(byte c)141 private static boolean isPrintable(byte c) { 142 return c >= 0x20 && c <= 0x7e; 143 } 144 } 145