public void ReadHeader(CancellationToken ct) { ct.ThrowIfCancellationRequested(); // minimum header prolog size is 64 but most will only a single page // we can preallocate a full page, and we will resize it later if needed var reader = m_file.CreateReader(0, SnapshotFormat.HEADER_METADATA_BYTES); // "PNDB" var signature = reader.ReadFixed32(); // v1.0 uint major = reader.ReadFixed16(); uint minor = reader.ReadFixed16(); m_version = new Version((int)major, (int)minor); // FLAGS m_dbFlags = (SnapshotFormat.Flags)reader.ReadFixed64(); // Database ID m_uid = new Uuid128(reader.ReadBytes(16).GetBytes()); // Database Version m_sequence = reader.ReadFixed64(); // Number of items in the database m_itemCount = checked ((long)reader.ReadFixed64()); // Database Timestamp m_timestamp = reader.ReadFixed64(); // Page Size m_pageSize = reader.ReadFixed32(); // Header Size m_headerSize = reader.ReadFixed32(); Contract.Assert(!reader.HasMore); #region Sanity checks // Signature if (signature != SnapshotFormat.HEADER_MAGIC_NUMBER) { throw ParseError("Invalid magic number"); } // Version if (m_version.Major != 1) { throw ParseError("Unsupported file version (major)"); } if (m_version.Minor > 0) { throw ParseError("Unsupported file version (minor)"); } // Flags // Page Size if (m_pageSize != UnmanagedHelpers.NextPowerOfTwo(m_pageSize)) { throw ParseError("Page size ({0}) is not a power of two", m_pageSize); } if (m_pageSize < SnapshotFormat.HEADER_METADATA_BYTES) { throw ParseError("Page size ({0}) is too small", m_pageSize); } if (m_pageSize > 1 << 20) { throw ParseError("Page size ({0}) is too big", m_pageSize); } // Header Size if (m_headerSize < 64 + 4 + 4) { throw ParseError("Header size ({0}) is too small", m_headerSize); } if (m_headerSize > m_file.Length) { throw ParseError("Header size is bigger than the file itself ({0} < {1})", m_headerSize, m_file.Length); } if (m_headerSize > 1 << 10) { throw ParseError("Header size ({0}) exceeds the maximum allowed size", m_headerSize); } #endregion // we know the page size and header size, read the rest... // read the rest reader = m_file.CreateReader(0, m_headerSize); reader.Skip(SnapshotFormat.HEADER_METADATA_BYTES); // parse the attributes Contract.Assert(reader.Offset == SnapshotFormat.HEADER_METADATA_BYTES); var attributeCount = checked ((int)reader.ReadFixed32()); if (attributeCount < 0 || attributeCount > 1024) { throw ParseError("Attributes count is invalid"); } var attributes = new Dictionary <string, IFdbTuple>(attributeCount); for (int i = 0; i < attributeCount; i++) { var name = reader.ReadVarbytes().ToSlice(); //TODO: max size ? if (name.IsNullOrEmpty) { throw ParseError("Header attribute name is empty"); } var data = reader.ReadVarbytes().ToSlice(); //TODO: max size + have a small scratch pad buffer for these ? var value = FdbTuple.Unpack(data); attributes.Add(name.ToUnicode(), value); } m_attributes = attributes; // read the header en marker var marker = reader.ReadFixed32(); if (marker != uint.MaxValue) { throw ParseError("Header end marker is invalid"); } // verify the header checksum uint actualHeaderChecksum = SnapshotFormat.ComputeChecksum(reader.Base, reader.Offset); uint headerChecksum = reader.ReadFixed32(); m_headerChecksum = headerChecksum; if (headerChecksum != actualHeaderChecksum) { throw ParseError("The header checksum does not match ({0} != {1}). This may be an indication of data corruption", headerChecksum, actualHeaderChecksum); } m_dataStart = RoundUp(m_headerSize, m_pageSize); m_hasHeader = true; }