/// <summary>
        ///  Split a string into alphanumeric words without allocation by using
        ///  the same PartialArray&lt;int&gt; in a loop. When traversing over the
        ///  returned String8Set, check if each first letter IsAlphaNumeric to
        ///  decide whether to include it.
        /// </summary>
        /// <param name="value">String8 to split</param>
        /// <param name="buffer">PartialArray to contain split positions [allows reuse without allocations]</param>
        /// <returns>String8Set containing value split at alpha-to-non-alpha boundaries</returns>
        public static String8Set Split(String8 value, ref PartialArray <int> buffer)
        {
            // Reset the buffer for our use
            buffer.Clear();

            if (!value.IsEmpty())
            {
                // Add the beginning as a part
                buffer.Add(0);

                bool inWord = IsAlphaNumeric(value[0]);

                for (int i = 1; i < value.Length; ++i)
                {
                    bool charIsAlpha = IsAlphaNumeric(value[i]);

                    if (inWord != charIsAlpha)
                    {
                        // Add a boundary at each alpha to non-alpha transition
                        buffer.Add(i);
                        inWord = charIsAlpha;
                    }
                }

                // Add the remaining part of the string
                buffer.Add(value.Length);
            }

            return(new String8Set(value, 0, buffer));
        }
        private String8Set SplitRows(String8 block, PartialArray <int> rowPositionArray)
        {
            // Split the block into lines (and save the split for use splitting columns)
            _blockLines = block.Split(UTF8.Newline, _lineArray);

            // Reset where which line the next row begins with
            _nextLineIndex = 0;

            rowPositionArray.Clear();
            rowPositionArray.Add(0);

            for (int i = 0; i < _blockLines.Count - 1; ++i)
            {
                String8 line = _blockLines[i];

                // An empty line (or \n\r\n) indicates a new logical row
                if (line.Length == 0 || (line.Length == 1 && line[0] == UTF8.CR))
                {
                    rowPositionArray.Add(_lineArray[i + 1]);
                }
            }

            rowPositionArray.Add(block.Length + 1);

            return(new String8Set(block, 1, rowPositionArray));
        }
        public ImmutableStringStore ConvertToImmutable(out int[] identifierToSerializedIdentifier)
        {
            int valueCount = _values.Count;

            // Build an array of string indexes sorted by the strings
            int[] valueIndexesSorted = new int[valueCount];
            for (int i = 0; i < valueCount; ++i)
            {
                valueIndexesSorted[i] = i;
            }

            // Sort index array to point to values in sorted order
            // NOTE: Sort is case insensitive stable (case insensitive search and minimal diff size)
            Array.Sort(_values.ToArray(), valueIndexesSorted, s_comparer);

            // Build a map from the original identifiers (ascending order by when inserted) to the new ones (sorted order) to fix references
            int[] map = new int[valueCount];
            for (int i = 0; i < valueCount; ++i)
            {
                int oldIdentifier = valueIndexesSorted[i];
                int newIdentifier = i;
                map[oldIdentifier] = newIdentifier;
            }

            // Walk in sorted order and determine the byte position where each value will be written
            int totalLength = 0;

            PartialArray <int> positions = new PartialArray <int>(new int[valueIndexesSorted.Length + 1]);

            for (int i = 0; i < valueIndexesSorted.Length; ++i)
            {
                positions.Add(totalLength);

                // Compute space needed for this value and delimiter (\r\n delimited)
                totalLength += String8.GetLength(_values[valueIndexesSorted[i]]) + 2;
            }

            positions.Add(totalLength);

            // Build byte[] with the concatenated values
            byte[] sortedValueBytes = new byte[totalLength];

            int nextWritePosition = 0;

            for (int i = 0; i < valueIndexesSorted.Length; ++i)
            {
                // Copy value to output array
                String8 newValue = String8.Convert(_values[valueIndexesSorted[i]], sortedValueBytes, nextWritePosition);
                nextWritePosition += newValue.Length;

                sortedValueBytes[nextWritePosition]     = UTF8.CR;
                sortedValueBytes[nextWritePosition + 1] = UTF8.LF;
                nextWritePosition += 2;
            }

            // Build the ImmutableStore equivalent of this and an array to translate identifiers
            identifierToSerializedIdentifier = map;
            return(new ImmutableStringStore(new String8Set(new String8(sortedValueBytes, 0, sortedValueBytes.Length), 2, positions)));
        }
        /// <summary>
        ///  Split a string on a given delimiter only outside matching double quotes.
        ///  Used to split CSV content where the delimiters are ignored within quotes.
        /// </summary>
        /// <param name="value">String8 value to split</param>
        /// <param name="delimiter">Delimiter to split on</param>
        /// <param name="positions">PartialArray&lt;int&gt; to contain split positions</param>
        /// <returns>String8Set containing split value</returns>
        public static String8Set SplitOutsideQuotes(String8 value, byte delimiter, PartialArray <int> positions)
        {
            if (value.IsEmpty())
            {
                return(String8Set.Empty);
            }

            // Clear any previous values in the array
            positions.Clear();

            // The first part always begins at the start of the string
            positions.Add(0);

            byte[] array = value._buffer;
            int    i     = value._index;
            int    end   = i + value._length;

            // Walk the string. Find and mark delimiters outside of quotes only
            while (i < end)
            {
                // Outside Quotes
                for (; i < end; ++i)
                {
                    // If a quote is found, we're now inside quotes
                    if (array[i] == UTF8.Quote)
                    {
                        i++;
                        break;
                    }

                    // If a delimiter is found, add another split position
                    if (array[i] == delimiter)
                    {
                        positions.Add(i - value._index + 1);
                    }
                }

                // Inside Quotes
                for (; i < end; ++i)
                {
                    // If a quote was found, we're now outside quotes
                    if (array[i] == UTF8.Quote)
                    {
                        i++;
                        break;
                    }
                }
            }

            // The last part always ends at the end of the string
            positions.Add(value.Length + 1);

            return(new String8Set(value, 1, positions));
        }
