/// <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(); } }