vsprintf method
Formats arguments into a String using a va_list.
This is the core formatting engine. Supports the full C99 format specifier
syntax: %[flags][width][.precision][length]specifier.
- Flags:
-,+,,0,# - Width / Precision: integer or
*(next arg) - Length modifiers:
h,l,ll,L,z,t,j— consumed and ignored (Dart integers are always 64-bit) - Specifiers:
diuoxXfFeEgGscpn%
Implementation
String vsprintf(String format, va_list arg) {
final buffer = StringBuffer();
int i = 0;
while (i < format.length) {
if (format[i] != '%') {
buffer.write(format[i++]);
continue;
}
i++; // skip '%'
if (i >= format.length) break;
if (format[i] == '%') {
buffer.write('%');
i++;
continue;
}
// --- Flags ---
bool flagMinus = false, flagPlus = false, flagSpace = false;
bool flagZero = false, flagHash = false;
bool parsingFlags = true;
while (parsingFlags && i < format.length) {
switch (format[i]) {
case '-':
flagMinus = true;
i++;
case '+':
flagPlus = true;
i++;
case ' ':
flagSpace = true;
i++;
case '0':
flagZero = true;
i++;
case '#':
flagHash = true;
i++;
default:
parsingFlags = false;
}
}
// --- Width ---
int width = 0;
if (i < format.length && format[i] == '*') {
dynamic wVal;
try {
wVal = va_arg<dynamic>(arg);
} on StateError {
break;
}
width = (wVal as num).toInt();
if (width < 0) {
flagMinus = true;
width = -width;
}
i++;
} else {
while (i < format.length && _fmtIsDigit(format[i])) {
width = width * 10 + (format.codeUnitAt(i) - 48);
i++;
}
}
// --- Precision ---
int? precision;
if (i < format.length && format[i] == '.') {
i++;
if (i < format.length && format[i] == '*') {
dynamic pVal;
try {
pVal = va_arg<dynamic>(arg);
} on StateError {
break;
}
final p = (pVal as num).toInt();
precision = p < 0 ? null : p;
i++;
} else {
precision = 0;
while (i < format.length && _fmtIsDigit(format[i])) {
precision = precision! * 10 + (format.codeUnitAt(i) - 48);
i++;
}
}
}
// --- Length modifier (consume, ignore) ---
if (i < format.length) {
final c = format[i];
if (c == 'h' ||
c == 'l' ||
c == 'L' ||
c == 'z' ||
c == 't' ||
c == 'j') {
i++;
if (i < format.length && (format[i] == 'l' || format[i] == 'h')) i++;
}
}
if (i >= format.length) break;
final spec = format[i++];
// --- Fetch argument ---
dynamic argVal;
if (spec != 'n') {
try {
argVal = va_arg<dynamic>(arg);
} on StateError {
buffer.write('%$spec');
continue;
}
}
// --- Format ---
switch (spec) {
case 'd':
case 'i':
buffer.write(
_fmtInt(
(argVal as num).toInt(),
10,
false,
flagMinus,
flagPlus,
flagSpace,
flagZero,
false,
width,
precision,
),
);
case 'u':
buffer.write(
_fmtUInt(
(argVal as num).toInt(),
10,
false,
flagMinus,
flagZero,
false,
width,
precision,
),
);
case 'o':
buffer.write(
_fmtUInt(
(argVal as num).toInt(),
8,
false,
flagMinus,
flagZero,
flagHash,
width,
precision,
),
);
case 'x':
buffer.write(
_fmtUInt(
(argVal as num).toInt(),
16,
false,
flagMinus,
flagZero,
flagHash,
width,
precision,
),
);
case 'X':
buffer.write(
_fmtUInt(
(argVal as num).toInt(),
16,
true,
flagMinus,
flagZero,
flagHash,
width,
precision,
),
);
case 'f':
case 'F':
buffer.write(
_fmtFloat(
(argVal as num).toDouble(),
precision ?? 6,
flagMinus,
flagPlus,
flagSpace,
flagZero,
flagHash,
width,
),
);
case 'e':
buffer.write(
_fmtSci(
(argVal as num).toDouble(),
precision ?? 6,
false,
flagMinus,
flagPlus,
flagSpace,
flagZero,
width,
),
);
case 'E':
buffer.write(
_fmtSci(
(argVal as num).toDouble(),
precision ?? 6,
true,
flagMinus,
flagPlus,
flagSpace,
flagZero,
width,
),
);
case 'g':
buffer.write(
_fmtG(
(argVal as num).toDouble(),
precision == 0 ? 1 : (precision ?? 6),
false,
flagMinus,
flagPlus,
flagSpace,
flagZero,
flagHash,
width,
),
);
case 'G':
buffer.write(
_fmtG(
(argVal as num).toDouble(),
precision == 0 ? 1 : (precision ?? 6),
true,
flagMinus,
flagPlus,
flagSpace,
flagZero,
flagHash,
width,
),
);
case 's':
String s = argVal.toString();
if (precision != null && precision < s.length)
s = s.substring(0, precision);
buffer.write(_fmtPad(s, width, flagMinus, false, ''));
case 'c':
final ch = argVal is int
? String.fromCharCode(argVal)
: (argVal is String && argVal.isNotEmpty ? argVal[0] : '');
buffer.write(_fmtPad(ch, width, flagMinus, false, ''));
case 'p':
final hex = '0x${identityHashCode(argVal).toRadixString(16)}';
buffer.write(_fmtPad(hex, width, flagMinus, false, ''));
case 'n':
// C writes chars-written count to a pointer; no-op in Dart.
break;
default:
buffer.write('%$spec');
}
}
return buffer.toString();
}