diff --git a/README.md b/README.md index 995ad1a..a8de3f9 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,21 @@ So, this library is a very simple, very fast and a more flexible alternative to There are no dependencies and is alocation free. πŸ₯³ ## 🧰 Features -There are the supported patterns operators: +### Supported patterns operators - `*` match zero or more characters - `?` match zero or one character - `.` match exactly one character +### Supported flags +- `FLAG_NONE` no flag +- `FLAG_CASEFOLD` ignore case + +This is irrelevant for now, but you can combine them with `|` operator. +For example: `FLAG_CASEFOLD|FLAG_NONE` + +Because of the `strings.ToLower` operation, using `FLAG_CASEFOLD` flag is slower and result allocation... +Even if this function is not self recursive, prefer to prepare your data before, because `strings.ToLower` is called for pattern and given string. + ## 🧐 How to >⚠️ WARNING: Unlike the GNU "libc", this library have no equivalent to "FNM_FILE_NAME". >To do this you can use "path/filepath" https://pkg.go.dev/path/filepath#Match @@ -51,49 +61,59 @@ The tested fonctions are: - filepath.Match(t.pattern, t.name) - oldMatchSimple(t.pattern, t.name) `From the commit a899be92514ed08aa5271bc3b93320b719ce2114` - oldMatch(t.pattern, t.name) `From the commit a899be92514ed08aa5271bc3b93320b719ce2114` -- Match(t.pattern, t.name) `The actual implementation` +- Match(t.pattern, t.name, FLAG_NONE) `The actual implementation` +- Match(t.pattern, t.name, FLAG_CASEFOLD) `The actual implementation, but with a strings.ToLower operation` ```bash goos: linux goarch: amd64 pkg: github.com/IGLOU-EU/go-wildcard cpu: AMD Ryzen 7 PRO 6850U with Radeon Graphics -BenchmarkRegex/0-16 1000000 1322 ns/op 765 B/op 9 allocs/op -BenchmarkRegex/1-16 134851 10461 ns/op 6592 B/op 26 allocs/op -BenchmarkRegex/2-16 5871756 280.8 ns/op 160 B/op 2 allocs/op -BenchmarkRegex/3-16 108092 12096 ns/op 6647 B/op 26 allocs/op -BenchmarkRegex/4-16 92070 13924 ns/op 7436 B/op 38 allocs/op -BenchmarkRegex/5-16 4702372 277.6 ns/op 160 B/op 2 allocs/op - -BenchmarkFilepath/0-16 548771120 1.836 ns/op 0 B/op 0 allocs/op -BenchmarkFilepath/1-16 9451810 117.8 ns/op 0 B/op 0 allocs/op -BenchmarkFilepath/2-16 151409767 7.853 ns/op 0 B/op 0 allocs/op -BenchmarkFilepath/3-16 8656650 143.8 ns/op 0 B/op 0 allocs/op -BenchmarkFilepath/4-16 67589983 18.33 ns/op 0 B/op 0 allocs/op -BenchmarkFilepath/5-16 4805623 240.3 ns/op 0 B/op 0 allocs/op - -BenchmarkOldMatchSimple/0-16 1000000000 0.4971 ns/op 0 B/op 0 allocs/op -BenchmarkOldMatchSimple/1-16 4738023 292.5 ns/op 176 B/op 1 allocs/op -BenchmarkOldMatchSimple/2-16 1000000000 0.9130 ns/op 0 B/op 0 allocs/op -BenchmarkOldMatchSimple/3-16 1688683 763.8 ns/op 352 B/op 2 allocs/op -BenchmarkOldMatchSimple/4-16 2242758 514.0 ns/op 336 B/op 2 allocs/op -BenchmarkOldMatchSimple/5-16 10435084 110.7 ns/op 0 B/op 0 allocs/op - -BenchmarkOldMatch/0-16 1000000000 0.4568 ns/op 0 B/op 0 allocs/op -BenchmarkOldMatch/1-16 5300286 286.8 ns/op 176 B/op 1 allocs/op -BenchmarkOldMatch/2-16 1000000000 0.7127 ns/op 0 B/op 0 allocs/op -BenchmarkOldMatch/3-16 1608777 772.9 ns/op 352 B/op 2 allocs/op -BenchmarkOldMatch/4-16 2283015 548.9 ns/op 336 B/op 2 allocs/op -BenchmarkOldMatch/5-16 10425933 113.0 ns/op 0 B/op 0 allocs/op - -BenchmarkMatch/0-16 654065395 1.774 ns/op 0 B/op 0 allocs/op -BenchmarkMatch/1-16 352847413 2.973 ns/op 0 B/op 0 allocs/op -BenchmarkMatch/2-16 652602918 1.822 ns/op 0 B/op 0 allocs/op -BenchmarkMatch/3-16 412494770 2.940 ns/op 0 B/op 0 allocs/op -BenchmarkMatch/4-16 197380323 5.447 ns/op 0 B/op 0 allocs/op -BenchmarkMatch/5-16 39741439 27.96 ns/op 0 B/op 0 allocs/op + +BenchmarkRegex/0-16 1000000 1344 ns/op 766 B/op 9 allocs/op +BenchmarkRegex/1-16 115628 11343 ns/op 6592 B/op 26 allocs/op +BenchmarkRegex/2-16 5015937 263.6 ns/op 160 B/op 2 allocs/op +BenchmarkRegex/3-16 109844 13607 ns/op 6646 B/op 26 allocs/op +BenchmarkRegex/4-16 119311 13226 ns/op 7440 B/op 38 allocs/op +BenchmarkRegex/5-16 5427733 247.1 ns/op 160 B/op 2 allocs/op + +BenchmarkFilepath/0-16 479149471 2.109 ns/op 0 B/op 0 allocs/op +BenchmarkFilepath/1-16 9473259 119.8 ns/op 0 B/op 0 allocs/op +BenchmarkFilepath/2-16 151451250 7.945 ns/op 0 B/op 0 allocs/op +BenchmarkFilepath/3-16 8295160 144.5 ns/op 0 B/op 0 allocs/op +BenchmarkFilepath/4-16 57564092 19.49 ns/op 0 B/op 0 allocs/op +BenchmarkFilepath/5-16 4911076 240.2 ns/op 0 B/op 0 allocs/op + +BenchmarkOldMatchSimple/0-16 1000000000 0.4878 ns/op 0 B/op 0 allocs/op +BenchmarkOldMatchSimple/1-16 4959806 283.4 ns/op 176 B/op 1 allocs/op +BenchmarkOldMatchSimple/2-16 1000000000 0.9326 ns/op 0 B/op 0 allocs/op +BenchmarkOldMatchSimple/3-16 1910574 725.4 ns/op 352 B/op 2 allocs/op +BenchmarkOldMatchSimple/4-16 2918268 511.0 ns/op 336 B/op 2 allocs/op +BenchmarkOldMatchSimple/5-16 10512922 113.3 ns/op 0 B/op 0 allocs/op + +BenchmarkOldMatch/0-16 1000000000 0.5195 ns/op 0 B/op 0 allocs/op +BenchmarkOldMatch/1-16 5393407 279.9 ns/op 176 B/op 1 allocs/op +BenchmarkOldMatch/2-16 1000000000 0.7294 ns/op 0 B/op 0 allocs/op +BenchmarkOldMatch/3-16 2123334 775.0 ns/op 352 B/op 2 allocs/op +BenchmarkOldMatch/4-16 2616628 496.1 ns/op 336 B/op 2 allocs/op +BenchmarkOldMatch/5-16 10818404 114.0 ns/op 0 B/op 0 allocs/op + +BenchmarkMatch/0-16 454892713 2.281 ns/op 0 B/op 0 allocs/op +BenchmarkMatch/1-16 349304181 2.928 ns/op 0 B/op 0 allocs/op +BenchmarkMatch/2-16 476728009 2.127 ns/op 0 B/op 0 allocs/op +BenchmarkMatch/3-16 290518609 3.822 ns/op 0 B/op 0 allocs/op +BenchmarkMatch/4-16 124291632 8.709 ns/op 0 B/op 0 allocs/op +BenchmarkMatch/5-16 23469135 48.56 ns/op 0 B/op 0 allocs/op + +BenchmarkMatchCasefold/0-16 7673000 216.3 ns/op 48 B/op 1 allocs/op +BenchmarkMatchCasefold/1-16 7057550 223.6 ns/op 48 B/op 1 allocs/op +BenchmarkMatchCasefold/2-16 6580888 215.7 ns/op 48 B/op 1 allocs/op +BenchmarkMatchCasefold/3-16 3193930 451.2 ns/op 96 B/op 2 allocs/op +BenchmarkMatchCasefold/4-16 3213775 473.1 ns/op 96 B/op 2 allocs/op +BenchmarkMatchCasefold/5-16 2305406 481.4 ns/op 32 B/op 1 allocs/op + PASS -ok github.com/IGLOU-EU/go-wildcard 48.707s +ok github.com/IGLOU-EU/go-wildcard 60.533s ``` ## πŸ•° History diff --git a/wildcard.go b/wildcard.go index 5cd5960..febc677 100644 --- a/wildcard.go +++ b/wildcard.go @@ -8,9 +8,34 @@ package wildcard -// Match returns true if the pattern matches the s string. -// The pattern can contain the wildcard characters '?' '.' and '*'. -func Match(pattern, s string) bool { +import "strings" + +// Flags type is used to specify matching options for the Match function +type Flags uint8 + +// Constants definition for matching options +const ( + FLAG_NONE = 1 << iota // No special behavior + FLAG_CASEFOLD // Case-insensitive match +) + +// Match function checks if the given string s matches the wildcard pattern +// with specified matching options (Flags). +// +// Supported wildcards: +// `*` match zero or more characters +// `?` match zero or one character +// `.` match exactly one character +// +// Supported matching options: +// FLAG_NONE - No special behavior +// FLAG_CASEFOLD - Case-insensitive match +func Match(pattern, s string, option Flags) bool { + if option&FLAG_CASEFOLD != 0 { + s = strings.ToLower(s) + pattern = strings.ToLower(pattern) + } + if pattern == "" { return s == pattern } diff --git a/wildcard_bench_test.go b/wildcard_bench_test.go index 9abf5b5..feeeeaf 100644 --- a/wildcard_bench_test.go +++ b/wildcard_bench_test.go @@ -65,7 +65,17 @@ func BenchmarkMatch(b *testing.B) { for i, t := range TestSet { b.Run(fmt.Sprint(i), func(b *testing.B) { for i := 0; i < b.N; i++ { - wildcard.Match(t.pattern, t.name) + wildcard.Match(t.pattern, t.name, wildcard.FLAG_NONE) + } + }) + } +} + +func BenchmarkMatchCasefold(b *testing.B) { + for i, t := range TestSet { + b.Run(fmt.Sprint(i), func(b *testing.B) { + for i := 0; i < b.N; i++ { + wildcard.Match(t.pattern, t.name, wildcard.FLAG_CASEFOLD) } }) } diff --git a/wildcard_test.go b/wildcard_test.go index 4261d66..da19e9e 100644 --- a/wildcard_test.go +++ b/wildcard_test.go @@ -21,70 +21,73 @@ func TestMatch(t *testing.T) { cases := []struct { s string pattern string + flag wildcard.Flags result bool }{ - {"", "", true}, - {"", "*", true}, - {"", "**", true}, - {"", "?", true}, - {"", "??", true}, - {"", "?*", true}, - {"", "*?", true}, - {"", ".", false}, - {"", ".?", false}, - {"", "?.", false}, - {"", ".*", false}, - {"", "*.", false}, - {"", "*.?", false}, - {"", "?.*", false}, + {"", "", wildcard.FLAG_NONE, true}, + {"", "*", wildcard.FLAG_NONE, true}, + {"", "**", wildcard.FLAG_NONE, true}, + {"", "?", wildcard.FLAG_NONE, true}, + {"", "??", wildcard.FLAG_NONE, true}, + {"", "?*", wildcard.FLAG_NONE, true}, + {"", "*?", wildcard.FLAG_NONE, true}, + {"", ".", wildcard.FLAG_NONE, false}, + {"", ".?", wildcard.FLAG_NONE, false}, + {"", "?.", wildcard.FLAG_NONE, false}, + {"", ".*", wildcard.FLAG_NONE, false}, + {"", "*.", wildcard.FLAG_NONE, false}, + {"", "*.?", wildcard.FLAG_NONE, false}, + {"", "?.*", wildcard.FLAG_NONE, false}, - {"a", "", false}, - {"a", "a", true}, - {"a", "*", true}, - {"a", "**", true}, - {"a", "?", true}, - {"a", "??", true}, - {"a", ".", true}, - {"a", ".?", true}, - {"a", "?.", false}, - {"a", ".*", true}, - {"a", "*.", true}, - {"a", "*.?", true}, - {"a", "?.*", false}, + {"a", "", wildcard.FLAG_NONE, false}, + {"a", "a", wildcard.FLAG_NONE, true}, + {"a", "*", wildcard.FLAG_NONE, true}, + {"a", "**", wildcard.FLAG_NONE, true}, + {"a", "?", wildcard.FLAG_NONE, true}, + {"a", "??", wildcard.FLAG_NONE, true}, + {"a", ".", wildcard.FLAG_NONE, true}, + {"a", ".?", wildcard.FLAG_NONE, true}, + {"a", "?.", wildcard.FLAG_NONE, false}, + {"a", ".*", wildcard.FLAG_NONE, true}, + {"a", "*.", wildcard.FLAG_NONE, true}, + {"a", "*.?", wildcard.FLAG_NONE, true}, + {"a", "?.*", wildcard.FLAG_NONE, false}, - {"match the exact string", "match the exact string", true}, - {"do not match a different string", "this is a different string", false}, - {"Match The Exact String WITH DIFFERENT CASE", "Match The Exact String WITH DIFFERENT CASE", true}, - {"do not match a different string WITH DIFFERENT CASE", "this is a different string WITH DIFFERENT CASE", false}, - {"Do Not Match The Exact String With Different Case", "do not match the exact string with different case", false}, - {"match an emoji πŸ˜ƒ", "match an emoji πŸ˜ƒ", true}, - {"do not match because of different emoji πŸ˜ƒ", "do not match because of different emoji πŸ˜„", false}, - {"πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", "πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", true}, - {"πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", "πŸ¦ŒπŸ‡πŸ¦‘πŸΏοΈπŸŒ²πŸŒ³πŸ°πŸŒ³πŸŒ²πŸŒžπŸŒ§οΈβ„οΈπŸŒ¬οΈβ›ˆοΈπŸ”₯πŸŽ„πŸŽ…πŸŽπŸŽ‰πŸŽŠπŸ₯³πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸ’πŸ‘ͺπŸ’–πŸ‘©β€πŸ’ΌπŸ›€", false}, + {"match the exact string", "match the exact string", wildcard.FLAG_NONE, true}, + {"do not match a different string", "this is a different string", wildcard.FLAG_NONE, false}, + {"Match The Exact String WITH DIFFERENT CASE", "Match The Exact String WITH DIFFERENT CASE", wildcard.FLAG_NONE, true}, + {"do not match a different string WITH DIFFERENT CASE", "this is a different string WITH DIFFERENT CASE", wildcard.FLAG_NONE, false}, + {"Do Not Match The Exact String With Different Case", "do not match the exact string with different case", wildcard.FLAG_NONE, false}, + {"match an emoji πŸ˜ƒ", "match an emoji πŸ˜ƒ", wildcard.FLAG_NONE, true}, + {"do not match because of different emoji πŸ˜ƒ", "do not match because of different emoji πŸ˜„", wildcard.FLAG_NONE, false}, + {"πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", "πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", wildcard.FLAG_NONE, true}, + {"πŸŒ…β˜•οΈπŸ“°πŸ‘¨β€πŸ’ΌπŸ‘©β€πŸ’ΌπŸ’πŸ–₯οΈπŸ’ΌπŸ’»πŸ“ŠπŸ“ˆπŸ“‰πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸπŸ•°οΈπŸ’ͺπŸ‹οΈβ€β™‚οΈπŸ‹οΈβ€β™€οΈπŸ‹οΈβ€β™‚οΈπŸ’ΌπŸš΄β€β™‚οΈπŸš΄β€β™€οΈπŸš΄β€β™‚οΈπŸ›€πŸ’€πŸŒƒ", "πŸ¦ŒπŸ‡πŸ¦‘πŸΏοΈπŸŒ²πŸŒ³πŸ°πŸŒ³πŸŒ²πŸŒžπŸŒ§οΈβ„οΈπŸŒ¬οΈβ›ˆοΈπŸ”₯πŸŽ„πŸŽ…πŸŽπŸŽ‰πŸŽŠπŸ₯³πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸ’πŸ‘ͺπŸ’–πŸ‘©β€πŸ’ΌπŸ›€", wildcard.FLAG_NONE, false}, - {"match a string with a *", "match a string *", true}, - {"match a string with a * at the beginning", "* at the beginning", true}, - {"match a string with two *", "match * with *", true}, - {"do not match a string with extra and a *", "do not match a string * with more", false}, + {"match a string with a *", "match a string *", wildcard.FLAG_NONE, true}, + {"match a string with a * at the beginning", "* at the beginning", wildcard.FLAG_NONE, true}, + {"match a string with two *", "match * with *", wildcard.FLAG_NONE, true}, + {"do not match a string with extra and a *", "do not match a string * with more", wildcard.FLAG_NONE, false}, - {"match a string with a ?", "match ? string with a ?", true}, - {"match a string with a ? at the beginning", "?atch a string with a ? at the beginning", true}, - {"match a string with two ?", "match a string with two ??", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"do not match a string with extra and a ?", "do not match ? string with extra and a ? like this", false}, + {"match a string with a ?", "match ? string with a ?", wildcard.FLAG_NONE, true}, + {"match a string with a ? at the beginning", "?atch a string with a ? at the beginning", wildcard.FLAG_NONE, true}, + {"match a string with two ?", "match a string with two ??", wildcard.FLAG_NONE, true}, + {"match a optional char with a ?", "match a optional? char with a ?", wildcard.FLAG_NONE, true}, + {"match a optional char with a ?", "match a optional? char with a ?", wildcard.FLAG_NONE, true}, + {"do not match a string with extra and a ?", "do not match ? string with extra and a ? like this", wildcard.FLAG_NONE, false}, - {"match a string with a .", "match . string with a .", true}, - {"match a string with a . at the beginning", ".atch a string with a . at the beginning", true}, - {"match a string with two .", "match a ..ring with two .", true}, - {"do not match a string with extra .", "do not match a string with extra ..", false}, + {"match a string with a .", "match . string with a .", wildcard.FLAG_NONE, true}, + {"match a string with a . at the beginning", ".atch a string with a . at the beginning", wildcard.FLAG_NONE, true}, + {"match a string with two .", "match a ..ring with two .", wildcard.FLAG_NONE, true}, + {"do not match a string with extra .", "do not match a string with extra ..", wildcard.FLAG_NONE, false}, - {"A big brown fox jumps over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", true}, - {"A big brown fox fails to jump over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, + {"A big brown fox jumps over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", wildcard.FLAG_NONE, true}, + {"A big brown fox fails to jump over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", wildcard.FLAG_NONE, false}, + + {"This IS a StrinG witH soMMe UppeRCase FriendS", "thIs is A stRINg wITh sOMmE uPpERcAse fRiENds", wildcard.FLAG_CASEFOLD, true}, } for i, c := range cases { - result := wildcard.Match(c.pattern, c.s) + result := wildcard.Match(c.pattern, c.s, c.flag) if c.result != result { t.Errorf("Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`", i+1, c.result, result, c.pattern, c.s) }