From 4d7d1a76a8f9c2209a10c9f84717e5dc61556228 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Tue, 30 Aug 2016 07:21:20 -0700 Subject: [PATCH] import match package --- gjson.go | 106 +++------------- gjson_test.go | 346 -------------------------------------------------- 2 files changed, 18 insertions(+), 434 deletions(-) diff --git a/gjson.go b/gjson.go index 99497d0..78a793a 100644 --- a/gjson.go +++ b/gjson.go @@ -1,7 +1,11 @@ // Package gjson provides searching for json strings. package gjson -import "strconv" +import ( + "strconv" + + "github.com/tidwall/match" +) // Type is Result type type Type int @@ -524,16 +528,11 @@ type objectPathResult struct { part string path string wild bool - uc bool more bool } func parseObjectPath(path string) (r objectPathResult) { for i := 0; i < len(path); i++ { - if path[i]&0x60 == 0x60 { - // alpha lowercase - continue - } if path[i] == '.' { r.part = path[:i] r.path = path[i+1:] @@ -544,10 +543,6 @@ func parseObjectPath(path string) (r objectPathResult) { r.wild = true continue } - if path[i] > 0x7f { - r.uc = true - continue - } if path[i] == '\\' { // go into escape mode. this is a slower path that // strips off the escape character from the part. @@ -557,10 +552,6 @@ func parseObjectPath(path string) (r objectPathResult) { epart = append(epart, path[i]) i++ for ; i < len(path); i++ { - if path[i] > 0x7f { - r.uc = true - continue - } if path[i] == '\\' { i++ if i < len(path) { @@ -636,7 +627,7 @@ func parseSquash(json string, i int) (int, string) { } func parseObject(c *parseContext, i int, path string) (int, bool) { - var match, kesc, vesc, ok, hit bool + var pmatch, kesc, vesc, ok, hit bool var key, val string rp := parseObjectPath(path) for i < len(c.json) { @@ -695,18 +686,18 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } if rp.wild { if kesc { - match = wildcardMatch(unescape(key), rp.part, rp.uc) + pmatch = match.Match(unescape(key), rp.part) } else { - match = wildcardMatch(key, rp.part, rp.uc) + pmatch = match.Match(key, rp.part) } } else { if kesc { - match = rp.part == unescape(key) + pmatch = rp.part == unescape(key) } else { - match = rp.part == key + pmatch = rp.part == key } } - hit = match && !rp.more + hit = pmatch && !rp.more for ; i < len(c.json); i++ { switch c.json[i] { default: @@ -728,7 +719,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { return i, true } case '{': - if match && !hit { + if pmatch && !hit { i, hit = parseObject(c, i+1, rp.path) if hit { return i, true @@ -742,7 +733,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } } case '[': - if match && !hit { + if pmatch && !hit { i, hit = parseArray(c, i+1, rp.path) if hit { return i, true @@ -784,7 +775,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } func parseArray(c *parseContext, i int, path string) (int, bool) { - var match, vesc, ok, hit bool + var pmatch, vesc, ok, hit bool var val string var h int var alog []int @@ -800,8 +791,8 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } for i < len(c.json) { if !rp.arrch { - match = partidx == h - hit = match && !rp.more + pmatch = partidx == h + hit = pmatch && !rp.more } h++ if rp.alogok { @@ -831,7 +822,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { return i, true } case '{': - if match && !hit { + if pmatch && !hit { i, hit = parseObject(c, i+1, rp.path) if hit { if rp.alogok { @@ -851,7 +842,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } } case '[': - if match && !hit { + if pmatch && !hit { i, hit = parseArray(c, i+1, rp.path) if hit { if rp.alogok { @@ -1106,64 +1097,3 @@ func stringLessInsensitive(a, b string) bool { } return len(a) < len(b) } - -// wilcardMatch returns true if str matches pattern. This is a very -// simple wildcard match where '*' matches on any number characters -// and '?' matches on any one character. -func wildcardMatch(str, pattern string, uc bool) bool { - if pattern == "*" { - return true - } - if !uc { - return deepMatch(str, pattern) - } - rstr := make([]rune, 0, len(str)) - rpattern := make([]rune, 0, len(pattern)) - for _, r := range str { - rstr = append(rstr, r) - } - for _, r := range pattern { - rpattern = append(rpattern, r) - } - return deepMatchRune(rstr, rpattern) -} -func deepMatch(str, pattern string) bool { - for len(pattern) > 0 { - switch pattern[0] { - default: - if len(str) == 0 || str[0] != pattern[0] { - return false - } - case '?': - if len(str) == 0 { - return false - } - case '*': - return deepMatch(str, pattern[1:]) || - (len(str) > 0 && deepMatch(str[1:], pattern)) - } - str = str[1:] - pattern = pattern[1:] - } - return len(str) == 0 && len(pattern) == 0 -} -func deepMatchRune(str, pattern []rune) bool { - for len(pattern) > 0 { - switch pattern[0] { - default: - if len(str) == 0 || str[0] != pattern[0] { - return false - } - case '?': - if len(str) == 0 { - return false - } - case '*': - return deepMatchRune(str, pattern[1:]) || - (len(str) > 0 && deepMatchRune(str[1:], pattern)) - } - str = str[1:] - pattern = pattern[1:] - } - return len(str) == 0 && len(pattern) == 0 -} diff --git a/gjson_test.go b/gjson_test.go index 3c32f38..c50b551 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -251,47 +251,6 @@ func TestBasic(t *testing.T) { t.Fatalf("expecting %v, got %v", "Jason", fn) } } -func TestMatch(t *testing.T) { - if !wildcardMatch("hello world", "hello world", false) { - t.Fatal("fail") - } - if wildcardMatch("hello world", "jello world", false) { - t.Fatal("fail") - } - if !wildcardMatch("hello world", "hello*", false) { - t.Fatal("fail") - } - if wildcardMatch("hello world", "jello*", false) { - t.Fatal("fail") - } - if !wildcardMatch("hello world", "hello?world", false) { - t.Fatal("fail") - } - if wildcardMatch("hello world", "jello?world", false) { - t.Fatal("fail") - } - if !wildcardMatch("hello world", "he*o?world", false) { - t.Fatal("fail") - } - if !wildcardMatch("hello world", "he*o?wor*", false) { - t.Fatal("fail") - } - if !wildcardMatch("hello world", "he*o?*r*", false) { - t.Fatal("fail") - } - if !wildcardMatch("的情况下解析一个", "*", true) { - t.Fatal("fail") - } - if !wildcardMatch("的情况下解析一个", "*况下*", true) { - t.Fatal("fail") - } - if !wildcardMatch("的情况下解析一个", "*况?*", true) { - t.Fatal("fail") - } - if !wildcardMatch("的情况下解析一个", "的情况?解析一个", true) { - t.Fatal("fail") - } -} func TestUnicode(t *testing.T) { var json = `{"key":0,"的情况下解":{"key":1,"的情况":2}}` if Get(json, "的情况下解.key").Num != 1 { @@ -317,311 +276,6 @@ func TestUnicode(t *testing.T) { } } -// TestWildcardMatch - Tests validate the logic of wild card matching. -// `WildcardMatch` supports '*' and '?' wildcards. -// Sample usage: In resource matching for folder policy validation. -func TestWildcardMatch(t *testing.T) { - testCases := []struct { - pattern string - text string - matched bool - }{ - // Test case - 1. - // Test case with pattern containing key name with a prefix. Should accept the same text without a "*". - { - pattern: "my-folder/oo*", - text: "my-folder/oo", - matched: true, - }, - // Test case - 2. - // Test case with "*" at the end of the pattern. - { - pattern: "my-folder/In*", - text: "my-folder/India/Karnataka/", - matched: true, - }, - // Test case - 3. - // Test case with prefixes shuffled. - // This should fail. - { - pattern: "my-folder/In*", - text: "my-folder/Karnataka/India/", - matched: false, - }, - // Test case - 4. - // Test case with text expanded to the wildcards in the pattern. - { - pattern: "my-folder/In*/Ka*/Ban", - text: "my-folder/India/Karnataka/Ban", - matched: true, - }, - // Test case - 5. - // Test case with the keyname part is repeated as prefix several times. - // This is valid. - { - pattern: "my-folder/In*/Ka*/Ban", - text: "my-folder/India/Karnataka/Ban/Ban/Ban/Ban/Ban", - matched: true, - }, - // Test case - 6. - // Test case to validate that `*` can be expanded into multiple prefixes. - { - pattern: "my-folder/In*/Ka*/Ban", - text: "my-folder/India/Karnataka/Area1/Area2/Area3/Ban", - matched: true, - }, - // Test case - 7. - // Test case to validate that `*` can be expanded into multiple prefixes. - { - pattern: "my-folder/In*/Ka*/Ban", - text: "my-folder/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban", - matched: true, - }, - // Test case - 8. - // Test case where the keyname part of the pattern is expanded in the text. - { - pattern: "my-folder/In*/Ka*/Ban", - text: "my-folder/India/Karnataka/Bangalore", - matched: false, - }, - // Test case - 9. - // Test case with prefixes and wildcard expanded for all "*". - { - pattern: "my-folder/In*/Ka*/Ban*", - text: "my-folder/India/Karnataka/Bangalore", - matched: true, - }, - // Test case - 10. - // Test case with keyname part being a wildcard in the pattern. - {pattern: "my-folder/*", - text: "my-folder/India", - matched: true, - }, - // Test case - 11. - { - pattern: "my-folder/oo*", - text: "my-folder/odo", - matched: false, - }, - - // Test case with pattern containing wildcard '?'. - // Test case - 12. - // "my-folder?/" matches "my-folder1/", "my-folder2/", "my-folder3" etc... - // doesn't match "myfolder/". - { - pattern: "my-folder?/abc*", - text: "myfolder/abc", - matched: false, - }, - // Test case - 13. - { - pattern: "my-folder?/abc*", - text: "my-folder1/abc", - matched: true, - }, - // Test case - 14. - { - pattern: "my-?-folder/abc*", - text: "my--folder/abc", - matched: false, - }, - // Test case - 15. - { - pattern: "my-?-folder/abc*", - text: "my-1-folder/abc", - matched: true, - }, - // Test case - 16. - { - pattern: "my-?-folder/abc*", - text: "my-k-folder/abc", - matched: true, - }, - // Test case - 17. - { - pattern: "my??folder/abc*", - text: "myfolder/abc", - matched: false, - }, - // Test case - 18. - { - pattern: "my??folder/abc*", - text: "my4afolder/abc", - matched: true, - }, - // Test case - 19. - { - pattern: "my-folder?abc*", - text: "my-folder/abc", - matched: true, - }, - // Test case 20-21. - // '?' matches '/' too. (works with s3). - // This is because the namespace is considered flat. - // "abc?efg" matches both "abcdefg" and "abc/efg". - { - pattern: "my-folder/abc?efg", - text: "my-folder/abcdefg", - matched: true, - }, - { - pattern: "my-folder/abc?efg", - text: "my-folder/abc/efg", - matched: true, - }, - // Test case - 22. - { - pattern: "my-folder/abc????", - text: "my-folder/abc", - matched: false, - }, - // Test case - 23. - { - pattern: "my-folder/abc????", - text: "my-folder/abcde", - matched: false, - }, - // Test case - 24. - { - pattern: "my-folder/abc????", - text: "my-folder/abcdefg", - matched: true, - }, - // Test case 25-26. - // test case with no '*'. - { - pattern: "my-folder/abc?", - text: "my-folder/abc", - matched: false, - }, - { - pattern: "my-folder/abc?", - text: "my-folder/abcd", - matched: true, - }, - { - pattern: "my-folder/abc?", - text: "my-folder/abcde", - matched: false, - }, - // Test case 27. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnop", - matched: false, - }, - // Test case 28. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnopqrst/mnopqr", - matched: true, - }, - // Test case 29. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnopqrst/mnopqrs", - matched: true, - }, - // Test case 30. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnop", - matched: false, - }, - // Test case 31. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnopq", - matched: true, - }, - // Test case 32. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnopqr", - matched: true, - }, - // Test case 33. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopqand", - matched: true, - }, - // Test case 34. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopand", - matched: false, - }, - // Test case 35. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopqand", - matched: true, - }, - // Test case 36. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mn", - matched: false, - }, - // Test case 37. - { - pattern: "my-folder/mnop*?", - text: "my-folder/mnopqrst/mnopqrs", - matched: true, - }, - // Test case 38. - { - pattern: "my-folder/mnop*??", - text: "my-folder/mnopqrst", - matched: true, - }, - // Test case 39. - { - pattern: "my-folder/mnop*qrst", - text: "my-folder/mnopabcdegqrst", - matched: true, - }, - // Test case 40. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopqand", - matched: true, - }, - // Test case 41. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopand", - matched: false, - }, - // Test case 42. - { - pattern: "my-folder/mnop*?and?", - text: "my-folder/mnopqanda", - matched: true, - }, - // Test case 43. - { - pattern: "my-folder/mnop*?and", - text: "my-folder/mnopqanda", - matched: false, - }, - // Test case 44. - - { - pattern: "my-?-folder/abc*", - text: "my-folder/mnopqanda", - matched: false, - }, - } - // Iterating over the test cases, call the function under test and asert the output. - for i, testCase := range testCases { - actualResult := wildcardMatch(testCase.text, testCase.pattern, false) - if testCase.matched != actualResult { - t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult) - } - } -} func TestUnescape(t *testing.T) { unescape(string([]byte{'\\', '\\', 0})) unescape(string([]byte{'\\', '/', '\\', 'b', '\\', 'f'}))