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 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 }