public CsvStats StripByEvents(string startString, string endString, bool invert, out int numFramesStripped)
        {
            CsvStats   newCsvStats  = new CsvStats();
            List <int> startIndices = null;
            List <int> endIndices   = null;

            GetEventFrameIndexDelimiters(startString, endString, out startIndices, out endIndices);
            numFramesStripped = 0;
            if (startIndices.Count == 0)
            {
                return(this);
            }

            numFramesStripped = -1;
            int frameCount = 0;

            // Strip out samples and recompute averages
            foreach (StatSamples stat in Stats.Values)
            {
                StatSamples newStat = new StatSamples(stat, false);
                newStat.samples = new List <float>(SampleCount);
                int stripEventIndex = 0;
                for (int i = 0; i < stat.samples.Count; i++)
                {
                    int startIndex = (stripEventIndex < startIndices.Count) ? startIndices[stripEventIndex] : stat.samples.Count;
                    int endIndex   = (stripEventIndex < endIndices.Count) ? endIndices[stripEventIndex] : stat.samples.Count;
                    if (i < startIndex)
                    {
                        newStat.samples.Add(stat.samples[i]);
                    }
                    else
                    {
                        if (i == endIndex)
                        {
                            stripEventIndex++;
                        }
                    }
                }
                if (numFramesStripped == -1)
                {
                    numFramesStripped = stat.samples.Count - newStat.samples.Count;
                    frameCount        = stat.samples.Count;
                }

                newStat.ComputeAverageAndTotal();
                newCsvStats.AddStat(newStat);
            }

            // Strip out the events
            int FrameOffset = 0;

            {
                int stripEventIndex = 0;
                for (int i = 0; i < Events.Count; i++)
                {
                    CsvEvent csvEvent   = Events[i];
                    int      startIndex = (stripEventIndex < startIndices.Count) ? startIndices[stripEventIndex] : frameCount;
                    int      endIndex   = (stripEventIndex < endIndices.Count) ? endIndices[stripEventIndex] : frameCount;
                    CsvEvent newEvent   = new CsvEvent(csvEvent.Name, csvEvent.Frame + FrameOffset);
                    if (csvEvent.Frame < startIndex)
                    {
                        newCsvStats.Events.Add(newEvent);
                    }
                    else
                    {
                        if (csvEvent.Frame == startIndex)
                        {
                            // Check if this is the last event this frame
                            if (i == Events.Count - 1 || Events[i + 1].Frame != csvEvent.Frame)
                            {
                                // Subsequent events will get offset by this amount
                                FrameOffset -= endIndex - startIndex;
                            }
                        }
                        if (csvEvent.Frame == endIndex + 1)
                        {
                            newCsvStats.Events.Add(newEvent);
                            stripEventIndex++;
                        }
                    }
                }
            }
            newCsvStats.metaData = metaData;
            return(newCsvStats);
        }
 public CsvEvent(CsvEvent source)
 {
     Name  = source.Name;
     Frame = source.Frame;
 }
        public void GetEventFrameIndexDelimiters(string startString, string endString, out List <int> startIndices, out List <int> endIndices)
        {
            bool startIsWild = false;
            bool endIsWild   = false;

            startString = startString.ToLower().Trim();
            if (endString != null)
            {
                endString = endString.ToLower().Trim();
                if (endString.EndsWith("*"))
                {
                    endIsWild = true;
                    endString = endString.TrimEnd('*');
                }
            }
            if (startString.EndsWith("*"))
            {
                startIsWild = true;
                startString = startString.TrimEnd('*');
            }

            bool insidePair = false;

            startIndices = new List <int>();
            endIndices   = new List <int>();
            for (int i = 0; i < Events.Count; i++)
            {
                CsvEvent csvEvent   = Events[i];
                string   evName     = csvEvent.Name.ToLower();
                string   strToMatch = insidePair ? endString : startString;
                bool     isWild     = insidePair ? endIsWild : startIsWild;

                bool found = false;
                if (strToMatch != null)
                {
                    if (isWild)
                    {
                        if (evName.StartsWith(strToMatch))
                        {
                            found = true;
                        }
                    }
                    else if (evName == strToMatch)
                    {
                        found = true;
                    }
                }

                if (found)
                {
                    if (insidePair)
                    {
                        endIndices.Add(csvEvent.Frame);
                    }
                    else
                    {
                        startIndices.Add(csvEvent.Frame);
                    }
                    insidePair = !insidePair;
                }
            }

            // If the end event was missing, add it at the end
            if (endIndices.Count == startIndices.Count - 1)
            {
                endIndices.Add(SampleCount);
            }
        }
        public static CsvStats ReadCSVFromLines(string[] linesArray, string[] statNames, int numRowsToSkip = 0, bool skipReadingData = false)
        {
            List <string> lines = linesArray.ToList();

            // Check if we have any metadata
            bool bHasMetaData = LineIsMetadata(lines.Last());

            CsvMetadata metaData = null;

            // Add the metadata to the stats collection
            if (bHasMetaData)
            {
                string[] lastLine = lines.Last().Split(',');
                metaData = new CsvMetadata(lastLine);

                // New CSVs from the csv profiler have a header row at the end of the file,
                // since the profiler writes out the file incrementally.
                if ("1" == metaData.GetValue("hasheaderrowatend", null))
                {
                    // Swap the header row for the one at the end of the file,
                    // then remove the end one.
                    lines[0] = lines[lines.Count - 2];
                    lines.RemoveAt(lines.Count - 2);
                }
            }

            if (numRowsToSkip > 0)
            {
                lines.RemoveRange(0, numRowsToSkip);
            }
            string[] headings = lines[0].Split(',');

            if (lines.Count > 2 && lines[lines.Count - 1] == "\"")
            {
                lines[lines.Count - 2] += lines[lines.Count - 1];
                lines.RemoveAt(lines.Count - 1);
            }

            if (skipReadingData)
            {
                int dataLineCount = bHasMetaData ? lines.Count - 2 : lines.Count - 1;
                lines.RemoveRange(1, dataLineCount);
            }

            // Get the list of lower case stat names, expanding wildcards
            string[] statNamesLowercase = null;
            if (statNames != null)
            {
                statNamesLowercase = statNames.Select(s => s.ToLowerInvariant()).ToArray();
                {
                    // Expand the list of stat names based on the wildcards and the headers. We do this here to make sorting simpler
                    HashSet <string> newStatNamesLowercase = new HashSet <string>();
                    foreach (string statname in statNamesLowercase)
                    {
                        if (statname.EndsWith("*"))
                        {
                            int    index  = statname.LastIndexOf('*');
                            string prefix = statname.Substring(0, index);
                            // Expand all the stat names
                            foreach (string headingStat in headings)
                            {
                                if (headingStat.ToLower().StartsWith(prefix))
                                {
                                    newStatNamesLowercase.Add(headingStat.ToLower());
                                }
                            }
                        }
                        else
                        {
                            newStatNamesLowercase.Add(statname);
                        }
                    }

                    statNamesLowercase = newStatNamesLowercase.ToArray();
                }
            }

            // First line is headings, last line contains build info
            int numSamples = lines.Count - (bHasMetaData ? 2 : 1);

            // Create the stats
            int eventHeadingIndex = -1;

            StatSamples[] stats = new StatSamples[headings.Length];
            for (int i = 0; i < headings.Length; i++)
            {
                string heading = headings[i].Trim();
                if (heading == "")
                {
                    continue;
                }
                // find the events column (if there is one)
                else if (heading.ToLower() == "events")
                {
                    eventHeadingIndex = i;
                }
                else if (statNamesLowercase == null || statNamesLowercase.Contains(heading.ToLower()))
                {
                    stats[i] = new StatSamples(heading, numSamples);
                }
            }


            List <CsvEvent> FilteredEvents = new List <CsvEvent>();

            if (!skipReadingData)
            {
                string[] eventStrings = new string[numSamples];

//                for (int i = 1; i < numSamples + 1; i++)
                Parallel.For(1, numSamples + 1,
                             i =>
                {
                    int sampleIndex = i - 1;
                    int statIndex   = 0;
                    string line     = lines[i] + "\n";
                    for (int j = 0; j < line.Length; j++)
                    {
                        // Note: we check statIndex<stats.length here in case of truncated CSVs
                        if (statIndex < stats.Length && stats[statIndex] != null)
                        {
                            // Read the stat
                            float value = 0.0f;

                            // Skip whitespace
                            while (line[j] == ' ')
                            {
                                j++;
                            }

                            bool negative = false;
                            if (line[j] == '-')
                            {
                                negative = true;
                                j++;
                            }

                            // Read the nonfractional part of the number
                            int num = 0;
                            while (line[j] >= '0' && line[j] <= '9')
                            {
                                num *= 10;
                                num += line[j] - '0';
                                j++;
                            }
                            value = (float)num;

                            if (line[j] == '.')
                            {
                                // read fractional part
                                num = 0;
                                j++;
                                float multiplier = 0.1f;
                                while (line[j] >= '0' && line[j] <= '9')
                                {
                                    value += (float)(line[j] - '0') * multiplier;
                                    j++;
                                    multiplier *= 0.1f;
                                }
                            }

                            if (negative)
                            {
                                value = -value;
                            }

                            stats[statIndex].samples[sampleIndex] = value;

                            // Skip everything else until the next newline or comma
                            while (line[j] != ',' && line[j] != '\n')
                            {
                                j++;
                            }
                        }
                        else
                        {
                            // Skip parsing
                            int startJ = j;
                            while (line[j] != ',' && line[j] != '\n')
                            {
                                j++;
                            }
                            if (statIndex == eventHeadingIndex)
                            {
                                eventStrings[sampleIndex] = line.Substring(startJ, j - startJ);
                            }
                        }
                        statIndex++;
                    }
                }
                             ); // Needed by parallel for

                // Read events
                for (int i = 0; i < eventStrings.Length; i++)
                {
                    string eventString = eventStrings[i];
                    if (!string.IsNullOrEmpty(eventString))
                    {
                        string[] Events = eventString.Split(';');
                        foreach (string EventString in Events)
                        {
                            if (EventString.Length > 0)
                            {
                                CsvEvent ev = new CsvEvent();
                                ev.Frame = i;
                                ev.Name  = EventString;
                                FilteredEvents.Add(ev);
                            }
                        }
                    }
                }
            }

            // Make sure the stat ordering matches the order they're passed in
            CsvStats csvStats = new CsvStats();

            if (statNamesLowercase != null)
            {
                CsvStats unorderedCsvStats = new CsvStats();
                foreach (StatSamples statSamples in stats)
                {
                    if (statSamples != null)
                    {
                        // Combine stats if we find a duplicate
                        if (unorderedCsvStats.Stats.ContainsKey(statSamples.Name.ToLower()))
                        {
                            StatSamples existingStat = unorderedCsvStats.GetStat(statSamples.Name);
                            for (int i = 0; i < statSamples.samples.Count; i++)
                            {
                                existingStat.samples[i] += statSamples.samples[i];
                            }
                        }
                        else
                        {
                            unorderedCsvStats.AddStat(statSamples);
                        }
                    }
                }

                foreach (string statName in statNamesLowercase)
                {
                    StatSamples stat = unorderedCsvStats.GetStat(statName);
                    if (stat != null)
                    {
                        csvStats.AddStat(stat);
                    }
                }
            }
            else
            {
                int c = 0;
                foreach (StatSamples statSamples in stats)
                {
                    c++;
                    if (statSamples != null)
                    {
                        if (csvStats.Stats.ContainsKey(statSamples.Name.ToLower()))
                        {
                            // Combine stats if we find a duplicate
                            StatSamples existingStat = csvStats.GetStat(statSamples.Name);
                            for (int i = 0; i < statSamples.samples.Count; i++)
                            {
                                existingStat.samples[i] += statSamples.samples[i];
                            }
                        }
                        else
                        {
                            csvStats.AddStat(statSamples);
                        }
                    }
                }
            }

            // Compute averages
            foreach (StatSamples stat in csvStats.Stats.Values.ToArray())
            {
                stat.ComputeAverageAndTotal();
            }

            csvStats.metaData = metaData;

            csvStats.Events = FilteredEvents;
            return(csvStats);
        }