encoding/jsonpb: improve and fix unmarshaling of Duration

Change use of regular expression to manually parsing the value.

Allow value with + symbol in front, e.g. "+3s". Previous regex missed
this.

Do not allow values without numbers, e.g. "-s". Previous regex missed
this as well.

name                  old time/op    new time/op    delta
Unmarshal_Duration-4    1.96µs ± 0%    1.24µs ± 0%   ~     (p=1.000 n=1+1)

name                  old alloc/op   new alloc/op   delta
Unmarshal_Duration-4      703B ± 0%      512B ± 0%   ~     (p=1.000 n=1+1)

name                  old allocs/op  new allocs/op  delta
Unmarshal_Duration-4      20.0 ± 0%      17.0 ± 0%   ~     (p=1.000 n=1+1)

Change-Id: I4db58d70f55607213631c49d698ee6a048b5e094
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170012
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/jsonpb/decode_test.go b/encoding/jsonpb/decode_test.go
index e7f4239..587b2b4 100644
--- a/encoding/jsonpb/decode_test.go
+++ b/encoding/jsonpb/decode_test.go
@@ -1757,6 +1757,11 @@
 		inputText:    `"-3s"`,
 		wantMessage:  &knownpb.Duration{Seconds: -3},
 	}, {
+		desc:         "Duration with plus sign",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"+3s"`,
+		wantMessage:  &knownpb.Duration{Seconds: 3},
+	}, {
 		desc:         "Duration with nanos",
 		inputMessage: &knownpb.Duration{},
 		inputText:    `"0.001s"`,
@@ -1767,6 +1772,16 @@
 		inputText:    `"-0.001s"`,
 		wantMessage:  &knownpb.Duration{Nanos: -1e6},
 	}, {
+		desc:         "Duration with -nanos",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"-.001s"`,
+		wantMessage:  &knownpb.Duration{Nanos: -1e6},
+	}, {
+		desc:         "Duration with +nanos",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"+.001s"`,
+		wantMessage:  &knownpb.Duration{Nanos: 1e6},
+	}, {
 		desc:         "Duration with -secs -nanos",
 		inputMessage: &knownpb.Duration{},
 		inputText:    `"-123.000000450s"`,
@@ -1799,7 +1814,7 @@
 	}, {
 		desc:         "Duration with nanos beyond 9 digits",
 		inputMessage: &knownpb.Duration{},
-		inputText:    `"0.9999999990s"`,
+		inputText:    `"0.1000000000s"`,
 		wantErr:      true,
 	}, {
 		desc:         "Duration without suffix s",
@@ -1807,6 +1822,21 @@
 		inputText:    `"123"`,
 		wantErr:      true,
 	}, {
+		desc:         "Duration invalid signed fraction",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"123.+123s"`,
+		wantErr:      true,
+	}, {
+		desc:         "Duration invalid multiple .",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"123.123.s"`,
+		wantErr:      true,
+	}, {
+		desc:         "Duration invalid integer",
+		inputMessage: &knownpb.Duration{},
+		inputText:    `"01s"`,
+		wantErr:      true,
+	}, {
 		desc:         "Timestamp zero",
 		inputMessage: &knownpb.Timestamp{},
 		inputText:    `"1970-01-01T00:00:00Z"`,