diff options
author | Wim <wim@42.be> | 2020-01-09 21:52:19 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-09 21:52:19 +0100 |
commit | 9d84d6dd643c4017074e81465671cd9b25f9539a (patch) | |
tree | 8a767f91d655a6cf21d476e4fb7aa6fd8a952df8 /vendor/github.com/d5/tengo/objects/formatter.go | |
parent | 0f708daf2d14dcca261ef98cc698a1b1f2a6aa74 (diff) | |
download | matterbridge-msglm-9d84d6dd643c4017074e81465671cd9b25f9539a.tar.gz matterbridge-msglm-9d84d6dd643c4017074e81465671cd9b25f9539a.tar.bz2 matterbridge-msglm-9d84d6dd643c4017074e81465671cd9b25f9539a.zip |
Update to tengo v2 (#976)
Diffstat (limited to 'vendor/github.com/d5/tengo/objects/formatter.go')
-rw-r--r-- | vendor/github.com/d5/tengo/objects/formatter.go | 1212 |
1 files changed, 0 insertions, 1212 deletions
diff --git a/vendor/github.com/d5/tengo/objects/formatter.go b/vendor/github.com/d5/tengo/objects/formatter.go deleted file mode 100644 index 95d7f6b1..00000000 --- a/vendor/github.com/d5/tengo/objects/formatter.go +++ /dev/null @@ -1,1212 +0,0 @@ -package objects - -import ( - "strconv" - "sync" - "unicode/utf8" - - "github.com/d5/tengo" -) - -// Strings for use with buffer.WriteString. -// This is less overhead than using buffer.Write with byte arrays. -const ( - commaSpaceString = ", " - nilParenString = "(nil)" - percentBangString = "%!" - missingString = "(MISSING)" - badIndexString = "(BADINDEX)" - extraString = "%!(EXTRA " - badWidthString = "%!(BADWIDTH)" - badPrecString = "%!(BADPREC)" - noVerbString = "%!(NOVERB)" -) - -const ( - ldigits = "0123456789abcdefx" - udigits = "0123456789ABCDEFX" -) - -const ( - signed = true - unsigned = false -) - -// flags placed in a separate struct for easy clearing. -type fmtFlags struct { - widPresent bool - precPresent bool - minus bool - plus bool - sharp bool - space bool - zero bool - - // For the formats %+v %#v, we set the plusV/sharpV flags - // and clear the plus/sharp flags since %+v and %#v are in effect - // different, flagless formats set at the top level. - plusV bool - sharpV bool - - // error-related flags. - inDetail bool - needNewline bool - needColon bool -} - -// A formatter is the raw formatter used by Printf etc. -// It prints into a buffer that must be set up separately. -type formatter struct { - buf *buffer - - fmtFlags - - wid int // width - prec int // precision - - // intbuf is large enough to store %b of an int64 with a sign and - // avoids padding at the end of the struct on 32 bit architectures. - intbuf [68]byte -} - -func (f *formatter) clearflags() { - f.fmtFlags = fmtFlags{} -} - -func (f *formatter) init(buf *buffer) { - f.buf = buf - f.clearflags() -} - -// writePadding generates n bytes of padding. -func (f *formatter) writePadding(n int) { - if n <= 0 { // No padding bytes needed. - return - } - buf := *f.buf - oldLen := len(buf) - newLen := oldLen + n - - if newLen > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - // Make enough room for padding. - if newLen > cap(buf) { - buf = make(buffer, cap(buf)*2+n) - copy(buf, *f.buf) - } - // Decide which byte the padding should be filled with. - padByte := byte(' ') - if f.zero { - padByte = byte('0') - } - // Fill padding with padByte. - padding := buf[oldLen:newLen] - for i := range padding { - padding[i] = padByte - } - *f.buf = buf[:newLen] -} - -// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *formatter) pad(b []byte) { - if !f.widPresent || f.wid == 0 { - f.buf.Write(b) - return - } - width := f.wid - utf8.RuneCount(b) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.Write(b) - } else { - // right padding - f.buf.Write(b) - f.writePadding(width) - } -} - -// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *formatter) padString(s string) { - if !f.widPresent || f.wid == 0 { - f.buf.WriteString(s) - return - } - width := f.wid - utf8.RuneCountInString(s) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.WriteString(s) - } else { - // right padding - f.buf.WriteString(s) - f.writePadding(width) - } -} - -// fmtBoolean formats a boolean. -func (f *formatter) fmtBoolean(v bool) { - if v { - f.padString("true") - } else { - f.padString("false") - } -} - -// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". -func (f *formatter) fmtUnicode(u uint64) { - buf := f.intbuf[0:] - - // With default precision set the maximum needed buf length is 18 - // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits - // into the already allocated intbuf with a capacity of 68 bytes. - prec := 4 - if f.precPresent && f.prec > 4 { - prec = f.prec - // Compute space needed for "U+" , number, " '", character, "'". - width := 2 + prec + 2 + utf8.UTFMax + 1 - if width > len(buf) { - buf = make([]byte, width) - } - } - - // Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left. - i := len(buf) - - // For %#U we want to add a space and a quoted character at the end of the buffer. - if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) { - i-- - buf[i] = '\'' - i -= utf8.RuneLen(rune(u)) - utf8.EncodeRune(buf[i:], rune(u)) - i-- - buf[i] = '\'' - i-- - buf[i] = ' ' - } - // Format the Unicode code point u as a hexadecimal number. - for u >= 16 { - i-- - buf[i] = udigits[u&0xF] - prec-- - u >>= 4 - } - i-- - buf[i] = udigits[u] - prec-- - // Add zeros in front of the number until requested precision is reached. - for prec > 0 { - i-- - buf[i] = '0' - prec-- - } - // Add a leading "U+". - i-- - buf[i] = '+' - i-- - buf[i] = 'U' - - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// fmtInteger formats signed and unsigned integers. -func (f *formatter) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) { - negative := isSigned && int64(u) < 0 - if negative { - u = -u - } - - buf := f.intbuf[0:] - // The already allocated f.intbuf with a capacity of 68 bytes - // is large enough for integer formatting when no precision or width is set. - if f.widPresent || f.precPresent { - // Account 3 extra bytes for possible addition of a sign and "0x". - width := 3 + f.wid + f.prec // wid and prec are always positive. - if width > len(buf) { - // We're going to need a bigger boat. - buf = make([]byte, width) - } - } - - // Two ways to ask for extra leading zero digits: %.3d or %03d. - // If both are specified the f.zero flag is ignored and - // padding with spaces is used instead. - prec := 0 - if f.precPresent { - prec = f.prec - // Precision of 0 and value of 0 means "print nothing" but padding. - if prec == 0 && u == 0 { - oldZero := f.zero - f.zero = false - f.writePadding(f.wid) - f.zero = oldZero - return - } - } else if f.zero && f.widPresent { - prec = f.wid - if negative || f.plus || f.space { - prec-- // leave room for sign - } - } - - // Because printing is easier right-to-left: format u into buf, ending at buf[i]. - // We could make things marginally faster by splitting the 32-bit case out - // into a separate block but it's not worth the duplication, so u has 64 bits. - i := len(buf) - // Use constants for the division and modulo for more efficient code. - // Switch cases ordered by popularity. - switch base { - case 10: - for u >= 10 { - i-- - next := u / 10 - buf[i] = byte('0' + u - next*10) - u = next - } - case 16: - for u >= 16 { - i-- - buf[i] = digits[u&0xF] - u >>= 4 - } - case 8: - for u >= 8 { - i-- - buf[i] = byte('0' + u&7) - u >>= 3 - } - case 2: - for u >= 2 { - i-- - buf[i] = byte('0' + u&1) - u >>= 1 - } - default: - panic("fmt: unknown base; can't happen") - } - i-- - buf[i] = digits[u] - for i > 0 && prec > len(buf)-i { - i-- - buf[i] = '0' - } - - // Various prefixes: 0x, -, etc. - if f.sharp { - switch base { - case 2: - // Add a leading 0b. - i-- - buf[i] = 'b' - i-- - buf[i] = '0' - case 8: - if buf[i] != '0' { - i-- - buf[i] = '0' - } - case 16: - // Add a leading 0x or 0X. - i-- - buf[i] = digits[16] - i-- - buf[i] = '0' - } - } - if verb == 'O' { - i-- - buf[i] = 'o' - i-- - buf[i] = '0' - } - - if negative { - i-- - buf[i] = '-' - } else if f.plus { - i-- - buf[i] = '+' - } else if f.space { - i-- - buf[i] = ' ' - } - - // Left padding with zeros has already been handled like precision earlier - // or the f.zero flag is ignored due to an explicitly set precision. - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// truncate truncates the string s to the specified precision, if present. -func (f *formatter) truncateString(s string) string { - if f.precPresent { - n := f.prec - for i := range s { - n-- - if n < 0 { - return s[:i] - } - } - } - return s -} - -// truncate truncates the byte slice b as a string of the specified precision, if present. -func (f *formatter) truncate(b []byte) []byte { - if f.precPresent { - n := f.prec - for i := 0; i < len(b); { - n-- - if n < 0 { - return b[:i] - } - wid := 1 - if b[i] >= utf8.RuneSelf { - _, wid = utf8.DecodeRune(b[i:]) - } - i += wid - } - } - return b -} - -// fmtS formats a string. -func (f *formatter) fmtS(s string) { - s = f.truncateString(s) - f.padString(s) -} - -// fmtBs formats the byte slice b as if it was formatted as string with fmtS. -func (f *formatter) fmtBs(b []byte) { - b = f.truncate(b) - f.pad(b) -} - -// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. -func (f *formatter) fmtSbx(s string, b []byte, digits string) { - length := len(b) - if b == nil { - // No byte slice present. Assume string s should be encoded. - length = len(s) - } - // Set length to not process more bytes than the precision demands. - if f.precPresent && f.prec < length { - length = f.prec - } - // Compute width of the encoding taking into account the f.sharp and f.space flag. - width := 2 * length - if width > 0 { - if f.space { - // Each element encoded by two hexadecimals will get a leading 0x or 0X. - if f.sharp { - width *= 2 - } - // Elements will be separated by a space. - width += length - 1 - } else if f.sharp { - // Only a leading 0x or 0X will be added for the whole string. - width += 2 - } - } else { // The byte slice or string that should be encoded is empty. - if f.widPresent { - f.writePadding(f.wid) - } - return - } - // Handle padding to the left. - if f.widPresent && f.wid > width && !f.minus { - f.writePadding(f.wid - width) - } - // Write the encoding directly into the output buffer. - buf := *f.buf - if f.sharp { - // Add leading 0x or 0X. - buf = append(buf, '0', digits[16]) - } - var c byte - for i := 0; i < length; i++ { - if f.space && i > 0 { - // Separate elements with a space. - buf = append(buf, ' ') - if f.sharp { - // Add leading 0x or 0X for each element. - buf = append(buf, '0', digits[16]) - } - } - if b != nil { - c = b[i] // Take a byte from the input byte slice. - } else { - c = s[i] // Take a byte from the input string. - } - // Encode each byte as two hexadecimal digits. - buf = append(buf, digits[c>>4], digits[c&0xF]) - } - *f.buf = buf - // Handle padding to the right. - if f.widPresent && f.wid > width && f.minus { - f.writePadding(f.wid - width) - } -} - -// fmtSx formats a string as a hexadecimal encoding of its bytes. -func (f *formatter) fmtSx(s, digits string) { - f.fmtSbx(s, nil, digits) -} - -// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. -func (f *formatter) fmtBx(b []byte, digits string) { - f.fmtSbx("", b, digits) -} - -// fmtQ formats a string as a double-quoted, escaped Go string constant. -// If f.sharp is set a raw (backquoted) string may be returned instead -// if the string does not contain any control characters other than tab. -func (f *formatter) fmtQ(s string) { - s = f.truncateString(s) - if f.sharp && strconv.CanBackquote(s) { - f.padString("`" + s + "`") - return - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteToASCII(buf, s)) - } else { - f.pad(strconv.AppendQuote(buf, s)) - } -} - -// fmtC formats an integer as a Unicode character. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *formatter) fmtC(c uint64) { - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - w := utf8.EncodeRune(buf[:utf8.UTFMax], r) - f.pad(buf[:w]) -} - -// fmtQc formats an integer as a single-quoted, escaped Go character constant. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *formatter) fmtQc(c uint64) { - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteRuneToASCII(buf, r)) - } else { - f.pad(strconv.AppendQuoteRune(buf, r)) - } -} - -// fmtFloat formats a float64. It assumes that verb is a valid format specifier -// for strconv.AppendFloat and therefore fits into a byte. -func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) { - // Explicit precision in format specifier overrules default precision. - if f.precPresent { - prec = f.prec - } - // Format number, reserving space for leading + sign if needed. - num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size) - if num[1] == '-' || num[1] == '+' { - num = num[1:] - } else { - num[0] = '+' - } - // f.space means to add a leading space instead of a "+" sign unless - // the sign is explicitly asked for by f.plus. - if f.space && num[0] == '+' && !f.plus { - num[0] = ' ' - } - // Special handling for infinities and NaN, - // which don't look like a number so shouldn't be padded with zeros. - if num[1] == 'I' || num[1] == 'N' { - oldZero := f.zero - f.zero = false - // Remove sign before NaN if not asked for. - if num[1] == 'N' && !f.space && !f.plus { - num = num[1:] - } - f.pad(num) - f.zero = oldZero - return - } - // The sharp flag forces printing a decimal point for non-binary formats - // and retains trailing zeros, which we may need to restore. - if f.sharp && verb != 'b' { - digits := 0 - switch verb { - case 'v', 'g', 'G', 'x': - digits = prec - // If no precision is set explicitly use a precision of 6. - if digits == -1 { - digits = 6 - } - } - - // Buffer pre-allocated with enough room for - // exponent notations of the form "e+123" or "p-1023". - var tailBuf [6]byte - tail := tailBuf[:0] - - hasDecimalPoint := false - // Starting from i = 1 to skip sign at num[0]. - for i := 1; i < len(num); i++ { - switch num[i] { - case '.': - hasDecimalPoint = true - case 'p', 'P': - tail = append(tail, num[i:]...) - num = num[:i] - case 'e', 'E': - if verb != 'x' && verb != 'X' { - tail = append(tail, num[i:]...) - num = num[:i] - break - } - fallthrough - default: - digits-- - } - } - if !hasDecimalPoint { - num = append(num, '.') - } - for digits > 0 { - num = append(num, '0') - digits-- - } - num = append(num, tail...) - } - // We want a sign if asked for and if the sign is not positive. - if f.plus || num[0] != '+' { - // If we're zero padding to the left we want the sign before the leading zeros. - // Achieve this by writing the sign out and then padding the unsigned number. - if f.zero && f.widPresent && f.wid > len(num) { - f.buf.WriteSingleByte(num[0]) - f.writePadding(f.wid - len(num)) - f.buf.Write(num[1:]) - return - } - f.pad(num) - return - } - // No sign to show and the number is positive; just print the unsigned number. - f.pad(num[1:]) -} - -// Use simple []byte instead of bytes.Buffer to avoid large dependency. -type buffer []byte - -func (b *buffer) Write(p []byte) { - if len(*b)+len(p) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, p...) -} - -func (b *buffer) WriteString(s string) { - if len(*b)+len(s) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, s...) -} - -func (b *buffer) WriteSingleByte(c byte) { - if len(*b) >= tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, c) -} - -func (b *buffer) WriteRune(r rune) { - if len(*b)+utf8.RuneLen(r) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - if r < utf8.RuneSelf { - *b = append(*b, byte(r)) - return - } - - b2 := *b - n := len(b2) - for n+utf8.UTFMax > cap(b2) { - b2 = append(b2, 0) - } - w := utf8.EncodeRune(b2[n:n+utf8.UTFMax], r) - *b = b2[:n+w] -} - -// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations. -type pp struct { - buf buffer - - // arg holds the current item. - arg Object - - // fmt is used to format basic items such as integers or strings. - fmt formatter - - // reordered records whether the format string used argument reordering. - reordered bool - - // goodArgNum records whether the most recent reordering directive was valid. - goodArgNum bool - - // erroring is set when printing an error string to guard against calling handleMethods. - erroring bool -} - -var ppFree = sync.Pool{ - New: func() interface{} { return new(pp) }, -} - -// newPrinter allocates a new pp struct or grabs a cached one. -func newPrinter() *pp { - p := ppFree.Get().(*pp) - p.erroring = false - p.fmt.init(&p.buf) - return p -} - -// free saves used pp structs in ppFree; avoids an allocation per invocation. -func (p *pp) free() { - // Proper usage of a sync.Pool requires each entry to have approximately - // the same memory cost. To obtain this property when the stored type - // contains a variably-sized buffer, we add a hard limit on the maximum buffer - // to place back in the pool. - // - // See https://golang.org/issue/23199 - if cap(p.buf) > 64<<10 { - return - } - - p.buf = p.buf[:0] - p.arg = nil - ppFree.Put(p) -} - -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } - -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } - -func (p *pp) Flag(b int) bool { - switch b { - case '-': - return p.fmt.minus - case '+': - return p.fmt.plus || p.fmt.plusV - case '#': - return p.fmt.sharp || p.fmt.sharpV - case ' ': - return p.fmt.space - case '0': - return p.fmt.zero - } - return false -} - -// Implement Write so we can call Fprintf on a pp (through State), for -// recursive use in custom verbs. -func (p *pp) Write(b []byte) (ret int, err error) { - p.buf.Write(b) - return len(b), nil -} - -// Implement WriteString so that we can call io.WriteString -// on a pp (through state), for efficiency. -func (p *pp) WriteString(s string) (ret int, err error) { - p.buf.WriteString(s) - return len(s), nil -} - -func (p *pp) WriteRune(r rune) (ret int, err error) { - p.buf.WriteRune(r) - return utf8.RuneLen(r), nil -} - -func (p *pp) WriteSingleByte(c byte) (ret int, err error) { - p.buf.WriteSingleByte(c) - return 1, nil -} - -// tooLarge reports whether the magnitude of the integer is -// too large to be used as a formatting width or precision. -func tooLarge(x int) bool { - const max int = 1e6 - return x > max || x < -max -} - -// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. -func parsenum(s string, start, end int) (num int, isnum bool, newi int) { - if start >= end { - return 0, false, end - } - for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { - if tooLarge(num) { - return 0, false, end // Overflow; crazy long number most likely. - } - num = num*10 + int(s[newi]-'0') - isnum = true - } - return -} - -func (p *pp) badVerb(verb rune) { - p.erroring = true - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteSingleByte('(') - switch { - case p.arg != nil: - _, _ = p.WriteString(p.arg.String()) - _, _ = p.WriteSingleByte('=') - p.printArg(p.arg, 'v') - default: - _, _ = p.WriteString(UndefinedValue.String()) - } - _, _ = p.WriteSingleByte(')') - p.erroring = false -} - -func (p *pp) fmtBool(v bool, verb rune) { - switch verb { - case 't', 'v': - p.fmt.fmtBoolean(v) - default: - p.badVerb(verb) - } -} - -// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or -// not, as requested, by temporarily setting the sharp flag. -func (p *pp) fmt0x64(v uint64, leading0x bool) { - sharp := p.fmt.sharp - p.fmt.sharp = leading0x - p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits) - p.fmt.sharp = sharp -} - -// fmtInteger formats a signed or unsigned integer. -func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV && !isSigned { - p.fmt0x64(v, true) - } else { - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - } - case 'd': - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - case 'b': - p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits) - case 'o', 'O': - p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits) - case 'x': - p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits) - case 'X': - p.fmt.fmtInteger(v, 16, isSigned, verb, udigits) - case 'c': - p.fmt.fmtC(v) - case 'q': - if v <= utf8.MaxRune { - p.fmt.fmtQc(v) - } else { - p.badVerb(verb) - } - case 'U': - p.fmt.fmtUnicode(v) - default: - p.badVerb(verb) - } -} - -// fmtFloat formats a float. The default precision for each verb -// is specified as last argument in the call to fmt_float. -func (p *pp) fmtFloat(v float64, size int, verb rune) { - switch verb { - case 'v': - p.fmt.fmtFloat(v, size, 'g', -1) - case 'b', 'g', 'G', 'x', 'X': - p.fmt.fmtFloat(v, size, verb, -1) - case 'f', 'e', 'E': - p.fmt.fmtFloat(v, size, verb, 6) - case 'F': - p.fmt.fmtFloat(v, size, 'f', 6) - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtString(v string, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV { - p.fmt.fmtQ(v) - } else { - p.fmt.fmtS(v) - } - case 's': - p.fmt.fmtS(v) - case 'x': - p.fmt.fmtSx(v, ldigits) - case 'X': - p.fmt.fmtSx(v, udigits) - case 'q': - p.fmt.fmtQ(v) - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { - switch verb { - case 'v', 'd': - if p.fmt.sharpV { - _, _ = p.WriteString(typeString) - if v == nil { - _, _ = p.WriteString(nilParenString) - return - } - _, _ = p.WriteSingleByte('{') - for i, c := range v { - if i > 0 { - _, _ = p.WriteString(commaSpaceString) - } - p.fmt0x64(uint64(c), true) - } - _, _ = p.WriteSingleByte('}') - } else { - _, _ = p.WriteSingleByte('[') - for i, c := range v { - if i > 0 { - _, _ = p.WriteSingleByte(' ') - } - p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits) - } - _, _ = p.WriteSingleByte(']') - } - case 's': - p.fmt.fmtBs(v) - case 'x': - p.fmt.fmtBx(v, ldigits) - case 'X': - p.fmt.fmtBx(v, udigits) - case 'q': - p.fmt.fmtQ(string(v)) - } -} - -func (p *pp) printArg(arg Object, verb rune) { - p.arg = arg - - if arg == nil { - arg = UndefinedValue - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.fmt.fmtS(arg.TypeName()) - return - case 'v': - p.fmt.fmtS(arg.String()) - return - } - - // Some types can be done without reflection. - switch f := arg.(type) { - case *Bool: - p.fmtBool(!f.IsFalsy(), verb) - case *Float: - p.fmtFloat(f.Value, 64, verb) - case *Int: - p.fmtInteger(uint64(f.Value), signed, verb) - case *String: - p.fmtString(f.Value, verb) - case *Bytes: - p.fmtBytes(f.Value, verb, "[]byte") - default: - p.fmtString(f.String(), verb) - } -} - -// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type. -func intFromArg(a []Object, argNum int) (num int, isInt bool, newArgNum int) { - newArgNum = argNum - if argNum < len(a) { - var num64 int64 - num64, isInt = ToInt64(a[argNum]) - num = int(num64) - newArgNum = argNum + 1 - if tooLarge(num) { - num = 0 - isInt = false - } - } - return -} - -// parseArgNumber returns the value of the bracketed number, minus 1 -// (explicit argument numbers are one-indexed but we want zero-indexed). -// The opening bracket is known to be present at format[0]. -// The returned values are the index, the number of bytes to consume -// up to the closing paren, if present, and whether the number parsed -// ok. The bytes to consume will be 1 if no closing paren is present. -func parseArgNumber(format string) (index int, wid int, ok bool) { - // There must be at least 3 bytes: [n]. - if len(format) < 3 { - return 0, 1, false - } - - // Find closing bracket. - for i := 1; i < len(format); i++ { - if format[i] == ']' { - width, ok, newi := parsenum(format, 1, i) - if !ok || newi != i { - return 0, i + 1, false - } - return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. - } - } - return 0, 1, false -} - -// argNumber returns the next argument to evaluate, which is either the value of the passed-in -// argNum or the value of the bracketed integer that begins format[i:]. It also returns -// the new value of i, that is, the index of the next byte of the format to process. -func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { - if len(format) <= i || format[i] != '[' { - return argNum, i, false - } - p.reordered = true - index, wid, ok := parseArgNumber(format[i:]) - if ok && 0 <= index && index < numArgs { - return index, i + wid, true - } - p.goodArgNum = false - return argNum, i + wid, ok -} - -func (p *pp) badArgNum(verb rune) { - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteString(badIndexString) -} - -func (p *pp) missingArg(verb rune) { - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteString(missingString) -} - -func (p *pp) doFormat(format string, a []Object) (err error) { - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok && e == ErrStringLimit { - err = e - return - } - panic(r) - } - }() - - end := len(format) - argNum := 0 // we process one argument per non-trivial format - afterIndex := false // previous item in format was an index like [3]. - p.reordered = false -formatLoop: - for i := 0; i < end; { - p.goodArgNum = true - lasti := i - for i < end && format[i] != '%' { - i++ - } - if i > lasti { - _, _ = p.WriteString(format[lasti:i]) - } - if i >= end { - // done processing format string - break - } - - // Process one verb - i++ - - // Do we have flags? - p.fmt.clearflags() - simpleFormat: - for ; i < end; i++ { - c := format[i] - switch c { - case '#': - p.fmt.sharp = true - case '0': - p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left. - case '+': - p.fmt.plus = true - case '-': - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - case ' ': - p.fmt.space = true - default: - // Fast path for common case of ascii lower case simple verbs - // without precision or width or argument indices. - if 'a' <= c && c <= 'z' && argNum < len(a) { - if c == 'v' { - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - } - p.printArg(a[argNum], rune(c)) - argNum++ - i++ - continue formatLoop - } - // Format is more complex than simple flags and a verb or is malformed. - break simpleFormat - } - } - - // Do we have an explicit argument index? - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - - // Do we have width? - if i < end && format[i] == '*' { - i++ - p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) - - if !p.fmt.widPresent { - _, _ = p.WriteString(badWidthString) - } - - // We have a negative width, so take its value and ensure - // that the minus flag is set - if p.fmt.wid < 0 { - p.fmt.wid = -p.fmt.wid - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - } - afterIndex = false - } else { - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - if afterIndex && p.fmt.widPresent { // "%[3]2d" - p.goodArgNum = false - } - } - - // Do we have precision? - if i+1 < end && format[i] == '.' { - i++ - if afterIndex { // "%[3].2d" - p.goodArgNum = false - } - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - if i < end && format[i] == '*' { - i++ - p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) - // Negative precision arguments don't make sense - if p.fmt.prec < 0 { - p.fmt.prec = 0 - p.fmt.precPresent = false - } - if !p.fmt.precPresent { - _, _ = p.WriteString(badPrecString) - } - afterIndex = false - } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) - if !p.fmt.precPresent { - p.fmt.prec = 0 - p.fmt.precPresent = true - } - } - } - - if !afterIndex { - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - } - - if i >= end { - _, _ = p.WriteString(noVerbString) - break - } - - verb, size := rune(format[i]), 1 - if verb >= utf8.RuneSelf { - verb, size = utf8.DecodeRuneInString(format[i:]) - } - i += size - - switch { - case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec. - _, _ = p.WriteSingleByte('%') - case !p.goodArgNum: - p.badArgNum(verb) - case argNum >= len(a): // No argument left over to print for the current verb. - p.missingArg(verb) - case verb == 'v': - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - fallthrough - default: - p.printArg(a[argNum], verb) - argNum++ - } - } - - // Check for extra arguments unless the call accessed the arguments - // out of order, in which case it's too expensive to detect if they've all - // been used and arguably OK if they're not. - if !p.reordered && argNum < len(a) { - p.fmt.clearflags() - _, _ = p.WriteString(extraString) - for i, arg := range a[argNum:] { - if i > 0 { - _, _ = p.WriteString(commaSpaceString) - } - if arg == nil { - _, _ = p.WriteString(UndefinedValue.String()) - } else { - _, _ = p.WriteString(arg.TypeName()) - _, _ = p.WriteSingleByte('=') - p.printArg(arg, 'v') - } - } - _, _ = p.WriteSingleByte(')') - } - - return nil -} - -// Format formats according to a format specifier and returns the resulting string. -func Format(format string, a ...Object) (string, error) { - p := newPrinter() - err := p.doFormat(format, a) - s := string(p.buf) - p.free() - - return s, err -} |