Skip to content

Instantly share code, notes, and snippets.

@kristopherjohnson
Last active May 2, 2023 05:43
Show Gist options
  • Save kristopherjohnson/90581f20aab44e669907 to your computer and use it in GitHub Desktop.
Save kristopherjohnson/90581f20aab44e669907 to your computer and use it in GitHub Desktop.
Hex and ASCII dump in C++
#include "HexDump.h"
#include <fstream>
#include <iostream>
int main(int argc, const char* argv[])
{
if (argc < 2) {
HexDump::dumpStream(std::cout, std::cin);
}
else for (auto i = 1; i < argc; ++i) {
auto fileStream = std::ifstream{ argv[i], std::ios::binary };
if (fileStream.is_open())
HexDump::dumpStream(std::cout, fileStream);
else
std::cerr << argv[i] << ": unable to open file" << std::endl;
}
return 0;
}
// Copyright (C) 2015 Kristopher Johnson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef net_kristopherjohnson_HexDump_h
#define net_kristopherjohnson_HexDump_h
#include <ios>
#include <iomanip>
#include <sstream>
#include <vector>
namespace HexDump {
using std::endl;
using std::hex;
using std::isprint;
using std::left;
using std::ostringstream;
using std::setfill;
using std::setw;
using std::size_t;
using std::vector;
// In the templates below, the type parameters must satisfy these constraints:
//
// - Stream: something like std::ostream or std::istream
//
// - InputStream: something like std::istream
//
// - OutputStream: something like std::ostream
//
// - ByteSequence: a collection of chars that can be iterated over, like std::vector<char>
const size_t BytesPerLine = 16;
// Saves original formatting state for a stream and
// restores that state before going out of scope
template<typename Stream>
class SavedState
{
public:
SavedState(Stream& s)
: stream(s), oldFlags(s.flags()), oldFill(s.fill())
{}
~SavedState() { stream.flags(oldFlags); stream.fill(oldFill); }
SavedState(const SavedState&) = delete;
void operator=(const SavedState&) = delete;
private:
Stream& stream;
decltype(stream.flags()) oldFlags;
decltype(stream.fill()) oldFill;
};
// Dump a sequence of bytes as hex with spaces between; e.g., "cf fa 4f a0 "
template<typename OutputStream, typename ByteSequence>
void dumpBytesAsHex(OutputStream& output, const ByteSequence& bytes)
{
SavedState<OutputStream> savedState{ output };
output << hex << setfill('0');
for (auto byte: bytes) {
unsigned widenedUIntValue = static_cast<unsigned char>(byte);
output << setw(2) << widenedUIntValue << " ";
}
}
// Dump a sequence of bytes as ASCII characters,
// substituting '.' for non-printing characters
template<typename OutputStream, typename ByteSequence>
void dumpBytesAsText(OutputStream& output, const ByteSequence& bytes)
{
for (auto byte: bytes)
output.put(isprint(byte) ? byte : '.');
}
// Dump a sequence of bytes in side-by-side hex and text formats
template<typename OutputStream, typename ByteSequence>
void dumpHexLine(OutputStream& output, const ByteSequence& bytes)
{
SavedState<OutputStream> savedState{ output };
ostringstream hexStream;
dumpBytesAsHex(hexStream, bytes);
const auto HexOutputWidth = BytesPerLine * 3 + 1;
output << setw(HexOutputWidth) << left << hexStream.str();
dumpBytesAsText(output, bytes);
output << endl;
}
// Dump a sequence of bytes in side-by-side hex and text formats,
// prefixed with a hex offset
template<typename OutputStream, typename ByteSequence>
void dumpHexLine(OutputStream& output, size_t offset, const ByteSequence& bytes)
{
{
SavedState<OutputStream> savedState{ output };
output << setw(8) << setfill('0') << hex
<< offset << " ";
}
dumpHexLine(output, bytes);
}
// Dump bytes from input stream in side-by-side hex and text formats
template<typename OutputStream, typename InputStream>
void dumpStream(OutputStream& output, InputStream& input)
{
vector<char> bytesToDump;
bytesToDump.reserve(BytesPerLine);
size_t offset = 0;
char byte;
while (input.get(byte)) {
bytesToDump.push_back(byte);
if (bytesToDump.size() == BytesPerLine) {
dumpHexLine(output, offset, bytesToDump);
bytesToDump.clear();
offset += BytesPerLine;
}
}
if (!bytesToDump.empty())
dumpHexLine(output, offset, bytesToDump);
}
} // namespace HexDump
#endif /* net_kristopherjohnson_HexDump_h */
CXXFLAGS=-std=c++11 -O2 -Wall
HexDump: HexDump.cpp HexDump.h Makefile
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $< -o $@
@kristopherjohnson
Copy link
Author

These functions in HexDump.h were written with the intention of making them easy to copy/paste into all the projects where I wish I had some hex-dumping code. Hence the header, namespace, and (over)use of templates.

Example:

$ make
c++ -std=c++11 -O2 -Wall    HexDump.cpp -o HexDump
$ ./HexDump HexDump.cpp
00000000  23 69 6e 63 6c 75 64 65 20 22 48 65 78 44 75 6d  #include "HexDum
00000010  70 2e 68 22 0a 0a 23 69 6e 63 6c 75 64 65 20 3c  p.h"..#include <
00000020  66 73 74 72 65 61 6d 3e 0a 23 69 6e 63 6c 75 64  fstream>.#includ
00000030  65 20 3c 69 6f 73 74 72 65 61 6d 3e 0a 0a 69 6e  e <iostream>..in
00000040  74 20 6d 61 69 6e 28 69 6e 74 20 61 72 67 63 2c  t main(int argc,
00000050  20 63 6f 6e 73 74 20 63 68 61 72 2a 20 61 72 67   const char* arg
00000060  76 5b 5d 29 0a 7b 0a 20 20 20 20 69 66 20 28 61  v[]).{.    if (a
00000070  72 67 63 20 3c 20 32 29 20 7b 0a 20 20 20 20 20  rgc < 2) {.     
00000080  20 20 20 48 65 78 44 75 6d 70 3a 3a 64 75 6d 70     HexDump::dump
00000090  53 74 72 65 61 6d 28 73 74 64 3a 3a 63 6f 75 74  Stream(std::cout
 .
 .
 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment