Rewrite hexdump

This commit is contained in:
Tatsuhiro Tsujikawa
2024-03-17 18:38:08 +09:00
parent 061998e6cf
commit be512f8298
2 changed files with 155 additions and 53 deletions

View File

@@ -1348,66 +1348,166 @@ StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
} }
namespace { namespace {
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { uint8_t *hexdump_addr(uint8_t *dest, size_t addr) {
auto stop = std::min(first + 8, last); // Lower 32 bits are displayed.
for (auto k = first; k != stop; ++k) { for (size_t i = 0; i < 4; ++i) {
fprintf(out, "%02x ", *k); auto a = (addr >> (3 - i) * 8) & 0xff;
*dest++ = LOWER_XDIGITS[a >> 4];
*dest++ = LOWER_XDIGITS[a & 0xf];
} }
// each byte needs 3 spaces (2 hex value and space)
for (; stop != first + 8; ++stop) { return dest;
fputs(" ", out);
}
// we have extra space after 8 bytes
fputc(' ', out);
} }
} // namespace } // namespace
void hexdump(FILE *out, const uint8_t *src, size_t len) { namespace {
if (len == 0) { uint8_t *hexdump_ascii(uint8_t *dest, const uint8_t *data, size_t datalen) {
return; *dest++ = '|';
for (size_t i = 0; i < datalen; ++i) {
if (0x20 <= data[i] && data[i] <= 0x7e) {
*dest++ = data[i];
} else {
*dest++ = '.';
}
} }
size_t buflen = 0;
*dest++ = '|';
return dest;
}
} // namespace
namespace {
uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) {
size_t i;
for (i = 0; i < datalen; ++i) {
*dest++ = LOWER_XDIGITS[data[i] >> 4];
*dest++ = LOWER_XDIGITS[data[i] & 0xf];
*dest++ = ' ';
}
for (; i < 8; ++i) {
*dest++ = ' ';
*dest++ = ' ';
*dest++ = ' ';
}
return dest;
}
} // namespace
namespace {
uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) {
if (datalen > 8) {
dest = hexdump8(dest, data, 8);
*dest++ = ' ';
dest = hexdump8(dest, data + 8, datalen - 8);
*dest++ = ' ';
} else {
dest = hexdump8(dest, data, datalen);
*dest++ = ' ';
dest = hexdump8(dest, nullptr, 0);
*dest++ = ' ';
}
return dest;
}
} // namespace
namespace {
uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen,
size_t addr) {
dest = hexdump_addr(dest, addr);
*dest++ = ' ';
*dest++ = ' ';
dest = hexdump16(dest, data, datalen);
return hexdump_ascii(dest, data, datalen);
}
} // namespace
namespace {
int hexdump_write(int fd, const uint8_t *data, size_t datalen) {
ssize_t nwrite;
for (; (nwrite = write(fd, data, datalen)) == -1 && errno == EINTR;)
;
if (nwrite == -1) {
return -1;
}
return 0;
}
} // namespace
int hexdump(FILE *out, const void *data, size_t datalen) {
if (datalen == 0) {
return 0;
}
// min_space is the additional minimum space that the buffer must
// accept, which is the size of a single full line output + one
// repeat line marker ("*\n"). If the remaining buffer size is less
// than that, flush the buffer and reset.
constexpr size_t min_space = 79 + 2;
auto fd = fileno(out);
std::array<uint8_t, 4096> buf;
auto last = buf.data();
auto in = reinterpret_cast<const uint8_t *>(data);
auto repeated = false; auto repeated = false;
std::array<uint8_t, 16> buf{};
auto end = src + len; for (size_t offset = 0; offset < datalen; offset += 16) {
auto i = src; auto n = datalen - offset;
for (;;) { auto s = in + offset;
auto nextlen =
std::min(static_cast<size_t>(16), static_cast<size_t>(end - i)); if (n >= 16) {
if (nextlen == buflen && n = 16;
std::equal(std::begin(buf), std::begin(buf) + buflen, i)) {
// as long as adjacent 16 bytes block are the same, we just if (offset > 0) {
// print single '*'. if (std::equal(s - 16, s, s)) {
if (!repeated) { if (repeated) {
repeated = true; continue;
fputs("*\n", out); }
}
i += nextlen; repeated = true;
continue;
} *last++ = '*';
repeated = false; *last++ = '\n';
fprintf(out, "%08lx", static_cast<unsigned long>(i - src));
if (i == end) { continue;
fputc('\n', out); }
break;
} repeated = false;
fputs(" ", out);
hexdump8(out, i, end);
hexdump8(out, i + 8, std::max(i + 8, end));
fputc('|', out);
auto stop = std::min(i + 16, end);
buflen = stop - i;
auto p = buf.data();
for (; i != stop; ++i) {
*p++ = *i;
if (0x20 <= *i && *i <= 0x7e) {
fputc(*i, out);
} else {
fputc('.', out);
} }
} }
fputs("|\n", out);
last = hexdump_line(last, s, n, offset);
*last++ = '\n';
auto len = static_cast<size_t>(last - buf.data());
if (len + min_space > buf.size()) {
if (hexdump_write(fd, buf.data(), len) != 0) {
return -1;
}
last = buf.data();
}
} }
last = hexdump_addr(last, datalen);
*last++ = '\n';
auto len = static_cast<size_t>(last - buf.data());
if (len) {
return hexdump_write(fd, buf.data(), len);
}
return 0;
} }
void put_uint16be(uint8_t *buf, uint16_t n) { void put_uint16be(uint8_t *buf, uint16_t n) {

View File

@@ -840,8 +840,10 @@ StringRef make_http_hostport(OutputIt first, const StringRef &host,
return StringRef{first, p}; return StringRef{first, p};
} }
// Dumps |src| of length |len| in the format similar to `hexdump -C`. // hexdump dumps |data| of length |datalen| in the format similar to
void hexdump(FILE *out, const uint8_t *src, size_t len); // hexdump(1) with -C option. This function returns 0 if it succeeds,
// or -1.
int hexdump(FILE *out, const void *data, size_t datalen);
// Copies 2 byte unsigned integer |n| in host byte order to |buf| in // Copies 2 byte unsigned integer |n| in host byte order to |buf| in
// network byte order. // network byte order.