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)); }
/// <summary> /// Split a string into alphanumeric words without allocation by using /// the same PartialArray<int> 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)); }
/// <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<int> 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)); }
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]; } }
/// <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<int> 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)); }
/// <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<int> 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 Clear() { _identifiers.Clear(); }
public void Clear() { _ticksValues.Clear(); }
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]); }
/// <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); }