Esempio n. 1
0
        /// <summary>
        /// Initializes the structure by reading metadata and content from disk
        /// </summary>
        /// <remarks>
        /// The file format is as follows:
        ///
        ///  int version - version for the format of the file
        ///  long metadataOffset - offset in the file to where the file metadata starts
        ///  Clean/Dirty - describes whether the file was closed cleanly. Used as a hint to avoid work when
        ///     not closed cleanly
        ///  byte[] fileContent - actual content of files. This is at the start so additional file and metadata can be
        ///     written to the end without rewriting the content at the beginning of the file
        ///  FileLocation[] - metadata of which files exist, their hashes, and where they are
        /// </remarks>
        private void Initialize()
        {
            Stopwatch sw = Stopwatch.StartNew();

            try
            {
                if (!File.Exists(m_path))
                {
                    TryCreateClean();

                    // Don't go through a many layers of exception handling if the file doesn't even exist.
                    return;
                }

                m_file   = FileUtilities.CreateFileStream(m_path, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete);
                m_reader = new BuildXLReader(false, m_file, leaveOpen: true);

                if (m_reader.ReadInt32() != Version)
                {
                    Tracing.Logger.Log.FileCombinerVersionIncremented(m_loggingContext, m_usage.ToString());
                    CreateClean();
                    return;
                }

                m_nextItemLocation = m_reader.ReadInt64();

                if (m_reader.ReadInt32() != Clean)
                {
                    Tracing.Logger.Log.FileCombinerFailedToInitialize(m_loggingContext, m_usage.ToString(), Strings.FileCombiner_FileNotClosedCleanly);
                    CreateClean();
                    return;
                }

                m_contentStartLocation = m_reader.BaseStream.Position;

                // Read out the file content in a single large read for best performance
                m_reader.BaseStream.Position = 0;
                Contract.Assume(m_maxReadChunkBytes > 0);
                int contentArrays = (int)((m_nextItemLocation + (m_maxReadChunkBytes - 1)) / m_maxReadChunkBytes);
                Contract.Assert(contentArrays > 0);
                m_content = new byte[contentArrays][];
                var bytesRemaining = m_nextItemLocation;
                for (int i = 0; i < m_content.Length; i++)
                {
                    var chunkSize = (int)Math.Min(bytesRemaining, m_maxReadChunkBytes);
                    m_content[i]    = m_reader.ReadBytes(chunkSize);
                    bytesRemaining -= chunkSize;
                }

                Contract.Assert(bytesRemaining == 0);

                // read out the metadata
                Contract.Assert(m_reader.BaseStream.Position == m_nextItemLocation);

                int count = 0;

                // If the file combiner is empty, the end of the stream may have been reached.
                if (m_reader.BaseStream.Position < m_reader.BaseStream.Length)
                {
                    count = m_reader.ReadInt32Compact();
                }

                Contract.Assume(count >= 0);
                m_fileLocations = new FileLocation[count];
                long lastOffset = 0;
                for (int i = 0; i < count; i++)
                {
                    FileLocation fileLocation = FileLocation.Deserialize(m_reader);
                    if (fileLocation.Offset < lastOffset)
                    {
                        throw new BuildXLException("File location offsets should be increasing");
                    }

                    lastOffset = fileLocation.Offset;

                    m_fileLocations[i] = fileLocation;

                    // If the same path is encountered more than once, always overwrite with the most recently added one
                    m_filesByPath.AddOrUpdate(fileLocation.Path, i, (oldPath, oldInt) => i);
                }

                // Initialize the writer and return success
                m_writer                     = new BuildXLWriter(false, m_file, true, false);
                m_stats.BeginCount           = count;
                m_stats.InitializationTimeMs = (int)sw.ElapsedMilliseconds;
            }
            catch (Exception ex)
            {
                if (File.Exists(m_path))
                {
                    // Only log the warning if the file previously existed. Otherwise we just failed to open it which
                    // isn't an error case
#pragma warning disable EPC12 // Suspicious exception handling: only Message property is observed in exception block.
                    Tracing.Logger.Log.FileCombinerFailedToInitialize(m_loggingContext, m_usage.ToString(), ex.Message);
#pragma warning restore EPC12 // Suspicious exception handling: only Message property is observed in exception block.
                }

                TryCreateClean();
            }
        }