jdk/src/java.base/share/classes/sun/security/util/HexDumpEncoder.java
changeset 34687 d302ed125dc9
parent 25859 3317bb8137f4
equal deleted inserted replaced
34686:29ea8310a27a 34687:d302ed125dc9
       
     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 
       
    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 
       
    75     protected int bytesPerAtom() {
       
    76         return (1);
       
    77     }
       
    78 
       
    79     protected int bytesPerLine() {
       
    80         return (16);
       
    81     }
       
    82 
       
    83     protected void encodeBufferPrefix(OutputStream o) throws IOException {
       
    84         offset = 0;
       
    85         pStream = new PrintStream(o);
       
    86     }
       
    87 
       
    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 
       
    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 
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   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      */
       
   324     public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
       
   325         throws IOException
       
   326     {
       
   327         byte [] buf = getBytes(aBuffer);
       
   328         encodeBuffer(buf, aStream);
       
   329     }
       
   330 
       
   331 }