Vault  4.1
vhex.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vhex.h"
00011 
00012 #include "vtextiostream.h"
00013 #include "vbinaryiostream.h"
00014 #include "vchar.h"
00015 
00016 // static
00017 void VHex::bufferToHexString(const Vu8* buffer, Vs64 bufferLength, VString& s, bool wantLeading0x) {
00018     int hexStringLength = (int)(bufferLength * 2);  // note we don't support string lengths > 32 bits
00019 
00020     if (wantLeading0x)
00021         hexStringLength += 2;
00022 
00023     s.preflight(hexStringLength);
00024 
00025     char*   hexStringBuffer = s.buffer();
00026     int     hexStringIndex = 0;
00027     int     bufferIndex = 0;
00028     char    highNibbleChar;
00029     char    lowNibbleChar;
00030 
00031     if (wantLeading0x) {
00032         hexStringBuffer[hexStringIndex++] = '0';
00033         hexStringBuffer[hexStringIndex++] = 'x';
00034     }
00035 
00036     while (bufferIndex < bufferLength) {
00037         VHex::byteToHexChars(buffer[bufferIndex++], &highNibbleChar, &lowNibbleChar);
00038 
00039         hexStringBuffer[hexStringIndex++] = highNibbleChar;
00040         hexStringBuffer[hexStringIndex++] = lowNibbleChar;
00041     }
00042 
00043     s.postflight(hexStringLength);
00044 }
00045 
00046 // static
00047 void VHex::hexStringToBuffer(const VString& hexDigits, Vu8* buffer, bool hasLeading0x) {
00048     int     digitsIndex = 0;
00049     int     bufferIndex = 0;
00050     int     numDigits = hexDigits.length();
00051     bool    oddNumHexDigits = (numDigits % 2) != 0;
00052     char    highNibbleChar = '0';
00053     char    lowNibbleChar;
00054 
00055     if (hasLeading0x) {
00056         digitsIndex += 2;
00057     }
00058 
00059     while (digitsIndex < numDigits) {
00060         if (oddNumHexDigits) {
00061             // First time through, use high nibble of 0, treat next digit as low nibble.
00062             lowNibbleChar = hexDigits.charAt(digitsIndex++);
00063             oddNumHexDigits = false; // done compensating for odd length, don't do again
00064         } else {
00065             highNibbleChar = hexDigits.charAt(digitsIndex++);
00066             lowNibbleChar = hexDigits.charAt(digitsIndex++);
00067         }
00068 
00069         buffer[bufferIndex++] = VHex::hexCharsToByte(highNibbleChar, lowNibbleChar);
00070     }
00071 }
00072 
00073 // static
00074 void VHex::stringToHex(const VString& text, VString& hexDigits, bool wantLeading0x) {
00075     VHex::bufferToHexString(text.getDataBufferConst(), static_cast<Vs64>(text.length()), hexDigits, wantLeading0x);
00076 }
00077 
00078 // static
00079 void VHex::hexToString(const VString& hexDigits, VString& text, bool hasLeading0x) {
00080     int outputLength = hexDigits.length() / 2;
00081 
00082     if (hasLeading0x) {
00083         --outputLength;
00084     }
00085 
00086     text.preflight(outputLength);
00087     VHex::hexStringToBuffer(hexDigits, text.getDataBuffer(), hasLeading0x);
00088     text.postflight(outputLength);
00089 }
00090 
00091 // static
00092 void VHex::byteToHexString(Vu8 byteValue, VString& s) {
00093     char hexChars[3];
00094 
00095     hexChars[2] = 0;
00096     VHex::byteToHexChars(byteValue, &hexChars[0], &hexChars[1]);
00097 
00098     s = hexChars;
00099 }
00100 
00101 // static
00102 void VHex::byteToHexChars(Vu8 byteValue, char* highNibbleChar, char* lowNibbleChar) {
00103     *highNibbleChar = VHex::nibbleToHexChar(static_cast<Vu8>(byteValue >> 4));
00104     *lowNibbleChar = VHex::nibbleToHexChar(static_cast<Vu8>(byteValue & 0x0F));
00105 }
00106 
00107 // static
00108 Vu8 VHex::hexStringToByte(const char* twoHexDigits) {
00109     Vu8 highNibble = VHex::hexCharToNibble(twoHexDigits[0]);
00110     Vu8 lowNibble = VHex::hexCharToNibble(twoHexDigits[1]);
00111 
00112     return static_cast<Vu8>((highNibble << 4) | lowNibble);
00113 }
00114 
00115 // static
00116 Vu8 VHex::hexCharsToByte(char highNibbleChar, char lowNibbleChar) {
00117     Vu8 highNibble = VHex::hexCharToNibble(highNibbleChar);
00118     Vu8 lowNibble = VHex::hexCharToNibble(lowNibbleChar);
00119 
00120     return static_cast<Vu8>((highNibble << 4) | lowNibble);
00121 }
00122 
00123 // static
00124 char VHex::nibbleToHexChar(Vu8 nibbleValue) {
00125     nibbleValue &= 0x0F;    // ensure that the high 4 bits are zero
00126 
00127     if (nibbleValue < 0x0A)
00128         return static_cast<char>('0' + nibbleValue);
00129     else
00130         return static_cast<char>('A' + (nibbleValue - 0x0A));
00131 }
00132 
00133 // static
00134 Vu8 VHex::hexCharToNibble(char hexChar) {
00135     if ((hexChar >= '0') && (hexChar <= '9'))
00136         return static_cast<Vu8>(hexChar - '0');
00137     else if ((hexChar >= 'A') && (hexChar <= 'F'))
00138         return static_cast<Vu8>(0x0A + hexChar - 'A');
00139     else if ((hexChar >= 'a') && (hexChar <= 'f'))
00140         return static_cast<Vu8>(0x0A + hexChar - 'a');
00141     else
00142         return 0;
00143 }
00144 
00145 // static
00146 void VHex::bufferToPrintableASCIIString(const Vu8* buffer, Vs64 bufferLength, VString& s) {
00147     for (int i = 0; i < (int) bufferLength; ++i) {
00148         char asciiValue = static_cast<char>(buffer[i]);
00149         if ((asciiValue <= 0x20) || (asciiValue > 0x7E)) {
00150             asciiValue = '.';
00151         }
00152         s += asciiValue;
00153     }
00154 }
00155 
00156 // static
00157 void VHex::readHexDump(VTextIOStream& inputStream, VBinaryIOStream& outputStream) {
00158     VString line;
00159 
00160     do {
00161         inputStream.readLine(line);
00162 
00163         if (line.isEmpty()) {
00164             break;
00165         }
00166 
00167         // Remove typical leading indent spaces.
00168         line.trim();
00169 
00170         // Lines we want must start with either of these offset formats:
00171         //   NNNNNNNN: (where N is a decimal digit)
00172         //   0xNNNNNNNN: (where N is a hexadecimal digit)
00173         // Anything else is a line to be skipped.
00174 
00175         int nextHexByteOffset = -1;
00176         if (line.startsWith("0x") && (line.length() > 10) && (line[10] == ':')) {
00177             nextHexByteOffset = 11;
00178         } else if (line.startsWith("0") && (line.length() > 8) && (line[8] == ':')) {
00179             nextHexByteOffset = 9;
00180         } else {
00181             continue; // skip this line
00182         }
00183 
00184         // Read each ' xx' triplet from the line. Space, hex digit, hex digit.
00185         // Anything else indicates end of this line's hex data.
00186         while (nextHexByteOffset > 0) {
00187             if (line.length() < nextHexByteOffset + 3) {
00188                 break;
00189             } else if (line[nextHexByteOffset] != ' ') {
00190                 break;
00191             } else if (!(VChar(line[nextHexByteOffset+1]).isHexadecimal()) || !(VChar(line[nextHexByteOffset+2]).isHexadecimal())) {
00192                 break;
00193             }
00194 
00195             // Now we know we have a space and two hex digits we can decode.
00196             Vu8 byteValue = VHex::hexCharsToByte(line[nextHexByteOffset+1], line[nextHexByteOffset+2]);
00197             outputStream.writeU8(byteValue);
00198 
00199             nextHexByteOffset += 3;
00200         }
00201 
00202     } while (line.isNotEmpty());
00203 }
00204 
00205 VHex::VHex(VTextIOStream* outputStream, int numBytesPerRow, int indentCount, bool labelsInHex, bool showASCIIValues)
00206     : mOutputStream(outputStream)
00207     , mNumBytesPerRow(numBytesPerRow)
00208     , mIndentCount(indentCount)
00209     , mLabelsInHex(labelsInHex)
00210     , mShowASCIIValues(showASCIIValues)
00211     , mStartColumn(0)
00212     , mOffset(0)
00213     , mPendingBufferUsed(0)
00214     , mPendingBuffer(NULL)
00215     , mLineBuffer()
00216     {
00217     mPendingBuffer = new Vu8[mNumBytesPerRow];
00218 }
00219 
00220 VHex::~VHex() {
00221     delete [] mPendingBuffer;
00222     mOutputStream = NULL; // we don't own it, so don't delete it
00223 }
00224 
00225 void VHex::printHex(const Vu8* buffer, Vs64 length, Vs64 offset) {
00226     while (length > 0) {
00227         if (mPendingBufferUsed == mNumBytesPerRow - mStartColumn) {
00228             this->_printPending();
00229         }
00230 
00231         mPendingBuffer[mPendingBufferUsed++] = buffer[offset++];
00232 
00233         --length;
00234     }
00235 
00236     this->flush();
00237 }
00238 
00239 void VHex::reset() {
00240     this->_printPending();
00241 
00242     mStartColumn = 0;
00243     mOffset = 0;
00244 }
00245 
00246 void VHex::flush() {
00247     this->_printPending();
00248 }
00249 
00250 void VHex::_printPending() {
00251     if (mPendingBufferUsed > 0) {
00252         char highNibbleChar;
00253         char lowNibbleChar;
00254 
00255         mLineBuffer = VString::EMPTY();
00256 
00257         // Add spaces to indent.
00258         for (int i = 0; i < mIndentCount; ++i) {
00259             mLineBuffer += ' ';
00260         }
00261 
00262         // Add the label.
00263         if (mLabelsInHex) {
00264             VString label(VSTRING_ARGS("0x%08X: ", (int) mOffset));
00265             mLineBuffer += label;
00266         } else {
00267             VString label(VSTRING_ARGS("%08lld: ", mOffset));
00268             mLineBuffer += label;
00269         }
00270 
00271         // If we're starting mid-row, add spaces to indent.
00272         for (int i = 0; i < mStartColumn; ++i) {
00273             mLineBuffer += "   ";
00274         }
00275 
00276         // Now append our hex data.
00277         for (int i = 0; i < mPendingBufferUsed; ++i) {
00278             VHex::byteToHexChars(mPendingBuffer[i], &highNibbleChar, &lowNibbleChar);
00279             mLineBuffer += highNibbleChar;
00280             mLineBuffer += lowNibbleChar;
00281             mLineBuffer += ' ';
00282         }
00283 
00284         // Now do the ASCII stuff if necessary.
00285         if (mShowASCIIValues) {
00286             // If we're starting mid-row, add spaces to indent.
00287             for (int i = mPendingBufferUsed; i < mNumBytesPerRow - mStartColumn; ++i) {
00288                 mLineBuffer += "   ";
00289             }
00290 
00291             mLineBuffer += "   ";
00292 
00293             for (int i = 0; i < mStartColumn; ++i) {
00294                 mLineBuffer += ' ';
00295             }
00296 
00297             // Append the ASCII data
00298             for (int i = 0; i < mPendingBufferUsed; ++i) {
00299                 char    asciiValue = (char) mPendingBuffer[i];
00300 
00301                 if ((asciiValue <= 0x20) || (asciiValue > 0x7E)) {
00302                     asciiValue = '.';
00303                 }
00304 
00305                 mLineBuffer += asciiValue;
00306             }
00307         }
00308 
00309         // Keep track of column in case of split lines
00310         if (mNumBytesPerRow == 0) {
00311             mStartColumn = 0;
00312         } else {
00313             mStartColumn = (mStartColumn + mPendingBufferUsed) % mNumBytesPerRow;
00314         }
00315 
00316         // Reset pending indicators
00317         mOffset += mPendingBufferUsed;
00318         mPendingBufferUsed = 0;
00319 
00320         // Finally, shove the string out to the stream or output pipe.
00321         if (mOutputStream == NULL) {
00322             std::cout << mLineBuffer.chars() << std::endl;
00323         } else {
00324             mOutputStream->writeLine(mLineBuffer);
00325         }
00326     }
00327 }
00328 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.