public void VerifyConsistency(VerificationLevel level, ExecutionDetails details) { if (details == null) { throw new ArgumentNullException("details"); } // Verify there are enough index items if (_itemCount > _index.Length) { details.AddError(ExecutionDetails.ColumnDoesNotHaveEnoughValues, this.Name, _itemCount, _index.Length); } for (int i = 0; i < _itemCount; ++i) { BlockPosition p = _index[i]; if (p.BatchIndex >= _batchCount) { // Verify every index item points to a valid batch details.AddError(ExecutionDetails.ByteBlockColumnBatchOutOfRange, this.Name, i, p.BatchIndex, _batchCount); } else { // Verify every empty item is represented correctly and every non-oversize item points to a valid array range BlockBatch batch = _batches[p.BatchIndex]; if (p.Length == 0) { if (p.Position != 0) { details.AddError(ExecutionDetails.ByteBlockEmptyValueMisrecorded, this.Name, i, p.Position); } } else if (p.Length == ushort.MaxValue) { if (p.Position != 0) { details.AddError(ExecutionDetails.ByteBlockHugeValueMisrecorded, this.Name, i, p.Position); } } else { if (p.Position >= batch.Array.Length || p.Position + p.Length > batch.Array.Length) { details.AddError(ExecutionDetails.ByteBlockColumnPositionOutOfRange, this.Name, i, p.Position, p.Length, batch.Array.Length); } } } } // Verify all out-of-range items are clear for (int i = _itemCount; i < _index.Length; ++i) { BlockPosition p = _index[i]; if (p.BatchIndex != 0 || p.Position != 0 || p.Length != 0) { details.AddError(ExecutionDetails.ByteBlockColumnUnclearedIndexEntry, this.Name, i, p); } } }
private bool CompactIfNeeded(int batchIndex, int additionalSizeNeeded) { BlockBatch batch = _batches[batchIndex]; // If there's not enough waste, nothing to clean up double wastePercentage = ((double)batch.WasteSpace / batch.Array.Length); if (wastePercentage < CompactWasteThreshold) { return(false); } // Otherwise, create a new array int neededSize = batch.UsedSpace - batch.WasteSpace + additionalSizeNeeded; int newSize = ArrayExtensions.RecommendedSize(batch.Array.Length, neededSize, ushort.MaxValue); byte[] oldArray = batch.Array; byte[] newArray = new byte[newSize]; // Next, copy every non-empty item in this batch to the new array contiguously ushort nextWritePosition = 0; for (int i = 0; i < _itemCount; ++i) { BlockPosition p = _index[i]; if (p.BatchIndex != batchIndex) { continue; } if (p.Length == 0) { continue; } Array.Copy(oldArray, p.Position, newArray, nextWritePosition, p.Length); p.Position = nextWritePosition; nextWritePosition += p.Length; _index[i] = p; } // Update and rewrite the batch batch.Array = newArray; batch.UsedSpace = nextWritePosition; batch.WasteSpace = 0; _batches[batchIndex] = batch; return(true); }
private bool TryWriteAppend(ref BlockPosition position, ByteBlock value) { BlockBatch appendBatch = _batches[_appendToBatchIndex]; // If the new value is too big, can we expand? if (appendBatch.Array.Length - appendBatch.UsedSpace < value.Length) { // Compact, if reasonable, or expand if not if (CompactIfNeeded(_appendToBatchIndex, value.Length)) { appendBatch = _batches[_appendToBatchIndex]; } else { ArrayExtensions.Resize(ref appendBatch.Array, appendBatch.UsedSpace + value.Length, ushort.MaxValue); _batches[_appendToBatchIndex] = appendBatch; } } // If the value is still too big, stop if (appendBatch.Array.Length - appendBatch.UsedSpace < value.Length) { return(false); } // Build the new position BlockPosition newPosition; newPosition.BatchIndex = _appendToBatchIndex; newPosition.Position = (ushort)appendBatch.UsedSpace; newPosition.Length = (ushort)value.Length; // Track the new used space appendBatch.UsedSpace += (ushort)value.Length; _batches[_appendToBatchIndex] = appendBatch; // Write the value to the new position value.CopyTo(appendBatch.Array, newPosition.Position); // Update the position and return position = newPosition; return(true); }
private bool TryWriteInPlace(ref BlockPosition position, ByteBlock value) { BlockBatch containingBatch = _batches[position.BatchIndex]; // If this item is new, there is no place to write it if (position.Length == 0) { return(false); } // If the old value was written alone, replace it with a new exact size array if (position.Length == ushort.MaxValue) { containingBatch.Array = new byte[value.Length]; containingBatch.UsedSpace = (ushort)value.Length; value.CopyTo(containingBatch.Array); } else { // If the new value is too big, we can't write it in place if (position.Length < value.Length) { return(false); } // Reduce the waste for reusing the space (we added it all as waste before placement) containingBatch.WasteSpace -= (ushort)value.Length; // Write the array to the existing position and record the new length value.CopyTo(_batches[position.BatchIndex].Array, position.Position); position.Length = (ushort)value.Length; } // Update the batch _batches[position.BatchIndex] = containingBatch; return(true); }