A hex::dumper
object which can output hexdumps in several ways, configured by using various stream manipulators.
Usage is as simple as:
std::cout << hex::hexstring << hex::dumper(data, size) << std::endl;
or
std::cout << hex::offset(0x12000) << std::hex << hex::dumper(data, size) << std::endl;
I use hexdumps in various ways, sometimes I just need a couple of bytes, without any spaces converted to hex, other times I want to have a full listing with offsets, and ascii, or yet another time I would like to see dwords instead of bytes.
So my hexdumper needs to be configurable in many ways. My idea is to use stream manipulators to configure the hexdump.
Looking at iostreams, there are many ways to configure a stream:
getter | setter |
---|---|
os.flags() & os.adjustfield | os « std::left os « std::right |
os.flags() & os.basefield | os « std::hex, os « std::dec, os « std::oct |
os.flags() & os.uppercase | os « std::uppercase os « std::lowercase |
os.width() | os « std::setw(n) |
os.precision() | os « std::setprecision(n) |
os.flags() & os.showpoint | os « std::showpoint |
os.flags() & os.showbase | os « std::showbase |
os.flags() & os.skipws | os « std::skipws |
os.fill() | os « std::setfill(c) |
os.flags() & os.showpos | os « std::showpos |
And this is a list of properties I would like to configure in my hexdumper:
- base offset
- item size ( byte, word, dword, qword )
- which items to output: offset, hexdata, ascdata
- separator: space, ‘:’, ‘-’, or nothing.
- line width
- stepsize ( do I print everything, or skip through the data in larger jumps )
- summarize identical lines
- whether to output hex, octal, decimal or binary
- wheter to show a base with the data ( 0, 0x, 0b )
Here is the mapping I made from the std stream manipulators to my desired features.
The reasoning is as follows: left
is where the hexdump usually is, right
is where the ascii dump
usually is.
manipulator | hexdump feature |
---|---|
std::left | only hexdump |
std::right | only ascdump |
std::hex | hexadecimal data |
std::dec | decimal data |
std::oct | octal data |
std::setw | nr of units per line |
std::fill | separator |
std::uppercase | uppercase hexadecimal |
std::showpoint | show offset |
std::showbase | show base |
std::skipws | summarize identical lines |
std::setprecision | unused |
std::showpos | unused |
sizeof(T) | unit type : byte, word, dword, … |
binary data | |
stepsize | |
base offset |
As you can see there are not enough std manipulators to use comfortably for all hexdumper features.
So, how do I add my own custom manipulators?
There are two kinds of stream manipulators, the first are simple ones, like std::hex
.
These are functions with the following signature:
template <class _CharT, class _Traits>
std::basic_ostream<_CharT, _Traits>&
MANIPULATOR(std::basic_ostream<_CharT, _Traits>&os)
{
...
}
The second are in the form of objects which have a friend operator «
struct MANIPULATOR {
MANIPULATOR(...) { ... }
friend std::ostream& operator<<(std::ostream&os, offset ofs)
{
...
}
}
Now these manipulators need to remember the change they are making to the stream somewhere.
This is where the stream iword
function comes in: this allows you to store custom parameters in a stream, and use them later.
Each stream has a number of iwords, and pwords associated with the stream, you have to reserve slots in the iword or pword arrays using the xalloc
function.
So now we can add our own manipulators
manipulator | hexdump feature |
---|---|
hex::bin | binary data |
hex::step(n) | stepsize |
hex::offset(n) | base offset |
The resulting hexdumper can be used as follows:
std::cout << hex::offset(0x12000) << std::hex << hex::dumper(data, size) << std::endl;
or
std::cout << std::fill(0) << std::left << hex::dumper(data, size) << std::endl;
I made some handy manipulators which make this easier to type:
std::cout << hex::hexstring << hex::dumper(data, size) << std::endl;
The resulting library can be found at https://github.com/nlitsme/cpputils/