public bool[] StringToTimeSpan(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _timeSpanArray, xarray.Count); Allocator.AllocateToSize(ref _couldNotConvertArray, xarray.Count); bool anyCouldNotConvert = false; string[] sourceArray = (string[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { string value = sourceArray[xarray.Index(i)]; bool couldNotConvert = !TryParseTimeSpanFriendly(value, out _timeSpanArray[i]); if (couldNotConvert) { couldNotConvert = String.IsNullOrEmpty(value) || !TimeSpan.TryParse(value, out _timeSpanArray[i]); } if (couldNotConvert) { _timeSpanArray[i] = _defaultValue; } _couldNotConvertArray[i] = couldNotConvert; anyCouldNotConvert |= couldNotConvert; } result = _timeSpanArray; return(anyCouldNotConvert ? _couldNotConvertArray : null); }
private XArray ReadIndices(ArraySelector selector) { Allocator.AllocateToSize(ref _resultArray, selector.Count); // Read all string positions XArray positions = _positionsReader.Read(ArraySelector.All(_positionsReader.Count)); int[] positionArray = (int[])positions.Array; // Read all raw string bytes XArray bytes = _bytesReader.Read(ArraySelector.All(_bytesReader.Count)); byte[] textArray = (byte[])bytes.Array; // Update the String8 array to point to them for (int i = 0; i < selector.Count; ++i) { int rowIndex = selector.Index(i); int valueStart = (rowIndex == 0 ? 0 : positionArray[rowIndex - 1]); int valueEnd = positionArray[rowIndex]; _resultArray[i] = new String8(textArray, valueStart, valueEnd - valueStart); } // Cache the xarray and return it _currentArray = XArray.All(_resultArray, selector.Count); _currentSelector = selector; return(_currentArray); }
public bool[] Convert(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _string8Array, xarray.Count); Allocator.AllocateToSize(ref _buffer, xarray.Count * _bytesPerItem); int bufferBytesUsed = 0; T[] sourceArray = (T[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { int index = xarray.Index(i); if (xarray.HasNulls && xarray.NullRows[index]) { // Always turn nulls into the default value rather than converting default of other type _string8Array[i] = _defaultValue; } else { String8 converted = _converter(sourceArray[index], _buffer, bufferBytesUsed); _string8Array[i] = converted; bufferBytesUsed += converted.Length; } } result = _string8Array; return(null); }
public BitVector TryGetValues(XArray keys, out ArraySelector rightSideSelector) { Allocator.AllocateToSize(ref _returnedVector, keys.Count); Allocator.AllocateToSize(ref _returnedIndicesBuffer, keys.Count); _returnedVector.None(); int countFound = 0; T[] keyArray = (T[])keys.Array; for (int i = 0; i < keys.Count; ++i) { int index = keys.Index(i); int foundAtIndex; if ((keys.HasNulls && keys.NullRows[index]) || !_dictionary.TryGetValue(keyArray[index], out foundAtIndex)) { _returnedVector.Clear(i); } else { _returnedVector.Set(i); _returnedIndicesBuffer[countFound++] = foundAtIndex; } } // Write out the indices of the joined rows for each value found rightSideSelector = ArraySelector.Map(_returnedIndicesBuffer, countFound); // Return the vector of which input rows matched return(_returnedVector); }
public bool[] Convert(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _array, (xarray.Selector.IsSingleValue ? 1 : xarray.Count)); Allocator.AllocateToSize(ref _couldNotConvertArray, (xarray.Selector.IsSingleValue ? 1 : xarray.Count)); bool anyCouldNotConvert = false; String8[] sourceArray = (String8[])xarray.Array; if (!xarray.Selector.IsSingleValue) { for (int i = 0; i < xarray.Count; ++i) { _couldNotConvertArray[i] = !_tryConvert(sourceArray[xarray.Index(i)], out _array[i]); if (_couldNotConvertArray[i]) { _array[i] = _defaultValue; anyCouldNotConvert = true; } } } else { _couldNotConvertArray[0] = !_tryConvert(sourceArray[xarray.Index(0)], out _array[0]); if (_couldNotConvertArray[0]) { _array[0] = _defaultValue; anyCouldNotConvert = true; } } result = _array; return(anyCouldNotConvert ? _couldNotConvertArray : null); }
public SingleValueColumn(IXTable source, string columnName, Type type) { _source = source; Allocator.AllocateToSize(ref _array, 1, type); ColumnDetails = new ColumnDetails(columnName, type); }
public XArray Read(ArraySelector selector) { if (selector.Indices != null) { throw new NotImplementedException(); } // Return the previous xarray if re-requested if (selector.Equals(_currentSelector)) { return(_currentArray); } // Allocate the result array Allocator.AllocateToSize(ref _array, selector.Count); // Read items in pages of 64k int byteStart = _bytesPerItem * selector.StartIndexInclusive; int byteEnd = _bytesPerItem * selector.EndIndexExclusive; int bytesRead = 0; for (int currentByteIndex = byteStart; currentByteIndex < byteEnd; currentByteIndex += ReadPageSize) { int currentByteEnd = Math.Min(byteEnd, currentByteIndex + ReadPageSize); XArray bytexarray = _byteReader.Read(ArraySelector.All(int.MaxValue).Slice(currentByteIndex, currentByteEnd)); Buffer.BlockCopy(bytexarray.Array, 0, _array, bytesRead, bytexarray.Count); bytesRead += currentByteEnd - currentByteIndex; } // Cache and return the current xarray _currentArray = XArray.All(_array, selector.Count); _currentSelector = selector; return(_currentArray); }
// Return an XArray with two empty array elements before and after the valid portion and indices pointing to the valid portion public static XArray Pad(XArray values) { Array modifiedArray = null; bool[] nulls = null; Allocator.AllocateToSize(ref modifiedArray, values.Array.Length + 4, values.Array.GetType().GetElementType()); if (values.HasNulls) { nulls = new bool[values.Array.Length + 4]; } int[] indices = new int[modifiedArray.Length]; // Copy values shifted over two (so, two default values at the beginning and two at the end) for (int i = 0; i < values.Array.Length; ++i) { indices[i] = i + 2; modifiedArray.SetValue(values.Array.GetValue(values.Index(i)), indices[i]); if (values.HasNulls) { nulls.SetValue(values.NullRows.GetValue(values.Index(i)), indices[i]); } } // Return an XArray with the padded array with the indices and shorter real length int[] remapArray = null; return(XArray.All(modifiedArray, values.Count, nulls).Select(ArraySelector.Map(indices, values.Count), ref remapArray)); }
public XArray Remap(XArray source, ref int[] remapArray) { // See if we have the remapping cached already ArraySelector cachedMapping; if (_cachedRemappings.TryGetValue(source.Selector, out cachedMapping)) { return(source.Reselect(cachedMapping)); } // Convert the BitVector to indices if we haven't yet (deferred to first column wanting values) if (!_indicesFound) { _indicesFound = true; Allocator.AllocateToSize(ref _indices, _count); int countFound = _vector.Page(_indices, ref _nextVectorIndex, _count); if (countFound != _count) { throw new InvalidOperationException($"RowRemapper found {countFound:n0} rows when {_count:n0} expected paging in Vector with {_vector.Count:n0} total matches up to index {_nextVectorIndex:n0}."); } } // Remap the outer selector XArray remapped = source.Select(ArraySelector.Map(_indices, _count), ref remapArray); // Cache the remapping _cachedRemappings[source.Selector] = remapped.Selector; return(remapped); }
/// <summary> /// Convert an XArray which uses indices into a contiguous, zero-based array. /// This allows using (fast) Reselect once converted. /// </summary> /// <param name="array">Buffer to use to write contiguous values</param> /// <param name="nulls">Buffer to use for new null array</param> /// <returns>XArray of values written contiguously</returns> public XArray ToContiguous <T>(ref T[] array, ref bool[] nulls) { // If this XArray isn't shifted or indirected, we can use it as-is if (this.Selector.Indices == null && this.Selector.StartIndexInclusive == 0) { return(this); } T[] thisArray = (T[])this.Array; Allocator.AllocateToSize(ref array, this.Count); if (!this.HasNulls) { for (int i = 0; i < this.Count; ++i) { int index = this.Index(i); array[i] = thisArray[index]; } return(XArray.All(array, this.Count)); } else { Allocator.AllocateToSize(ref nulls, this.Count); for (int i = 0; i < this.Count; ++i) { int index = this.Index(i); array[i] = thisArray[index]; nulls[i] = this.NullRows[index]; } return(XArray.All(array, this.Count, nulls)); } }
/// <summary> /// Remap the IsNull array from the source XArray, if any, to a non-indexed array. /// Used when the values in the XAray were converted into an in-order array but IsNull /// from the source needs to be preserved. /// </summary> /// <param name="array">XArray to remap nulls from</param> /// <param name="remapArray">bool[] to use to remap Nulls values, if needed</param> /// <returns>Nulls array to use in returned XArray</returns> public static bool[] RemapNulls(XArray array, ref bool[] remapArray) { // If there were no source nulls, there are none for the output if (!array.HasNulls) { return(null); } // If the source isn't indexed or shifted, the Nulls array may be reused if (array.Selector.Indices == null && array.Selector.StartIndexInclusive == 0) { return(array.NullRows); } // Otherwise, we must remap nulls Allocator.AllocateToSize(ref remapArray, array.Count); bool areAnyNulls = false; for (int i = 0; i < array.Count; ++i) { areAnyNulls |= (remapArray[i] = array.NullRows[array.Index(i)]); } return(areAnyNulls ? remapArray : null); }
private XArray Convert(XArray xarray1, XArray xarray2) { int count = xarray1.Count; if (count != xarray2.Count) { throw new InvalidOperationException("SimpleTwoArgumentFunction must get the same number of rows from each argument."); } // Allocate for results Allocator.AllocateToSize(ref _buffer, count); Allocator.AllocateToSize(ref _isNull, count); // Convert each non-null value bool areAnyNull = false; T[] array1 = (T[])xarray1.Array; U[] array2 = (U[])xarray2.Array; for (int i = 0; i < count; ++i) { int index1 = xarray1.Index(i); int index2 = xarray2.Index(i); bool rowIsNull = (xarray1.HasNulls && xarray1.NullRows[index1]) || (xarray2.HasNulls && xarray2.NullRows[index2]); areAnyNull |= rowIsNull; _isNull[i] = rowIsNull; _buffer[i] = (rowIsNull ? default(V) : _function(array1[index1], array2[index2])); } return(XArray.All(_buffer, count, (areAnyNull ? _isNull : null))); }
public ConcatenatingColumn(IXTable table, ColumnDetails columnDetails) { _table = table; _sources = new List <IXColumn>(); ColumnDetails = columnDetails; Allocator.AllocateToSize(ref _nullArray, 1, columnDetails.Type); }
// Return an IsSingleElement array with just the first value of the xarray, but the same count public static XArray First(XArray values) { Array modifiedArray = null; Allocator.AllocateToSize(ref modifiedArray, 1, values.Array.GetType().GetElementType()); modifiedArray.SetValue(values.Array.GetValue(values.Index(0)), 0); return(XArray.Single(modifiedArray, values.Count)); }
public void Append(XArray xarray) { // Write the values (without the null markers; we're writing those here) _valueWriter.Append(xarray.WithoutNulls()); // Track the row count written so we know how many null=false values to write when we first see a null _rowCountWritten += xarray.Count; // If there are no nulls in this set and none previously, no null markers need to be written if (!xarray.HasNulls && _nullWriter == null) { return; } if (_nullWriter == null) { // Check whether any rows in the set are actually null; the source may contain nulls but the filtered rows might not bool areAnyNulls = false; for (int i = 0; i < xarray.Count && !areAnyNulls; ++i) { areAnyNulls |= xarray.NullRows[xarray.Index(i)]; } // If there are not actually any null rows in this set, don't write null output yet if (!areAnyNulls) { return; } // Open a new file to write IsNull booleans string nullsPath = Path.Combine(_columnPath, "Vn.b8.bin"); _nullWriter = new PrimitiveArrayWriter <bool>(_streamProvider.OpenWrite(nullsPath)); // Write false for every value so far int previousCount = _rowCountWritten - xarray.Count; Allocator.AllocateToSize(ref _falseArray, 1024); for (int i = 0; i < previousCount; i += 1024) { int rowCount = Math.Min(1024, previousCount - i); _nullWriter.Append(XArray.All(_falseArray, rowCount)); } } if (!xarray.HasNulls) { // If this xarray doesn't have any nulls, write false for every value in this page Allocator.AllocateToSize(ref _falseArray, xarray.Count); _nullWriter.Append(XArray.All(_falseArray, xarray.Count)); } else { // Write the actual true/false values for this page _nullWriter.Append(XArray.All(xarray.NullRows).Reselect(xarray.Selector)); } }
private void AddInt(XArray rowIndices, XArray sumValues, int newDistinctCount) { long[] sumArray = (long[])sumValues.Array; int[] indicesArray = (int[])rowIndices.Array; if (sumValues.HasNulls) { // Nulls: Find nulls and mark those buckets null Allocator.AllocateToSize(ref _isNullPerBucket, newDistinctCount); for (int i = 0; i < rowIndices.Count; ++i) { int bucketIndex = rowIndices.Index(i); int sumIndex = sumValues.Index(i); if (sumValues.NullRows[sumIndex]) { _isNullPerBucket[indicesArray[bucketIndex]] = true; } _sumPerBucket[indicesArray[bucketIndex]] += sumArray[sumIndex]; } } else if (rowIndices.Selector.Indices != null || sumValues.Selector.Indices != null) { // Indices: Look up raw indices on both sides for (int i = 0; i < rowIndices.Count; ++i) { _sumPerBucket[indicesArray[rowIndices.Index(i)]] += sumArray[sumValues.Index(i)]; } } else if (sumValues.Selector.IsSingleValue == false) { // Non-Indexed: Loop over arrays directly int indicesOffset = rowIndices.Selector.StartIndexInclusive; int sumOffset = sumValues.Selector.StartIndexInclusive; for (int i = 0; i < rowIndices.Count; ++i) { _sumPerBucket[indicesArray[i + indicesOffset]] += sumArray[i + sumOffset]; } } else if (rowIndices.Selector.IsSingleValue == false) { // Single Sum Value: Add constant to each bucket long sumValue = sumArray[sumValues.Index(0)]; for (int i = rowIndices.Selector.StartIndexInclusive; i < rowIndices.Selector.EndIndexExclusive; ++i) { _sumPerBucket[indicesArray[i]] += sumValue; } } else { // Single Bucket, Single Sum: Add (Value * RowCount) to target bucket _sumPerBucket[indicesArray[rowIndices.Index(0)]] += sumValues.Count * sumArray[sumValues.Index(0)]; } }
public void Evaluate(BitVector vector) { Allocator.AllocateToSize(ref _termVector, vector.Capacity); foreach (IExpression term in _terms) { _termVector.None(); term.Evaluate(_termVector); vector.Or(_termVector); } }
public bool[] LongToTimeSpan(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _timeSpanArray, xarray.Count); long[] sourceArray = (long[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { _timeSpanArray[i] = TimeSpan.FromTicks(sourceArray[xarray.Index(i)]); } result = _timeSpanArray; return(null); }
public ConstantColumn(IXTable source, object value, Type type, bool wasUnwrappedLiteral = false) { Source = source; Allocator.AllocateToSize(ref _array, 1, type); _array.SetValue(value, 0); IsNull = (value == null || value.Equals("null")); _xArray = (IsNull ? XArray.Null(_array, 1) : XArray.Single(_array, 1)); WasUnwrappedLiteral = wasUnwrappedLiteral; ColumnDetails = new ColumnDetails(string.Empty, type); }
public bool[] DateTimeToLong(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _longArray, xarray.Count); DateTime[] sourceArray = (DateTime[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { _longArray[i] = sourceArray[xarray.Index(i)].ToUniversalTime().Ticks; } result = _longArray; return(null); }
public bool[] LongToDateTime(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _dateTimeArray, xarray.Count); long[] sourceArray = (long[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { _dateTimeArray[i] = new DateTime(sourceArray[xarray.Index(i)], DateTimeKind.Utc); } result = _dateTimeArray; return(null); }
public bool[] TimeSpanToLong(XArray xarray, out Array result) { Allocator.AllocateToSize(ref _longArray, xarray.Count); TimeSpan[] sourceArray = (TimeSpan[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { _longArray[i] = sourceArray[xarray.Index(i)].Ticks; } result = _longArray; return(null); }
public XArray Concatenate(IList <XArray> columns) { _block.Clear(); int count = columns.First().Count; Allocator.AllocateToSize(ref _buffer, count); Allocator.AllocateToSize(ref _isNull, count); bool couldBeNulls = columns.Any((col) => col.HasNulls); bool areAnyNulls = false; String8[][] arrays = columns.Select((xarray) => (String8[])xarray.Array).ToArray(); if (!couldBeNulls) { for (int i = 0; i < count; ++i) { String8 result = String8.Empty; for (int columnIndex = 0; columnIndex < columns.Count; ++columnIndex) { int rowIndex = columns[columnIndex].Index(i); result = _block.Concatenate(result, String8.Empty, arrays[columnIndex][rowIndex]); } _buffer[i] = result; } } else { for (int i = 0; i < count; ++i) { String8 result = String8.Empty; bool isNull = false; for (int columnIndex = 0; columnIndex < columns.Count; ++columnIndex) { int rowIndex = columns[columnIndex].Index(i); isNull |= columns[columnIndex].HasNulls && columns[columnIndex].NullRows[rowIndex]; result = _block.Concatenate(result, String8.Empty, arrays[columnIndex][rowIndex]); } _buffer[i] = result; _isNull[i] = isNull; areAnyNulls |= isNull; } } return(XArray.All(_buffer, count, (areAnyNulls ? _isNull : null))); }
public XArray Read(ArraySelector selector) { if (selector.Indices != null) { throw new NotImplementedException(); } Allocator.AllocateToSize(ref _array, selector.Count); _stream.Seek(selector.StartIndexInclusive, SeekOrigin.Begin); _stream.Read(_array, 0, selector.Count); return(XArray.All(_array, selector.Count)); }
public void Append(XArray xarray) { Allocator.AllocateToSize(ref _positionsBuffer, xarray.Count); String8[] array = (String8[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { String8 value = array[xarray.Index(i)]; value.WriteTo(_bytesWriter); _position += value.Length; _positionsBuffer[i] = _position; } _positionsWriter.Append(XArray.All(_positionsBuffer, xarray.Count)); }
// Return an XArray with nulls inserted for every other value public static XArray Nulls(XArray values) { Array modifiedArray = null; Allocator.AllocateToSize(ref modifiedArray, values.Array.Length * 2, values.Array.GetType().GetElementType()); bool[] nulls = new bool[modifiedArray.Length]; // Every other value is null for (int i = 0; i < modifiedArray.Length; ++i) { nulls[i] = (i % 2 == 0); modifiedArray.SetValue(values.Array.GetValue(values.Index(i / 2)), i); } // Return an XArray with the doubled length and alternating nulls return(XArray.All(modifiedArray, modifiedArray.Length, nulls)); }
public void Allocator_Basics() { int[] buffer = null; int[] previous; // Verify allocation happens on first call Allocator.AllocateToSize(ref buffer, 10); Assert.IsNotNull(buffer); Assert.AreEqual(10, buffer.Length); // Verify no re-allocation if size already fine previous = buffer; Allocator.AllocateToSize(ref buffer, 5); Assert.AreEqual(10, buffer.Length); Assert.ReferenceEquals(buffer, previous); previous = buffer; Allocator.AllocateToSize(ref buffer, 10); Assert.AreEqual(10, buffer.Length); Assert.ReferenceEquals(buffer, previous); // Verify generic allocator works Array generic = null; Allocator.AllocateToSize(ref generic, 10, typeof(int)); Assert.IsNotNull(generic as int[]); Assert.AreEqual(10, generic.Length); Array string8 = null; Allocator.AllocateToSize(ref string8, 1, typeof(String8)); Assert.IsNotNull(string8 as String8[]); Assert.AreEqual(1, string8.Length); // Verify generic object creators work object list; list = Allocator.ConstructGenericOf(typeof(List <>), typeof(int)); Assert.AreEqual(typeof(List <int>), list.GetType()); Assert.AreEqual(0, ((List <int>)list).Capacity); list = Allocator.ConstructGenericOf(typeof(List <>), typeof(int), 1000); Assert.AreEqual(typeof(List <int>), list.GetType()); Assert.AreEqual(1000, ((List <int>)list).Capacity); }
public void WhereBlock(XArray left, String8 rightValue, bool ignoreCase, CompareOperator cOp, BitVector vector) { if (rightValue.Length == 0) return; if (cOp != CompareOperator.Contains && cOp != CompareOperator.Equal && cOp != CompareOperator.StartsWith) throw new NotImplementedException(cOp.ToString()); String8[] leftArray = (String8[])left.Array; String8 last = leftArray[left.Selector.EndIndexExclusive - 1]; String8 all = new String8(last.Array, 0, last.Index + last.Length); Allocator.AllocateToSize(ref _indicesBuffer, 1024); int nextIndex = leftArray[left.Selector.StartIndexInclusive].Index; int nextRowIndex = left.Selector.StartIndexInclusive + 1; while (true) { // Find a batch of matches int countFound; if (s_IndexOfAllNative != null) { countFound = s_IndexOfAllNative(all.Array, nextIndex, all.Length - nextIndex, rightValue.Array, rightValue.Index, rightValue.Length, ignoreCase, _indicesBuffer); } else { countFound = all.IndexOfAll(rightValue, nextIndex, true, _indicesBuffer); } // Map the indices found to rows if (cOp == CompareOperator.Contains) { IndicesToContainsRows(leftArray, rightValue.Length, ref nextRowIndex, left.Selector.StartIndexInclusive, left.Selector.EndIndexExclusive, countFound, vector); } else if (cOp == CompareOperator.Equal) { IndicesToEqualsRows(leftArray, rightValue.Length, ref nextRowIndex, left.Selector.StartIndexInclusive, left.Selector.EndIndexExclusive, countFound, vector); } else // if(cOp == CompareOperator.StartsWith) { IndicesToStartsWithRows(leftArray, rightValue.Length, ref nextRowIndex, left.Selector.StartIndexInclusive, left.Selector.EndIndexExclusive, countFound, vector); } // Find the next index to search for matches from if (countFound < _indicesBuffer.Length) break; nextIndex = _indicesBuffer[countFound - 1] + 1; } }
public static bool TryConvertSingle(object value, Type targetType, out object result) { // Nulls are always converted to null if (value == null) { result = null; return(true); } // If the type is already right, just return it Type sourceType = value.GetType(); if (sourceType.Equals(targetType)) { result = value; return(true); } // Get the converter for the desired type combination Func <XArray, XArray> converter = GetConverter(sourceType, targetType); Array array = null; Allocator.AllocateToSize(ref array, 1, sourceType); array.SetValue(value, 0); XArray resultxarray = converter(XArray.Single(array, 1)); // Verify the result was not null unless the input was "" or 'null' if (resultxarray.HasNulls && resultxarray.NullRows[resultxarray.Index(0)]) { result = null; string stringValue = value.ToString(); if (stringValue != "" || String.Compare(stringValue, "null", true) == 0) { return(true); } return(false); } result = resultxarray.Array.GetValue(resultxarray.Index(0)); return(true); }
public XArray Read(ArraySelector selector) { if (selector.Indices != null) { return(ReadIndices(selector)); } if (selector.Count == 0) { return(XArray.All(_resultArray, 0)); } // Return previous xarray if re-requested if (selector.Equals(_currentSelector)) { return(_currentArray); } Allocator.AllocateToSize(ref _resultArray, selector.Count); bool includesFirstString = (selector.StartIndexInclusive == 0); // Read the string positions and bytes ReadRaw(selector); // Update the String8 array to point to them byte[] textArray = (byte[])_currentRaw.Bytes.Array; int[] positionArray = (int[])_currentRaw.Positions.Array; int firstStringStart = (includesFirstString ? 0 : positionArray[_currentRaw.Positions.Index(0)]); int positionOffset = _currentRaw.Positions.Index((includesFirstString ? 0 : 1)); int textOffset = firstStringStart - _currentRaw.Bytes.Index(0); int previousStringEnd = firstStringStart - textOffset; for (int i = 0; i < selector.Count; ++i) { int valueEnd = positionArray[i + positionOffset] - textOffset; _resultArray[i] = new String8(textArray, previousStringEnd, valueEnd - previousStringEnd); previousStringEnd = valueEnd; } // Cache the xarray and return it _currentArray = XArray.All(_resultArray, selector.Count); _currentSelector = selector; return(_currentArray); }