number.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright 2018 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. package text
  5. import (
  6. "bytes"
  7. "io"
  8. "math"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "google.golang.org/protobuf/internal/errors"
  13. )
  14. // marshalNumber encodes v as either a Bool, Int, Uint, Float32, or Float64.
  15. func (p *encoder) marshalNumber(v Value) error {
  16. var err error
  17. p.out, err = appendNumber(p.out, v)
  18. return err
  19. }
  20. func appendNumber(out []byte, v Value) ([]byte, error) {
  21. if len(v.raw) > 0 {
  22. switch v.Type() {
  23. case Bool, Int, Uint, Float32, Float64:
  24. return append(out, v.raw...), nil
  25. }
  26. }
  27. switch v.Type() {
  28. case Bool:
  29. if b, _ := v.Bool(); b {
  30. return append(out, "true"...), nil
  31. } else {
  32. return append(out, "false"...), nil
  33. }
  34. case Int:
  35. return strconv.AppendInt(out, int64(v.num), 10), nil
  36. case Uint:
  37. return strconv.AppendUint(out, uint64(v.num), 10), nil
  38. case Float32:
  39. return appendFloat(out, v, 32)
  40. case Float64:
  41. return appendFloat(out, v, 64)
  42. default:
  43. return nil, errors.New("invalid type %v, expected bool or number", v.Type())
  44. }
  45. }
  46. func appendFloat(out []byte, v Value, bitSize int) ([]byte, error) {
  47. switch n := math.Float64frombits(v.num); {
  48. case math.IsNaN(n):
  49. return append(out, "nan"...), nil
  50. case math.IsInf(n, +1):
  51. return append(out, "inf"...), nil
  52. case math.IsInf(n, -1):
  53. return append(out, "-inf"...), nil
  54. default:
  55. return strconv.AppendFloat(out, n, 'g', -1, bitSize), nil
  56. }
  57. }
  58. // These regular expressions were derived by reverse engineering the C++ code
  59. // in tokenizer.cc and text_format.cc.
  60. var (
  61. literals = map[string]interface{}{
  62. // These exact literals are the ones supported in C++.
  63. // In C++, a 1-bit unsigned integers is also allowed to represent
  64. // a boolean. This is handled in Value.Bool.
  65. "t": true,
  66. "true": true,
  67. "True": true,
  68. "f": false,
  69. "false": false,
  70. "False": false,
  71. // C++ permits "-nan" and the case-insensitive variants of these.
  72. // However, Go continues to be case-sensitive.
  73. "nan": math.NaN(),
  74. "inf": math.Inf(+1),
  75. "-inf": math.Inf(-1),
  76. }
  77. literalRegexp = regexp.MustCompile("^-?[a-zA-Z]+")
  78. intRegexp = regexp.MustCompile("^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*)")
  79. floatRegexp = regexp.MustCompile("^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)")
  80. )
  81. // unmarshalNumber decodes a Bool, Int, Uint, or Float64 from the input.
  82. func (p *decoder) unmarshalNumber() (Value, error) {
  83. v, n, err := consumeNumber(p.in)
  84. p.consume(n)
  85. return v, err
  86. }
  87. func consumeNumber(in []byte) (Value, int, error) {
  88. if len(in) == 0 {
  89. return Value{}, 0, io.ErrUnexpectedEOF
  90. }
  91. if n := matchWithDelim(literalRegexp, in); n > 0 {
  92. if v, ok := literals[string(in[:n])]; ok {
  93. return rawValueOf(v, in[:n:n]), n, nil
  94. }
  95. }
  96. if n := matchWithDelim(floatRegexp, in); n > 0 {
  97. if bytes.ContainsAny(in[:n], ".eEfF") {
  98. s := strings.TrimRight(string(in[:n]), "fF")
  99. // Always decode float as 64-bit.
  100. f, err := strconv.ParseFloat(s, 64)
  101. if err != nil {
  102. return Value{}, 0, err
  103. }
  104. return rawValueOf(f, in[:n:n]), n, nil
  105. }
  106. }
  107. if n := matchWithDelim(intRegexp, in); n > 0 {
  108. if in[0] == '-' {
  109. v, err := strconv.ParseInt(string(in[:n]), 0, 64)
  110. if err != nil {
  111. return Value{}, 0, err
  112. }
  113. return rawValueOf(v, in[:n:n]), n, nil
  114. } else {
  115. v, err := strconv.ParseUint(string(in[:n]), 0, 64)
  116. if err != nil {
  117. return Value{}, 0, err
  118. }
  119. return rawValueOf(v, in[:n:n]), n, nil
  120. }
  121. }
  122. return Value{}, 0, newSyntaxError("invalid %q as number or bool", errRegexp.Find(in))
  123. }