diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
commit | f6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch) | |
tree | 7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/fmt | |
parent | Initial commit. (diff) | |
download | golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip |
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fmt')
-rw-r--r-- | src/fmt/doc.go | 384 | ||||
-rw-r--r-- | src/fmt/errors.go | 78 | ||||
-rw-r--r-- | src/fmt/errors_test.go | 107 | ||||
-rw-r--r-- | src/fmt/example_test.go | 365 | ||||
-rw-r--r-- | src/fmt/export_test.go | 8 | ||||
-rw-r--r-- | src/fmt/fmt_test.go | 1948 | ||||
-rw-r--r-- | src/fmt/format.go | 594 | ||||
-rw-r--r-- | src/fmt/gostringer_example_test.go | 59 | ||||
-rw-r--r-- | src/fmt/print.go | 1224 | ||||
-rw-r--r-- | src/fmt/scan.go | 1238 | ||||
-rw-r--r-- | src/fmt/scan_test.go | 1334 | ||||
-rw-r--r-- | src/fmt/state_test.go | 80 | ||||
-rw-r--r-- | src/fmt/stringer_example_test.go | 29 | ||||
-rw-r--r-- | src/fmt/stringer_test.go | 61 |
14 files changed, 7509 insertions, 0 deletions
diff --git a/src/fmt/doc.go b/src/fmt/doc.go new file mode 100644 index 0000000..1cda484 --- /dev/null +++ b/src/fmt/doc.go @@ -0,0 +1,384 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package fmt implements formatted I/O with functions analogous +to C's printf and scanf. The format 'verbs' are derived from C's but +are simpler. + +# Printing + +The verbs: + +General: + + %v the value in a default format + when printing structs, the plus flag (%+v) adds field names + %#v a Go-syntax representation of the value + %T a Go-syntax representation of the type of the value + %% a literal percent sign; consumes no value + +Boolean: + + %t the word true or false + +Integer: + + %b base 2 + %c the character represented by the corresponding Unicode code point + %d base 10 + %o base 8 + %O base 8 with 0o prefix + %q a single-quoted character literal safely escaped with Go syntax. + %x base 16, with lower-case letters for a-f + %X base 16, with upper-case letters for A-F + %U Unicode format: U+1234; same as "U+%04X" + +Floating-point and complex constituents: + + %b decimalless scientific notation with exponent a power of two, + in the manner of strconv.FormatFloat with the 'b' format, + e.g. -123456p-78 + %e scientific notation, e.g. -1.234456e+78 + %E scientific notation, e.g. -1.234456E+78 + %f decimal point but no exponent, e.g. 123.456 + %F synonym for %f + %g %e for large exponents, %f otherwise. Precision is discussed below. + %G %E for large exponents, %F otherwise + %x hexadecimal notation (with decimal power of two exponent), e.g. -0x1.23abcp+20 + %X upper-case hexadecimal notation, e.g. -0X1.23ABCP+20 + +String and slice of bytes (treated equivalently with these verbs): + + %s the uninterpreted bytes of the string or slice + %q a double-quoted string safely escaped with Go syntax + %x base 16, lower-case, two characters per byte + %X base 16, upper-case, two characters per byte + +Slice: + + %p address of 0th element in base 16 notation, with leading 0x + +Pointer: + + %p base 16 notation, with leading 0x + The %b, %d, %o, %x and %X verbs also work with pointers, + formatting the value exactly as if it were an integer. + +The default format for %v is: + + bool: %t + int, int8 etc.: %d + uint, uint8 etc.: %d, %#x if printed with %#v + float32, complex64, etc: %g + string: %s + chan: %p + pointer: %p + +For compound objects, the elements are printed using these rules, recursively, +laid out like this: + + struct: {field0 field1 ...} + array, slice: [elem0 elem1 ...] + maps: map[key1:value1 key2:value2 ...] + pointer to above: &{}, &[], &map[] + +Width is specified by an optional decimal number immediately preceding the verb. +If absent, the width is whatever is necessary to represent the value. +Precision is specified after the (optional) width by a period followed by a +decimal number. If no period is present, a default precision is used. +A period with no following number specifies a precision of zero. +Examples: + + %f default width, default precision + %9f width 9, default precision + %.2f default width, precision 2 + %9.2f width 9, precision 2 + %9.f width 9, precision 0 + +Width and precision are measured in units of Unicode code points, +that is, runes. (This differs from C's printf where the +units are always measured in bytes.) Either or both of the flags +may be replaced with the character '*', causing their values to be +obtained from the next operand (preceding the one to format), +which must be of type int. + +For most values, width is the minimum number of runes to output, +padding the formatted form with spaces if necessary. + +For strings, byte slices and byte arrays, however, precision +limits the length of the input to be formatted (not the size of +the output), truncating if necessary. Normally it is measured in +runes, but for these types when formatted with the %x or %X format +it is measured in bytes. + +For floating-point values, width sets the minimum width of the field and +precision sets the number of places after the decimal, if appropriate, +except that for %g/%G precision sets the maximum number of significant +digits (trailing zeros are removed). For example, given 12.345 the format +%6.3f prints 12.345 while %.3g prints 12.3. The default precision for %e, %f +and %#g is 6; for %g it is the smallest number of digits necessary to identify +the value uniquely. + +For complex numbers, the width and precision apply to the two +components independently and the result is parenthesized, so %f applied +to 1.2+3.4i produces (1.200000+3.400000i). + +When formatting a single integer code point or a rune string (type []rune) +with %q, invalid Unicode code points are changed to the Unicode replacement +character, U+FFFD, as in strconv.QuoteRune. + +Other flags: + + '+' always print a sign for numeric values; + guarantee ASCII-only output for %q (%+q) + '-' pad with spaces on the right rather than the left (left-justify the field) + '#' alternate format: add leading 0b for binary (%#b), 0 for octal (%#o), + 0x or 0X for hex (%#x or %#X); suppress 0x for %p (%#p); + for %q, print a raw (backquoted) string if strconv.CanBackquote + returns true; + always print a decimal point for %e, %E, %f, %F, %g and %G; + do not remove trailing zeros for %g and %G; + write e.g. U+0078 'x' if the character is printable for %U (%#U). + ' ' (space) leave a space for elided sign in numbers (% d); + put spaces between bytes printing strings or slices in hex (% x, % X) + '0' pad with leading zeros rather than spaces; + for numbers, this moves the padding after the sign; + ignored for strings, byte slices and byte arrays + +Flags are ignored by verbs that do not expect them. +For example there is no alternate decimal format, so %#d and %d +behave identically. + +For each Printf-like function, there is also a Print function +that takes no format and is equivalent to saying %v for every +operand. Another variant Println inserts blanks between +operands and appends a newline. + +Regardless of the verb, if an operand is an interface value, +the internal concrete value is used, not the interface itself. +Thus: + + var i interface{} = 23 + fmt.Printf("%v\n", i) + +will print 23. + +Except when printed using the verbs %T and %p, special +formatting considerations apply for operands that implement +certain interfaces. In order of application: + +1. If the operand is a reflect.Value, the operand is replaced by the +concrete value that it holds, and printing continues with the next rule. + +2. If an operand implements the Formatter interface, it will +be invoked. In this case the interpretation of verbs and flags is +controlled by that implementation. + +3. If the %v verb is used with the # flag (%#v) and the operand +implements the GoStringer interface, that will be invoked. + +If the format (which is implicitly %v for Println etc.) is valid +for a string (%s %q %x %X), or is %v but not %#v, +the following two rules apply: + +4. If an operand implements the error interface, the Error method +will be invoked to convert the object to a string, which will then +be formatted as required by the verb (if any). + +5. If an operand implements method String() string, that method +will be invoked to convert the object to a string, which will then +be formatted as required by the verb (if any). + +For compound operands such as slices and structs, the format +applies to the elements of each operand, recursively, not to the +operand as a whole. Thus %q will quote each element of a slice +of strings, and %6.2f will control formatting for each element +of a floating-point array. + +However, when printing a byte slice with a string-like verb +(%s %q %x %X), it is treated identically to a string, as a single item. + +To avoid recursion in cases such as + + type X string + func (x X) String() string { return Sprintf("<%s>", x) } + +convert the value before recurring: + + func (x X) String() string { return Sprintf("<%s>", string(x)) } + +Infinite recursion can also be triggered by self-referential data +structures, such as a slice that contains itself as an element, if +that type has a String method. Such pathologies are rare, however, +and the package does not protect against them. + +When printing a struct, fmt cannot and therefore does not invoke +formatting methods such as Error or String on unexported fields. + +# Explicit argument indexes + +In Printf, Sprintf, and Fprintf, the default behavior is for each +formatting verb to format successive arguments passed in the call. +However, the notation [n] immediately before the verb indicates that the +nth one-indexed argument is to be formatted instead. The same notation +before a '*' for a width or precision selects the argument index holding +the value. After processing a bracketed expression [n], subsequent verbs +will use arguments n+1, n+2, etc. unless otherwise directed. + +For example, + + fmt.Sprintf("%[2]d %[1]d\n", 11, 22) + +will yield "22 11", while + + fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6) + +equivalent to + + fmt.Sprintf("%6.2f", 12.0) + +will yield " 12.00". Because an explicit index affects subsequent verbs, +this notation can be used to print the same values multiple times +by resetting the index for the first argument to be repeated: + + fmt.Sprintf("%d %d %#[1]x %#x", 16, 17) + +will yield "16 17 0x10 0x11". + +# Format errors + +If an invalid argument is given for a verb, such as providing +a string to %d, the generated string will contain a +description of the problem, as in these examples: + + Wrong type or unknown verb: %!verb(type=value) + Printf("%d", "hi"): %!d(string=hi) + Too many arguments: %!(EXTRA type=value) + Printf("hi", "guys"): hi%!(EXTRA string=guys) + Too few arguments: %!verb(MISSING) + Printf("hi%d"): hi%!d(MISSING) + Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) + Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi + Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi + Invalid or invalid use of argument index: %!(BADINDEX) + Printf("%*[2]d", 7): %!d(BADINDEX) + Printf("%.[2]d", 7): %!d(BADINDEX) + +All errors begin with the string "%!" followed sometimes +by a single character (the verb) and end with a parenthesized +description. + +If an Error or String method triggers a panic when called by a +print routine, the fmt package reformats the error message +from the panic, decorating it with an indication that it came +through the fmt package. For example, if a String method +calls panic("bad"), the resulting formatted message will look +like + + %!s(PANIC=bad) + +The %!s just shows the print verb in use when the failure +occurred. If the panic is caused by a nil receiver to an Error +or String method, however, the output is the undecorated +string, "<nil>". + +# Scanning + +An analogous set of functions scans formatted text to yield +values. Scan, Scanf and Scanln read from os.Stdin; Fscan, +Fscanf and Fscanln read from a specified io.Reader; Sscan, +Sscanf and Sscanln read from an argument string. + +Scan, Fscan, Sscan treat newlines in the input as spaces. + +Scanln, Fscanln and Sscanln stop scanning at a newline and +require that the items be followed by a newline or EOF. + +Scanf, Fscanf, and Sscanf parse the arguments according to a +format string, analogous to that of Printf. In the text that +follows, 'space' means any Unicode whitespace character +except newline. + +In the format string, a verb introduced by the % character +consumes and parses input; these verbs are described in more +detail below. A character other than %, space, or newline in +the format consumes exactly that input character, which must +be present. A newline with zero or more spaces before it in +the format string consumes zero or more spaces in the input +followed by a single newline or the end of the input. A space +following a newline in the format string consumes zero or more +spaces in the input. Otherwise, any run of one or more spaces +in the format string consumes as many spaces as possible in +the input. Unless the run of spaces in the format string +appears adjacent to a newline, the run must consume at least +one space from the input or find the end of the input. + +The handling of spaces and newlines differs from that of C's +scanf family: in C, newlines are treated as any other space, +and it is never an error when a run of spaces in the format +string finds no spaces to consume in the input. + +The verbs behave analogously to those of Printf. +For example, %x will scan an integer as a hexadecimal number, +and %v will scan the default representation format for the value. +The Printf verbs %p and %T and the flags # and + are not implemented. +For floating-point and complex values, all valid formatting verbs +(%b %e %E %f %F %g %G %x %X and %v) are equivalent and accept +both decimal and hexadecimal notation (for example: "2.3e+7", "0x4.5p-8") +and digit-separating underscores (for example: "3.14159_26535_89793"). + +Input processed by verbs is implicitly space-delimited: the +implementation of every verb except %c starts by discarding +leading spaces from the remaining input, and the %s verb +(and %v reading into a string) stops consuming input at the first +space or newline character. + +The familiar base-setting prefixes 0b (binary), 0o and 0 (octal), +and 0x (hexadecimal) are accepted when scanning integers +without a format or with the %v verb, as are digit-separating +underscores. + +Width is interpreted in the input text but there is no +syntax for scanning with a precision (no %5.2f, just %5f). +If width is provided, it applies after leading spaces are +trimmed and specifies the maximum number of runes to read +to satisfy the verb. For example, + + Sscanf(" 1234567 ", "%5s%d", &s, &i) + +will set s to "12345" and i to 67 while + + Sscanf(" 12 34 567 ", "%5s%d", &s, &i) + +will set s to "12" and i to 34. + +In all the scanning functions, a carriage return followed +immediately by a newline is treated as a plain newline +(\r\n means the same as \n). + +In all the scanning functions, if an operand implements method +Scan (that is, it implements the Scanner interface) that +method will be used to scan the text for that operand. Also, +if the number of arguments scanned is less than the number of +arguments provided, an error is returned. + +All arguments to be scanned must be either pointers to basic +types or implementations of the Scanner interface. + +Like Scanf and Fscanf, Sscanf need not consume its entire input. +There is no way to recover how much of the input string Sscanf used. + +Note: Fscan etc. can read one character (rune) past the input +they return, which means that a loop calling a scan routine +may skip some of the input. This is usually a problem only +when there is no space between input values. If the reader +provided to Fscan implements ReadRune, that method will be used +to read characters. If the reader also implements UnreadRune, +that method will be used to save the character and successive +calls will not lose data. To attach ReadRune and UnreadRune +methods to a reader without that capability, use +bufio.NewReader. +*/ +package fmt diff --git a/src/fmt/errors.go b/src/fmt/errors.go new file mode 100644 index 0000000..1fbd39f --- /dev/null +++ b/src/fmt/errors.go @@ -0,0 +1,78 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +import ( + "errors" + "sort" +) + +// Errorf formats according to a format specifier and returns the string as a +// value that satisfies error. +// +// If the format specifier includes a %w verb with an error operand, +// the returned error will implement an Unwrap method returning the operand. +// If there is more than one %w verb, the returned error will implement an +// Unwrap method returning a []error containing all the %w operands in the +// order they appear in the arguments. +// It is invalid to supply the %w verb with an operand that does not implement +// the error interface. The %w verb is otherwise a synonym for %v. +func Errorf(format string, a ...any) error { + p := newPrinter() + p.wrapErrs = true + p.doPrintf(format, a) + s := string(p.buf) + var err error + switch len(p.wrappedErrs) { + case 0: + err = errors.New(s) + case 1: + w := &wrapError{msg: s} + w.err, _ = a[p.wrappedErrs[0]].(error) + err = w + default: + if p.reordered { + sort.Ints(p.wrappedErrs) + } + var errs []error + for i, argNum := range p.wrappedErrs { + if i > 0 && p.wrappedErrs[i-1] == argNum { + continue + } + if e, ok := a[argNum].(error); ok { + errs = append(errs, e) + } + } + err = &wrapErrors{s, errs} + } + p.free() + return err +} + +type wrapError struct { + msg string + err error +} + +func (e *wrapError) Error() string { + return e.msg +} + +func (e *wrapError) Unwrap() error { + return e.err +} + +type wrapErrors struct { + msg string + errs []error +} + +func (e *wrapErrors) Error() string { + return e.msg +} + +func (e *wrapErrors) Unwrap() []error { + return e.errs +} diff --git a/src/fmt/errors_test.go b/src/fmt/errors_test.go new file mode 100644 index 0000000..4eb55fa --- /dev/null +++ b/src/fmt/errors_test.go @@ -0,0 +1,107 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "errors" + "fmt" + "reflect" + "testing" +) + +func TestErrorf(t *testing.T) { + // noVetErrorf is an alias for fmt.Errorf that does not trigger vet warnings for + // %w format strings. + noVetErrorf := fmt.Errorf + + wrapped := errors.New("inner error") + for _, test := range []struct { + err error + wantText string + wantUnwrap error + wantSplit []error + }{{ + err: fmt.Errorf("%w", wrapped), + wantText: "inner error", + wantUnwrap: wrapped, + }, { + err: fmt.Errorf("added context: %w", wrapped), + wantText: "added context: inner error", + wantUnwrap: wrapped, + }, { + err: fmt.Errorf("%w with added context", wrapped), + wantText: "inner error with added context", + wantUnwrap: wrapped, + }, { + err: fmt.Errorf("%s %w %v", "prefix", wrapped, "suffix"), + wantText: "prefix inner error suffix", + wantUnwrap: wrapped, + }, { + err: fmt.Errorf("%[2]s: %[1]w", wrapped, "positional verb"), + wantText: "positional verb: inner error", + wantUnwrap: wrapped, + }, { + err: fmt.Errorf("%v", wrapped), + wantText: "inner error", + }, { + err: fmt.Errorf("added context: %v", wrapped), + wantText: "added context: inner error", + }, { + err: fmt.Errorf("%v with added context", wrapped), + wantText: "inner error with added context", + }, { + err: noVetErrorf("%w is not an error", "not-an-error"), + wantText: "%!w(string=not-an-error) is not an error", + }, { + err: noVetErrorf("wrapped two errors: %w %w", errString("1"), errString("2")), + wantText: "wrapped two errors: 1 2", + wantSplit: []error{errString("1"), errString("2")}, + }, { + err: noVetErrorf("wrapped three errors: %w %w %w", errString("1"), errString("2"), errString("3")), + wantText: "wrapped three errors: 1 2 3", + wantSplit: []error{errString("1"), errString("2"), errString("3")}, + }, { + err: noVetErrorf("wrapped nil error: %w %w %w", errString("1"), nil, errString("2")), + wantText: "wrapped nil error: 1 %!w(<nil>) 2", + wantSplit: []error{errString("1"), errString("2")}, + }, { + err: noVetErrorf("wrapped one non-error: %w %w %w", errString("1"), "not-an-error", errString("3")), + wantText: "wrapped one non-error: 1 %!w(string=not-an-error) 3", + wantSplit: []error{errString("1"), errString("3")}, + }, { + err: fmt.Errorf("wrapped errors out of order: %[3]w %[2]w %[1]w", errString("1"), errString("2"), errString("3")), + wantText: "wrapped errors out of order: 3 2 1", + wantSplit: []error{errString("1"), errString("2"), errString("3")}, + }, { + err: fmt.Errorf("wrapped several times: %[1]w %[1]w %[2]w %[1]w", errString("1"), errString("2")), + wantText: "wrapped several times: 1 1 2 1", + wantSplit: []error{errString("1"), errString("2")}, + }, { + err: fmt.Errorf("%w", nil), + wantText: "%!w(<nil>)", + wantUnwrap: nil, // still nil + }} { + if got, want := errors.Unwrap(test.err), test.wantUnwrap; got != want { + t.Errorf("Formatted error: %v\nerrors.Unwrap() = %v, want %v", test.err, got, want) + } + if got, want := splitErr(test.err), test.wantSplit; !reflect.DeepEqual(got, want) { + t.Errorf("Formatted error: %v\nUnwrap() []error = %v, want %v", test.err, got, want) + } + if got, want := test.err.Error(), test.wantText; got != want { + t.Errorf("err.Error() = %q, want %q", got, want) + } + } +} + +func splitErr(err error) []error { + if e, ok := err.(interface{ Unwrap() []error }); ok { + return e.Unwrap() + } + return nil +} + +type errString string + +func (e errString) Error() string { return string(e) } diff --git a/src/fmt/example_test.go b/src/fmt/example_test.go new file mode 100644 index 0000000..5962834 --- /dev/null +++ b/src/fmt/example_test.go @@ -0,0 +1,365 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "fmt" + "io" + "math" + "os" + "strings" + "time" +) + +// The Errorf function lets us use formatting features +// to create descriptive error messages. +func ExampleErrorf() { + const name, id = "bueller", 17 + err := fmt.Errorf("user %q (id %d) not found", name, id) + fmt.Println(err.Error()) + + // Output: user "bueller" (id 17) not found +} + +func ExampleFscanf() { + var ( + i int + b bool + s string + ) + r := strings.NewReader("5 true gophers") + n, err := fmt.Fscanf(r, "%d %t %s", &i, &b, &s) + if err != nil { + fmt.Fprintf(os.Stderr, "Fscanf: %v\n", err) + } + fmt.Println(i, b, s) + fmt.Println(n) + // Output: + // 5 true gophers + // 3 +} + +func ExampleFscanln() { + s := `dmr 1771 1.61803398875 + ken 271828 3.14159` + r := strings.NewReader(s) + var a string + var b int + var c float64 + for { + n, err := fmt.Fscanln(r, &a, &b, &c) + if err == io.EOF { + break + } + if err != nil { + panic(err) + } + fmt.Printf("%d: %s, %d, %f\n", n, a, b, c) + } + // Output: + // 3: dmr, 1771, 1.618034 + // 3: ken, 271828, 3.141590 +} + +func ExampleSscanf() { + var name string + var age int + n, err := fmt.Sscanf("Kim is 22 years old", "%s is %d years old", &name, &age) + if err != nil { + panic(err) + } + fmt.Printf("%d: %s, %d\n", n, name, age) + + // Output: + // 2: Kim, 22 +} + +func ExamplePrint() { + const name, age = "Kim", 22 + fmt.Print(name, " is ", age, " years old.\n") + + // It is conventional not to worry about any + // error returned by Print. + + // Output: + // Kim is 22 years old. +} + +func ExamplePrintln() { + const name, age = "Kim", 22 + fmt.Println(name, "is", age, "years old.") + + // It is conventional not to worry about any + // error returned by Println. + + // Output: + // Kim is 22 years old. +} + +func ExamplePrintf() { + const name, age = "Kim", 22 + fmt.Printf("%s is %d years old.\n", name, age) + + // It is conventional not to worry about any + // error returned by Printf. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprint() { + const name, age = "Kim", 22 + s := fmt.Sprint(name, " is ", age, " years old.\n") + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprintln() { + const name, age = "Kim", 22 + s := fmt.Sprintln(name, "is", age, "years old.") + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. +} + +func ExampleSprintf() { + const name, age = "Kim", 22 + s := fmt.Sprintf("%s is %d years old.\n", name, age) + + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + + // Output: + // Kim is 22 years old. +} + +func ExampleFprint() { + const name, age = "Kim", 22 + n, err := fmt.Fprint(os.Stdout, name, " is ", age, " years old.\n") + + // The n and err return values from Fprint are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprint: %v\n", err) + } + fmt.Print(n, " bytes written.\n") + + // Output: + // Kim is 22 years old. + // 21 bytes written. +} + +func ExampleFprintln() { + const name, age = "Kim", 22 + n, err := fmt.Fprintln(os.Stdout, name, "is", age, "years old.") + + // The n and err return values from Fprintln are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprintln: %v\n", err) + } + fmt.Println(n, "bytes written.") + + // Output: + // Kim is 22 years old. + // 21 bytes written. +} + +func ExampleFprintf() { + const name, age = "Kim", 22 + n, err := fmt.Fprintf(os.Stdout, "%s is %d years old.\n", name, age) + + // The n and err return values from Fprintf are + // those returned by the underlying io.Writer. + if err != nil { + fmt.Fprintf(os.Stderr, "Fprintf: %v\n", err) + } + fmt.Printf("%d bytes written.\n", n) + + // Output: + // Kim is 22 years old. + // 21 bytes written. +} + +// Print, Println, and Printf lay out their arguments differently. In this example +// we can compare their behaviors. Println always adds blanks between the items it +// prints, while Print adds blanks only between non-string arguments and Printf +// does exactly what it is told. +// Sprint, Sprintln, Sprintf, Fprint, Fprintln, and Fprintf behave the same as +// their corresponding Print, Println, and Printf functions shown here. +func Example_printers() { + a, b := 3.0, 4.0 + h := math.Hypot(a, b) + + // Print inserts blanks between arguments when neither is a string. + // It does not add a newline to the output, so we add one explicitly. + fmt.Print("The vector (", a, b, ") has length ", h, ".\n") + + // Println always inserts spaces between its arguments, + // so it cannot be used to produce the same output as Print in this case; + // its output has extra spaces. + // Also, Println always adds a newline to the output. + fmt.Println("The vector (", a, b, ") has length", h, ".") + + // Printf provides complete control but is more complex to use. + // It does not add a newline to the output, so we add one explicitly + // at the end of the format specifier string. + fmt.Printf("The vector (%g %g) has length %g.\n", a, b, h) + + // Output: + // The vector (3 4) has length 5. + // The vector ( 3 4 ) has length 5 . + // The vector (3 4) has length 5. +} + +// These examples demonstrate the basics of printing using a format string. Printf, +// Sprintf, and Fprintf all take a format string that specifies how to format the +// subsequent arguments. For example, %d (we call that a 'verb') says to print the +// corresponding argument, which must be an integer (or something containing an +// integer, such as a slice of ints) in decimal. The verb %v ('v' for 'value') +// always formats the argument in its default form, just how Print or Println would +// show it. The special verb %T ('T' for 'Type') prints the type of the argument +// rather than its value. The examples are not exhaustive; see the package comment +// for all the details. +func Example_formats() { + // A basic set of examples showing that %v is the default format, in this + // case decimal for integers, which can be explicitly requested with %d; + // the output is just what Println generates. + integer := 23 + // Each of these prints "23" (without the quotes). + fmt.Println(integer) + fmt.Printf("%v\n", integer) + fmt.Printf("%d\n", integer) + + // The special verb %T shows the type of an item rather than its value. + fmt.Printf("%T %T\n", integer, &integer) + // Result: int *int + + // Println(x) is the same as Printf("%v\n", x) so we will use only Printf + // in the following examples. Each one demonstrates how to format values of + // a particular type, such as integers or strings. We start each format + // string with %v to show the default output and follow that with one or + // more custom formats. + + // Booleans print as "true" or "false" with %v or %t. + truth := true + fmt.Printf("%v %t\n", truth, truth) + // Result: true true + + // Integers print as decimals with %v and %d, + // or in hex with %x, octal with %o, or binary with %b. + answer := 42 + fmt.Printf("%v %d %x %o %b\n", answer, answer, answer, answer, answer) + // Result: 42 42 2a 52 101010 + + // Floats have multiple formats: %v and %g print a compact representation, + // while %f prints a decimal point and %e uses exponential notation. The + // format %6.2f used here shows how to set the width and precision to + // control the appearance of a floating-point value. In this instance, 6 is + // the total width of the printed text for the value (note the extra spaces + // in the output) and 2 is the number of decimal places to show. + pi := math.Pi + fmt.Printf("%v %g %.2f (%6.2f) %e\n", pi, pi, pi, pi, pi) + // Result: 3.141592653589793 3.141592653589793 3.14 ( 3.14) 3.141593e+00 + + // Complex numbers format as parenthesized pairs of floats, with an 'i' + // after the imaginary part. + point := 110.7 + 22.5i + fmt.Printf("%v %g %.2f %.2e\n", point, point, point, point) + // Result: (110.7+22.5i) (110.7+22.5i) (110.70+22.50i) (1.11e+02+2.25e+01i) + + // Runes are integers but when printed with %c show the character with that + // Unicode value. The %q verb shows them as quoted characters, %U as a + // hex Unicode code point, and %#U as both a code point and a quoted + // printable form if the rune is printable. + smile := '😀' + fmt.Printf("%v %d %c %q %U %#U\n", smile, smile, smile, smile, smile, smile) + // Result: 128512 128512 😀 '😀' U+1F600 U+1F600 '😀' + + // Strings are formatted with %v and %s as-is, with %q as quoted strings, + // and %#q as backquoted strings. + placeholders := `foo "bar"` + fmt.Printf("%v %s %q %#q\n", placeholders, placeholders, placeholders, placeholders) + // Result: foo "bar" foo "bar" "foo \"bar\"" `foo "bar"` + + // Maps formatted with %v show keys and values in their default formats. + // The %#v form (the # is called a "flag" in this context) shows the map in + // the Go source format. Maps are printed in a consistent order, sorted + // by the values of the keys. + isLegume := map[string]bool{ + "peanut": true, + "dachshund": false, + } + fmt.Printf("%v %#v\n", isLegume, isLegume) + // Result: map[dachshund:false peanut:true] map[string]bool{"dachshund":false, "peanut":true} + + // Structs formatted with %v show field values in their default formats. + // The %+v form shows the fields by name, while %#v formats the struct in + // Go source format. + person := struct { + Name string + Age int + }{"Kim", 22} + fmt.Printf("%v %+v %#v\n", person, person, person) + // Result: {Kim 22} {Name:Kim Age:22} struct { Name string; Age int }{Name:"Kim", Age:22} + + // The default format for a pointer shows the underlying value preceded by + // an ampersand. The %p verb prints the pointer value in hex. We use a + // typed nil for the argument to %p here because the value of any non-nil + // pointer would change from run to run; run the commented-out Printf + // call yourself to see. + pointer := &person + fmt.Printf("%v %p\n", pointer, (*int)(nil)) + // Result: &{Kim 22} 0x0 + // fmt.Printf("%v %p\n", pointer, pointer) + // Result: &{Kim 22} 0x010203 // See comment above. + + // Arrays and slices are formatted by applying the format to each element. + greats := [5]string{"Kitano", "Kobayashi", "Kurosawa", "Miyazaki", "Ozu"} + fmt.Printf("%v %q\n", greats, greats) + // Result: [Kitano Kobayashi Kurosawa Miyazaki Ozu] ["Kitano" "Kobayashi" "Kurosawa" "Miyazaki" "Ozu"] + + kGreats := greats[:3] + fmt.Printf("%v %q %#v\n", kGreats, kGreats, kGreats) + // Result: [Kitano Kobayashi Kurosawa] ["Kitano" "Kobayashi" "Kurosawa"] []string{"Kitano", "Kobayashi", "Kurosawa"} + + // Byte slices are special. Integer verbs like %d print the elements in + // that format. The %s and %q forms treat the slice like a string. The %x + // verb has a special form with the space flag that puts a space between + // the bytes. + cmd := []byte("a⌘") + fmt.Printf("%v %d %s %q %x % x\n", cmd, cmd, cmd, cmd, cmd, cmd) + // Result: [97 226 140 152] [97 226 140 152] a⌘ "a⌘" 61e28c98 61 e2 8c 98 + + // Types that implement Stringer are printed the same as strings. Because + // Stringers return a string, we can print them using a string-specific + // verb such as %q. + now := time.Unix(123456789, 0).UTC() // time.Time implements fmt.Stringer. + fmt.Printf("%v %q\n", now, now) + // Result: 1973-11-29 21:33:09 +0000 UTC "1973-11-29 21:33:09 +0000 UTC" + + // Output: + // 23 + // 23 + // 23 + // int *int + // true true + // 42 42 2a 52 101010 + // 3.141592653589793 3.141592653589793 3.14 ( 3.14) 3.141593e+00 + // (110.7+22.5i) (110.7+22.5i) (110.70+22.50i) (1.11e+02+2.25e+01i) + // 128512 128512 😀 '😀' U+1F600 U+1F600 '😀' + // foo "bar" foo "bar" "foo \"bar\"" `foo "bar"` + // map[dachshund:false peanut:true] map[string]bool{"dachshund":false, "peanut":true} + // {Kim 22} {Name:Kim Age:22} struct { Name string; Age int }{Name:"Kim", Age:22} + // &{Kim 22} 0x0 + // [Kitano Kobayashi Kurosawa Miyazaki Ozu] ["Kitano" "Kobayashi" "Kurosawa" "Miyazaki" "Ozu"] + // [Kitano Kobayashi Kurosawa] ["Kitano" "Kobayashi" "Kurosawa"] []string{"Kitano", "Kobayashi", "Kurosawa"} + // [97 226 140 152] [97 226 140 152] a⌘ "a⌘" 61e28c98 61 e2 8c 98 + // 1973-11-29 21:33:09 +0000 UTC "1973-11-29 21:33:09 +0000 UTC" +} diff --git a/src/fmt/export_test.go b/src/fmt/export_test.go new file mode 100644 index 0000000..14163a2 --- /dev/null +++ b/src/fmt/export_test.go @@ -0,0 +1,8 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +var IsSpace = isSpace +var Parsenum = parsenum diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go new file mode 100644 index 0000000..6a79862 --- /dev/null +++ b/src/fmt/fmt_test.go @@ -0,0 +1,1948 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "bytes" + . "fmt" + "internal/race" + "io" + "math" + "reflect" + "runtime" + "strings" + "testing" + "time" + "unicode" +) + +type ( + renamedBool bool + renamedInt int + renamedInt8 int8 + renamedInt16 int16 + renamedInt32 int32 + renamedInt64 int64 + renamedUint uint + renamedUint8 uint8 + renamedUint16 uint16 + renamedUint32 uint32 + renamedUint64 uint64 + renamedUintptr uintptr + renamedString string + renamedBytes []byte + renamedFloat32 float32 + renamedFloat64 float64 + renamedComplex64 complex64 + renamedComplex128 complex128 +) + +func TestFmtInterface(t *testing.T) { + var i1 any + i1 = "abc" + s := Sprintf("%s", i1) + if s != "abc" { + t.Errorf(`Sprintf("%%s", empty("abc")) = %q want %q`, s, "abc") + } +} + +var ( + NaN = math.NaN() + posInf = math.Inf(1) + negInf = math.Inf(-1) + + intVar = 0 + + array = [5]int{1, 2, 3, 4, 5} + iarray = [4]any{1, "hello", 2.5, nil} + slice = array[:] + islice = iarray[:] +) + +type A struct { + i int + j uint + s string + x []int +} + +type I int + +func (i I) String() string { return Sprintf("<%d>", int(i)) } + +type B struct { + I I + j int +} + +type C struct { + i int + B +} + +type F int + +func (f F) Format(s State, c rune) { + Fprintf(s, "<%c=F(%d)>", c, int(f)) +} + +type G int + +func (g G) GoString() string { + return Sprintf("GoString(%d)", int(g)) +} + +type S struct { + F F // a struct field that Formats + G G // a struct field that GoStrings +} + +type SI struct { + I any +} + +// P is a type with a String method with pointer receiver for testing %p. +type P int + +var pValue P + +func (p *P) String() string { + return "String(p)" +} + +var barray = [5]renamedUint8{1, 2, 3, 4, 5} +var bslice = barray[:] + +type byteStringer byte + +func (byteStringer) String() string { + return "X" +} + +var byteStringerSlice = []byteStringer{'h', 'e', 'l', 'l', 'o'} + +type byteFormatter byte + +func (byteFormatter) Format(f State, _ rune) { + Fprint(f, "X") +} + +var byteFormatterSlice = []byteFormatter{'h', 'e', 'l', 'l', 'o'} + +type writeStringFormatter string + +func (sf writeStringFormatter) Format(f State, c rune) { + if sw, ok := f.(io.StringWriter); ok { + sw.WriteString("***" + string(sf) + "***") + } +} + +var fmtTests = []struct { + fmt string + val any + out string +}{ + {"%d", 12345, "12345"}, + {"%v", 12345, "12345"}, + {"%t", true, "true"}, + + // basic string + {"%s", "abc", "abc"}, + {"%q", "abc", `"abc"`}, + {"%x", "abc", "616263"}, + {"%x", "\xff\xf0\x0f\xff", "fff00fff"}, + {"%X", "\xff\xf0\x0f\xff", "FFF00FFF"}, + {"%x", "", ""}, + {"% x", "", ""}, + {"%#x", "", ""}, + {"%# x", "", ""}, + {"%x", "xyz", "78797a"}, + {"%X", "xyz", "78797A"}, + {"% x", "xyz", "78 79 7a"}, + {"% X", "xyz", "78 79 7A"}, + {"%#x", "xyz", "0x78797a"}, + {"%#X", "xyz", "0X78797A"}, + {"%# x", "xyz", "0x78 0x79 0x7a"}, + {"%# X", "xyz", "0X78 0X79 0X7A"}, + + // basic bytes + {"%s", []byte("abc"), "abc"}, + {"%s", [3]byte{'a', 'b', 'c'}, "abc"}, + {"%s", &[3]byte{'a', 'b', 'c'}, "&abc"}, + {"%q", []byte("abc"), `"abc"`}, + {"%x", []byte("abc"), "616263"}, + {"%x", []byte("\xff\xf0\x0f\xff"), "fff00fff"}, + {"%X", []byte("\xff\xf0\x0f\xff"), "FFF00FFF"}, + {"%x", []byte(""), ""}, + {"% x", []byte(""), ""}, + {"%#x", []byte(""), ""}, + {"%# x", []byte(""), ""}, + {"%x", []byte("xyz"), "78797a"}, + {"%X", []byte("xyz"), "78797A"}, + {"% x", []byte("xyz"), "78 79 7a"}, + {"% X", []byte("xyz"), "78 79 7A"}, + {"%#x", []byte("xyz"), "0x78797a"}, + {"%#X", []byte("xyz"), "0X78797A"}, + {"%# x", []byte("xyz"), "0x78 0x79 0x7a"}, + {"%# X", []byte("xyz"), "0X78 0X79 0X7A"}, + + // escaped strings + {"%q", "", `""`}, + {"%#q", "", "``"}, + {"%q", "\"", `"\""`}, + {"%#q", "\"", "`\"`"}, + {"%q", "`", `"` + "`" + `"`}, + {"%#q", "`", `"` + "`" + `"`}, + {"%q", "\n", `"\n"`}, + {"%#q", "\n", `"\n"`}, + {"%q", `\n`, `"\\n"`}, + {"%#q", `\n`, "`\\n`"}, + {"%q", "abc", `"abc"`}, + {"%#q", "abc", "`abc`"}, + {"%q", "日本語", `"日本語"`}, + {"%+q", "日本語", `"\u65e5\u672c\u8a9e"`}, + {"%#q", "日本語", "`日本語`"}, + {"%#+q", "日本語", "`日本語`"}, + {"%q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`}, + {"%+q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`}, + {"%#q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`}, + {"%#+q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`}, + {"%q", "☺", `"☺"`}, + {"% q", "☺", `"☺"`}, // The space modifier should have no effect. + {"%+q", "☺", `"\u263a"`}, + {"%#q", "☺", "`☺`"}, + {"%#+q", "☺", "`☺`"}, + {"%10q", "⌘", ` "⌘"`}, + {"%+10q", "⌘", ` "\u2318"`}, + {"%-10q", "⌘", `"⌘" `}, + {"%+-10q", "⌘", `"\u2318" `}, + {"%010q", "⌘", `0000000"⌘"`}, + {"%+010q", "⌘", `00"\u2318"`}, + {"%-010q", "⌘", `"⌘" `}, // 0 has no effect when - is present. + {"%+-010q", "⌘", `"\u2318" `}, + {"%#8q", "\n", ` "\n"`}, + {"%#+8q", "\r", ` "\r"`}, + {"%#-8q", "\t", "` ` "}, + {"%#+-8q", "\b", `"\b" `}, + {"%q", "abc\xffdef", `"abc\xffdef"`}, + {"%+q", "abc\xffdef", `"abc\xffdef"`}, + {"%#q", "abc\xffdef", `"abc\xffdef"`}, + {"%#+q", "abc\xffdef", `"abc\xffdef"`}, + // Runes that are not printable. + {"%q", "\U0010ffff", `"\U0010ffff"`}, + {"%+q", "\U0010ffff", `"\U0010ffff"`}, + {"%#q", "\U0010ffff", "``"}, + {"%#+q", "\U0010ffff", "``"}, + // Runes that are not valid. + {"%q", string(rune(0x110000)), `"�"`}, + {"%+q", string(rune(0x110000)), `"\ufffd"`}, + {"%#q", string(rune(0x110000)), "`�`"}, + {"%#+q", string(rune(0x110000)), "`�`"}, + + // characters + {"%c", uint('x'), "x"}, + {"%c", 0xe4, "ä"}, + {"%c", 0x672c, "本"}, + {"%c", '日', "日"}, + {"%.0c", '⌘', "⌘"}, // Specifying precision should have no effect. + {"%3c", '⌘', " ⌘"}, + {"%-3c", '⌘', "⌘ "}, + {"%c", uint64(0x100000000), "\ufffd"}, + // Runes that are not printable. + {"%c", '\U00000e00', "\u0e00"}, + {"%c", '\U0010ffff', "\U0010ffff"}, + // Runes that are not valid. + {"%c", -1, "�"}, + {"%c", 0xDC80, "�"}, + {"%c", rune(0x110000), "�"}, + {"%c", int64(0xFFFFFFFFF), "�"}, + {"%c", uint64(0xFFFFFFFFF), "�"}, + + // escaped characters + {"%q", uint(0), `'\x00'`}, + {"%+q", uint(0), `'\x00'`}, + {"%q", '"', `'"'`}, + {"%+q", '"', `'"'`}, + {"%q", '\'', `'\''`}, + {"%+q", '\'', `'\''`}, + {"%q", '`', "'`'"}, + {"%+q", '`', "'`'"}, + {"%q", 'x', `'x'`}, + {"%+q", 'x', `'x'`}, + {"%q", 'ÿ', `'ÿ'`}, + {"%+q", 'ÿ', `'\u00ff'`}, + {"%q", '\n', `'\n'`}, + {"%+q", '\n', `'\n'`}, + {"%q", '☺', `'☺'`}, + {"%+q", '☺', `'\u263a'`}, + {"% q", '☺', `'☺'`}, // The space modifier should have no effect. + {"%.0q", '☺', `'☺'`}, // Specifying precision should have no effect. + {"%10q", '⌘', ` '⌘'`}, + {"%+10q", '⌘', ` '\u2318'`}, + {"%-10q", '⌘', `'⌘' `}, + {"%+-10q", '⌘', `'\u2318' `}, + {"%010q", '⌘', `0000000'⌘'`}, + {"%+010q", '⌘', `00'\u2318'`}, + {"%-010q", '⌘', `'⌘' `}, // 0 has no effect when - is present. + {"%+-010q", '⌘', `'\u2318' `}, + // Runes that are not printable. + {"%q", '\U00000e00', `'\u0e00'`}, + {"%q", '\U0010ffff', `'\U0010ffff'`}, + // Runes that are not valid. + {"%q", int32(-1), `'�'`}, + {"%q", 0xDC80, `'�'`}, + {"%q", rune(0x110000), `'�'`}, + {"%q", int64(0xFFFFFFFFF), `'�'`}, + {"%q", uint64(0xFFFFFFFFF), `'�'`}, + + // width + {"%5s", "abc", " abc"}, + {"%5s", []byte("abc"), " abc"}, + {"%2s", "\u263a", " ☺"}, + {"%2s", []byte("\u263a"), " ☺"}, + {"%-5s", "abc", "abc "}, + {"%-5s", []byte("abc"), "abc "}, + {"%05s", "abc", "00abc"}, + {"%05s", []byte("abc"), "00abc"}, + {"%5s", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}, + {"%5s", []byte("abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz"}, + {"%.5s", "abcdefghijklmnopqrstuvwxyz", "abcde"}, + {"%.5s", []byte("abcdefghijklmnopqrstuvwxyz"), "abcde"}, + {"%.0s", "日本語日本語", ""}, + {"%.0s", []byte("日本語日本語"), ""}, + {"%.5s", "日本語日本語", "日本語日本"}, + {"%.5s", []byte("日本語日本語"), "日本語日本"}, + {"%.10s", "日本語日本語", "日本語日本語"}, + {"%.10s", []byte("日本語日本語"), "日本語日本語"}, + {"%08q", "abc", `000"abc"`}, + {"%08q", []byte("abc"), `000"abc"`}, + {"%-8q", "abc", `"abc" `}, + {"%-8q", []byte("abc"), `"abc" `}, + {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, + {"%.5q", []byte("abcdefghijklmnopqrstuvwxyz"), `"abcde"`}, + {"%.5x", "abcdefghijklmnopqrstuvwxyz", "6162636465"}, + {"%.5x", []byte("abcdefghijklmnopqrstuvwxyz"), "6162636465"}, + {"%.3q", "日本語日本語", `"日本語"`}, + {"%.3q", []byte("日本語日本語"), `"日本語"`}, + {"%.1q", "日本語", `"日"`}, + {"%.1q", []byte("日本語"), `"日"`}, + {"%.1x", "日本語", "e6"}, + {"%.1X", []byte("日本語"), "E6"}, + {"%10.1q", "日本語日本語", ` "日"`}, + {"%10.1q", []byte("日本語日本語"), ` "日"`}, + {"%10v", nil, " <nil>"}, + {"%-10v", nil, "<nil> "}, + + // integers + {"%d", uint(12345), "12345"}, + {"%d", int(-12345), "-12345"}, + {"%d", ^uint8(0), "255"}, + {"%d", ^uint16(0), "65535"}, + {"%d", ^uint32(0), "4294967295"}, + {"%d", ^uint64(0), "18446744073709551615"}, + {"%d", int8(-1 << 7), "-128"}, + {"%d", int16(-1 << 15), "-32768"}, + {"%d", int32(-1 << 31), "-2147483648"}, + {"%d", int64(-1 << 63), "-9223372036854775808"}, + {"%.d", 0, ""}, + {"%.0d", 0, ""}, + {"%6.0d", 0, " "}, + {"%06.0d", 0, " "}, + {"% d", 12345, " 12345"}, + {"%+d", 12345, "+12345"}, + {"%+d", -12345, "-12345"}, + {"%b", 7, "111"}, + {"%b", -6, "-110"}, + {"%#b", 7, "0b111"}, + {"%#b", -6, "-0b110"}, + {"%b", ^uint32(0), "11111111111111111111111111111111"}, + {"%b", ^uint64(0), "1111111111111111111111111111111111111111111111111111111111111111"}, + {"%b", int64(-1 << 63), zeroFill("-1", 63, "")}, + {"%o", 01234, "1234"}, + {"%o", -01234, "-1234"}, + {"%#o", 01234, "01234"}, + {"%#o", -01234, "-01234"}, + {"%O", 01234, "0o1234"}, + {"%O", -01234, "-0o1234"}, + {"%o", ^uint32(0), "37777777777"}, + {"%o", ^uint64(0), "1777777777777777777777"}, + {"%#X", 0, "0X0"}, + {"%x", 0x12abcdef, "12abcdef"}, + {"%X", 0x12abcdef, "12ABCDEF"}, + {"%x", ^uint32(0), "ffffffff"}, + {"%X", ^uint64(0), "FFFFFFFFFFFFFFFF"}, + {"%.20b", 7, "00000000000000000111"}, + {"%10d", 12345, " 12345"}, + {"%10d", -12345, " -12345"}, + {"%+10d", 12345, " +12345"}, + {"%010d", 12345, "0000012345"}, + {"%010d", -12345, "-000012345"}, + {"%20.8d", 1234, " 00001234"}, + {"%20.8d", -1234, " -00001234"}, + {"%020.8d", 1234, " 00001234"}, + {"%020.8d", -1234, " -00001234"}, + {"%-20.8d", 1234, "00001234 "}, + {"%-20.8d", -1234, "-00001234 "}, + {"%-#20.8x", 0x1234abc, "0x01234abc "}, + {"%-#20.8X", 0x1234abc, "0X01234ABC "}, + {"%-#20.8o", 01234, "00001234 "}, + + // Test correct f.intbuf overflow checks. + {"%068d", 1, zeroFill("", 68, "1")}, + {"%068d", -1, zeroFill("-", 67, "1")}, + {"%#.68x", 42, zeroFill("0x", 68, "2a")}, + {"%.68d", -42, zeroFill("-", 68, "42")}, + {"%+.68d", 42, zeroFill("+", 68, "42")}, + {"% .68d", 42, zeroFill(" ", 68, "42")}, + {"% +.68d", 42, zeroFill("+", 68, "42")}, + + // unicode format + {"%U", 0, "U+0000"}, + {"%U", -1, "U+FFFFFFFFFFFFFFFF"}, + {"%U", '\n', `U+000A`}, + {"%#U", '\n', `U+000A`}, + {"%+U", 'x', `U+0078`}, // Plus flag should have no effect. + {"%# U", 'x', `U+0078 'x'`}, // Space flag should have no effect. + {"%#.2U", 'x', `U+0078 'x'`}, // Precisions below 4 should print 4 digits. + {"%U", '\u263a', `U+263A`}, + {"%#U", '\u263a', `U+263A '☺'`}, + {"%U", '\U0001D6C2', `U+1D6C2`}, + {"%#U", '\U0001D6C2', `U+1D6C2 '𝛂'`}, + {"%#14.6U", '⌘', " U+002318 '⌘'"}, + {"%#-14.6U", '⌘', "U+002318 '⌘' "}, + {"%#014.6U", '⌘', " U+002318 '⌘'"}, + {"%#-014.6U", '⌘', "U+002318 '⌘' "}, + {"%.68U", uint(42), zeroFill("U+", 68, "2A")}, + {"%#.68U", '日', zeroFill("U+", 68, "65E5") + " '日'"}, + + // floats + {"%+.3e", 0.0, "+0.000e+00"}, + {"%+.3e", 1.0, "+1.000e+00"}, + {"%+.3x", 0.0, "+0x0.000p+00"}, + {"%+.3x", 1.0, "+0x1.000p+00"}, + {"%+.3f", -1.0, "-1.000"}, + {"%+.3F", -1.0, "-1.000"}, + {"%+.3F", float32(-1.0), "-1.000"}, + {"%+07.2f", 1.0, "+001.00"}, + {"%+07.2f", -1.0, "-001.00"}, + {"%-07.2f", 1.0, "1.00 "}, + {"%-07.2f", -1.0, "-1.00 "}, + {"%+-07.2f", 1.0, "+1.00 "}, + {"%+-07.2f", -1.0, "-1.00 "}, + {"%-+07.2f", 1.0, "+1.00 "}, + {"%-+07.2f", -1.0, "-1.00 "}, + {"%+10.2f", +1.0, " +1.00"}, + {"%+10.2f", -1.0, " -1.00"}, + {"% .3E", -1.0, "-1.000E+00"}, + {"% .3e", 1.0, " 1.000e+00"}, + {"% .3X", -1.0, "-0X1.000P+00"}, + {"% .3x", 1.0, " 0x1.000p+00"}, + {"%+.3g", 0.0, "+0"}, + {"%+.3g", 1.0, "+1"}, + {"%+.3g", -1.0, "-1"}, + {"% .3g", -1.0, "-1"}, + {"% .3g", 1.0, " 1"}, + {"%b", float32(1.0), "8388608p-23"}, + {"%b", 1.0, "4503599627370496p-52"}, + // Test sharp flag used with floats. + {"%#g", 1e-323, "1.00000e-323"}, + {"%#g", -1.0, "-1.00000"}, + {"%#g", 1.1, "1.10000"}, + {"%#g", 123456.0, "123456."}, + {"%#g", 1234567.0, "1.234567e+06"}, + {"%#g", 1230000.0, "1.23000e+06"}, + {"%#g", 1000000.0, "1.00000e+06"}, + {"%#.0f", 1.0, "1."}, + {"%#.0e", 1.0, "1.e+00"}, + {"%#.0x", 1.0, "0x1.p+00"}, + {"%#.0g", 1.0, "1."}, + {"%#.0g", 1100000.0, "1.e+06"}, + {"%#.4f", 1.0, "1.0000"}, + {"%#.4e", 1.0, "1.0000e+00"}, + {"%#.4x", 1.0, "0x1.0000p+00"}, + {"%#.4g", 1.0, "1.000"}, + {"%#.4g", 100000.0, "1.000e+05"}, + {"%#.4g", 1.234, "1.234"}, + {"%#.4g", 0.1234, "0.1234"}, + {"%#.4g", 1.23, "1.230"}, + {"%#.4g", 0.123, "0.1230"}, + {"%#.4g", 1.2, "1.200"}, + {"%#.4g", 0.12, "0.1200"}, + {"%#.4g", 10.2, "10.20"}, + {"%#.4g", 0.0, "0.000"}, + {"%#.4g", 0.012, "0.01200"}, + {"%#.0f", 123.0, "123."}, + {"%#.0e", 123.0, "1.e+02"}, + {"%#.0x", 123.0, "0x1.p+07"}, + {"%#.0g", 123.0, "1.e+02"}, + {"%#.4f", 123.0, "123.0000"}, + {"%#.4e", 123.0, "1.2300e+02"}, + {"%#.4x", 123.0, "0x1.ec00p+06"}, + {"%#.4g", 123.0, "123.0"}, + {"%#.4g", 123000.0, "1.230e+05"}, + {"%#9.4g", 1.0, " 1.000"}, + // The sharp flag has no effect for binary float format. + {"%#b", 1.0, "4503599627370496p-52"}, + // Precision has no effect for binary float format. + {"%.4b", float32(1.0), "8388608p-23"}, + {"%.4b", -1.0, "-4503599627370496p-52"}, + // Test correct f.intbuf boundary checks. + {"%.68f", 1.0, zeroFill("1.", 68, "")}, + {"%.68f", -1.0, zeroFill("-1.", 68, "")}, + // float infinites and NaNs + {"%f", posInf, "+Inf"}, + {"%.1f", negInf, "-Inf"}, + {"% f", NaN, " NaN"}, + {"%20f", posInf, " +Inf"}, + {"% 20F", posInf, " Inf"}, + {"% 20e", negInf, " -Inf"}, + {"% 20x", negInf, " -Inf"}, + {"%+20E", negInf, " -Inf"}, + {"%+20X", negInf, " -Inf"}, + {"% +20g", negInf, " -Inf"}, + {"%+-20G", posInf, "+Inf "}, + {"%20e", NaN, " NaN"}, + {"%20x", NaN, " NaN"}, + {"% +20E", NaN, " +NaN"}, + {"% +20X", NaN, " +NaN"}, + {"% -20g", NaN, " NaN "}, + {"%+-20G", NaN, "+NaN "}, + // Zero padding does not apply to infinities and NaN. + {"%+020e", posInf, " +Inf"}, + {"%+020x", posInf, " +Inf"}, + {"%-020f", negInf, "-Inf "}, + {"%-020E", NaN, "NaN "}, + {"%-020X", NaN, "NaN "}, + + // complex values + {"%.f", 0i, "(0+0i)"}, + {"% .f", 0i, "( 0+0i)"}, + {"%+.f", 0i, "(+0+0i)"}, + {"% +.f", 0i, "(+0+0i)"}, + {"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, + {"%+.3x", 0i, "(+0x0.000p+00+0x0.000p+00i)"}, + {"%+.3f", 0i, "(+0.000+0.000i)"}, + {"%+.3g", 0i, "(+0+0i)"}, + {"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"}, + {"%+.3x", 1 + 2i, "(+0x1.000p+00+0x1.000p+01i)"}, + {"%+.3f", 1 + 2i, "(+1.000+2.000i)"}, + {"%+.3g", 1 + 2i, "(+1+2i)"}, + {"%.3e", 0i, "(0.000e+00+0.000e+00i)"}, + {"%.3x", 0i, "(0x0.000p+00+0x0.000p+00i)"}, + {"%.3f", 0i, "(0.000+0.000i)"}, + {"%.3F", 0i, "(0.000+0.000i)"}, + {"%.3F", complex64(0i), "(0.000+0.000i)"}, + {"%.3g", 0i, "(0+0i)"}, + {"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"}, + {"%.3x", 1 + 2i, "(0x1.000p+00+0x1.000p+01i)"}, + {"%.3f", 1 + 2i, "(1.000+2.000i)"}, + {"%.3g", 1 + 2i, "(1+2i)"}, + {"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"}, + {"%.3x", -1 - 2i, "(-0x1.000p+00-0x1.000p+01i)"}, + {"%.3f", -1 - 2i, "(-1.000-2.000i)"}, + {"%.3g", -1 - 2i, "(-1-2i)"}, + {"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, + {"% .3X", -1 - 2i, "(-0X1.000P+00-0X1.000P+01i)"}, + {"%+.3g", 1 + 2i, "(+1+2i)"}, + {"%+.3g", complex64(1 + 2i), "(+1+2i)"}, + {"%#g", 1 + 2i, "(1.00000+2.00000i)"}, + {"%#g", 123456 + 789012i, "(123456.+789012.i)"}, + {"%#g", 1e-10i, "(0.00000+1.00000e-10i)"}, + {"%#g", -1e10 - 1.11e100i, "(-1.00000e+10-1.11000e+100i)"}, + {"%#.0f", 1.23 + 1.0i, "(1.+1.i)"}, + {"%#.0e", 1.23 + 1.0i, "(1.e+00+1.e+00i)"}, + {"%#.0x", 1.23 + 1.0i, "(0x1.p+00+0x1.p+00i)"}, + {"%#.0g", 1.23 + 1.0i, "(1.+1.i)"}, + {"%#.0g", 0 + 100000i, "(0.+1.e+05i)"}, + {"%#.0g", 1230000 + 0i, "(1.e+06+0.i)"}, + {"%#.4f", 1 + 1.23i, "(1.0000+1.2300i)"}, + {"%#.4e", 123 + 1i, "(1.2300e+02+1.0000e+00i)"}, + {"%#.4x", 123 + 1i, "(0x1.ec00p+06+0x1.0000p+00i)"}, + {"%#.4g", 123 + 1.23i, "(123.0+1.230i)"}, + {"%#12.5g", 0 + 100000i, "( 0.0000 +1.0000e+05i)"}, + {"%#12.5g", 1230000 - 0i, "( 1.2300e+06 +0.0000i)"}, + {"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"}, + {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"}, + // The sharp flag has no effect for binary complex format. + {"%#b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"}, + // Precision has no effect for binary complex format. + {"%.4b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"}, + {"%.4b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"}, + // complex infinites and NaNs + {"%f", complex(posInf, posInf), "(+Inf+Infi)"}, + {"%f", complex(negInf, negInf), "(-Inf-Infi)"}, + {"%f", complex(NaN, NaN), "(NaN+NaNi)"}, + {"%.1f", complex(posInf, posInf), "(+Inf+Infi)"}, + {"% f", complex(posInf, posInf), "( Inf+Infi)"}, + {"% f", complex(negInf, negInf), "(-Inf-Infi)"}, + {"% f", complex(NaN, NaN), "( NaN+NaNi)"}, + {"%8e", complex(posInf, posInf), "( +Inf +Infi)"}, + {"%8x", complex(posInf, posInf), "( +Inf +Infi)"}, + {"% 8E", complex(posInf, posInf), "( Inf +Infi)"}, + {"% 8X", complex(posInf, posInf), "( Inf +Infi)"}, + {"%+8f", complex(negInf, negInf), "( -Inf -Infi)"}, + {"% +8g", complex(negInf, negInf), "( -Inf -Infi)"}, + {"% -8G", complex(NaN, NaN), "( NaN +NaN i)"}, + {"%+-8b", complex(NaN, NaN), "(+NaN +NaN i)"}, + // Zero padding does not apply to infinities and NaN. + {"%08f", complex(posInf, posInf), "( +Inf +Infi)"}, + {"%-08g", complex(negInf, negInf), "(-Inf -Inf i)"}, + {"%-08G", complex(NaN, NaN), "(NaN +NaN i)"}, + + // old test/fmt_test.go + {"%e", 1.0, "1.000000e+00"}, + {"%e", 1234.5678e3, "1.234568e+06"}, + {"%e", 1234.5678e-8, "1.234568e-05"}, + {"%e", -7.0, "-7.000000e+00"}, + {"%e", -1e-9, "-1.000000e-09"}, + {"%f", 1234.5678e3, "1234567.800000"}, + {"%f", 1234.5678e-8, "0.000012"}, + {"%f", -7.0, "-7.000000"}, + {"%f", -1e-9, "-0.000000"}, + {"%g", 1234.5678e3, "1.2345678e+06"}, + {"%g", float32(1234.5678e3), "1.2345678e+06"}, + {"%g", 1234.5678e-8, "1.2345678e-05"}, + {"%g", -7.0, "-7"}, + {"%g", -1e-9, "-1e-09"}, + {"%g", float32(-1e-9), "-1e-09"}, + {"%E", 1.0, "1.000000E+00"}, + {"%E", 1234.5678e3, "1.234568E+06"}, + {"%E", 1234.5678e-8, "1.234568E-05"}, + {"%E", -7.0, "-7.000000E+00"}, + {"%E", -1e-9, "-1.000000E-09"}, + {"%G", 1234.5678e3, "1.2345678E+06"}, + {"%G", float32(1234.5678e3), "1.2345678E+06"}, + {"%G", 1234.5678e-8, "1.2345678E-05"}, + {"%G", -7.0, "-7"}, + {"%G", -1e-9, "-1E-09"}, + {"%G", float32(-1e-9), "-1E-09"}, + {"%20.5s", "qwertyuiop", " qwert"}, + {"%.5s", "qwertyuiop", "qwert"}, + {"%-20.5s", "qwertyuiop", "qwert "}, + {"%20c", 'x', " x"}, + {"%-20c", 'x', "x "}, + {"%20.6e", 1.2345e3, " 1.234500e+03"}, + {"%20.6e", 1.2345e-3, " 1.234500e-03"}, + {"%20e", 1.2345e3, " 1.234500e+03"}, + {"%20e", 1.2345e-3, " 1.234500e-03"}, + {"%20.8e", 1.2345e3, " 1.23450000e+03"}, + {"%20f", 1.23456789e3, " 1234.567890"}, + {"%20f", 1.23456789e-3, " 0.001235"}, + {"%20f", 12345678901.23456789, " 12345678901.234568"}, + {"%-20f", 1.23456789e3, "1234.567890 "}, + {"%20.8f", 1.23456789e3, " 1234.56789000"}, + {"%20.8f", 1.23456789e-3, " 0.00123457"}, + {"%g", 1.23456789e3, "1234.56789"}, + {"%g", 1.23456789e-3, "0.00123456789"}, + {"%g", 1.23456789e20, "1.23456789e+20"}, + + // arrays + {"%v", array, "[1 2 3 4 5]"}, + {"%v", iarray, "[1 hello 2.5 <nil>]"}, + {"%v", barray, "[1 2 3 4 5]"}, + {"%v", &array, "&[1 2 3 4 5]"}, + {"%v", &iarray, "&[1 hello 2.5 <nil>]"}, + {"%v", &barray, "&[1 2 3 4 5]"}, + + // slices + {"%v", slice, "[1 2 3 4 5]"}, + {"%v", islice, "[1 hello 2.5 <nil>]"}, + {"%v", bslice, "[1 2 3 4 5]"}, + {"%v", &slice, "&[1 2 3 4 5]"}, + {"%v", &islice, "&[1 hello 2.5 <nil>]"}, + {"%v", &bslice, "&[1 2 3 4 5]"}, + + // byte arrays and slices with %b,%c,%d,%o,%U and %v + {"%b", [3]byte{65, 66, 67}, "[1000001 1000010 1000011]"}, + {"%c", [3]byte{65, 66, 67}, "[A B C]"}, + {"%d", [3]byte{65, 66, 67}, "[65 66 67]"}, + {"%o", [3]byte{65, 66, 67}, "[101 102 103]"}, + {"%U", [3]byte{65, 66, 67}, "[U+0041 U+0042 U+0043]"}, + {"%v", [3]byte{65, 66, 67}, "[65 66 67]"}, + {"%v", [1]byte{123}, "[123]"}, + {"%012v", []byte{}, "[]"}, + {"%#012v", []byte{}, "[]byte{}"}, + {"%6v", []byte{1, 11, 111}, "[ 1 11 111]"}, + {"%06v", []byte{1, 11, 111}, "[000001 000011 000111]"}, + {"%-6v", []byte{1, 11, 111}, "[1 11 111 ]"}, + {"%-06v", []byte{1, 11, 111}, "[1 11 111 ]"}, + {"%#v", []byte{1, 11, 111}, "[]byte{0x1, 0xb, 0x6f}"}, + {"%#6v", []byte{1, 11, 111}, "[]byte{ 0x1, 0xb, 0x6f}"}, + {"%#06v", []byte{1, 11, 111}, "[]byte{0x000001, 0x00000b, 0x00006f}"}, + {"%#-6v", []byte{1, 11, 111}, "[]byte{0x1 , 0xb , 0x6f }"}, + {"%#-06v", []byte{1, 11, 111}, "[]byte{0x1 , 0xb , 0x6f }"}, + // f.space should and f.plus should not have an effect with %v. + {"% v", []byte{1, 11, 111}, "[ 1 11 111]"}, + {"%+v", [3]byte{1, 11, 111}, "[1 11 111]"}, + {"%# -6v", []byte{1, 11, 111}, "[]byte{ 0x1 , 0xb , 0x6f }"}, + {"%#+-6v", [3]byte{1, 11, 111}, "[3]uint8{0x1 , 0xb , 0x6f }"}, + // f.space and f.plus should have an effect with %d. + {"% d", []byte{1, 11, 111}, "[ 1 11 111]"}, + {"%+d", [3]byte{1, 11, 111}, "[+1 +11 +111]"}, + {"%# -6d", []byte{1, 11, 111}, "[ 1 11 111 ]"}, + {"%#+-6d", [3]byte{1, 11, 111}, "[+1 +11 +111 ]"}, + + // floates with %v + {"%v", 1.2345678, "1.2345678"}, + {"%v", float32(1.2345678), "1.2345678"}, + + // complexes with %v + {"%v", 1 + 2i, "(1+2i)"}, + {"%v", complex64(1 + 2i), "(1+2i)"}, + + // structs + {"%v", A{1, 2, "a", []int{1, 2}}, `{1 2 a [1 2]}`}, + {"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`}, + + // +v on structs with Stringable items + {"%+v", B{1, 2}, `{I:<1> j:2}`}, + {"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`}, + + // other formats on Stringable items + {"%s", I(23), `<23>`}, + {"%q", I(23), `"<23>"`}, + {"%x", I(23), `3c32333e`}, + {"%#x", I(23), `0x3c32333e`}, + {"%# x", I(23), `0x3c 0x32 0x33 0x3e`}, + // Stringer applies only to string formats. + {"%d", I(23), `23`}, + // Stringer applies to the extracted value. + {"%s", reflect.ValueOf(I(23)), `<23>`}, + + // go syntax + {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, + {"%#v", new(byte), "(*uint8)(0xPTR)"}, + {"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"}, + {"%#v", make(chan int), "(chan int)(0xPTR)"}, + {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, + {"%#v", 1000000000, "1000000000"}, + {"%#v", map[string]int{"a": 1}, `map[string]int{"a":1}`}, + {"%#v", map[string]B{"a": {1, 2}}, `map[string]fmt_test.B{"a":fmt_test.B{I:1, j:2}}`}, + {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, + {"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`}, + {"%#v", []int(nil), `[]int(nil)`}, + {"%#v", []int{}, `[]int{}`}, + {"%#v", array, `[5]int{1, 2, 3, 4, 5}`}, + {"%#v", &array, `&[5]int{1, 2, 3, 4, 5}`}, + {"%#v", iarray, `[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, + {"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, + {"%#v", map[int]byte(nil), `map[int]uint8(nil)`}, + {"%#v", map[int]byte{}, `map[int]uint8{}`}, + {"%#v", "foo", `"foo"`}, + {"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, + {"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, + {"%#v", []int32(nil), "[]int32(nil)"}, + {"%#v", 1.2345678, "1.2345678"}, + {"%#v", float32(1.2345678), "1.2345678"}, + + // Whole number floats are printed without decimals. See Issue 27634. + {"%#v", 1.0, "1"}, + {"%#v", 1000000.0, "1e+06"}, + {"%#v", float32(1.0), "1"}, + {"%#v", float32(1000000.0), "1e+06"}, + + // Only print []byte and []uint8 as type []byte if they appear at the top level. + {"%#v", []byte(nil), "[]byte(nil)"}, + {"%#v", []uint8(nil), "[]byte(nil)"}, + {"%#v", []byte{}, "[]byte{}"}, + {"%#v", []uint8{}, "[]byte{}"}, + {"%#v", reflect.ValueOf([]byte{}), "[]uint8{}"}, + {"%#v", reflect.ValueOf([]uint8{}), "[]uint8{}"}, + {"%#v", &[]byte{}, "&[]uint8{}"}, + {"%#v", &[]byte{}, "&[]uint8{}"}, + {"%#v", [3]byte{}, "[3]uint8{0x0, 0x0, 0x0}"}, + {"%#v", [3]uint8{}, "[3]uint8{0x0, 0x0, 0x0}"}, + + // slices with other formats + {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, + {"%x", []int{1, 2, 15}, `[1 2 f]`}, + {"%d", []int{1, 2, 15}, `[1 2 15]`}, + {"%d", []byte{1, 2, 15}, `[1 2 15]`}, + {"%q", []string{"a", "b"}, `["a" "b"]`}, + {"% 02x", []byte{1}, "01"}, + {"% 02x", []byte{1, 2, 3}, "01 02 03"}, + + // Padding with byte slices. + {"%2x", []byte{}, " "}, + {"%#2x", []byte{}, " "}, + {"% 02x", []byte{}, "00"}, + {"%# 02x", []byte{}, "00"}, + {"%-2x", []byte{}, " "}, + {"%-02x", []byte{}, " "}, + {"%8x", []byte{0xab}, " ab"}, + {"% 8x", []byte{0xab}, " ab"}, + {"%#8x", []byte{0xab}, " 0xab"}, + {"%# 8x", []byte{0xab}, " 0xab"}, + {"%08x", []byte{0xab}, "000000ab"}, + {"% 08x", []byte{0xab}, "000000ab"}, + {"%#08x", []byte{0xab}, "00000xab"}, + {"%# 08x", []byte{0xab}, "00000xab"}, + {"%10x", []byte{0xab, 0xcd}, " abcd"}, + {"% 10x", []byte{0xab, 0xcd}, " ab cd"}, + {"%#10x", []byte{0xab, 0xcd}, " 0xabcd"}, + {"%# 10x", []byte{0xab, 0xcd}, " 0xab 0xcd"}, + {"%010x", []byte{0xab, 0xcd}, "000000abcd"}, + {"% 010x", []byte{0xab, 0xcd}, "00000ab cd"}, + {"%#010x", []byte{0xab, 0xcd}, "00000xabcd"}, + {"%# 010x", []byte{0xab, 0xcd}, "00xab 0xcd"}, + {"%-10X", []byte{0xab}, "AB "}, + {"% -010X", []byte{0xab}, "AB "}, + {"%#-10X", []byte{0xab, 0xcd}, "0XABCD "}, + {"%# -010X", []byte{0xab, 0xcd}, "0XAB 0XCD "}, + // Same for strings + {"%2x", "", " "}, + {"%#2x", "", " "}, + {"% 02x", "", "00"}, + {"%# 02x", "", "00"}, + {"%-2x", "", " "}, + {"%-02x", "", " "}, + {"%8x", "\xab", " ab"}, + {"% 8x", "\xab", " ab"}, + {"%#8x", "\xab", " 0xab"}, + {"%# 8x", "\xab", " 0xab"}, + {"%08x", "\xab", "000000ab"}, + {"% 08x", "\xab", "000000ab"}, + {"%#08x", "\xab", "00000xab"}, + {"%# 08x", "\xab", "00000xab"}, + {"%10x", "\xab\xcd", " abcd"}, + {"% 10x", "\xab\xcd", " ab cd"}, + {"%#10x", "\xab\xcd", " 0xabcd"}, + {"%# 10x", "\xab\xcd", " 0xab 0xcd"}, + {"%010x", "\xab\xcd", "000000abcd"}, + {"% 010x", "\xab\xcd", "00000ab cd"}, + {"%#010x", "\xab\xcd", "00000xabcd"}, + {"%# 010x", "\xab\xcd", "00xab 0xcd"}, + {"%-10X", "\xab", "AB "}, + {"% -010X", "\xab", "AB "}, + {"%#-10X", "\xab\xcd", "0XABCD "}, + {"%# -010X", "\xab\xcd", "0XAB 0XCD "}, + + // renamings + {"%v", renamedBool(true), "true"}, + {"%d", renamedBool(true), "%!d(fmt_test.renamedBool=true)"}, + {"%o", renamedInt(8), "10"}, + {"%d", renamedInt8(-9), "-9"}, + {"%v", renamedInt16(10), "10"}, + {"%v", renamedInt32(-11), "-11"}, + {"%X", renamedInt64(255), "FF"}, + {"%v", renamedUint(13), "13"}, + {"%o", renamedUint8(14), "16"}, + {"%X", renamedUint16(15), "F"}, + {"%d", renamedUint32(16), "16"}, + {"%X", renamedUint64(17), "11"}, + {"%o", renamedUintptr(18), "22"}, + {"%x", renamedString("thing"), "7468696e67"}, + {"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`}, + {"%q", renamedBytes([]byte("hello")), `"hello"`}, + {"%x", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "68656c6c6f"}, + {"%X", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "68656C6C6F"}, + {"%s", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "hello"}, + {"%q", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, `"hello"`}, + {"%v", renamedFloat32(22), "22"}, + {"%v", renamedFloat64(33), "33"}, + {"%v", renamedComplex64(3 + 4i), "(3+4i)"}, + {"%v", renamedComplex128(4 - 3i), "(4-3i)"}, + + // Formatter + {"%x", F(1), "<x=F(1)>"}, + {"%x", G(2), "2"}, + {"%+v", S{F(4), G(5)}, "{F:<v=F(4)> G:5}"}, + + // GoStringer + {"%#v", G(6), "GoString(6)"}, + {"%#v", S{F(7), G(8)}, "fmt_test.S{F:<v=F(7)>, G:GoString(8)}"}, + + // %T + {"%T", byte(0), "uint8"}, + {"%T", reflect.ValueOf(nil), "reflect.Value"}, + {"%T", (4 - 3i), "complex128"}, + {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, + {"%T", intVar, "int"}, + {"%6T", &intVar, " *int"}, + {"%10T", nil, " <nil>"}, + {"%-10T", nil, "<nil> "}, + + // %p with pointers + {"%p", (*int)(nil), "0x0"}, + {"%#p", (*int)(nil), "0"}, + {"%p", &intVar, "0xPTR"}, + {"%#p", &intVar, "PTR"}, + {"%p", &array, "0xPTR"}, + {"%p", &slice, "0xPTR"}, + {"%8.2p", (*int)(nil), " 0x00"}, + {"%-20.16p", &intVar, "0xPTR "}, + // %p on non-pointers + {"%p", make(chan int), "0xPTR"}, + {"%p", make(map[int]int), "0xPTR"}, + {"%p", func() {}, "0xPTR"}, + {"%p", 27, "%!p(int=27)"}, // not a pointer at all + {"%p", nil, "%!p(<nil>)"}, // nil on its own has no type ... + {"%#p", nil, "%!p(<nil>)"}, // ... and hence is not a pointer type. + // pointers with specified base + {"%b", &intVar, "PTR_b"}, + {"%d", &intVar, "PTR_d"}, + {"%o", &intVar, "PTR_o"}, + {"%x", &intVar, "PTR_x"}, + {"%X", &intVar, "PTR_X"}, + // %v on pointers + {"%v", nil, "<nil>"}, + {"%#v", nil, "<nil>"}, + {"%v", (*int)(nil), "<nil>"}, + {"%#v", (*int)(nil), "(*int)(nil)"}, + {"%v", &intVar, "0xPTR"}, + {"%#v", &intVar, "(*int)(0xPTR)"}, + {"%8.2v", (*int)(nil), " <nil>"}, + {"%-20.16v", &intVar, "0xPTR "}, + // string method on pointer + {"%s", &pValue, "String(p)"}, // String method... + {"%p", &pValue, "0xPTR"}, // ... is not called with %p. + + // %d on Stringer should give integer if possible + {"%s", time.Time{}.Month(), "January"}, + {"%d", time.Time{}.Month(), "1"}, + + // erroneous things + {"", nil, "%!(EXTRA <nil>)"}, + {"", 2, "%!(EXTRA int=2)"}, + {"no args", "hello", "no args%!(EXTRA string=hello)"}, + {"%s %", "hello", "hello %!(NOVERB)"}, + {"%s %.2", "hello", "hello %!(NOVERB)"}, + {"%017091901790959340919092959340919017929593813360", 0, "%!(NOVERB)%!(EXTRA int=0)"}, + {"%184467440737095516170v", 0, "%!(NOVERB)%!(EXTRA int=0)"}, + // Extra argument errors should format without flags set. + {"%010.2", "12345", "%!(NOVERB)%!(EXTRA string=12345)"}, + + // Test that maps with non-reflexive keys print all keys and values. + {"%v", map[float64]int{NaN: 1, NaN: 1}, "map[NaN:1 NaN:1]"}, + + // Comparison of padding rules with C printf. + /* + C program: + #include <stdio.h> + + char *format[] = { + "[%.2f]", + "[% .2f]", + "[%+.2f]", + "[%7.2f]", + "[% 7.2f]", + "[%+7.2f]", + "[% +7.2f]", + "[%07.2f]", + "[% 07.2f]", + "[%+07.2f]", + "[% +07.2f]" + }; + + int main(void) { + int i; + for(i = 0; i < 11; i++) { + printf("%s: ", format[i]); + printf(format[i], 1.0); + printf(" "); + printf(format[i], -1.0); + printf("\n"); + } + } + + Output: + [%.2f]: [1.00] [-1.00] + [% .2f]: [ 1.00] [-1.00] + [%+.2f]: [+1.00] [-1.00] + [%7.2f]: [ 1.00] [ -1.00] + [% 7.2f]: [ 1.00] [ -1.00] + [%+7.2f]: [ +1.00] [ -1.00] + [% +7.2f]: [ +1.00] [ -1.00] + [%07.2f]: [0001.00] [-001.00] + [% 07.2f]: [ 001.00] [-001.00] + [%+07.2f]: [+001.00] [-001.00] + [% +07.2f]: [+001.00] [-001.00] + + */ + {"%.2f", 1.0, "1.00"}, + {"%.2f", -1.0, "-1.00"}, + {"% .2f", 1.0, " 1.00"}, + {"% .2f", -1.0, "-1.00"}, + {"%+.2f", 1.0, "+1.00"}, + {"%+.2f", -1.0, "-1.00"}, + {"%7.2f", 1.0, " 1.00"}, + {"%7.2f", -1.0, " -1.00"}, + {"% 7.2f", 1.0, " 1.00"}, + {"% 7.2f", -1.0, " -1.00"}, + {"%+7.2f", 1.0, " +1.00"}, + {"%+7.2f", -1.0, " -1.00"}, + {"% +7.2f", 1.0, " +1.00"}, + {"% +7.2f", -1.0, " -1.00"}, + {"%07.2f", 1.0, "0001.00"}, + {"%07.2f", -1.0, "-001.00"}, + {"% 07.2f", 1.0, " 001.00"}, + {"% 07.2f", -1.0, "-001.00"}, + {"%+07.2f", 1.0, "+001.00"}, + {"%+07.2f", -1.0, "-001.00"}, + {"% +07.2f", 1.0, "+001.00"}, + {"% +07.2f", -1.0, "-001.00"}, + + // Complex numbers: exhaustively tested in TestComplexFormatting. + {"%7.2f", 1 + 2i, "( 1.00 +2.00i)"}, + {"%+07.2f", -1 - 2i, "(-001.00-002.00i)"}, + + // Use spaces instead of zero if padding to the right. + {"%0-5s", "abc", "abc "}, + {"%-05.1f", 1.0, "1.0 "}, + + // float and complex formatting should not change the padding width + // for other elements. See issue 14642. + {"%06v", []any{+10.0, 10}, "[000010 000010]"}, + {"%06v", []any{-10.0, 10}, "[-00010 000010]"}, + {"%06v", []any{+10.0 + 10i, 10}, "[(000010+00010i) 000010]"}, + {"%06v", []any{-10.0 + 10i, 10}, "[(-00010+00010i) 000010]"}, + + // integer formatting should not alter padding for other elements. + {"%03.6v", []any{1, 2.0, "x"}, "[000001 002 00x]"}, + {"%03.0v", []any{0, 2.0, "x"}, "[ 002 000]"}, + + // Complex fmt used to leave the plus flag set for future entries in the array + // causing +2+0i and +3+0i instead of 2+0i and 3+0i. + {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, + {"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, + + // Incomplete format specification caused crash. + {"%.", 3, "%!.(int=3)"}, + + // Padding for complex numbers. Has been bad, then fixed, then bad again. + {"%+10.2f", +104.66 + 440.51i, "( +104.66 +440.51i)"}, + {"%+10.2f", -104.66 + 440.51i, "( -104.66 +440.51i)"}, + {"%+10.2f", +104.66 - 440.51i, "( +104.66 -440.51i)"}, + {"%+10.2f", -104.66 - 440.51i, "( -104.66 -440.51i)"}, + {"%+010.2f", +104.66 + 440.51i, "(+000104.66+000440.51i)"}, + {"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"}, + {"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"}, + {"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"}, + + // []T where type T is a byte with a Stringer method. + {"%v", byteStringerSlice, "[X X X X X]"}, + {"%s", byteStringerSlice, "hello"}, + {"%q", byteStringerSlice, "\"hello\""}, + {"%x", byteStringerSlice, "68656c6c6f"}, + {"%X", byteStringerSlice, "68656C6C6F"}, + {"%#v", byteStringerSlice, "[]fmt_test.byteStringer{0x68, 0x65, 0x6c, 0x6c, 0x6f}"}, + + // And the same for Formatter. + {"%v", byteFormatterSlice, "[X X X X X]"}, + {"%s", byteFormatterSlice, "hello"}, + {"%q", byteFormatterSlice, "\"hello\""}, + {"%x", byteFormatterSlice, "68656c6c6f"}, + {"%X", byteFormatterSlice, "68656C6C6F"}, + // This next case seems wrong, but the docs say the Formatter wins here. + {"%#v", byteFormatterSlice, "[]fmt_test.byteFormatter{X, X, X, X, X}"}, + + // pp.WriteString + {"%s", writeStringFormatter(""), "******"}, + {"%s", writeStringFormatter("xyz"), "***xyz***"}, + {"%s", writeStringFormatter("⌘/⌘"), "***⌘/⌘***"}, + + // reflect.Value handled specially in Go 1.5, making it possible to + // see inside non-exported fields (which cannot be accessed with Interface()). + // Issue 8965. + {"%v", reflect.ValueOf(A{}).Field(0).String(), "<int Value>"}, // Equivalent to the old way. + {"%v", reflect.ValueOf(A{}).Field(0), "0"}, // Sees inside the field. + + // verbs apply to the extracted value too. + {"%s", reflect.ValueOf("hello"), "hello"}, + {"%q", reflect.ValueOf("hello"), `"hello"`}, + {"%#04x", reflect.ValueOf(256), "0x0100"}, + + // invalid reflect.Value doesn't crash. + {"%v", reflect.Value{}, "<invalid reflect.Value>"}, + {"%v", &reflect.Value{}, "<invalid Value>"}, + {"%v", SI{reflect.Value{}}, "{<invalid Value>}"}, + + // Tests to check that not supported verbs generate an error string. + {"%☠", nil, "%!☠(<nil>)"}, + {"%☠", any(nil), "%!☠(<nil>)"}, + {"%☠", int(0), "%!☠(int=0)"}, + {"%☠", uint(0), "%!☠(uint=0)"}, + {"%☠", []byte{0, 1}, "[%!☠(uint8=0) %!☠(uint8=1)]"}, + {"%☠", []uint8{0, 1}, "[%!☠(uint8=0) %!☠(uint8=1)]"}, + {"%☠", [1]byte{0}, "[%!☠(uint8=0)]"}, + {"%☠", [1]uint8{0}, "[%!☠(uint8=0)]"}, + {"%☠", "hello", "%!☠(string=hello)"}, + {"%☠", 1.2345678, "%!☠(float64=1.2345678)"}, + {"%☠", float32(1.2345678), "%!☠(float32=1.2345678)"}, + {"%☠", 1.2345678 + 1.2345678i, "%!☠(complex128=(1.2345678+1.2345678i))"}, + {"%☠", complex64(1.2345678 + 1.2345678i), "%!☠(complex64=(1.2345678+1.2345678i))"}, + {"%☠", &intVar, "%!☠(*int=0xPTR)"}, + {"%☠", make(chan int), "%!☠(chan int=0xPTR)"}, + {"%☠", func() {}, "%!☠(func()=0xPTR)"}, + {"%☠", reflect.ValueOf(renamedInt(0)), "%!☠(fmt_test.renamedInt=0)"}, + {"%☠", SI{renamedInt(0)}, "{%!☠(fmt_test.renamedInt=0)}"}, + {"%☠", &[]any{I(1), G(2)}, "&[%!☠(fmt_test.I=1) %!☠(fmt_test.G=2)]"}, + {"%☠", SI{&[]any{I(1), G(2)}}, "{%!☠(*[]interface {}=&[1 2])}"}, + {"%☠", reflect.Value{}, "<invalid reflect.Value>"}, + {"%☠", map[float64]int{NaN: 1}, "map[%!☠(float64=NaN):%!☠(int=1)]"}, +} + +// zeroFill generates zero-filled strings of the specified width. The length +// of the suffix (but not the prefix) is compensated for in the width calculation. +func zeroFill(prefix string, width int, suffix string) string { + return prefix + strings.Repeat("0", width-len(suffix)) + suffix +} + +func TestSprintf(t *testing.T) { + for _, tt := range fmtTests { + s := Sprintf(tt.fmt, tt.val) + i := strings.Index(tt.out, "PTR") + if i >= 0 && i < len(s) { + var pattern, chars string + switch { + case strings.HasPrefix(tt.out[i:], "PTR_b"): + pattern = "PTR_b" + chars = "01" + case strings.HasPrefix(tt.out[i:], "PTR_o"): + pattern = "PTR_o" + chars = "01234567" + case strings.HasPrefix(tt.out[i:], "PTR_d"): + pattern = "PTR_d" + chars = "0123456789" + case strings.HasPrefix(tt.out[i:], "PTR_x"): + pattern = "PTR_x" + chars = "0123456789abcdef" + case strings.HasPrefix(tt.out[i:], "PTR_X"): + pattern = "PTR_X" + chars = "0123456789ABCDEF" + default: + pattern = "PTR" + chars = "0123456789abcdefABCDEF" + } + p := s[:i] + pattern + for j := i; j < len(s); j++ { + if !strings.ContainsRune(chars, rune(s[j])) { + p += s[j:] + break + } + } + s = p + } + if s != tt.out { + if _, ok := tt.val.(string); ok { + // Don't requote the already-quoted strings. + // It's too confusing to read the errors. + t.Errorf("Sprintf(%q, %q) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out) + } else { + t.Errorf("Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out) + } + } + } +} + +// TestComplexFormatting checks that a complex always formats to the same +// thing as if done by hand with two singleton prints. +func TestComplexFormatting(t *testing.T) { + var yesNo = []bool{true, false} + var values = []float64{1, 0, -1, posInf, negInf, NaN} + for _, plus := range yesNo { + for _, zero := range yesNo { + for _, space := range yesNo { + for _, char := range "fFeEgG" { + realFmt := "%" + if zero { + realFmt += "0" + } + if space { + realFmt += " " + } + if plus { + realFmt += "+" + } + realFmt += "10.2" + realFmt += string(char) + // Imaginary part always has a sign, so force + and ignore space. + imagFmt := "%" + if zero { + imagFmt += "0" + } + imagFmt += "+" + imagFmt += "10.2" + imagFmt += string(char) + for _, realValue := range values { + for _, imagValue := range values { + one := Sprintf(realFmt, complex(realValue, imagValue)) + two := Sprintf("("+realFmt+imagFmt+"i)", realValue, imagValue) + if one != two { + t.Error(f, one, two) + } + } + } + } + } + } + } +} + +type SE []any // slice of empty; notational compactness. + +var reorderTests = []struct { + fmt string + val SE + out string +}{ + {"%[1]d", SE{1}, "1"}, + {"%[2]d", SE{2, 1}, "1"}, + {"%[2]d %[1]d", SE{1, 2}, "2 1"}, + {"%[2]*[1]d", SE{2, 5}, " 2"}, + {"%6.2f", SE{12.0}, " 12.00"}, // Explicit version of next line. + {"%[3]*.[2]*[1]f", SE{12.0, 2, 6}, " 12.00"}, + {"%[1]*.[2]*[3]f", SE{6, 2, 12.0}, " 12.00"}, + {"%10f", SE{12.0}, " 12.000000"}, + {"%[1]*[3]f", SE{10, 99, 12.0}, " 12.000000"}, + {"%.6f", SE{12.0}, "12.000000"}, // Explicit version of next line. + {"%.[1]*[3]f", SE{6, 99, 12.0}, "12.000000"}, + {"%6.f", SE{12.0}, " 12"}, // // Explicit version of next line; empty precision means zero. + {"%[1]*.[3]f", SE{6, 3, 12.0}, " 12"}, + // An actual use! Print the same arguments twice. + {"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"}, + + // Erroneous cases. + {"%[d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"}, + {"%[]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[-3]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[99]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[3]", SE{2, 1}, "%!(NOVERB)"}, + {"%[1].2d", SE{5, 6}, "%!d(BADINDEX)"}, + {"%[1]2d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%3.[2]d", SE{7}, "%!d(BADINDEX)"}, + {"%.[2]d", SE{7}, "%!d(BADINDEX)"}, + {"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %!o(MISSING)"}, + {"%[5]d %[2]d %d", SE{1, 2, 3}, "%!d(BADINDEX) 2 3"}, + {"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence. + {"%.[]", SE{}, "%!](BADINDEX)"}, // Issue 10675 + {"%.-3d", SE{42}, "%!-(int=42)3d"}, // TODO: Should this set return better error messages? + {"%2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"}, + {"%-2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"}, + {"%.2147483648d", SE{42}, "%!(NOVERB)%!(EXTRA int=42)"}, +} + +func TestReorder(t *testing.T) { + for _, tt := range reorderTests { + s := Sprintf(tt.fmt, tt.val...) + if s != tt.out { + t.Errorf("Sprintf(%q, %v) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out) + } else { + } + } +} + +func BenchmarkSprintfPadding(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%16f", 1.0) + } + }) +} + +func BenchmarkSprintfEmpty(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("") + } + }) +} + +func BenchmarkSprintfString(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%s", "hello") + } + }) +} + +func BenchmarkSprintfTruncateString(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%.3s", "日本語日本語日本語日本語") + } + }) +} + +func BenchmarkSprintfTruncateBytes(b *testing.B) { + var bytes any = []byte("日本語日本語日本語日本語") + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%.3s", bytes) + } + }) +} + +func BenchmarkSprintfSlowParsingPath(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%.v", nil) + } + }) +} + +func BenchmarkSprintfQuoteString(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%q", "日本語日本語日本語") + } + }) +} + +func BenchmarkSprintfInt(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%d", 5) + } + }) +} + +func BenchmarkSprintfIntInt(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%d %d", 5, 6) + } + }) +} + +func BenchmarkSprintfPrefixedInt(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6) + } + }) +} + +func BenchmarkSprintfFloat(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%g", 5.23184) + } + }) +} + +func BenchmarkSprintfComplex(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%f", 5.23184+5.23184i) + } + }) +} + +func BenchmarkSprintfBoolean(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%t", true) + } + }) +} + +func BenchmarkSprintfHexString(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("% #x", "0123456789abcdef") + } + }) +} + +func BenchmarkSprintfHexBytes(b *testing.B) { + data := []byte("0123456789abcdef") + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("% #x", data) + } + }) +} + +func BenchmarkSprintfBytes(b *testing.B) { + data := []byte("0123456789abcdef") + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%v", data) + } + }) +} + +func BenchmarkSprintfStringer(b *testing.B) { + stringer := I(12345) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%v", stringer) + } + }) +} + +func BenchmarkSprintfStructure(b *testing.B) { + s := &[]any{SI{12345}, map[int]string{0: "hello"}} + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = Sprintf("%#v", s) + } + }) +} + +func BenchmarkManyArgs(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + var buf bytes.Buffer + for pb.Next() { + buf.Reset() + Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world") + } + }) +} + +func BenchmarkFprintInt(b *testing.B) { + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprint(&buf, 123456) + } +} + +func BenchmarkFprintfBytes(b *testing.B) { + data := []byte(string("0123456789")) + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprintf(&buf, "%s", data) + } +} + +func BenchmarkFprintIntNoAlloc(b *testing.B) { + var x any = 123456 + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprint(&buf, x) + } +} + +var mallocBuf bytes.Buffer +var mallocPointer *int // A pointer so we know the interface value won't allocate. + +var mallocTest = []struct { + count int + desc string + fn func() +}{ + {0, `Sprintf("")`, func() { _ = Sprintf("") }}, + {1, `Sprintf("xxx")`, func() { _ = Sprintf("xxx") }}, + {0, `Sprintf("%x")`, func() { _ = Sprintf("%x", 7) }}, + {1, `Sprintf("%x")`, func() { _ = Sprintf("%x", 1<<16) }}, + {3, `Sprintf("%80000s")`, func() { _ = Sprintf("%80000s", "hello") }}, // large buffer (>64KB) + {1, `Sprintf("%s")`, func() { _ = Sprintf("%s", "hello") }}, + {1, `Sprintf("%x %x")`, func() { _ = Sprintf("%x %x", 7, 112) }}, + {1, `Sprintf("%g")`, func() { _ = Sprintf("%g", float32(3.14159)) }}, + {0, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }}, + {0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 7) }}, + {0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 1<<16) }}, + {2, `Fprintf(buf, "%80000s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%80000s", "hello") }}, // large buffer (>64KB) + // If the interface value doesn't need to allocate, amortized allocation overhead should be zero. + {0, `Fprintf(buf, "%x %x %x")`, func() { + mallocBuf.Reset() + Fprintf(&mallocBuf, "%x %x %x", mallocPointer, mallocPointer, mallocPointer) + }}, +} + +var _ bytes.Buffer + +func TestCountMallocs(t *testing.T) { + switch { + case testing.Short(): + t.Skip("skipping malloc count in short mode") + case runtime.GOMAXPROCS(0) > 1: + t.Skip("skipping; GOMAXPROCS>1") + case race.Enabled: + t.Skip("skipping malloc count under race detector") + } + for _, mt := range mallocTest { + mallocs := testing.AllocsPerRun(100, mt.fn) + if got, max := mallocs, float64(mt.count); got > max { + t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max) + } + } +} + +type flagPrinter struct{} + +func (flagPrinter) Format(f State, c rune) { + s := "%" + for i := 0; i < 128; i++ { + if f.Flag(i) { + s += string(rune(i)) + } + } + if w, ok := f.Width(); ok { + s += Sprintf("%d", w) + } + if p, ok := f.Precision(); ok { + s += Sprintf(".%d", p) + } + s += string(c) + io.WriteString(f, "["+s+"]") +} + +var flagtests = []struct { + in string + out string +}{ + {"%a", "[%a]"}, + {"%-a", "[%-a]"}, + {"%+a", "[%+a]"}, + {"%#a", "[%#a]"}, + {"% a", "[% a]"}, + {"%0a", "[%0a]"}, + {"%1.2a", "[%1.2a]"}, + {"%-1.2a", "[%-1.2a]"}, + {"%+1.2a", "[%+1.2a]"}, + {"%-+1.2a", "[%+-1.2a]"}, + {"%-+1.2abc", "[%+-1.2a]bc"}, + {"%-1.2abc", "[%-1.2a]bc"}, +} + +func TestFlagParser(t *testing.T) { + var flagprinter flagPrinter + for _, tt := range flagtests { + s := Sprintf(tt.in, &flagprinter) + if s != tt.out { + t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out) + } + } +} + +func TestStructPrinter(t *testing.T) { + type T struct { + a string + b string + c int + } + var s T + s.a = "abc" + s.b = "def" + s.c = 123 + var tests = []struct { + fmt string + out string + }{ + {"%v", "{abc def 123}"}, + {"%+v", "{a:abc b:def c:123}"}, + {"%#v", `fmt_test.T{a:"abc", b:"def", c:123}`}, + } + for _, tt := range tests { + out := Sprintf(tt.fmt, s) + if out != tt.out { + t.Errorf("Sprintf(%q, s) = %#q, want %#q", tt.fmt, out, tt.out) + } + // The same but with a pointer. + out = Sprintf(tt.fmt, &s) + if out != "&"+tt.out { + t.Errorf("Sprintf(%q, &s) = %#q, want %#q", tt.fmt, out, "&"+tt.out) + } + } +} + +func TestSlicePrinter(t *testing.T) { + slice := []int{} + s := Sprint(slice) + if s != "[]" { + t.Errorf("empty slice printed as %q not %q", s, "[]") + } + slice = []int{1, 2, 3} + s = Sprint(slice) + if s != "[1 2 3]" { + t.Errorf("slice: got %q expected %q", s, "[1 2 3]") + } + s = Sprint(&slice) + if s != "&[1 2 3]" { + t.Errorf("&slice: got %q expected %q", s, "&[1 2 3]") + } +} + +// presentInMap checks map printing using substrings so we don't depend on the +// print order. +func presentInMap(s string, a []string, t *testing.T) { + for i := 0; i < len(a); i++ { + loc := strings.Index(s, a[i]) + if loc < 0 { + t.Errorf("map print: expected to find %q in %q", a[i], s) + } + // make sure the match ends here + loc += len(a[i]) + if loc >= len(s) || (s[loc] != ' ' && s[loc] != ']') { + t.Errorf("map print: %q not properly terminated in %q", a[i], s) + } + } +} + +func TestMapPrinter(t *testing.T) { + m0 := make(map[int]string) + s := Sprint(m0) + if s != "map[]" { + t.Errorf("empty map printed as %q not %q", s, "map[]") + } + m1 := map[int]string{1: "one", 2: "two", 3: "three"} + a := []string{"1:one", "2:two", "3:three"} + presentInMap(Sprintf("%v", m1), a, t) + presentInMap(Sprint(m1), a, t) + // Pointer to map prints the same but with initial &. + if !strings.HasPrefix(Sprint(&m1), "&") { + t.Errorf("no initial & for address of map") + } + presentInMap(Sprintf("%v", &m1), a, t) + presentInMap(Sprint(&m1), a, t) +} + +func TestEmptyMap(t *testing.T) { + const emptyMapStr = "map[]" + var m map[string]int + s := Sprint(m) + if s != emptyMapStr { + t.Errorf("nil map printed as %q not %q", s, emptyMapStr) + } + m = make(map[string]int) + s = Sprint(m) + if s != emptyMapStr { + t.Errorf("empty map printed as %q not %q", s, emptyMapStr) + } +} + +// TestBlank checks that Sprint (and hence Print, Fprint) puts spaces in the +// right places, that is, between arg pairs in which neither is a string. +func TestBlank(t *testing.T) { + got := Sprint("<", 1, ">:", 1, 2, 3, "!") + expect := "<1>:1 2 3!" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + +// TestBlankln checks that Sprintln (and hence Println, Fprintln) puts spaces in +// the right places, that is, between all arg pairs. +func TestBlankln(t *testing.T) { + got := Sprintln("<", 1, ">:", 1, 2, 3, "!") + expect := "< 1 >: 1 2 3 !\n" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + +// TestFormatterPrintln checks Formatter with Sprint, Sprintln, Sprintf. +func TestFormatterPrintln(t *testing.T) { + f := F(1) + expect := "<v=F(1)>\n" + s := Sprint(f, "\n") + if s != expect { + t.Errorf("Sprint wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintln(f) + if s != expect { + t.Errorf("Sprintln wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintf("%v\n", f) + if s != expect { + t.Errorf("Sprintf wrong with Formatter: expected %q got %q", expect, s) + } +} + +func args(a ...any) []any { return a } + +var startests = []struct { + fmt string + in []any + out string +}{ + {"%*d", args(4, 42), " 42"}, + {"%-*d", args(4, 42), "42 "}, + {"%*d", args(-4, 42), "42 "}, + {"%-*d", args(-4, 42), "42 "}, + {"%.*d", args(4, 42), "0042"}, + {"%*.*d", args(8, 4, 42), " 0042"}, + {"%0*d", args(4, 42), "0042"}, + // Some non-int types for width. (Issue 10732). + {"%0*d", args(uint(4), 42), "0042"}, + {"%0*d", args(uint64(4), 42), "0042"}, + {"%0*d", args('\x04', 42), "0042"}, + {"%0*d", args(uintptr(4), 42), "0042"}, + + // erroneous + {"%*d", args(nil, 42), "%!(BADWIDTH)42"}, + {"%*d", args(int(1e7), 42), "%!(BADWIDTH)42"}, + {"%*d", args(int(-1e7), 42), "%!(BADWIDTH)42"}, + {"%.*d", args(nil, 42), "%!(BADPREC)42"}, + {"%.*d", args(-1, 42), "%!(BADPREC)42"}, + {"%.*d", args(int(1e7), 42), "%!(BADPREC)42"}, + {"%.*d", args(uint(1e7), 42), "%!(BADPREC)42"}, + {"%.*d", args(uint64(1<<63), 42), "%!(BADPREC)42"}, // Huge negative (-inf). + {"%.*d", args(uint64(1<<64-1), 42), "%!(BADPREC)42"}, // Small negative (-1). + {"%*d", args(5, "foo"), "%!d(string= foo)"}, + {"%*% %d", args(20, 5), "% 5"}, + {"%*", args(4), "%!(NOVERB)"}, +} + +func TestWidthAndPrecision(t *testing.T) { + for i, tt := range startests { + s := Sprintf(tt.fmt, tt.in...) + if s != tt.out { + t.Errorf("#%d: %q: got %q expected %q", i, tt.fmt, s, tt.out) + } + } +} + +// PanicS is a type that panics in String. +type PanicS struct { + message any +} + +// Value receiver. +func (p PanicS) String() string { + panic(p.message) +} + +// PanicGo is a type that panics in GoString. +type PanicGo struct { + message any +} + +// Value receiver. +func (p PanicGo) GoString() string { + panic(p.message) +} + +// PanicF is a type that panics in Format. +type PanicF struct { + message any +} + +// Value receiver. +func (p PanicF) Format(f State, c rune) { + panic(p.message) +} + +var panictests = []struct { + fmt string + in any + out string +}{ + // String + {"%s", (*PanicS)(nil), "<nil>"}, // nil pointer special case + {"%s", PanicS{io.ErrUnexpectedEOF}, "%!s(PANIC=String method: unexpected EOF)"}, + {"%s", PanicS{3}, "%!s(PANIC=String method: 3)"}, + // GoString + {"%#v", (*PanicGo)(nil), "<nil>"}, // nil pointer special case + {"%#v", PanicGo{io.ErrUnexpectedEOF}, "%!v(PANIC=GoString method: unexpected EOF)"}, + {"%#v", PanicGo{3}, "%!v(PANIC=GoString method: 3)"}, + // Issue 18282. catchPanic should not clear fmtFlags permanently. + {"%#v", []any{PanicGo{3}, PanicGo{3}}, "[]interface {}{%!v(PANIC=GoString method: 3), %!v(PANIC=GoString method: 3)}"}, + // Format + {"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case + {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=Format method: unexpected EOF)"}, + {"%s", PanicF{3}, "%!s(PANIC=Format method: 3)"}, +} + +func TestPanics(t *testing.T) { + for i, tt := range panictests { + s := Sprintf(tt.fmt, tt.in) + if s != tt.out { + t.Errorf("%d: %q: got %q expected %q", i, tt.fmt, s, tt.out) + } + } +} + +// recurCount tests that erroneous String routine doesn't cause fatal recursion. +var recurCount = 0 + +type Recur struct { + i int + failed *bool +} + +func (r *Recur) String() string { + if recurCount++; recurCount > 10 { + *r.failed = true + return "FAIL" + } + // This will call badVerb. Before the fix, that would cause us to recur into + // this routine to print %!p(value). Now we don't call the user's method + // during an error. + return Sprintf("recur@%p value: %d", r, r.i) +} + +func TestBadVerbRecursion(t *testing.T) { + failed := false + r := &Recur{3, &failed} + _ = Sprintf("recur@%p value: %d\n", &r, r.i) + if failed { + t.Error("fail with pointer") + } + failed = false + r = &Recur{4, &failed} + _ = Sprintf("recur@%p, value: %d\n", r, r.i) + if failed { + t.Error("fail with value") + } +} + +func TestIsSpace(t *testing.T) { + // This tests the internal isSpace function. + // IsSpace = isSpace is defined in export_test.go. + for i := rune(0); i <= unicode.MaxRune; i++ { + if IsSpace(i) != unicode.IsSpace(i) { + t.Errorf("isSpace(%U) = %v, want %v", i, IsSpace(i), unicode.IsSpace(i)) + } + } +} + +func hideFromVet(s string) string { return s } + +func TestNilDoesNotBecomeTyped(t *testing.T) { + type A struct{} + type B struct{} + var a *A = nil + var b B = B{} + got := Sprintf(hideFromVet("%s %s %s %s %s"), nil, a, nil, b, nil) + const expect = "%!s(<nil>) %!s(*fmt_test.A=<nil>) %!s(<nil>) {} %!s(<nil>)" + if got != expect { + t.Errorf("expected:\n\t%q\ngot:\n\t%q", expect, got) + } +} + +var formatterFlagTests = []struct { + in string + val any + out string +}{ + // scalar values with the (unused by fmt) 'a' verb. + {"%a", flagPrinter{}, "[%a]"}, + {"%-a", flagPrinter{}, "[%-a]"}, + {"%+a", flagPrinter{}, "[%+a]"}, + {"%#a", flagPrinter{}, "[%#a]"}, + {"% a", flagPrinter{}, "[% a]"}, + {"%0a", flagPrinter{}, "[%0a]"}, + {"%1.2a", flagPrinter{}, "[%1.2a]"}, + {"%-1.2a", flagPrinter{}, "[%-1.2a]"}, + {"%+1.2a", flagPrinter{}, "[%+1.2a]"}, + {"%-+1.2a", flagPrinter{}, "[%+-1.2a]"}, + {"%-+1.2abc", flagPrinter{}, "[%+-1.2a]bc"}, + {"%-1.2abc", flagPrinter{}, "[%-1.2a]bc"}, + + // composite values with the 'a' verb + {"%a", [1]flagPrinter{}, "[[%a]]"}, + {"%-a", [1]flagPrinter{}, "[[%-a]]"}, + {"%+a", [1]flagPrinter{}, "[[%+a]]"}, + {"%#a", [1]flagPrinter{}, "[[%#a]]"}, + {"% a", [1]flagPrinter{}, "[[% a]]"}, + {"%0a", [1]flagPrinter{}, "[[%0a]]"}, + {"%1.2a", [1]flagPrinter{}, "[[%1.2a]]"}, + {"%-1.2a", [1]flagPrinter{}, "[[%-1.2a]]"}, + {"%+1.2a", [1]flagPrinter{}, "[[%+1.2a]]"}, + {"%-+1.2a", [1]flagPrinter{}, "[[%+-1.2a]]"}, + {"%-+1.2abc", [1]flagPrinter{}, "[[%+-1.2a]]bc"}, + {"%-1.2abc", [1]flagPrinter{}, "[[%-1.2a]]bc"}, + + // simple values with the 'v' verb + {"%v", flagPrinter{}, "[%v]"}, + {"%-v", flagPrinter{}, "[%-v]"}, + {"%+v", flagPrinter{}, "[%+v]"}, + {"%#v", flagPrinter{}, "[%#v]"}, + {"% v", flagPrinter{}, "[% v]"}, + {"%0v", flagPrinter{}, "[%0v]"}, + {"%1.2v", flagPrinter{}, "[%1.2v]"}, + {"%-1.2v", flagPrinter{}, "[%-1.2v]"}, + {"%+1.2v", flagPrinter{}, "[%+1.2v]"}, + {"%-+1.2v", flagPrinter{}, "[%+-1.2v]"}, + {"%-+1.2vbc", flagPrinter{}, "[%+-1.2v]bc"}, + {"%-1.2vbc", flagPrinter{}, "[%-1.2v]bc"}, + + // composite values with the 'v' verb. + {"%v", [1]flagPrinter{}, "[[%v]]"}, + {"%-v", [1]flagPrinter{}, "[[%-v]]"}, + {"%+v", [1]flagPrinter{}, "[[%+v]]"}, + {"%#v", [1]flagPrinter{}, "[1]fmt_test.flagPrinter{[%#v]}"}, + {"% v", [1]flagPrinter{}, "[[% v]]"}, + {"%0v", [1]flagPrinter{}, "[[%0v]]"}, + {"%1.2v", [1]flagPrinter{}, "[[%1.2v]]"}, + {"%-1.2v", [1]flagPrinter{}, "[[%-1.2v]]"}, + {"%+1.2v", [1]flagPrinter{}, "[[%+1.2v]]"}, + {"%-+1.2v", [1]flagPrinter{}, "[[%+-1.2v]]"}, + {"%-+1.2vbc", [1]flagPrinter{}, "[[%+-1.2v]]bc"}, + {"%-1.2vbc", [1]flagPrinter{}, "[[%-1.2v]]bc"}, +} + +func TestFormatterFlags(t *testing.T) { + for _, tt := range formatterFlagTests { + s := Sprintf(tt.in, tt.val) + if s != tt.out { + t.Errorf("Sprintf(%q, %T) = %q, want %q", tt.in, tt.val, s, tt.out) + } + } +} + +func TestParsenum(t *testing.T) { + testCases := []struct { + s string + start, end int + num int + isnum bool + newi int + }{ + {"a123", 0, 4, 0, false, 0}, + {"1234", 1, 1, 0, false, 1}, + {"123a", 0, 4, 123, true, 3}, + {"12a3", 0, 4, 12, true, 2}, + {"1234", 0, 4, 1234, true, 4}, + {"1a234", 1, 3, 0, false, 1}, + } + for _, tt := range testCases { + num, isnum, newi := Parsenum(tt.s, tt.start, tt.end) + if num != tt.num || isnum != tt.isnum || newi != tt.newi { + t.Errorf("parsenum(%q, %d, %d) = %d, %v, %d, want %d, %v, %d", tt.s, tt.start, tt.end, num, isnum, newi, tt.num, tt.isnum, tt.newi) + } + } +} + +// Test the various Append printers. The details are well tested above; +// here we just make sure the byte slice is updated. + +const ( + appendResult = "hello world, 23" + hello = "hello " +) + +func TestAppendf(t *testing.T) { + b := make([]byte, 100) + b = b[:copy(b, hello)] + got := Appendf(b, "world, %d", 23) + if string(got) != appendResult { + t.Fatalf("Appendf returns %q not %q", got, appendResult) + } + if &b[0] != &got[0] { + t.Fatalf("Appendf allocated a new slice") + } +} + +func TestAppend(t *testing.T) { + b := make([]byte, 100) + b = b[:copy(b, hello)] + got := Append(b, "world", ", ", 23) + if string(got) != appendResult { + t.Fatalf("Append returns %q not %q", got, appendResult) + } + if &b[0] != &got[0] { + t.Fatalf("Append allocated a new slice") + } +} + +func TestAppendln(t *testing.T) { + b := make([]byte, 100) + b = b[:copy(b, hello)] + got := Appendln(b, "world,", 23) + if string(got) != appendResult+"\n" { + t.Fatalf("Appendln returns %q not %q", got, appendResult+"\n") + } + if &b[0] != &got[0] { + t.Fatalf("Appendln allocated a new slice") + } +} diff --git a/src/fmt/format.go b/src/fmt/format.go new file mode 100644 index 0000000..617f78f --- /dev/null +++ b/src/fmt/format.go @@ -0,0 +1,594 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +import ( + "strconv" + "unicode/utf8" +) + +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 +} + +// A fmt is the raw formatter used by Printf etc. +// It prints into a buffer that must be set up separately. +type fmt 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 *fmt) clearflags() { + f.fmtFlags = fmtFlags{} +} + +func (f *fmt) init(buf *buffer) { + f.buf = buf + f.clearflags() +} + +// writePadding generates n bytes of padding. +func (f *fmt) writePadding(n int) { + if n <= 0 { // No padding bytes needed. + return + } + buf := *f.buf + oldLen := len(buf) + newLen := oldLen + n + // 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 *fmt) 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 *fmt) 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 *fmt) 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 *fmt) 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 *fmt) 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 +} + +// truncateString truncates the string s to the specified precision, if present. +func (f *fmt) 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 *fmt) 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 *fmt) 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 *fmt) 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 *fmt) 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 *fmt) fmtSx(s, digits string) { + f.fmtSbx(s, nil, digits) +} + +// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) 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 *fmt) 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 *fmt) fmtC(c uint64) { + // Explicitly check whether c exceeds utf8.MaxRune since the conversion + // of a uint64 to a rune may lose precision that indicates an overflow. + r := rune(c) + if c > utf8.MaxRune { + r = utf8.RuneError + } + buf := f.intbuf[:0] + f.pad(utf8.AppendRune(buf, r)) +} + +// 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 *fmt) 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 *fmt) 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 + sawNonzeroDigit := 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: + if num[i] != '0' { + sawNonzeroDigit = true + } + // Count significant digits after the first non-zero digit. + if sawNonzeroDigit { + digits-- + } + } + } + if !hasDecimalPoint { + // Leading digit 0 should contribute once to digits. + if len(num) == 2 && num[1] == '0' { + digits-- + } + 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.writeByte(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:]) +} diff --git a/src/fmt/gostringer_example_test.go b/src/fmt/gostringer_example_test.go new file mode 100644 index 0000000..ab19ee3 --- /dev/null +++ b/src/fmt/gostringer_example_test.go @@ -0,0 +1,59 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "fmt" +) + +// Address has a City, State and a Country. +type Address struct { + City string + State string + Country string +} + +// Person has a Name, Age and Address. +type Person struct { + Name string + Age uint + Addr *Address +} + +// GoString makes Person satisfy the GoStringer interface. +// The return value is valid Go code that can be used to reproduce the Person struct. +func (p Person) GoString() string { + if p.Addr != nil { + return fmt.Sprintf("Person{Name: %q, Age: %d, Addr: &Address{City: %q, State: %q, Country: %q}}", p.Name, int(p.Age), p.Addr.City, p.Addr.State, p.Addr.Country) + } + return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, int(p.Age)) +} + +func ExampleGoStringer() { + p1 := Person{ + Name: "Warren", + Age: 31, + Addr: &Address{ + City: "Denver", + State: "CO", + Country: "U.S.A.", + }, + } + // If GoString() wasn't implemented, the output of `fmt.Printf("%#v", p1)` would be similar to + // Person{Name:"Warren", Age:0x1f, Addr:(*main.Address)(0x10448240)} + fmt.Printf("%#v\n", p1) + + p2 := Person{ + Name: "Theia", + Age: 4, + } + // If GoString() wasn't implemented, the output of `fmt.Printf("%#v", p2)` would be similar to + // Person{Name:"Theia", Age:0x4, Addr:(*main.Address)(nil)} + fmt.Printf("%#v\n", p2) + + // Output: + // Person{Name: "Warren", Age: 31, Addr: &Address{City: "Denver", State: "CO", Country: "U.S.A."}} + // Person{Name: "Theia", Age: 4} +} diff --git a/src/fmt/print.go b/src/fmt/print.go new file mode 100644 index 0000000..cb393bd --- /dev/null +++ b/src/fmt/print.go @@ -0,0 +1,1224 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +import ( + "internal/fmtsort" + "io" + "os" + "reflect" + "strconv" + "sync" + "unicode/utf8" +) + +// Strings for use with buffer.WriteString. +// This is less overhead than using buffer.Write with byte arrays. +const ( + commaSpaceString = ", " + nilAngleString = "<nil>" + nilParenString = "(nil)" + nilString = "nil" + mapString = "map[" + percentBangString = "%!" + missingString = "(MISSING)" + badIndexString = "(BADINDEX)" + panicString = "(PANIC=" + extraString = "%!(EXTRA " + badWidthString = "%!(BADWIDTH)" + badPrecString = "%!(BADPREC)" + noVerbString = "%!(NOVERB)" + invReflectString = "<invalid reflect.Value>" +) + +// State represents the printer state passed to custom formatters. +// It provides access to the io.Writer interface plus information about +// the flags and options for the operand's format specifier. +type State interface { + // Write is the function to call to emit formatted output to be printed. + Write(b []byte) (n int, err error) + // Width returns the value of the width option and whether it has been set. + Width() (wid int, ok bool) + // Precision returns the value of the precision option and whether it has been set. + Precision() (prec int, ok bool) + + // Flag reports whether the flag c, a character, has been set. + Flag(c int) bool +} + +// Formatter is implemented by any value that has a Format method. +// The implementation controls how State and rune are interpreted, +// and may call Sprint() or Fprint(f) etc. to generate its output. +type Formatter interface { + Format(f State, verb rune) +} + +// Stringer is implemented by any value that has a String method, +// which defines the “native” format for that value. +// The String method is used to print values passed as an operand +// to any format that accepts a string or to an unformatted printer +// such as Print. +type Stringer interface { + String() string +} + +// GoStringer is implemented by any value that has a GoString method, +// which defines the Go syntax for that value. +// The GoString method is used to print values passed as an operand +// to a %#v format. +type GoStringer interface { + GoString() string +} + +// FormatString returns a string representing the fully qualified formatting +// directive captured by the State, followed by the argument verb. (State does not +// itself contain the verb.) The result has a leading percent sign followed by any +// flags, the width, and the precision. Missing flags, width, and precision are +// omitted. This function allows a Formatter to reconstruct the original +// directive triggering the call to Format. +func FormatString(state State, verb rune) string { + var tmp [16]byte // Use a local buffer. + b := append(tmp[:0], '%') + for _, c := range " +-#0" { // All known flags + if state.Flag(int(c)) { // The argument is an int for historical reasons. + b = append(b, byte(c)) + } + } + if w, ok := state.Width(); ok { + b = strconv.AppendInt(b, int64(w), 10) + } + if p, ok := state.Precision(); ok { + b = append(b, '.') + b = strconv.AppendInt(b, int64(p), 10) + } + b = utf8.AppendRune(b, verb) + return string(b) +} + +// Use simple []byte instead of bytes.Buffer to avoid large dependency. +type buffer []byte + +func (b *buffer) write(p []byte) { + *b = append(*b, p...) +} + +func (b *buffer) writeString(s string) { + *b = append(*b, s...) +} + +func (b *buffer) writeByte(c byte) { + *b = append(*b, c) +} + +func (b *buffer) writeRune(r rune) { + *b = utf8.AppendRune(*b, r) +} + +// 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, as an interface{}. + arg any + + // value is used instead of arg for reflect values. + value reflect.Value + + // fmt is used to format basic items such as integers or strings. + fmt fmt + + // reordered records whether the format string used argument reordering. + reordered bool + // goodArgNum records whether the most recent reordering directive was valid. + goodArgNum bool + // panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion. + panicking bool + // erroring is set when printing an error string to guard against calling handleMethods. + erroring bool + // wrapErrs is set when the format string may contain a %w verb. + wrapErrs bool + // wrappedErrs records the targets of the %w verb. + wrappedErrs []int +} + +var ppFree = sync.Pool{ + New: func() any { return new(pp) }, +} + +// newPrinter allocates a new pp struct or grabs a cached one. +func newPrinter() *pp { + p := ppFree.Get().(*pp) + p.panicking = false + p.erroring = false + p.wrapErrs = 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. If the buffer is larger than the + // limit, we drop the buffer and recycle just the printer. + // + // See https://golang.org/issue/23199. + if cap(p.buf) > 64*1024 { + p.buf = nil + } else { + p.buf = p.buf[:0] + } + if cap(p.wrappedErrs) > 8 { + p.wrappedErrs = nil + } + + p.arg = nil + p.value = reflect.Value{} + p.wrappedErrs = p.wrappedErrs[:0] + 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 +} + +// These routines end in 'f' and take a format string. + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +func Fprintf(w io.Writer, format string, a ...any) (n int, err error) { + p := newPrinter() + p.doPrintf(format, a) + n, err = w.Write(p.buf) + p.free() + return +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +func Printf(format string, a ...any) (n int, err error) { + return Fprintf(os.Stdout, format, a...) +} + +// Sprintf formats according to a format specifier and returns the resulting string. +func Sprintf(format string, a ...any) string { + p := newPrinter() + p.doPrintf(format, a) + s := string(p.buf) + p.free() + return s +} + +// Appendf formats according to a format specifier, appends the result to the byte +// slice, and returns the updated slice. +func Appendf(b []byte, format string, a ...any) []byte { + p := newPrinter() + p.doPrintf(format, a) + b = append(b, p.buf...) + p.free() + return b +} + +// These routines do not take a format string + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +func Fprint(w io.Writer, a ...any) (n int, err error) { + p := newPrinter() + p.doPrint(a) + n, err = w.Write(p.buf) + p.free() + return +} + +// Print formats using the default formats for its operands and writes to standard output. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +func Print(a ...any) (n int, err error) { + return Fprint(os.Stdout, a...) +} + +// Sprint formats using the default formats for its operands and returns the resulting string. +// Spaces are added between operands when neither is a string. +func Sprint(a ...any) string { + p := newPrinter() + p.doPrint(a) + s := string(p.buf) + p.free() + return s +} + +// Append formats using the default formats for its operands, appends the result to +// the byte slice, and returns the updated slice. +func Append(b []byte, a ...any) []byte { + p := newPrinter() + p.doPrint(a) + b = append(b, p.buf...) + p.free() + return b +} + +// These routines end in 'ln', do not take a format string, +// always add spaces between operands, and add a newline +// after the last operand. + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. +func Fprintln(w io.Writer, a ...any) (n int, err error) { + p := newPrinter() + p.doPrintln(a) + n, err = w.Write(p.buf) + p.free() + return +} + +// Println formats using the default formats for its operands and writes to standard output. +// Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. +func Println(a ...any) (n int, err error) { + return Fprintln(os.Stdout, a...) +} + +// Sprintln formats using the default formats for its operands and returns the resulting string. +// Spaces are always added between operands and a newline is appended. +func Sprintln(a ...any) string { + p := newPrinter() + p.doPrintln(a) + s := string(p.buf) + p.free() + return s +} + +// Appendln formats using the default formats for its operands, appends the result +// to the byte slice, and returns the updated slice. Spaces are always added +// between operands and a newline is appended. +func Appendln(b []byte, a ...any) []byte { + p := newPrinter() + p.doPrintln(a) + b = append(b, p.buf...) + p.free() + return b +} + +// getField gets the i'th field of the struct value. +// If the field itself is a non-nil interface, return a value for +// the thing inside the interface, not the interface itself. +func getField(v reflect.Value, i int) reflect.Value { + val := v.Field(i) + if val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() + } + return val +} + +// 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) unknownType(v reflect.Value) { + if !v.IsValid() { + p.buf.writeString(nilAngleString) + return + } + p.buf.writeByte('?') + p.buf.writeString(v.Type().String()) + p.buf.writeByte('?') +} + +func (p *pp) badVerb(verb rune) { + p.erroring = true + p.buf.writeString(percentBangString) + p.buf.writeRune(verb) + p.buf.writeByte('(') + switch { + case p.arg != nil: + p.buf.writeString(reflect.TypeOf(p.arg).String()) + p.buf.writeByte('=') + p.printArg(p.arg, 'v') + case p.value.IsValid(): + p.buf.writeString(p.value.Type().String()) + p.buf.writeByte('=') + p.printValue(p.value, 'v', 0) + default: + p.buf.writeString(nilAngleString) + } + p.buf.writeByte(')') + 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': + p.fmt.fmtQc(v) + 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) + } +} + +// fmtComplex formats a complex number v with +// r = real(v) and j = imag(v) as (r+ji) using +// fmtFloat for r and j formatting. +func (p *pp) fmtComplex(v complex128, size int, verb rune) { + // Make sure any unsupported verbs are found before the + // calls to fmtFloat to not generate an incorrect error string. + switch verb { + case 'v', 'b', 'g', 'G', 'x', 'X', 'f', 'F', 'e', 'E': + oldPlus := p.fmt.plus + p.buf.writeByte('(') + p.fmtFloat(real(v), size/2, verb) + // Imaginary part always has a sign. + p.fmt.plus = true + p.fmtFloat(imag(v), size/2, verb) + p.buf.writeString("i)") + p.fmt.plus = oldPlus + 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.buf.writeString(typeString) + if v == nil { + p.buf.writeString(nilParenString) + return + } + p.buf.writeByte('{') + for i, c := range v { + if i > 0 { + p.buf.writeString(commaSpaceString) + } + p.fmt0x64(uint64(c), true) + } + p.buf.writeByte('}') + } else { + p.buf.writeByte('[') + for i, c := range v { + if i > 0 { + p.buf.writeByte(' ') + } + p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits) + } + p.buf.writeByte(']') + } + 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)) + default: + p.printValue(reflect.ValueOf(v), verb, 0) + } +} + +func (p *pp) fmtPointer(value reflect.Value, verb rune) { + var u uintptr + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer: + u = uintptr(value.UnsafePointer()) + default: + p.badVerb(verb) + return + } + + switch verb { + case 'v': + if p.fmt.sharpV { + p.buf.writeByte('(') + p.buf.writeString(value.Type().String()) + p.buf.writeString(")(") + if u == 0 { + p.buf.writeString(nilString) + } else { + p.fmt0x64(uint64(u), true) + } + p.buf.writeByte(')') + } else { + if u == 0 { + p.fmt.padString(nilAngleString) + } else { + p.fmt0x64(uint64(u), !p.fmt.sharp) + } + } + case 'p': + p.fmt0x64(uint64(u), !p.fmt.sharp) + case 'b', 'o', 'd', 'x', 'X': + p.fmtInteger(uint64(u), unsigned, verb) + default: + p.badVerb(verb) + } +} + +func (p *pp) catchPanic(arg any, verb rune, method string) { + if err := recover(); err != nil { + // If it's a nil pointer, just say "<nil>". The likeliest causes are a + // Stringer that fails to guard against nil or a nil pointer for a + // value receiver, and in either case, "<nil>" is a nice result. + if v := reflect.ValueOf(arg); v.Kind() == reflect.Pointer && v.IsNil() { + p.buf.writeString(nilAngleString) + return + } + // Otherwise print a concise panic message. Most of the time the panic + // value will print itself nicely. + if p.panicking { + // Nested panics; the recursion in printArg cannot succeed. + panic(err) + } + + oldFlags := p.fmt.fmtFlags + // For this output we want default behavior. + p.fmt.clearflags() + + p.buf.writeString(percentBangString) + p.buf.writeRune(verb) + p.buf.writeString(panicString) + p.buf.writeString(method) + p.buf.writeString(" method: ") + p.panicking = true + p.printArg(err, 'v') + p.panicking = false + p.buf.writeByte(')') + + p.fmt.fmtFlags = oldFlags + } +} + +func (p *pp) handleMethods(verb rune) (handled bool) { + if p.erroring { + return + } + if verb == 'w' { + // It is invalid to use %w other than with Errorf or with a non-error arg. + _, ok := p.arg.(error) + if !ok || !p.wrapErrs { + p.badVerb(verb) + return true + } + // If the arg is a Formatter, pass 'v' as the verb to it. + verb = 'v' + } + + // Is it a Formatter? + if formatter, ok := p.arg.(Formatter); ok { + handled = true + defer p.catchPanic(p.arg, verb, "Format") + formatter.Format(p, verb) + return + } + + // If we're doing Go syntax and the argument knows how to supply it, take care of it now. + if p.fmt.sharpV { + if stringer, ok := p.arg.(GoStringer); ok { + handled = true + defer p.catchPanic(p.arg, verb, "GoString") + // Print the result of GoString unadorned. + p.fmt.fmtS(stringer.GoString()) + return + } + } else { + // If a string is acceptable according to the format, see if + // the value satisfies one of the string-valued interfaces. + // Println etc. set verb to %v, which is "stringable". + switch verb { + case 'v', 's', 'x', 'X', 'q': + // Is it an error or Stringer? + // The duplication in the bodies is necessary: + // setting handled and deferring catchPanic + // must happen before calling the method. + switch v := p.arg.(type) { + case error: + handled = true + defer p.catchPanic(p.arg, verb, "Error") + p.fmtString(v.Error(), verb) + return + + case Stringer: + handled = true + defer p.catchPanic(p.arg, verb, "String") + p.fmtString(v.String(), verb) + return + } + } + } + return false +} + +func (p *pp) printArg(arg any, verb rune) { + p.arg = arg + p.value = reflect.Value{} + + if arg == nil { + switch verb { + case 'T', 'v': + p.fmt.padString(nilAngleString) + default: + p.badVerb(verb) + } + return + } + + // 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(reflect.TypeOf(arg).String()) + return + case 'p': + p.fmtPointer(reflect.ValueOf(arg), 'p') + return + } + + // Some types can be done without reflection. + switch f := arg.(type) { + case bool: + p.fmtBool(f, verb) + case float32: + p.fmtFloat(float64(f), 32, verb) + case float64: + p.fmtFloat(f, 64, verb) + case complex64: + p.fmtComplex(complex128(f), 64, verb) + case complex128: + p.fmtComplex(f, 128, verb) + case int: + p.fmtInteger(uint64(f), signed, verb) + case int8: + p.fmtInteger(uint64(f), signed, verb) + case int16: + p.fmtInteger(uint64(f), signed, verb) + case int32: + p.fmtInteger(uint64(f), signed, verb) + case int64: + p.fmtInteger(uint64(f), signed, verb) + case uint: + p.fmtInteger(uint64(f), unsigned, verb) + case uint8: + p.fmtInteger(uint64(f), unsigned, verb) + case uint16: + p.fmtInteger(uint64(f), unsigned, verb) + case uint32: + p.fmtInteger(uint64(f), unsigned, verb) + case uint64: + p.fmtInteger(f, unsigned, verb) + case uintptr: + p.fmtInteger(uint64(f), unsigned, verb) + case string: + p.fmtString(f, verb) + case []byte: + p.fmtBytes(f, verb, "[]byte") + case reflect.Value: + // Handle extractable values with special methods + // since printValue does not handle them at depth 0. + if f.IsValid() && f.CanInterface() { + p.arg = f.Interface() + if p.handleMethods(verb) { + return + } + } + p.printValue(f, verb, 0) + default: + // If the type is not simple, it might have methods. + if !p.handleMethods(verb) { + // Need to use reflection, since the type had no + // interface methods that could be used for formatting. + p.printValue(reflect.ValueOf(f), verb, 0) + } + } +} + +// printValue is similar to printArg but starts with a reflect value, not an interface{} value. +// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg. +func (p *pp) printValue(value reflect.Value, verb rune, depth int) { + // Handle values with special methods if not already handled by printArg (depth == 0). + if depth > 0 && value.IsValid() && value.CanInterface() { + p.arg = value.Interface() + if p.handleMethods(verb) { + return + } + } + p.arg = nil + p.value = value + + switch f := value; value.Kind() { + case reflect.Invalid: + if depth == 0 { + p.buf.writeString(invReflectString) + } else { + switch verb { + case 'v': + p.buf.writeString(nilAngleString) + default: + p.badVerb(verb) + } + } + case reflect.Bool: + p.fmtBool(f.Bool(), verb) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.fmtInteger(uint64(f.Int()), signed, verb) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.fmtInteger(f.Uint(), unsigned, verb) + case reflect.Float32: + p.fmtFloat(f.Float(), 32, verb) + case reflect.Float64: + p.fmtFloat(f.Float(), 64, verb) + case reflect.Complex64: + p.fmtComplex(f.Complex(), 64, verb) + case reflect.Complex128: + p.fmtComplex(f.Complex(), 128, verb) + case reflect.String: + p.fmtString(f.String(), verb) + case reflect.Map: + if p.fmt.sharpV { + p.buf.writeString(f.Type().String()) + if f.IsNil() { + p.buf.writeString(nilParenString) + return + } + p.buf.writeByte('{') + } else { + p.buf.writeString(mapString) + } + sorted := fmtsort.Sort(f) + for i, key := range sorted.Key { + if i > 0 { + if p.fmt.sharpV { + p.buf.writeString(commaSpaceString) + } else { + p.buf.writeByte(' ') + } + } + p.printValue(key, verb, depth+1) + p.buf.writeByte(':') + p.printValue(sorted.Value[i], verb, depth+1) + } + if p.fmt.sharpV { + p.buf.writeByte('}') + } else { + p.buf.writeByte(']') + } + case reflect.Struct: + if p.fmt.sharpV { + p.buf.writeString(f.Type().String()) + } + p.buf.writeByte('{') + for i := 0; i < f.NumField(); i++ { + if i > 0 { + if p.fmt.sharpV { + p.buf.writeString(commaSpaceString) + } else { + p.buf.writeByte(' ') + } + } + if p.fmt.plusV || p.fmt.sharpV { + if name := f.Type().Field(i).Name; name != "" { + p.buf.writeString(name) + p.buf.writeByte(':') + } + } + p.printValue(getField(f, i), verb, depth+1) + } + p.buf.writeByte('}') + case reflect.Interface: + value := f.Elem() + if !value.IsValid() { + if p.fmt.sharpV { + p.buf.writeString(f.Type().String()) + p.buf.writeString(nilParenString) + } else { + p.buf.writeString(nilAngleString) + } + } else { + p.printValue(value, verb, depth+1) + } + case reflect.Array, reflect.Slice: + switch verb { + case 's', 'q', 'x', 'X': + // Handle byte and uint8 slices and arrays special for the above verbs. + t := f.Type() + if t.Elem().Kind() == reflect.Uint8 { + var bytes []byte + if f.Kind() == reflect.Slice || f.CanAddr() { + bytes = f.Bytes() + } else { + // We have an array, but we cannot Bytes() a non-addressable array, + // so we build a slice by hand. This is a rare case but it would be nice + // if reflection could help a little more. + bytes = make([]byte, f.Len()) + for i := range bytes { + bytes[i] = byte(f.Index(i).Uint()) + } + } + p.fmtBytes(bytes, verb, t.String()) + return + } + } + if p.fmt.sharpV { + p.buf.writeString(f.Type().String()) + if f.Kind() == reflect.Slice && f.IsNil() { + p.buf.writeString(nilParenString) + return + } + p.buf.writeByte('{') + for i := 0; i < f.Len(); i++ { + if i > 0 { + p.buf.writeString(commaSpaceString) + } + p.printValue(f.Index(i), verb, depth+1) + } + p.buf.writeByte('}') + } else { + p.buf.writeByte('[') + for i := 0; i < f.Len(); i++ { + if i > 0 { + p.buf.writeByte(' ') + } + p.printValue(f.Index(i), verb, depth+1) + } + p.buf.writeByte(']') + } + case reflect.Pointer: + // pointer to array or slice or struct? ok at top level + // but not embedded (avoid loops) + if depth == 0 && f.UnsafePointer() != nil { + switch a := f.Elem(); a.Kind() { + case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: + p.buf.writeByte('&') + p.printValue(a, verb, depth+1) + return + } + } + fallthrough + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + p.fmtPointer(f, verb) + default: + p.unknownType(f) + } +} + +// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type. +func intFromArg(a []any, argNum int) (num int, isInt bool, newArgNum int) { + newArgNum = argNum + if argNum < len(a) { + num, isInt = a[argNum].(int) // Almost always OK. + if !isInt { + // Work harder. + switch v := reflect.ValueOf(a[argNum]); v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n := v.Int() + if int64(int(n)) == n { + num = int(n) + isInt = true + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n := v.Uint() + if int64(n) >= 0 && uint64(int(n)) == n { + num = int(n) + isInt = true + } + default: + // Already 0, false. + } + } + 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.buf.writeString(percentBangString) + p.buf.writeRune(verb) + p.buf.writeString(badIndexString) +} + +func (p *pp) missingArg(verb rune) { + p.buf.writeString(percentBangString) + p.buf.writeRune(verb) + p.buf.writeString(missingString) +} + +func (p *pp) doPrintf(format string, a []any) { + 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.buf.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) { + switch c { + case 'w': + p.wrappedErrs = append(p.wrappedErrs, argNum) + fallthrough + case '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.buf.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.buf.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.buf.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.buf.writeByte('%') + 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 == 'w': + p.wrappedErrs = append(p.wrappedErrs, argNum) + fallthrough + 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.buf.writeString(extraString) + for i, arg := range a[argNum:] { + if i > 0 { + p.buf.writeString(commaSpaceString) + } + if arg == nil { + p.buf.writeString(nilAngleString) + } else { + p.buf.writeString(reflect.TypeOf(arg).String()) + p.buf.writeByte('=') + p.printArg(arg, 'v') + } + } + p.buf.writeByte(')') + } +} + +func (p *pp) doPrint(a []any) { + prevString := false + for argNum, arg := range a { + isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String + // Add a space between two non-string arguments. + if argNum > 0 && !isString && !prevString { + p.buf.writeByte(' ') + } + p.printArg(arg, 'v') + prevString = isString + } +} + +// doPrintln is like doPrint but always adds a space between arguments +// and a newline after the last argument. +func (p *pp) doPrintln(a []any) { + for argNum, arg := range a { + if argNum > 0 { + p.buf.writeByte(' ') + } + p.printArg(arg, 'v') + } + p.buf.writeByte('\n') +} diff --git a/src/fmt/scan.go b/src/fmt/scan.go new file mode 100644 index 0000000..5dd0971 --- /dev/null +++ b/src/fmt/scan.go @@ -0,0 +1,1238 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +import ( + "errors" + "io" + "math" + "os" + "reflect" + "strconv" + "sync" + "unicode/utf8" +) + +// ScanState represents the scanner state passed to custom scanners. +// Scanners may do rune-at-a-time scanning or ask the ScanState +// to discover the next space-delimited token. +type ScanState interface { + // ReadRune reads the next rune (Unicode code point) from the input. + // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will + // return EOF after returning the first '\n' or when reading beyond + // the specified width. + ReadRune() (r rune, size int, err error) + // UnreadRune causes the next call to ReadRune to return the same rune. + UnreadRune() error + // SkipSpace skips space in the input. Newlines are treated appropriately + // for the operation being performed; see the package documentation + // for more information. + SkipSpace() + // Token skips space in the input if skipSpace is true, then returns the + // run of Unicode code points c satisfying f(c). If f is nil, + // !unicode.IsSpace(c) is used; that is, the token will hold non-space + // characters. Newlines are treated appropriately for the operation being + // performed; see the package documentation for more information. + // The returned slice points to shared data that may be overwritten + // by the next call to Token, a call to a Scan function using the ScanState + // as input, or when the calling Scan method returns. + Token(skipSpace bool, f func(rune) bool) (token []byte, err error) + // Width returns the value of the width option and whether it has been set. + // The unit is Unicode code points. + Width() (wid int, ok bool) + // Because ReadRune is implemented by the interface, Read should never be + // called by the scanning routines and a valid implementation of + // ScanState may choose always to return an error from Read. + Read(buf []byte) (n int, err error) +} + +// Scanner is implemented by any value that has a Scan method, which scans +// the input for the representation of a value and stores the result in the +// receiver, which must be a pointer to be useful. The Scan method is called +// for any argument to Scan, Scanf, or Scanln that implements it. +type Scanner interface { + Scan(state ScanState, verb rune) error +} + +// Scan scans text read from standard input, storing successive +// space-separated values into successive arguments. Newlines count +// as space. It returns the number of items successfully scanned. +// If that is less than the number of arguments, err will report why. +func Scan(a ...any) (n int, err error) { + return Fscan(os.Stdin, a...) +} + +// Scanln is similar to Scan, but stops scanning at a newline and +// after the final item there must be a newline or EOF. +func Scanln(a ...any) (n int, err error) { + return Fscanln(os.Stdin, a...) +} + +// Scanf scans text read from standard input, storing successive +// space-separated values into successive arguments as determined by +// the format. It returns the number of items successfully scanned. +// If that is less than the number of arguments, err will report why. +// Newlines in the input must match newlines in the format. +// The one exception: the verb %c always scans the next rune in the +// input, even if it is a space (or tab etc.) or newline. +func Scanf(format string, a ...any) (n int, err error) { + return Fscanf(os.Stdin, format, a...) +} + +type stringReader string + +func (r *stringReader) Read(b []byte) (n int, err error) { + n = copy(b, *r) + *r = (*r)[n:] + if n == 0 { + err = io.EOF + } + return +} + +// Sscan scans the argument string, storing successive space-separated +// values into successive arguments. Newlines count as space. It +// returns the number of items successfully scanned. If that is less +// than the number of arguments, err will report why. +func Sscan(str string, a ...any) (n int, err error) { + return Fscan((*stringReader)(&str), a...) +} + +// Sscanln is similar to Sscan, but stops scanning at a newline and +// after the final item there must be a newline or EOF. +func Sscanln(str string, a ...any) (n int, err error) { + return Fscanln((*stringReader)(&str), a...) +} + +// Sscanf scans the argument string, storing successive space-separated +// values into successive arguments as determined by the format. It +// returns the number of items successfully parsed. +// Newlines in the input must match newlines in the format. +func Sscanf(str string, format string, a ...any) (n int, err error) { + return Fscanf((*stringReader)(&str), format, a...) +} + +// Fscan scans text read from r, storing successive space-separated +// values into successive arguments. Newlines count as space. It +// returns the number of items successfully scanned. If that is less +// than the number of arguments, err will report why. +func Fscan(r io.Reader, a ...any) (n int, err error) { + s, old := newScanState(r, true, false) + n, err = s.doScan(a) + s.free(old) + return +} + +// Fscanln is similar to Fscan, but stops scanning at a newline and +// after the final item there must be a newline or EOF. +func Fscanln(r io.Reader, a ...any) (n int, err error) { + s, old := newScanState(r, false, true) + n, err = s.doScan(a) + s.free(old) + return +} + +// Fscanf scans text read from r, storing successive space-separated +// values into successive arguments as determined by the format. It +// returns the number of items successfully parsed. +// Newlines in the input must match newlines in the format. +func Fscanf(r io.Reader, format string, a ...any) (n int, err error) { + s, old := newScanState(r, false, false) + n, err = s.doScanf(format, a) + s.free(old) + return +} + +// scanError represents an error generated by the scanning software. +// It's used as a unique signature to identify such errors when recovering. +type scanError struct { + err error +} + +const eof = -1 + +// ss is the internal implementation of ScanState. +type ss struct { + rs io.RuneScanner // where to read input + buf buffer // token accumulator + count int // runes consumed so far. + atEOF bool // already read EOF + ssave +} + +// ssave holds the parts of ss that need to be +// saved and restored on recursive scans. +type ssave struct { + validSave bool // is or was a part of an actual ss. + nlIsEnd bool // whether newline terminates scan + nlIsSpace bool // whether newline counts as white space + argLimit int // max value of ss.count for this arg; argLimit <= limit + limit int // max value of ss.count. + maxWid int // width of this arg. +} + +// The Read method is only in ScanState so that ScanState +// satisfies io.Reader. It will never be called when used as +// intended, so there is no need to make it actually work. +func (s *ss) Read(buf []byte) (n int, err error) { + return 0, errors.New("ScanState's Read should not be called. Use ReadRune") +} + +func (s *ss) ReadRune() (r rune, size int, err error) { + if s.atEOF || s.count >= s.argLimit { + err = io.EOF + return + } + + r, size, err = s.rs.ReadRune() + if err == nil { + s.count++ + if s.nlIsEnd && r == '\n' { + s.atEOF = true + } + } else if err == io.EOF { + s.atEOF = true + } + return +} + +func (s *ss) Width() (wid int, ok bool) { + if s.maxWid == hugeWid { + return 0, false + } + return s.maxWid, true +} + +// The public method returns an error; this private one panics. +// If getRune reaches EOF, the return value is EOF (-1). +func (s *ss) getRune() (r rune) { + r, _, err := s.ReadRune() + if err != nil { + if err == io.EOF { + return eof + } + s.error(err) + } + return +} + +// mustReadRune turns io.EOF into a panic(io.ErrUnexpectedEOF). +// It is called in cases such as string scanning where an EOF is a +// syntax error. +func (s *ss) mustReadRune() (r rune) { + r = s.getRune() + if r == eof { + s.error(io.ErrUnexpectedEOF) + } + return +} + +func (s *ss) UnreadRune() error { + s.rs.UnreadRune() + s.atEOF = false + s.count-- + return nil +} + +func (s *ss) error(err error) { + panic(scanError{err}) +} + +func (s *ss) errorString(err string) { + panic(scanError{errors.New(err)}) +} + +func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) { + defer func() { + if e := recover(); e != nil { + if se, ok := e.(scanError); ok { + err = se.err + } else { + panic(e) + } + } + }() + if f == nil { + f = notSpace + } + s.buf = s.buf[:0] + tok = s.token(skipSpace, f) + return +} + +// space is a copy of the unicode.White_Space ranges, +// to avoid depending on package unicode. +var space = [][2]uint16{ + {0x0009, 0x000d}, + {0x0020, 0x0020}, + {0x0085, 0x0085}, + {0x00a0, 0x00a0}, + {0x1680, 0x1680}, + {0x2000, 0x200a}, + {0x2028, 0x2029}, + {0x202f, 0x202f}, + {0x205f, 0x205f}, + {0x3000, 0x3000}, +} + +func isSpace(r rune) bool { + if r >= 1<<16 { + return false + } + rx := uint16(r) + for _, rng := range space { + if rx < rng[0] { + return false + } + if rx <= rng[1] { + return true + } + } + return false +} + +// notSpace is the default scanning function used in Token. +func notSpace(r rune) bool { + return !isSpace(r) +} + +// readRune is a structure to enable reading UTF-8 encoded code points +// from an io.Reader. It is used if the Reader given to the scanner does +// not already implement io.RuneScanner. +type readRune struct { + reader io.Reader + buf [utf8.UTFMax]byte // used only inside ReadRune + pending int // number of bytes in pendBuf; only >0 for bad UTF-8 + pendBuf [utf8.UTFMax]byte // bytes left over + peekRune rune // if >=0 next rune; when <0 is ^(previous Rune) +} + +// readByte returns the next byte from the input, which may be +// left over from a previous read if the UTF-8 was ill-formed. +func (r *readRune) readByte() (b byte, err error) { + if r.pending > 0 { + b = r.pendBuf[0] + copy(r.pendBuf[0:], r.pendBuf[1:]) + r.pending-- + return + } + n, err := io.ReadFull(r.reader, r.pendBuf[:1]) + if n != 1 { + return 0, err + } + return r.pendBuf[0], err +} + +// ReadRune returns the next UTF-8 encoded code point from the +// io.Reader inside r. +func (r *readRune) ReadRune() (rr rune, size int, err error) { + if r.peekRune >= 0 { + rr = r.peekRune + r.peekRune = ^r.peekRune + size = utf8.RuneLen(rr) + return + } + r.buf[0], err = r.readByte() + if err != nil { + return + } + if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case + rr = rune(r.buf[0]) + size = 1 // Known to be 1. + // Flip the bits of the rune so it's available to UnreadRune. + r.peekRune = ^rr + return + } + var n int + for n = 1; !utf8.FullRune(r.buf[:n]); n++ { + r.buf[n], err = r.readByte() + if err != nil { + if err == io.EOF { + err = nil + break + } + return + } + } + rr, size = utf8.DecodeRune(r.buf[:n]) + if size < n { // an error, save the bytes for the next read + copy(r.pendBuf[r.pending:], r.buf[size:n]) + r.pending += n - size + } + // Flip the bits of the rune so it's available to UnreadRune. + r.peekRune = ^rr + return +} + +func (r *readRune) UnreadRune() error { + if r.peekRune >= 0 { + return errors.New("fmt: scanning called UnreadRune with no rune available") + } + // Reverse bit flip of previously read rune to obtain valid >=0 state. + r.peekRune = ^r.peekRune + return nil +} + +var ssFree = sync.Pool{ + New: func() any { return new(ss) }, +} + +// newScanState allocates a new ss struct or grab a cached one. +func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { + s = ssFree.Get().(*ss) + if rs, ok := r.(io.RuneScanner); ok { + s.rs = rs + } else { + s.rs = &readRune{reader: r, peekRune: -1} + } + s.nlIsSpace = nlIsSpace + s.nlIsEnd = nlIsEnd + s.atEOF = false + s.limit = hugeWid + s.argLimit = hugeWid + s.maxWid = hugeWid + s.validSave = true + s.count = 0 + return +} + +// free saves used ss structs in ssFree; avoid an allocation per invocation. +func (s *ss) free(old ssave) { + // If it was used recursively, just restore the old state. + if old.validSave { + s.ssave = old + return + } + // Don't hold on to ss structs with large buffers. + if cap(s.buf) > 1024 { + return + } + s.buf = s.buf[:0] + s.rs = nil + ssFree.Put(s) +} + +// SkipSpace provides Scan methods the ability to skip space and newline +// characters in keeping with the current scanning mode set by format strings +// and Scan/Scanln. +func (s *ss) SkipSpace() { + for { + r := s.getRune() + if r == eof { + return + } + if r == '\r' && s.peek("\n") { + continue + } + if r == '\n' { + if s.nlIsSpace { + continue + } + s.errorString("unexpected newline") + return + } + if !isSpace(r) { + s.UnreadRune() + break + } + } +} + +// token returns the next space-delimited string from the input. It +// skips white space. For Scanln, it stops at newlines. For Scan, +// newlines are treated as spaces. +func (s *ss) token(skipSpace bool, f func(rune) bool) []byte { + if skipSpace { + s.SkipSpace() + } + // read until white space or newline + for { + r := s.getRune() + if r == eof { + break + } + if !f(r) { + s.UnreadRune() + break + } + s.buf.writeRune(r) + } + return s.buf +} + +var errComplex = errors.New("syntax error scanning complex number") +var errBool = errors.New("syntax error scanning boolean") + +func indexRune(s string, r rune) int { + for i, c := range s { + if c == r { + return i + } + } + return -1 +} + +// consume reads the next rune in the input and reports whether it is in the ok string. +// If accept is true, it puts the character into the input token. +func (s *ss) consume(ok string, accept bool) bool { + r := s.getRune() + if r == eof { + return false + } + if indexRune(ok, r) >= 0 { + if accept { + s.buf.writeRune(r) + } + return true + } + if r != eof && accept { + s.UnreadRune() + } + return false +} + +// peek reports whether the next character is in the ok string, without consuming it. +func (s *ss) peek(ok string) bool { + r := s.getRune() + if r != eof { + s.UnreadRune() + } + return indexRune(ok, r) >= 0 +} + +func (s *ss) notEOF() { + // Guarantee there is data to be read. + if r := s.getRune(); r == eof { + panic(io.EOF) + } + s.UnreadRune() +} + +// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the +// buffer and returns true. Otherwise it return false. +func (s *ss) accept(ok string) bool { + return s.consume(ok, true) +} + +// okVerb verifies that the verb is present in the list, setting s.err appropriately if not. +func (s *ss) okVerb(verb rune, okVerbs, typ string) bool { + for _, v := range okVerbs { + if v == verb { + return true + } + } + s.errorString("bad verb '%" + string(verb) + "' for " + typ) + return false +} + +// scanBool returns the value of the boolean represented by the next token. +func (s *ss) scanBool(verb rune) bool { + s.SkipSpace() + s.notEOF() + if !s.okVerb(verb, "tv", "boolean") { + return false + } + // Syntax-checking a boolean is annoying. We're not fastidious about case. + switch s.getRune() { + case '0': + return false + case '1': + return true + case 't', 'T': + if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) { + s.error(errBool) + } + return true + case 'f', 'F': + if s.accept("aA") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) { + s.error(errBool) + } + return false + } + return false +} + +// Numerical elements +const ( + binaryDigits = "01" + octalDigits = "01234567" + decimalDigits = "0123456789" + hexadecimalDigits = "0123456789aAbBcCdDeEfF" + sign = "+-" + period = "." + exponent = "eEpP" +) + +// getBase returns the numeric base represented by the verb and its digit string. +func (s *ss) getBase(verb rune) (base int, digits string) { + s.okVerb(verb, "bdoUxXv", "integer") // sets s.err + base = 10 + digits = decimalDigits + switch verb { + case 'b': + base = 2 + digits = binaryDigits + case 'o': + base = 8 + digits = octalDigits + case 'x', 'X', 'U': + base = 16 + digits = hexadecimalDigits + } + return +} + +// scanNumber returns the numerical string with specified digits starting here. +func (s *ss) scanNumber(digits string, haveDigits bool) string { + if !haveDigits { + s.notEOF() + if !s.accept(digits) { + s.errorString("expected integer") + } + } + for s.accept(digits) { + } + return string(s.buf) +} + +// scanRune returns the next rune value in the input. +func (s *ss) scanRune(bitSize int) int64 { + s.notEOF() + r := s.getRune() + n := uint(bitSize) + x := (int64(r) << (64 - n)) >> (64 - n) + if x != int64(r) { + s.errorString("overflow on character value " + string(r)) + } + return int64(r) +} + +// scanBasePrefix reports whether the integer begins with a base prefix +// and returns the base, digit string, and whether a zero was found. +// It is called only if the verb is %v. +func (s *ss) scanBasePrefix() (base int, digits string, zeroFound bool) { + if !s.peek("0") { + return 0, decimalDigits + "_", false + } + s.accept("0") + // Special cases for 0, 0b, 0o, 0x. + switch { + case s.peek("bB"): + s.consume("bB", true) + return 0, binaryDigits + "_", true + case s.peek("oO"): + s.consume("oO", true) + return 0, octalDigits + "_", true + case s.peek("xX"): + s.consume("xX", true) + return 0, hexadecimalDigits + "_", true + default: + return 0, octalDigits + "_", true + } +} + +// scanInt returns the value of the integer represented by the next +// token, checking for overflow. Any error is stored in s.err. +func (s *ss) scanInt(verb rune, bitSize int) int64 { + if verb == 'c' { + return s.scanRune(bitSize) + } + s.SkipSpace() + s.notEOF() + base, digits := s.getBase(verb) + haveDigits := false + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } else { + s.accept(sign) // If there's a sign, it will be left in the token buffer. + if verb == 'v' { + base, digits, haveDigits = s.scanBasePrefix() + } + } + tok := s.scanNumber(digits, haveDigits) + i, err := strconv.ParseInt(tok, base, 64) + if err != nil { + s.error(err) + } + n := uint(bitSize) + x := (i << (64 - n)) >> (64 - n) + if x != i { + s.errorString("integer overflow on token " + tok) + } + return i +} + +// scanUint returns the value of the unsigned integer represented +// by the next token, checking for overflow. Any error is stored in s.err. +func (s *ss) scanUint(verb rune, bitSize int) uint64 { + if verb == 'c' { + return uint64(s.scanRune(bitSize)) + } + s.SkipSpace() + s.notEOF() + base, digits := s.getBase(verb) + haveDigits := false + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } else if verb == 'v' { + base, digits, haveDigits = s.scanBasePrefix() + } + tok := s.scanNumber(digits, haveDigits) + i, err := strconv.ParseUint(tok, base, 64) + if err != nil { + s.error(err) + } + n := uint(bitSize) + x := (i << (64 - n)) >> (64 - n) + if x != i { + s.errorString("unsigned integer overflow on token " + tok) + } + return i +} + +// floatToken returns the floating-point number starting here, no longer than swid +// if the width is specified. It's not rigorous about syntax because it doesn't check that +// we have at least some digits, but Atof will do that. +func (s *ss) floatToken() string { + s.buf = s.buf[:0] + // NaN? + if s.accept("nN") && s.accept("aA") && s.accept("nN") { + return string(s.buf) + } + // leading sign? + s.accept(sign) + // Inf? + if s.accept("iI") && s.accept("nN") && s.accept("fF") { + return string(s.buf) + } + digits := decimalDigits + "_" + exp := exponent + if s.accept("0") && s.accept("xX") { + digits = hexadecimalDigits + "_" + exp = "pP" + } + // digits? + for s.accept(digits) { + } + // decimal point? + if s.accept(period) { + // fraction? + for s.accept(digits) { + } + } + // exponent? + if s.accept(exp) { + // leading sign? + s.accept(sign) + // digits? + for s.accept(decimalDigits + "_") { + } + } + return string(s.buf) +} + +// complexTokens returns the real and imaginary parts of the complex number starting here. +// The number might be parenthesized and has the format (N+Ni) where N is a floating-point +// number and there are no spaces within. +func (s *ss) complexTokens() (real, imag string) { + // TODO: accept N and Ni independently? + parens := s.accept("(") + real = s.floatToken() + s.buf = s.buf[:0] + // Must now have a sign. + if !s.accept("+-") { + s.error(errComplex) + } + // Sign is now in buffer + imagSign := string(s.buf) + imag = s.floatToken() + if !s.accept("i") { + s.error(errComplex) + } + if parens && !s.accept(")") { + s.error(errComplex) + } + return real, imagSign + imag +} + +func hasX(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] == 'x' || s[i] == 'X' { + return true + } + } + return false +} + +// convertFloat converts the string to a float64value. +func (s *ss) convertFloat(str string, n int) float64 { + // strconv.ParseFloat will handle "+0x1.fp+2", + // but we have to implement our non-standard + // decimal+binary exponent mix (1.2p4) ourselves. + if p := indexRune(str, 'p'); p >= 0 && !hasX(str) { + // Atof doesn't handle power-of-2 exponents, + // but they're easy to evaluate. + f, err := strconv.ParseFloat(str[:p], n) + if err != nil { + // Put full string into error. + if e, ok := err.(*strconv.NumError); ok { + e.Num = str + } + s.error(err) + } + m, err := strconv.Atoi(str[p+1:]) + if err != nil { + // Put full string into error. + if e, ok := err.(*strconv.NumError); ok { + e.Num = str + } + s.error(err) + } + return math.Ldexp(f, m) + } + f, err := strconv.ParseFloat(str, n) + if err != nil { + s.error(err) + } + return f +} + +// scanComplex converts the next token to a complex128 value. +// The atof argument is a type-specific reader for the underlying type. +// If we're reading complex64, atof will parse float32s and convert them +// to float64's to avoid reproducing this code for each complex type. +func (s *ss) scanComplex(verb rune, n int) complex128 { + if !s.okVerb(verb, floatVerbs, "complex") { + return 0 + } + s.SkipSpace() + s.notEOF() + sreal, simag := s.complexTokens() + real := s.convertFloat(sreal, n/2) + imag := s.convertFloat(simag, n/2) + return complex(real, imag) +} + +// convertString returns the string represented by the next input characters. +// The format of the input is determined by the verb. +func (s *ss) convertString(verb rune) (str string) { + if !s.okVerb(verb, "svqxX", "string") { + return "" + } + s.SkipSpace() + s.notEOF() + switch verb { + case 'q': + str = s.quotedString() + case 'x', 'X': + str = s.hexString() + default: + str = string(s.token(true, notSpace)) // %s and %v just return the next word + } + return +} + +// quotedString returns the double- or back-quoted string represented by the next input characters. +func (s *ss) quotedString() string { + s.notEOF() + quote := s.getRune() + switch quote { + case '`': + // Back-quoted: Anything goes until EOF or back quote. + for { + r := s.mustReadRune() + if r == quote { + break + } + s.buf.writeRune(r) + } + return string(s.buf) + case '"': + // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes. + s.buf.writeByte('"') + for { + r := s.mustReadRune() + s.buf.writeRune(r) + if r == '\\' { + // In a legal backslash escape, no matter how long, only the character + // immediately after the escape can itself be a backslash or quote. + // Thus we only need to protect the first character after the backslash. + s.buf.writeRune(s.mustReadRune()) + } else if r == '"' { + break + } + } + result, err := strconv.Unquote(string(s.buf)) + if err != nil { + s.error(err) + } + return result + default: + s.errorString("expected quoted string") + } + return "" +} + +// hexDigit returns the value of the hexadecimal digit. +func hexDigit(d rune) (int, bool) { + digit := int(d) + switch digit { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return digit - '0', true + case 'a', 'b', 'c', 'd', 'e', 'f': + return 10 + digit - 'a', true + case 'A', 'B', 'C', 'D', 'E', 'F': + return 10 + digit - 'A', true + } + return -1, false +} + +// hexByte returns the next hex-encoded (two-character) byte from the input. +// It returns ok==false if the next bytes in the input do not encode a hex byte. +// If the first byte is hex and the second is not, processing stops. +func (s *ss) hexByte() (b byte, ok bool) { + rune1 := s.getRune() + if rune1 == eof { + return + } + value1, ok := hexDigit(rune1) + if !ok { + s.UnreadRune() + return + } + value2, ok := hexDigit(s.mustReadRune()) + if !ok { + s.errorString("illegal hex digit") + return + } + return byte(value1<<4 | value2), true +} + +// hexString returns the space-delimited hexpair-encoded string. +func (s *ss) hexString() string { + s.notEOF() + for { + b, ok := s.hexByte() + if !ok { + break + } + s.buf.writeByte(b) + } + if len(s.buf) == 0 { + s.errorString("no hex data for %x string") + return "" + } + return string(s.buf) +} + +const ( + floatVerbs = "beEfFgGv" + + hugeWid = 1 << 30 + + intBits = 32 << (^uint(0) >> 63) + uintptrBits = 32 << (^uintptr(0) >> 63) +) + +// scanPercent scans a literal percent character. +func (s *ss) scanPercent() { + s.SkipSpace() + s.notEOF() + if !s.accept("%") { + s.errorString("missing literal %") + } +} + +// scanOne scans a single value, deriving the scanner from the type of the argument. +func (s *ss) scanOne(verb rune, arg any) { + s.buf = s.buf[:0] + var err error + // If the parameter has its own Scan method, use that. + if v, ok := arg.(Scanner); ok { + err = v.Scan(s, verb) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + s.error(err) + } + return + } + + switch v := arg.(type) { + case *bool: + *v = s.scanBool(verb) + case *complex64: + *v = complex64(s.scanComplex(verb, 64)) + case *complex128: + *v = s.scanComplex(verb, 128) + case *int: + *v = int(s.scanInt(verb, intBits)) + case *int8: + *v = int8(s.scanInt(verb, 8)) + case *int16: + *v = int16(s.scanInt(verb, 16)) + case *int32: + *v = int32(s.scanInt(verb, 32)) + case *int64: + *v = s.scanInt(verb, 64) + case *uint: + *v = uint(s.scanUint(verb, intBits)) + case *uint8: + *v = uint8(s.scanUint(verb, 8)) + case *uint16: + *v = uint16(s.scanUint(verb, 16)) + case *uint32: + *v = uint32(s.scanUint(verb, 32)) + case *uint64: + *v = s.scanUint(verb, 64) + case *uintptr: + *v = uintptr(s.scanUint(verb, uintptrBits)) + // Floats are tricky because you want to scan in the precision of the result, not + // scan in high precision and convert, in order to preserve the correct error condition. + case *float32: + if s.okVerb(verb, floatVerbs, "float32") { + s.SkipSpace() + s.notEOF() + *v = float32(s.convertFloat(s.floatToken(), 32)) + } + case *float64: + if s.okVerb(verb, floatVerbs, "float64") { + s.SkipSpace() + s.notEOF() + *v = s.convertFloat(s.floatToken(), 64) + } + case *string: + *v = s.convertString(verb) + case *[]byte: + // We scan to string and convert so we get a copy of the data. + // If we scanned to bytes, the slice would point at the buffer. + *v = []byte(s.convertString(verb)) + default: + val := reflect.ValueOf(v) + ptr := val + if ptr.Kind() != reflect.Pointer { + s.errorString("type not a pointer: " + val.Type().String()) + return + } + switch v := ptr.Elem(); v.Kind() { + case reflect.Bool: + v.SetBool(s.scanBool(verb)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v.SetInt(s.scanInt(verb, v.Type().Bits())) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + v.SetUint(s.scanUint(verb, v.Type().Bits())) + case reflect.String: + v.SetString(s.convertString(verb)) + case reflect.Slice: + // For now, can only handle (renamed) []byte. + typ := v.Type() + if typ.Elem().Kind() != reflect.Uint8 { + s.errorString("can't scan type: " + val.Type().String()) + } + str := s.convertString(verb) + v.Set(reflect.MakeSlice(typ, len(str), len(str))) + for i := 0; i < len(str); i++ { + v.Index(i).SetUint(uint64(str[i])) + } + case reflect.Float32, reflect.Float64: + s.SkipSpace() + s.notEOF() + v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits())) + case reflect.Complex64, reflect.Complex128: + v.SetComplex(s.scanComplex(verb, v.Type().Bits())) + default: + s.errorString("can't scan type: " + val.Type().String()) + } + } +} + +// errorHandler turns local panics into error returns. +func errorHandler(errp *error) { + if e := recover(); e != nil { + if se, ok := e.(scanError); ok { // catch local error + *errp = se.err + } else if eof, ok := e.(error); ok && eof == io.EOF { // out of input + *errp = eof + } else { + panic(e) + } + } +} + +// doScan does the real work for scanning without a format string. +func (s *ss) doScan(a []any) (numProcessed int, err error) { + defer errorHandler(&err) + for _, arg := range a { + s.scanOne('v', arg) + numProcessed++ + } + // Check for newline (or EOF) if required (Scanln etc.). + if s.nlIsEnd { + for { + r := s.getRune() + if r == '\n' || r == eof { + break + } + if !isSpace(r) { + s.errorString("expected newline") + break + } + } + } + return +} + +// advance determines whether the next characters in the input match +// those of the format. It returns the number of bytes (sic) consumed +// in the format. All runs of space characters in either input or +// format behave as a single space. Newlines are special, though: +// newlines in the format must match those in the input and vice versa. +// This routine also handles the %% case. If the return value is zero, +// either format starts with a % (with no following %) or the input +// is empty. If it is negative, the input did not match the string. +func (s *ss) advance(format string) (i int) { + for i < len(format) { + fmtc, w := utf8.DecodeRuneInString(format[i:]) + + // Space processing. + // In the rest of this comment "space" means spaces other than newline. + // Newline in the format matches input of zero or more spaces and then newline or end-of-input. + // Spaces in the format before the newline are collapsed into the newline. + // Spaces in the format after the newline match zero or more spaces after the corresponding input newline. + // Other spaces in the format match input of one or more spaces or end-of-input. + if isSpace(fmtc) { + newlines := 0 + trailingSpace := false + for isSpace(fmtc) && i < len(format) { + if fmtc == '\n' { + newlines++ + trailingSpace = false + } else { + trailingSpace = true + } + i += w + fmtc, w = utf8.DecodeRuneInString(format[i:]) + } + for j := 0; j < newlines; j++ { + inputc := s.getRune() + for isSpace(inputc) && inputc != '\n' { + inputc = s.getRune() + } + if inputc != '\n' && inputc != eof { + s.errorString("newline in format does not match input") + } + } + if trailingSpace { + inputc := s.getRune() + if newlines == 0 { + // If the trailing space stood alone (did not follow a newline), + // it must find at least one space to consume. + if !isSpace(inputc) && inputc != eof { + s.errorString("expected space in input to match format") + } + if inputc == '\n' { + s.errorString("newline in input does not match format") + } + } + for isSpace(inputc) && inputc != '\n' { + inputc = s.getRune() + } + if inputc != eof { + s.UnreadRune() + } + } + continue + } + + // Verbs. + if fmtc == '%' { + // % at end of string is an error. + if i+w == len(format) { + s.errorString("missing verb: % at end of format string") + } + // %% acts like a real percent + nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // will not match % if string is empty + if nextc != '%' { + return + } + i += w // skip the first % + } + + // Literals. + inputc := s.mustReadRune() + if fmtc != inputc { + s.UnreadRune() + return -1 + } + i += w + } + return +} + +// doScanf does the real work when scanning with a format string. +// At the moment, it handles only pointers to basic types. +func (s *ss) doScanf(format string, a []any) (numProcessed int, err error) { + defer errorHandler(&err) + end := len(format) - 1 + // We process one item per non-trivial format + for i := 0; i <= end; { + w := s.advance(format[i:]) + if w > 0 { + i += w + continue + } + // Either we failed to advance, we have a percent character, or we ran out of input. + if format[i] != '%' { + // Can't advance format. Why not? + if w < 0 { + s.errorString("input does not match format") + } + // Otherwise at EOF; "too many operands" error handled below + break + } + i++ // % is one byte + + // do we have 20 (width)? + var widPresent bool + s.maxWid, widPresent, i = parsenum(format, i, end) + if !widPresent { + s.maxWid = hugeWid + } + + c, w := utf8.DecodeRuneInString(format[i:]) + i += w + + if c != 'c' { + s.SkipSpace() + } + if c == '%' { + s.scanPercent() + continue // Do not consume an argument. + } + s.argLimit = s.limit + if f := s.count + s.maxWid; f < s.argLimit { + s.argLimit = f + } + + if numProcessed >= len(a) { // out of operands + s.errorString("too few operands for format '%" + format[i-w:] + "'") + break + } + arg := a[numProcessed] + + s.scanOne(c, arg) + numProcessed++ + s.argLimit = s.limit + } + if numProcessed < len(a) { + s.errorString("too many operands") + } + return +} diff --git a/src/fmt/scan_test.go b/src/fmt/scan_test.go new file mode 100644 index 0000000..a4f80c2 --- /dev/null +++ b/src/fmt/scan_test.go @@ -0,0 +1,1334 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "bufio" + "bytes" + "errors" + . "fmt" + "io" + "math" + "reflect" + "regexp" + "strings" + "testing" + "testing/iotest" + "unicode/utf8" +) + +type ScanTest struct { + text string + in any + out any +} + +type ScanfTest struct { + format string + text string + in any + out any +} + +type ScanfMultiTest struct { + format string + text string + in []any + out []any + err string +} + +var ( + boolVal bool + intVal int + int8Val int8 + int16Val int16 + int32Val int32 + int64Val int64 + uintVal uint + uint8Val uint8 + uint16Val uint16 + uint32Val uint32 + uint64Val uint64 + uintptrVal uintptr + float32Val float32 + float64Val float64 + stringVal string + bytesVal []byte + runeVal rune + complex64Val complex64 + complex128Val complex128 + renamedBoolVal renamedBool + renamedIntVal renamedInt + renamedInt8Val renamedInt8 + renamedInt16Val renamedInt16 + renamedInt32Val renamedInt32 + renamedInt64Val renamedInt64 + renamedUintVal renamedUint + renamedUint8Val renamedUint8 + renamedUint16Val renamedUint16 + renamedUint32Val renamedUint32 + renamedUint64Val renamedUint64 + renamedUintptrVal renamedUintptr + renamedStringVal renamedString + renamedBytesVal renamedBytes + renamedFloat32Val renamedFloat32 + renamedFloat64Val renamedFloat64 + renamedComplex64Val renamedComplex64 + renamedComplex128Val renamedComplex128 +) + +// Xs accepts any non-empty run of the verb character +type Xs string + +func (x *Xs) Scan(state ScanState, verb rune) error { + tok, err := state.Token(true, func(r rune) bool { return r == verb }) + if err != nil { + return err + } + s := string(tok) + if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(s) { + return errors.New("syntax error for xs") + } + *x = Xs(s) + return nil +} + +var xVal Xs + +// IntString accepts an integer followed immediately by a string. +// It tests the embedding of a scan within a scan. +type IntString struct { + i int + s string +} + +func (s *IntString) Scan(state ScanState, verb rune) error { + if _, err := Fscan(state, &s.i); err != nil { + return err + } + + tok, err := state.Token(true, nil) + if err != nil { + return err + } + s.s = string(tok) + return nil +} + +var intStringVal IntString + +var scanTests = []ScanTest{ + // Basic types + {"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written + {"F\n", &boolVal, false}, // restored to zero value + {"21\n", &intVal, 21}, + {"2_1\n", &intVal, 21}, + {"0\n", &intVal, 0}, + {"000\n", &intVal, 0}, + {"0x10\n", &intVal, 0x10}, + {"0x_1_0\n", &intVal, 0x10}, + {"-0x10\n", &intVal, -0x10}, + {"0377\n", &intVal, 0377}, + {"0_3_7_7\n", &intVal, 0377}, + {"0o377\n", &intVal, 0377}, + {"0o_3_7_7\n", &intVal, 0377}, + {"-0377\n", &intVal, -0377}, + {"-0o377\n", &intVal, -0377}, + {"0\n", &uintVal, uint(0)}, + {"000\n", &uintVal, uint(0)}, + {"0x10\n", &uintVal, uint(0x10)}, + {"0377\n", &uintVal, uint(0377)}, + {"22\n", &int8Val, int8(22)}, + {"23\n", &int16Val, int16(23)}, + {"24\n", &int32Val, int32(24)}, + {"25\n", &int64Val, int64(25)}, + {"127\n", &int8Val, int8(127)}, + {"-21\n", &intVal, -21}, + {"-22\n", &int8Val, int8(-22)}, + {"-23\n", &int16Val, int16(-23)}, + {"-24\n", &int32Val, int32(-24)}, + {"-25\n", &int64Val, int64(-25)}, + {"-128\n", &int8Val, int8(-128)}, + {"+21\n", &intVal, +21}, + {"+22\n", &int8Val, int8(+22)}, + {"+23\n", &int16Val, int16(+23)}, + {"+24\n", &int32Val, int32(+24)}, + {"+25\n", &int64Val, int64(+25)}, + {"+127\n", &int8Val, int8(+127)}, + {"26\n", &uintVal, uint(26)}, + {"27\n", &uint8Val, uint8(27)}, + {"28\n", &uint16Val, uint16(28)}, + {"29\n", &uint32Val, uint32(29)}, + {"30\n", &uint64Val, uint64(30)}, + {"31\n", &uintptrVal, uintptr(31)}, + {"255\n", &uint8Val, uint8(255)}, + {"32767\n", &int16Val, int16(32767)}, + {"2.3\n", &float64Val, 2.3}, + {"2.3e1\n", &float32Val, float32(2.3e1)}, + {"2.3e2\n", &float64Val, 2.3e2}, + {"2.3p2\n", &float64Val, 2.3 * 4}, + {"2.3p+2\n", &float64Val, 2.3 * 4}, + {"2.3p+66\n", &float64Val, 2.3 * (1 << 66)}, + {"2.3p-66\n", &float64Val, 2.3 / (1 << 66)}, + {"0x2.3p-66\n", &float64Val, float64(0x23) / (1 << 70)}, + {"2_3.4_5\n", &float64Val, 23.45}, + {"2.35\n", &stringVal, "2.35"}, + {"2345678\n", &bytesVal, []byte("2345678")}, + {"(3.4e1-2i)\n", &complex128Val, 3.4e1 - 2i}, + {"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, + {"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, + {"-.4_5e1-1E2i\n", &complex128Val, complex128(-.45e1 - 100i)}, + {"0x1.0p1+0x1.0P2i\n", &complex128Val, complex128(2 + 4i)}, + {"-0x1p1-0x1p2i\n", &complex128Val, complex128(-2 - 4i)}, + {"-0x1ep-1-0x1p2i\n", &complex128Val, complex128(-15 - 4i)}, + {"-0x1_Ep-1-0x1p0_2i\n", &complex128Val, complex128(-15 - 4i)}, + {"hello\n", &stringVal, "hello"}, + + // Carriage-return followed by newline. (We treat \r\n as \n always.) + {"hello\r\n", &stringVal, "hello"}, + {"27\r\n", &uint8Val, uint8(27)}, + + // Renamed types + {"true\n", &renamedBoolVal, renamedBool(true)}, + {"F\n", &renamedBoolVal, renamedBool(false)}, + {"101\n", &renamedIntVal, renamedInt(101)}, + {"102\n", &renamedIntVal, renamedInt(102)}, + {"103\n", &renamedUintVal, renamedUint(103)}, + {"104\n", &renamedUintVal, renamedUint(104)}, + {"105\n", &renamedInt8Val, renamedInt8(105)}, + {"106\n", &renamedInt16Val, renamedInt16(106)}, + {"107\n", &renamedInt32Val, renamedInt32(107)}, + {"108\n", &renamedInt64Val, renamedInt64(108)}, + {"109\n", &renamedUint8Val, renamedUint8(109)}, + {"110\n", &renamedUint16Val, renamedUint16(110)}, + {"111\n", &renamedUint32Val, renamedUint32(111)}, + {"112\n", &renamedUint64Val, renamedUint64(112)}, + {"113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"114\n", &renamedStringVal, renamedString("114")}, + {"115\n", &renamedBytesVal, renamedBytes([]byte("115"))}, + + // Custom scanners. + {" vvv ", &xVal, Xs("vvv")}, + {" 1234hello", &intStringVal, IntString{1234, "hello"}}, + + // Fixed bugs + {"2147483648\n", &int64Val, int64(2147483648)}, // was: integer overflow +} + +var scanfTests = []ScanfTest{ + {"%v", "TRUE\n", &boolVal, true}, + {"%t", "false\n", &boolVal, false}, + {"%v", "-71\n", &intVal, -71}, + {"%v", "-7_1\n", &intVal, -71}, + {"%v", "0b111\n", &intVal, 7}, + {"%v", "0b_1_1_1\n", &intVal, 7}, + {"%v", "0377\n", &intVal, 0377}, + {"%v", "0_3_7_7\n", &intVal, 0377}, + {"%v", "0o377\n", &intVal, 0377}, + {"%v", "0o_3_7_7\n", &intVal, 0377}, + {"%v", "0x44\n", &intVal, 0x44}, + {"%v", "0x_4_4\n", &intVal, 0x44}, + {"%d", "72\n", &intVal, 72}, + {"%c", "a\n", &runeVal, 'a'}, + {"%c", "\u5072\n", &runeVal, '\u5072'}, + {"%c", "\u1234\n", &runeVal, '\u1234'}, + {"%d", "73\n", &int8Val, int8(73)}, + {"%d", "+74\n", &int16Val, int16(74)}, + {"%d", "75\n", &int32Val, int32(75)}, + {"%d", "76\n", &int64Val, int64(76)}, + {"%b", "1001001\n", &intVal, 73}, + {"%o", "075\n", &intVal, 075}, + {"%x", "a75\n", &intVal, 0xa75}, + {"%v", "71\n", &uintVal, uint(71)}, + {"%d", "72\n", &uintVal, uint(72)}, + {"%d", "7_2\n", &uintVal, uint(7)}, // only %v takes underscores + {"%d", "73\n", &uint8Val, uint8(73)}, + {"%d", "74\n", &uint16Val, uint16(74)}, + {"%d", "75\n", &uint32Val, uint32(75)}, + {"%d", "76\n", &uint64Val, uint64(76)}, + {"%d", "77\n", &uintptrVal, uintptr(77)}, + {"%b", "1001001\n", &uintVal, uint(73)}, + {"%b", "100_1001\n", &uintVal, uint(4)}, + {"%o", "075\n", &uintVal, uint(075)}, + {"%o", "07_5\n", &uintVal, uint(07)}, // only %v takes underscores + {"%x", "a75\n", &uintVal, uint(0xa75)}, + {"%x", "A75\n", &uintVal, uint(0xa75)}, + {"%x", "A7_5\n", &uintVal, uint(0xa7)}, // only %v takes underscores + {"%U", "U+1234\n", &intVal, int(0x1234)}, + {"%U", "U+4567\n", &uintVal, uint(0x4567)}, + + {"%e", "2.3\n", &float64Val, 2.3}, + {"%E", "2.3e1\n", &float32Val, float32(2.3e1)}, + {"%f", "2.3e2\n", &float64Val, 2.3e2}, + {"%g", "2.3p2\n", &float64Val, 2.3 * 4}, + {"%G", "2.3p+2\n", &float64Val, 2.3 * 4}, + {"%v", "2.3p+66\n", &float64Val, 2.3 * (1 << 66)}, + {"%f", "2.3p-66\n", &float64Val, 2.3 / (1 << 66)}, + {"%G", "0x2.3p-66\n", &float64Val, float64(0x23) / (1 << 70)}, + {"%E", "2_3.4_5\n", &float64Val, 23.45}, + + // Strings + {"%s", "using-%s\n", &stringVal, "using-%s"}, + {"%x", "7573696e672d2578\n", &stringVal, "using-%x"}, + {"%X", "7573696E672D2558\n", &stringVal, "using-%X"}, + {"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"}, + {"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"}, + + // Byte slices + {"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")}, + {"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")}, + {"%X", "62797465732D2558\n", &bytesVal, []byte("bytes-%X")}, + {"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")}, + {"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")}, + + // Renamed types + {"%v\n", "true\n", &renamedBoolVal, renamedBool(true)}, + {"%t\n", "F\n", &renamedBoolVal, renamedBool(false)}, + {"%v", "101\n", &renamedIntVal, renamedInt(101)}, + {"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')}, + {"%o", "0146\n", &renamedIntVal, renamedInt(102)}, + {"%v", "103\n", &renamedUintVal, renamedUint(103)}, + {"%d", "104\n", &renamedUintVal, renamedUint(104)}, + {"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, + {"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, + {"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, + {"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, + {"%x", "6D\n", &renamedUint8Val, renamedUint8(109)}, + {"%o", "0156\n", &renamedUint16Val, renamedUint16(110)}, + {"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, + {"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, + {"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"%s", "114\n", &renamedStringVal, renamedString("114")}, + {"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))}, + {"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)}, + {"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)}, + {"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)}, + {"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)}, + + // Interesting formats + {"here is\tthe value:%d", "here is the\tvalue:118\n", &intVal, 118}, + {"%% %%:%d", "% %:119\n", &intVal, 119}, + {"%d%%", "42%", &intVal, 42}, // %% at end of string. + + // Corner cases + {"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)}, + + // Custom scanner. + {"%s", " sss ", &xVal, Xs("sss")}, + {"%2s", "sssss", &xVal, Xs("ss")}, + + // Fixed bugs + {"%d\n", "27\n", &intVal, 27}, // ok + {"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline" + {"%v", "0", &intVal, 0}, // was: "EOF"; 0 was taken as base prefix and not counted. + {"%v", "0", &uintVal, uint(0)}, // was: "EOF"; 0 was taken as base prefix and not counted. + {"%c", " ", &uintVal, uint(' ')}, // %c must accept a blank. + {"%c", "\t", &uintVal, uint('\t')}, // %c must accept any space. + {"%c", "\n", &uintVal, uint('\n')}, // %c must accept any space. + {"%d%%", "23%\n", &uintVal, uint(23)}, // %% matches literal %. + {"%%%d", "%23\n", &uintVal, uint(23)}, // %% matches literal %. + + // space handling + {"%d", "27", &intVal, 27}, + {"%d", "27 ", &intVal, 27}, + {"%d", " 27", &intVal, 27}, + {"%d", " 27 ", &intVal, 27}, + + {"X%d", "X27", &intVal, 27}, + {"X%d", "X27 ", &intVal, 27}, + {"X%d", "X 27", &intVal, 27}, + {"X%d", "X 27 ", &intVal, 27}, + + {"X %d", "X27", &intVal, nil}, // expected space in input to match format + {"X %d", "X27 ", &intVal, nil}, // expected space in input to match format + {"X %d", "X 27", &intVal, 27}, + {"X %d", "X 27 ", &intVal, 27}, + + {"%dX", "27X", &intVal, 27}, + {"%dX", "27 X", &intVal, nil}, // input does not match format + {"%dX", " 27X", &intVal, 27}, + {"%dX", " 27 X", &intVal, nil}, // input does not match format + + {"%d X", "27X", &intVal, nil}, // expected space in input to match format + {"%d X", "27 X", &intVal, 27}, + {"%d X", " 27X", &intVal, nil}, // expected space in input to match format + {"%d X", " 27 X", &intVal, 27}, + + {"X %d X", "X27X", &intVal, nil}, // expected space in input to match format + {"X %d X", "X27 X", &intVal, nil}, // expected space in input to match format + {"X %d X", "X 27X", &intVal, nil}, // expected space in input to match format + {"X %d X", "X 27 X", &intVal, 27}, + + {"X %s X", "X27X", &stringVal, nil}, // expected space in input to match format + {"X %s X", "X27 X", &stringVal, nil}, // expected space in input to match format + {"X %s X", "X 27X", &stringVal, nil}, // unexpected EOF + {"X %s X", "X 27 X", &stringVal, "27"}, + + {"X%sX", "X27X", &stringVal, nil}, // unexpected EOF + {"X%sX", "X27 X", &stringVal, nil}, // input does not match format + {"X%sX", "X 27X", &stringVal, nil}, // unexpected EOF + {"X%sX", "X 27 X", &stringVal, nil}, // input does not match format + + {"X%s", "X27", &stringVal, "27"}, + {"X%s", "X27 ", &stringVal, "27"}, + {"X%s", "X 27", &stringVal, "27"}, + {"X%s", "X 27 ", &stringVal, "27"}, + + {"X%dX", "X27X", &intVal, 27}, + {"X%dX", "X27 X", &intVal, nil}, // input does not match format + {"X%dX", "X 27X", &intVal, 27}, + {"X%dX", "X 27 X", &intVal, nil}, // input does not match format + + {"X%dX", "X27X", &intVal, 27}, + {"X%dX", "X27X ", &intVal, 27}, + {"X%dX", " X27X", &intVal, nil}, // input does not match format + {"X%dX", " X27X ", &intVal, nil}, // input does not match format + + {"X%dX\n", "X27X", &intVal, 27}, + {"X%dX \n", "X27X ", &intVal, 27}, + {"X%dX\n", "X27X\n", &intVal, 27}, + {"X%dX\n", "X27X \n", &intVal, 27}, + + {"X%dX \n", "X27X", &intVal, 27}, + {"X%dX \n", "X27X ", &intVal, 27}, + {"X%dX \n", "X27X\n", &intVal, 27}, + {"X%dX \n", "X27X \n", &intVal, 27}, + + {"X%c", "X\n", &runeVal, '\n'}, + {"X%c", "X \n", &runeVal, ' '}, + {"X %c", "X!", &runeVal, nil}, // expected space in input to match format + {"X %c", "X\n", &runeVal, nil}, // newline in input does not match format + {"X %c", "X !", &runeVal, '!'}, + {"X %c", "X \n", &runeVal, '\n'}, + + {" X%dX", "X27X", &intVal, nil}, // expected space in input to match format + {" X%dX", "X27X ", &intVal, nil}, // expected space in input to match format + {" X%dX", " X27X", &intVal, 27}, + {" X%dX", " X27X ", &intVal, 27}, + + {"X%dX ", "X27X", &intVal, 27}, + {"X%dX ", "X27X ", &intVal, 27}, + {"X%dX ", " X27X", &intVal, nil}, // input does not match format + {"X%dX ", " X27X ", &intVal, nil}, // input does not match format + + {" X%dX ", "X27X", &intVal, nil}, // expected space in input to match format + {" X%dX ", "X27X ", &intVal, nil}, // expected space in input to match format + {" X%dX ", " X27X", &intVal, 27}, + {" X%dX ", " X27X ", &intVal, 27}, + + {"%d\nX", "27\nX", &intVal, 27}, + {"%dX\n X", "27X\n X", &intVal, 27}, +} + +var overflowTests = []ScanTest{ + {"128", &int8Val, 0}, + {"32768", &int16Val, 0}, + {"-129", &int8Val, 0}, + {"-32769", &int16Val, 0}, + {"256", &uint8Val, 0}, + {"65536", &uint16Val, 0}, + {"1e100", &float32Val, 0}, + {"1e500", &float64Val, 0}, + {"(1e100+0i)", &complex64Val, 0}, + {"(1+1e100i)", &complex64Val, 0}, + {"(1-1e500i)", &complex128Val, 0}, +} + +var truth bool +var i, j, k int +var f float64 +var s, t string +var c complex128 +var x, y Xs +var z IntString +var r1, r2, r3 rune + +var multiTests = []ScanfMultiTest{ + {"", "", []any{}, []any{}, ""}, + {"%d", "23", args(&i), args(23), ""}, + {"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, + {"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, + {"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""}, + {"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""}, + {"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""}, + {"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), 2.5), ""}, + {"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""}, + {"%c%c%c", "2\u50c2X", args(&r1, &r2, &r3), args('2', '\u50c2', 'X'), ""}, + {"%5s%d", " 1234567 ", args(&s, &i), args("12345", 67), ""}, + {"%5s%d", " 12 34 567 ", args(&s, &i), args("12", 34), ""}, + + // Custom scanners. + {"%e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""}, + {"%4v%s", "12abcd", args(&z, &s), args(IntString{12, "ab"}, "cd"), ""}, + + // Errors + {"%t", "23 18", args(&i), nil, "bad verb"}, + {"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"}, + {"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"}, + {"%c", "\u0100", args(&int8Val), nil, "overflow"}, + {"X%d", "10X", args(&intVal), nil, "input does not match format"}, + {"%d%", "42%", args(&intVal), args(42), "missing verb: % at end of format string"}, + {"%d% ", "42%", args(&intVal), args(42), "too few operands for format '% '"}, // Slightly odd error, but correct. + {"%%%d", "xxx 42", args(&intVal), args(42), "missing literal %"}, + {"%%%d", "x42", args(&intVal), args(42), "missing literal %"}, + {"%%%d", "42", args(&intVal), args(42), "missing literal %"}, + + // Bad UTF-8: should see every byte. + {"%c%c%c", "\xc2X\xc2", args(&r1, &r2, &r3), args(utf8.RuneError, 'X', utf8.RuneError), ""}, + + // Fixed bugs + {"%v%v", "FALSE23", args(&truth, &i), args(false, 23), ""}, +} + +var readers = []struct { + name string + f func(string) io.Reader +}{ + {"StringReader", func(s string) io.Reader { + return strings.NewReader(s) + }}, + {"ReaderOnly", func(s string) io.Reader { + return struct{ io.Reader }{strings.NewReader(s)} + }}, + {"OneByteReader", func(s string) io.Reader { + return iotest.OneByteReader(strings.NewReader(s)) + }}, + {"DataErrReader", func(s string) io.Reader { + return iotest.DataErrReader(strings.NewReader(s)) + }}, +} + +func testScan(t *testing.T, f func(string) io.Reader, scan func(r io.Reader, a ...any) (int, error)) { + for _, test := range scanTests { + r := f(test.text) + n, err := scan(r, test.in) + if err != nil { + m := "" + if n > 0 { + m = Sprintf(" (%d fields ok)", n) + } + t.Errorf("got error scanning %q: %s%s", test.text, err, m) + continue + } + if n != 1 { + t.Errorf("count error on entry %q: got %d", test.text, n) + continue + } + // The incoming value may be a pointer + v := reflect.ValueOf(test.in) + if p := v; p.Kind() == reflect.Pointer { + v = p.Elem() + } + val := v.Interface() + if !reflect.DeepEqual(val, test.out) { + t.Errorf("scanning %q: expected %#v got %#v, type %T", test.text, test.out, val, val) + } + } +} + +func TestScan(t *testing.T) { + for _, r := range readers { + t.Run(r.name, func(t *testing.T) { + testScan(t, r.f, Fscan) + }) + } +} + +func TestScanln(t *testing.T) { + for _, r := range readers { + t.Run(r.name, func(t *testing.T) { + testScan(t, r.f, Fscanln) + }) + } +} + +func TestScanf(t *testing.T) { + for _, test := range scanfTests { + n, err := Sscanf(test.text, test.format, test.in) + if err != nil { + if test.out != nil { + t.Errorf("Sscanf(%q, %q): unexpected error: %v", test.text, test.format, err) + } + continue + } + if test.out == nil { + t.Errorf("Sscanf(%q, %q): unexpected success", test.text, test.format) + continue + } + if n != 1 { + t.Errorf("Sscanf(%q, %q): parsed %d field, want 1", test.text, test.format, n) + continue + } + // The incoming value may be a pointer + v := reflect.ValueOf(test.in) + if p := v; p.Kind() == reflect.Pointer { + v = p.Elem() + } + val := v.Interface() + if !reflect.DeepEqual(val, test.out) { + t.Errorf("Sscanf(%q, %q): parsed value %T(%#v), want %T(%#v)", test.text, test.format, val, val, test.out, test.out) + } + } +} + +func TestScanOverflow(t *testing.T) { + // different machines and different types report errors with different strings. + re := regexp.MustCompile("overflow|too large|out of range|not representable") + for _, test := range overflowTests { + _, err := Sscan(test.text, test.in) + if err == nil { + t.Errorf("expected overflow scanning %q", test.text) + continue + } + if !re.MatchString(err.Error()) { + t.Errorf("expected overflow error scanning %q: %s", test.text, err) + } + } +} + +func verifyNaN(str string, t *testing.T) { + var f float64 + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + if !math.IsNaN(float64(f)) || !math.IsNaN(float64(f32)) || !math.IsNaN(f64) { + t.Errorf("didn't get NaNs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + +func TestNaN(t *testing.T) { + for _, s := range []string{"nan", "NAN", "NaN"} { + verifyNaN(s, t) + } +} + +func verifyInf(str string, t *testing.T) { + var f float64 + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + sign := 1 + if str[0] == '-' { + sign = -1 + } + if !math.IsInf(float64(f), sign) || !math.IsInf(float64(f32), sign) || !math.IsInf(f64, sign) { + t.Errorf("didn't get right Infs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + +func TestInf(t *testing.T) { + for _, s := range []string{"inf", "+inf", "-inf", "INF", "-INF", "+INF", "Inf", "-Inf", "+Inf"} { + verifyInf(s, t) + } +} + +func testScanfMulti(t *testing.T, f func(string) io.Reader) { + sliceType := reflect.TypeOf(make([]any, 1)) + for _, test := range multiTests { + r := f(test.text) + n, err := Fscanf(r, test.format, test.in...) + if err != nil { + if test.err == "" { + t.Errorf("got error scanning (%q, %q): %q", test.format, test.text, err) + } else if !strings.Contains(err.Error(), test.err) { + t.Errorf("got wrong error scanning (%q, %q): %q; expected %q", test.format, test.text, err, test.err) + } + continue + } + if test.err != "" { + t.Errorf("expected error %q error scanning (%q, %q)", test.err, test.format, test.text) + } + if n != len(test.out) { + t.Errorf("count error on entry (%q, %q): expected %d got %d", test.format, test.text, len(test.out), n) + continue + } + // Convert the slice of pointers into a slice of values + resultVal := reflect.MakeSlice(sliceType, n, n) + for i := 0; i < n; i++ { + v := reflect.ValueOf(test.in[i]).Elem() + resultVal.Index(i).Set(v) + } + result := resultVal.Interface() + if !reflect.DeepEqual(result, test.out) { + t.Errorf("scanning (%q, %q): expected %#v got %#v", test.format, test.text, test.out, result) + } + } +} + +func TestScanfMulti(t *testing.T) { + for _, r := range readers { + t.Run(r.name, func(t *testing.T) { + testScanfMulti(t, r.f) + }) + } +} + +func TestScanMultiple(t *testing.T) { + var a int + var s string + n, err := Sscan("123abc", &a, &s) + if n != 2 { + t.Errorf("Sscan count error: expected 2: got %d", n) + } + if err != nil { + t.Errorf("Sscan expected no error; got %s", err) + } + if a != 123 || s != "abc" { + t.Errorf("Sscan wrong values: got (%d %q) expected (123 \"abc\")", a, s) + } + n, err = Sscan("asdf", &s, &a) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Errorf("Sscan expected error; got none: %s", err) + } + if s != "asdf" { + t.Errorf("Sscan wrong values: got %q expected \"asdf\"", s) + } +} + +// Empty strings are not valid input when scanning a string. +func TestScanEmpty(t *testing.T) { + var s1, s2 string + n, err := Sscan("abc", &s1, &s2) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Error("Sscan <one item> expected error; got none") + } + if s1 != "abc" { + t.Errorf("Sscan wrong values: got %q expected \"abc\"", s1) + } + n, err = Sscan("", &s1, &s2) + if n != 0 { + t.Errorf("Sscan count error: expected 0: got %d", n) + } + if err == nil { + t.Error("Sscan <empty> expected error; got none") + } + // Quoted empty string is OK. + n, err = Sscanf(`""`, "%q", &s1) + if n != 1 { + t.Errorf("Sscanf count error: expected 1: got %d", n) + } + if err != nil { + t.Errorf("Sscanf <empty> expected no error with quoted string; got %s", err) + } +} + +func TestScanNotPointer(t *testing.T) { + r := strings.NewReader("1") + var a int + _, err := Fscan(r, a) + if err == nil { + t.Error("expected error scanning non-pointer") + } else if !strings.Contains(err.Error(), "pointer") { + t.Errorf("expected pointer error scanning non-pointer, got: %s", err) + } +} + +func TestScanlnNoNewline(t *testing.T) { + var a int + _, err := Sscanln("1 x\n", &a) + if err == nil { + t.Error("expected error scanning string missing newline") + } else if !strings.Contains(err.Error(), "newline") { + t.Errorf("expected newline error scanning string missing newline, got: %s", err) + } +} + +func TestScanlnWithMiddleNewline(t *testing.T) { + r := strings.NewReader("123\n456\n") + var a, b int + _, err := Fscanln(r, &a, &b) + if err == nil { + t.Error("expected error scanning string with extra newline") + } else if !strings.Contains(err.Error(), "newline") { + t.Errorf("expected newline error scanning string with extra newline, got: %s", err) + } +} + +// eofCounter is a special Reader that counts reads at end of file. +type eofCounter struct { + reader *strings.Reader + eofCount int +} + +func (ec *eofCounter) Read(b []byte) (n int, err error) { + n, err = ec.reader.Read(b) + if n == 0 { + ec.eofCount++ + } + return +} + +// TestEOF verifies that when we scan, we see at most EOF once per call to a +// Scan function, and then only when it's really an EOF. +func TestEOF(t *testing.T) { + ec := &eofCounter{strings.NewReader("123\n"), 0} + var a int + n, err := Fscanln(ec, &a) + if err != nil { + t.Error("unexpected error", err) + } + if n != 1 { + t.Error("expected to scan one item, got", n) + } + if ec.eofCount != 0 { + t.Error("expected zero EOFs", ec.eofCount) + ec.eofCount = 0 // reset for next test + } + n, err = Fscanln(ec, &a) + if err == nil { + t.Error("expected error scanning empty string") + } + if n != 0 { + t.Error("expected to scan zero items, got", n) + } + if ec.eofCount != 1 { + t.Error("expected one EOF, got", ec.eofCount) + } +} + +// TestEOFAtEndOfInput verifies that we see an EOF error if we run out of input. +// This was a buglet: we used to get "expected integer". +func TestEOFAtEndOfInput(t *testing.T) { + var i, j int + n, err := Sscanf("23", "%d %d", &i, &j) + if n != 1 || i != 23 { + t.Errorf("Sscanf expected one value of 23; got %d %d", n, i) + } + if err != io.EOF { + t.Errorf("Sscanf expected EOF; got %q", err) + } + n, err = Sscan("234", &i, &j) + if n != 1 || i != 234 { + t.Errorf("Sscan expected one value of 234; got %d %d", n, i) + } + if err != io.EOF { + t.Errorf("Sscan expected EOF; got %q", err) + } + // Trailing space is tougher. + n, err = Sscan("234 ", &i, &j) + if n != 1 || i != 234 { + t.Errorf("Sscan expected one value of 234; got %d %d", n, i) + } + if err != io.EOF { + t.Errorf("Sscan expected EOF; got %q", err) + } +} + +var eofTests = []struct { + format string + v any +}{ + {"%s", &stringVal}, + {"%q", &stringVal}, + {"%x", &stringVal}, + {"%v", &stringVal}, + {"%v", &bytesVal}, + {"%v", &intVal}, + {"%v", &uintVal}, + {"%v", &boolVal}, + {"%v", &float32Val}, + {"%v", &complex64Val}, + {"%v", &renamedStringVal}, + {"%v", &renamedBytesVal}, + {"%v", &renamedIntVal}, + {"%v", &renamedUintVal}, + {"%v", &renamedBoolVal}, + {"%v", &renamedFloat32Val}, + {"%v", &renamedComplex64Val}, +} + +func TestEOFAllTypes(t *testing.T) { + for i, test := range eofTests { + if _, err := Sscanf("", test.format, test.v); err != io.EOF { + t.Errorf("#%d: %s %T not eof on empty string: %s", i, test.format, test.v, err) + } + if _, err := Sscanf(" ", test.format, test.v); err != io.EOF { + t.Errorf("#%d: %s %T not eof on trailing blanks: %s", i, test.format, test.v, err) + } + } +} + +// TestUnreadRuneWithBufio verifies that, at least when using bufio, successive +// calls to Fscan do not lose runes. +func TestUnreadRuneWithBufio(t *testing.T) { + r := bufio.NewReader(strings.NewReader("123αb")) + var i int + var a string + n, err := Fscanf(r, "%d", &i) + if n != 1 || err != nil { + t.Errorf("reading int expected one item, no errors; got %d %q", n, err) + } + if i != 123 { + t.Errorf("expected 123; got %d", i) + } + n, err = Fscanf(r, "%s", &a) + if n != 1 || err != nil { + t.Errorf("reading string expected one item, no errors; got %d %q", n, err) + } + if a != "αb" { + t.Errorf("expected αb; got %q", a) + } +} + +type TwoLines string + +// Scan attempts to read two lines into the object. Scanln should prevent this +// because it stops at newline; Scan and Scanf should be fine. +func (t *TwoLines) Scan(state ScanState, verb rune) error { + chars := make([]rune, 0, 100) + for nlCount := 0; nlCount < 2; { + c, _, err := state.ReadRune() + if err != nil { + return err + } + chars = append(chars, c) + if c == '\n' { + nlCount++ + } + } + *t = TwoLines(string(chars)) + return nil +} + +func TestMultiLine(t *testing.T) { + input := "abc\ndef\n" + // Sscan should work + var tscan TwoLines + n, err := Sscan(input, &tscan) + if n != 1 { + t.Errorf("Sscan: expected 1 item; got %d", n) + } + if err != nil { + t.Errorf("Sscan: expected no error; got %s", err) + } + if string(tscan) != input { + t.Errorf("Sscan: expected %q; got %q", input, tscan) + } + // Sscanf should work + var tscanf TwoLines + n, err = Sscanf(input, "%s", &tscanf) + if n != 1 { + t.Errorf("Sscanf: expected 1 item; got %d", n) + } + if err != nil { + t.Errorf("Sscanf: expected no error; got %s", err) + } + if string(tscanf) != input { + t.Errorf("Sscanf: expected %q; got %q", input, tscanf) + } + // Sscanln should not work + var tscanln TwoLines + n, err = Sscanln(input, &tscanln) + if n != 0 { + t.Errorf("Sscanln: expected 0 items; got %d: %q", n, tscanln) + } + if err == nil { + t.Error("Sscanln: expected error; got none") + } else if err != io.ErrUnexpectedEOF { + t.Errorf("Sscanln: expected io.ErrUnexpectedEOF (ha!); got %s", err) + } +} + +// TestLineByLineFscanf tests that Fscanf does not read past newline. Issue +// 3481. +func TestLineByLineFscanf(t *testing.T) { + r := struct{ io.Reader }{strings.NewReader("1\n2\n")} + var i, j int + n, err := Fscanf(r, "%v\n", &i) + if n != 1 || err != nil { + t.Fatalf("first read: %d %q", n, err) + } + n, err = Fscanf(r, "%v\n", &j) + if n != 1 || err != nil { + t.Fatalf("second read: %d %q", n, err) + } + if i != 1 || j != 2 { + t.Errorf("wrong values; wanted 1 2 got %d %d", i, j) + } +} + +// TestScanStateCount verifies the correct byte count is returned. Issue 8512. + +// runeScanner implements the Scanner interface for TestScanStateCount. +type runeScanner struct { + rune rune + size int +} + +func (rs *runeScanner) Scan(state ScanState, verb rune) error { + r, size, err := state.ReadRune() + rs.rune = r + rs.size = size + return err +} + +func TestScanStateCount(t *testing.T) { + var a, b, c runeScanner + n, err := Sscanf("12➂", "%c%c%c", &a, &b, &c) + if err != nil { + t.Fatal(err) + } + if n != 3 { + t.Fatalf("expected 3 items consumed, got %d", n) + } + if a.rune != '1' || b.rune != '2' || c.rune != '➂' { + t.Errorf("bad scan rune: %q %q %q should be '1' '2' '➂'", a.rune, b.rune, c.rune) + } + if a.size != 1 || b.size != 1 || c.size != 3 { + t.Errorf("bad scan size: %q %q %q should be 1 1 3", a.size, b.size, c.size) + } +} + +// RecursiveInt accepts a string matching %d.%d.%d.... +// and parses it into a linked list. +// It allows us to benchmark recursive descent style scanners. +type RecursiveInt struct { + i int + next *RecursiveInt +} + +func (r *RecursiveInt) Scan(state ScanState, verb rune) (err error) { + _, err = Fscan(state, &r.i) + if err != nil { + return + } + next := new(RecursiveInt) + _, err = Fscanf(state, ".%v", next) + if err != nil { + if err == io.ErrUnexpectedEOF { + err = nil + } + return + } + r.next = next + return +} + +// scanInts performs the same scanning task as RecursiveInt.Scan +// but without recurring through scanner, so we can compare +// performance more directly. +func scanInts(r *RecursiveInt, b *bytes.Buffer) (err error) { + r.next = nil + _, err = Fscan(b, &r.i) + if err != nil { + return + } + c, _, err := b.ReadRune() + if err != nil { + if err == io.EOF { + err = nil + } + return + } + if c != '.' { + return + } + next := new(RecursiveInt) + err = scanInts(next, b) + if err == nil { + r.next = next + } + return +} + +func makeInts(n int) []byte { + var buf bytes.Buffer + Fprintf(&buf, "1") + for i := 1; i < n; i++ { + Fprintf(&buf, ".%d", i+1) + } + return buf.Bytes() +} + +func TestScanInts(t *testing.T) { + testScanInts(t, scanInts) + testScanInts(t, func(r *RecursiveInt, b *bytes.Buffer) (err error) { + _, err = Fscan(b, r) + return + }) +} + +// 800 is small enough to not overflow the stack when using gccgo on a +// platform that does not support split stack. +const intCount = 800 + +func testScanInts(t *testing.T, scan func(*RecursiveInt, *bytes.Buffer) error) { + r := new(RecursiveInt) + ints := makeInts(intCount) + buf := bytes.NewBuffer(ints) + err := scan(r, buf) + if err != nil { + t.Error("unexpected error", err) + } + i := 1 + for ; r != nil; r = r.next { + if r.i != i { + t.Fatalf("bad scan: expected %d got %d", i, r.i) + } + i++ + } + if i-1 != intCount { + t.Fatalf("bad scan count: expected %d got %d", intCount, i-1) + } +} + +func BenchmarkScanInts(b *testing.B) { + b.StopTimer() + ints := makeInts(intCount) + var r RecursiveInt + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(ints) + b.StartTimer() + scanInts(&r, buf) + b.StopTimer() + } +} + +func BenchmarkScanRecursiveInt(b *testing.B) { + b.StopTimer() + ints := makeInts(intCount) + var r RecursiveInt + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(ints) + b.StartTimer() + Fscan(buf, &r) + b.StopTimer() + } +} + +func BenchmarkScanRecursiveIntReaderWrapper(b *testing.B) { + b.StopTimer() + ints := makeInts(intCount) + var r RecursiveInt + for i := 0; i < b.N; i++ { + buf := struct{ io.Reader }{strings.NewReader(string(ints))} + b.StartTimer() + Fscan(buf, &r) + b.StopTimer() + } +} + +// Issue 9124. +// %x on bytes couldn't handle non-space bytes terminating the scan. +func TestHexBytes(t *testing.T) { + var a, b []byte + n, err := Sscanf("00010203", "%x", &a) + if n != 1 || err != nil { + t.Errorf("simple: got count, err = %d, %v; expected 1, nil", n, err) + } + check := func(msg string, x []byte) { + if len(x) != 4 { + t.Errorf("%s: bad length %d", msg, len(x)) + } + for i, b := range x { + if int(b) != i { + t.Errorf("%s: bad x[%d] = %x", msg, i, x[i]) + } + } + } + check("simple", a) + a = nil + + n, err = Sscanf("00010203 00010203", "%x %x", &a, &b) + if n != 2 || err != nil { + t.Errorf("simple pair: got count, err = %d, %v; expected 2, nil", n, err) + } + check("simple pair a", a) + check("simple pair b", b) + a = nil + b = nil + + n, err = Sscanf("00010203:", "%x", &a) + if n != 1 || err != nil { + t.Errorf("colon: got count, err = %d, %v; expected 1, nil", n, err) + } + check("colon", a) + a = nil + + n, err = Sscanf("00010203:00010203", "%x:%x", &a, &b) + if n != 2 || err != nil { + t.Errorf("colon pair: got count, err = %d, %v; expected 2, nil", n, err) + } + check("colon pair a", a) + check("colon pair b", b) + a = nil + b = nil + + // This one fails because there is a hex byte after the data, + // that is, an odd number of hex input bytes. + n, err = Sscanf("000102034:", "%x", &a) + if n != 0 || err == nil { + t.Errorf("odd count: got count, err = %d, %v; expected 0, error", n, err) + } +} + +func TestScanNewlinesAreSpaces(t *testing.T) { + var a, b int + var tests = []struct { + name string + text string + count int + }{ + {"newlines", "1\n2\n", 2}, + {"no final newline", "1\n2", 2}, + {"newlines with spaces ", "1 \n 2 \n", 2}, + {"no final newline with spaces", "1 \n 2", 2}, + } + for _, test := range tests { + n, err := Sscan(test.text, &a, &b) + if n != test.count { + t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n) + } + if err != nil { + t.Errorf("%s: unexpected error: %s", test.name, err) + } + } +} + +func TestScanlnNewlinesTerminate(t *testing.T) { + var a, b int + var tests = []struct { + name string + text string + count int + ok bool + }{ + {"one line one item", "1\n", 1, false}, + {"one line two items with spaces ", " 1 2 \n", 2, true}, + {"one line two items no newline", " 1 2", 2, true}, + {"two lines two items", "1\n2\n", 1, false}, + } + for _, test := range tests { + n, err := Sscanln(test.text, &a, &b) + if n != test.count { + t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n) + } + if test.ok && err != nil { + t.Errorf("%s: unexpected error: %s", test.name, err) + } + if !test.ok && err == nil { + t.Errorf("%s: expected error; got none", test.name) + } + } +} + +func TestScanfNewlineMatchFormat(t *testing.T) { + var a, b int + var tests = []struct { + name string + text string + format string + count int + ok bool + }{ + {"newline in both", "1\n2", "%d\n%d\n", 2, true}, + {"newline in input", "1\n2", "%d %d", 1, false}, + {"space-newline in input", "1 \n2", "%d %d", 1, false}, + {"newline in format", "1 2", "%d\n%d", 1, false}, + {"space-newline in format", "1 2", "%d \n%d", 1, false}, + {"space-newline in both", "1 \n2", "%d \n%d", 2, true}, + {"extra space in format", "1\n2", "%d\n %d", 2, true}, + {"two extra spaces in format", "1\n2", "%d \n %d", 2, true}, + {"space vs newline 0000", "1\n2", "%d\n%d", 2, true}, + {"space vs newline 0001", "1\n2", "%d\n %d", 2, true}, + {"space vs newline 0010", "1\n2", "%d \n%d", 2, true}, + {"space vs newline 0011", "1\n2", "%d \n %d", 2, true}, + {"space vs newline 0100", "1\n 2", "%d\n%d", 2, true}, + {"space vs newline 0101", "1\n 2", "%d\n%d ", 2, true}, + {"space vs newline 0110", "1\n 2", "%d \n%d", 2, true}, + {"space vs newline 0111", "1\n 2", "%d \n %d", 2, true}, + {"space vs newline 1000", "1 \n2", "%d\n%d", 2, true}, + {"space vs newline 1001", "1 \n2", "%d\n %d", 2, true}, + {"space vs newline 1010", "1 \n2", "%d \n%d", 2, true}, + {"space vs newline 1011", "1 \n2", "%d \n %d", 2, true}, + {"space vs newline 1100", "1 \n 2", "%d\n%d", 2, true}, + {"space vs newline 1101", "1 \n 2", "%d\n %d", 2, true}, + {"space vs newline 1110", "1 \n 2", "%d \n%d", 2, true}, + {"space vs newline 1111", "1 \n 2", "%d \n %d", 2, true}, + {"space vs newline no-percent 0000", "1\n2", "1\n2", 0, true}, + {"space vs newline no-percent 0001", "1\n2", "1\n 2", 0, true}, + {"space vs newline no-percent 0010", "1\n2", "1 \n2", 0, true}, + {"space vs newline no-percent 0011", "1\n2", "1 \n 2", 0, true}, + {"space vs newline no-percent 0100", "1\n 2", "1\n2", 0, false}, // fails: space after nl in input but not pattern + {"space vs newline no-percent 0101", "1\n 2", "1\n2 ", 0, false}, // fails: space after nl in input but not pattern + {"space vs newline no-percent 0110", "1\n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern + {"space vs newline no-percent 0111", "1\n 2", "1 \n 2", 0, true}, + {"space vs newline no-percent 1000", "1 \n2", "1\n2", 0, true}, + {"space vs newline no-percent 1001", "1 \n2", "1\n 2", 0, true}, + {"space vs newline no-percent 1010", "1 \n2", "1 \n2", 0, true}, + {"space vs newline no-percent 1011", "1 \n2", "1 \n 2", 0, true}, + {"space vs newline no-percent 1100", "1 \n 2", "1\n2", 0, false}, // fails: space after nl in input but not pattern + {"space vs newline no-percent 1101", "1 \n 2", "1\n 2", 0, true}, + {"space vs newline no-percent 1110", "1 \n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern + {"space vs newline no-percent 1111", "1 \n 2", "1 \n 2", 0, true}, + } + for _, test := range tests { + var n int + var err error + if strings.Contains(test.format, "%") { + n, err = Sscanf(test.text, test.format, &a, &b) + } else { + n, err = Sscanf(test.text, test.format) + } + if n != test.count { + t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n) + } + if test.ok && err != nil { + t.Errorf("%s: unexpected error: %s", test.name, err) + } + if !test.ok && err == nil { + t.Errorf("%s: expected error; got none", test.name) + } + } +} + +// Test for issue 12090: Was unreading at EOF, double-scanning a byte. + +type hexBytes [2]byte + +func (h *hexBytes) Scan(ss ScanState, verb rune) error { + var b []byte + _, err := Fscanf(ss, "%4x", &b) + if err != nil { + panic(err) // Really shouldn't happen. + } + copy((*h)[:], b) + return err +} + +func TestHexByte(t *testing.T) { + var h hexBytes + n, err := Sscanln("0123\n", &h) + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Fatalf("expected 1 item; scanned %d", n) + } + if h[0] != 0x01 || h[1] != 0x23 { + t.Fatalf("expected 0123 got %x", h) + } +} diff --git a/src/fmt/state_test.go b/src/fmt/state_test.go new file mode 100644 index 0000000..fda660a --- /dev/null +++ b/src/fmt/state_test.go @@ -0,0 +1,80 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "fmt" + "testing" +) + +type testState struct { + width int + widthOK bool + prec int + precOK bool + flag map[int]bool +} + +var _ fmt.State = testState{} + +func (s testState) Write(b []byte) (n int, err error) { + panic("unimplemented") +} + +func (s testState) Width() (wid int, ok bool) { + return s.width, s.widthOK +} + +func (s testState) Precision() (prec int, ok bool) { + return s.prec, s.precOK +} + +func (s testState) Flag(c int) bool { + return s.flag[c] +} + +const NO = -1000 + +func mkState(w, p int, flags string) testState { + s := testState{} + if w != NO { + s.width = w + s.widthOK = true + } + if p != NO { + s.prec = p + s.precOK = true + } + s.flag = make(map[int]bool) + for _, c := range flags { + s.flag[int(c)] = true + } + return s +} + +func TestFormatString(t *testing.T) { + var tests = []struct { + width, prec int + flags string + result string + }{ + {NO, NO, "", "%x"}, + {NO, 3, "", "%.3x"}, + {3, NO, "", "%3x"}, + {7, 3, "", "%7.3x"}, + {NO, NO, " +-#0", "% +-#0x"}, + {7, 3, "+", "%+7.3x"}, + {7, -3, "-", "%-7.-3x"}, + {7, 3, " ", "% 7.3x"}, + {7, 3, "#", "%#7.3x"}, + {7, 3, "0", "%07.3x"}, + } + for _, test := range tests { + got := fmt.FormatString(mkState(test.width, test.prec, test.flags), 'x') + if got != test.result { + t.Errorf("%v: got %s", test, got) + } + } +} diff --git a/src/fmt/stringer_example_test.go b/src/fmt/stringer_example_test.go new file mode 100644 index 0000000..c77e788 --- /dev/null +++ b/src/fmt/stringer_example_test.go @@ -0,0 +1,29 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + "fmt" +) + +// Animal has a Name and an Age to represent an animal. +type Animal struct { + Name string + Age uint +} + +// String makes Animal satisfy the Stringer interface. +func (a Animal) String() string { + return fmt.Sprintf("%v (%d)", a.Name, a.Age) +} + +func ExampleStringer() { + a := Animal{ + Name: "Gopher", + Age: 2, + } + fmt.Println(a) + // Output: Gopher (2) +} diff --git a/src/fmt/stringer_test.go b/src/fmt/stringer_test.go new file mode 100644 index 0000000..0ca3f52 --- /dev/null +++ b/src/fmt/stringer_test.go @@ -0,0 +1,61 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt_test + +import ( + . "fmt" + "testing" +) + +type TI int +type TI8 int8 +type TI16 int16 +type TI32 int32 +type TI64 int64 +type TU uint +type TU8 uint8 +type TU16 uint16 +type TU32 uint32 +type TU64 uint64 +type TUI uintptr +type TF float64 +type TF32 float32 +type TF64 float64 +type TB bool +type TS string + +func (v TI) String() string { return Sprintf("I: %d", int(v)) } +func (v TI8) String() string { return Sprintf("I8: %d", int8(v)) } +func (v TI16) String() string { return Sprintf("I16: %d", int16(v)) } +func (v TI32) String() string { return Sprintf("I32: %d", int32(v)) } +func (v TI64) String() string { return Sprintf("I64: %d", int64(v)) } +func (v TU) String() string { return Sprintf("U: %d", uint(v)) } +func (v TU8) String() string { return Sprintf("U8: %d", uint8(v)) } +func (v TU16) String() string { return Sprintf("U16: %d", uint16(v)) } +func (v TU32) String() string { return Sprintf("U32: %d", uint32(v)) } +func (v TU64) String() string { return Sprintf("U64: %d", uint64(v)) } +func (v TUI) String() string { return Sprintf("UI: %d", uintptr(v)) } +func (v TF) String() string { return Sprintf("F: %f", float64(v)) } +func (v TF32) String() string { return Sprintf("F32: %f", float32(v)) } +func (v TF64) String() string { return Sprintf("F64: %f", float64(v)) } +func (v TB) String() string { return Sprintf("B: %t", bool(v)) } +func (v TS) String() string { return Sprintf("S: %q", string(v)) } + +func check(t *testing.T, got, want string) { + if got != want { + t.Error(got, "!=", want) + } +} + +func TestStringer(t *testing.T) { + s := Sprintf("%v %v %v %v %v", TI(0), TI8(1), TI16(2), TI32(3), TI64(4)) + check(t, s, "I: 0 I8: 1 I16: 2 I32: 3 I64: 4") + s = Sprintf("%v %v %v %v %v %v", TU(5), TU8(6), TU16(7), TU32(8), TU64(9), TUI(10)) + check(t, s, "U: 5 U8: 6 U16: 7 U32: 8 U64: 9 UI: 10") + s = Sprintf("%v %v %v", TF(1.0), TF32(2.0), TF64(3.0)) + check(t, s, "F: 1.000000 F32: 2.000000 F64: 3.000000") + s = Sprintf("%v %v", TB(true), TS("x")) + check(t, s, "B: true S: \"x\"") +} |