Eric Salo would like Michael Stapelberg and Nicolas Hillegeer to review this change.
defval: support hex and octal default values for numeric fields
Update defval.Unmarshal to use base 0 for strconv.ParseInt and strconv.ParseUint, allowing it to handle hex (0x) and octal (0) string representations in dynamic descriptors, matching the flexibility of C++ and Java runtimes.
diff --git a/internal/encoding/defval/default.go b/internal/encoding/defval/default.go
index 328dc73..079a53d 100644
--- a/internal/encoding/defval/default.go
+++ b/internal/encoding/defval/default.go
@@ -69,19 +69,19 @@
}
}
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
- if v, err := strconv.ParseInt(s, 10, 32); err == nil {
+ if v, err := strconv.ParseInt(s, 0, 32); err == nil {
return protoreflect.ValueOfInt32(int32(v)), nil, nil
}
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
- if v, err := strconv.ParseInt(s, 10, 64); err == nil {
+ if v, err := strconv.ParseInt(s, 0, 64); err == nil {
return protoreflect.ValueOfInt64(int64(v)), nil, nil
}
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
- if v, err := strconv.ParseUint(s, 10, 32); err == nil {
+ if v, err := strconv.ParseUint(s, 0, 32); err == nil {
return protoreflect.ValueOfUint32(uint32(v)), nil, nil
}
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
- if v, err := strconv.ParseUint(s, 10, 64); err == nil {
+ if v, err := strconv.ParseUint(s, 0, 64); err == nil {
return protoreflect.ValueOfUint64(uint64(v)), nil, nil
}
case protoreflect.FloatKind, protoreflect.DoubleKind:
diff --git a/internal/encoding/defval/default_test.go b/internal/encoding/defval/default_test.go
index bde8ca2..bcdb9c5 100644
--- a/internal/encoding/defval/default_test.go
+++ b/internal/encoding/defval/default_test.go
@@ -103,3 +103,57 @@
})
}
}
+
+func TestUnmarshalNonDecimal(t *testing.T) {
+ V := protoreflect.ValueOf
+ tests := []struct {
+ str string
+ kind protoreflect.Kind
+ want protoreflect.Value
+ }{
+ {str: "0x10", kind: protoreflect.Int32Kind, want: V(int32(16))},
+ {str: "-0x10", kind: protoreflect.Int32Kind, want: V(int32(-16))},
+ {str: "0x10", kind: protoreflect.Int64Kind, want: V(int64(16))},
+ {str: "-0x10", kind: protoreflect.Int64Kind, want: V(int64(-16))},
+ {str: "0x10", kind: protoreflect.Uint32Kind, want: V(uint32(16))},
+ {str: "0x10", kind: protoreflect.Uint64Kind, want: V(uint64(16))},
+ {str: "020", kind: protoreflect.Int32Kind, want: V(int32(16))},
+ {str: "-020", kind: protoreflect.Int32Kind, want: V(int32(-16))},
+ {str: "020", kind: protoreflect.Int64Kind, want: V(int64(16))},
+ {str: "-020", kind: protoreflect.Int64Kind, want: V(int64(-16))},
+ {str: "020", kind: protoreflect.Uint32Kind, want: V(uint32(16))},
+ {str: "020", kind: protoreflect.Uint64Kind, want: V(uint64(16))},
+ {str: "0x100000000", kind: protoreflect.Int64Kind, want: V(int64(4294967296))},
+ {str: "-0x100000000", kind: protoreflect.Int64Kind, want: V(int64(-4294967296))},
+ {str: "0x100000000", kind: protoreflect.Uint64Kind, want: V(uint64(4294967296))},
+ {str: "040000000000", kind: protoreflect.Int64Kind, want: V(int64(4294967296))},
+ {str: "-040000000000", kind: protoreflect.Int64Kind, want: V(int64(-4294967296))},
+ {str: "040000000000", kind: protoreflect.Uint64Kind, want: V(uint64(4294967296))},
+ // MaxInt32
+ {str: "0x7fffffff", kind: protoreflect.Int32Kind, want: V(int32(2147483647))},
+ {str: "017777777777", kind: protoreflect.Int32Kind, want: V(int32(2147483647))},
+ // MaxInt64
+ {str: "0x7fffffffffffffff", kind: protoreflect.Int64Kind, want: V(int64(9223372036854775807))},
+ {str: "0777777777777777777777", kind: protoreflect.Int64Kind, want: V(int64(9223372036854775807))},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.str+"_"+tt.kind.String(), func(t *testing.T) {
+ gotVal, _, err := defval.Unmarshal(tt.str, tt.kind, nil, defval.Descriptor)
+ if err != nil {
+ t.Fatalf("Unmarshal(%q, %v, Descriptor) failed: %v", tt.str, tt.kind, err)
+ }
+ if !reflect.DeepEqual(gotVal.Interface(), tt.want.Interface()) {
+ t.Errorf("Unmarshal(%q, %v, Descriptor) = %v, want %v", tt.str, tt.kind, gotVal, tt.want)
+ }
+
+ gotVal, _, err = defval.Unmarshal(tt.str, tt.kind, nil, defval.GoTag)
+ if err != nil {
+ t.Fatalf("Unmarshal(%q, %v, GoTag) failed: %v", tt.str, tt.kind, err)
+ }
+ if !reflect.DeepEqual(gotVal.Interface(), tt.want.Interface()) {
+ t.Errorf("Unmarshal(%q, %v, GoTag) = %v, want %v", tt.str, tt.kind, gotVal, tt.want)
+ }
+ })
+ }
+}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Congratulations on opening your first change. Thank you for your contribution!
Next steps:
A maintainer will review your change and provide feedback. See
https://go.dev/doc/contribute#review for more info and tips to get your
patch through code review.
Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |