From f09b0f064167b1f26c5c13e2c6d47ce84f1e56bf Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Fri, 22 Mar 2024 16:41:42 -0700 Subject: [PATCH] Move `DebugChar` into its own file Change-Id: Ia40549e5d0b78ece8dd0722c3a5a032b9915f24b --- src/libutil/escape-char.cc | 22 +++++++++++++++++ src/libutil/escape-char.hh | 22 +++++++++++++++++ src/libutil/meson.build | 2 ++ .../repl_characterization/test-session.cc | 6 ++--- .../tests/cli-literate-parser.cc | 4 ++-- .../unit/libutil-support/tests/debug-char.hh | 24 ------------------- .../tests/terminal-code-eater.cc | 6 ++--- 7 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 src/libutil/escape-char.cc create mode 100644 src/libutil/escape-char.hh delete mode 100644 tests/unit/libutil-support/tests/debug-char.hh diff --git a/src/libutil/escape-char.cc b/src/libutil/escape-char.cc new file mode 100644 index 000000000..132260286 --- /dev/null +++ b/src/libutil/escape-char.cc @@ -0,0 +1,22 @@ +#include +#include +#include + +#include "escape-char.hh" + +namespace nix { + +std::ostream & operator<<(std::ostream & s, MaybeHexEscapedChar c) +{ + boost::io::ios_flags_saver _ifs(s); + + if (isprint(c.c)) { + s << static_cast(c.c); + } else { + s << "\\x" << std::hex << std::setfill('0') << std::setw(2) + << (static_cast(c.c) & 0xff); + } + return s; +} + +} // namespace nix diff --git a/src/libutil/escape-char.hh b/src/libutil/escape-char.hh new file mode 100644 index 000000000..c7bae7ec0 --- /dev/null +++ b/src/libutil/escape-char.hh @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace nix { + +/** + * A struct that prints a debug representation of a character, like `\x1f` for + * non-printable characters, or the character itself for other characters. + * + * Note that these are suitable for human readable output, but further care is + * necessary to include them in C++ strings to avoid running into adjacent + * hex-like characters. (`"puppy\x1bdoggy"` parses as `"puppy" "\x1bd" "oggy"` + * and errors because 0x1bd is too big for a `char`.) + */ +struct MaybeHexEscapedChar +{ + char c; +}; + +std::ostream & operator<<(std::ostream & s, MaybeHexEscapedChar c); + +} // namespace nix diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 8e4b5211d..cdfda3cf5 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -8,6 +8,7 @@ libutil_sources = files( 'config.cc', 'english.cc', 'error.cc', + 'escape-char.cc', 'exit.cc', 'experimental-features.cc', 'filesystem.cc', @@ -49,6 +50,7 @@ libutil_headers = files( 'config.hh', 'english.hh', 'error.hh', + 'escape-char.hh', 'exit.hh', 'experimental-features.hh', 'experimental-features-json.hh', diff --git a/tests/functional/repl_characterization/test-session.cc b/tests/functional/repl_characterization/test-session.cc index c35030fc7..50e27e58c 100644 --- a/tests/functional/repl_characterization/test-session.cc +++ b/tests/functional/repl_characterization/test-session.cc @@ -3,7 +3,7 @@ #include "test-session.hh" #include "util.hh" -#include "tests/debug-char.hh" +#include "escape-char.hh" namespace nix { @@ -60,7 +60,7 @@ std::ostream & operator<<(std::ostream & os, ReplOutputParser::State s) void ReplOutputParser::transition(State new_state, char responsible_char, bool wasPrompt) { if constexpr (DEBUG_REPL_PARSER) { - std::cerr << "transition " << new_state << " for " << DebugChar{responsible_char} + std::cerr << "transition " << new_state << " for " << MaybeHexEscapedChar{responsible_char} << (wasPrompt ? " [prompt]" : "") << "\n"; } state = new_state; @@ -118,7 +118,7 @@ bool TestSession::waitForPrompt() }); if constexpr (DEBUG_REPL_PARSER) { - std::cerr << "raw " << DebugChar{buf[i]} << (wasEaten ? " [eaten]" : "") << "\n"; + std::cerr << "raw " << MaybeHexEscapedChar{buf[i]} << (wasEaten ? " [eaten]" : "") << "\n"; } } diff --git a/tests/unit/libutil-support/tests/cli-literate-parser.cc b/tests/unit/libutil-support/tests/cli-literate-parser.cc index b3830e32c..08ebddebc 100644 --- a/tests/unit/libutil-support/tests/cli-literate-parser.cc +++ b/tests/unit/libutil-support/tests/cli-literate-parser.cc @@ -1,6 +1,6 @@ #include "cli-literate-parser.hh" #include "libexpr/print.hh" -#include "debug-char.hh" +#include "escape-char.hh" #include "types.hh" #include "util.hh" #include @@ -77,7 +77,7 @@ CLILiterateParser::CLILiterateParser(std::string prompt, size_t indent) void CLILiterateParser::feed(char c) { if constexpr (DEBUG_PARSER) { - std::cout << stateDebug(state_) << " " << DebugChar{c} << "\n"; + std::cout << stateDebug(state_) << " " << MaybeHexEscapedChar{c} << "\n"; } if (c == '\n') { diff --git a/tests/unit/libutil-support/tests/debug-char.hh b/tests/unit/libutil-support/tests/debug-char.hh deleted file mode 100644 index 765d8553f..000000000 --- a/tests/unit/libutil-support/tests/debug-char.hh +++ /dev/null @@ -1,24 +0,0 @@ -///@file -#include -#include - -namespace nix { - -struct DebugChar -{ - char c; -}; - -inline std::ostream & operator<<(std::ostream & s, DebugChar c) -{ - boost::io::ios_flags_saver _ifs(s); - - if (isprint(c.c)) { - s << static_cast(c.c); - } else { - s << std::hex << "0x" << (static_cast(c.c) & 0xff); - } - return s; -} - -} diff --git a/tests/unit/libutil-support/tests/terminal-code-eater.cc b/tests/unit/libutil-support/tests/terminal-code-eater.cc index 51e1d565e..ad05ed1f6 100644 --- a/tests/unit/libutil-support/tests/terminal-code-eater.cc +++ b/tests/unit/libutil-support/tests/terminal-code-eater.cc @@ -1,5 +1,5 @@ #include "terminal-code-eater.hh" -#include "debug-char.hh" +#include "escape-char.hh" #include #include #include @@ -14,7 +14,7 @@ void TerminalCodeEater::feed(char c, std::function on_char) auto isIntermediateChar = [](char v) -> bool { return v >= 0x20 && v <= 0x2f; }; auto isFinalChar = [](char v) -> bool { return v >= 0x40 && v <= 0x7e; }; if constexpr (DEBUG_EATER) { - std::cerr << "eater" << DebugChar{c} << "\n"; + std::cerr << "eater" << MaybeHexEscapedChar{c} << "\n"; } switch (state) { @@ -28,7 +28,7 @@ void TerminalCodeEater::feed(char c, std::function on_char) return; } if constexpr (DEBUG_EATER) { - std::cerr << "eater uneat" << DebugChar{c} << "\n"; + std::cerr << "eater uneat" << MaybeHexEscapedChar{c} << "\n"; } on_char(c); break;