Ejemplo n.º 1
0
        public void Combine_should_correctly_combine_multiple_hashes()
        {
            var usualHash    = Crc32Fast.Compute(data);
            var combinedHash = PartitionData().Aggregate(0U, (current, segment) => Crc32Fast.Combine(current, Crc32Fast.Compute(segment), segment.Count));

            combinedHash.Should().Be(usualHash);
        }
Ejemplo n.º 2
0
        public void Compute_should_compute_incremental_hash_correctly()
        {
            var usualHash       = Crc32Fast.Compute(data);
            var incrementalHash = PartitionData().Aggregate(0U, (current, segment) => Crc32Fast.Compute(segment, current));

            incrementalHash.Should().Be(usualHash);
        }
Ejemplo n.º 3
0
        public void ComputeForZeros_should_return_same_result_as_compute_for_zero_filled_array()
        {
            const int minPrecomputedSize = 2 * 1024;
            const int maxPrecomputedSize = 32 * 1024 * 1024;

            var size = maxPrecomputedSize;

            for (var i = maxPrecomputedSize / 2; i >= minPrecomputedSize; i /= 2)
            {
                size += i;
            }

            size += random.Next(minPrecomputedSize);

            var referenceHash = Crc32Fast.Compute(new byte[size]);
            var actualHash    = Crc32Fast.ComputeForZeros(size);

            actualHash.Should().Be(referenceHash);
        }
