private void ReadColumns(Stream stream) { // Make a reader to split the input on newlines BufferedRowReader reader = new BufferedRowReader(stream, (block, array) => block.Split(UTF8.Newline, array)); // Scan the lines for column names (something before a colon) while (true) { String8 line = reader.NextRow(); if (line.IsEmpty()) { break; } ReadColumnLine(line); } // Reset the stream for the second read stream.Seek(0, SeekOrigin.Begin); }
public static void WriteWordsForLength(string dictionaryPath, int length) { int countWritten = 0; using (WordCompressor compressor = WordCompressor.OpenRead(dictionaryPath)) { for (int wordIndex = 0; wordIndex < compressor.WordCount; ++wordIndex) { String8 word = compressor[wordIndex].Word; if (word.Length == length) { System.Console.WriteLine(word); if (++countWritten == 100) { return; } } } } }
private static void WriteValidSample(Stream stream, Func <Stream, ITabularWriter> buildWriter) { String8Block block = new String8Block(); String8 simple = block.GetCopy("Simple"); String8 commasAndQuotes = block.GetCopy("Value, but with \"quotes\" and commas"); using (ITabularWriter writer = buildWriter(stream)) { writer.SetColumns(new string[] { "LineNumber", "Count", "Description" }); for (int i = 2; i < 10; ++i) { writer.Write(i); writer.Write(simple); writer.Write(commasAndQuotes); writer.NextRow(); } } }
protected override void WriteCellValue(Stream stream, String8 value) { // Escaping: If value contains cell or row delimiter, just omit them // No standard for TSV escaping. int nextWriteStartIndex = 0; int end = value._index + value._length; for (int i = value._index; i < end; ++i) { byte c = value._buffer[i]; if (c == UTF8.Tab || c == UTF8.Newline) { int inStringIndex = i - value._index; value.Substring(nextWriteStartIndex, inStringIndex - nextWriteStartIndex).WriteTo(stream); nextWriteStartIndex = inStringIndex + 1; } } value.Substring(nextWriteStartIndex).WriteTo(stream); }
/// <summary> /// Move the reader to the next row. This must be called before /// reading the first row. /// </summary> /// <returns>True if another row exists, False if the TSV is out of content</returns> public String8 NextRow() { // If we're on the last row, ask for more (we don't read the last row in case it was only partially read into the buffer) if (_nextRowIndexInBlock >= _currentBlock.Count - 1) { NextBlock(); } // If there are no more rows, return false if (_nextRowIndexInBlock >= _currentBlock.Count) { return(String8.Empty); } // Get the next (complete) row from the current block _currentRow = _currentBlock[_nextRowIndexInBlock]; _nextRowIndexInBlock++; return(_currentRow); }
public void Writer_CheckValidation(Func <Stream, ITabularWriter> buildWriter) { using (MemoryStream s = new MemoryStream()) { using (ITabularWriter w = buildWriter(s)) { // Write before SetColumns Verify.Exception <InvalidOperationException>(() => w.Write(0)); w.SetColumns(new string[] { "ID", "IsEven" }); // SetColumns already called Verify.Exception <InvalidOperationException>(() => w.SetColumns(new string[] { "Three", "Four" })); w.Write(0); // Not enough columns Verify.Exception <InvalidOperationException>(() => w.NextRow()); w.Write(true); // Too many columns Verify.Exception <InvalidOperationException>(() => w.Write(String8.FromBoolean(false))); w.NextRow(); // WriteValuePart without WriteValueStart Verify.Exception <InvalidOperationException>(() => w.WriteValuePart(true)); // WriteValueEnd not in partial value Verify.Exception <InvalidOperationException>(() => w.WriteValueEnd()); w.WriteValueStart(); // Write in partial value Verify.Exception <InvalidOperationException>(() => w.Write(true)); w.WriteValueEnd(); } } }
public void Reader_Performance(string sampleFilePath, Func <string, bool, ITabularReader> buildReader) { long rowCountRead = 0; long xsvLengthBytes = new FileInfo(sampleFilePath).Length; // Goal: 100MB/sec [Surface Book i7] Verify.PerformanceByBytes(50 * LongExtensions.Megabyte, () => { int iterations = 100; for (int iteration = 0; iteration < iterations; ++iteration) { using (ITabularReader r = buildReader(sampleFilePath, true)) { int lineNumberIndex = r.ColumnIndex("LineNumber"); int countIndex = r.ColumnIndex("Count"); int descriptionIndex = r.ColumnIndex("Description"); while (r.NextRow()) { rowCountRead++; if (r.CurrentRowColumns < 2) { continue; } int lineNumber; r.Current(lineNumberIndex).TryToInteger(out lineNumber); int count; r.Current(countIndex).TryToInteger(out count); String8 description = r.Current(descriptionIndex).ToString8(); } } } return(iterations * xsvLengthBytes); }); }
public void Function_ToUpper() { String8Block block = new String8Block(); String8[] values = new String8[] { String8.Empty, block.GetCopy("Simple"), block.GetCopy("ALREADY"), block.GetCopy(" "), }; String8[] expected = new String8[] { String8.Empty, block.GetCopy("SIMPLE"), block.GetCopy("ALREADY"), block.GetCopy(" "), }; RunQueryAndVerify(values, "Name", expected, "Name", "set [Name] ToUpper([Name])"); }
public void Comparisons() { var a = "12345678"; var b = "12345679"; var sa = String8.FromString(a); var sb = String8.FromString(b); Assert.IsFalse(sa.Equals(null)); Assert.AreEqual(System.Math.Sign(sa.CompareTo(sb)), System.Math.Sign(String.Compare(a, b, StringComparison.Ordinal))); Assert.AreEqual(System.Math.Sign(String8.Compare(sa, sb)), System.Math.Sign(String.Compare(a, b, StringComparison.Ordinal))); Assert.AreEqual(sa == sb, a == b); Assert.AreEqual(String8.Equals(sa, sb), a == b); Assert.AreEqual(sa != sb, a != b); Assert.IsTrue(sa < sb); Assert.IsTrue(sa <= sb); Assert.IsFalse(sa > sb); Assert.IsFalse(sa >= sb); }
/// <summary> /// Compute a cryptographic hash of 'value' and 'hashKeyHash' together. /// Used to map values in the Sanitizer. /// </summary> /// <param name="value">Source value to hash</param> /// <param name="hashKeyHash">HashKey for this hash</param> /// <returns>uint of hash result</returns> public static uint Hash(String8 value, uint hashKeyHash) { if (s_hasher == null) { s_hasher = SHA256Managed.Create(); } if (s_buffer == null || s_buffer.Length < value.Length + 4) { s_buffer = new byte[value.Length + 4]; } s_buffer[0] = (byte)(hashKeyHash & 0xFF); s_buffer[1] = (byte)((hashKeyHash >> 8) & 0xFF); s_buffer[2] = (byte)((hashKeyHash >> 16) & 0xFF); s_buffer[3] = (byte)((hashKeyHash >> 24) & 0xFF); value.WriteTo(s_buffer, 4); byte[] hash = s_hasher.ComputeHash(s_buffer, 0, value.Length + 4); uint result = (uint)((hash[0] << 24) + (hash[1] << 16) + (hash[2] << 8) + hash[3]); return(result); }
public void MemberIndex_CaseSensitivity() { StringStore strings = new StringStore(); MemberIndex index = new MemberIndex(); byte[] buffer = new byte[20]; // Add strings to store (some differ only by casing), ten values string[] testValues = new string[] { "null", "bool", "Bool", "array", "ARRAY", "Collections", "Dictionary", "int", "Int", "friend" }; int[] testValueIDs = new int[testValues.Length]; for (int i = 0; i < testValues.Length; ++i) { testValueIDs[i] = strings.FindOrAddString(testValues[i]); } // Add 3 items per string to index [0, 10, 20 => "null", 1, 11, 21 => "bool", 2, 12, 22 => "Bool", ...] int indexId = 0; for (int countToIndex = 0; countToIndex < 3; ++countToIndex) { for (int wordIndex = 0; wordIndex < testValueIDs.Length; ++wordIndex) { index.AddItem(testValueIDs[wordIndex], indexId++); } } // Convert index for search. Pages should be merged into case-insensitive groups in insertion (ID) order strings.ConvertToImmutable(); index.ConvertToImmutable(strings); // Verify "BOOL" gets matches for "bool" and "Bool" in insertion order Assert.AreEqual("1, 2, 11, 12, 21, 22", MatchesForWordToString(index, strings, String8.Convert("BOOL", buffer))); // Verify "array" gets matches for "array" and "ARRAY" in insertion order Assert.AreEqual("3, 4, 13, 14, 23, 24", MatchesForWordToString(index, strings, String8.Convert("array", buffer))); // Verify "Dictionary" matches unmerged Assert.AreEqual("6, 16, 26", MatchesForWordToString(index, strings, String8.Convert("Dictionary", buffer))); }
/// <summary> /// Write UTF8 content escaped properly to be in double-quotes, but don't /// write the surrounding quotes. /// </summary> /// <param name="value">UTF8 value to write</param> private void WriteEscaped(String8 value) { int nextWriteStartIndex = 0; int end = value.Index + value.Length; for (int i = value.Index; i < end; ++i) { byte c = value.Array[i]; bool isControl = c < 32; if (isControl || c == UTF8.Backslash || c == UTF8.Quote) { int inStringIndex = i - value.Index; // Write everything before this escaped portion value.Substring(nextWriteStartIndex, inStringIndex - nextWriteStartIndex).WriteTo(_stream); // Write the escaped character if (isControl) { s_escapedCharPrefix.WriteTo(_stream); _stream.WriteByte(ToHexDigit(c / 16)); _stream.WriteByte(ToHexDigit(c & 0xF)); } else { _stream.WriteByte(UTF8.Backslash); _stream.WriteByte(c); } // Track the next block which doesn't need escaping nextWriteStartIndex = inStringIndex + 1; } } // Write the trailing unescaped block value.Substring(nextWriteStartIndex).WriteTo(_stream); }
public void Utf8ToUtf16_All() { // Validate that index conversions on "a*b" map correctly for every Unicode codepoint byte[] buffer = new byte[10]; String8.Convert("a", ref buffer); for (int codepoint = 0; codepoint < 0x10FFFF; ++codepoint) { // Skip illegal codepoints if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { continue; } // Convert the codepoint to UTF16 string value = Char.ConvertFromUtf32(codepoint); // Append it to the String8 after 'a' String8 value8 = String8.Convert(value, ref buffer, 1); // Append 'b' after that String8.Convert("b", ref buffer, value8.Index + value8.Length); // Map the whole value String8 whole8 = new String8(buffer, 0, value8.Index + value8.Length + 1); // 'a' should always map to index 0 Assert.Equal(0, String8.Utf8ToUtf16(0, whole8)); // 'b' should always map to the last .NET char (the length needed for the .NET string + 1 for 'a') Assert.Equal(1 + value.Length, String8.Utf8ToUtf16(whole8.Length - 1, whole8)); // All indices in between are the middle character (index 1, since it's after 'a') for (int i = 1; i < whole8.Length - 1; ++i) { Assert.Equal(1, String8.Utf8ToUtf16(i, whole8)); } } }
private static void ITabularValue_Basics(string value, String8 value8, ITabularValue itv) { Assert.AreEqual(String.IsNullOrEmpty(value), itv.IsNullOrEmpty()); Assert.AreEqual(value8, itv.ToString8()); Assert.AreEqual(value, itv.ToString()); bool asBoolean; if (bool.TryParse(value, out asBoolean)) { Assert.AreEqual(asBoolean, itv.ToBoolean()); } else { Verify.Exception <FormatException>(() => { var result = itv.ToBoolean(); }); } int asInteger; if (int.TryParse(value, out asInteger)) { Assert.AreEqual(asInteger, itv.ToInteger()); } else { Verify.Exception <FormatException>(() => { var result = itv.ToInteger(); }); } DateTime asDateTime; if (DateTime.TryParse(value, CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out asDateTime)) { Assert.AreEqual(asDateTime, itv.ToDateTime()); } else { Verify.Exception <FormatException>(() => { var result = itv.ToDateTime(); }); } }
public void SetLocation(int memberIndex, string filePath, ushort line, ushort charInLine) { // TODO: Handle '/' or '\' in ItemTree to avoid canonicalizing if (!String.IsNullOrEmpty(filePath) && (filePath.StartsWith("http:", StringComparison.OrdinalIgnoreCase) || filePath.StartsWith("https:", StringComparison.OrdinalIgnoreCase))) { filePath = filePath.Replace('/', '\\'); } // Find (or add) the file path to the File Tree int fileIndex = 0; if (!String.IsNullOrEmpty(filePath)) { String8 path8 = String8.Convert(filePath, new byte[String8.GetLength(filePath)]); String8Set splitPath8 = path8.Split('\\', new int[String8Set.GetLength(path8, '\\')]); fileIndex = this.FileTree.AddPath(0, splitPath8, this.StringStore); } // Write the updated location SymbolLocation location; location.FileIndex = fileIndex; location.Line = line; location.CharInLine = charInLine; if (this.DeclaredMemberLocations.Count == memberIndex) { this.DeclaredMemberLocations.Add(location); } else if (this.DeclaredMemberLocations.Count > memberIndex) { this.DeclaredMemberLocations[memberIndex] = location; } else { throw new InvalidOperationException(String.Format(Resources.DatabaseArraysOutOfSync, "DeclaredMemberLocations")); } }
private static void Search(string filePath, String8 term) { int iterations = 1; int matchCount = 0; string outputPath = Path.ChangeExtension(filePath, ".search.json"); BionSearcher searcher = null; try { using (new ConsoleWatch($"Loading {filePath}...")) { searcher = new BionSearcher(filePath, 3); } using (new ConsoleWatch($"Finding \"{term}\" [{iterations:n0}x]...", () => $"Done. Wrote {matchCount:n0} matches to {outputPath}")) { for (int iteration = 0; iteration < iterations; ++iteration) { ISearchResult result = searcher.Find(term); using (JsonTextWriter writer = new JsonTextWriter(new StreamWriter(outputPath))) { writer.Formatting = Formatting.Indented; writer.WriteStartArray(); matchCount = searcher.Write(writer, result, 0, -1); writer.WriteEndArray(); } } } } finally { searcher?.Dispose(); } }
internal static String8 ConvertMapiPtString8ToString8(object stringData, int codePage) { String8 @string = null; string text = stringData as string; if (text != null) { @string = new String8(text); } else { byte[] array = stringData as byte[]; if (array != null) { @string = new String8(new ArraySegment <byte>(array)); } } if (@string != null) { @string.ResolveString8Values(MarshalHelper.GetString8Encoding(codePage)); } return(@string); }
public Region2 DeserializeRegion2(BionReader reader) { Region2 result = new Region2(); Expect(reader, BionToken.StartObject, "Region2"); while (reader.TokenType == BionToken.PropertyName) { String8 propertyName = reader.CurrentString8(); if (!classFieldParsers.TryGetValue(propertyName, out Action <BionReader, Region2> parser)) { throw new NotImplementedException($"Unknown property Region.{propertyName}. Stopping."); } reader.Read(); parser(reader, result); reader.Read(); } Expect(reader, BionToken.EndObject, "Region2"); return(result); }
private static void LoadFullCode() { long totalCodeLength = 0; FileInfo[] codeFiles = new DirectoryInfo(@"..\..\..").GetFiles("*.cs", SearchOption.AllDirectories); foreach (FileInfo file in codeFiles) { totalCodeLength += file.Length; } int lengthRead = 0; byte[] allCode = new byte[totalCodeLength]; foreach (FileInfo file in codeFiles) { using (FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { lengthRead += stream.Read(allCode, lengthRead, (int)file.Length); } } s_allCode8 = new String8(allCode, 0, allCode.Length); }
private bool TryFindAssembly(string name, out Symbol assembly) { assembly = default(Symbol); String8 name8 = String8.Convert(name, new byte[String8.GetLength(name)]); Range nameRange; if (!this.Previous.StringStore.TryFindString(name8, out nameRange)) { return(false); } int matchIndex; if (!this.Previous.DeclaredMembers.TryFindChildByName(1, nameRange, out matchIndex)) { return(false); } assembly = new Symbol(this.Previous, matchIndex); return(true); }
public IXColumn Build(IXTable source, XDatabaseContext context) { // Create a String8Block to hold the uppercase copy of the values String8Block block = new String8Block(); return(SimpleTransformFunction <String8, String8> .Build( Name, source, context.Parser.NextColumn(source, context, typeof(String8)), (string8) => { // Make a copy, make it uppercase, and return it String8 upper = block.GetCopy(string8); upper.ToUpperInvariant(); return upper; }, () => { // Before each page, clear the String8Block to reuse the memory block.Clear(); } )); }
public void String8_BeforeFirstAfterFirst() { string binaryName = "System.Collections.Generic.List!"; String8 binaryName8 = binaryName.TestConvert(); Assert.AreEqual("System", binaryName8.BeforeFirst((byte)'.').ToString()); Assert.AreEqual("Collections.Generic.List!", binaryName8.AfterFirst((byte)'.').ToString()); Assert.AreEqual(binaryName8, binaryName8.BeforeFirst((byte)'|').ToString()); Assert.AreEqual(binaryName8, binaryName8.AfterFirst((byte)'|').ToString()); Assert.AreEqual(string.Empty, String8.Empty.BeforeFirst((byte)'.').ToString()); Assert.AreEqual(string.Empty, String8.Empty.AfterFirst((byte)'.').ToString()); Assert.AreEqual(string.Empty, String8.Empty.BeforeFirst((byte)'S').ToString()); Assert.AreEqual(string.Empty, String8.Empty.AfterFirst((byte)'!').ToString()); TrySplitOnFirst(String8.Empty, (byte)'.', "", ""); TrySplitOnFirst(binaryName8, (byte)'@', "", ""); TrySplitOnFirst(binaryName8, (byte)'.', "System", "Collections.Generic.List!"); TrySplitOnFirst(binaryName8, (byte)'!', "System.Collections.Generic.List", ""); TrySplitOnFirst(binaryName8, (byte)'S', "", "ystem.Collections.Generic.List!"); }
public void String8_StartsWithEndsWith() { string collections = "Collections"; String8 collections8 = collections.TestConvert(); string collectionsCasing = "coLLecTionS"; String8 collectionsCasing8 = collectionsCasing.TestConvert(); Assert.IsFalse(String8.Empty.StartsWith(UTF8.Space)); Assert.IsFalse(String8.Empty.EndsWith(UTF8.Space)); Assert.IsTrue(collections8.StartsWith((byte)'C')); Assert.IsFalse(collections8.StartsWith((byte)'c')); Assert.IsFalse(collections8.StartsWith(UTF8.Newline)); Assert.IsTrue(collections8.EndsWith((byte)'s')); Assert.IsFalse(collections8.EndsWith((byte)'S')); Assert.IsFalse(collections8.EndsWith(UTF8.Newline)); Assert.IsFalse(String8.Empty.StartsWith(collections8)); Assert.IsFalse(String8.Empty.EndsWith(collections8)); Assert.IsFalse(String8.Empty.StartsWith(collections8, true)); Assert.IsFalse(String8.Empty.EndsWith(collections8, true)); Assert.IsTrue(collections8.EndsWith(collections8)); Assert.IsTrue(collections8.EndsWith(collections8.Substring(1))); Assert.IsTrue(collections8.EndsWith(collections8.Substring(8))); Assert.IsFalse(collections8.EndsWith(collectionsCasing8)); Assert.IsTrue(collections8.EndsWith(collectionsCasing8, true)); Assert.IsTrue(collections8.StartsWith(collections8)); Assert.IsTrue(collections8.StartsWith(collections8.Substring(0, collections8.Length - 1))); Assert.IsTrue(collections8.StartsWith(collections8.Substring(0, 3))); Assert.IsFalse(collections8.StartsWith(collectionsCasing8)); Assert.IsTrue(collections8.StartsWith(collectionsCasing8, true)); }
public void Function_Truncate() { String8Block block = new String8Block(); String8[] values = new String8[] { String8.Empty, block.GetCopy(" "), block.GetCopy(@"XForm.Test\bin\x64\Release"), block.GetCopy(@"XForm.Test\bin"), block.GetCopy(@"XForm.Test"), }; String8[] expected = new String8[] { String8.Empty, block.GetCopy(" "), block.GetCopy(@"XForm.Test"), block.GetCopy(@"XForm.Test"), block.GetCopy(@"XForm.Test"), }; RunQueryAndVerify(values, "Name", expected, "Name", "set [Name] Truncate([Name], 10)"); }
protected override void WriteValuePart(Stream stream, String8 value) { // Look for quotes in string int nextWriteStartIndex = 0; int end = value._index + value._length; for (int i = value._index; i < end; ++i) { byte c = value._buffer[i]; if (c == UTF8.Quote) { // Write everything including quote int inStringIndex = i - value._index; value.Substring(nextWriteStartIndex, inStringIndex - nextWriteStartIndex).WriteTo(stream); nextWriteStartIndex = inStringIndex; // Write a second quote stream.WriteByte(UTF8.Quote); } } // Write content after the last quote seen value.Substring(nextWriteStartIndex).WriteTo(stream); }
public MutableSymbol FindOrAddPath(string path, char delimiter, SymbolType pathPartType) { String8 path8 = String8.Convert(path, new byte[String8.GetLength(path)]); String8Set splitPath8 = path8.Split(delimiter, new int[String8Set.GetLength(path8, delimiter)]); int currentIndex = _index; for (int i = 0; i < splitPath8.Count; ++i) { String8 part = splitPath8[i]; int partNameIdentifier = _database.StringStore.FindOrAddString(part); int foundNode; if (!_database.DeclaredMembers.TryFindChildByName(currentIndex, partNameIdentifier, out foundNode)) { foundNode = _database.DeclaredMembers.Add(currentIndex, partNameIdentifier); _database.DeclaredMemberDetails.Add(new SymbolDetails() { Type = pathPartType }); _database.DeclaredMemberLocations.Add(default(SymbolLocation)); } currentIndex = foundNode; } return new MutableSymbol(_database, currentIndex); }
private void WriteRunSubsetStart(JsonTextWriter writer) { int depth = _bionReader.Depth; // Read and copy everything in the run until the end object except the large collections while (_bionReader.Read() && _bionReader.Depth > depth) { if (_bionReader.TokenType == BionToken.PropertyName) { String8 propertyName = _bionReader.CurrentString8(); if (propertyName.Equals(Files) || propertyName.Equals(Results)) { _bionReader.Skip(); continue; } } JsonBionConverter.WriteToken(_bionReader, writer); } // Start a results array and leave open writer.WritePropertyName("results"); writer.WriteStartArray(); }
/// <summary> /// Return the first match for the given Regular Expression, index -1 if no matches. /// </summary> /// <param name="text">UTF8 text to search within.</param> /// <param name="expression">Regular Expression to match.</param> /// <param name="options">RegexOptions to use.</param> /// <param name="timeout">Timeout in ms.</param> /// <returns>First Match found in text; index will be -1 if no matches found.</returns> public static Match2 Match(String8 text, string expression, RegexOptions options = RegexOptions.None, Timeout timeout = default) { ParsedRegexCache cache = null; try { cache = CheckoutCache(); var matches = new Match2[1]; int expressionIndex = BuildRegex(cache, expression, options); int countFound = Matches(expressionIndex, text, 0, matches, timeout.RemainingMilliseconds); if (countFound == 0) { matches[0].Index = -1; matches[0].Length = -1; } return(matches[0]); } finally { CheckinCache(cache); } }
public String8 Sanitize(String8 value8) { this.Block.Clear(); StringBuilder result = new StringBuilder(); string value = value8.ToString(); int nextIndexToWrite = 0; foreach (Match m in this.Regex.Matches(value)) { // Replace the whole expression if no groups, otherwise the first parenthesized group Group g = m.Groups[0]; if (m.Groups.Count > 1) { g = m.Groups[1]; } // Write content before this match result.Append(value.Substring(nextIndexToWrite, g.Index - nextIndexToWrite)); // Convert and write the match String8 part = this.Inner.Sanitize(this.Block.GetCopy(g.Value)); result.Append(part.ToString()); // Set the next non-match we need to write nextIndexToWrite = g.Index + g.Length; } // Write anything after the last match if (nextIndexToWrite < value.Length) { result.Append(value.Substring(nextIndexToWrite)); } return(this.Block.GetCopy(result.ToString())); }
public void Function_Trim() { String8Block block = new String8Block(); String8[] values = new String8[] { String8.Empty, block.GetCopy("Simple"), block.GetCopy(" Simple Spaced "), block.GetCopy(" "), block.GetCopy("\t\t\t"), }; String8[] expected = new String8[] { String8.Empty, block.GetCopy("Simple"), block.GetCopy("Simple Spaced"), String8.Empty, String8.Empty }; RunQueryAndVerify(values, "Name", expected, "Name", "set [Name] Trim([Name])"); }