Path: blob/master/src/java.base/share/classes/sun/security/util/HexDumpEncoder.java
41159 views
/*1* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/242526package sun.security.util;2728import java.io.ByteArrayInputStream;29import java.io.ByteArrayOutputStream;30import java.io.InputStream;31import java.io.PrintStream;32import java.io.OutputStream;33import java.io.IOException;34import java.nio.ByteBuffer;3536import static java.nio.charset.StandardCharsets.ISO_8859_1;3738/**39* This class encodes a buffer into the classic: "Hexadecimal Dump" format of40* the past. It is useful for analyzing the contents of binary buffers.41* The format produced is as follows:42* <pre>43* xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................44* </pre>45* Where xxxx is the offset into the buffer in 16 byte chunks, followed46* by ascii coded hexadecimal bytes followed by the ASCII representation of47* the bytes or '.' if they are not valid bytes.48*49* @author Chuck McManis50*/5152public class HexDumpEncoder {5354private int offset;55private int thisLineLength;56private int currentByte;57private byte thisLine[] = new byte[16];5859static void hexDigit(PrintStream p, byte x) {60char c;6162c = (char) ((x >> 4) & 0xf);63if (c > 9)64c = (char) ((c-10) + 'A');65else66c = (char)(c + '0');67p.write(c);68c = (char) (x & 0xf);69if (c > 9)70c = (char)((c-10) + 'A');71else72c = (char)(c + '0');73p.write(c);74}7576protected int bytesPerAtom() {77return (1);78}7980protected int bytesPerLine() {81return (16);82}8384protected void encodeBufferPrefix(OutputStream o) throws IOException {85offset = 0;86pStream = new PrintStream(o);87}8889protected void encodeLinePrefix(OutputStream o, int len) throws IOException {90hexDigit(pStream, (byte)((offset >>> 8) & 0xff));91hexDigit(pStream, (byte)(offset & 0xff));92pStream.print(": ");93currentByte = 0;94thisLineLength = len;95}9697protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException {98thisLine[currentByte] = buf[off];99hexDigit(pStream, buf[off]);100pStream.print(" ");101currentByte++;102if (currentByte == 8)103pStream.print(" ");104}105106protected void encodeLineSuffix(OutputStream o) throws IOException {107if (thisLineLength < 16) {108for (int i = thisLineLength; i < 16; i++) {109pStream.print(" ");110if (i == 7)111pStream.print(" ");112}113}114pStream.print(" ");115for (int i = 0; i < thisLineLength; i++) {116if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) {117pStream.print(".");118} else {119pStream.write(thisLine[i]);120}121}122pStream.println();123offset += thisLineLength;124}125126/** Stream that understands "printing" */127protected PrintStream pStream;128129/**130* This method works around the bizarre semantics of BufferedInputStream's131* read method.132*/133protected int readFully(InputStream in, byte buffer[])134throws java.io.IOException {135for (int i = 0; i < buffer.length; i++) {136int q = in.read();137if (q == -1)138return i;139buffer[i] = (byte)q;140}141return buffer.length;142}143144/**145* Encode bytes from the input stream, and write them as text characters146* to the output stream. This method will run until it exhausts the147* input stream, but does not print the line suffix for a final148* line that is shorter than bytesPerLine().149*/150public void encode(InputStream inStream, OutputStream outStream)151throws IOException152{153int j;154int numBytes;155byte tmpbuffer[] = new byte[bytesPerLine()];156157encodeBufferPrefix(outStream);158159while (true) {160numBytes = readFully(inStream, tmpbuffer);161if (numBytes == 0) {162break;163}164encodeLinePrefix(outStream, numBytes);165for (j = 0; j < numBytes; j += bytesPerAtom()) {166167if ((j + bytesPerAtom()) <= numBytes) {168encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());169} else {170encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);171}172}173if (numBytes < bytesPerLine()) {174break;175} else {176encodeLineSuffix(outStream);177}178}179}180181/**182* A 'streamless' version of encode that simply takes a buffer of183* bytes and returns a string containing the encoded buffer.184*/185public String encode(byte aBuffer[]) {186ByteArrayOutputStream outStream = new ByteArrayOutputStream();187ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);188try {189encode(inStream, outStream);190// explicit ascii->unicode conversion191return outStream.toString(ISO_8859_1);192} catch (IOException ignore) {193// This should never happen.194throw new Error("CharacterEncoder.encode internal error");195}196}197198/**199* Return a byte array from the remaining bytes in this ByteBuffer.200* <P>201* The ByteBuffer's position will be advanced to ByteBuffer's limit.202* <P>203* To avoid an extra copy, the implementation will attempt to return the204* byte array backing the ByteBuffer. If this is not possible, a205* new byte array will be created.206*/207private byte [] getBytes(ByteBuffer bb) {208/*209* This should never return a BufferOverflowException, as we're210* careful to allocate just the right amount.211*/212byte [] buf = null;213214/*215* If it has a usable backing byte buffer, use it. Use only216* if the array exactly represents the current ByteBuffer.217*/218if (bb.hasArray()) {219byte [] tmp = bb.array();220if ((tmp.length == bb.capacity()) &&221(tmp.length == bb.remaining())) {222buf = tmp;223bb.position(bb.limit());224}225}226227if (buf == null) {228/*229* This class doesn't have a concept of encode(buf, len, off),230* so if we have a partial buffer, we must reallocate231* space.232*/233buf = new byte[bb.remaining()];234235/*236* position() automatically updated237*/238bb.get(buf);239}240241return buf;242}243244/**245* A 'streamless' version of encode that simply takes a ByteBuffer246* and returns a string containing the encoded buffer.247* <P>248* The ByteBuffer's position will be advanced to ByteBuffer's limit.249*/250public String encode(ByteBuffer aBuffer) {251byte [] buf = getBytes(aBuffer);252return encode(buf);253}254255/**256* Encode bytes from the input stream, and write them as text characters257* to the output stream. This method will run until it exhausts the258* input stream. It differs from encode in that it will add the259* line at the end of a final line that is shorter than bytesPerLine().260*/261public void encodeBuffer(InputStream inStream, OutputStream outStream)262throws IOException263{264int j;265int numBytes;266byte tmpbuffer[] = new byte[bytesPerLine()];267268encodeBufferPrefix(outStream);269270while (true) {271numBytes = readFully(inStream, tmpbuffer);272if (numBytes == 0) {273break;274}275encodeLinePrefix(outStream, numBytes);276for (j = 0; j < numBytes; j += bytesPerAtom()) {277if ((j + bytesPerAtom()) <= numBytes) {278encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());279} else {280encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);281}282}283encodeLineSuffix(outStream);284if (numBytes < bytesPerLine()) {285break;286}287}288}289290/**291* Encode the buffer in <i>aBuffer</i> and write the encoded292* result to the OutputStream <i>aStream</i>.293*/294public void encodeBuffer(byte aBuffer[], OutputStream aStream)295throws IOException296{297ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);298encodeBuffer(inStream, aStream);299}300301/**302* A 'streamless' version of encode that simply takes a buffer of303* bytes and returns a string containing the encoded buffer.304*/305public String encodeBuffer(byte aBuffer[]) {306ByteArrayOutputStream outStream = new ByteArrayOutputStream();307ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);308try {309encodeBuffer(inStream, outStream);310} catch (Exception IOException) {311// This should never happen.312throw new Error("CharacterEncoder.encodeBuffer internal error");313}314return (outStream.toString());315}316317/**318* Encode the <i>aBuffer</i> ByteBuffer and write the encoded319* result to the OutputStream <i>aStream</i>.320* <P>321* The ByteBuffer's position will be advanced to ByteBuffer's limit.322*/323public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)324throws IOException325{326byte [] buf = getBytes(aBuffer);327encodeBuffer(buf, aStream);328}329330}331332333