Check remaining space to avoid buffer overruns; some cleanups

This commit is contained in:
Alexander Gololobov 2022-12-20 02:21:23 +01:00
parent 4cd91b1ec6
commit 2ab6b7d033
3 changed files with 45 additions and 13 deletions

View File

@ -9,6 +9,13 @@
#include <iostream> #include <iostream>
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
}
namespace UnicodeBar namespace UnicodeBar
{ {
@ -26,36 +33,61 @@ namespace UnicodeBar
return (x - min) / (max - min) * max_width; return (x - min) / (max - min) * max_width;
} }
/// We use the following Unicode characters to draw the bar:
/// U+2588 "█" Full block
/// U+2589 "▉" Left seven eighths block
/// U+258A "▊" Left three quarters block
/// U+258B "▋" Left five eighths block
/// U+258C "▌" Left half block
/// U+258D "▍" Left three eighths block
/// U+258E "▎" Left one quarter block
/// U+258F "▏" Left one eighth block
constexpr size_t GRADES_IN_FULL_BAR = 8;
constexpr char FULL_BAR[] = "";
constexpr char FRACTIONAL_BARS[] = "▏▎▍▌▋▊▉"; /// 7 elements: 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8
size_t getWidthInBytes(double width) size_t getWidthInBytes(double width)
{ {
return static_cast<size_t>(ceil(width - 1.0 / 8) * UNICODE_BAR_CHAR_SIZE); Int64 int_width = Int64(width * GRADES_IN_FULL_BAR);
return (int_width / GRADES_IN_FULL_BAR) * UNICODE_BAR_CHAR_SIZE + (int_width % GRADES_IN_FULL_BAR ? UNICODE_BAR_CHAR_SIZE : 0);
} }
void render(double width, char * dst) static char* checkedCopy(const char * src, size_t src_size, char * dst, const char * dst_end)
{ {
size_t floor_width = static_cast<size_t>(floor(width)); if (dst + src_size > dst_end)
throw DB::Exception(
DB::ErrorCodes::LOGICAL_ERROR,
"Not enough space in buffer for UnicodeBar::render, required: {}, got: {}",
src_size, dst_end - dst);
memcpy(dst, src, src_size);
return dst + src_size;
}
void render(double width, char * dst, const char * dst_end)
{
Int64 int_width = Int64(width * GRADES_IN_FULL_BAR);
size_t floor_width = (int_width / GRADES_IN_FULL_BAR);
for (size_t i = 0; i < floor_width; ++i) for (size_t i = 0; i < floor_width; ++i)
{ {
memcpy(dst, "", UNICODE_BAR_CHAR_SIZE); dst = checkedCopy(FULL_BAR, UNICODE_BAR_CHAR_SIZE, dst, dst_end);
dst += UNICODE_BAR_CHAR_SIZE;
} }
size_t remainder = static_cast<size_t>(floor((width - floor_width) * 8)); size_t remainder = int_width % GRADES_IN_FULL_BAR;
if (remainder) if (remainder)
{ {
memcpy(dst, &"▏▎▍▌▋▋▊▉"[(remainder - 1) * UNICODE_BAR_CHAR_SIZE], UNICODE_BAR_CHAR_SIZE); dst = checkedCopy(&FRACTIONAL_BARS[(remainder - 1) * UNICODE_BAR_CHAR_SIZE], UNICODE_BAR_CHAR_SIZE, dst, dst_end);
dst += UNICODE_BAR_CHAR_SIZE;
} }
*dst = 0; dst = checkedCopy("\0", 1, dst, dst_end);
} }
std::string render(double width) std::string render(double width)
{ {
std::string res(getWidthInBytes(width), '\0'); std::string res(getWidthInBytes(width) + 1, '\0');
render(width, res.data()); render(width, res.data(), res.data() + res.size());
return res; return res;
} }
} }

View File

@ -14,6 +14,6 @@ namespace UnicodeBar
size_t getWidthInBytes(double width); size_t getWidthInBytes(double width);
/// In `dst` there must be a space for barWidthInBytes(width) characters and a trailing zero. /// In `dst` there must be a space for barWidthInBytes(width) characters and a trailing zero.
void render(double width, char * dst); void render(double width, char * dst, const char * dst_end);
std::string render(double width); std::string render(double width);
} }

View File

@ -118,7 +118,7 @@ public:
size_t next_size = current_offset + UnicodeBar::getWidthInBytes(width) + 1; size_t next_size = current_offset + UnicodeBar::getWidthInBytes(width) + 1;
dst_chars.resize(next_size); dst_chars.resize(next_size);
UnicodeBar::render(width, reinterpret_cast<char *>(&dst_chars[current_offset])); UnicodeBar::render(width, reinterpret_cast<char *>(&dst_chars[current_offset]), reinterpret_cast<char *>(&dst_chars[next_size]));
current_offset = next_size; current_offset = next_size;
dst_offsets[i] = current_offset; dst_offsets[i] = current_offset;
} }