117 lines
3.5 KiB
C#
117 lines
3.5 KiB
C#
namespace NATS.Server.Subscriptions;
|
|
|
|
public static class SubjectMatch
|
|
{
|
|
public const char Pwc = '*'; // partial wildcard
|
|
public const char Fwc = '>'; // full wildcard
|
|
public const char Sep = '.'; // token separator
|
|
|
|
public static bool IsValidSubject(string subject)
|
|
{
|
|
if (string.IsNullOrEmpty(subject))
|
|
return false;
|
|
|
|
bool sawFwc = false;
|
|
int start = 0;
|
|
|
|
for (int i = 0; i <= subject.Length; i++)
|
|
{
|
|
if (i == subject.Length || subject[i] == Sep)
|
|
{
|
|
int tokenLen = i - start;
|
|
if (tokenLen == 0 || sawFwc)
|
|
return false;
|
|
|
|
if (tokenLen == 1)
|
|
{
|
|
char c = subject[start];
|
|
if (c == Fwc)
|
|
sawFwc = true;
|
|
else if (c is ' ' or '\t' or '\n' or '\r' or '\f')
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
for (int j = start; j < i; j++)
|
|
{
|
|
char c = subject[j];
|
|
if (c is ' ' or '\t' or '\n' or '\r' or '\f')
|
|
return false;
|
|
}
|
|
}
|
|
|
|
start = i + 1;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsLiteral(string subject)
|
|
{
|
|
for (int i = 0; i < subject.Length; i++)
|
|
{
|
|
char c = subject[i];
|
|
if (c is Pwc or Fwc)
|
|
{
|
|
bool atStart = i == 0 || subject[i - 1] == Sep;
|
|
bool atEnd = i + 1 == subject.Length || subject[i + 1] == Sep;
|
|
if (atStart && atEnd)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsValidPublishSubject(string subject)
|
|
{
|
|
return IsValidSubject(subject) && IsLiteral(subject);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Match a literal subject against a pattern that may contain wildcards.
|
|
/// </summary>
|
|
public static bool MatchLiteral(string literal, string pattern)
|
|
{
|
|
int li = 0, pi = 0;
|
|
|
|
while (pi < pattern.Length)
|
|
{
|
|
// Get next pattern token
|
|
int pTokenStart = pi;
|
|
while (pi < pattern.Length && pattern[pi] != Sep)
|
|
pi++;
|
|
int pTokenLen = pi - pTokenStart;
|
|
if (pi < pattern.Length)
|
|
pi++; // skip separator
|
|
|
|
// Full wildcard -- matches everything remaining
|
|
if (pTokenLen == 1 && pattern[pTokenStart] == Fwc)
|
|
return li < literal.Length; // must have at least one token left
|
|
|
|
// Get next literal token
|
|
if (li >= literal.Length)
|
|
return false;
|
|
int lTokenStart = li;
|
|
while (li < literal.Length && literal[li] != Sep)
|
|
li++;
|
|
int lTokenLen = li - lTokenStart;
|
|
if (li < literal.Length)
|
|
li++; // skip separator
|
|
|
|
// Partial wildcard -- matches any single token
|
|
if (pTokenLen == 1 && pattern[pTokenStart] == Pwc)
|
|
continue;
|
|
|
|
// Literal comparison
|
|
if (pTokenLen != lTokenLen)
|
|
return false;
|
|
if (string.Compare(literal, lTokenStart, pattern, pTokenStart, pTokenLen, StringComparison.Ordinal) != 0)
|
|
return false;
|
|
}
|
|
|
|
return li >= literal.Length; // both exhausted
|
|
}
|
|
}
|