Commit 36e08e89 authored by Jeremy Meng's avatar Jeremy Meng

Replace StringComparer.InvariantCultrueIgnoreCase with...

Replace StringComparer.InvariantCultrueIgnoreCase with STringComparer.OrdinalIgnoreCase as the former is almost always incorrect for string equality.
parent dfebeb7d
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
/// <summary> /// <summary>
/// Represents the commands mapped on a particular configuration /// Represents the commands mapped on a particular configuration
/// </summary> /// </summary>
public sealed class CommandMap public sealed class CommandMap
{ {
private static readonly CommandMap private static readonly CommandMap
@default = CreateImpl(null, null), @default = CreateImpl(null, null),
twemproxy = CreateImpl(null, exclusions: new HashSet<RedisCommand> twemproxy = CreateImpl(null, exclusions: new HashSet<RedisCommand>
{ {
// see https://github.com/twitter/twemproxy/blob/master/notes/redis.md // see https://github.com/twitter/twemproxy/blob/master/notes/redis.md
RedisCommand.KEYS, RedisCommand.MIGRATE, RedisCommand.MOVE, RedisCommand.OBJECT, RedisCommand.RANDOMKEY, RedisCommand.KEYS, RedisCommand.MIGRATE, RedisCommand.MOVE, RedisCommand.OBJECT, RedisCommand.RANDOMKEY,
RedisCommand.RENAME, RedisCommand.RENAMENX, RedisCommand.SORT, RedisCommand.SCAN, RedisCommand.RENAME, RedisCommand.RENAMENX, RedisCommand.SORT, RedisCommand.SCAN,
RedisCommand.BITOP, RedisCommand.MSET, RedisCommand.MSETNX, RedisCommand.BITOP, RedisCommand.MSET, RedisCommand.MSETNX,
RedisCommand.HSCAN, RedisCommand.HSCAN,
RedisCommand.BLPOP, RedisCommand.BRPOP, RedisCommand.BRPOPLPUSH, // yeah, me neither! RedisCommand.BLPOP, RedisCommand.BRPOP, RedisCommand.BRPOPLPUSH, // yeah, me neither!
RedisCommand.SSCAN, RedisCommand.SSCAN,
RedisCommand.ZSCAN, RedisCommand.ZSCAN,
RedisCommand.PSUBSCRIBE, RedisCommand.PUBLISH, RedisCommand.PUNSUBSCRIBE, RedisCommand.SUBSCRIBE, RedisCommand.UNSUBSCRIBE, RedisCommand.PSUBSCRIBE, RedisCommand.PUBLISH, RedisCommand.PUNSUBSCRIBE, RedisCommand.SUBSCRIBE, RedisCommand.UNSUBSCRIBE,
RedisCommand.DISCARD, RedisCommand.EXEC, RedisCommand.MULTI, RedisCommand.UNWATCH, RedisCommand.WATCH, RedisCommand.DISCARD, RedisCommand.EXEC, RedisCommand.MULTI, RedisCommand.UNWATCH, RedisCommand.WATCH,
RedisCommand.SCRIPT, RedisCommand.SCRIPT,
RedisCommand.AUTH, RedisCommand.ECHO, RedisCommand.PING, RedisCommand.QUIT, RedisCommand.SELECT, RedisCommand.AUTH, RedisCommand.ECHO, RedisCommand.PING, RedisCommand.QUIT, RedisCommand.SELECT,
RedisCommand.BGREWRITEAOF, RedisCommand.BGSAVE, RedisCommand.CLIENT, RedisCommand.CLUSTER, RedisCommand.CONFIG, RedisCommand.DBSIZE, RedisCommand.BGREWRITEAOF, RedisCommand.BGSAVE, RedisCommand.CLIENT, RedisCommand.CLUSTER, RedisCommand.CONFIG, RedisCommand.DBSIZE,
RedisCommand.DEBUG, RedisCommand.FLUSHALL, RedisCommand.FLUSHDB, RedisCommand.INFO, RedisCommand.LASTSAVE, RedisCommand.MONITOR, RedisCommand.SAVE, RedisCommand.DEBUG, RedisCommand.FLUSHALL, RedisCommand.FLUSHDB, RedisCommand.INFO, RedisCommand.LASTSAVE, RedisCommand.MONITOR, RedisCommand.SAVE,
RedisCommand.SHUTDOWN, RedisCommand.SLAVEOF, RedisCommand.SLOWLOG, RedisCommand.SYNC, RedisCommand.TIME RedisCommand.SHUTDOWN, RedisCommand.SLAVEOF, RedisCommand.SLOWLOG, RedisCommand.SYNC, RedisCommand.TIME
}), ssdb = Create(new HashSet<string> { }), ssdb = Create(new HashSet<string> {
// see http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html // see http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html
"ping", "ping",
"get", "set", "del", "incr", "incrby", "mget", "mset", "keys", "getset", "setnx", "get", "set", "del", "incr", "incrby", "mget", "mset", "keys", "getset", "setnx",
"hget", "hset", "hdel", "hincrby", "hkeys", "hvals", "hmget", "hmset", "hlen", "hget", "hset", "hdel", "hincrby", "hkeys", "hvals", "hmget", "hmset", "hlen",
"zscore", "zadd", "zrem", "zrange", "zrangebyscore", "zincrby", "zdecrby", "zcard", "zscore", "zadd", "zrem", "zrange", "zrangebyscore", "zincrby", "zdecrby", "zcard",
"llen", "lpush", "rpush", "lpop", "rpop", "lrange", "lindex" "llen", "lpush", "rpush", "lpop", "rpop", "lrange", "lindex"
}, true), }, true),
sentinel = Create(new HashSet<string> { sentinel = Create(new HashSet<string> {
// see http://redis.io/topics/sentinel // see http://redis.io/topics/sentinel
"ping", "info", "sentinel", "subscribe", "psubscribe", "unsubscribe", "punsubscribe" }, true); "ping", "info", "sentinel", "subscribe", "psubscribe", "unsubscribe", "punsubscribe" }, true);
private readonly byte[][] map; private readonly byte[][] map;
internal CommandMap(byte[][] map) internal CommandMap(byte[][] map)
{ {
this.map = map; this.map = map;
} }
/// <summary> /// <summary>
/// The default commands specified by redis /// The default commands specified by redis
/// </summary> /// </summary>
public static CommandMap Default { get { return @default; } } public static CommandMap Default { get { return @default; } }
/// <summary> /// <summary>
/// The commands available to <a href="twemproxy">https://github.com/twitter/twemproxy</a> /// The commands available to <a href="twemproxy">https://github.com/twitter/twemproxy</a>
/// </summary> /// </summary>
/// <remarks>https://github.com/twitter/twemproxy/blob/master/notes/redis.md</remarks> /// <remarks>https://github.com/twitter/twemproxy/blob/master/notes/redis.md</remarks>
public static CommandMap Twemproxy { get { return twemproxy; } } public static CommandMap Twemproxy { get { return twemproxy; } }
/// <summary> /// <summary>
/// The commands available to <a href="ssdb">http://www.ideawu.com/ssdb/</a> /// The commands available to <a href="ssdb">http://www.ideawu.com/ssdb/</a>
/// </summary> /// </summary>
/// <remarks>http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html</remarks> /// <remarks>http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html</remarks>
public static CommandMap SSDB { get { return ssdb; } } public static CommandMap SSDB { get { return ssdb; } }
/// <summary> /// <summary>
/// The commands available to <a href="Sentinel">http://redis.io/topics/sentinel</a> /// The commands available to <a href="Sentinel">http://redis.io/topics/sentinel</a>
/// </summary> /// </summary>
/// <remarks>http://redis.io/topics/sentinel</remarks> /// <remarks>http://redis.io/topics/sentinel</remarks>
public static CommandMap Sentinel { get { return sentinel; } } public static CommandMap Sentinel { get { return sentinel; } }
/// <summary> /// <summary>
/// Create a new CommandMap, customizing some commands /// Create a new CommandMap, customizing some commands
/// </summary> /// </summary>
public static CommandMap Create(Dictionary<string, string> overrides) public static CommandMap Create(Dictionary<string, string> overrides)
{ {
if (overrides == null || overrides.Count == 0) return Default; if (overrides == null || overrides.Count == 0) return Default;
if (ReferenceEquals(overrides.Comparer, StringComparer.OrdinalIgnoreCase) || if (ReferenceEquals(overrides.Comparer, StringComparer.OrdinalIgnoreCase))
ReferenceEquals(overrides.Comparer, StringComparer.InvariantCultureIgnoreCase)) {
{ // that's ok; we're happy with ordinal/invariant case-insensitive
// that's ok; we're happy with ordinal/invariant case-insensitive // (but not culture-specific insensitive; completely untested)
// (but not culture-specific insensitive; completely untested) }
} else
else {
{ // need case insensitive
// need case insensitive overrides = new Dictionary<string, string>(overrides, StringComparer.OrdinalIgnoreCase);
overrides = new Dictionary<string, string>(overrides, StringComparer.OrdinalIgnoreCase); }
} return CreateImpl(overrides, null);
return CreateImpl(overrides, null); }
}
/// <summary>
/// <summary> /// Creates a CommandMap by specifying which commands are available or unavailable
/// Creates a CommandMap by specifying which commands are available or unavailable /// </summary>
/// </summary> public static CommandMap Create(HashSet<string> commands, bool available = true)
public static CommandMap Create(HashSet<string> commands, bool available = true) {
{
if (available)
if (available) {
{ var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // nix everything
// nix everything foreach (RedisCommand command in Enum.GetValues(typeof(RedisCommand)))
foreach (RedisCommand command in Enum.GetValues(typeof(RedisCommand))) {
{ dictionary[command.ToString()] = null;
dictionary[command.ToString()] = null; }
} if (commands != null)
if (commands != null) {
{ // then include (by removal) the things that are available
// then include (by removal) the things that are available foreach (string command in commands)
foreach (string command in commands) {
{ dictionary.Remove(command);
dictionary.Remove(command); }
} }
} return CreateImpl(dictionary, null);
return CreateImpl(dictionary, null); }
} else
else {
{ HashSet<RedisCommand> exclusions = null;
HashSet<RedisCommand> exclusions = null; if (commands != null)
if (commands != null) {
{ // nix the things that are specified
// nix the things that are specified foreach (var command in commands)
foreach (var command in commands) {
{ RedisCommand parsed;
RedisCommand parsed; if (Enum.TryParse(command, true, out parsed))
if (Enum.TryParse(command, true, out parsed)) {
{ (exclusions ?? (exclusions = new HashSet<RedisCommand>())).Add(parsed);
(exclusions ?? (exclusions = new HashSet<RedisCommand>())).Add(parsed); }
} }
} }
} if (exclusions == null || exclusions.Count == 0) return Default;
if (exclusions == null || exclusions.Count == 0) return Default; return CreateImpl(null, exclusions);
return CreateImpl(null, exclusions); }
}
}
}
/// <summary>
/// <summary> /// See Object.ToString()
/// See Object.ToString() /// </summary>
/// </summary> public override string ToString()
public override string ToString() {
{ var sb = new StringBuilder();
var sb = new StringBuilder(); AppendDeltas(sb);
AppendDeltas(sb); return sb.ToString();
return sb.ToString(); }
}
internal void AppendDeltas(StringBuilder sb)
internal void AppendDeltas(StringBuilder sb) {
{ for (int i = 0; i < map.Length; i++)
for (int i = 0; i < map.Length; i++) {
{ var key = ((RedisCommand)i).ToString();
var key = ((RedisCommand)i).ToString(); var value = map[i] == null ? "" : Encoding.UTF8.GetString(map[i]);
var value = map[i] == null ? "" : Encoding.UTF8.GetString(map[i]); if (key != value)
if (key != value) {
{ if (sb.Length != 0) sb.Append(',');
if (sb.Length != 0) sb.Append(','); sb.Append('$').Append(key).Append('=').Append(value);
sb.Append('$').Append(key).Append('=').Append(value); }
} }
} }
}
internal void AssertAvailable(RedisCommand command)
internal void AssertAvailable(RedisCommand command) {
{ if (map[(int)command] == null) throw ExceptionFactory.CommandDisabled(false, command, null, null);
if (map[(int)command] == null) throw ExceptionFactory.CommandDisabled(false, command, null, null); }
}
internal byte[] GetBytes(RedisCommand command)
internal byte[] GetBytes(RedisCommand command) {
{ return map[(int)command];
return map[(int)command]; }
}
internal bool IsAvailable(RedisCommand command)
internal bool IsAvailable(RedisCommand command) {
{ return map[(int)command] != null;
return map[(int)command] != null; }
}
private static CommandMap CreateImpl(Dictionary<string, string> caseInsensitiveOverrides, HashSet<RedisCommand> exclusions)
private static CommandMap CreateImpl(Dictionary<string, string> caseInsensitiveOverrides, HashSet<RedisCommand> exclusions) {
{ var commands = (RedisCommand[])Enum.GetValues(typeof(RedisCommand));
var commands = (RedisCommand[])Enum.GetValues(typeof(RedisCommand));
byte[][] map = new byte[commands.Length][];
byte[][] map = new byte[commands.Length][]; bool haveDelta = false;
bool haveDelta = false; for (int i = 0; i < commands.Length; i++)
for (int i = 0; i < commands.Length; i++) {
{ int idx = (int)commands[i];
int idx = (int)commands[i]; string name = commands[i].ToString(), value = name;
string name = commands[i].ToString(), value = name;
if (exclusions != null && exclusions.Contains(commands[i]))
if (exclusions != null && exclusions.Contains(commands[i])) {
{ map[idx] = null;
map[idx] = null; }
} else
else {
{ if (caseInsensitiveOverrides != null)
if (caseInsensitiveOverrides != null) {
{ string tmp;
string tmp; if (caseInsensitiveOverrides.TryGetValue(name, out tmp))
if (caseInsensitiveOverrides.TryGetValue(name, out tmp)) {
{ value = tmp;
value = tmp; }
} }
} if (value != name) haveDelta = true;
if (value != name) haveDelta = true;
haveDelta = true;
haveDelta = true; byte[] val = string.IsNullOrWhiteSpace(value) ? null : Encoding.UTF8.GetBytes(value);
byte[] val = string.IsNullOrWhiteSpace(value) ? null : Encoding.UTF8.GetBytes(value); map[idx] = val;
map[idx] = val; }
} }
} if (!haveDelta && @default != null) return @default;
if (!haveDelta && @default != null) return @default;
return new CommandMap(map);
return new CommandMap(map); }
} }
} }
}
...@@ -87,7 +87,7 @@ internal static void Unknown(string key) ...@@ -87,7 +87,7 @@ internal static void Unknown(string key)
ConfigChannel, AbortOnConnectFail, ResolveDns, ConfigChannel, AbortOnConnectFail, ResolveDns,
ChannelPrefix, Proxy, ConnectRetry, ChannelPrefix, Proxy, ConnectRetry,
ConfigCheckSeconds, DefaultDatabase, ConfigCheckSeconds, DefaultDatabase,
}.ToDictionary(x => x, StringComparer.InvariantCultureIgnoreCase); }.ToDictionary(x => x, StringComparer.OrdinalIgnoreCase);
public static string TryNormalize(string value) public static string TryNormalize(string value)
{ {
...@@ -401,7 +401,7 @@ internal bool HasDnsEndPoints() ...@@ -401,7 +401,7 @@ internal bool HasDnsEndPoints()
#pragma warning disable 1998 // NET40 is sync, not async, currently #pragma warning disable 1998 // NET40 is sync, not async, currently
internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log) internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log)
{ {
Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.InvariantCultureIgnoreCase); Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < endpoints.Count; i++) for (int i = 0; i < endpoints.Count; i++)
{ {
var dns = endpoints[i] as DnsEndPoint; var dns = endpoints[i] as DnsEndPoint;
...@@ -598,7 +598,7 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -598,7 +598,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
var cmdName = option.Substring(1, idx - 1); var cmdName = option.Substring(1, idx - 1);
if (Enum.TryParse(cmdName, true, out cmd)) if (Enum.TryParse(cmdName, true, out cmd))
{ {
if (map == null) map = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); if (map == null) map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
map[cmdName] = value; map[cmdName] = value;
} }
} }
......
...@@ -1447,7 +1447,7 @@ private async Task<ServerEndPoint> NominatePreferredMaster(TextWriter log, Serve ...@@ -1447,7 +1447,7 @@ private async Task<ServerEndPoint> NominatePreferredMaster(TextWriter log, Serve
Dictionary<string, int> uniques = null; Dictionary<string, int> uniques = null;
if (useTieBreakers) if (useTieBreakers)
{ // count the votes { // count the votes
uniques = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase); uniques = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
await WaitAllIgnoreErrorsAsync(tieBreakers, 50, log).ForAwait(); await WaitAllIgnoreErrorsAsync(tieBreakers, 50, log).ForAwait();
for (int i = 0; i < tieBreakers.Length; i++) for (int i = 0; i < tieBreakers.Length; i++)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment