Add stub database selection, and fix differences between pekoe and c-redis
- PUBSUB CHANNELS glob pattern matching - Implement SELECT + max DB validation (0-15) (default db max is 16), for perl clients - CLIENT KILL ID (single/range) + MAXAGE - CLIENT INFO events + resp fields - PUBSUB SHARDCHANNELS/SHARDNUMSUB stubs
This commit is contained in:
58
internal/glob/glob.go
Normal file
58
internal/glob/glob.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// @tag glob
|
||||
package glob
|
||||
|
||||
// Match checks if a string matches a glob-style pattern.
|
||||
// Supports:
|
||||
// - * (asterisk): matches any sequence of characters (including empty)
|
||||
// - ? (question): matches any single character
|
||||
//
|
||||
// This matches Redis PUBSUB CHANNELS pattern behavior.
|
||||
func Match(pattern, s string) bool {
|
||||
if pattern == "" {
|
||||
return s == ""
|
||||
}
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Simple glob matching using dynamic programming
|
||||
// Convert pattern to regex-like matching
|
||||
patternLen := len(pattern)
|
||||
sLen := len(s)
|
||||
|
||||
// dp[i][j] = true if pattern[0:i] matches s[0:j]
|
||||
dp := make([][]bool, patternLen+1)
|
||||
for i := range dp {
|
||||
dp[i] = make([]bool, sLen+1)
|
||||
}
|
||||
|
||||
// Empty pattern matches empty string
|
||||
dp[0][0] = true
|
||||
|
||||
// Handle leading * in pattern
|
||||
for i := 1; i <= patternLen; i++ {
|
||||
if pattern[i-1] == '*' {
|
||||
dp[i][0] = dp[i-1][0]
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the DP table
|
||||
for i := 1; i <= patternLen; i++ {
|
||||
for j := 1; j <= sLen; j++ {
|
||||
switch pattern[i-1] {
|
||||
case '*':
|
||||
// * matches: either consume nothing (stay at same position in pattern)
|
||||
// or consume one character from string
|
||||
dp[i][j] = dp[i-1][j] || dp[i][j-1]
|
||||
case '?':
|
||||
// ? matches any single character
|
||||
dp[i][j] = dp[i-1][j-1]
|
||||
default:
|
||||
// Literal character must match exactly
|
||||
dp[i][j] = dp[i-1][j-1] && pattern[i-1] == s[j-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dp[patternLen][sLen]
|
||||
}
|
||||
69
internal/glob/glob_test.go
Normal file
69
internal/glob/glob_test.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// @tag glob
|
||||
// @tag test
|
||||
package glob
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
pattern string
|
||||
s string
|
||||
want bool
|
||||
}{
|
||||
{"", "", true},
|
||||
{"", "foo", false},
|
||||
{"*", "", true},
|
||||
{"*", "foo", true},
|
||||
{"*", "foo/bar", true},
|
||||
{"foo", "foo", true},
|
||||
{"foo", "bar", false},
|
||||
{"foo*", "foo", true},
|
||||
{"foo*", "foobar", true},
|
||||
{"foo*", "fo", false},
|
||||
{"*foo", "foo", true},
|
||||
{"*foo", "barfoo", true},
|
||||
{"*foo", "bar", false},
|
||||
{"foo*bar", "foobar", true},
|
||||
{"foo*bar", "foobazbar", true},
|
||||
{"foo*bar", "foobaz", false},
|
||||
{"foo?bar", "fooXbar", true},
|
||||
{"foo?bar", "fooXXbar", false},
|
||||
{"foo??bar", "fooXXbar", true},
|
||||
{"?", "a", true},
|
||||
{"?", "", false},
|
||||
{"a?c", "abc", true},
|
||||
{"a?c", "ac", false},
|
||||
{"foo?*bar", "fooabcbar", true},
|
||||
{"foo?*bar", "foobar", false},
|
||||
{"*foo*bar*", "ahellofoocherrybarb", true},
|
||||
{"ch*", "channel", true},
|
||||
{"ch*", "ch", true},
|
||||
{"ch*", "c", false},
|
||||
{"*", "*", true},
|
||||
{"**", "*", true},
|
||||
{"*a*b", "aXb", true},
|
||||
{"a*b*c", "aXbYc", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.pattern+"/"+tt.s, func(t *testing.T) {
|
||||
if got := Match(tt.pattern, tt.s); got != tt.want {
|
||||
t.Errorf("Match(%q, %q) = %v, want %v", tt.pattern, tt.s, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatch(b *testing.B) {
|
||||
patterns := []string{"*", "ch*", "foo*bar", "???", "*channel*"}
|
||||
strings := []string{"channel", "channel123", "foobar", "abc", "somechannelname"}
|
||||
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, p := range patterns {
|
||||
for _, s := range strings {
|
||||
Match(p, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user