in runtime/runtime.cpp [1133:1281]
static word strFormat(Thread* thread, const MutableBytes& dst,
bool determine_size, const char* fmt, va_list args) {
HandleScope scope(thread);
word fragment_begin = 0;
word fmt_index = 0;
word dst_index = 0;
word size = determine_size ? -1 : dst.length();
for (; fmt[fmt_index] != '\0'; fmt_index++) {
if (fmt[fmt_index] != '%') {
continue;
}
word fragment_length = fmt_index - fragment_begin;
if (!determine_size) {
std::memcpy(reinterpret_cast<void*>(dst.address() + dst_index),
fmt + fragment_begin, fragment_length);
}
dst_index += fragment_length;
fmt_index++;
fragment_begin = fmt_index + 1;
switch (fmt[fmt_index]) {
case 'c': {
int value_int = va_arg(args, int); // Note that C promotes char to int.
if (value_int < 0 || value_int > kMaxASCII) {
// Replace non-ASCII characters.
RawSmallStr value = SmallStr::fromCodePoint(kReplacementCharacter);
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, Str::cast(value), length);
}
dst_index += length;
break;
}
if (!determine_size) {
dst.byteAtPut(dst_index, static_cast<char>(value_int));
}
dst_index++;
} break;
case 'd': {
int value = va_arg(args, int);
char* print_dst =
determine_size ? nullptr
: reinterpret_cast<char*>(dst.address() + dst_index);
size_t print_len = determine_size ? 0 : size - dst_index + 1;
dst_index += std::snprintf(print_dst, print_len, "%d", value);
} break;
case 'g': {
double value = va_arg(args, double);
char* print_dst =
determine_size ? nullptr
: reinterpret_cast<char*>(dst.address() + dst_index);
size_t print_len = determine_size ? 0 : size - dst_index + 1;
dst_index += std::snprintf(print_dst, print_len, "%g", value);
} break;
case 's': {
const char* value = va_arg(args, char*);
word length = std::strlen(value);
if (!determine_size) {
std::memcpy(reinterpret_cast<byte*>(dst.address() + dst_index), value,
length);
}
dst_index += length;
} break;
case 'w': {
word value = va_arg(args, word);
char* print_dst =
determine_size ? nullptr
: reinterpret_cast<char*>(dst.address() + dst_index);
size_t print_len = determine_size ? 0 : size - dst_index + 1;
dst_index += std::snprintf(print_dst, print_len, "%" PRIdPTR, value);
} break;
case 'x': {
unsigned value = va_arg(args, unsigned);
char* print_dst =
determine_size ? nullptr
: reinterpret_cast<char*>(dst.address() + dst_index);
size_t print_len = determine_size ? 0 : size - dst_index + 1;
dst_index += std::snprintf(print_dst, print_len, "%x", value);
} break;
case 'C': {
int32_t value_int = va_arg(args, int32_t);
if (value_int < 0 || value_int > kMaxUnicode) {
value_int = kReplacementCharacter;
}
RawSmallStr value = SmallStr::fromCodePoint(value_int);
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, Str::cast(value), length);
}
dst_index += length;
} break;
case 'S': {
Object value_obj(&scope, **va_arg(args, Object*));
Str value(&scope, strUnderlying(*value_obj));
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, *value, length);
}
dst_index += length;
} break;
case 'F': {
Object obj(&scope, **va_arg(args, Object*));
Function function(&scope, *obj);
Str value(&scope, function.qualname());
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, *value, length);
}
dst_index += length;
} break;
case 'T': {
Object obj(&scope, **va_arg(args, Object*));
Type type(&scope, thread->runtime()->typeOf(*obj));
Str value(&scope, type.name());
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, *value, length);
}
dst_index += length;
} break;
case 'Y': {
SymbolId symbol = va_arg(args, SymbolId);
RawStr value = Str::cast(thread->runtime()->symbols()->at(symbol));
word length = value.length();
if (!determine_size) {
dst.replaceFromWithStr(dst_index, value, length);
}
dst_index += length;
} break;
case '%':
if (!determine_size) {
dst.byteAtPut(dst_index, '%');
}
dst_index++;
break;
default:
UNIMPLEMENTED("Unsupported format specifier");
}
DCHECK(determine_size || dst_index <= size, "dst buffer overflow");
}
word fragment_length = fmt_index - fragment_begin;
if (!determine_size) {
std::memcpy(reinterpret_cast<void*>(dst.address() + dst_index),
fmt + fragment_begin, fragment_length);
}
dst_index += fragment_length;
DCHECK(determine_size || dst_index == size, "dst buffer underflow");
return dst_index;
}