github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/text/internal/number/format.go (about)

     1  // Copyright 2017 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  package number
     6  
     7  import (
     8  	"strconv"
     9  	"unicode/utf8"
    10  
    11  	"golang.org/x/text/language"
    12  )
    13  
    14  // TODO:
    15  // - grouping of fractions
    16  // - allow user-defined superscript notation (such as <sup>4</sup>)
    17  // - same for non-breaking spaces, like &nbsp;
    18  
    19  // A VisibleDigits computes digits, comma placement and trailing zeros as they
    20  // will be shown to the user.
    21  type VisibleDigits interface {
    22  	Digits(buf []byte, t language.Tag, scale int) Digits
    23  	// TODO: Do we also need to add the verb or pass a format.State?
    24  }
    25  
    26  // Formatting proceeds along the following lines:
    27  // 0) Compose rounding information from format and context.
    28  // 1) Convert a number into a Decimal.
    29  // 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
    30  //    (non-increment) rounding. The Decimal that results from this is suitable
    31  //    for determining the plural form.
    32  // 3) Render the Decimal in the localized form.
    33  
    34  // Formatter contains all the information needed to render a number.
    35  type Formatter struct {
    36  	Pattern
    37  	Info
    38  }
    39  
    40  func (f *Formatter) init(t language.Tag, index []uint8) {
    41  	f.Info = InfoFromTag(t)
    42  	f.Pattern = formats[index[tagToID(t)]]
    43  }
    44  
    45  // InitPattern initializes a Formatter for the given Pattern.
    46  func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
    47  	f.Info = InfoFromTag(t)
    48  	f.Pattern = *pat
    49  }
    50  
    51  // InitDecimal initializes a Formatter using the default Pattern for the given
    52  // language.
    53  func (f *Formatter) InitDecimal(t language.Tag) {
    54  	f.init(t, tagToDecimal)
    55  }
    56  
    57  // InitScientific initializes a Formatter using the default Pattern for the
    58  // given language.
    59  func (f *Formatter) InitScientific(t language.Tag) {
    60  	f.init(t, tagToScientific)
    61  	f.Pattern.MinFractionDigits = 0
    62  	f.Pattern.MaxFractionDigits = -1
    63  }
    64  
    65  // InitEngineering initializes a Formatter using the default Pattern for the
    66  // given language.
    67  func (f *Formatter) InitEngineering(t language.Tag) {
    68  	f.init(t, tagToScientific)
    69  	f.Pattern.MinFractionDigits = 0
    70  	f.Pattern.MaxFractionDigits = -1
    71  	f.Pattern.MaxIntegerDigits = 3
    72  	f.Pattern.MinIntegerDigits = 1
    73  }
    74  
    75  // InitPercent initializes a Formatter using the default Pattern for the given
    76  // language.
    77  func (f *Formatter) InitPercent(t language.Tag) {
    78  	f.init(t, tagToPercent)
    79  }
    80  
    81  // InitPerMille initializes a Formatter using the default Pattern for the given
    82  // language.
    83  func (f *Formatter) InitPerMille(t language.Tag) {
    84  	f.init(t, tagToPercent)
    85  	f.Pattern.DigitShift = 3
    86  }
    87  
    88  func (f *Formatter) Append(dst []byte, x interface{}) []byte {
    89  	var d Decimal
    90  	r := f.RoundingContext
    91  	d.Convert(r, x)
    92  	return f.Render(dst, FormatDigits(&d, r))
    93  }
    94  
    95  func FormatDigits(d *Decimal, r RoundingContext) Digits {
    96  	if r.isScientific() {
    97  		return scientificVisibleDigits(r, d)
    98  	}
    99  	return decimalVisibleDigits(r, d)
   100  }
   101  
   102  func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
   103  	return f.Render(dst, FormatDigits(d, f.RoundingContext))
   104  }
   105  
   106  func (f *Formatter) Render(dst []byte, d Digits) []byte {
   107  	var result []byte
   108  	var postPrefix, preSuffix int
   109  	if d.IsScientific {
   110  		result, postPrefix, preSuffix = appendScientific(dst, f, &d)
   111  	} else {
   112  		result, postPrefix, preSuffix = appendDecimal(dst, f, &d)
   113  	}
   114  	if f.PadRune == 0 {
   115  		return result
   116  	}
   117  	width := int(f.FormatWidth)
   118  	if count := utf8.RuneCount(result); count < width {
   119  		insertPos := 0
   120  		switch f.Flags & PadMask {
   121  		case PadAfterPrefix:
   122  			insertPos = postPrefix
   123  		case PadBeforeSuffix:
   124  			insertPos = preSuffix
   125  		case PadAfterSuffix:
   126  			insertPos = len(result)
   127  		}
   128  		num := width - count
   129  		pad := [utf8.UTFMax]byte{' '}
   130  		sz := 1
   131  		if r := f.PadRune; r != 0 {
   132  			sz = utf8.EncodeRune(pad[:], r)
   133  		}
   134  		extra := sz * num
   135  		if n := len(result) + extra; n < cap(result) {
   136  			result = result[:n]
   137  			copy(result[insertPos+extra:], result[insertPos:])
   138  		} else {
   139  			buf := make([]byte, n)
   140  			copy(buf, result[:insertPos])
   141  			copy(buf[insertPos+extra:], result[insertPos:])
   142  			result = buf
   143  		}
   144  		for ; num > 0; num-- {
   145  			insertPos += copy(result[insertPos:], pad[:sz])
   146  		}
   147  	}
   148  	return result
   149  }
   150  
   151  // decimalVisibleDigits converts d according to the RoundingContext. Note that
   152  // the exponent may change as a result of this operation.
   153  func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
   154  	if d.NaN || d.Inf {
   155  		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
   156  	}
   157  	n := Digits{digits: d.normalize().digits}
   158  
   159  	exp := n.Exp
   160  	exp += int32(r.DigitShift)
   161  
   162  	// Cap integer digits. Remove *most-significant* digits.
   163  	if r.MaxIntegerDigits > 0 {
   164  		if p := int(exp) - int(r.MaxIntegerDigits); p > 0 {
   165  			if p > len(n.Digits) {
   166  				p = len(n.Digits)
   167  			}
   168  			if n.Digits = n.Digits[p:]; len(n.Digits) == 0 {
   169  				exp = 0
   170  			} else {
   171  				exp -= int32(p)
   172  			}
   173  			// Strip leading zeros.
   174  			for len(n.Digits) > 0 && n.Digits[0] == 0 {
   175  				n.Digits = n.Digits[1:]
   176  				exp--
   177  			}
   178  		}
   179  	}
   180  
   181  	// Rounding if not already done by Convert.
   182  	p := len(n.Digits)
   183  	if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
   184  		p = maxSig
   185  	}
   186  	if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 {
   187  		if cap := int(exp) + maxFrac; cap < p {
   188  			p = int(exp) + maxFrac
   189  		}
   190  		if p < 0 {
   191  			p = 0
   192  		}
   193  	}
   194  	n.round(r.Mode, p)
   195  
   196  	// set End (trailing zeros)
   197  	n.End = int32(len(n.Digits))
   198  	if n.End == 0 {
   199  		exp = 0
   200  		if r.MinFractionDigits > 0 {
   201  			n.End = int32(r.MinFractionDigits)
   202  		}
   203  		if p := int32(r.MinSignificantDigits) - 1; p > n.End {
   204  			n.End = p
   205  		}
   206  	} else {
   207  		if end := exp + int32(r.MinFractionDigits); end > n.End {
   208  			n.End = end
   209  		}
   210  		if n.End < int32(r.MinSignificantDigits) {
   211  			n.End = int32(r.MinSignificantDigits)
   212  		}
   213  	}
   214  	n.Exp = exp
   215  	return n
   216  }
   217  
   218  // appendDecimal appends a formatted number to dst. It returns two possible
   219  // insertion points for padding.
   220  func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
   221  	if dst, ok := f.renderSpecial(dst, n); ok {
   222  		return dst, 0, len(dst)
   223  	}
   224  	digits := n.Digits
   225  	exp := n.Exp
   226  
   227  	// Split in integer and fraction part.
   228  	var intDigits, fracDigits []byte
   229  	numInt := 0
   230  	numFrac := int(n.End - n.Exp)
   231  	if exp > 0 {
   232  		numInt = int(exp)
   233  		if int(exp) >= len(digits) { // ddddd | ddddd00
   234  			intDigits = digits
   235  		} else { // ddd.dd
   236  			intDigits = digits[:exp]
   237  			fracDigits = digits[exp:]
   238  		}
   239  	} else {
   240  		fracDigits = digits
   241  	}
   242  
   243  	neg := n.Neg
   244  	affix, suffix := f.getAffixes(neg)
   245  	dst = appendAffix(dst, f, affix, neg)
   246  	savedLen := len(dst)
   247  
   248  	minInt := int(f.MinIntegerDigits)
   249  	if minInt == 0 && f.MinSignificantDigits > 0 {
   250  		minInt = 1
   251  	}
   252  	// add leading zeros
   253  	for i := minInt; i > numInt; i-- {
   254  		dst = f.AppendDigit(dst, 0)
   255  		if f.needsSep(i) {
   256  			dst = append(dst, f.Symbol(SymGroup)...)
   257  		}
   258  	}
   259  	i := 0
   260  	for ; i < len(intDigits); i++ {
   261  		dst = f.AppendDigit(dst, intDigits[i])
   262  		if f.needsSep(numInt - i) {
   263  			dst = append(dst, f.Symbol(SymGroup)...)
   264  		}
   265  	}
   266  	for ; i < numInt; i++ {
   267  		dst = f.AppendDigit(dst, 0)
   268  		if f.needsSep(numInt - i) {
   269  			dst = append(dst, f.Symbol(SymGroup)...)
   270  		}
   271  	}
   272  
   273  	if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
   274  		dst = append(dst, f.Symbol(SymDecimal)...)
   275  	}
   276  	// Add trailing zeros
   277  	i = 0
   278  	for n := -int(n.Exp); i < n; i++ {
   279  		dst = f.AppendDigit(dst, 0)
   280  	}
   281  	for _, d := range fracDigits {
   282  		i++
   283  		dst = f.AppendDigit(dst, d)
   284  	}
   285  	for ; i < numFrac; i++ {
   286  		dst = f.AppendDigit(dst, 0)
   287  	}
   288  	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
   289  }
   290  
   291  func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
   292  	if d.NaN || d.Inf {
   293  		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
   294  	}
   295  	n := Digits{digits: d.normalize().digits, IsScientific: true}
   296  
   297  	// Normalize to have at least one digit. This simplifies engineering
   298  	// notation.
   299  	if len(n.Digits) == 0 {
   300  		n.Digits = append(n.Digits, 0)
   301  		n.Exp = 1
   302  	}
   303  
   304  	// Significant digits are transformed by the parser for scientific notation
   305  	// and do not need to be handled here.
   306  	maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits)
   307  	if numInt == 0 {
   308  		numInt = 1
   309  	}
   310  
   311  	// If a maximum number of integers is specified, the minimum must be 1
   312  	// and the exponent is grouped by this number (e.g. for engineering)
   313  	if maxInt > numInt {
   314  		// Correct the exponent to reflect a single integer digit.
   315  		numInt = 1
   316  		// engineering
   317  		// 0.01234 ([12345]e-1) -> 1.2345e-2  12.345e-3
   318  		// 12345   ([12345]e+5) -> 1.2345e4  12.345e3
   319  		d := int(n.Exp-1) % maxInt
   320  		if d < 0 {
   321  			d += maxInt
   322  		}
   323  		numInt += d
   324  	}
   325  
   326  	p := len(n.Digits)
   327  	if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
   328  		p = maxSig
   329  	}
   330  	if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p {
   331  		p = numInt + maxFrac
   332  	}
   333  	n.round(r.Mode, p)
   334  
   335  	n.Comma = uint8(numInt)
   336  	n.End = int32(len(n.Digits))
   337  	if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig {
   338  		n.End = minSig
   339  	}
   340  	return n
   341  }
   342  
   343  // appendScientific appends a formatted number to dst. It returns two possible
   344  // insertion points for padding.
   345  func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
   346  	if dst, ok := f.renderSpecial(dst, n); ok {
   347  		return dst, 0, 0
   348  	}
   349  	digits := n.Digits
   350  	numInt := int(n.Comma)
   351  	numFrac := int(n.End) - int(n.Comma)
   352  
   353  	var intDigits, fracDigits []byte
   354  	if numInt <= len(digits) {
   355  		intDigits = digits[:numInt]
   356  		fracDigits = digits[numInt:]
   357  	} else {
   358  		intDigits = digits
   359  	}
   360  	neg := n.Neg
   361  	affix, suffix := f.getAffixes(neg)
   362  	dst = appendAffix(dst, f, affix, neg)
   363  	savedLen := len(dst)
   364  
   365  	i := 0
   366  	for ; i < len(intDigits); i++ {
   367  		dst = f.AppendDigit(dst, intDigits[i])
   368  		if f.needsSep(numInt - i) {
   369  			dst = append(dst, f.Symbol(SymGroup)...)
   370  		}
   371  	}
   372  	for ; i < numInt; i++ {
   373  		dst = f.AppendDigit(dst, 0)
   374  		if f.needsSep(numInt - i) {
   375  			dst = append(dst, f.Symbol(SymGroup)...)
   376  		}
   377  	}
   378  
   379  	if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
   380  		dst = append(dst, f.Symbol(SymDecimal)...)
   381  	}
   382  	i = 0
   383  	for ; i < len(fracDigits); i++ {
   384  		dst = f.AppendDigit(dst, fracDigits[i])
   385  	}
   386  	for ; i < numFrac; i++ {
   387  		dst = f.AppendDigit(dst, 0)
   388  	}
   389  
   390  	// exp
   391  	buf := [12]byte{}
   392  	// TODO: use exponential if superscripting is not available (no Latin
   393  	// numbers or no tags) and use exponential in all other cases.
   394  	exp := n.Exp - int32(n.Comma)
   395  	exponential := f.Symbol(SymExponential)
   396  	if exponential == "E" {
   397  		dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
   398  		dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
   399  		dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
   400  		dst = f.AppendDigit(dst, 1)
   401  		dst = f.AppendDigit(dst, 0)
   402  		switch {
   403  		case exp < 0:
   404  			dst = append(dst, superMinus...)
   405  			exp = -exp
   406  		case f.Flags&AlwaysExpSign != 0:
   407  			dst = append(dst, superPlus...)
   408  		}
   409  		b = strconv.AppendUint(buf[:0], uint64(exp), 10)
   410  		for i := len(b); i < int(f.MinExponentDigits); i++ {
   411  			dst = append(dst, superDigits[0]...)
   412  		}
   413  		for _, c := range b {
   414  			dst = append(dst, superDigits[c-'0']...)
   415  		}
   416  	} else {
   417  		dst = append(dst, exponential...)
   418  		switch {
   419  		case exp < 0:
   420  			dst = append(dst, f.Symbol(SymMinusSign)...)
   421  			exp = -exp
   422  		case f.Flags&AlwaysExpSign != 0:
   423  			dst = append(dst, f.Symbol(SymPlusSign)...)
   424  		}
   425  		b = strconv.AppendUint(buf[:0], uint64(exp), 10)
   426  		for i := len(b); i < int(f.MinExponentDigits); i++ {
   427  			dst = f.AppendDigit(dst, 0)
   428  		}
   429  		for _, c := range b {
   430  			dst = f.AppendDigit(dst, c-'0')
   431  		}
   432  	}
   433  	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
   434  }
   435  
   436  const (
   437  	superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
   438  	superPlus  = "\u207A" // SUPERSCRIPT PLUS SIGN
   439  )
   440  
   441  var (
   442  	// Note: the digits are not sequential!!!
   443  	superDigits = []string{
   444  		"\u2070", // SUPERSCRIPT DIGIT ZERO
   445  		"\u00B9", // SUPERSCRIPT DIGIT ONE
   446  		"\u00B2", // SUPERSCRIPT DIGIT TWO
   447  		"\u00B3", // SUPERSCRIPT DIGIT THREE
   448  		"\u2074", // SUPERSCRIPT DIGIT FOUR
   449  		"\u2075", // SUPERSCRIPT DIGIT FIVE
   450  		"\u2076", // SUPERSCRIPT DIGIT SIX
   451  		"\u2077", // SUPERSCRIPT DIGIT SEVEN
   452  		"\u2078", // SUPERSCRIPT DIGIT EIGHT
   453  		"\u2079", // SUPERSCRIPT DIGIT NINE
   454  	}
   455  )
   456  
   457  func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
   458  	str := f.Affix
   459  	if str != "" {
   460  		if f.NegOffset > 0 {
   461  			if neg {
   462  				str = str[f.NegOffset:]
   463  			} else {
   464  				str = str[:f.NegOffset]
   465  			}
   466  		}
   467  		sufStart := 1 + str[0]
   468  		affix = str[1:sufStart]
   469  		suffix = str[sufStart+1:]
   470  	}
   471  	// TODO: introduce a NeedNeg sign to indicate if the left pattern already
   472  	// has a sign marked?
   473  	if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
   474  		affix = "-" + affix
   475  	}
   476  	return affix, suffix
   477  }
   478  
   479  func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) {
   480  	if d.NaN {
   481  		return fmtNaN(dst, f), true
   482  	}
   483  	if d.Inf {
   484  		return fmtInfinite(dst, f, d), true
   485  	}
   486  	return dst, false
   487  }
   488  
   489  func fmtNaN(dst []byte, f *Formatter) []byte {
   490  	return append(dst, f.Symbol(SymNan)...)
   491  }
   492  
   493  func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte {
   494  	affix, suffix := f.getAffixes(d.Neg)
   495  	dst = appendAffix(dst, f, affix, d.Neg)
   496  	dst = append(dst, f.Symbol(SymInfinity)...)
   497  	dst = appendAffix(dst, f, suffix, d.Neg)
   498  	return dst
   499  }
   500  
   501  func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
   502  	quoting := false
   503  	escaping := false
   504  	for _, r := range affix {
   505  		switch {
   506  		case escaping:
   507  			// escaping occurs both inside and outside of quotes
   508  			dst = append(dst, string(r)...)
   509  			escaping = false
   510  		case r == '\\':
   511  			escaping = true
   512  		case r == '\'':
   513  			quoting = !quoting
   514  		case quoting:
   515  			dst = append(dst, string(r)...)
   516  		case r == '%':
   517  			if f.DigitShift == 3 {
   518  				dst = append(dst, f.Symbol(SymPerMille)...)
   519  			} else {
   520  				dst = append(dst, f.Symbol(SymPercentSign)...)
   521  			}
   522  		case r == '-' || r == '+':
   523  			if neg {
   524  				dst = append(dst, f.Symbol(SymMinusSign)...)
   525  			} else if f.Flags&ElideSign == 0 {
   526  				dst = append(dst, f.Symbol(SymPlusSign)...)
   527  			} else {
   528  				dst = append(dst, ' ')
   529  			}
   530  		default:
   531  			dst = append(dst, string(r)...)
   532  		}
   533  	}
   534  	return dst
   535  }