/// <summary> /// Устанавливает указатель текущего положения в потоке в новое место. /// Примечание: за границу потока установить указатель нельзя (допускается лишь устанавливать ее за последним записанным/считанным байтом). /// </summary> /// <param name="newPosition">Новая позиция указателя текущего положения.</param> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ObjectDisposedException"></exception> public override void SetPosition(int newPosition) { lock (_stateChangeCriticalSection) { this.ThrowIfDisposed(); MethodArgumentValidator.ThrowIfNegative(newPosition, "newPosition"); if (newPosition > this.Length) { throw new ArgumentOutOfRangeException("newPosition", "Невозможно установить позицию за пределами потока данных."); } if (newPosition == this.Length) { this.MoveToEnd(); return; } int blockIndex = newPosition / _blockSize; _diskBlockEnumerator.SetPosition(blockIndex); _diskBlockEnumerator.Current.Position = newPosition % _blockSize; } }
/// <summary> /// Записывает в поток указанное число байт из массива. Если поток недостаточного размера, он увеличивается. /// Запись производится, начиная с текущей позиции в потоке. /// </summary> /// <param name="bytesToWrite">Массив байт, данные из которого следует записать в поток.</param> /// <param name="arrayOffset">Указывает начальную позицию в массиве (<paramref name="bytesToWrite"/>), с которой нужно брать байты для записи.</param> /// <param name="count">Количество байт, которые, начиная с <paramref name="arrayOffset"/>, следует записать в поток.</param> /// <exception cref="InsufficientSpaceException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="MaximumFileSizeReachedException"></exception> public override void Write(byte[] bytesToWrite, int arrayOffset, int count) { if (bytesToWrite == null) { throw new ArgumentNullException("bytesToWrite"); } // Note: этому методу, подозреваю, сильно не хватило тестов. MethodArgumentValidator.ThrowIfNegative(arrayOffset, "arrayOffset"); MethodArgumentValidator.ThrowIfNegative(count, "count"); if ((arrayOffset + count) > bytesToWrite.Length) { throw new ArgumentException("Не удастся прочитать {0} байт, начиная с позиции {1} в массиве. В массиве содержится следующее число элементов: {2}".FormatWith(count, arrayOffset, bytesToWrite.Length)); } if (count == 0) { return; } Monitor.Enter(_stateChangeCriticalSection); try { this.ThrowIfDisposed(); int numberOfBytesAvailable = this.Length - this.Position; // есть еще зажатые в выделенном блоке, ими мы занимаемся ниже. int numberOfBytesNeeded = count - numberOfBytesAvailable; bool canAvoidNewBlocksAcquisition = _diskBlockEnumerator.Current.NumberOfBytesCanBeWrittenAtCurrentPosition >= numberOfBytesNeeded; if ((numberOfBytesNeeded > 0) && !canAvoidNewBlocksAcquisition) { int oldBlockIndex = _diskBlockEnumerator.Position; int oldInsideBlockPosition = _diskBlockEnumerator.Current.Position; _streamStructureBuilder.SetSize(_streamStructureBuilder.CurrentSize + numberOfBytesNeeded); _diskBlockEnumerator = _streamStructureBuilder.CreateEnumerator(); _diskBlockEnumerator.SetPosition(oldBlockIndex); if (!_diskBlockEnumerator.Current.IsNull) { _diskBlockEnumerator.Current.Position = oldInsideBlockPosition; } } WriteBytesKnowingStreamLengthIsRight(bytesToWrite, arrayOffset, count); } catch (MaximumFileSizeReachedException) { throw new MaximumFileSizeReachedException("Не удаcтся записать {0} байт в файл: максимальный размер файла в текущей версии системы - {1} байт, текущий размер файла - {2} байт.".FormatWith(count, _streamStructureBuilder.MaximumSize, this.Length)); } catch (NoFreeBlocksException) { throw new InsufficientSpaceException("Операция записи отменена: недостаточно места на диске для записи в поток следующего числа байт -- {0}".FormatWith(count)); } finally { Monitor.Exit(_stateChangeCriticalSection); } }