Exemple #5
0
        public int Add(int parentIndex, int nameIdentifier)
        {
            if (parentIndex < -1 || parentIndex >= Count)
            {
                throw new ArgumentOutOfRangeException("parentIndex");
            }

            // If parent was not found from a search, parent is the root
            if (parentIndex == -1)
            {
                parentIndex = 0;
            }

            // Make a new node, pointing to the parent, with the parent's first child as a sibling
            int newNodeIndex = Count;

            _parentIndex.Add(parentIndex);
            _firstChildIndex.Add(-1);
            _nextSiblingIndex.Add(_firstChildIndex[parentIndex]);
            _nameIdentifier.Add(nameIdentifier);

            // Make the parent's first child now the new node
            _firstChildIndex[parentIndex] = newNodeIndex;

            return(newNodeIndex);
        }
        public void PartialArray_MembersProvided()
        {
            // Build a partially filled array
            int[] array = new int[10];
            for (int i = 0; i < 5; ++i)
            {
                array[i] = i;
            }

            // Build a PartialArray around it
            PartialArray <int> a = new PartialArray <int>(array, 5, true);

            // Verify it knows it's partially full already
            Assert.AreEqual(5, a.Count);
            Assert.AreEqual(3, a[3]);
            Assert.AreEqual(10, a.Capacity);
            Assert.IsTrue(a.IsStaticSize);
            Assert.IsFalse(a.IsFull);

            // Add another value; verify it goes to the right place
            a.Add(5);
            Assert.AreEqual(6, a.Count);
            Assert.AreEqual(5, a[5]);
            Assert.AreEqual(5, array[5]);
        }
