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