public ReplacementPosition(ReplacementPosition copy)
 {
     Start  = copy.Start;
     Length = copy.Length;
 }
        public String MaskSecrets(String input)
        {
            if (String.IsNullOrEmpty(input))
            {
                return(String.Empty);
            }

            var secretPositions = new List <ReplacementPosition>();

            // Read section.
            try
            {
                m_lock.EnterReadLock();

                // Get indexes and lengths of all substrings that will be replaced.
                foreach (RegexSecret regexSecret in m_regexSecrets)
                {
                    secretPositions.AddRange(regexSecret.GetPositions(input));
                }

                foreach (ValueSecret valueSecret in m_valueSecrets)
                {
                    secretPositions.AddRange(valueSecret.GetPositions(input));
                }
            }
            finally
            {
                if (m_lock.IsReadLockHeld)
                {
                    m_lock.ExitReadLock();
                }
            }

            // Short-circuit if nothing to replace.
            if (secretPositions.Count == 0)
            {
                return(input);
            }

            // Merge positions into ranges of characters to replace.
            List <ReplacementPosition> replacementPositions = new List <ReplacementPosition>();
            ReplacementPosition        currentReplacement   = null;

            foreach (ReplacementPosition secretPosition in secretPositions.OrderBy(x => x.Start))
            {
                if (currentReplacement == null)
                {
                    currentReplacement = new ReplacementPosition(copy: secretPosition);
                    replacementPositions.Add(currentReplacement);
                }
                else
                {
                    if (secretPosition.Start <= currentReplacement.End)
                    {
                        // Overlap
                        currentReplacement.Length = Math.Max(currentReplacement.End, secretPosition.End) - currentReplacement.Start;
                    }
                    else
                    {
                        // No overlap
                        currentReplacement = new ReplacementPosition(copy: secretPosition);
                        replacementPositions.Add(currentReplacement);
                    }
                }
            }

            // Replace
            var   stringBuilder = new StringBuilder();
            Int32 startIndex    = 0;

            foreach (var replacement in replacementPositions)
            {
                stringBuilder.Append(input.Substring(startIndex, replacement.Start - startIndex));
                stringBuilder.Append("***");
                startIndex = replacement.Start + replacement.Length;
            }

            if (startIndex < input.Length)
            {
                stringBuilder.Append(input.Substring(startIndex));
            }

            return(stringBuilder.ToString());
        }