Exemple #7
0
        public ItemTree(int rootIdentifier)
        {
            _parentIndex      = new PartialArray <int>();
            _firstChildIndex  = new PartialArray <int>();
            _nextSiblingIndex = new PartialArray <int>();
            _nameIdentifier   = new PartialArray <int>();

            // Add sentinel root
            _parentIndex.Add(-1);
            _firstChildIndex.Add(-1);
            _nextSiblingIndex.Add(-1);
            _nameIdentifier.Add(rootIdentifier);
        }
        /// <summary>
        ///  Split a string on a given delimiter into a provided byte[]. Used
        ///  to split strings without allocation when a large byte[] is created
        ///  and reused for many strings.
        /// </summary>
        /// <param name="value">String8 value to split</param>
        /// <param name="delimiter">Delimiter to split on</param>
        /// <param name="positions">PartialArray&lt;int&gt; to contain split positions</param>
        /// <returns>String8Set containing split value</returns>
        public static String8Set Split(String8 value, byte delimiter, PartialArray <int> positions)
        {
            // Ensure the delimiter is single byte
            if (delimiter >= 128)
            {
                throw new ArgumentException(String.Format(Resources.UnableToSupportMultibyteCharacter, delimiter));
            }

            if (value.IsEmpty())
            {
                return(String8Set.Empty);
            }

            // Clear any previous values in the array
            positions.Clear();

            // Record each delimiter position
            positions.Add(0);

            // Get the String8 array directly and loop from index to (index + length)
            // 3x faster than String8[index].
            byte[] array = value._buffer;
            int    end   = value._index + value._length;

            for (int i = value._index; i < end; ++i)
            {
                if (array[i] == delimiter)
                {
                    // Next start position is after this delimiter
                    positions.Add(i - value._index + 1);
                }
            }

            positions.Add(value.Length + 1);

            return(new String8Set(value, 1, positions));
        }
