/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Fill into the buffer using the data in src bool mozilla::PrintfTarget::fill2(constchar* src, int srclen, int width, int flags) { char space = ' ';
width -= srclen; if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting if (flags & FLAG_ZEROS) {
space = '0';
} while (--width >= 0) { if (!emit(&space, 1)) { returnfalse;
}
}
}
// Copy out the source data if (!emit(src, srclen)) { returnfalse;
}
if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting while (--width >= 0) { if (!emit(&space, 1)) { returnfalse;
}
}
} returntrue;
}
/* * Fill a number. The order is: optional-sign zero-filling conversion-digits
*/ bool mozilla::PrintfTarget::fill_n(constchar* src, int srclen, int width, int prec, int type, int flags) { int zerowidth = 0; int precwidth = 0; int leftspaces = 0; int rightspaces = 0; int cvtwidth; char sign = 0;
if (flags & FLAG_LEFT) { if (width > cvtwidth) { // Space filling on the right (i.e. left adjusting)
rightspaces = width - cvtwidth;
}
} else { if (width > cvtwidth) { // Space filling on the left (i.e. right adjusting)
leftspaces = width - cvtwidth;
}
} while (--leftspaces >= 0) { if (!emit(" ", 1)) { returnfalse;
}
} if (sign) { if (!emit(&sign, 1)) { returnfalse;
}
} while (--precwidth >= 0) { if (!emit("0", 1)) { returnfalse;
}
} while (--zerowidth >= 0) { if (!emit("0", 1)) { returnfalse;
}
} if (!emit(src, uint32_t(srclen))) { returnfalse;
} while (--rightspaces >= 0) { if (!emit(" ", 1)) { returnfalse;
}
} returntrue;
}
// All that the cvt_* functions care about as far as the TYPE_* constants is // that the low bit is set to indicate unsigned, or unset to indicate signed. // So we don't try to hard to ensure that the passed TYPE_* constant lines // up with the actual size of the number being printed here. The main printf // code, below, does have to care so that the correct bits are extracted from // the varargs list. bool mozilla::PrintfTarget::appendIntDec(int32_t num) { int flags = 0; long n = num; if (n < 0) {
n = -n;
flags |= FLAG_NEG;
} return cvt_l(n, -1, -1, 10, TYPE_INTN, flags, hex);
}
/* Convert a long into its printable form. */ bool mozilla::PrintfTarget::cvt_l(long num, int width, int prec, int radix, int type, int flags, constchar* hexp) { char cvtbuf[100]; char* cvt; int digits;
// according to the man page this needs to happen if ((prec == 0) && (num == 0)) { return fill_n("", 0, width, prec, type, flags);
}
// Converting decimal is a little tricky. In the unsigned case we // need to stop when we hit 10 digits. In the signed case, we can // stop when the number is zero.
cvt = cvtbuf + sizeof(cvtbuf);
digits = 0; while (num) { int digit = (((unsignedlong)num) % radix) & 0xF;
*--cvt = hexp[digit];
digits++;
num = (long)(((unsignedlong)num) / radix);
} if (digits == 0) {
*--cvt = '0';
digits++;
}
// Now that we have the number converted without its sign, deal with // the sign and zero padding. return fill_n(cvt, digits, width, prec, type, flags);
}
/* Convert a 64-bit integer into its printable form. */ bool mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags, constchar* hexp) { // According to the man page, this needs to happen. if (prec == 0 && num == 0) { return fill_n("", 0, width, prec, type, flags);
}
// Converting decimal is a little tricky. In the unsigned case we // need to stop when we hit 10 digits. In the signed case, we can // stop when the number is zero.
int64_t rad = int64_t(radix); char cvtbuf[100]; char* cvt = cvtbuf + sizeof(cvtbuf); int digits = 0; while (num != 0) {
int64_t quot = uint64_t(num) / rad;
int64_t rem = uint64_t(num) % rad;
int32_t digit = int32_t(rem);
*--cvt = hexp[digit & 0xf];
digits++;
num = quot;
} if (digits == 0) {
*--cvt = '0';
digits++;
}
// Now that we have the number converted without its sign, deal with // the sign and zero padding. return fill_n(cvt, digits, width, prec, type, flags);
}
// Longest possible output from ToFixed for positive numbers: // [0-9]{kMaxFixedDigitsBeforePoint}\.[0-9]{kMaxFixedDigitsAfterPoint}?
constexpr int FIXED_MAX_CHARS =
DTSC::kMaxFixedDigitsBeforePoint + 1 + DTSC::kMaxFixedDigitsAfterPoint;
// Longest possible output from ToExponential: // [1-9]\.[0-9]{kMaxExponentialDigits}e[+-][0-9]{1,3} // (std::numeric_limits<double>::max() has exponent 308).
constexpr int EXPONENTIAL_MAX_CHARS =
lengthof("1.") + DTSC::kMaxExponentialDigits + lengthof("e+999");
// Longest possible output from ToPrecise: // [0-9\.]{kMaxPrecisionDigits+1} or // [1-9]\.[0-9]{kMaxPrecisionDigits-1}e[+-][0-9]{1,3}
constexpr int PRECISE_MAX_CHARS =
lengthof("1.") + DTSC::kMaxPrecisionDigits - 1 + lengthof("e+999");
constexpr int DTSC_MAX_CHARS =
std::max({FIXED_MAX_CHARS, EXPONENTIAL_MAX_CHARS, PRECISE_MAX_CHARS});
/* * Convert a double precision floating point number into its printable * form.
*/ bool mozilla::PrintfTarget::cvt_f(double d, char c, int width, int prec, int flags) { bool lower = islower(c); constchar* inf = lower ? "inf" : "INF"; constchar* nan = lower ? "nan" : "NAN"; char e = lower ? 'e' : 'E';
DoubleToStringConverter converter(DTSC::UNIQUE_ZERO | DTSC::NO_TRAILING_ZERO |
DTSC::EMIT_POSITIVE_EXPONENT_SIGN,
inf, nan, e, 0, 0, 4, 0, 2); // Longest of the above cases, plus space for a terminal nul character. char buf[DTSC_MAX_CHARS + 1];
double_conversion::StringBuilder builder(buf, sizeof(buf)); bool success = false; if (std::signbit(d)) {
d = std::abs(d);
flags |= FLAG_NEG;
} if (!std::isfinite(d)) {
flags &= ~FLAG_ZEROS;
} // "If the precision is missing, it shall be taken as 6." if (prec < 0) {
prec = 6;
} switch (c) { case'e': case'E':
success = converter.ToExponential(d, prec, &builder); break; case'f': case'F':
success = converter.ToFixed(d, prec, &builder); break; case'g': case'G': // "If an explicit precision is zero, it shall be taken as 1."
success = converter.ToPrecision(d, prec ? prec : 1, &builder); break;
} if (!success) { returnfalse;
} int len = builder.position(); char* cvt = builder.Finalize(); return fill_n(cvt, len, width, prec, TYPE_DOUBLE, flags);
}
/* * Convert a string into its printable form. "width" is the output * width. "prec" is the maximum number of characters of "s" to output, * where -1 means until NUL.
*/ bool mozilla::PrintfTarget::cvt_s(constchar* s, int width, int prec, int flags) { if (prec == 0) { returntrue;
} if (!s) {
s = "(null)";
}
// Limit string length by precision value int slen = int(strlen(s)); if (0 < prec && prec < slen) {
slen = prec;
}
// and away we go return fill2(s, slen, width, flags);
}
/* * BuildArgArray stands for Numbered Argument list Sprintf * for example, * fmp = "%4$i, %2$d, %3s, %1d"; * the number must start from 1, and no gap among them
*/ staticbool BuildArgArray(constchar* fmt, va_list ap, NumArgStateVector& nas) {
size_t number = 0, cn = 0, i; constchar* p; char c;
// First pass: // Detemine how many legal % I have got, then allocate space.
p = fmt;
i = 0; while ((c = *p++) != 0) { if (c != '%') { continue;
} if ((c = *p++) == '%') { // skip %% case continue;
}
while (c != 0) { if (c > '9' || c < '0') { if (c == '$') { // numbered argument case if (i > 0) {
MOZ_CRASH("Bad format string");
}
number++;
} else { // non-numbered argument case if (number > 0) {
MOZ_CRASH("Bad format string");
}
i = 1;
} break;
}
c = *p++;
}
}
if (number == 0) { returntrue;
}
// Only allow a limited number of arguments.
MOZ_RELEASE_ASSERT(number <= 20);
if (!nas.growByUninitialized(number)) { returnfalse;
}
for (i = 0; i < number; i++) {
nas[i].type = TYPE_UNKNOWN;
}
// Second pass: // Set nas[].type.
p = fmt; while ((c = *p++) != 0) { if (c != '%') { continue;
}
c = *p++; if (c == '%') { continue;
}
cn = 0; while (c && c != '$') { // should improve error check later
cn = cn * 10 + c - '0';
c = *p++;
}
if (!c || cn < 1 || cn > number) {
MOZ_CRASH("Bad format string");
}
// nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
cn--; if (nas[cn].type != TYPE_UNKNOWN) { continue;
}
// get a legal para. if (nas[cn].type == TYPE_UNKNOWN) {
MOZ_CRASH("Bad format string");
}
}
// Third pass: // Fill nas[].ap.
cn = 0; while (cn < number) { // A TYPE_UNKNOWN here means that the format asked for a // positional argument without specifying the meaning of some // earlier argument.
MOZ_ASSERT(nas[cn].type != TYPE_UNKNOWN);
va_copy(nas[cn].ap, ap);
switch (nas[cn].type) { case TYPE_SCHAR: case TYPE_UCHAR: case TYPE_SHORT: case TYPE_USHORT: case TYPE_INTN: case TYPE_UINTN:
(void)va_arg(ap, int); break; case TYPE_LONG:
(void)va_arg(ap, long); break; case TYPE_ULONG:
(void)va_arg(ap, unsignedlong); break; case TYPE_LONGLONG:
(void)va_arg(ap, longlong); break; case TYPE_ULONGLONG:
(void)va_arg(ap, unsignedlonglong); break; case TYPE_STRING:
(void)va_arg(ap, char*); break; case TYPE_INTSTR:
(void)va_arg(ap, int*); break; case TYPE_DOUBLE:
(void)va_arg(ap, double); break; case TYPE_POINTER:
(void)va_arg(ap, void*); break; #ifdefined(XP_WIN) case TYPE_WSTRING:
(void)va_arg(ap, wchar_t*); break; #endif
bool mozilla::PrintfTarget::vprint(constchar* fmt, va_list ap) { char c; int flags, width, prec, radix, type; union { char ch; int i; long l; longlong ll; double d; constchar* s; int* ip; void* p; #ifdefined(XP_WIN) constwchar_t* ws; #endif
} u{}; constchar* hexp; int i;
// Build an argument array, IF the fmt is numbered argument // list style, to contain the Numbered Argument list pointers.
NumArgStateVector nas; if (!BuildArgArray(fmt, ap, nas)) { // the fmt contains error Numbered Argument format, jliu@netscape.com
MOZ_CRASH("Bad format string");
}
while ((c = *fmt++) != 0) { if (c != '%') { if (!emit(fmt - 1, 1)) { returnfalse;
}
continue;
}
// Gobble up the % format string. Hopefully we have handled all // of the strange cases!
flags = 0;
c = *fmt++; if (c == '%') { // quoting a % with %% if (!emit(fmt - 1, 1)) { returnfalse;
}
continue;
}
if (!nas.empty()) { // the fmt contains the Numbered Arguments feature
i = 0; while (c && c != '$') { // should improve error check later
i = (i * 10) + (c - '0');
c = *fmt++;
}
if (nas[i - 1].type == TYPE_UNKNOWN) {
MOZ_CRASH("Bad format string");
}
ap = nas[i - 1].ap;
c = *fmt++;
}
// Examine optional flags. Note that we do not implement the // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is // somewhat ambiguous and not ideal, which is perhaps why // the various sprintf() implementations are inconsistent // on this feature. while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { if (c == '-') {
flags |= FLAG_LEFT;
} if (c == '+') {
flags |= FLAG_SIGNED;
} if (c == ' ') {
flags |= FLAG_SPACED;
} if (c == '0') {
flags |= FLAG_ZEROS;
}
c = *fmt++;
} if (flags & FLAG_SIGNED) {
flags &= ~FLAG_SPACED;
} if (flags & FLAG_LEFT) {
flags &= ~FLAG_ZEROS;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.