diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go index ac8105efadb403..217efe9e2ee7f4 100644 --- a/src/archive/tar/strconv.go +++ b/src/archive/tar/strconv.go @@ -213,15 +213,17 @@ func parsePAXTime(s string) (time.Time, error) { } // Parse the nanoseconds. - if strings.Trim(sn, "0123456789") != "" { - return time.Time{}, ErrHeader - } - if len(sn) < maxNanoSecondDigits { - sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad - } else { - sn = sn[:maxNanoSecondDigits] // Right truncate + // Initialize an array with '0's to handle right padding automatically. + nanoDigits := [maxNanoSecondDigits]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0'} + for i := range len(sn) { + switch c := sn[i]; { + case c < '0' || c > '9': + return time.Time{}, ErrHeader + case i < len(nanoDigits): + nanoDigits[i] = c + } } - nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + nsecs, _ := strconv.ParseInt(string(nanoDigits[:]), 10, 64) // Must succeed after validation if len(ss) > 0 && ss[0] == '-' { return time.Unix(secs, -1*nsecs), nil // Negative correction } diff --git a/src/archive/tar/strconv_test.go b/src/archive/tar/strconv_test.go index add65e272ae6d7..d411153ce2b896 100644 --- a/src/archive/tar/strconv_test.go +++ b/src/archive/tar/strconv_test.go @@ -439,3 +439,66 @@ func TestFormatPAXRecord(t *testing.T) { } } } + +func BenchmarkParsePAXTIme(b *testing.B) { + tests := []struct { + name string + in string + want time.Time + ok bool + }{ + { + name: "NoNanos", + in: "123456", + want: time.Unix(123456, 0), + ok: true, + }, + { + name: "ExactNanos", + in: "1.123456789", + want: time.Unix(1, 123456789), + ok: true, + }, + { + name: "WithNanoPadding", + in: "1.123", + want: time.Unix(1, 123000000), + ok: true, + }, + { + name: "WithNanoTruncate", + in: "1.123456789123", + want: time.Unix(1, 123456789), + ok: true, + }, + { + name: "TrailingError", + in: "1.123abc", + want: time.Time{}, + ok: false, + }, + { + name: "LeadingError", + in: "1.abc123", + want: time.Time{}, + ok: false, + }, + } + for _, tt := range tests { + b.Run(tt.name, func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ts, err := parsePAXTime(tt.in) + if (err == nil) != tt.ok { + if err != nil { + b.Fatal(err) + } + b.Fatal("expected error") + } + if !ts.Equal(tt.want) { + b.Fatalf("time mismatch: got %v, want %v", ts, tt.want) + } + } + }) + } +}