Exemple #9
0
        private void Sort(int parentNodeIndex, IComparer <int> nodeIndexComparer, ref PartialArray <int> buffer)
        {
            int currentChild = _firstChildIndex[parentNodeIndex];

            // If no children, return
            if (currentChild <= 0)
            {
                return;
            }

            // Add all children of the current element to the buffer
            buffer.Clear();
            while (currentChild > 0)
            {
                buffer.Add(currentChild);
                currentChild = _nextSiblingIndex[currentChild];
            }

            // Sort the children by the compare function
            buffer.Sort(nodeIndexComparer);

            // Modify the FirstChild and NextSibling pointers to be in sorted order
            currentChild = buffer[0];
            _firstChildIndex[parentNodeIndex] = currentChild;

            for (int i = 1; i < buffer.Count; ++i)
            {
                int nextChild = buffer[i];
                _nextSiblingIndex[currentChild] = nextChild;
                currentChild = nextChild;
            }

            _nextSiblingIndex[currentChild] = -1;

            // Recurse on the children
            currentChild = _firstChildIndex[parentNodeIndex];
            while (currentChild > 0)
            {
                Sort(currentChild, nodeIndexComparer, ref buffer);
                currentChild = _nextSiblingIndex[currentChild];
            }
        }
        public void PartialArray_NonResizable()
        {
            // Create a fixed PartialArray
            PartialArray <int> a = new PartialArray <int>(10, true);

            // Verify it was allocated already
            Assert.AreEqual(10, a.Capacity);

            // Try to add too many values to it (checking IsFull)
            for (int i = 0; i < 100; ++i)
            {
                if (a.IsFull)
                {
                    break;
                }
                a.Add(i);
            }

            // Verify the first Capacity were added, and no resize happened
            Assert.AreEqual(10, a.Count);
            Assert.AreEqual(10, a.Capacity);
        }
        /// <summary>
        ///  Split a CSV row into cells. This method splits and unencodes quoted values together.
        ///  It changes the underlying buffer in the process.
        /// </summary>
        /// <param name="row">String8 containing a CSV row</param>
        /// <param name="positions">PartialArray&lt;int&gt; to contain split positions</param>
        /// <returns>String8Set containing unencoded cell values</returns>
        public static String8Set SplitAndDecodeCsvCells(String8 row, PartialArray <int> positions)
        {
            // If row is empty, return empty set
            if (row.IsEmpty())
            {
                return(String8Set.Empty);
            }

            // Clear any previous values in the array
            positions.Clear();

            // The first part always begins at the start of the (shifted) string
            positions.Add(0);

            byte[] array = row._buffer;
            int    i     = row._index;
            int    end   = i + row._length;

            // We're shifting values in the string to overwrite quotes around cells
            // and doubled quotes. copyTo is where we've written to in the unescaped
            // string.
            int copyTo = i;

            // Walk each cell, handling quoted and unquoted cells.
            while (i < end)
            {
                bool inQuote = (array[i] == UTF8.Quote);

                if (!inQuote)
                {
                    // Unquoted cell. Copy until next comma.
                    for (; i < end; ++i, ++copyTo)
                    {
                        // Copy everything as-is (no unescaping)
                        array[copyTo] = array[i];

                        // If a delimiter is found, add another split position
                        if (array[i] == UTF8.Comma)
                        {
                            positions.Add(copyTo - row._index + 1);
                            i++; copyTo++;
                            break;
                        }
                    }
                }
                else
                {
                    // Quoted cell.

                    // Overwrite opening quote
                    i++;

                    // Look for end quote (undoubled quote)
                    for (; i < end; ++i, ++copyTo)
                    {
                        if (array[i] != UTF8.Quote)
                        {
                            // Copy everything that wasn't an escaped quote
                            array[copyTo] = array[i];
                        }
                        else
                        {
                            // Quote found. End of cell, escaped quote, or unescaped quote (error)?
                            i++;

                            // End of cell [end of line]
                            if (i == end)
                            {
                                break;
                            }

                            if (array[i] == UTF8.Comma)
                            {
                                // End of cell [comma]. Copy comma, end of cell.
                                positions.Add(copyTo - row._index + 1);
                                array[copyTo] = array[i];
                                i++; copyTo++;
                                break;
                            }
                            else if (array[i] == UTF8.Quote)
                            {
                                // Escaped quote. Copy the second quote, continue cell.
                                array[copyTo] = array[i];
                            }
                            else
                            {
                                // Unescaped quote. Abort; caller will see incomplete row and can throw
                                return(new String8Set(row, 1, positions));
                            }
                        }
                    }
                }
            }

            // The last part always ends at the end of the (shifted) string
            positions.Add(copyTo - row._index + 1);

            // Overwrite duplicate values left from shifting to make bugs clearer
            for (; copyTo < end; ++copyTo)
            {
                array[copyTo] = UTF8.Null;
            }

            return(new String8Set(row, 1, positions));
        }
 public void Add()
 {
     _identifiers.Add();
 }
 /// <summary>
 ///  Add a link from one item to another. Links must be added to items in
 ///  order of insertion.
 /// </summary>
 /// <param name="groupIndex">Index of item from which to link</param>
 /// <param name="memberIndex">Index of item to which to link</param>
 public void AddLink(int groupIndex, int memberIndex)
 {
     _groupIndices.Add(groupIndex);
     _memberIndices.Add(memberIndex);
 }
 public void Add()
 {
     _ticksValues.Add();
 }
        public void PartialArray_Basics()
        {
            // Default Constructor - no fixed size
            PartialArray <int> a = new PartialArray <int>();

            // Verify empty to start
            Assert.AreEqual(0, a.Count);
            Assert.AreEqual(0, a.Capacity);
            Assert.IsFalse(a.IsStaticSize);
            Assert.IsFalse(a.IsFull);

            // Verify Add doesn't throw
            for (int i = 0; i < 100; ++i)
            {
                a.Add(i);
            }

            // Verify count and capacity are right, IsFull is still false
            Assert.AreEqual(100, a.Count);
            Assert.IsTrue(a.Capacity >= 100);
            Assert.IsFalse(a.IsFull);

            // Verify we can get values back
            for (int i = 0; i < 100; ++i)
            {
                Assert.AreEqual(i, a[i]);
            }

            // Verify changing a value works
            a[0] = 50;
            Assert.AreEqual(50, a[0]);
            a[0] = 0;

            // Verify round trip works [Primitives only]
            PartialArray <int> readArray = new PartialArray <int>();

            Verify.RoundTrip <PartialArray <int> >(a, readArray);
            a = readArray;

            // Verify count and capacity are right, IsFull is still false
            Assert.AreEqual(100, a.Count);
            Assert.IsTrue(a.Capacity >= 100);
            Assert.IsFalse(a.IsFull);

            // Verify we can get values back
            for (int i = 0; i < 100; ++i)
            {
                Assert.AreEqual(i, a[i]);
            }

            // Verify clear works
            a.Clear();
            Assert.AreEqual(0, a.Count);
            Assert.IsTrue(a.Capacity >= 100);
            Assert.IsFalse(a.IsFull);

            // Verify Add after Clear works
            a.Add(10);
            Assert.AreEqual(1, a.Count);
            Assert.AreEqual(10, a[0]);
        }
