private void DecodingThread(CancellationToken token) { for (; ;) { if (token.WaitHandle.WaitOne(1000)) { return; } DownloadItemInfo item = null; lock (_lockObject) { item = CollectionUtils.Unite(_volatileDownloadItemInfoManager, _downloadItemInfoManager) .Where(n => !_workingItems.Contains(n)) .Where(n => n.State == DownloadState.Decoding || n.State == DownloadState.ParityDecoding) .OrderBy(n => (n.Depth == n.Metadata.Depth) ? 0 : 1) .OrderBy(n => (n.State == DownloadState.Decoding) ? 0 : 1) .FirstOrDefault(); if (item != null) { _workingItems.Add(item); } } if (item == null) { continue; } try { if ((item.Depth == 0 && !_cacheManager.Contains(item.Metadata.Hash)) || (item.Depth > 0 && !item.Index.Groups.All(n => _existManager.GetCount(n, true) >= n.Hashes.Count() / 2))) { item.State = DownloadState.Downloading; } else { var hashes = new HashCollection(); var totalHashes = new HashCollection(); if (item.Depth == 0) { hashes.Add(item.Metadata.Hash); totalHashes.Add(item.Metadata.Hash); } else { try { foreach (var group in item.Index.Groups) { if (item.State == DownloadState.Error) { throw new OperationCanceledException(); } hashes.AddRange(_cacheManager.ParityDecoding(group, token).Result); } totalHashes.AddRange(item.Index.Groups.SelectMany(n => n.Hashes)); } catch (OperationCanceledException) { continue; } item.State = DownloadState.Decoding; } if (item.Depth < item.Metadata.Depth) { Index index; try { using (var stream = _cacheManager.Decoding(hashes)) using (var progressStream = new ProgressStream(stream, null, 1024 * 1024, token)) { if (item.State == DownloadState.Error) { throw new OperationCanceledException(); } if (progressStream.Length > item.MaxLength) { throw new ArgumentException(); } index = Index.Import(progressStream, _bufferManager); } } catch (OperationCanceledException) { continue; } lock (_lockObject) { if (item.Path != null) { _protectCacheInfoManager.Add(new ProtectedCacheInfo(DateTime.UtcNow, totalHashes)); } this.CheckState(index); this.UncheckState(item.Index); item.Index = index; item.Depth++; item.State = DownloadState.Downloading; } } else { if (item.Path != null) { string filePath = null; try { token.ThrowIfCancellationRequested(); string targetPath; if (Path.IsPathRooted(item.Path)) { targetPath = item.Path; } else { targetPath = Path.GetFullPath(Path.Combine(_basePath, item.Path)); // ディレクトリトラバーサル対策 if (!targetPath.StartsWith(Path.GetFullPath(_basePath))) { targetPath = Path.GetFullPath(Path.Combine(_basePath, Path.GetFileName(item.Path))); } } Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); using (var inStream = _cacheManager.Decoding(hashes)) using (var outStream = DownloadManager.GetUniqueFileStream(targetPath + ".tmp")) using (var safeBuffer = _bufferManager.CreateSafeBuffer(1024 * 1024)) { filePath = outStream.Name; int readLength; while ((readLength = inStream.Read(safeBuffer.Value, 0, safeBuffer.Value.Length)) > 0) { if (item.State == DownloadState.Error) { throw new OperationCanceledException(); } token.ThrowIfCancellationRequested(); outStream.Write(safeBuffer.Value, 0, readLength); } } File.Move(filePath, DownloadManager.GetUniqueFilePath(targetPath)); } catch (OperationCanceledException) { if (filePath != null) { File.Delete(filePath); } continue; } } lock (_lockObject) { if (item.Path != null) { _protectCacheInfoManager.Add(new ProtectedCacheInfo(DateTime.UtcNow, totalHashes)); } item.ResultHashes.AddRange(hashes); item.State = DownloadState.Completed; } } } } catch (Exception e) { item.State = DownloadState.Error; Log.Error(e); } finally { _workingItems.Remove(item); } } }