number.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  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 json
  5. import (
  6. "io"
  7. "math"
  8. "regexp"
  9. "strconv"
  10. "github.com/golang/protobuf/v2/internal/errors"
  11. )
  12. // marshalNumber encodes v as a Number.
  13. func (p *encoder) marshalNumber(v Value) error {
  14. var err error
  15. p.out, err = appendNumber(p.out, v)
  16. return err
  17. }
  18. func appendNumber(out []byte, v Value) ([]byte, error) {
  19. if v.Type() != Number {
  20. return nil, errors.New("invalid type %v, expected number", v.Type())
  21. }
  22. if len(v.raw) > 0 {
  23. return append(out, v.raw...), nil
  24. }
  25. n := v.Number()
  26. if math.IsInf(n, 0) || math.IsNaN(n) {
  27. return nil, errors.New("invalid number value: %v", n)
  28. }
  29. // JSON number formatting logic based on encoding/json.
  30. // See floatEncoder.encode for reference.
  31. bits := 64
  32. if float64(float32(n)) == n {
  33. bits = 32
  34. }
  35. fmt := byte('f')
  36. if abs := math.Abs(n); abs != 0 {
  37. if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
  38. fmt = 'e'
  39. }
  40. }
  41. out = strconv.AppendFloat(out, n, fmt, -1, bits)
  42. if fmt == 'e' {
  43. n := len(out)
  44. if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
  45. out[n-2] = out[n-1]
  46. out = out[:n-1]
  47. }
  48. }
  49. return out, nil
  50. }
  51. // Exact expression to match a JSON floating-point number.
  52. // JSON's grammar for floats is more restrictive than Go's grammar.
  53. var floatRegexp = regexp.MustCompile("^-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?")
  54. // unmarshalNumber decodes a Number from the input.
  55. func (p *decoder) unmarshalNumber() (Value, error) {
  56. v, n, err := consumeNumber(p.in)
  57. p.consume(n)
  58. return v, err
  59. }
  60. func consumeNumber(in []byte) (Value, int, error) {
  61. if len(in) == 0 {
  62. return Value{}, 0, io.ErrUnexpectedEOF
  63. }
  64. if n := matchWithDelim(floatRegexp, in); n > 0 {
  65. v, err := strconv.ParseFloat(string(in[:n]), 64)
  66. if err != nil {
  67. return Value{}, 0, err
  68. }
  69. return rawValueOf(v, in[:n:n]), n, nil
  70. }
  71. return Value{}, 0, newSyntaxError("invalid %q as number", errRegexp.Find(in))
  72. }