github.com/go-enjin/golang-org-x-text@v0.12.1-enjin.2/message/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 message
     6  
     7  import (
     8  	"bytes"
     9  	"strconv"
    10  	"unicode/utf8"
    11  
    12  	"github.com/go-enjin/golang-org-x-text/internal/format"
    13  )
    14  
    15  const (
    16  	ldigits = "0123456789abcdefx"
    17  	udigits = "0123456789ABCDEFX"
    18  )
    19  
    20  const (
    21  	signed   = true
    22  	unsigned = false
    23  )
    24  
    25  // A formatInfo is the raw formatter used by Printf etc.
    26  // It prints into a buffer that must be set up separately.
    27  type formatInfo struct {
    28  	buf *bytes.Buffer
    29  
    30  	format.Parser
    31  
    32  	// intbuf is large enough to store %b of an int64 with a sign and
    33  	// avoids padding at the end of the struct on 32 bit architectures.
    34  	intbuf [68]byte
    35  }
    36  
    37  func (f *formatInfo) init(buf *bytes.Buffer) {
    38  	f.ClearFlags()
    39  	f.buf = buf
    40  }
    41  
    42  // writePadding generates n bytes of padding.
    43  func (f *formatInfo) writePadding(n int) {
    44  	if n <= 0 { // No padding bytes needed.
    45  		return
    46  	}
    47  	f.buf.Grow(n)
    48  	// Decide which byte the padding should be filled with.
    49  	padByte := byte(' ')
    50  	if f.Zero {
    51  		padByte = byte('0')
    52  	}
    53  	// Fill padding with padByte.
    54  	for i := 0; i < n; i++ {
    55  		f.buf.WriteByte(padByte) // TODO: make more efficient.
    56  	}
    57  }
    58  
    59  // pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
    60  func (f *formatInfo) pad(b []byte) {
    61  	if !f.WidthPresent || f.Width == 0 {
    62  		f.buf.Write(b)
    63  		return
    64  	}
    65  	width := f.Width - utf8.RuneCount(b)
    66  	if !f.Minus {
    67  		// left padding
    68  		f.writePadding(width)
    69  		f.buf.Write(b)
    70  	} else {
    71  		// right padding
    72  		f.buf.Write(b)
    73  		f.writePadding(width)
    74  	}
    75  }
    76  
    77  // padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
    78  func (f *formatInfo) padString(s string) {
    79  	if !f.WidthPresent || f.Width == 0 {
    80  		f.buf.WriteString(s)
    81  		return
    82  	}
    83  	width := f.Width - utf8.RuneCountInString(s)
    84  	if !f.Minus {
    85  		// left padding
    86  		f.writePadding(width)
    87  		f.buf.WriteString(s)
    88  	} else {
    89  		// right padding
    90  		f.buf.WriteString(s)
    91  		f.writePadding(width)
    92  	}
    93  }
    94  
    95  // fmt_boolean formats a boolean.
    96  func (f *formatInfo) fmt_boolean(v bool) {
    97  	if v {
    98  		f.padString("true")
    99  	} else {
   100  		f.padString("false")
   101  	}
   102  }
   103  
   104  // fmt_unicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'".
   105  func (f *formatInfo) fmt_unicode(u uint64) {
   106  	buf := f.intbuf[0:]
   107  
   108  	// With default precision set the maximum needed buf length is 18
   109  	// for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
   110  	// into the already allocated intbuf with a capacity of 68 bytes.
   111  	prec := 4
   112  	if f.PrecPresent && f.Prec > 4 {
   113  		prec = f.Prec
   114  		// Compute space needed for "U+" , number, " '", character, "'".
   115  		width := 2 + prec + 2 + utf8.UTFMax + 1
   116  		if width > len(buf) {
   117  			buf = make([]byte, width)
   118  		}
   119  	}
   120  
   121  	// Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left.
   122  	i := len(buf)
   123  
   124  	// For %#U we want to add a space and a quoted character at the end of the buffer.
   125  	if f.Sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
   126  		i--
   127  		buf[i] = '\''
   128  		i -= utf8.RuneLen(rune(u))
   129  		utf8.EncodeRune(buf[i:], rune(u))
   130  		i--
   131  		buf[i] = '\''
   132  		i--
   133  		buf[i] = ' '
   134  	}
   135  	// Format the Unicode code point u as a hexadecimal number.
   136  	for u >= 16 {
   137  		i--
   138  		buf[i] = udigits[u&0xF]
   139  		prec--
   140  		u >>= 4
   141  	}
   142  	i--
   143  	buf[i] = udigits[u]
   144  	prec--
   145  	// Add zeros in front of the number until requested precision is reached.
   146  	for prec > 0 {
   147  		i--
   148  		buf[i] = '0'
   149  		prec--
   150  	}
   151  	// Add a leading "U+".
   152  	i--
   153  	buf[i] = '+'
   154  	i--
   155  	buf[i] = 'U'
   156  
   157  	oldZero := f.Zero
   158  	f.Zero = false
   159  	f.pad(buf[i:])
   160  	f.Zero = oldZero
   161  }
   162  
   163  // fmt_integer formats signed and unsigned integers.
   164  func (f *formatInfo) fmt_integer(u uint64, base int, isSigned bool, digits string) {
   165  	negative := isSigned && int64(u) < 0
   166  	if negative {
   167  		u = -u
   168  	}
   169  
   170  	buf := f.intbuf[0:]
   171  	// The already allocated f.intbuf with a capacity of 68 bytes
   172  	// is large enough for integer formatting when no precision or width is set.
   173  	if f.WidthPresent || f.PrecPresent {
   174  		// Account 3 extra bytes for possible addition of a sign and "0x".
   175  		width := 3 + f.Width + f.Prec // wid and prec are always positive.
   176  		if width > len(buf) {
   177  			// We're going to need a bigger boat.
   178  			buf = make([]byte, width)
   179  		}
   180  	}
   181  
   182  	// Two ways to ask for extra leading zero digits: %.3d or %03d.
   183  	// If both are specified the f.zero flag is ignored and
   184  	// padding with spaces is used instead.
   185  	prec := 0
   186  	if f.PrecPresent {
   187  		prec = f.Prec
   188  		// Precision of 0 and value of 0 means "print nothing" but padding.
   189  		if prec == 0 && u == 0 {
   190  			oldZero := f.Zero
   191  			f.Zero = false
   192  			f.writePadding(f.Width)
   193  			f.Zero = oldZero
   194  			return
   195  		}
   196  	} else if f.Zero && f.WidthPresent {
   197  		prec = f.Width
   198  		if negative || f.Plus || f.Space {
   199  			prec-- // leave room for sign
   200  		}
   201  	}
   202  
   203  	// Because printing is easier right-to-left: format u into buf, ending at buf[i].
   204  	// We could make things marginally faster by splitting the 32-bit case out
   205  	// into a separate block but it's not worth the duplication, so u has 64 bits.
   206  	i := len(buf)
   207  	// Use constants for the division and modulo for more efficient code.
   208  	// Switch cases ordered by popularity.
   209  	switch base {
   210  	case 10:
   211  		for u >= 10 {
   212  			i--
   213  			next := u / 10
   214  			buf[i] = byte('0' + u - next*10)
   215  			u = next
   216  		}
   217  	case 16:
   218  		for u >= 16 {
   219  			i--
   220  			buf[i] = digits[u&0xF]
   221  			u >>= 4
   222  		}
   223  	case 8:
   224  		for u >= 8 {
   225  			i--
   226  			buf[i] = byte('0' + u&7)
   227  			u >>= 3
   228  		}
   229  	case 2:
   230  		for u >= 2 {
   231  			i--
   232  			buf[i] = byte('0' + u&1)
   233  			u >>= 1
   234  		}
   235  	default:
   236  		panic("fmt: unknown base; can't happen")
   237  	}
   238  	i--
   239  	buf[i] = digits[u]
   240  	for i > 0 && prec > len(buf)-i {
   241  		i--
   242  		buf[i] = '0'
   243  	}
   244  
   245  	// Various prefixes: 0x, -, etc.
   246  	if f.Sharp {
   247  		switch base {
   248  		case 8:
   249  			if buf[i] != '0' {
   250  				i--
   251  				buf[i] = '0'
   252  			}
   253  		case 16:
   254  			// Add a leading 0x or 0X.
   255  			i--
   256  			buf[i] = digits[16]
   257  			i--
   258  			buf[i] = '0'
   259  		}
   260  	}
   261  
   262  	if negative {
   263  		i--
   264  		buf[i] = '-'
   265  	} else if f.Plus {
   266  		i--
   267  		buf[i] = '+'
   268  	} else if f.Space {
   269  		i--
   270  		buf[i] = ' '
   271  	}
   272  
   273  	// Left padding with zeros has already been handled like precision earlier
   274  	// or the f.zero flag is ignored due to an explicitly set precision.
   275  	oldZero := f.Zero
   276  	f.Zero = false
   277  	f.pad(buf[i:])
   278  	f.Zero = oldZero
   279  }
   280  
   281  // truncate truncates the string to the specified precision, if present.
   282  func (f *formatInfo) truncate(s string) string {
   283  	if f.PrecPresent {
   284  		n := f.Prec
   285  		for i := range s {
   286  			n--
   287  			if n < 0 {
   288  				return s[:i]
   289  			}
   290  		}
   291  	}
   292  	return s
   293  }
   294  
   295  // fmt_s formats a string.
   296  func (f *formatInfo) fmt_s(s string) {
   297  	s = f.truncate(s)
   298  	f.padString(s)
   299  }
   300  
   301  // fmt_sbx formats a string or byte slice as a hexadecimal encoding of its bytes.
   302  func (f *formatInfo) fmt_sbx(s string, b []byte, digits string) {
   303  	length := len(b)
   304  	if b == nil {
   305  		// No byte slice present. Assume string s should be encoded.
   306  		length = len(s)
   307  	}
   308  	// Set length to not process more bytes than the precision demands.
   309  	if f.PrecPresent && f.Prec < length {
   310  		length = f.Prec
   311  	}
   312  	// Compute width of the encoding taking into account the f.sharp and f.space flag.
   313  	width := 2 * length
   314  	if width > 0 {
   315  		if f.Space {
   316  			// Each element encoded by two hexadecimals will get a leading 0x or 0X.
   317  			if f.Sharp {
   318  				width *= 2
   319  			}
   320  			// Elements will be separated by a space.
   321  			width += length - 1
   322  		} else if f.Sharp {
   323  			// Only a leading 0x or 0X will be added for the whole string.
   324  			width += 2
   325  		}
   326  	} else { // The byte slice or string that should be encoded is empty.
   327  		if f.WidthPresent {
   328  			f.writePadding(f.Width)
   329  		}
   330  		return
   331  	}
   332  	// Handle padding to the left.
   333  	if f.WidthPresent && f.Width > width && !f.Minus {
   334  		f.writePadding(f.Width - width)
   335  	}
   336  	// Write the encoding directly into the output buffer.
   337  	buf := f.buf
   338  	if f.Sharp {
   339  		// Add leading 0x or 0X.
   340  		buf.WriteByte('0')
   341  		buf.WriteByte(digits[16])
   342  	}
   343  	var c byte
   344  	for i := 0; i < length; i++ {
   345  		if f.Space && i > 0 {
   346  			// Separate elements with a space.
   347  			buf.WriteByte(' ')
   348  			if f.Sharp {
   349  				// Add leading 0x or 0X for each element.
   350  				buf.WriteByte('0')
   351  				buf.WriteByte(digits[16])
   352  			}
   353  		}
   354  		if b != nil {
   355  			c = b[i] // Take a byte from the input byte slice.
   356  		} else {
   357  			c = s[i] // Take a byte from the input string.
   358  		}
   359  		// Encode each byte as two hexadecimal digits.
   360  		buf.WriteByte(digits[c>>4])
   361  		buf.WriteByte(digits[c&0xF])
   362  	}
   363  	// Handle padding to the right.
   364  	if f.WidthPresent && f.Width > width && f.Minus {
   365  		f.writePadding(f.Width - width)
   366  	}
   367  }
   368  
   369  // fmt_sx formats a string as a hexadecimal encoding of its bytes.
   370  func (f *formatInfo) fmt_sx(s, digits string) {
   371  	f.fmt_sbx(s, nil, digits)
   372  }
   373  
   374  // fmt_bx formats a byte slice as a hexadecimal encoding of its bytes.
   375  func (f *formatInfo) fmt_bx(b []byte, digits string) {
   376  	f.fmt_sbx("", b, digits)
   377  }
   378  
   379  // fmt_q formats a string as a double-quoted, escaped Go string constant.
   380  // If f.sharp is set a raw (backquoted) string may be returned instead
   381  // if the string does not contain any control characters other than tab.
   382  func (f *formatInfo) fmt_q(s string) {
   383  	s = f.truncate(s)
   384  	if f.Sharp && strconv.CanBackquote(s) {
   385  		f.padString("`" + s + "`")
   386  		return
   387  	}
   388  	buf := f.intbuf[:0]
   389  	if f.Plus {
   390  		f.pad(strconv.AppendQuoteToASCII(buf, s))
   391  	} else {
   392  		f.pad(strconv.AppendQuote(buf, s))
   393  	}
   394  }
   395  
   396  // fmt_c formats an integer as a Unicode character.
   397  // If the character is not valid Unicode, it will print '\ufffd'.
   398  func (f *formatInfo) fmt_c(c uint64) {
   399  	r := rune(c)
   400  	if c > utf8.MaxRune {
   401  		r = utf8.RuneError
   402  	}
   403  	buf := f.intbuf[:0]
   404  	w := utf8.EncodeRune(buf[:utf8.UTFMax], r)
   405  	f.pad(buf[:w])
   406  }
   407  
   408  // fmt_qc formats an integer as a single-quoted, escaped Go character constant.
   409  // If the character is not valid Unicode, it will print '\ufffd'.
   410  func (f *formatInfo) fmt_qc(c uint64) {
   411  	r := rune(c)
   412  	if c > utf8.MaxRune {
   413  		r = utf8.RuneError
   414  	}
   415  	buf := f.intbuf[:0]
   416  	if f.Plus {
   417  		f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
   418  	} else {
   419  		f.pad(strconv.AppendQuoteRune(buf, r))
   420  	}
   421  }
   422  
   423  // fmt_float formats a float64. It assumes that verb is a valid format specifier
   424  // for strconv.AppendFloat and therefore fits into a byte.
   425  func (f *formatInfo) fmt_float(v float64, size int, verb rune, prec int) {
   426  	// Explicit precision in format specifier overrules default precision.
   427  	if f.PrecPresent {
   428  		prec = f.Prec
   429  	}
   430  	// Format number, reserving space for leading + sign if needed.
   431  	num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
   432  	if num[1] == '-' || num[1] == '+' {
   433  		num = num[1:]
   434  	} else {
   435  		num[0] = '+'
   436  	}
   437  	// f.space means to add a leading space instead of a "+" sign unless
   438  	// the sign is explicitly asked for by f.plus.
   439  	if f.Space && num[0] == '+' && !f.Plus {
   440  		num[0] = ' '
   441  	}
   442  	// Special handling for infinities and NaN,
   443  	// which don't look like a number so shouldn't be padded with zeros.
   444  	if num[1] == 'I' || num[1] == 'N' {
   445  		oldZero := f.Zero
   446  		f.Zero = false
   447  		// Remove sign before NaN if not asked for.
   448  		if num[1] == 'N' && !f.Space && !f.Plus {
   449  			num = num[1:]
   450  		}
   451  		f.pad(num)
   452  		f.Zero = oldZero
   453  		return
   454  	}
   455  	// The sharp flag forces printing a decimal point for non-binary formats
   456  	// and retains trailing zeros, which we may need to restore.
   457  	if f.Sharp && verb != 'b' {
   458  		digits := 0
   459  		switch verb {
   460  		case 'v', 'g', 'G':
   461  			digits = prec
   462  			// If no precision is set explicitly use a precision of 6.
   463  			if digits == -1 {
   464  				digits = 6
   465  			}
   466  		}
   467  
   468  		// Buffer pre-allocated with enough room for
   469  		// exponent notations of the form "e+123".
   470  		var tailBuf [5]byte
   471  		tail := tailBuf[:0]
   472  
   473  		hasDecimalPoint := false
   474  		// Starting from i = 1 to skip sign at num[0].
   475  		for i := 1; i < len(num); i++ {
   476  			switch num[i] {
   477  			case '.':
   478  				hasDecimalPoint = true
   479  			case 'e', 'E':
   480  				tail = append(tail, num[i:]...)
   481  				num = num[:i]
   482  			default:
   483  				digits--
   484  			}
   485  		}
   486  		if !hasDecimalPoint {
   487  			num = append(num, '.')
   488  		}
   489  		for digits > 0 {
   490  			num = append(num, '0')
   491  			digits--
   492  		}
   493  		num = append(num, tail...)
   494  	}
   495  	// We want a sign if asked for and if the sign is not positive.
   496  	if f.Plus || num[0] != '+' {
   497  		// If we're zero padding to the left we want the sign before the leading zeros.
   498  		// Achieve this by writing the sign out and then padding the unsigned number.
   499  		if f.Zero && f.WidthPresent && f.Width > len(num) {
   500  			f.buf.WriteByte(num[0])
   501  			f.writePadding(f.Width - len(num))
   502  			f.buf.Write(num[1:])
   503  			return
   504  		}
   505  		f.pad(num)
   506  		return
   507  	}
   508  	// No sign to show and the number is positive; just print the unsigned number.
   509  	f.pad(num[1:])
   510  }