Skip to content

Commit

Permalink
feat(io): add Output_Stream::append_padded_decimal_integer
Browse files Browse the repository at this point in the history
I want to port some std::ostream-using code to Output_Stream. That
code uses set::setw, but Output_Stream has no equivalent. Implement
Output_Stream::append_padded_decimal_integer for unsigned integers so
we can port that code properly.
  • Loading branch information
strager committed Oct 28, 2023
1 parent b74b691 commit b738de6
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/quick-lint-js/io/output-stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <quick-lint-js/io/file-handle.h>
#include <quick-lint-js/io/file.h>
#include <quick-lint-js/port/char8.h>
#include <quick-lint-js/port/type-traits.h>
#include <quick-lint-js/util/integer.h>
#include <quick-lint-js/util/narrow-cast.h>

Expand Down Expand Up @@ -53,6 +54,37 @@ class Output_Stream {
});
}

// Write pad_character then write a decimal integer. The total number of
// characters written is at least pad_width.
//
// append_padded_decimal_integer(42, 4, '0')
// => u8"0042"
template <class T>
void append_padded_decimal_integer(
T value, int pad_width,
std::enable_if_t<std::is_unsigned_v<T>, Char8> pad_character) {
int max_width = std::max(integer_string_length<T>, pad_width);
this->append(max_width, [&](Char8* out) -> int {
Char8* integer_end = write_integer<T>(value, out);
int integer_width = narrow_cast<int>(integer_end - out);
bool need_padding = pad_width > integer_width;
if (!need_padding) {
return integer_width;
}

Char8* out_end = out + pad_width;
Char8* integer_start = std::copy_backward(out, integer_end, out_end);
QLJS_ASSERT(out < integer_start &&
"should pad with at least one character");
QLJS_ASSERT(integer_start < out_end &&
"integer should consume at least one character");
for (Char8* c = out; c != integer_start; ++c) {
*c = pad_character;
}
return pad_width;
});
}

template <class T>
void append_fixed_hexadecimal_integer(T value, int width) {
this->append(width, [&](Char8* out) -> int {
Expand Down
49 changes: 49 additions & 0 deletions test/test-output-stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,55 @@ TEST(Test_Memory_Output_Stream, append_fixed_hexadecimal_integer) {
}
}

TEST(Test_Memory_Output_Stream, append_padded_decimal_integer_exact_width) {
{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(1234u, 4, u8'x');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8"1234"_sv);
}

{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(0u, 1, u8'x');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8"0"_sv);
}
}

TEST(Test_Memory_Output_Stream, append_padded_decimal_integer_over_width) {
{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(1234u, 5, u8'x');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8"x1234"_sv);
}

{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(0u, 20, u8' ');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8" 0"_sv);
}
}

TEST(Test_Memory_Output_Stream,
append_padded_decimal_integer_under_width_behaves_as_exact_width) {
{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(1234u, 1, u8'x');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8"1234"_sv);
}

{
Memory_Output_Stream s(/*buffer_size=*/16);
s.append_padded_decimal_integer(0u, 0, u8' ');
s.flush();
EXPECT_EQ(s.get_flushed_string8(), u8"0"_sv);
}
}

#if defined(__EMSCRIPTEN__)
// No filesystem on web.
#else
Expand Down

0 comments on commit b738de6

Please sign in to comment.