private List<Cursor> GetCursorsFromEventKeys(IList<string> eventKeys, TopicLookup topics)
        {
            var list = new List<Cursor>(eventKeys.Count);
            foreach (var eventKey in eventKeys)
            {
                var cursor = new Cursor(eventKey, GetMessageId(topics, eventKey), _stringMinifier.Minify(eventKey));
                list.Add(cursor);
            }

            return list;
        }
 public static Cursor Clone(Cursor cursor)
 {
     return new Cursor(cursor.Key, cursor.Id, cursor._escapedKey);
 }
        private void AddCursorForStream(int streamIndex, List<Cursor> cursors)
        {
            ScaleoutMapping maxMapping = _streams[streamIndex].MaxMapping;

            ulong id = UInt64.MaxValue;
            string key = streamIndex.ToString(CultureInfo.InvariantCulture);

            if (maxMapping != null)
            {
                id = maxMapping.Id;
            }

            var newCursor = new Cursor(key, id);
            cursors.Add(newCursor);
        }
        public static List<Cursor> GetCursors(string cursor, string prefix, Func<string, object, string> keyMaximizer, object state)
        {
            // Technically GetCursors should never be called with a null value, so this is extra cautious
            if (String.IsNullOrEmpty(cursor))
            {
                throw new FormatException(Resources.Error_InvalidCursorFormat);
            }

            // If the cursor does not begin with the prefix stream, it isn't necessarily a formatting problem.
            // The cursor with a different prefix might have had different, but also valid, formatting.
            // Null should be returned so new cursors will be generated
            if (!cursor.StartsWith(prefix, StringComparison.Ordinal))
            {
                return null;
            }

            var signals = new HashSet<string>();
            var cursors = new List<Cursor>();
            string currentKey = null;
            string currentEscapedKey = null;
            ulong currentId;
            bool escape = false;
            bool consumingKey = true;
            var sb = new StringBuilder();
            var sbEscaped = new StringBuilder();
            Cursor parsedCursor;

            for (int i = prefix.Length; i < cursor.Length; i++)
            {
                var ch = cursor[i];

                // escape can only be true if we are consuming the key
                if (escape)
                {
                    if (ch != '\\' && ch != ',' && ch != '|')
                    {
                        throw new FormatException(Resources.Error_InvalidCursorFormat);
                    }

                    sb.Append(ch);
                    sbEscaped.Append(ch);
                    escape = false;
                }
                else
                {
                    if (ch == '\\')
                    {
                        if (!consumingKey)
                        {
                            throw new FormatException(Resources.Error_InvalidCursorFormat);
                        }

                        sbEscaped.Append('\\');
                        escape = true;
                    }
                    else if (ch == ',')
                    {
                        if (!consumingKey)
                        {
                            throw new FormatException(Resources.Error_InvalidCursorFormat);
                        }

                        // For now String.Empty is an acceptable key, but this should change once we verify
                        // that empty keys cannot be created legitimately.
                        currentKey = keyMaximizer(sb.ToString(), state);

                        // If the keyMap cannot find a key, we cannot create an array of cursors.
                        // This most likely means there was an AppDomain restart or a misbehaving client.
                        if (currentKey == null)
                        {
                            return null;
                        }
                        // Don't allow duplicate keys
                        if (!signals.Add(currentKey))
                        {
                            throw new FormatException(Resources.Error_InvalidCursorFormat);
                        }

                        currentEscapedKey = sbEscaped.ToString();

                        sb.Clear();
                        sbEscaped.Clear();
                        consumingKey = false;
                    }
                    else if (ch == '|')
                    {
                        if (consumingKey)
                        {
                            throw new FormatException(Resources.Error_InvalidCursorFormat);
                        }

                        ParseCursorId(sb, out currentId);

                        parsedCursor = new Cursor(currentKey, currentId, currentEscapedKey);

                        cursors.Add(parsedCursor);
                        sb.Clear();
                        consumingKey = true;
                    }
                    else
                    {
                        sb.Append(ch);
                        if (consumingKey)
                        {
                            sbEscaped.Append(ch);
                        }
                    }
                }
            }

            if (consumingKey)
            {
                throw new FormatException(Resources.Error_InvalidCursorFormat);
            }

            ParseCursorId(sb, out currentId);

            parsedCursor = new Cursor(currentKey, currentId, currentEscapedKey);

            cursors.Add(parsedCursor);

            return cursors;
        }