Permalink
Browse files

Modified Cursors to cache escaped keys again.

  • Loading branch information...
1 parent 2a2eb79 commit 27413cb4163d3cfb0c1d426a8d247ba7b95b17eb @halter73 halter73 committed Nov 1, 2012
@@ -11,53 +11,46 @@ namespace Microsoft.AspNet.SignalR
internal unsafe class Cursor
{
private static char[] _escapeChars = new[] { '\\', '|', ',' };
+ private string _escapedKey;
- private string _key;
- public string Key
- {
- get
- {
- return _key;
- }
- set
- {
- _key = value;
- }
- }
+ public string Key { get; private set; }
public ulong Id { get; set; }
public static Cursor Clone(Cursor cursor)
{
- return new Cursor
- {
- Id = cursor.Id,
- Key = cursor.Key
- };
+ return new Cursor(cursor.Key, cursor.Id, cursor._escapedKey);
}
- public static string MakeCursor(IList<Cursor> cursors)
+ public Cursor(string key, ulong id)
+ : this(key, id, Escape(key))
{
- return MakeCursor(cursors, s => s);
+ }
+
+ public Cursor(string key, ulong id, string minifiedKey)
+ {
+ Key = key;
+ Id = id;
+ _escapedKey = minifiedKey;
}
- public static string MakeCursor(IList<Cursor> cursors, Func<string, string> keyMap)
+ public static string MakeCursor(IList<Cursor> cursors)
{
- return MakeCursorFast(cursors, keyMap) ?? MakeCursorSlow(cursors, keyMap);
+ return MakeCursorFast(cursors) ?? MakeCursorSlow(cursors);
}
- private static string MakeCursorSlow(IList<Cursor> cursors, Func<string, string> keyMap)
+ private static string MakeCursorSlow(IList<Cursor> cursors)
{
var serialized = new string[cursors.Count];
for (int i = 0; i < cursors.Count; i++)
{
- serialized[i] = Escape(keyMap(cursors[i].Key)) + ',' + cursors[i].Id.ToString("X");
+ serialized[i] = cursors[i]._escapedKey + ',' + cursors[i].Id.ToString("X");
}
return String.Join("|", serialized);
}
- private static string MakeCursorFast(IList<Cursor> cursors, Func<string, string> keyMap)
+ private static string MakeCursorFast(IList<Cursor> cursors)
{
const int MAX_CHARS = 8 * 1024;
char* pChars = stackalloc char[MAX_CHARS];
@@ -68,7 +61,7 @@ private static string MakeCursorFast(IList<Cursor> cursors, Func<string, string>
for (int i = 0; i < cursors.Count; i++)
{
Cursor cursor = cursors[i];
- string escapedKey = Escape(keyMap(cursor.Key));
+ string escapedKey = cursor._escapedKey;
// comma + up to 16-char hex Id + pipe
numCharsInBuffer += escapedKey.Length + 18;
@@ -171,54 +164,73 @@ public static Cursor[] GetCursors(string cursor)
return GetCursors(cursor, s => s);
}
- public static Cursor[] GetCursors(string cursor, Func<string, string> keyMap)
+ public static Cursor[] GetCursors(string cursor, Func<string, string> keyMaximizer)
{
var cursors = new List<Cursor>();
- var current = new Cursor();
+ string currentKey = null;
+ string currentEscapedKey = null;
+ ulong currentId;
bool escape = false;
+ bool consumingKey = true;
var sb = new StringBuilder();
+ var sbEscaped = new StringBuilder();
foreach (var ch in cursor)
{
if (escape)
{
sb.Append(ch);
+ if (consumingKey)
+ {
+ sbEscaped.Append(ch);
+ }
escape = false;
}
else
{
if (ch == '\\')
{
+ if (consumingKey)
+ {
+ sbEscaped.Append('\\');
+ }
escape = true;
}
else if (ch == ',')
{
- current.Key = keyMap(sb.ToString());
+ currentEscapedKey = sbEscaped.ToString();
+ currentKey = keyMaximizer(sb.ToString());
// If the keyMap cannot find a key, we cannot create an array of cursors.
- if (current.Key == null)
+ if (currentKey == null)
{
return null;
}
sb.Clear();
+ sbEscaped.Clear();
+ consumingKey = false;
}
else if (ch == '|')
{
- current.Id = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
- cursors.Add(current);
- current = new Cursor();
+ currentId = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
+ cursors.Add(new Cursor(currentKey, currentId, currentEscapedKey));
sb.Clear();
+ consumingKey = true;
}
else
{
sb.Append(ch);
+ if (consumingKey)
+ {
+ sbEscaped.Append(ch);
+ }
}
}
}
if (sb.Length > 0)
{
- current.Id = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
- cursors.Add(current);
+ currentId = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber);
+ cursors.Add(new Cursor(currentKey, currentId, currentEscapedKey));
}
return cursors.ToArray();
@@ -74,11 +74,7 @@ public override bool AddEvent(string eventKey, Topic topic)
var index = _cursors.FindIndex(c => c.Key == eventKey);
if (index == -1)
{
- _cursors.Add(new Cursor
- {
- Key = eventKey,
- Id = GetMessageId(topic)
- });
+ _cursors.Add(new Cursor(eventKey, GetMessageId(topic), _stringMinifier.Minify(eventKey)));
_cursorTopics.Add(topic);
@@ -117,7 +113,7 @@ public override void SetEventTopic(string eventKey, Topic topic)
public override string GetCursor()
{
- return Cursor.MakeCursor(_cursors, _stringMinifier.Minify);
+ return Cursor.MakeCursor(_cursors);
}
protected override void PerformWork(ref List<ArraySegment<Message>> items, out string nextCursor, ref int totalCount, out object state)
@@ -144,7 +140,7 @@ protected override void PerformWork(ref List<ArraySegment<Message>> items, out s
}
}
- nextCursor = Cursor.MakeCursor(cursors, _stringMinifier.Minify);
+ nextCursor = Cursor.MakeCursor(cursors);
// Return the state as a list of cursors
state = cursors;
@@ -179,11 +175,7 @@ private bool UpdateCursor(string key, ulong id)
private IEnumerable<Cursor> GetCursorsFromEventKeys(IEnumerable<string> eventKeys, IDictionary<string, Topic> topics)
{
return from key in eventKeys
- select new Cursor
- {
- Key = key,
- Id = GetMessageId(topics, key)
- };
+ select new Cursor(key, GetMessageId(topics, key), _stringMinifier.Minify(key));
}
private ulong GetMessageId(IDictionary<string, Topic> topics, string key)
@@ -6,7 +6,7 @@ public interface IStringMinifier
/// Minifies a string in a way that can be reversed by this instance of <see cref="IStringMinifier"/>.
/// </summary>
/// <param name="fullString">The string to be minified</param>
- /// <returns>A minified representation of the <see cref="fullString"/>.</returns>
+ /// <returns>A minified representation of the <see cref="fullString"/> without the following characters:,|\</returns>
string Minify(string fullString);
/// <summary>
@@ -30,11 +30,7 @@ public class ScaleoutSubscription : Subscription
if (cursor == null)
{
cursors = from key in _streams.Keys
- select new Cursor
- {
- Key = key,
- Id = GetCursorId(key)
- };
+ select new Cursor(key, GetCursorId(key));
}
else
{
@@ -86,11 +82,7 @@ protected override void PerformWork(ref List<ArraySegment<Message>> items, out s
{
// Create a cursor and add it to the list.
// Point the Id to the first value
- cursor = new Cursor
- {
- Id = stream.Value.MinKey,
- Key = stream.Key
- };
+ cursor = new Cursor(key: stream.Key, id: stream.Value.MinKey);
consumed = false;
}
@@ -11,9 +11,9 @@ public class CursorFacts
public void Symmetric()
{
var cursors = new[] {
- new Cursor { Id = 10, Key = @"\foo|1,4,\|\\\," },
- new Cursor { Id = 0, Key = ""},
- new Cursor { Id = 0xffffffffffffffff, Key = "\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u13A1"}
+ new Cursor(@"\foo|1,4,\|\\\,", 10),
+ new Cursor("", 0),
+ new Cursor("\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u13A1", 0xffffffffffffffff)
};
var serialized = Cursor.MakeCursor(cursors);
@@ -27,9 +27,20 @@ public void Symmetric()
}
[Fact]
+ public void SerializesCorrectlyWithIdEqualTo0()
+ {
+ var cursors = new[] { new Cursor("", 0) };
+
+ var serialized = Cursor.MakeCursor(cursors);
+ var deserializedCursors = Cursor.GetCursors(serialized);
+
+ Assert.Equal(cursors[0].Id, deserializedCursors[0].Id);
+ }
+
+ [Fact]
public void SymmetricWithManyCursors()
{
- var repeatedCursor = new Cursor { Id = 0xffffffffffffffff, Key = Guid.NewGuid().ToString() };
+ var repeatedCursor = new Cursor(Guid.NewGuid().ToString(), 0xffffffffffffffff);
var manyCursors = Enumerable.Repeat(repeatedCursor, 8192).ToList();
var serialized = Cursor.MakeCursor(manyCursors);
@@ -67,12 +78,12 @@ public void SymmetricWithKeyMap()
Func<string, string> inverseMap = s => inverseDict[s];
var cursors = new[] {
- new Cursor { Id = 10, Key = @"\foo|1,4,\|\\\," },
- new Cursor { Id = 0, Key = ""},
- new Cursor { Id = 0xffffffffffffffff, Key = "\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u13A1"}
+ new Cursor(@"\foo|1,4,\|\\\,", 10, map(@"\foo|1,4,\|\\\,")),
+ new Cursor("", 0, map("")),
+ new Cursor("\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u13A1", 0xffffffffffffffff, map("\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u13A1"))
};
- var serialized = Cursor.MakeCursor(cursors, map);
+ var serialized = Cursor.MakeCursor(cursors);
var deserializedCursors = Cursor.GetCursors(serialized, inverseMap);
for (var i = 0; i < cursors.Length; i++)
@@ -85,19 +96,18 @@ public void SymmetricWithKeyMap()
[Fact]
public void GetCursorsReturnsNullIfKeyMapReturnsNull()
{
- Func<string, string> identity = s => s;
Func<string, string> sometimesReturnsNull = s => s != "" ? s : null;
var cursors = new[] {
- new Cursor { Id = 10, Key = @"\foo|1,4,\|\\\," },
- new Cursor { Id = 0, Key = ""},
- new Cursor { Id = 0xffffffffffffffff, Key = "\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u03A1"}
+ new Cursor(@"\foo|1,4,\|\\\,", 10),
+ new Cursor("", 0),
+ new Cursor("\u03A3\u03B9\u03B3\u03BD\u03B1\u03BB\u03A1", 0xffffffffffffffff)
};
- var serialized = Cursor.MakeCursor(cursors, identity);
+ var serialized = Cursor.MakeCursor(cursors);
var deserializedCursors = Cursor.GetCursors(serialized, sometimesReturnsNull);
- Assert.Equal(null, deserializedCursors);
+ Assert.Null(deserializedCursors);
}
}
}
@@ -59,6 +59,8 @@ public void NewSubscriptionGetsAllMessages()
public void SubscriptionWithExistingCursor()
{
var dr = new DefaultDependencyResolver();
+ var passThroughMinfier = new PassThroughStringMinifier();
+ dr.Register(typeof(IStringMinifier), () => passThroughMinfier);
using (var bus = new MessageBus(dr))
{
var subscriber = new TestSubscriber(new[] { "key" });
@@ -102,6 +104,8 @@ public void SubscriptionWithExistingCursor()
public void SubscriptionWithMultipleExistingCursors()
{
var dr = new DefaultDependencyResolver();
+ var passThroughMinfier = new PassThroughStringMinifier();
+ dr.Register(typeof(IStringMinifier), () => passThroughMinfier);
using (var bus = new MessageBus(dr))
{
var subscriber = new TestSubscriber(new[] { "key", "key2" });
@@ -284,5 +288,22 @@ public void DisposingBusShutsWorkersDown()
Assert.Equal(bus.AllocatedWorkers, 0);
}
}
+
+ private class PassThroughStringMinifier : IStringMinifier
+ {
+ public string Minify(string s)
+ {
+ return s;
+ }
+
+ public string Unminify(string s)
+ {
+ return s;
+ }
+
+ public void RemoveUnminified(string s)
+ {
+ }
+ }
}
}
Oops, something went wrong.

0 comments on commit 27413cb

Please sign in to comment.