diff --git a/README.md b/README.md index f7ad997..d2c24da 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,40 @@ Benchmark_ToUpperBytes/fiber-12 25859680 46.43 ns/op Benchmark_ToUpperBytes/default-12 10015346 117.2 ns/op 80 B/op 1 allocs/op Benchmark_ToUpperBytes/default-12 10185375 117.8 ns/op 80 B/op 1 allocs/op +Benchmark_TrimRight/fiber-12 522399795 2.138 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRight/fiber-12 578245326 2.084 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRight/default-12 345155300 3.380 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRight/default-12 366972850 3.328 ns/op 0 B/op 0 allocs/op + +Benchmark_TrimRightBytes/fiber-12 586471208 2.099 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRightBytes/fiber-12 576055069 2.087 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRightBytes/default-12 348849292 3.316 ns/op 0 B/op 0 allocs/op +Benchmark_TrimRightBytes/default-12 359904445 3.384 ns/op 0 B/op 0 allocs/op + +Benchmark_TrimLeft/fiber-12 578044544 2.122 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeft/fiber-12 585290433 2.074 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeft/default-12 351906888 3.667 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeft/default-12 330852666 3.448 ns/op 0 B/op 0 allocs/op + +Benchmark_TrimLeftBytes/fiber-12 545400109 2.239 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeftBytes/fiber-12 544800061 2.270 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeftBytes/default-12 329749006 3.521 ns/op 0 B/op 0 allocs/op +Benchmark_TrimLeftBytes/default-12 344199560 3.452 ns/op 0 B/op 0 allocs/op + +Benchmark_Trim/fiber-12 280692232 4.128 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/fiber-12 297070083 3.961 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default-12 232522952 5.163 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default-12 230659057 5.172 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default.trimspace-12 227328967 5.245 ns/op 0 B/op 0 allocs/op +Benchmark_Trim/default.trimspace-12 227340775 5.253 ns/op 0 B/op 0 allocs/op + +Benchmark_TrimBytes/fiber-12 275612090 4.280 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/fiber-12 284892168 4.302 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default-12 224021550 5.163 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default-12 239689282 4.922 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default.trimspace-12 216809300 5.514 ns/op 0 B/op 0 allocs/op +Benchmark_TrimBytes/default.trimspace-12 218177734 5.603 ns/op 0 B/op 0 allocs/op + Benchmark_EqualFoldBytes/fiber-12 22944849 47.14 ns/op 0 B/op 0 allocs/op Benchmark_EqualFoldBytes/fiber-12 26006342 46.82 ns/op 0 B/op 0 allocs/op Benchmark_EqualFoldBytes/default-12 5222006 222.5 ns/op 0 B/op 0 allocs/op @@ -100,6 +134,7 @@ Benchmark_CalculateTimestamp/fiber-12 1000000000 0.2634 ns/op Benchmark_CalculateTimestamp/fiber-12 1000000000 0.2935 ns/op 0 B/op 0 allocs/op Benchmark_CalculateTimestamp/default-12 15740576 73.79 ns/op 0 B/op 0 allocs/op Benchmark_CalculateTimestamp/default-12 15789036 71.12 ns/op 0 B/op 0 allocs/op + ``` See all the benchmarks under https://gofiber.github.io/utils/ \ No newline at end of file diff --git a/byteseq.go b/byteseq.go index 82932ca..f0f4ce9 100644 --- a/byteseq.go +++ b/byteseq.go @@ -16,3 +16,38 @@ func EqualFold[S byteSeq](b, s S) bool { } return true } + +// TrimLeft is the equivalent of strings/bytes.TrimLeft +func TrimLeft[S byteSeq](s S, cutset byte) S { + lenStr, start := len(s), 0 + for start < lenStr && s[start] == cutset { + start++ + } + return s[start:] +} + +// Trim is the equivalent of strings/bytes.Trim +func Trim[S byteSeq](s S, cutset byte) S { + i, j := 0, len(s)-1 + for ; i <= j; i++ { + if s[i] != cutset { + break + } + } + for ; i < j; j-- { + if s[j] != cutset { + break + } + } + + return s[i : j+1] +} + +// TrimRight is the equivalent of strings/bytes.TrimRight +func TrimRight[S byteSeq](s S, cutset byte) S { + lenStr := len(s) + for lenStr > 0 && s[lenStr-1] == cutset { + lenStr-- + } + return s[:lenStr] +} diff --git a/byteseq_test.go b/byteseq_test.go index 21aed7d..0c82684 100644 --- a/byteseq_test.go +++ b/byteseq_test.go @@ -39,12 +39,14 @@ func Benchmark_EqualFoldBytes(b *testing.B) { right := []byte(lowerStr) var res bool b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() for n := 0; n < b.N; n++ { res = EqualFold(left, right) } require.True(b, res) }) b.Run("default", func(b *testing.B) { + b.ReportAllocs() for n := 0; n < b.N; n++ { res = bytes.EqualFold(left, right) } @@ -56,15 +58,227 @@ func Benchmark_EqualFoldBytes(b *testing.B) { func Benchmark_EqualFold(b *testing.B) { var res bool b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() for n := 0; n < b.N; n++ { res = EqualFold(upperStr, lowerStr) } require.True(b, res) }) b.Run("default", func(b *testing.B) { + b.ReportAllocs() for n := 0; n < b.N; n++ { res = strings.EqualFold(upperStr, lowerStr) } require.True(b, res) }) } + +func Test_TrimRight(t *testing.T) { + t.Parallel() + testCases := []struct { + S1 string + S2 string + Cutset byte + }{ + {S1: "/test//////", S2: "/test", Cutset: '/'}, + {S1: "/test", S2: "/test", Cutset: '/'}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: "", S2: "", Cutset: ' '}, + } + + for _, tc := range testCases { + res := TrimRight(tc.S1, tc.Cutset) + require.Equal(t, tc.S2, res, "string") + + resB := TrimRight([]byte(tc.S1), tc.Cutset) + require.Equal(t, []byte(tc.S2), resB, "bytes") + } +} + +func Benchmark_TrimRight(b *testing.B) { + var res string + word := "foobar " + expected := "foobar" + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = TrimRight(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = strings.TrimRight(word, " ") + } + require.Equal(b, expected, res) + }) +} + +func Benchmark_TrimRightBytes(b *testing.B) { + var res []byte + word := []byte("foobar ") + expected := []byte("foobar") + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = TrimRight(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = bytes.TrimRight(word, " ") + } + require.Equal(b, expected, res) + }) +} + +func Test_TrimLeft(t *testing.T) { + t.Parallel() + testCases := []struct { + S1 string + S2 string + Cutset byte + }{ + {S1: "////test/", S2: "test/", Cutset: '/'}, + {S1: "test/", S2: "test/", Cutset: '/'}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: "", S2: "", Cutset: ' '}, + } + + for _, tc := range testCases { + res := TrimLeft(tc.S1, tc.Cutset) + require.Equal(t, tc.S2, res, "string") + + resB := TrimLeft([]byte(tc.S1), tc.Cutset) + require.Equal(t, []byte(tc.S2), resB, "bytes") + } +} + +func Benchmark_TrimLeft(b *testing.B) { + var res string + word := " foobar" + expected := "foobar" + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = TrimLeft(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = strings.TrimLeft(word, " ") + } + require.Equal(b, expected, res) + }) +} + +func Benchmark_TrimLeftBytes(b *testing.B) { + var res []byte + word := []byte(" foobar") + expected := []byte("foobar") + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = TrimLeft(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = bytes.TrimLeft(word, " ") + } + require.Equal(b, expected, res) + }) +} + +func Test_Trim(t *testing.T) { + t.Parallel() + testCases := []struct { + S1 string + S2 string + Cutset byte + }{ + {S1: " test ", S2: "test", Cutset: ' '}, + {S1: "test", S2: "test", Cutset: ' '}, + {S1: ".test", S2: "test", Cutset: '.'}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: " ", S2: "", Cutset: ' '}, + {S1: "", S2: "", Cutset: ' '}, + } + + for _, tc := range testCases { + res := Trim(tc.S1, tc.Cutset) + require.Equal(t, tc.S2, res, "string") + + resB := Trim([]byte(tc.S1), tc.Cutset) + require.Equal(t, []byte(tc.S2), resB, "bytes") + } +} + +func Benchmark_Trim(b *testing.B) { + var res string + word := " foobar " + expected := "foobar" + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = Trim(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = strings.Trim(word, " ") + } + require.Equal(b, expected, res) + }) + b.Run("default.trimspace", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = strings.TrimSpace(word) + } + require.Equal(b, expected, res) + }) +} + +func Benchmark_TrimBytes(b *testing.B) { + var res []byte + word := []byte(" foobar ") + expected := []byte("foobar") + + b.Run("fiber", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = Trim(word, ' ') + } + require.Equal(b, expected, res) + }) + b.Run("default", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = bytes.Trim(word, " ") + } + require.Equal(b, expected, res) + }) + b.Run("default.trimspace", func(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + res = bytes.TrimSpace(word) + } + require.Equal(b, expected, res) + }) +}