pw_hex_dump: Fix bug with unaligned trailing bytes
When bytes at the end of a dump were not aligned with the "group_every"
option, half of the last byte would be truncated.
No-Docs-Update-Reason: Bugfix
Change-Id: Ibe349c9f34f41281122583a6e7dff84d4f991333
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/49305
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Armando Montanez <amontanez@google.com>
diff --git a/pw_hex_dump/hex_dump.cc b/pw_hex_dump/hex_dump.cc
index 67f0f3c..77c15cf 100644
--- a/pw_hex_dump/hex_dump.cc
+++ b/pw_hex_dump/hex_dump.cc
@@ -44,6 +44,26 @@
return std::to_integer<char>(b);
}
+void AddGroupingByte(size_t byte_index,
+ FormattedHexDumper::Flags& flags,
+ StringBuilder& builder) {
+ // Never add grouping when it is disabled.
+ if (flags.group_every == 0) {
+ return;
+ }
+ // If this byte isn't at the end of a group, don't add a space.
+ if ((byte_index + 1) % flags.group_every != 0) {
+ return;
+ }
+ // If this byte is the last byte in a line, don't add a grouping byte
+ // (prevents trailing spaces).
+ if (byte_index + 1 == flags.bytes_per_line) {
+ return;
+ }
+
+ builder << ' ';
+}
+
} // namespace
Status DumpAddr(std::span<char> dest, uintptr_t addr) {
@@ -100,17 +120,7 @@
} else {
builder.append(2, ' ');
}
- if (flags.group_every != 0 && (i + 1) % flags.group_every == 0) {
- builder << ' ';
- }
- }
-
- // Removes extraneous space from end when bytes_per_line is divisible by
- // group_every. kSectionSeparator includes a space, so it's unnecessary.
- // Ommitting the space from the section separator actually makes for more
- // workarounds and code duplication, so this is better.
- if (flags.group_every != 0 && flags.bytes_per_line % flags.group_every == 0) {
- builder.pop_back();
+ AddGroupingByte(i, flags, builder);
}
if (flags.show_ascii) {
@@ -173,9 +183,7 @@
// width bytes? (`04` instead of `4`, for example)
builder << std::byte(c >> 4);
builder << std::byte(c & 0xF);
- if (flags.group_every != 0 && (i + 1) % flags.group_every == 0) {
- builder << ' ';
- }
+ AddGroupingByte(i, flags, builder);
}
// Add padding spaces to ensure lines are aligned.
if (flags.show_ascii) {
@@ -183,20 +191,10 @@
i < static_cast<size_t>(flags.bytes_per_line);
++i) {
builder.append(2, ' ');
- if (flags.group_every != 0 && (i + 1) % flags.group_every == 0) {
- builder << ' ';
- }
+ AddGroupingByte(i, flags, builder);
}
}
- // Removes extraneous space from end when bytes_per_line is divisible by
- // group_every. kSectionSeparator includes a space, so it's unnecessary.
- // Ommitting the space from the section separator actually makes for more
- // workarounds and code duplication, so this is better.
- if (flags.group_every != 0 && flags.bytes_per_line % flags.group_every == 0) {
- builder.pop_back();
- }
-
// Interpret bytes as characters.
if (flags.show_ascii) {
builder << kSectionSeparator;
diff --git a/pw_hex_dump/hex_dump_test.cc b/pw_hex_dump/hex_dump_test.cc
index f374431..d283c18 100644
--- a/pw_hex_dump/hex_dump_test.cc
+++ b/pw_hex_dump/hex_dump_test.cc
@@ -223,6 +223,27 @@
EXPECT_STREQ(expected2, dest_.data());
}
+TEST_F(HexDump, FormattedHexDump_LastLineCheck) {
+ constexpr const char* expected1 = "a4cc32629b46381a 231a2a7abce240a0";
+ constexpr const char* expected2 = "ff33e52b9e9f6b3c be9b893c7e4a7a48";
+ constexpr const char* expected3 = "18";
+
+ default_flags_.bytes_per_line = 16;
+ default_flags_.group_every = 8;
+ dumper_ = FormattedHexDumper(dest_, default_flags_);
+
+ EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
+ // Dump first line.
+ EXPECT_TRUE(dumper_.DumpLine().ok());
+ EXPECT_STREQ(expected1, dest_.data());
+ // Dump second line.
+ EXPECT_TRUE(dumper_.DumpLine().ok());
+ EXPECT_STREQ(expected2, dest_.data());
+ // Dump third line.
+ EXPECT_TRUE(dumper_.DumpLine().ok());
+ EXPECT_STREQ(expected3, dest_.data());
+}
+
TEST_F(HexDump, FormattedHexDump_Ascii) {
constexpr const char* expected1 = "6d 79 20 74 65 73 74 20 my test ";
constexpr const char* expected2 = "73 74 72 69 6e 67 0a string.";