Ejemplo n.º 1
0
        /// <summary>
        /// Appends a new metadata section to the end of the file.
        /// </summary>
        public Task WriteMetadata(ArchiveMetadataProvider metadata)
        {
            if (metadata == null)
                throw new ArgumentNullException(nameof(metadata));

            int metadataCount = metadata.GetCount();
            if (metadataCount < 0)
                throw new InvalidOperationException(nameof(ArchiveMetadataProvider) + " returned negative count.");

            // TODO: wait for completion of pending writes

            mMetadataPosition = mAppendPosition;
            mArchiveStream.Position = mAppendPosition;

            // TODO: we'll want to write the metadata into a stream, compress it, calculate the checksum on the fly
            // TODO: we'll probably also want to have a scratch buffer to assembly vectors with no allocation overhead and in a single iteration
            //       (a scratch buffer allows to record the vector in a single iteration and if it was unnecessary it doesn't need to be forwarded to the actual stream)

            var subStreamCount = mDecoderSections.Sum(x => x != null ? x.Streams.Length : 0);

            WriteToken(ArchiveMetadataToken.Header);

            if (mDecoderSections.Count > 0)
            {
                WriteToken(ArchiveMetadataToken.MainStreams);
                WritePackInfo();
                WriteUnpackInfo();
                WriteSubStreamsInfo();
                WriteToken(ArchiveMetadataToken.End);
            }

            if (subStreamCount > 0)
            {
                WriteToken(ArchiveMetadataToken.Files);
                WriteNumber(metadataCount);

                #region Types
                {
                    int emptyStreamCount = 0;

                    for (int i = 0; i < metadataCount; i++)
                        if (!metadata.HasStream(i))
                            emptyStreamCount++;

                    if (emptyStreamCount > 0)
                    {
                        WriteBitVectorWithHeader(ArchiveMetadataToken.EmptyStream,
                            Enumerable.Range(0, metadataCount)
                            .Select(x => !metadata.HasStream(x)),
                            metadataCount);

                        if (Enumerable.Range(0, metadataCount).Where(x => !metadata.HasStream(x)).Any(x => !metadata.IsDirectory(x)))
                            WriteBitVectorWithHeader(ArchiveMetadataToken.EmptyFile,
                                Enumerable.Range(0, metadataCount)
                                .Where(x => !metadata.HasStream(x))
                                .Select(x => !metadata.IsDirectory(x)),
                                emptyStreamCount);

                        if (Enumerable.Range(0, metadataCount).Where(x => !metadata.HasStream(x)).Any(x => metadata.IsDeleted(x)))
                            WriteBitVectorWithHeader(ArchiveMetadataToken.Anti,
                                Enumerable.Range(0, metadataCount)
                                .Where(x => !metadata.HasStream(x))
                                .Select(x => metadata.IsDeleted(x)),
                                emptyStreamCount);
                    }
                }
                #endregion

                #region Names
                {
                    bool hasNames = false;
                    int nameSize = 1;

                    for (int i = 0; i < subStreamCount; i++)
                    {
                        var name = metadata.GetName(i);
                        if (!string.IsNullOrEmpty(name))
                        {
                            hasNames = true;
                            nameSize += (name.Length + 1) * 2;
                        }
                        else
                        {
                            nameSize += 2;
                        }
                    }

                    if (hasNames)
                    {
                        WritePadding(2 + GetNumberSize(nameSize), 16);
                        WriteToken(ArchiveMetadataToken.Name);
                        WriteNumber(nameSize);
                        WriteByte(0);

                        System.Diagnostics.Debug.Assert((mArchiveStream.Position & 15) == 0);

                        for (int i = 0; i < subStreamCount; i++)
                        {
                            var name = metadata.GetName(i);
                            foreach (char ch in name)
                            {
                                WriteByte((byte)ch);
                                WriteByte((byte)(ch >> 8));
                            }
                            WriteByte(0);
                            WriteByte(0);
                        }
                    }
                }
                #endregion

                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetCreationDate(x)), ArchiveMetadataToken.CTime);
                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetLastAccessDate(x)), ArchiveMetadataToken.ATime);
                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetLastWriteDate(x)), ArchiveMetadataToken.MTime);
                // TODO: what does the start position mean? it doesn't seem to be what I thought it was.
                WriteUInt64Vector(Enumerable.Range(0, metadataCount).Select(x => default(ulong?)), ArchiveMetadataToken.StartPos);
                WriteUInt32Vector(Enumerable.Range(0, metadataCount).Select(x => {
                    var attr = metadata.GetAttributes(x);
                    return attr.HasValue ? (uint)attr.Value : default(uint?);
                }), ArchiveMetadataToken.WinAttributes);

                WriteToken(ArchiveMetadataToken.End);
            }

            WriteToken(ArchiveMetadataToken.End);

            mMetadataLength = mArchiveStream.Position - mMetadataPosition;

            // TODO: this is only a temporary implementation; the checksum can be calculated on the fly in the final implementation
            {
                var buffer = new byte[0x1000];
                mArchiveStream.Position = mMetadataPosition;
                var checksum = CRC.kInitCRC;
                int offset = 0;
                while (offset < mMetadataLength)
                {
                    var fetched = mArchiveStream.Read(buffer, 0, (int)Math.Min(mMetadataLength - offset, buffer.Length));
                    if (fetched <= 0 || fetched > mMetadataLength - offset)
                        throw new InternalFailureException();

                    offset += fetched;
                    checksum = CRC.Update(checksum, buffer, 0, fetched);
                }
                mMetadataChecksum = new Checksum((int)CRC.Finish(checksum));
            }

            return Utilities.CompletedTask;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Appends a new metadata section to the end of the file.
        /// </summary>
        public Task WriteMetadata(ArchiveMetadataProvider metadata)
        {
            if (metadata == null)
            {
                throw new ArgumentNullException(nameof(metadata));
            }

            int metadataCount = metadata.GetCount();

            if (metadataCount < 0)
            {
                throw new InvalidOperationException(nameof(ArchiveMetadataProvider) + " returned negative count.");
            }

            // TODO: wait for completion of pending writes

            mMetadataPosition       = mAppendPosition;
            mArchiveStream.Position = mAppendPosition;

            // TODO: we'll want to write the metadata into a stream, compress it, calculate the checksum on the fly
            // TODO: we'll probably also want to have a scratch buffer to assembly vectors with no allocation overhead and in a single iteration
            //       (a scratch buffer allows to record the vector in a single iteration and if it was unnecessary it doesn't need to be forwarded to the actual stream)

            var subStreamCount = mDecoderSections.Sum(x => x != null ? x.Streams.Length : 0);

            WriteToken(ArchiveMetadataToken.Header);

            if (mDecoderSections.Count > 0)
            {
                WriteToken(ArchiveMetadataToken.MainStreams);
                WritePackInfo();
                WriteUnpackInfo();
                WriteSubStreamsInfo();
                WriteToken(ArchiveMetadataToken.End);
            }

            if (subStreamCount > 0)
            {
                WriteToken(ArchiveMetadataToken.Files);
                WriteNumber(metadataCount);

                #region Types
                {
                    int emptyStreamCount = 0;

                    for (int i = 0; i < metadataCount; i++)
                    {
                        if (!metadata.HasStream(i))
                        {
                            emptyStreamCount++;
                        }
                    }

                    if (emptyStreamCount > 0)
                    {
                        WriteBitVectorWithHeader(ArchiveMetadataToken.EmptyStream,
                                                 Enumerable.Range(0, metadataCount)
                                                 .Select(x => !metadata.HasStream(x)),
                                                 metadataCount);

                        if (Enumerable.Range(0, metadataCount).Where(x => !metadata.HasStream(x)).Any(x => !metadata.IsDirectory(x)))
                        {
                            WriteBitVectorWithHeader(ArchiveMetadataToken.EmptyFile,
                                                     Enumerable.Range(0, metadataCount)
                                                     .Where(x => !metadata.HasStream(x))
                                                     .Select(x => !metadata.IsDirectory(x)),
                                                     emptyStreamCount);
                        }

                        if (Enumerable.Range(0, metadataCount).Where(x => !metadata.HasStream(x)).Any(x => metadata.IsDeleted(x)))
                        {
                            WriteBitVectorWithHeader(ArchiveMetadataToken.Anti,
                                                     Enumerable.Range(0, metadataCount)
                                                     .Where(x => !metadata.HasStream(x))
                                                     .Select(x => metadata.IsDeleted(x)),
                                                     emptyStreamCount);
                        }
                    }
                }
                #endregion

                #region Names
                {
                    bool hasNames = false;
                    int  nameSize = 1;

                    for (int i = 0; i < subStreamCount; i++)
                    {
                        var name = metadata.GetName(i);
                        if (!string.IsNullOrEmpty(name))
                        {
                            hasNames  = true;
                            nameSize += (name.Length + 1) * 2;
                        }
                        else
                        {
                            nameSize += 2;
                        }
                    }

                    if (hasNames)
                    {
                        WritePadding(2 + GetNumberSize(nameSize), 16);
                        WriteToken(ArchiveMetadataToken.Name);
                        WriteNumber(nameSize);
                        WriteByte(0);

                        System.Diagnostics.Debug.Assert((mArchiveStream.Position & 15) == 0);

                        for (int i = 0; i < subStreamCount; i++)
                        {
                            var name = metadata.GetName(i);
                            foreach (char ch in name)
                            {
                                WriteByte((byte)ch);
                                WriteByte((byte)(ch >> 8));
                            }
                            WriteByte(0);
                            WriteByte(0);
                        }
                    }
                }
                #endregion

                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetCreationDate(x)), ArchiveMetadataToken.CTime);
                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetLastAccessDate(x)), ArchiveMetadataToken.ATime);
                WriteDateVector(Enumerable.Range(0, metadataCount).Select(x => metadata.GetLastWriteDate(x)), ArchiveMetadataToken.MTime);
                // TODO: what does the start position mean? it doesn't seem to be what I thought it was.
                WriteUInt64Vector(Enumerable.Range(0, metadataCount).Select(x => default(ulong?)), ArchiveMetadataToken.StartPos);
                WriteUInt32Vector(Enumerable.Range(0, metadataCount).Select(x => {
                    var attr = metadata.GetAttributes(x);
                    return(attr.HasValue ? (uint)attr.Value : default(uint?));
                }), ArchiveMetadataToken.WinAttributes);

                WriteToken(ArchiveMetadataToken.End);
            }

            WriteToken(ArchiveMetadataToken.End);

            mMetadataLength = mArchiveStream.Position - mMetadataPosition;

            // TODO: this is only a temporary implementation; the checksum can be calculated on the fly in the final implementation
            {
                var buffer = new byte[0x1000];
                mArchiveStream.Position = mMetadataPosition;
                var checksum = CRC.kInitCRC;
                int offset   = 0;
                while (offset < mMetadataLength)
                {
                    var fetched = mArchiveStream.Read(buffer, 0, (int)Math.Min(mMetadataLength - offset, buffer.Length));
                    if (fetched <= 0 || fetched > mMetadataLength - offset)
                    {
                        throw new InternalFailureException();
                    }

                    offset  += fetched;
                    checksum = CRC.Update(checksum, buffer, 0, fetched);
                }
                mMetadataChecksum = new Checksum((int)CRC.Finish(checksum));
            }

            return(Utilities.CompletedTask);
        }