Exemple #16
0
        /// <summary>
        ///  Search the given IMemberDatabase for matches to this query and put
        ///  results into the results array provided. The capacity of the results
        ///  array determines how many results are returned.
        /// </summary>
        /// <param name="db">Database to search</param>
        /// <param name="results">PartialArray to contain results, sized for the count desired.</param>
        /// <returns>True if results were added, False otherwise</returns>
        public bool TryFindMembers(IMemberDatabase db, ref PartialArray <Symbol> results)
        {
            // Ensure strings must be found again so that benchmarks are realistic
            ForceReresolve();

            // Clear results from a previous query
            results.Clear();

            // If there was no query, return with no results
            if (String.IsNullOrEmpty(SymbolName))
            {
                return(false);
            }

            // Get required members from database
            StringStore strings         = db.StringStore;
            ItemTree    declaredMembers = db.DeclaredMembers;
            MemberIndex index           = db.Index;

            // Map strings to the local StringStore. Stop immediately if any values aren't found.
            if (!ResolveStringsTo(strings))
            {
                return(false);
            }

            // Cache whether this query needs details to match
            bool usesDetails = !this.Parameters8.IsEmpty() || this.Type != SymbolType.Any || this.Modifiers != SymbolModifier.None;

            int[] matches;
            int   matchesIndex, matchesCount;

            if (SplitSymbolName8.Count == 1)
            {
                // Find the set of symbols with names in range. If no symbols in index, return nothing
                if (!index.TryGetMatchesInRange(SymbolNameSuffixIdentifiers, out matches, out matchesIndex, out matchesCount))
                {
                    return(false);
                }

                // If there was just one name part searched for, all matches count
                for (int i = matchesIndex; i < matchesIndex + matchesCount; ++i)
                {
                    if ((usesDetails ? MatchesDetailed(declaredMembers, strings, db, matches[i]) : Matches(declaredMembers, strings, matches[i])))
                    {
                        results.Add(new Symbol(db, matches[i]));
                        if (results.IsFull)
                        {
                            return(true);
                        }
                    }
                }
            }
            else
            {
                // Find all entries with exactly the second-to-last name
                if (!index.TryGetMatchesInRange(SymbolNamePrefixIdentifiers[SymbolNamePrefixIdentifiers.Length - 1], out matches, out matchesIndex, out matchesCount))
                {
                    return(false);
                }

                for (int i = matchesIndex; i < matchesIndex + matchesCount; ++i)
                {
                    int currentMatchIndex = matches[i];

                    // First, do all previous name parts in the query match?
                    int currentAncestorIndex = currentMatchIndex;
                    int namePartIndex        = SymbolNamePrefixIdentifiers.Length - 2;
                    for (; namePartIndex >= 0; --namePartIndex)
                    {
                        currentAncestorIndex = declaredMembers.GetParent(currentAncestorIndex);
                        int currentAncestorNameIdentifier = declaredMembers.GetNameIdentifier(currentAncestorIndex);
                        if (!SymbolNamePrefixIdentifiers[namePartIndex].Contains(currentAncestorNameIdentifier))
                        {
                            break;
                        }
                    }

                    if (namePartIndex != -1)
                    {
                        continue;
                    }

                    // If this was a full match, are we out of namespaces?
                    if (IsFullNamespace)
                    {
                        currentAncestorIndex = declaredMembers.GetParent(currentAncestorIndex);
                        SymbolType symbolAboveFullNameType = db.GetMemberType(currentAncestorIndex);
                        if (!symbolAboveFullNameType.IsAboveNamespace())
                        {
                            return(false);
                        }
                    }

                    // Next, find children of this item which match the last part typed
                    int leafId = declaredMembers.GetFirstChild(currentMatchIndex);
                    while (leafId > 0)
                    {
                        if ((usesDetails ? MatchesDetailed(declaredMembers, strings, db, leafId) : Matches(declaredMembers, strings, leafId)))
                        {
                            results.Add(new Symbol(db, leafId));
                            if (results.IsFull)
                            {
                                return(true);
                            }
                        }

                        leafId = declaredMembers.GetNextSibling(leafId);
                    }
                }
            }

            return(results.Count > 0);
        }