1 /* 2 * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package sun.security.util; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.InputStream; 32 import java.io.PrintStream; 33 import java.io.OutputStream; 34 import java.io.IOException; 35 import java.nio.ByteBuffer; 36 37 /** 38 * This class encodes a buffer into the classic: "Hexadecimal Dump" format of 39 * the past. It is useful for analyzing the contents of binary buffers. 40 * The format produced is as follows: 41 * <pre> 42 * xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................ 43 * </pre> 44 * Where xxxx is the offset into the buffer in 16 byte chunks, followed 45 * by ascii coded hexadecimal bytes followed by the ASCII representation of 46 * the bytes or '.' if they are not valid bytes. 47 * 48 * @author Chuck McManis 49 */ 50 51 public class HexDumpEncoder { 52 53 private int offset; 54 private int thisLineLength; 55 private int currentByte; 56 private byte thisLine[] = new byte[16]; 57 hexDigit(PrintStream p, byte x)58 static void hexDigit(PrintStream p, byte x) { 59 char c; 60 61 c = (char) ((x >> 4) & 0xf); 62 if (c > 9) 63 c = (char) ((c-10) + 'A'); 64 else 65 c = (char)(c + '0'); 66 p.write(c); 67 c = (char) (x & 0xf); 68 if (c > 9) 69 c = (char)((c-10) + 'A'); 70 else 71 c = (char)(c + '0'); 72 p.write(c); 73 } 74 bytesPerAtom()75 protected int bytesPerAtom() { 76 return (1); 77 } 78 bytesPerLine()79 protected int bytesPerLine() { 80 return (16); 81 } 82 encodeBufferPrefix(OutputStream o)83 protected void encodeBufferPrefix(OutputStream o) throws IOException { 84 offset = 0; 85 pStream = new PrintStream(o); 86 } 87 encodeLinePrefix(OutputStream o, int len)88 protected void encodeLinePrefix(OutputStream o, int len) throws IOException { 89 hexDigit(pStream, (byte)((offset >>> 8) & 0xff)); 90 hexDigit(pStream, (byte)(offset & 0xff)); 91 pStream.print(": "); 92 currentByte = 0; 93 thisLineLength = len; 94 } 95 encodeAtom(OutputStream o, byte buf[], int off, int len)96 protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException { 97 thisLine[currentByte] = buf[off]; 98 hexDigit(pStream, buf[off]); 99 pStream.print(" "); 100 currentByte++; 101 if (currentByte == 8) 102 pStream.print(" "); 103 } 104 encodeLineSuffix(OutputStream o)105 protected void encodeLineSuffix(OutputStream o) throws IOException { 106 if (thisLineLength < 16) { 107 for (int i = thisLineLength; i < 16; i++) { 108 pStream.print(" "); 109 if (i == 7) 110 pStream.print(" "); 111 } 112 } 113 pStream.print(" "); 114 for (int i = 0; i < thisLineLength; i++) { 115 if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) { 116 pStream.print("."); 117 } else { 118 pStream.write(thisLine[i]); 119 } 120 } 121 pStream.println(); 122 offset += thisLineLength; 123 } 124 125 /** Stream that understands "printing" */ 126 protected PrintStream pStream; 127 128 /** 129 * This method works around the bizarre semantics of BufferedInputStream's 130 * read method. 131 */ readFully(InputStream in, byte buffer[])132 protected int readFully(InputStream in, byte buffer[]) 133 throws java.io.IOException { 134 for (int i = 0; i < buffer.length; i++) { 135 int q = in.read(); 136 if (q == -1) 137 return i; 138 buffer[i] = (byte)q; 139 } 140 return buffer.length; 141 } 142 143 /** 144 * Encode bytes from the input stream, and write them as text characters 145 * to the output stream. This method will run until it exhausts the 146 * input stream, but does not print the line suffix for a final 147 * line that is shorter than bytesPerLine(). 148 */ encode(InputStream inStream, OutputStream outStream)149 public void encode(InputStream inStream, OutputStream outStream) 150 throws IOException 151 { 152 int j; 153 int numBytes; 154 byte tmpbuffer[] = new byte[bytesPerLine()]; 155 156 encodeBufferPrefix(outStream); 157 158 while (true) { 159 numBytes = readFully(inStream, tmpbuffer); 160 if (numBytes == 0) { 161 break; 162 } 163 encodeLinePrefix(outStream, numBytes); 164 for (j = 0; j < numBytes; j += bytesPerAtom()) { 165 166 if ((j + bytesPerAtom()) <= numBytes) { 167 encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); 168 } else { 169 encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); 170 } 171 } 172 if (numBytes < bytesPerLine()) { 173 break; 174 } else { 175 encodeLineSuffix(outStream); 176 } 177 } 178 } 179 180 /** 181 * A 'streamless' version of encode that simply takes a buffer of 182 * bytes and returns a string containing the encoded buffer. 183 */ encode(byte aBuffer[])184 public String encode(byte aBuffer[]) { 185 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 186 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 187 String retVal = null; 188 try { 189 encode(inStream, outStream); 190 // explicit ascii->unicode conversion 191 retVal = outStream.toString("ISO-8859-1"); 192 } catch (Exception IOException) { 193 // This should never happen. 194 throw new Error("CharacterEncoder.encode internal error"); 195 } 196 return (retVal); 197 } 198 199 /** 200 * Return a byte array from the remaining bytes in this ByteBuffer. 201 * <P> 202 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 203 * <P> 204 * To avoid an extra copy, the implementation will attempt to return the 205 * byte array backing the ByteBuffer. If this is not possible, a 206 * new byte array will be created. 207 */ getBytes(ByteBuffer bb)208 private byte [] getBytes(ByteBuffer bb) { 209 /* 210 * This should never return a BufferOverflowException, as we're 211 * careful to allocate just the right amount. 212 */ 213 byte [] buf = null; 214 215 /* 216 * If it has a usable backing byte buffer, use it. Use only 217 * if the array exactly represents the current ByteBuffer. 218 */ 219 if (bb.hasArray()) { 220 byte [] tmp = bb.array(); 221 if ((tmp.length == bb.capacity()) && 222 (tmp.length == bb.remaining())) { 223 buf = tmp; 224 bb.position(bb.limit()); 225 } 226 } 227 228 if (buf == null) { 229 /* 230 * This class doesn't have a concept of encode(buf, len, off), 231 * so if we have a partial buffer, we must reallocate 232 * space. 233 */ 234 buf = new byte[bb.remaining()]; 235 236 /* 237 * position() automatically updated 238 */ 239 bb.get(buf); 240 } 241 242 return buf; 243 } 244 245 /** 246 * A 'streamless' version of encode that simply takes a ByteBuffer 247 * and returns a string containing the encoded buffer. 248 * <P> 249 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 250 */ encode(ByteBuffer aBuffer)251 public String encode(ByteBuffer aBuffer) { 252 byte [] buf = getBytes(aBuffer); 253 return encode(buf); 254 } 255 256 /** 257 * Encode bytes from the input stream, and write them as text characters 258 * to the output stream. This method will run until it exhausts the 259 * input stream. It differs from encode in that it will add the 260 * line at the end of a final line that is shorter than bytesPerLine(). 261 */ encodeBuffer(InputStream inStream, OutputStream outStream)262 public void encodeBuffer(InputStream inStream, OutputStream outStream) 263 throws IOException 264 { 265 int j; 266 int numBytes; 267 byte tmpbuffer[] = new byte[bytesPerLine()]; 268 269 encodeBufferPrefix(outStream); 270 271 while (true) { 272 numBytes = readFully(inStream, tmpbuffer); 273 if (numBytes == 0) { 274 break; 275 } 276 encodeLinePrefix(outStream, numBytes); 277 for (j = 0; j < numBytes; j += bytesPerAtom()) { 278 if ((j + bytesPerAtom()) <= numBytes) { 279 encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); 280 } else { 281 encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); 282 } 283 } 284 encodeLineSuffix(outStream); 285 if (numBytes < bytesPerLine()) { 286 break; 287 } 288 } 289 } 290 291 /** 292 * Encode the buffer in <i>aBuffer</i> and write the encoded 293 * result to the OutputStream <i>aStream</i>. 294 */ encodeBuffer(byte aBuffer[], OutputStream aStream)295 public void encodeBuffer(byte aBuffer[], OutputStream aStream) 296 throws IOException 297 { 298 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 299 encodeBuffer(inStream, aStream); 300 } 301 302 /** 303 * A 'streamless' version of encode that simply takes a buffer of 304 * bytes and returns a string containing the encoded buffer. 305 */ encodeBuffer(byte aBuffer[])306 public String encodeBuffer(byte aBuffer[]) { 307 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 308 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 309 try { 310 encodeBuffer(inStream, outStream); 311 } catch (Exception IOException) { 312 // This should never happen. 313 throw new Error("CharacterEncoder.encodeBuffer internal error"); 314 } 315 return (outStream.toString()); 316 } 317 318 /** 319 * Encode the <i>aBuffer</i> ByteBuffer and write the encoded 320 * result to the OutputStream <i>aStream</i>. 321 * <P> 322 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 323 */ encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)324 public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream) 325 throws IOException 326 { 327 byte [] buf = getBytes(aBuffer); 328 encodeBuffer(buf, aStream); 329 } 330 331 } 332