public ReusableTask <int> ReadAsync(ITorrentManagerFile file, long offset, Memory <byte> buffer) { if (DoNotReadFrom.Contains(file)) { return(ReusableTask.FromResult(0)); } if (!Paths.Contains(file.FullPath)) { Paths.Add(file.FullPath); } if ((offset + buffer.Length) > file.Length) { throw new ArgumentOutOfRangeException("Tried to read past the end of the file"); } if (!DontWrite) { for (int i = 0; i < buffer.Length; i++) { buffer.Span[i] = (byte)i; } } return(ReusableTask.FromResult(buffer.Length)); }
public async ReusableTask WriteAsync(ITorrentManagerFile file, long offset, ReadOnlyMemory <byte> buffer) { if (file is null) { throw new ArgumentNullException(nameof(file)); } if (offset < 0 || offset + buffer.Length > file.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } using (await Limiter.EnterAsync()) { using var rented = await StreamCache.GetOrCreateStreamAsync(file, FileAccess.ReadWrite).ConfigureAwait(false); // FileStream.WriteAsync does some work synchronously, according to the profiler. // It looks like if the file is too small it is expanded (SetLength is called) // synchronously before the asynchronous Write is performed. // // We also want the Seek operation to execute on the threadpool. await SwitchToThreadpool(); rented.Stream !.Seek(offset, SeekOrigin.Begin); rented.Stream.Write(buffer); } }
public async ReusableTask <int> ReadAsync(ITorrentManagerFile file, long offset, Memory <byte> buffer) { if (file is null) { throw new ArgumentNullException(nameof(file)); } if (offset < 0 || offset + buffer.Length > file.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } using (await Limiter.EnterAsync()) { using var rented = await StreamCache.GetOrCreateStreamAsync(file, FileAccess.Read).ConfigureAwait(false); await SwitchToThreadpool(); if (rented.Stream !.Length < offset + buffer.Length) { return(0); } if (rented.Stream.Position != offset) { rented.Stream.Seek(offset, SeekOrigin.Begin); } return(rented.Stream.Read(buffer)); } }
public override async ReusableTask WriteAsync(ITorrentManagerFile file, long offset, ReadOnlyMemory <byte> buffer) { var tcs = new ReusableTaskCompletionSource <object> (); Writes.Add(tcs); await tcs.Task; }
public ReusableTask <int> ReadAsync(ITorrentManagerFile file, long offset, Memory <byte> buffer) { if (read) { throw new Exception("read"); } return(ReusableTask.FromResult(buffer.Length)); }
public ReusableTask MoveAsync(ITorrentManagerFile file, string newPath, bool overwrite) { if (move) { throw new Exception("move"); } return(ReusableTask.CompletedTask); }
public ReusableTask <bool> ExistsAsync(ITorrentManagerFile file) { if (exist) { throw new Exception("exists"); } return(ReusableTask.FromResult(true)); }
public ReusableTask WriteAsync(ITorrentManagerFile file, long offset, ReadOnlyMemory <byte> buffer) { if (write) { throw new Exception("write"); } return(ReusableTask.CompletedTask); }
public ReusableTask CloseAsync(ITorrentManagerFile file) { if (close) { throw new Exception("close"); } return(ReusableTask.CompletedTask); }
public ReusableTask WriteAsync(ITorrentManagerFile file, long offset, ReadOnlyMemory <byte> buffer) { var result = new byte[buffer.Length]; buffer.CopyTo(result.AsMemory()); WrittenData.Add(Tuple.Create(file, offset, result)); return(ReusableTask.CompletedTask); }
/// <summary> /// Creates a <see cref="Stream"/> which can be used to access the given <see cref="TorrentFile"/> /// while it is downloading. This stream is seekable and readable. This stream must be disposed /// before another stream can be created. /// </summary> /// <param name="file">The file to open</param> /// <param name="prebuffer">True if the first and last piece should be downloaded before the Stream is created.</param> /// <param name="token">The cancellation token</param> /// <returns></returns> public async Task <IUriStream> CreateHttpStreamAsync(ITorrentManagerFile file, bool prebuffer, CancellationToken token) { var stream = await CreateStreamAsync(file, prebuffer, token); var httpStreamer = new HttpStream(stream); return(httpStreamer); }
public ReusableTask FlushAsync(ITorrentManagerFile file) { if (flush) { throw new Exception("flush"); } FlushedFiles.Add(file); return(ReusableTask.CompletedTask); }
internal async ReusableTask FlushAsync(ITorrentManagerFile file) { using var rented = await GetStream(file); if (rented.Stream != null) { await rented.Stream.FlushAsync(); } }
public async ReusableTask MoveAsync(ITorrentManagerFile file, string newPath, bool overwrite) { await StreamCache.CloseStreamAsync(file); if (overwrite) { File.Delete(newPath); } if (File.Exists(file.FullPath)) { Directory.CreateDirectory(Path.GetDirectoryName(newPath) !); File.Move(file.FullPath, newPath); } }
internal async ReusableTask <RentedStream> GetOrCreateStreamAsync(ITorrentManagerFile file, FileAccess access) { if (!Streams.TryGetValue(file, out StreamData? data)) { data = Streams[file] = new StreamData(); } var releaser = await data.Locker.EnterAsync(); if (data.Stream != null) { // If we are requesting write access and the current stream does not have it if (((access & FileAccess.Write) == FileAccess.Write) && !data.Stream.CanWrite) { data.Stream.Dispose(); data.Stream = null; Count--; } } if (data.Stream == null) { if (!File.Exists(file.FullPath)) { if (Path.GetDirectoryName(file.FullPath) is string parentDirectory) { Directory.CreateDirectory(parentDirectory); } NtfsSparseFile.CreateSparse(file.FullPath, file.Length); } data.Stream = StreamCreator(file, access); Count++; // Ensure that we truncate existing files which are too large if (data.Stream.Length > file.Length) { if (!data.Stream.CanWrite) { data.Stream.Dispose(); data.Stream = StreamCreator(file, FileAccess.ReadWrite); } data.Stream.SetLength(file.Length); } } data.LastUsedStamp = Stopwatch.GetTimestamp(); MaybeRemoveOldestStream(); return(new RentedStream(data.Stream, releaser)); }
public void Setup() { var pieceLength = Constants.BlockSize * 2; Temp = Path.GetTempPath() + "monotorrent_tests"; var files = TorrentFileInfo.Create(pieceLength, ("test1.file", 12345, Path.Combine(Temp, "test1.file")), ("test2.file", 12345, Path.Combine(Temp, "test2.file")), ("test3.file", 12345, Path.Combine(Temp, "test3.file")), ("test4.file", 12345, Path.Combine(Temp, "test4.file")) ); TorrentFile = files.First(); Others = files.Skip(1).ToArray(); }
internal async ReusableTask <bool> CloseStreamAsync(ITorrentManagerFile file) { if (Streams.TryGetValue(file, out StreamData? data)) { using var releaser = await data.Locker.EnterAsync(); if (data.Stream != null) { data.Stream.Dispose(); data.Stream = null; Count--; return(true); } } return(false); }
internal async ReusableTask <RentedStream> GetStream(ITorrentManagerFile file) { if (Streams.TryGetValue(file, out StreamData? data)) { var releaser = await data.Locker.EnterAsync(); if (data.Stream == null) { releaser.Dispose(); } else { data.LastUsedStamp = Stopwatch.GetTimestamp(); return(new RentedStream(data.Stream, releaser)); } } return(new RentedStream(null, default)); }
public ReusableTask <int> ReadAsync(ITorrentManagerFile file, long offset, Memory <byte> buffer) { ReadData.Add(Tuple.Create(file, offset, buffer.Length)); if (Data == null) { var fileData = WrittenData .Where(t => t.Item1 == file) .OrderBy(t => t.Item2) .SelectMany(t => t.Item3) .ToArray(); fileData.AsSpan((int)offset, buffer.Length).CopyTo(buffer.Span); } else { var data = Data[file]; data.AsSpan((int)offset, buffer.Length).CopyTo(buffer.Span); } return(ReusableTask.FromResult(buffer.Length)); }
public async Task SetFilePriorityAsync(ITorrentManagerFile file, Priority priority) { if (!Files.Contains(file)) { throw new ArgumentNullException(nameof(file), "The file is not part of this torrent"); } // No change if (priority == file.Priority) { return; } await ClientEngine.MainLoop; // If the old priority, or new priority, is 'DoNotDownload' then the selector needs to be refreshed bool needsToUpdateSelector = file.Priority == Priority.DoNotDownload || priority == Priority.DoNotDownload; ((TorrentFileInfo)file).Priority = priority; if (needsToUpdateSelector) { // If we change the priority of a file we need to figure out which files are marked // as 'DoNotDownload' and which ones are downloadable. PartialProgressSelector.SetAll(false); if (Files.All(t => t.Priority != Priority.DoNotDownload)) { PartialProgressSelector.SetAll(true); } else { PartialProgressSelector.SetAll(false); foreach (var f in Files.Where(t => t.Priority != Priority.DoNotDownload)) { PartialProgressSelector.SetTrue((f.StartPieceIndex, f.EndPieceIndex)); } } } }
/// <summary> /// Creates a <see cref="Stream"/> which can be used to access the given <see cref="TorrentFile"/> /// while it is downloading. This stream is seekable and readable. The first and last pieces of /// this file will be buffered before the stream is created if <paramref name="prebuffer"/> is /// set to true. Finally, this stream must be disposed before another stream can be created. /// </summary> /// <param name="file">The file to open</param> /// <param name="prebuffer">True if the first and last piece should be downloaded before the Stream is created.</param> /// <param name="token">The cancellation token.</param> /// <returns></returns> public async Task <Stream> CreateStreamAsync(ITorrentManagerFile file, bool prebuffer, CancellationToken token) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (Manager.Files == null) { throw new InvalidOperationException("The metadata for this torrent has not been downloaded. You must call WaitForMetadataAsync before creating a stream."); } if (Manager.State == TorrentState.Stopped || Manager.State == TorrentState.Stopping || Manager.State == TorrentState.Error) { throw new InvalidOperationException($"The torrent state was {Manager.State}. StreamProvider cannot be used unless the torrent manager has been successfully started."); } if (!Manager.Files.Contains(file)) { throw new ArgumentException("The TorrentFile is not from this TorrentManager", nameof(file)); } if (ActiveStream != null && !ActiveStream.Disposed) { throw new InvalidOperationException("You must Dispose the previous stream before creating a new one."); } ActiveStream = new LocalStream(Manager, file, PieceRequester); var tcs = CancellationTokenSource.CreateLinkedTokenSource(Cancellation.Token, token); if (prebuffer) { ActiveStream.Seek(ActiveStream.Length - Manager.Torrent !.PieceLength * 2, SeekOrigin.Begin); await ActiveStream.ReadAsync(new byte[1], 0, 1, tcs.Token); ActiveStream.Seek(0, SeekOrigin.Begin); await ActiveStream.ReadAsync(new byte[1], 0, 1, tcs.Token); } ActiveStream.Seek(0, SeekOrigin.Begin); return(ActiveStream); }
public void Set(ITorrentManagerFile file, Priority priority) => ((TorrentFileInfo)file).Priority = priority;
public static long BytesDownloaded(this ITorrentManagerFile info) => (long)(info.BitField.PercentComplete * info.Length / 100.0);
internal async Task <bool> CheckFileExistsAsync(ITorrentManagerFile file) { await IOLoop; return(await Cache.Writer.ExistsAsync(file).ConfigureAwait(false)); }
public ReusableTask CloseAsync(ITorrentManagerFile file) { return(ReusableTask.CompletedTask); }
public ReusableTask WriteAsync(ITorrentManagerFile file, long offset, ReadOnlyMemory <byte> buffer) { return(ReusableTask.CompletedTask); }
public ReusableTask MoveAsync(ITorrentManagerFile file, string newPath, bool overwrite) { return(ReusableTask.CompletedTask); }
public ReusableTask <bool> ExistsAsync(ITorrentManagerFile file) { return(ReusableTask.FromResult(FilesThatExist.Contains(file))); }
public ReusableTask <int> ReadAsync(ITorrentManagerFile file, long offset, Memory <byte> buffer) { return(ReusableTask.FromResult(0)); }
public Files(ITorrentManagerFile file) { Priority = file.Priority; File = file; }