| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- // Copyright 2018 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package text
- import (
- "bytes"
- "io"
- "math"
- "regexp"
- "strconv"
- "strings"
- "github.com/golang/protobuf/v2/internal/errors"
- )
- // marshalNumber encodes v as either a Bool, Int, Uint, or Float.
- func (p *encoder) marshalNumber(v Value) error {
- var err error
- p.out, err = appendNumber(p.out, v)
- return err
- }
- func appendNumber(out []byte, v Value) ([]byte, error) {
- if len(v.raw) > 0 {
- switch v.Type() {
- case Bool, Int, Uint, Float:
- return append(out, v.raw...), nil
- }
- }
- switch v.Type() {
- case Bool:
- if b, _ := v.Bool(); b {
- return append(out, "true"...), nil
- } else {
- return append(out, "false"...), nil
- }
- case Int:
- return strconv.AppendInt(out, int64(v.num), 10), nil
- case Uint:
- return strconv.AppendUint(out, uint64(v.num), 10), nil
- case Float:
- switch n := math.Float64frombits(v.num); {
- case math.IsNaN(n):
- return append(out, "nan"...), nil
- case math.IsInf(n, +1):
- return append(out, "inf"...), nil
- case math.IsInf(n, -1):
- return append(out, "-inf"...), nil
- default:
- return strconv.AppendFloat(out, n, 'g', -1, 64), nil
- }
- default:
- return nil, errors.New("invalid type %v, expected bool or number", v.Type())
- }
- }
- // These regular expressions were derived by reverse engineering the C++ code
- // in tokenizer.cc and text_format.cc.
- var (
- literals = map[string]interface{}{
- // These exact literals are the ones supported in C++.
- // In C++, a 1-bit unsigned integers is also allowed to represent
- // a boolean. This is handled in Value.Bool.
- "t": true,
- "true": true,
- "True": true,
- "f": false,
- "false": false,
- "False": false,
- // C++ permits "-nan" and the case-insensitive variants of these.
- // However, Go continues to be case-sensitive.
- "nan": math.NaN(),
- "inf": math.Inf(+1),
- "-inf": math.Inf(-1),
- }
- literalRegexp = regexp.MustCompile("^-?[a-zA-Z]+")
- intRegexp = regexp.MustCompile("^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*)")
- floatRegexp = regexp.MustCompile("^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)")
- )
- // unmarshalNumber decodes a Bool, Int, Uint, or Float from the input.
- func (p *decoder) unmarshalNumber() (Value, error) {
- v, n, err := consumeNumber(p.in)
- p.consume(n)
- return v, err
- }
- func consumeNumber(in []byte) (Value, int, error) {
- if len(in) == 0 {
- return Value{}, 0, io.ErrUnexpectedEOF
- }
- if n := matchWithDelim(literalRegexp, in); n > 0 {
- if v, ok := literals[string(in[:n])]; ok {
- return rawValueOf(v, in[:n:n]), n, nil
- }
- }
- if n := matchWithDelim(floatRegexp, in); n > 0 {
- if bytes.ContainsAny(in[:n], ".eEfF") {
- s := strings.TrimRight(string(in[:n]), "fF")
- f, err := strconv.ParseFloat(s, 64)
- if err != nil {
- return Value{}, 0, err
- }
- return rawValueOf(f, in[:n:n]), n, nil
- }
- }
- if n := matchWithDelim(intRegexp, in); n > 0 {
- if in[0] == '-' {
- v, err := strconv.ParseInt(string(in[:n]), 0, 64)
- if err != nil {
- return Value{}, 0, err
- }
- return rawValueOf(v, in[:n:n]), n, nil
- } else {
- v, err := strconv.ParseUint(string(in[:n]), 0, 64)
- if err != nil {
- return Value{}, 0, err
- }
- return rawValueOf(v, in[:n:n]), n, nil
- }
- }
- return Value{}, 0, newSyntaxError("invalid %q as number or bool", errRegexp.Find(in))
- }
|