Ejemplo n.º 4
0
        public void Compute_should_return_zero_for_empty_data()
        {
            var hash = Crc32Fast.Compute(data, 0, 0);

            hash.Should().Be(0);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// </summary>
        public List <int> Validate(string RootPath, RateTracker Tracker, AsyncIOQueue IOQueue, BuildManfiestValidateProgressCallbackHandler Callback = null)
        {
            LazyCacheBlockInfo();

            List <int> FailedBlocks = new List <int>();

            try
            {
                LockBlockInfo();

                int    TaskCount    = Environment.ProcessorCount;
                Task[] FileTasks    = new Task[TaskCount];
                int    BlockCounter = 0;

                long BytesValidated = 0;
                bool Aborted        = false;

                // Check the size of each file.
                for (int i = 0; i < Files.Count; i++)
                {
                    BuildManifestFileInfo FileInfo = Files[i];
                    string FilePath = Path.Combine(RootPath, FileInfo.Path);
                    string DirPath  = Path.GetDirectoryName(FilePath);

                    if (!Directory.Exists(DirPath))
                    {
                        Directory.CreateDirectory(DirPath);
                        Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Expected directory {0} does not exist, creating.", DirPath);
                    }

                    FileInfo Info = new FileInfo(FilePath);
                    if (!Info.Exists || Info.Length != FileInfo.Size)
                    {
                        using (FileStream Stream = new FileStream(FilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
                        {
                            Logger.Log(LogLevel.Warning, LogCategory.Manifest, "File {0} is not of expected length {1} (is {2}) settting length.", FilePath, FileInfo.Size, Info.Length);
                            Stream.SetLength(FileInfo.Size);
                        }
                    }
                }

                // Check each individual block of data for validity.
                for (int i = 0; i < TaskCount; i++)
                {
                    FileTasks[i] = Task.Run(
                        () =>
                    {
                        byte[] Buffer = new byte[BlockSize];

                        while (!Aborted)
                        {
                            int BlockIndex = Interlocked.Increment(ref BlockCounter) - 1;
                            if (BlockIndex >= BlockCount)
                            {
                                break;
                            }

                            BuildManifestBlockInfo BInfo = BlockInfo[BlockIndex];

                            long BufferLength = 0;
                            bool Success      = GetBlockData(BlockIndex, RootPath, IOQueue, Buffer, out BufferLength);

                            uint Checksum = 0;
                            if (Version >= 2)
                            {
                                Checksum = Crc32Fast.Compute(Buffer, 0, (int)BufferLength);
                            }
                            else
                            {
                                Checksum = Crc32Slow.Compute(Buffer, (int)BufferLength);
                            }

                            if (!Success || BlockChecksums[BlockIndex] != Checksum)
                            {
                                Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Block {0} failed checksum, block contains following sub-blocks:", BlockIndex);

                                for (int SubBlock = 0; SubBlock < BInfo.SubBlocks.Length; SubBlock++)
                                {
                                    BuildManifestSubBlockInfo SubBInfo = BInfo.SubBlocks[SubBlock];
                                    Logger.Log(LogLevel.Warning, LogCategory.Manifest, "\tfile={0} offset={1} size={2}", SubBInfo.File.Path, SubBInfo.FileOffset, SubBInfo.FileSize);
                                }

                                lock (FailedBlocks)
                                {
                                    FailedBlocks.Add(BlockIndex);
                                }
                            }

                            Interlocked.Add(ref BytesValidated, BInfo.TotalSize);

                            if (Callback != null)
                            {
                                if (!Callback.Invoke(BytesValidated, TotalSize, Guid, BlockIndex))
                                {
                                    Aborted = true;
                                }
                            }
                        }
                    }
                        );
                }

                Task.WaitAll(FileTasks);
            }
            finally
            {
                UnlockBlockInfo();
            }

            return(FailedBlocks);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// </summary>
        public static BuildManifest BuildFromDirectory(Guid NewManifestId, string RootPath, string VirtualPath, AsyncIOQueue IOQueue, BuildManfiestProgressCallbackHandler Callback = null)
        {
            string[] FileNames = Directory.GetFiles(RootPath, "*", SearchOption.AllDirectories);
            long     TotalSize = 0;

            // Try and order in the most efficient packing order.
            List <FileInfo> RemainingFiles = new List <FileInfo>();
            List <FileInfo> OrderedList    = new List <FileInfo>();

            for (int i = 0; i < FileNames.Length; i++)
            {
                FileInfo Info = new FileInfo(FileNames[i]);
                RemainingFiles.Add(Info);
                TotalSize += Info.Length;
            }

            // Order main list from largest to smallest.
            RemainingFiles.Sort((Item1, Item2) => - Item1.Length.CompareTo(Item2.Length));

            // Block size.
            long ActualBlockSize = DefaultBlockSize;

            // Try and add in optimal block packing format.
            long BlockIndex = 0;

            while (RemainingFiles.Count > 0)
            {
                FileInfo Info = RemainingFiles[0];
                RemainingFiles.RemoveAt(0);
                OrderedList.Add(Info);
                //Console.WriteLine("Block[{0}] {1}", BlockIndex, Info.Name);

                long BlockCount     = (Info.Length + (ActualBlockSize - 1)) / ActualBlockSize;
                long BytesRemaining = (Info.Length % ActualBlockSize) == 0 ? 0 : ActualBlockSize - Info.Length % ActualBlockSize;
                BlockIndex += BlockCount;

                long SubBlockCount = BlockCount;

                // Try and fit some smaller files into the remaining block space.
                for (int i = 0; i < RemainingFiles.Count && BytesRemaining > 0 && SubBlockCount < MaxSubBlockCount; i++)
                {
                    FileInfo PotentialFile = RemainingFiles[i];
                    if (PotentialFile.Length <= BytesRemaining)
                    {
                        BytesRemaining -= PotentialFile.Length;
                        SubBlockCount++;

                        //Console.WriteLine("\tSubblock[{0}] {1}", BlockIndex, PotentialFile.Name);

                        RemainingFiles.RemoveAt(i);
                        OrderedList.Add(PotentialFile);
                        i--;
                    }
                }
            }

            // Our final list!
            FileInfo[] FileInfos = OrderedList.ToArray();

            BuildManifest Manifest = new BuildManifest();

            Manifest.Guid           = NewManifestId;
            Manifest.VirtualPath    = VirtualPath;
            Manifest.BlockCount     = BlockIndex;
            Manifest.BlockChecksums = new uint[Manifest.BlockCount];
            Manifest.CreateTime     = DateTime.UtcNow;
#if USE_SPARSE_CHECKSUMS
            Manifest.SparseBlockChecksums = new uint[Manifest.BlockCount];
#else
            Manifest.SparseBlockChecksums = null;
#endif
            Manifest.Version   = BuildManifest.CurrentVersion;
            Manifest.BlockSize = ActualBlockSize;

            List <Task> ChecksumTasks = new List <Task>();
            int         FileCounter   = 0;
            int         BlockCounter  = 0;

            for (int i = 0; i < FileInfos.Length; i++)
            {
                Manifest.Files.Add(new BuildManifestFileInfo());
            }

            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                ChecksumTasks.Add(
                    Task.Run(
                        () =>
                {
                    while (true)
                    {
                        int FileIndex = Interlocked.Increment(ref FileCounter) - 1;
                        if (FileIndex >= FileInfos.Length)
                        {
                            break;
                        }

                        FileInfo SubFileInfo = FileInfos[FileIndex];
                        string SubFile       = SubFileInfo.FullName;
                        string RelativePath  = SubFile.Substring(RootPath.Length).Trim('\\', '/');

                        if (Callback != null)
                        {
                            lock (Callback)
                            {
                                float Progress = (FileCounter + BlockCounter) / (float)(FileInfos.Length + Manifest.BlockCount);
                                Callback(RelativePath, Progress * 100);
                            }
                        }

                        BuildManifestFileInfo ManifestFileInfo = new BuildManifestFileInfo();
                        ManifestFileInfo.Path     = RelativePath;
                        ManifestFileInfo.Size     = new FileInfo(SubFile).Length;
                        ManifestFileInfo.Checksum = FileUtils.GetChecksum(SubFile, null);
                        lock (Manifest)
                        {
                            Manifest.Files[FileIndex] = ManifestFileInfo;
                        }
                    }
                }
                        )
                    );
            }

            foreach (Task task in ChecksumTasks)
            {
                task.Wait();
            }

            ChecksumTasks.Clear();

            // Calculate which data goes in eahc block.
            Manifest.CacheBlockInfo();
            Manifest.CacheSizeInfo();
            Manifest.DebugCheck();

            // Calculate checksum for each individual block.
            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                ChecksumTasks.Add(
                    Task.Run(
                        () =>
                {
                    byte[] Buffer = new byte[ActualBlockSize];

                    Stopwatch stopwatch = new Stopwatch();

                    while (true)
                    {
                        int CalculateBlockIndex = Interlocked.Increment(ref BlockCounter) - 1;
                        if (CalculateBlockIndex >= Manifest.BlockCount)
                        {
                            break;
                        }

                        long BufferLength = 0;
                        if (!Manifest.GetBlockData(CalculateBlockIndex, RootPath, IOQueue, Buffer, out BufferLength))
                        {
                            // We should never end up in this situation when publishing ...
                            Debug.Assert(false);
                        }

                        uint Checksum = 0;
                        if (Manifest.Version >= 2)
                        {
                            Checksum = Crc32Fast.Compute(Buffer, 0, (int)BufferLength);
                        }
                        else
                        {
                            Checksum = Crc32Slow.Compute(Buffer, (int)BufferLength);
                        }

                        Manifest.BlockChecksums[CalculateBlockIndex] = Checksum;
#if USE_SPARSE_CHECKSUMS
                        Manifest.SparseBlockChecksums[CalculateBlockIndex] = Crc32Slow.ComputeSparse(Buffer, (int)BufferLength);
#endif

                        if (Callback != null)
                        {
                            lock (Callback)
                            {
                                float Progress = (FileCounter + BlockCounter) / (float)(FileInfos.Length + Manifest.BlockCount);
                                Callback("Checksuming blocks", Progress * 100);
                            }
                        }
                    }
                }
                        )
                    );
            }

            foreach (Task task in ChecksumTasks)
            {
                task.Wait();
            }

            return(Manifest);
        }