blob: f03acdf4912f464f38b28a70bcdf286abca3030b [file] [log] [blame]
Joe Tsaic9899da2018-12-06 18:34:53 -08001// 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 defval marshals and unmarshals textual forms of default values.
6//
7// This package handles both the form historically used in Go struct field tags
8// and also the form used by google.protobuf.FieldDescriptorProto.default_value
9// since they differ in superficial ways.
10package defval
11
12import (
13 "fmt"
14 "math"
15 "strconv"
16
Damien Neile89e6242019-05-13 23:55:40 -070017 ptext "google.golang.org/protobuf/internal/encoding/text"
18 errors "google.golang.org/protobuf/internal/errors"
19 pref "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaic9899da2018-12-06 18:34:53 -080020)
21
22// Format is the serialization format used to represent the default value.
23type Format int
24
25const (
26 _ Format = iota
27
28 // Descriptor uses the serialization format that protoc uses with the
29 // google.protobuf.FieldDescriptorProto.default_value field.
30 Descriptor
31
32 // GoTag uses the historical serialization format in Go struct field tags.
33 GoTag
34)
35
36// Unmarshal deserializes the default string s according to the given kind k.
Joe Tsaid8881392019-06-06 13:01:53 -070037// When k is an enum, a list of enum value descriptors must be provided.
38func Unmarshal(s string, k pref.Kind, evs pref.EnumValueDescriptors, f Format) (pref.Value, pref.EnumValueDescriptor, error) {
Joe Tsaic9899da2018-12-06 18:34:53 -080039 switch k {
40 case pref.BoolKind:
41 if f == GoTag {
42 switch s {
43 case "1":
Joe Tsaid8881392019-06-06 13:01:53 -070044 return pref.ValueOf(true), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080045 case "0":
Joe Tsaid8881392019-06-06 13:01:53 -070046 return pref.ValueOf(false), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080047 }
48 } else {
49 switch s {
50 case "true":
Joe Tsaid8881392019-06-06 13:01:53 -070051 return pref.ValueOf(true), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080052 case "false":
Joe Tsaid8881392019-06-06 13:01:53 -070053 return pref.ValueOf(false), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080054 }
55 }
56 case pref.EnumKind:
57 if f == GoTag {
Joe Tsaid8881392019-06-06 13:01:53 -070058 // Go tags use the numeric form of the enum value.
Joe Tsaic9899da2018-12-06 18:34:53 -080059 if n, err := strconv.ParseInt(s, 10, 32); err == nil {
Joe Tsaid8881392019-06-06 13:01:53 -070060 if ev := evs.ByNumber(pref.EnumNumber(n)); ev != nil {
61 return pref.ValueOf(ev.Number()), ev, nil
62 }
Joe Tsaic9899da2018-12-06 18:34:53 -080063 }
64 } else {
Joe Tsaid8881392019-06-06 13:01:53 -070065 // Descriptor default_value use the enum identifier.
66 ev := evs.ByName(pref.Name(s))
67 if ev != nil {
68 return pref.ValueOf(ev.Number()), ev, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080069 }
70 }
71 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
72 if v, err := strconv.ParseInt(s, 10, 32); err == nil {
Joe Tsaid8881392019-06-06 13:01:53 -070073 return pref.ValueOf(int32(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080074 }
75 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
76 if v, err := strconv.ParseInt(s, 10, 64); err == nil {
Joe Tsaid8881392019-06-06 13:01:53 -070077 return pref.ValueOf(int64(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080078 }
79 case pref.Uint32Kind, pref.Fixed32Kind:
80 if v, err := strconv.ParseUint(s, 10, 32); err == nil {
Joe Tsaid8881392019-06-06 13:01:53 -070081 return pref.ValueOf(uint32(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080082 }
83 case pref.Uint64Kind, pref.Fixed64Kind:
84 if v, err := strconv.ParseUint(s, 10, 64); err == nil {
Joe Tsaid8881392019-06-06 13:01:53 -070085 return pref.ValueOf(uint64(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -080086 }
87 case pref.FloatKind, pref.DoubleKind:
88 var v float64
89 var err error
90 switch s {
91 case "-inf":
92 v = math.Inf(-1)
93 case "inf":
94 v = math.Inf(+1)
95 case "nan":
96 v = math.NaN()
97 default:
98 v, err = strconv.ParseFloat(s, 64)
99 }
100 if err == nil {
101 if k == pref.FloatKind {
Joe Tsaid8881392019-06-06 13:01:53 -0700102 return pref.ValueOf(float32(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -0800103 } else {
Joe Tsaid8881392019-06-06 13:01:53 -0700104 return pref.ValueOf(float64(v)), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -0800105 }
106 }
107 case pref.StringKind:
108 // String values are already unescaped and can be used as is.
Joe Tsaid8881392019-06-06 13:01:53 -0700109 return pref.ValueOf(s), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -0800110 case pref.BytesKind:
111 if b, ok := unmarshalBytes(s); ok {
Joe Tsaid8881392019-06-06 13:01:53 -0700112 return pref.ValueOf(b), nil, nil
Joe Tsaic9899da2018-12-06 18:34:53 -0800113 }
114 }
Joe Tsaid8881392019-06-06 13:01:53 -0700115 return pref.Value{}, nil, errors.New("invalid default value for %v: %q", k, s)
Joe Tsaic9899da2018-12-06 18:34:53 -0800116}
117
118// Marshal serializes v as the default string according to the given kind k.
Joe Tsaid8881392019-06-06 13:01:53 -0700119// When specifying the Descriptor format for an enum kind, the associated
120// enum value descriptor must be provided.
121func Marshal(v pref.Value, ev pref.EnumValueDescriptor, k pref.Kind, f Format) (string, error) {
Joe Tsaic9899da2018-12-06 18:34:53 -0800122 switch k {
123 case pref.BoolKind:
124 if f == GoTag {
125 if v.Bool() {
126 return "1", nil
127 } else {
128 return "0", nil
129 }
130 } else {
131 if v.Bool() {
132 return "true", nil
133 } else {
134 return "false", nil
135 }
136 }
137 case pref.EnumKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700138 if f == GoTag {
139 return strconv.FormatInt(int64(v.Enum()), 10), nil
140 } else {
141 return string(ev.Name()), nil
142 }
Joe Tsaic9899da2018-12-06 18:34:53 -0800143 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind, pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
144 return strconv.FormatInt(v.Int(), 10), nil
145 case pref.Uint32Kind, pref.Fixed32Kind, pref.Uint64Kind, pref.Fixed64Kind:
146 return strconv.FormatUint(v.Uint(), 10), nil
147 case pref.FloatKind, pref.DoubleKind:
148 f := v.Float()
149 switch {
150 case math.IsInf(f, -1):
151 return "-inf", nil
152 case math.IsInf(f, +1):
153 return "inf", nil
154 case math.IsNaN(f):
155 return "nan", nil
156 default:
157 if k == pref.FloatKind {
158 return strconv.FormatFloat(f, 'g', -1, 32), nil
159 } else {
160 return strconv.FormatFloat(f, 'g', -1, 64), nil
161 }
162 }
163 case pref.StringKind:
164 // String values are serialized as is without any escaping.
165 return v.String(), nil
166 case pref.BytesKind:
167 if s, ok := marshalBytes(v.Bytes()); ok {
168 return s, nil
169 }
170 }
171 return "", errors.New("invalid default value for %v: %v", k, v)
172}
173
174// unmarshalBytes deserializes bytes by applying C unescaping.
175func unmarshalBytes(s string) ([]byte, bool) {
176 // Bytes values use the same escaping as the text format,
177 // however they lack the surrounding double quotes.
178 // TODO: Export unmarshalString in the text package to avoid this hack.
179 v, err := ptext.Unmarshal([]byte(`["` + s + `"]:0`))
180 if err == nil && len(v.Message()) == 1 {
181 s := v.Message()[0][0].String()
182 return []byte(s), true
183 }
184 return nil, false
185}
186
187// marshalBytes serializes bytes by using C escaping.
188// To match the exact output of protoc, this is identical to the
189// CEscape function in strutil.cc of the protoc source code.
190func marshalBytes(b []byte) (string, bool) {
191 var s []byte
192 for _, c := range b {
193 switch c {
194 case '\n':
195 s = append(s, `\n`...)
196 case '\r':
197 s = append(s, `\r`...)
198 case '\t':
199 s = append(s, `\t`...)
200 case '"':
201 s = append(s, `\"`...)
202 case '\'':
203 s = append(s, `\'`...)
204 case '\\':
205 s = append(s, `\\`...)
206 default:
207 if printableASCII := c >= 0x20 && c <= 0x7e; printableASCII {
208 s = append(s, c)
209 } else {
210 s = append(s, fmt.Sprintf(`\%03o`, c)...)
211 }
212 }
213 }
214 return string(s), true
215}