mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-24 18:39:49 +00:00
125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
|
// 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"
|
||
|
|
||
|
"google.golang.org/proto/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))
|
||
|
}
|