intconv.mx raw

   1  // Copyright 2015 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  // This file implements int-to-string conversion functions.
   6  
   7  package big
   8  
   9  import (
  10  	"errors"
  11  	"fmt"
  12  	"io"
  13  )
  14  
  15  // Text returns the string representation of x in the given base.
  16  // Base must be between 2 and 62, inclusive. The result uses the
  17  // lower-case letters 'a' to 'z' for digit values 10 to 35, and
  18  // the upper-case letters 'A' to 'Z' for digit values 36 to 61.
  19  // No prefix (such as "0x") is added to the string. If x is a nil
  20  // pointer it returns "<nil>".
  21  func (x *Int) Text(base int) []byte {
  22  	if x == nil {
  23  		return "<nil>"
  24  	}
  25  	return []byte(x.abs.itoa(x.neg, base))
  26  }
  27  
  28  // Append appends the string representation of x, as generated by
  29  // x.Text(base), to buf and returns the extended buffer.
  30  func (x *Int) Append(buf []byte, base int) []byte {
  31  	if x == nil {
  32  		return append(buf, "<nil>"...)
  33  	}
  34  	return append(buf, x.abs.itoa(x.neg, base)...)
  35  }
  36  
  37  // String returns the decimal representation of x as generated by
  38  // x.Text(10).
  39  func (x *Int) String() string {
  40  	return x.Text(10)
  41  }
  42  
  43  // write count copies of text to s.
  44  func writeMultiple(s fmt.State, text []byte, count int) {
  45  	if len(text) > 0 {
  46  		b := []byte(text)
  47  		for ; count > 0; count-- {
  48  			s.Write(b)
  49  		}
  50  	}
  51  }
  52  
  53  var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter
  54  
  55  // Format implements [fmt.Formatter]. It accepts the formats
  56  // 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix),
  57  // 'd' (decimal), 'x' (lowercase hexadecimal), and
  58  // 'X' (uppercase hexadecimal).
  59  // Also supported are the full suite of package fmt's format
  60  // flags for integral types, including '+' and ' ' for sign
  61  // control, '#' for leading zero in octal and for hexadecimal,
  62  // a leading "0x" or "0X" for "%#x" and "%#X" respectively,
  63  // specification of minimum digits precision, output field
  64  // width, space or zero padding, and '-' for left or right
  65  // justification.
  66  func (x *Int) Format(s fmt.State, ch rune) {
  67  	// determine base
  68  	var base int
  69  	switch ch {
  70  	case 'b':
  71  		base = 2
  72  	case 'o', 'O':
  73  		base = 8
  74  	case 'd', 's', 'v':
  75  		base = 10
  76  	case 'x', 'X':
  77  		base = 16
  78  	default:
  79  		// unknown format
  80  		fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String())
  81  		return
  82  	}
  83  
  84  	if x == nil {
  85  		fmt.Fprint(s, "<nil>")
  86  		return
  87  	}
  88  
  89  	// determine sign character
  90  	sign := ""
  91  	switch {
  92  	case x.neg:
  93  		sign = "-"
  94  	case s.Flag('+'): // supersedes ' ' when both specified
  95  		sign = "+"
  96  	case s.Flag(' '):
  97  		sign = " "
  98  	}
  99  
 100  	// determine prefix characters for indicating output base
 101  	prefix := ""
 102  	if s.Flag('#') {
 103  		switch ch {
 104  		case 'b': // binary
 105  			prefix = "0b"
 106  		case 'o': // octal
 107  			prefix = "0"
 108  		case 'x': // hexadecimal
 109  			prefix = "0x"
 110  		case 'X':
 111  			prefix = "0X"
 112  		}
 113  	}
 114  	if ch == 'O' {
 115  		prefix = "0o"
 116  	}
 117  
 118  	digits := x.abs.utoa(base)
 119  	if ch == 'X' {
 120  		// faster than bytes.ToUpper
 121  		for i, d := range digits {
 122  			if 'a' <= d && d <= 'z' {
 123  				digits[i] = 'A' + (d - 'a')
 124  			}
 125  		}
 126  	}
 127  
 128  	// number of characters for the three classes of number padding
 129  	var left int  // space characters to left of digits for right justification ("%8d")
 130  	var zeros int // zero characters (actually cs[0]) as left-most digits ("%.8d")
 131  	var right int // space characters to right of digits for left justification ("%-8d")
 132  
 133  	// determine number padding from precision: the least number of digits to output
 134  	precision, precisionSet := s.Precision()
 135  	if precisionSet {
 136  		switch {
 137  		case len(digits) < precision:
 138  			zeros = precision - len(digits) // count of zero padding
 139  		case len(digits) == 1 && digits[0] == '0' && precision == 0:
 140  			return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
 141  		}
 142  	}
 143  
 144  	// determine field pad from width: the least number of characters to output
 145  	length := len(sign) + len(prefix) + zeros + len(digits)
 146  	if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
 147  		switch d := width - length; {
 148  		case s.Flag('-'):
 149  			// pad on the right with spaces; supersedes '0' when both specified
 150  			right = d
 151  		case s.Flag('0') && !precisionSet:
 152  			// pad with zeros unless precision also specified
 153  			zeros = d
 154  		default:
 155  			// pad on the left with spaces
 156  			left = d
 157  		}
 158  	}
 159  
 160  	// print number as [left pad][sign][prefix][zero pad][digits][right pad]
 161  	writeMultiple(s, " ", left)
 162  	writeMultiple(s, sign, 1)
 163  	writeMultiple(s, prefix, 1)
 164  	writeMultiple(s, "0", zeros)
 165  	s.Write(digits)
 166  	writeMultiple(s, " ", right)
 167  }
 168  
 169  // scan sets z to the integer value corresponding to the longest possible prefix
 170  // read from r representing a signed integer number in a given conversion base.
 171  // It returns z, the actual conversion base used, and an error, if any. In the
 172  // error case, the value of z is undefined but the returned value is nil. The
 173  // syntax follows the syntax of integer literals in Go.
 174  //
 175  // The base argument must be 0 or a value from 2 through MaxBase. If the base
 176  // is 0, the string prefix determines the actual conversion base. A prefix of
 177  // “0b” or “0B” selects base 2; a “0”, “0o”, or “0O” prefix selects
 178  // base 8, and a “0x” or “0X” prefix selects base 16. Otherwise the selected
 179  // base is 10.
 180  func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
 181  	// determine sign
 182  	neg, err := scanSign(r)
 183  	if err != nil {
 184  		return nil, 0, err
 185  	}
 186  
 187  	// determine mantissa
 188  	z.abs, base, _, err = z.abs.scan(r, base, false)
 189  	if err != nil {
 190  		return nil, base, err
 191  	}
 192  	z.neg = len(z.abs) > 0 && neg // 0 has no sign
 193  
 194  	return z, base, nil
 195  }
 196  
 197  func scanSign(r io.ByteScanner) (neg bool, err error) {
 198  	var ch byte
 199  	if ch, err = r.ReadByte(); err != nil {
 200  		return false, err
 201  	}
 202  	switch ch {
 203  	case '-':
 204  		neg = true
 205  	case '+':
 206  		// nothing to do
 207  	default:
 208  		r.UnreadByte()
 209  	}
 210  	return
 211  }
 212  
 213  // byteReader is a local wrapper around fmt.ScanState;
 214  // it implements the ByteReader interface.
 215  type byteReader struct {
 216  	fmt.ScanState
 217  }
 218  
 219  func (r byteReader) ReadByte() (byte, error) {
 220  	ch, size, err := r.ReadRune()
 221  	if size != 1 && err == nil {
 222  		err = fmt.Errorf("invalid rune %#U", ch)
 223  	}
 224  	return byte(ch), err
 225  }
 226  
 227  func (r byteReader) UnreadByte() error {
 228  	return r.UnreadRune()
 229  }
 230  
 231  var _ fmt.Scanner = intOne // *Int must implement fmt.Scanner
 232  
 233  // Scan is a support routine for [fmt.Scanner]; it sets z to the value of
 234  // the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
 235  // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
 236  func (z *Int) Scan(s fmt.ScanState, ch rune) error {
 237  	s.SkipSpace() // skip leading space characters
 238  	base := 0
 239  	switch ch {
 240  	case 'b':
 241  		base = 2
 242  	case 'o':
 243  		base = 8
 244  	case 'd':
 245  		base = 10
 246  	case 'x', 'X':
 247  		base = 16
 248  	case 's', 'v':
 249  		// let scan determine the base
 250  	default:
 251  		return errors.New("Int.Scan: invalid verb")
 252  	}
 253  	_, _, err := z.scan(byteReader{s}, base)
 254  	return err
 255  }
 256