public void ReadAndWriteUntilPosition_WhenReaderNull_Throws()
        {
            using (var test = new ReadWriteTest())
            {
                var exception = Assert.Throws <ArgumentNullException>(
                    () => SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(
                        reader: null,
                        writer: test.Writer,
                        position: 0));

                Assert.Equal("reader", exception.ParamName);
            }
        }
        private static Task ShiftSignatureMetadata(SigningSpecifications spec, BinaryReader reader, BinaryWriter writer, int centralDirectoryIndex, int fileHeaderIndex)
        {
            // Read metadata
            var metadata = SignedPackageArchiveIOUtility.ReadSignedArchiveMetadata(reader);

            // Update central directory records by excluding the signature entry
            SignedPackageArchiveIOUtility.UpdateSignedPackageArchiveMetadata(reader, metadata);

            // Calculate new central directory record metadata with the the signature record and entry shifted
            var shiftedCdr = ShiftMetadata(spec, metadata, newSignatureFileEntryIndex: fileHeaderIndex, newSignatureCentralDirectoryRecordIndex: centralDirectoryIndex);

            // Order records by shifted ofset (new offset = old offset + change in offset).
            // This is the order they will appear in the new shifted package, but not necesarily the same order they were in the old package
            shiftedCdr.Sort((x, y) => (x.OffsetToFileHeader + x.ChangeInOffset).CompareTo(y.OffsetToFileHeader + y.ChangeInOffset));

            // Write data from start of file to first file entry
            reader.BaseStream.Seek(offset: 0, origin: SeekOrigin.Begin);
            SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(reader, writer, metadata.StartOfFileHeaders);

            // Write all file entries in the new order
            foreach (var entry in shiftedCdr)
            {
                // We need to read each entry from their position in the old package and write them sequencially to the new package
                // The order in which they will appear in the new shited package is defined by the sorting done before starting to write
                reader.BaseStream.Seek(offset: entry.OffsetToFileHeader, origin: SeekOrigin.Begin);
                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(reader, writer, entry.OffsetToFileHeader + entry.FileEntryTotalSize);
            }

            // Write all central directory records with updated offsets
            // We first need to sort them in the order they will appear in the new shifted package
            shiftedCdr.Sort((x, y) => x.IndexInHeaders.CompareTo(y.IndexInHeaders));
            foreach (var entry in shiftedCdr)
            {
                reader.BaseStream.Seek(offset: entry.Position, origin: SeekOrigin.Begin);
                // Read and write from the start of the central directory record until the relative offset of local file header (42 from the start of central directory record, incluing signature length)
                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(reader, writer, reader.BaseStream.Position + 42);

                var relativeOffsetOfLocalFileHeader = (uint)(reader.ReadUInt32() + entry.ChangeInOffset);
                writer.Write(relativeOffsetOfLocalFileHeader);

                // We already read and hash the whole header, skip only filenameLength + extraFieldLength + fileCommentLength (46 is the size of the header without those lengths)
                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(reader, writer, reader.BaseStream.Position + entry.HeaderSize - CentralDirectoryFileHeaderSizeWithoutSignature);
            }

            // Write everything after central directory records
            reader.BaseStream.Seek(offset: metadata.EndOfCentralDirectory, origin: SeekOrigin.Begin);
            SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(reader, writer, reader.BaseStream.Length);

            return(Task.FromResult(0));
        }
        public void ReadAndWriteUntilPosition_WhenPositionInMiddle_ReadsAndWrites()
        {
            using (var test = new ReadWriteTest())
            {
                test.Reader.BaseStream.Seek(offset: 2, origin: SeekOrigin.Begin);

                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(test.Reader, test.Writer, position: 5);

                var actual = test.GetWrittenBytes();

                Assert.Equal(new byte[] { 2, 3, 4 }, actual);
                Assert.Equal(5, test.Reader.BaseStream.Position);
            }
        }
        public void ReadAndWriteUntilPosition_WhenPositionTooBig_Throws()
        {
            using (var test = new ReadWriteTest())
            {
                var exception = Assert.Throws <ArgumentOutOfRangeException>(
                    () => SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(
                        test.Reader,
                        test.Writer,
                        test.Reader.BaseStream.Length + 1));

                Assert.Equal("position", exception.ParamName);
                Assert.Equal(0, test.Reader.BaseStream.Position);
            }
        }
        public void ReadAndWriteUntilPosition_WhenPositionAtStart_ReadsAndWrites()
        {
            using (var test = new ReadWriteTest())
            {
                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(
                    test.Reader,
                    test.Writer,
                    test.Reader.BaseStream.Length);

                var actual = test.GetWrittenBytes();

                Assert.Equal(test.Bytes, actual);
                Assert.Equal(test.Reader.BaseStream.Length, test.Reader.BaseStream.Position);
            }
        }
        public void ReadAndWriteUntilPosition_WhenPositionBeforeCurrentReadPosition_Throws()
        {
            using (var test = new ReadWriteTest())
            {
                test.Reader.BaseStream.Seek(offset: 1, origin: SeekOrigin.Begin);

                var exception = Assert.Throws <ArgumentOutOfRangeException>(
                    () => SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(
                        test.Reader,
                        test.Writer,
                        position: 0));

                Assert.Equal("position", exception.ParamName);
                Assert.Equal(1, test.Reader.BaseStream.Position);
            }
        }
        public void ReadAndWriteUntilPosition_WhenPositionAtEnd_ReadsAndWrites()
        {
            using (var test = new ReadWriteTest())
            {
                test.Reader.BaseStream.Seek(offset: 0, origin: SeekOrigin.End);

                SignedPackageArchiveIOUtility.ReadAndWriteUntilPosition(
                    test.Reader,
                    test.Writer,
                    test.Reader.BaseStream.Length);

                var actual = test.GetWrittenBytes();

                Assert.Empty(actual);
                Assert.Equal(test.Reader.BaseStream.Length, test.Reader.BaseStream.Position);
            }
        }