in src/butil/strings/safe_sprintf.cc [275:421]
bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, int base,
char pad, size_t padding, const char* prefix) {
// Sanity check for parameters. None of these should ever fail, but see
// above for the rationale why we can't call CHECK().
DEBUG_CHECK(base >= 2, "");
DEBUG_CHECK(base <= 16, "");
DEBUG_CHECK(!sign || base == 10, "");
DEBUG_CHECK(pad == '0' || pad == ' ', "");
DEBUG_CHECK(padding <= kSSizeMax, "");
DEBUG_CHECK(!(sign && prefix && *prefix), "");
// Handle negative numbers, if the caller indicated that |i| should be
// treated as a signed number; otherwise treat |i| as unsigned (even if the
// MSB is set!)
// Details are tricky, because of limited data-types, but equivalent pseudo-
// code would look like:
// if (sign && i < 0)
// prefix = "-";
// num = abs(i);
int minint = 0;
uint64_t num;
if (sign && i < 0) {
prefix = "-";
// Turn our number positive.
if (i == std::numeric_limits<int64_t>::min()) {
// The most negative integer needs special treatment.
minint = 1;
num = static_cast<uint64_t>(-(i + 1));
} else {
// "Normal" negative numbers are easy.
num = static_cast<uint64_t>(-i);
}
} else {
num = static_cast<uint64_t>(i);
}
// If padding with '0' zero, emit the prefix or '-' character now. Otherwise,
// make the prefix accessible in reverse order, so that we can later output
// it right between padding and the number.
// We cannot choose the easier approach of just reversing the number, as that
// fails in situations where we need to truncate numbers that have padding
// and/or prefixes.
const char* reverse_prefix = NULL;
if (prefix && *prefix) {
if (pad == '0') {
while (*prefix) {
if (padding) {
--padding;
}
Out(*prefix++);
}
prefix = NULL;
} else {
for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) {
}
}
} else
prefix = NULL;
const size_t prefix_length = reverse_prefix - prefix;
// Loop until we have converted the entire number. Output at least one
// character (i.e. '0').
size_t start = count_;
size_t discarded = 0;
bool started = false;
do {
// Make sure there is still enough space left in our output buffer.
if (count_ >= size_) {
if (start < size_) {
// It is rare that we need to output a partial number. But if asked
// to do so, we will still make sure we output the correct number of
// leading digits.
// Since we are generating the digits in reverse order, we actually
// have to discard digits in the order that we have already emitted
// them. This is essentially equivalent to:
// memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1)
for (char* move = buffer_ + start, *end = buffer_ + size_ - 1;
move < end;
++move) {
*move = move[1];
}
++discarded;
--count_;
} else if (count_ - size_ > 1) {
// Need to increment either |count_| or |discarded| to make progress.
// The latter is more efficient, as it eventually triggers fast
// handling of padding. But we have to ensure we don't accidentally
// change the overall state (i.e. switch the state-machine from
// discarding to non-discarding). |count_| needs to always stay
// bigger than |size_|.
--count_;
++discarded;
}
}
// Output the next digit and (if necessary) compensate for the most
// negative integer needing special treatment. This works because,
// no matter the bit width of the integer, the lowest-most decimal
// integer always ends in 2, 4, 6, or 8.
if (!num && started) {
if (reverse_prefix > prefix) {
Out(*--reverse_prefix);
} else {
Out(pad);
}
} else {
started = true;
Out((upcase ? kUpCaseHexDigits : kDownCaseHexDigits)[num%base + minint]);
}
minint = 0;
num /= base;
// Add padding, if requested.
if (padding > 0) {
--padding;
// Performance optimization for when we are asked to output excessive
// padding, but our output buffer is limited in size. Even if we output
// a 64bit number in binary, we would never write more than 64 plus
// prefix non-padding characters. So, once this limit has been passed,
// any further state change can be computed arithmetically; we know that
// by this time, our entire final output consists of padding characters
// that have all already been output.
if (discarded > 8*sizeof(num) + prefix_length) {
IncrementCount(padding);
padding = 0;
}
}
} while (num || padding || (reverse_prefix > prefix));
// Conversion to ASCII actually resulted in the digits being in reverse
// order. We can't easily generate them in forward order, as we can't tell
// the number of characters needed until we are done converting.
// So, now, we reverse the string (except for the possible '-' sign).
char* front = buffer_ + start;
char* back = GetInsertionPoint();
while (--back > front) {
char ch = *back;
*back = *front;
*front++ = ch;
}
IncrementCount(discarded);
return !discarded;
}