private void FindOrAdd(XArray[] keys, XArray indices) { if (keys.Length != _keys.Length) { throw new ArgumentOutOfRangeException("keys.Length"); } int rowCount = keys[0].Count; // Give the arrays to the keys columns SetCurrentArrays(keys); int[] indicesArray = (int[])indices.Array; for (uint rowIndex = 0; rowIndex < rowCount; ++rowIndex) { // Set values to insert as current SetCurrent(rowIndex, indicesArray[indices.Index((int)rowIndex)]); // Get the hash of the row uint hash = HashCurrent(); // Add the new item if (!this.Add(hash)) { Expand(); // Reset current to the xarray we're trying to add SetCurrentArrays(keys); SetCurrent(rowIndex, indicesArray[indices.Index((int)rowIndex)]); Add(hash); } } }
public void Add(XArray keys, int firstRowIndex) { T[] keyArray = (T[])keys.Array; if (_valueCopier != null || keys.HasNulls) { for (int i = 0; i < keys.Count; ++i) { int index = keys.Index(i); if (keys.HasNulls && keys.NullRows[index]) { continue; } T key = keyArray[index]; if (_valueCopier != null) { key = _valueCopier.Copy(key); } _dictionary[key] = firstRowIndex + i; } } else { for (int i = 0; i < keys.Count; ++i) { int index = keys.Index(i); T key = keyArray[index]; _dictionary[key] = firstRowIndex + i; } } }
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); }
private static void Comparer_VerifySetMatches <T>(BitVector set, XArray left, XArray right, CompareOperator cOp) where T : IComparable <T> { // Validate each array entry is included (or not) as the individual comparison would int expectedCount = 0; T[] leftArray = (T[])left.Array; T[] rightArray = (T[])right.Array; for (int i = 0; i < left.Selector.Count; ++i) { bool shouldBeIncluded = CompareSingle(leftArray[left.Index(i)], rightArray[right.Index(i)], cOp); if (shouldBeIncluded) { expectedCount++; } Assert.AreEqual(shouldBeIncluded, set[i]); } // Verify overall count is right Assert.AreEqual(expectedCount, set.Count); // Get a page of all matching indices int[] values = new int[set.Capacity]; int index = 0; int count = set.Page(values, ref index); // Verify the paged indices have the right count and every index there matched Assert.AreEqual(expectedCount, count); for (int i = 0; i < count; ++i) { Assert.IsTrue(CompareSingle(leftArray[left.Index(values[i])], rightArray[right.Index(values[i])], cOp)); } }
// 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)); }
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(XArray left, XArray unused, BitVector vector) { byte[] leftArray = (byte[])left.Array; // Check how the arrays are configured and run the fastest loop possible for the configuration. if (left.HasNulls) { // Slowest Path: Null checks and look up indices on both sides for (int i = 0; i < left.Count; ++i) { int leftIndex = left.Index(i); if (left.NullRows[leftIndex]) { continue; } if (_array[leftArray[leftIndex]]) { vector.Set(i); } } } else if (left.Selector.Indices != null) { // Slow Path: Look up indices on both sides. for (int i = 0; i < left.Count; ++i) { if (_array[leftArray[left.Index(i)]]) { vector.Set(i); } } } else if (!left.Selector.IsSingleValue) { // Fastest Path: Contiguous Array to constant. int zeroOffset = left.Selector.StartIndexInclusive; for (int i = left.Selector.StartIndexInclusive; i < left.Selector.EndIndexExclusive; ++i) { if (_array[leftArray[i]]) { vector.Set(i - zeroOffset); } } } else { // Single Static comparison. if (_array[leftArray[left.Selector.StartIndexInclusive]]) { vector.All(left.Count); } } }
private static bool[] MergeNulls(XArray xarray, bool[] couldNotConvert, ref bool[] buffer) { // No nulls? Just return the values which didn't convert if (!xarray.HasNulls) { return(couldNotConvert); } if (couldNotConvert == null) { return(XArray.RemapNulls(xarray, ref buffer)); } // Or together the nulls and values which didn't convert if (xarray.Selector.Indices != null) { for (int i = 0; i < xarray.Count; ++i) { couldNotConvert[i] |= xarray.NullRows[xarray.Index(i)]; } } else { int start = xarray.Selector.StartIndexInclusive; for (int i = 0; i < xarray.Count; ++i) { couldNotConvert[i] |= xarray.NullRows[i + start]; } } return(couldNotConvert); }
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 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 static void ErrorWhenSpecified(ValueKinds errorOnKinds, XArray source, bool[] couldNotConvert, string errorContextMessage) { // If not erroring on anything, nothing to check if (errorOnKinds == ValueKinds.None) { return; } // TODO: Use table failure logs. Log should track absolute row count to improve message because this is an IColumn and can't tell. if (errorOnKinds == ValueKinds.InvalidOrNull) { if (couldNotConvert != null) { throw new InvalidOperationException($"{errorContextMessage} failed for at least one value."); } } else if (errorOnKinds == ValueKinds.Invalid) { if (couldNotConvert != null && source.HasNulls) { for (int i = 0; i < source.Count; ++i) { if (couldNotConvert[i] == true && !source.NullRows[source.Index(i)]) { throw new InvalidOperationException($"{errorContextMessage} failed for at least one value."); } } } } else { throw new NotImplementedException(errorOnKinds.ToString()); } }
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); }
private void AddInt(XArray rowIndices, int newDistinctCount) { int[] array = (int[])rowIndices.Array; if (rowIndices.Selector.Indices != null) { // Indexed XArray - look up indexed values int[] indices = rowIndices.Selector.Indices; for (int i = rowIndices.Selector.StartIndexInclusive; i < rowIndices.Selector.EndIndexExclusive; ++i) { _countPerBucket[array[indices[i]]]++; } } else if (rowIndices.Selector.IsSingleValue == false) { // Non-Indexed XArray - loop from Start to End for (int i = rowIndices.Selector.StartIndexInclusive; i < rowIndices.Selector.EndIndexExclusive; ++i) { _countPerBucket[array[i]]++; } } else { // All rows are one value - add to that count _countPerBucket[array[rowIndices.Index(0)]] += rowIndices.Count; } }
private bool ShouldStopEarly(GroupByDictionary dictionary, CountAggregator counter) { // If every value was unique so far, stop if (dictionary.Count == counter.TotalRowCount) { return(true); } // If any value had enough rows to report, keep going XArray counts = counter.Values; int[] countsArray = (int[])counts.Array; int threshold = (int)(counter.TotalRowCount * MinimumPercentageToReport); for (int i = 0; i < counts.Count; ++i) { int count = countsArray[counts.Index(i)]; if (count >= threshold) { return(false); } } // Otherwise, stop (not all unique, but no values in > 0.5% of rows) return(true); }
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 void GetHashCodes(XArray xarray, int[] hashes) { if (hashes.Length < xarray.Count) { throw new ArgumentOutOfRangeException("hashes.Length"); } bool[] array = (bool[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { int index = xarray.Index(i); if (!xarray.HasNulls || xarray.NullRows[index] == false) { hashes[i] = (hashes[i] << 5) - hashes[i] + GetHashCode(array[xarray.Index(i)]); } } }
// 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 static void WhereNull(XArray left, bool isValue, BitVector vector) { bool[] leftArray = (bool[])left.NullRows; if (leftArray == null) { if (isValue == false) { vector.All(left.Count); } return; } // Check how the arrays are configured and run the fastest loop possible for the configuration. if (left.Selector.Indices != null) { // Slow Path: Look up indices on both sides. ~55ms for 16M for (int i = 0; i < left.Count; ++i) { if (leftArray[left.Index(i)] == isValue) { vector.Set(i); } } } else if (!left.Selector.IsSingleValue) { // Fastest Path: Contiguous Array to constant. ~15ms for 16M int zeroOffset = left.Selector.StartIndexInclusive; if (s_WhereSingleNative != null) { s_WhereSingleNative(leftArray, left.Selector.StartIndexInclusive, left.Selector.Count, (byte)CompareOperator.Equal, isValue, (byte)BooleanOperator.Or, vector.Array, 0); } else { for (int i = left.Selector.StartIndexInclusive; i < left.Selector.EndIndexExclusive; ++i) { if (leftArray[i] == isValue) { vector.Set(i - zeroOffset); } } } } else { // Single Static comparison. ~0.7ms for 16M [called every 10,240 rows] if (leftArray[left.Selector.StartIndexInclusive] == isValue) { vector.All(left.Count); } else { vector.None(); } } }
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 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)); } }
public static bool IsNullConstant(this IXColumn column) { if (!column.IsConstantColumn()) { return(false); } XArray value = column.ValuesGetter()(); return(value.HasNulls && value.NullRows[value.Index(0)]); }
private void PostSortAndFilter(XArray groups, XArray counts, int totalRowCount, bool wasAllRows) { int[] finalIndices = new int[groups.Count]; int[] finalCounts = new int[groups.Count]; int groupCount = 0; // Filter to counts over the minimum percentage threshold int[] countsArray = (int[])counts.Array; if (countsArray != null) { int threshold = (int)(totalRowCount * MinimumPercentageToReport); for (int i = 0; i < groups.Count; ++i) { int count = countsArray[counts.Index(i)]; if (count >= threshold) { finalIndices[groupCount] = i; finalCounts[groupCount] = count; groupCount++; } } } // Sort the values by count descending Array.Sort <int, int>(finalCounts, finalIndices, 0, groupCount, new ReverseComparer()); // Limit to the top N if needed if (groupCount > MaximumCountToReturn) { groupCount = MaximumCountToReturn; } // Set the distinct count (now that it's known) _distinctCount = groupCount; // Set the output values int[] groupsRemap = null; XArray finalCountsX = XArray.All(finalCounts, groupCount); _columns[0].SetValues(groups.Select(ArraySelector.Map(finalIndices, groupCount), ref groupsRemap)); _columns[1].SetValues(finalCountsX); if (wasAllRows) { _columns[2].SetValues(PercentageAggregator.ToPercentageStrings(finalCountsX, totalRowCount, PercentageAggregator.TwoSigFigs)); } else { _columns[2].SetValues(PercentageAggregator.ToPercentageStrings(finalCountsX, totalRowCount, PercentageAggregator.WholePercentage)); } }
public void Sample() { SampleDatabase.EnsureBuilt(); string xqlQuery = @" read WebRequest select [ServerPort], Cast([ResponseBytes], Int32) where [ServerPort] = ""80"" limit 1000 "; int desiredRowCountPerPage = 100; int maxResponseBytes = -1; // Build a Pipeline for the query. Wrap in a using statement to Dispose it when done. using (IXTable pipeline = SampleDatabase.XDatabaseContext.Query(xqlQuery)) { // Identify the columns you're consuming by requesting and caching the getter functions for them. // You must request the getters before the first call to Next(). // This tells the caller which columns you care about and builds hardcoded logic to get the data you want. Func <XArray> columnGetter = pipeline.Columns.Find("ResponseBytes").CurrentGetter(); // Call Next() to get an XArray of rows. Ask for only as many as you need. Ask for an XArray size convenient to work with. // Next() may return fewer rows than you asked for, but will not return zero until the input has run out of rows. while (pipeline.Next(desiredRowCountPerPage) > 0) { // Get the values for your desired column for this set of rows. XArray responseBytesxarray = columnGetter(); // If you know the type of the column, you can safely cast the array to the right type. // This allows you to write C# code hard-coded to the type, so there's no boxing and no interface calls. int[] array = (int[])responseBytesxarray.Array; // Loop from zero to the count the xarray says it returned for (int i = 0; i < responseBytesxarray.Count; ++i) { // You need to look up the real index of each value in the array. // Index() allows callers to pass a whole array, and array slice, or lookup indices into the array. // The CPU figures out the pattern quickly so branch costs are minimal. int responseBytes = array[responseBytesxarray.Index(i)]; // Run your own logic (in this case, track the Max of the value) if (responseBytes > maxResponseBytes) { maxResponseBytes = responseBytes; } } } } Assert.AreEqual(1335, maxResponseBytes); }
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[] 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); }
/// <summary> /// Get a single value from the source (the first column in the first row). /// </summary> /// <typeparam name="T">Data Type of result</typeparam> /// <param name="pipeline">IDatxarrayEnumerator to run</param> /// <returns>Single value result (first column, first row)</returns> public static T Single <T>(this IXTable pipeline, CancellationToken cancellationToken = default(CancellationToken)) { Func <XArray> getter = pipeline.Columns[0].CurrentGetter(); using (pipeline) { pipeline.Next(1, cancellationToken); XArray xarray = getter(); T[] array = (T[])(getter().Array); return(array[xarray.Index(0)]); } }
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 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 void GetHashCodes(XArray xarray, int[] hashes) { if (hashes.Length < xarray.Count) throw new ArgumentOutOfRangeException("hashes.Length"); String8[] array = (String8[])xarray.Array; for (int i = 0; i < xarray.Count; ++i) { int index = xarray.Index(i); if (xarray.HasNulls || xarray.NullRows[index] == false) { hashes[i] = (hashes[i] << 5) - hashes[i] + unchecked((int)Hashing.Hash(array[xarray.Index(i)], 0)); } } }
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)); }