// Copyright 2015 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. // This file implements int-to-string conversion functions. package big import ( "errors" "fmt" "io" ) // Text returns the string representation of x in the given base. // Base must be between 2 and 62, inclusive. The result uses the // lower-case letters 'a' to 'z' for digit values 10 to 35, and // the upper-case letters 'A' to 'Z' for digit values 36 to 61. // No prefix (such as "0x") is added to the string. If x is a nil // pointer it returns "". func (x *Int) Text(base int) string { if x == nil { return "" } return string(x.abs.itoa(x.neg, base)) } // Append appends the string representation of x, as generated by // x.Text(base), to buf and returns the extended buffer. func (x *Int) Append(buf []byte, base int) []byte { if x == nil { return append(buf, ""...) } return append(buf, x.abs.itoa(x.neg, base)...) } // String returns the decimal representation of x as generated by // x.Text(10). func (x *Int) String() string { return x.Text(10) } // write count copies of text to s. func writeMultiple(s fmt.State, text string, count int) { if len(text) > 0 { b := []byte(text) for ; count > 0; count-- { s.Write(b) } } } var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter // Format implements [fmt.Formatter]. It accepts the formats // 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // 'd' (decimal), 'x' (lowercase hexadecimal), and // 'X' (uppercase hexadecimal). // Also supported are the full suite of package fmt's format // flags for integral types, including '+' and ' ' for sign // control, '#' for leading zero in octal and for hexadecimal, // a leading "0x" or "0X" for "%#x" and "%#X" respectively, // specification of minimum digits precision, output field // width, space or zero padding, and '-' for left or right // justification. func (x *Int) Format(s fmt.State, ch rune) { // determine base var base int switch ch { case 'b': base = 2 case 'o', 'O': base = 8 case 'd', 's', 'v': base = 10 case 'x', 'X': base = 16 default: // unknown format fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) return } if x == nil { fmt.Fprint(s, "") return } // determine sign character sign := "" switch { case x.neg: sign = "-" case s.Flag('+'): // supersedes ' ' when both specified sign = "+" case s.Flag(' '): sign = " " } // determine prefix characters for indicating output base prefix := "" if s.Flag('#') { switch ch { case 'b': // binary prefix = "0b" case 'o': // octal prefix = "0" case 'x': // hexadecimal prefix = "0x" case 'X': prefix = "0X" } } if ch == 'O' { prefix = "0o" } digits := x.abs.utoa(base) if ch == 'X' { // faster than bytes.ToUpper for i, d := range digits { if 'a' <= d && d <= 'z' { digits[i] = 'A' + (d - 'a') } } } // number of characters for the three classes of number padding var left int // space characters to left of digits for right justification ("%8d") var zeros int // zero characters (actually cs[0]) as left-most digits ("%.8d") var right int // space characters to right of digits for left justification ("%-8d") // determine number padding from precision: the least number of digits to output precision, precisionSet := s.Precision() if precisionSet { switch { case len(digits) < precision: zeros = precision - len(digits) // count of zero padding case len(digits) == 1 && digits[0] == '0' && precision == 0: return // print nothing if zero value (x == 0) and zero precision ("." or ".0") } } // determine field pad from width: the least number of characters to output length := len(sign) + len(prefix) + zeros + len(digits) if width, widthSet := s.Width(); widthSet && length < width { // pad as specified switch d := width - length; { case s.Flag('-'): // pad on the right with spaces; supersedes '0' when both specified right = d case s.Flag('0') && !precisionSet: // pad with zeros unless precision also specified zeros = d default: // pad on the left with spaces left = d } } // print number as [left pad][sign][prefix][zero pad][digits][right pad] writeMultiple(s, " ", left) writeMultiple(s, sign, 1) writeMultiple(s, prefix, 1) writeMultiple(s, "0", zeros) s.Write(digits) writeMultiple(s, " ", right) } // scan sets z to the integer value corresponding to the longest possible prefix // read from r representing a signed integer number in a given conversion base. // It returns z, the actual conversion base used, and an error, if any. In the // error case, the value of z is undefined but the returned value is nil. The // syntax follows the syntax of integer literals in Go. // // The base argument must be 0 or a value from 2 through MaxBase. If the base // is 0, the string prefix determines the actual conversion base. A prefix of // “0b” or “0B” selects base 2; a “0”, “0o”, or “0O” prefix selects // base 8, and a “0x” or “0X” prefix selects base 16. Otherwise the selected // base is 10. func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { // determine sign neg, err := scanSign(r) if err != nil { return nil, 0, err } // determine mantissa z.abs, base, _, err = z.abs.scan(r, base, false) if err != nil { return nil, base, err } z.neg = len(z.abs) > 0 && neg // 0 has no sign return z, base, nil } func scanSign(r io.ByteScanner) (neg bool, err error) { var ch byte if ch, err = r.ReadByte(); err != nil { return false, err } switch ch { case '-': neg = true case '+': // nothing to do default: r.UnreadByte() } return } // byteReader is a local wrapper around fmt.ScanState; // it implements the ByteReader interface. type byteReader struct { fmt.ScanState } func (r byteReader) ReadByte() (byte, error) { ch, size, err := r.ReadRune() if size != 1 && err == nil { err = fmt.Errorf("invalid rune %#U", ch) } return byte(ch), err } func (r byteReader) UnreadByte() error { return r.UnreadRune() } var _ fmt.Scanner = intOne // *Int must implement fmt.Scanner // Scan is a support routine for [fmt.Scanner]; it sets z to the value of // the scanned number. It accepts the formats 'b' (binary), 'o' (octal), // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). func (z *Int) Scan(s fmt.ScanState, ch rune) error { s.SkipSpace() // skip leading space characters base := 0 switch ch { case 'b': base = 2 case 'o': base = 8 case 'd': base = 10 case 'x', 'X': base = 16 case 's', 'v': // let scan determine the base default: return errors.New("Int.Scan: invalid verb") } _, _, err := z.scan(byteReader{s}, base) return err }