public void Test_Uuid_NewUuid()
        {
            var uuid = Uuid128.NewUuid();

            Assert.That(uuid, Is.Not.EqualTo(Uuid128.Empty));
            Assert.That(uuid.ToGuid().ToString(), Is.EqualTo(uuid.ToString()));
        }
        public void Test_Uuid_Timestamp_And_ClockSequence()
        {
            DateTime now = DateTime.UtcNow;

            // UUID V1 : 60-bit timestamp, in 100-ns ticks since 1582-10-15T00:00:00.000

            // note: this uuid was generated in Python as 'uuid.uuid1(None, 12345)' on the 2013-09-09 at 14:33:50 GMT+2
            var uuid = Uuid128.Parse("14895400-194c-11e3-b039-1803deadb33f");

            Assert.That(uuid.Timestamp, Is.EqualTo(135980228304000000L));
            Assert.That(uuid.ClockSequence, Is.EqualTo(12345));
            Assert.That(uuid.Node, Is.EqualTo(0x1803deadb33f));             // no, this is not my real mac address !

            // the Timestamp should be roughly equal to the current UTC time (note: epoch is 1582-10-15T00:00:00.000)
            var epoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc);

            Assert.That(epoch.AddTicks(uuid.Timestamp).ToString("O"), Is.EqualTo("2013-09-09T12:33:50.4000000Z"));

            // UUID V3 : MD5 hash of the name

            //note: this uuid was generated in Python as 'uuid.uuid3(uuid.NAMESPACE_DNS, 'foundationdb.com')'
            uuid = Uuid128.Parse("4b1ddea9-d4d0-39a0-82d8-9d53e2c42a3d");
            Assert.That(uuid.Timestamp, Is.EqualTo(0x9A0D4D04B1DDEA9L));
            Assert.That(uuid.ClockSequence, Is.EqualTo(728));
            Assert.That(uuid.Node, Is.EqualTo(0x9D53E2C42A3D));

            // UUID V5 : SHA1 hash of the name

            //note: this uuid was generated in Python as 'uuid.uuid5(uuid.NAMESPACE_DNS, 'foundationdb.com')'
            uuid = Uuid128.Parse("e449df19-a87d-5410-aaab-d5870625c6b7");
            Assert.That(uuid.Timestamp, Is.EqualTo(0x410a87de449df19L));
            Assert.That(uuid.ClockSequence, Is.EqualTo(10923));
            Assert.That(uuid.Node, Is.EqualTo(0xD5870625C6B7));
        }
        public void Test_Uuid_Ordered()
        {
            const int N = 1000;

            // create a a list of random ids
            var source = new List <Uuid128>(N);

            for (int i = 0; i < N; i++)
            {
                source.Add(Uuid128.NewUuid());
            }

            // sort them by their string literals
            var literals = source.Select(id => id.ToString()).ToList();

            literals.Sort();

            // sort them by their byte representation
            var bytes = source.Select(id => id.ToSlice()).ToList();

            bytes.Sort();

            // now sort the Uuid themselves
            source.Sort();

            // they all should be in the same order
            for (int i = 0; i < N; i++)
            {
                Assert.That(literals[i], Is.EqualTo(source[i].ToString()));
                Assert.That(bytes[i], Is.EqualTo(source[i].ToSlice()));
            }
        }
        public void Test_Uuid_Version()
        {
            //note: these UUIDs are from http://docs.python.org/2/library/uuid.html

            Assert.That(Uuid128.Parse("a8098c1a-f86e-11da-bd1a-00112444be1e").Version, Is.EqualTo(1));
            Assert.That(Uuid128.Parse("6fa459ea-ee8a-3ca4-894e-db77e160355e").Version, Is.EqualTo(3));
            Assert.That(Uuid128.Parse("16fd2706-8baf-433b-82eb-8c7fada847da").Version, Is.EqualTo(4));
            Assert.That(Uuid128.Parse("886313e1-3b8a-5372-9b90-0c9aee199e5d").Version, Is.EqualTo(5));
        }
Beispiel #5
0
        public void Test_FdbKey_PrettyPrint()
        {
            // verify that the pretty printing of keys produce a user friendly output

            Assert.That(FdbKey.Dump(Slice.Nil), Is.EqualTo("<null>"));
            Assert.That(FdbKey.Dump(Slice.Empty), Is.EqualTo("<empty>"));

            Assert.That(FdbKey.Dump(Slice.FromByte(0)), Is.EqualTo("<00>"));
            Assert.That(FdbKey.Dump(Slice.FromByte(255)), Is.EqualTo("<FF>"));

            Assert.That(FdbKey.Dump(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }.AsSlice()), Is.EqualTo("<00><01><02><03><04><05><06><07>"));
            Assert.That(FdbKey.Dump(new byte[] { 255, 254, 253, 252, 251, 250, 249, 248 }.AsSlice()), Is.EqualTo("<FF><FE><FD><FC><FB><FA><F9><F8>"));

            Assert.That(FdbKey.Dump(Slice.FromString("hello")), Is.EqualTo("hello"));
            Assert.That(FdbKey.Dump(Slice.FromString("héllø")), Is.EqualTo("h<C3><A9>ll<C3><B8>"));

            // tuples should be decoded properly

            Assert.That(FdbKey.Dump(TuPack.EncodeKey(123)), Is.EqualTo("(123,)"), "Singleton tuples should end with a ','");
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(Slice.FromByteString("hello"))), Is.EqualTo("(`hello`,)"), "ASCII strings should use single back quotes");
            Assert.That(FdbKey.Dump(TuPack.EncodeKey("héllø")), Is.EqualTo("(\"héllø\",)"), "Unicode strings should use double quotes");
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(new byte[] { 1, 2, 3 }.AsSlice())), Is.EqualTo("(`<01><02><03>`,)"));
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(123, 456)), Is.EqualTo("(123, 456)"), "Elements should be separated with a space, and not end up with ','");
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(default(object), true, false)), Is.EqualTo("(null, true, false)"), "Booleans should be displayed as numbers, and null should be in lowercase");             //note: even though it's tempting to using Python's "Nil", it's not very ".NETty"
            //note: the string representation of double is not identical between NetFx and .NET Core! So we cannot used a constant literal here
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(1.0d, Math.PI, Math.E)), Is.EqualTo("(1, " + Math.PI.ToString("R", CultureInfo.InvariantCulture) + ", " + Math.E.ToString("R", CultureInfo.InvariantCulture) + ")"), "Doubles should used dot and have full precision (17 digits)");
            Assert.That(FdbKey.Dump(TuPack.EncodeKey(1.0f, (float)Math.PI, (float)Math.E)), Is.EqualTo("(1, " + ((float)Math.PI).ToString("R", CultureInfo.InvariantCulture) + ", " + ((float)Math.E).ToString("R", CultureInfo.InvariantCulture) + ")"), "Singles should used dot and have full precision (10 digits)");
            var guid = Guid.NewGuid();

            Assert.That(FdbKey.Dump(TuPack.EncodeKey(guid)), Is.EqualTo($"({guid:B},)"), "GUIDs should be displayed as a string literal, surrounded by {{...}}, and without quotes");
            var uuid128 = Uuid128.NewUuid();

            Assert.That(FdbKey.Dump(TuPack.EncodeKey(uuid128)), Is.EqualTo($"({uuid128:B},)"), "Uuid128s should be displayed as a string literal, surrounded by {{...}}, and without quotes");
            var uuid64 = Uuid64.NewUuid();

            Assert.That(FdbKey.Dump(TuPack.EncodeKey(uuid64)), Is.EqualTo($"({uuid64:B},)"), "Uuid64s should be displayed as a string literal, surrounded by {{...}}, and without quotes");

            // ranges should be decoded when possible
            var key = TuPack.ToRange(STuple.Create("hello"));

            // "<02>hello<00><00>" .. "<02>hello<00><FF>"
            Assert.That(FdbKey.PrettyPrint(key.Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(\"hello\",).<00>"));
            Assert.That(FdbKey.PrettyPrint(key.End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(\"hello\",).<FF>"));

            key = KeyRange.StartsWith(TuPack.EncodeKey("hello"));
            // "<02>hello<00>" .. "<02>hello<01>"
            Assert.That(FdbKey.PrettyPrint(key.Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(\"hello\",)"));
            Assert.That(FdbKey.PrettyPrint(key.End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(\"hello\",) + 1"));

            var t = TuPack.EncodeKey(123);

            Assert.That(FdbKey.PrettyPrint(t, FdbKey.PrettyPrintMode.Single), Is.EqualTo("(123,)"));
            Assert.That(FdbKey.PrettyPrint(TuPack.ToRange(t).Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(123,).<00>"));
            Assert.That(FdbKey.PrettyPrint(TuPack.ToRange(t).End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(123,).<FF>"));
        }
 /// <summary>Writes a RFC 4122 encoded 128-bit UUID</summary>
 public static void WriteUuid128(ref TupleWriter writer, Uuid128 value)
 {
     writer.Output.EnsureBytes(17);
     writer.Output.UnsafeWriteByte(FdbTupleTypes.Uuid128);
     unsafe
     {
         byte *ptr = stackalloc byte[16];
         value.WriteTo(ptr);
         writer.Output.UnsafeWriteBytes(ptr, 16);
     }
 }
Beispiel #7
0
        public void Test_FdbKey_PrettyPrint()
        {
            // verify that the pretty printing of keys produce a user friendly output

            Assert.That(FdbKey.Dump(Slice.Nil), Is.EqualTo("<null>"));
            Assert.That(FdbKey.Dump(Slice.Empty), Is.EqualTo("<empty>"));

            Assert.That(FdbKey.Dump(Slice.FromByte(0)), Is.EqualTo("<00>"));
            Assert.That(FdbKey.Dump(Slice.FromByte(255)), Is.EqualTo("<FF>"));

            Assert.That(FdbKey.Dump(Slice.Create(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 })), Is.EqualTo("<00><01><02><03><04><05><06><07>"));
            Assert.That(FdbKey.Dump(Slice.Create(new byte[] { 255, 254, 253, 252, 251, 250, 249, 248 })), Is.EqualTo("<FF><FE><FD><FC><FB><FA><F9><F8>"));

            Assert.That(FdbKey.Dump(Slice.FromString("hello")), Is.EqualTo("hello"));
            Assert.That(FdbKey.Dump(Slice.FromString("héllø")), Is.EqualTo("h<C3><A9>ll<C3><B8>"));

            // tuples should be decoded properly

            Assert.That(FdbKey.Dump(FdbTuple.Pack(123)), Is.EqualTo("(123,)"), "Singleton tuples should end with a ','");
            Assert.That(FdbKey.Dump(FdbTuple.Pack(Slice.FromAscii("hello"))), Is.EqualTo("('hello',)"), "ASCII strings should use single quotes");
            Assert.That(FdbKey.Dump(FdbTuple.Pack("héllø")), Is.EqualTo("(\"héllø\",)"), "Unicode strings should use double quotes");
            Assert.That(FdbKey.Dump(FdbTuple.Pack(Slice.Create(new byte[] { 1, 2, 3 }))), Is.EqualTo("(<01 02 03>,)"));
            Assert.That(FdbKey.Dump(FdbTuple.Pack(123, 456)), Is.EqualTo("(123, 456)"), "Elements should be separated with a space, and not end up with ','");
            Assert.That(FdbKey.Dump(FdbTuple.Pack(true, false, default(object))), Is.EqualTo("(1, 0, null)"), "Booleans should be displayed as numbers, and null should be in lowercase");             //note: even though it's tempting to using Python's "Nil", it's not very ".NETty"
            Assert.That(FdbKey.Dump(FdbTuple.Pack(1.0d, Math.PI, Math.E)), Is.EqualTo("(1, 3.1415926535897931, 2.7182818284590451)"), "Doubles should used dot and have full precision (17 digits)");
            Assert.That(FdbKey.Dump(FdbTuple.Pack(1.0f, (float)Math.PI, (float)Math.E)), Is.EqualTo("(1, 3.14159274, 2.71828175)"), "Singles should used dot and have full precision (10 digits)");
            var guid = Guid.NewGuid();

            Assert.That(FdbKey.Dump(FdbTuple.Pack(guid)), Is.EqualTo(String.Format("({0},)", guid.ToString("D"))), "GUIDs should be displayed as a string literal, without quotes");
            var uuid128 = Uuid128.NewUuid();

            Assert.That(FdbKey.Dump(FdbTuple.Pack(uuid128)), Is.EqualTo(String.Format("({0},)", uuid128.ToString("D"))), "Uuid128s should be displayed as a string literal, without quotes");
            var uuid64 = Uuid64.NewUuid();

            Assert.That(FdbKey.Dump(FdbTuple.Pack(uuid64)), Is.EqualTo(String.Format("({0},)", uuid64.ToString("D"))), "Uuid64s should be displayed as a string literal, without quotes");

            // ranges should be decoded when possible
            var key = FdbTuple.ToRange(FdbTuple.Create("hello"));

            // "<02>hello<00><00>" .. "<02>hello<00><FF>"
            Assert.That(FdbKey.PrettyPrint(key.Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(\"hello\",).<00>"));
            Assert.That(FdbKey.PrettyPrint(key.End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(\"hello\",).<FF>"));

            key = FdbKeyRange.StartsWith(FdbTuple.Pack("hello"));
            // "<02>hello<00>" .. "<02>hello<01>"
            Assert.That(FdbKey.PrettyPrint(key.Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(\"hello\",)"));
            Assert.That(FdbKey.PrettyPrint(key.End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(\"hello\",) + 1"));

            var t = FdbTuple.Pack(123);

            Assert.That(FdbKey.PrettyPrint(t, FdbKey.PrettyPrintMode.Single), Is.EqualTo("(123,)"));
            Assert.That(FdbKey.PrettyPrint(FdbTuple.ToRange(t).Begin, FdbKey.PrettyPrintMode.Begin), Is.EqualTo("(123,).<00>"));
            Assert.That(FdbKey.PrettyPrint(FdbTuple.ToRange(t).End, FdbKey.PrettyPrintMode.End), Is.EqualTo("(123,).<FF>"));
        }
        public void Test_Uuid_ToSlice()
        {
            var uuid = Uuid128.NewUuid();

            Assert.That(uuid.ToSlice().Count, Is.EqualTo(16));
            Assert.That(uuid.ToSlice().Offset, Is.GreaterThanOrEqualTo(0));
            Assert.That(uuid.ToSlice().Array, Is.Not.Null);
            Assert.That(uuid.ToSlice().Array.Length, Is.GreaterThanOrEqualTo(16));
            Assert.That(uuid.ToSlice(), Is.EqualTo(uuid.ToByteArray().AsSlice()));
            Assert.That(uuid.ToSlice().GetBytes(), Is.EqualTo(uuid.ToByteArray()));
        }
		public void Test_Uuid_From_Bytes()
		{
			Uuid128 uuid;

			uuid = new Uuid128(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
			Assert.That(uuid.ToString(), Is.EqualTo("00010203-0405-0607-0809-0a0b0c0d0e0f"));
			Assert.That(uuid.ToByteArray(), Is.EqualTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }));

			uuid = new Uuid128(new byte[16]);
			Assert.That(uuid.ToString(), Is.EqualTo("00000000-0000-0000-0000-000000000000"));
		}
        public void Test_Uuid_From_Bytes()
        {
            Uuid128 uuid;

            uuid = new Uuid128(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
            Assert.That(uuid.ToString(), Is.EqualTo("00010203-0405-0607-0809-0a0b0c0d0e0f"));
            Assert.That(uuid.ToByteArray(), Is.EqualTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }));

            uuid = new Uuid128(new byte[16]);
            Assert.That(uuid.ToString(), Is.EqualTo("00000000-0000-0000-0000-000000000000"));
        }
        /// <summary>Parse a tuple segment containing a 128-bit GUID</summary>
        public static Guid ParseGuid(Slice slice)
        {
            Contract.Requires(slice.HasValue && slice[0] == FdbTupleTypes.Uuid128);

            if (slice.Count != 17)
            {
                throw new FormatException("Slice has invalid size for a GUID");
            }

            // We store them in RFC 4122 under the hood, so we need to reverse them to the MS format
            return(Uuid128.Convert(new Slice(slice.Array, slice.Offset + 1, 16)));
        }
        public void Test_Uuid_Parse()
        {
            Uuid128 uuid;

            uuid = Uuid128.Parse("00010203-0405-0607-0809-0a0b0c0d0e0f");
            Assert.That(uuid.ToString(), Is.EqualTo("00010203-0405-0607-0809-0a0b0c0d0e0f"));
            Assert.That(uuid.ToByteArray(), Is.EqualTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }));

            uuid = Uuid128.Parse("{00010203-0405-0607-0809-0a0b0c0d0e0f}");
            Assert.That(uuid.ToString(), Is.EqualTo("00010203-0405-0607-0809-0a0b0c0d0e0f"));
            Assert.That(uuid.ToByteArray(), Is.EqualTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }));
        }
        /// <summary>Writes a RFC 4122 encoded 16-byte Microsoft GUID</summary>
        public static void WriteGuid(ref TupleWriter writer, Guid value)
        {
            writer.Output.EnsureBytes(17);
            writer.Output.UnsafeWriteByte(FdbTupleTypes.Uuid128);
            unsafe
            {
                // UUIDs are stored using the RFC 4122 standard, so we need to swap some parts of the System.Guid

                byte *ptr = stackalloc byte[16];
                Uuid128.Write(value, ptr);
                writer.Output.UnsafeWriteBytes(ptr, 16);
            }
        }
		public void Test_Uuid_Vs_Guid()
		{
			Guid guid = Guid.NewGuid();

			var uuid = new Uuid128(guid);
			Assert.That(uuid.ToString(), Is.EqualTo(guid.ToString()));
			Assert.That(uuid.ToGuid(), Is.EqualTo(guid));
			Assert.That((Guid)uuid, Is.EqualTo(guid));
			Assert.That((Uuid128)guid, Is.EqualTo(uuid));
			Assert.That(Uuid128.Parse(guid.ToString()), Is.EqualTo(uuid));
			Assert.That(uuid.Equals(guid), Is.True);
			Assert.That(uuid.Equals((object)guid), Is.True);
			Assert.That(uuid == guid, Is.True);
			Assert.That(guid == uuid, Is.True);

			Assert.That(uuid.Equals(Guid.NewGuid()), Is.False);
			Assert.That(uuid == Guid.NewGuid(), Is.False);
			Assert.That(Guid.NewGuid() == uuid, Is.False);
		}
        public void Test_Uuid_Vs_Guid()
        {
            Guid guid = Guid.NewGuid();

            var uuid = new Uuid128(guid);

            Assert.That(uuid.ToString(), Is.EqualTo(guid.ToString()));
            Assert.That(uuid.ToGuid(), Is.EqualTo(guid));
            Assert.That((Guid)uuid, Is.EqualTo(guid));
            Assert.That((Uuid128)guid, Is.EqualTo(uuid));
            Assert.That(Uuid128.Parse(guid.ToString()), Is.EqualTo(uuid));
            Assert.That(uuid.Equals(guid), Is.True);
            Assert.That(uuid.Equals((object)guid), Is.True);
            Assert.That(uuid == guid, Is.True);
            Assert.That(guid == uuid, Is.True);

            Assert.That(uuid.Equals(Guid.NewGuid()), Is.False);
            Assert.That(uuid == Guid.NewGuid(), Is.False);
            Assert.That(Guid.NewGuid() == uuid, Is.False);
        }
        public void Test_Uuid_Increment()
        {
            var @base = Uuid128.Parse("6be5d394-03a6-42ab-aac2-89b7d9312402");

            Log(@base);
            //DumpHexa(@base.ToByteArray());

            {             // +1
                var uuid = @base.Increment(1);
                Log(uuid);
                //DumpHexa(uuid.ToByteArray());
                Assert.That(uuid.ToString(), Is.EqualTo("6be5d394-03a6-42ab-aac2-89b7d9312403"));
            }
            {             // +256
                var uuid = @base.Increment(256);
                Log(uuid);
                //DumpHexa(uuid.ToByteArray());
                Assert.That(uuid.ToString(), Is.EqualTo("6be5d394-03a6-42ab-aac2-89b7d9312502"));
            }
            {                                                     // almost overflow (low)
                var uuid = @base.Increment(0x553D764826CEDBFDUL); // delta nécessaire pour avoir 0xFFFFFFFFFFFFFFFF a la fin
                Log(uuid);
                //DumpHexa(uuid.ToByteArray());
                Assert.That(uuid.ToString(), Is.EqualTo("6be5d394-03a6-42ab-ffff-ffffffffffff"));
            }
            {                                                     // overflow (low)
                var uuid = @base.Increment(0x553D764826CEDBFEUL); // encore 1 de plus pour trigger l'overflow
                Log(uuid);
                //DumpHexa(uuid.ToByteArray());
                Assert.That(uuid.ToString(), Is.EqualTo("6be5d394-03a6-42ac-0000-000000000000"));
            }
            {             // overflow (cascade)
                var uuid = Uuid128.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff").Increment(1);
                Log(uuid);
                //DumpHexa(uuid.ToByteArray());
                Assert.That(uuid.ToString(), Is.EqualTo("00000000-0000-0000-0000-000000000000"));
            }
        }
        public void Test_Uuid_Equality()
        {
            Assert.That(Uuid128.Empty.Equals(new Uuid128(new byte[16])), Is.True);
            Assert.That(Uuid128.Empty.Equals(Uuid128.NewUuid()), Is.False);

            var uuid1 = Uuid128.NewUuid();
            var uuid2 = Uuid128.NewUuid();

            Assert.That(uuid1.Equals(uuid1), Is.True);
            Assert.That(uuid2.Equals(uuid2), Is.True);
            Assert.That(uuid1.Equals(uuid2), Is.False);
            Assert.That(uuid2.Equals(uuid1), Is.False);

            Assert.That(uuid1.Equals((object)uuid1), Is.True);
            Assert.That(uuid2.Equals((object)uuid2), Is.True);
            Assert.That(uuid1.Equals((object)uuid2), Is.False);
            Assert.That(uuid2.Equals((object)uuid1), Is.False);

            var uuid1b = Uuid128.Parse(uuid1.ToString());

            Assert.That(uuid1b.Equals(uuid1), Is.True);
            Assert.That(uuid1b.Equals((object)uuid1), Is.True);
        }
        public void ReadJumpTable(CancellationToken ct)
        {
            ct.ThrowIfCancellationRequested();

            if (!m_hasHeader)
            {
                throw new InvalidOperationException("Cannot read the Jump Table without reading the Header first!");
            }

            // an empty database will have at least 2 pages: the header and the JT
            if (m_file.Length < checked (m_pageSize << 1))
            {
                throw ParseError("File size ({0}) is too small to be a valid snapshot", m_file.Length);
            }

            // the jumptable is always in the last page of the file and is expected to fit nicely
            // > file size MUST be evenly divible by page size
            // > then JT offset will be file.Length - pageSize
            if (m_file.Length % m_pageSize != 0)
            {
                throw ParseError("The file size ({0}) is not a multiple of the page size ({1}), which may be a symptom of truncation", m_file.Length, m_pageSize);
            }

            var jumpTableStart = m_file.Length - m_pageSize;

            Contract.Assert(jumpTableStart % m_pageSize == 0);
            m_dataEnd = jumpTableStart;

            var reader = m_file.CreateReader(jumpTableStart, m_pageSize);

            // "JMPT"
            var signature = reader.ReadFixed32();
            // Page Size (repeated)
            var pageSizeRepeated = (int)reader.ReadFixed32();
            // Sequence Number (repeated)
            var sequenceRepeated = reader.ReadFixed64();
            // Database ID (repeated)
            var uidRepeated = new Uuid128(reader.ReadBytes(16).GetBytes());
            // Header CRC (repeated)
            var headerChecksumRepeated = reader.ReadFixed32();

            // Sanity checks

            if (signature != SnapshotFormat.JUMP_TABLE_MAGIC_NUMBER)
            {
                throw ParseError("Last page does not appear to be the Jump Table");
            }
            if (pageSizeRepeated != m_pageSize)
            {
                throw ParseError("Page size in Jump Table does not match the header value");
            }
            if (sequenceRepeated != m_sequence)
            {
                throw ParseError("Sequence in Jump Table does not match the header value");
            }
            if (uidRepeated != m_uid)
            {
                throw ParseError("Database ID in Jump Table does not match the header value");
            }
            if (headerChecksumRepeated != m_headerChecksum)
            {
                throw ParseError("Database ID in Jump Table does not match the header value");
            }

            // read the table itself
            int levels = (int)reader.ReadFixed32();

            if (levels < 0 || levels > 32)
            {
                throw ParseError("The number of levels in the snapshot does not appear to be valid");
            }

            var table = new LevelAddress[levels];

            for (int level = 0; level < levels; level++)
            {
                ulong offset = reader.ReadFixed64();
                ulong size   = reader.ReadFixed64();

                // Offset and Size cannot be negative
                // Empty levels (size == 0) must have a zero offset
                // Non empty levels (size > 0) must have a non zero offset that is greater than the headerSize
                if ((size == 0 && offset != 0) || (size > 0 && offset < m_dataStart))
                {
                    throw ParseError("Level in Jump Table has invalid size ({0}) or offset ({1})", size, offset);
                }
                if (checked (offset + size) > m_dataEnd)
                {
                    throw ParseError("Level in Jump Table would end after the end of the file");
                }

                table[level].Offset     = offset;
                table[level].Size       = size;
                table[level].PaddedSize = RoundUp(size, m_pageSize);
            }

            // end attributes
            uint attributeCount = reader.ReadFixed32();

            if (attributeCount != 0)
            {
                throw new NotImplementedException("Footer attributes not yet implemented!");
            }

            // end marker
            if (reader.ReadFixed32() != uint.MaxValue)
            {
                throw ParseError("Jump Table end marker not found");
            }

            // checksum
            uint actualChecksum = SnapshotFormat.ComputeChecksum(reader.Base, reader.Offset);
            uint checksum       = reader.ReadFixed32();

            if (actualChecksum != checksum)
            {
                throw ParseError("Jump Table checksum does not match ({0} != {1}). This may be an indication of data corruption", checksum, actualChecksum);
            }

            m_jumpTable    = table;
            m_levels       = levels;
            m_hasJumpTable = true;
        }
Beispiel #19
0
        /// <summary>Register all the default converters</summary>
        private static void RegisterDefaultConverters()
        {
            //TODO: there is too much generic type combinations! need to refactor this ...

            RegisterUnsafe <bool, Slice>((value) => Slice.FromByte(value ? (byte)1 : default(byte)));
            RegisterUnsafe <bool, byte[]>((value) => Slice.FromByte(value ? (byte)1 : default(byte)).GetBytes());
            RegisterUnsafe <bool, string>((value) => value ? "true" : "false");
            RegisterUnsafe <bool, sbyte>((value) => value ? (sbyte)1 : default(sbyte));
            RegisterUnsafe <bool, byte>((value) => value ? (byte)1 : default(byte));
            RegisterUnsafe <bool, short>((value) => value ? (short)1 : default(short));
            RegisterUnsafe <bool, ushort>((value) => value ? (ushort)1 : default(ushort));
            RegisterUnsafe <bool, int>((value) => value ? 1 : default(int));
            RegisterUnsafe <bool, uint>((value) => value ? 1U : default(uint));
            RegisterUnsafe <bool, long>((value) => value ? 1L : default(long));
            RegisterUnsafe <bool, ulong>((value) => value ? 1UL : default(ulong));
            RegisterUnsafe <bool, double>((value) => value ? 1.0d : default(double));
            RegisterUnsafe <bool, float>((value) => value ? 1.0f : default(float));
            RegisterUnsafe <bool, decimal>((value) => value ? 1m : default(decimal));

            RegisterUnsafe <int, Slice>(Slice.FromInt32);
            RegisterUnsafe <int, byte[]>((value) => Slice.FromInt32(value).GetBytes());
            RegisterUnsafe <int, string>(StringConverters.ToString);
            RegisterUnsafe <int, bool>((value) => value != 0);
            RegisterUnsafe <int, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <int, byte>((value) => checked ((byte)value));
            RegisterUnsafe <int, short>((value) => checked ((short)value));
            RegisterUnsafe <int, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <int, uint>((value) => (uint)value);
            RegisterUnsafe <int, long>((value) => value);
            RegisterUnsafe <int, ulong>((value) => (ulong)value);
            RegisterUnsafe <int, double>((value) => value);
            RegisterUnsafe <int, float>((value) => value);            // possible loss of precision
            RegisterUnsafe <int, decimal>((value) => value);

            RegisterUnsafe <uint, Slice>(Slice.FromUInt32);
            RegisterUnsafe <uint, byte[]>((value) => Slice.FromUInt32(value).GetBytes());
            RegisterUnsafe <uint, string>(StringConverters.ToString);
            RegisterUnsafe <uint, bool>((value) => value != 0);
            RegisterUnsafe <uint, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <uint, byte>((value) => checked ((byte)value));
            RegisterUnsafe <uint, short>((value) => checked ((short)value));
            RegisterUnsafe <uint, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <uint, int>((value) => (int)value);
            RegisterUnsafe <uint, long>((value) => value);
            RegisterUnsafe <uint, ulong>((value) => value);
            RegisterUnsafe <uint, double>((value) => value);
            RegisterUnsafe <uint, float>((value) => value);            // possible loss of precision
            RegisterUnsafe <uint, decimal>((value) => value);

            RegisterUnsafe <long, Slice>(Slice.FromInt64);
            RegisterUnsafe <long, byte[]>((value) => Slice.FromInt64(value).GetBytes());
            RegisterUnsafe <long, string>(StringConverters.ToString);
            RegisterUnsafe <long, bool>((value) => value != 0);
            RegisterUnsafe <long, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <long, byte>((value) => checked ((byte)value));
            RegisterUnsafe <long, short>((value) => checked ((short)value));
            RegisterUnsafe <long, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <long, int>((value) => checked ((int)value));
            RegisterUnsafe <long, uint>((value) => (uint)value);
            RegisterUnsafe <long, ulong>((value) => (ulong)value);
            RegisterUnsafe <long, double>((value) => value);           // possible loss of precision
            RegisterUnsafe <long, float>((value) => value);            // possible loss of precision
            RegisterUnsafe <long, TimeSpan>(TimeSpan.FromTicks);
            RegisterUnsafe <long, Uuid64>((value) => new Uuid64(value));
            RegisterUnsafe <long, System.Net.IPAddress>((value) => new System.Net.IPAddress(value));
            RegisterUnsafe <long, decimal>((value) => value);

            RegisterUnsafe <ulong, Slice>(Slice.FromUInt64);
            RegisterUnsafe <ulong, byte[]>((value) => Slice.FromUInt64(value).GetBytes());
            RegisterUnsafe <ulong, string>(StringConverters.ToString);
            RegisterUnsafe <ulong, bool>((value) => value != 0);
            RegisterUnsafe <ulong, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <ulong, byte>((value) => checked ((byte)value));
            RegisterUnsafe <ulong, short>((value) => checked ((short)value));
            RegisterUnsafe <ulong, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <ulong, int>((value) => checked ((int)value));
            RegisterUnsafe <ulong, uint>((value) => checked ((uint)value));
            RegisterUnsafe <ulong, long>((value) => checked ((long)value));
            RegisterUnsafe <ulong, double>((value) => value);           // possible loss of precision
            RegisterUnsafe <ulong, float>((value) => value);            // possible loss of precision
            RegisterUnsafe <ulong, Uuid64>((value) => new Uuid64(value));
            RegisterUnsafe <ulong, TimeSpan>((value) => TimeSpan.FromTicks(checked ((long)value)));
            RegisterUnsafe <ulong, decimal>((value) => value);

            RegisterUnsafe <short, Slice>(Slice.FromInt16);
            RegisterUnsafe <short, byte[]>((value) => Slice.FromInt16(value).GetBytes());
            RegisterUnsafe <short, string>((value) => StringConverters.ToString(value));
            RegisterUnsafe <short, bool>((value) => value != 0);
            RegisterUnsafe <short, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <short, byte>((value) => checked ((byte)value));
            RegisterUnsafe <short, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <short, int>((value) => value);
            RegisterUnsafe <short, uint>((value) => checked ((uint)value));
            RegisterUnsafe <short, long>((value) => value);
            RegisterUnsafe <short, ulong>((value) => checked ((ulong)value));
            RegisterUnsafe <short, double>((value) => value);
            RegisterUnsafe <short, float>((value) => value);
            RegisterUnsafe <short, decimal>((value) => value);

            RegisterUnsafe <ushort, Slice>(Slice.FromUInt16);
            RegisterUnsafe <ushort, byte[]>((value) => Slice.FromUInt16(value).GetBytes());
            RegisterUnsafe <ushort, string>((value) => StringConverters.ToString(value));
            RegisterUnsafe <ushort, bool>((value) => value != 0);
            RegisterUnsafe <ushort, byte>((value) => checked ((byte)value));
            RegisterUnsafe <ushort, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <ushort, short>((value) => checked ((short)value));
            RegisterUnsafe <ushort, int>((value) => value);
            RegisterUnsafe <ushort, uint>((value) => value);
            RegisterUnsafe <ushort, long>((value) => value);
            RegisterUnsafe <ushort, ulong>((value) => value);
            RegisterUnsafe <ushort, double>((value) => value);
            RegisterUnsafe <ushort, float>((value) => value);
            RegisterUnsafe <ushort, decimal>((value) => value);

            RegisterUnsafe <byte, Slice>(Slice.FromByte);
            RegisterUnsafe <byte, byte[]>((value) => Slice.FromByte(value).GetBytes());
            RegisterUnsafe <byte, string>((value) => StringConverters.ToString(value));
            RegisterUnsafe <byte, bool>((value) => value != 0);
            RegisterUnsafe <byte, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <byte, short>((value) => value);
            RegisterUnsafe <byte, ushort>((value) => value);
            RegisterUnsafe <byte, int>((value) => value);
            RegisterUnsafe <byte, uint>((value) => value);
            RegisterUnsafe <byte, long>((value) => value);
            RegisterUnsafe <byte, ulong>((value) => value);
            RegisterUnsafe <byte, double>((value) => value);
            RegisterUnsafe <byte, float>((value) => value);
            RegisterUnsafe <byte, decimal>((value) => value);

            RegisterUnsafe <sbyte, Slice>((value) => Slice.FromInt64(value));
            RegisterUnsafe <sbyte, byte[]>((value) => Slice.FromInt64(value).GetBytes());
            RegisterUnsafe <sbyte, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <sbyte, bool>((value) => value != 0);
            RegisterUnsafe <sbyte, byte>((value) => checked ((byte)value));
            RegisterUnsafe <sbyte, short>((value) => value);
            RegisterUnsafe <sbyte, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <sbyte, int>((value) => value);
            RegisterUnsafe <sbyte, uint>((value) => checked ((uint)value));
            RegisterUnsafe <sbyte, long>((value) => value);
            RegisterUnsafe <sbyte, ulong>((value) => checked ((ulong)value));
            RegisterUnsafe <sbyte, double>((value) => value);
            RegisterUnsafe <sbyte, float>((value) => value);
            RegisterUnsafe <sbyte, decimal>((value) => value);

            RegisterUnsafe <float, Slice>(Slice.FromSingle);
            RegisterUnsafe <float, byte[]>((value) => Slice.FromSingle(value).GetBytes());
            RegisterUnsafe <float, string>((value) => value.ToString("R", CultureInfo.InvariantCulture));
            RegisterUnsafe <float, bool>((value) => !(value == 0f || float.IsNaN(value)));
            RegisterUnsafe <float, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <float, byte>((value) => checked ((byte)value));
            RegisterUnsafe <float, short>((value) => checked ((short)value));
            RegisterUnsafe <float, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <float, int>((value) => checked ((int)value));
            RegisterUnsafe <float, uint>((value) => (uint)value);
            RegisterUnsafe <float, long>((value) => checked ((long)value));
            RegisterUnsafe <float, ulong>((value) => (ulong)value);
            RegisterUnsafe <float, double>((value) => value);
            RegisterUnsafe <float, decimal>((value) => (decimal)value);             // possible loss of precision

            RegisterUnsafe <double, Slice>((value) => Slice.FromDouble(value));
            RegisterUnsafe <double, byte[]>((value) => Slice.FromDouble(value).GetBytes());
            RegisterUnsafe <double, string>((value) => value.ToString("R", CultureInfo.InvariantCulture));
            RegisterUnsafe <double, bool>((value) => !(value == 0d || double.IsNaN(value)));
            RegisterUnsafe <double, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <double, byte>((value) => checked ((byte)value));
            RegisterUnsafe <double, short>((value) => checked ((short)value));
            RegisterUnsafe <double, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <double, int>((value) => checked ((int)value));
            RegisterUnsafe <double, uint>((value) => (uint)value);
            RegisterUnsafe <double, long>((value) => checked ((long)value));
            RegisterUnsafe <double, ulong>((value) => (ulong)value);
            RegisterUnsafe <double, float>((value) => (float)value);            // possible loss of precision
            RegisterUnsafe <double, decimal>((value) => (decimal)value);        // possible loss of precision

            RegisterUnsafe <string, Slice>((value) => Slice.FromString(value));
            RegisterUnsafe <string, byte[]>((value) => Slice.FromString(value).GetBytes());            //REVIEW: string=>byte[] use UTF-8, but byte[]=>string uses Base64 ?
            RegisterUnsafe <string, bool>((value) => !string.IsNullOrEmpty(value));
            RegisterUnsafe <string, sbyte>((value) => string.IsNullOrEmpty(value) ? default(sbyte) : sbyte.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, byte>((value) => string.IsNullOrEmpty(value) ? default(byte) : byte.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, short>((value) => string.IsNullOrEmpty(value) ? default(short) : short.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, ushort>((value) => string.IsNullOrEmpty(value) ? default(ushort) : ushort.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, int>((value) => string.IsNullOrEmpty(value) ? default(int) : int.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, uint>((value) => string.IsNullOrEmpty(value) ? default(uint) : uint.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, long>((value) => string.IsNullOrEmpty(value) ? default(long) : long.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, ulong>((value) => string.IsNullOrEmpty(value) ? default(ulong) : ulong.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, float>((value) => string.IsNullOrEmpty(value) ? default(float) : float.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, double>((value) => string.IsNullOrEmpty(value) ? default(double) : double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, decimal>((value) => string.IsNullOrEmpty(value) ? default(decimal) : decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, Guid>((value) => string.IsNullOrEmpty(value) ? default(Guid) : Guid.Parse(value));
            RegisterUnsafe <string, Uuid128>((value) => string.IsNullOrEmpty(value) ? default(Uuid128) : Uuid128.Parse(value));
            RegisterUnsafe <string, Uuid64>((value) => string.IsNullOrEmpty(value) ? default(Uuid64) : Uuid64.Parse(value));
            RegisterUnsafe <string, System.Net.IPAddress>((value) => string.IsNullOrEmpty(value) ? default(System.Net.IPAddress) : System.Net.IPAddress.Parse(value));

            RegisterUnsafe <byte[], Slice>((value) => value.AsSlice());
            RegisterUnsafe <byte[], string>((value) => value == null ? default(string) : value.Length == 0 ? string.Empty : System.Convert.ToBase64String(value));            //REVIEW: string=>byte[] use UTF-8, but byte[]=>string uses Base64 ?
            RegisterUnsafe <byte[], bool>((value) => value != null && value.Length > 0);
            RegisterUnsafe <byte[], sbyte>((value) => value == null ? default(sbyte) : value.AsSlice().ToSByte());
            RegisterUnsafe <byte[], byte>((value) => value == null ? default(byte) : value.AsSlice().ToByte());
            RegisterUnsafe <byte[], short>((value) => value == null ? default(short) : value.AsSlice().ToInt16());
            RegisterUnsafe <byte[], ushort>((value) => value == null ? default(ushort) : value.AsSlice().ToUInt16());
            RegisterUnsafe <byte[], int>((value) => value == null ? 0 : value.AsSlice().ToInt32());
            RegisterUnsafe <byte[], uint>((value) => value == null ? 0U : value.AsSlice().ToUInt32());
            RegisterUnsafe <byte[], long>((value) => value == null ? 0L : value.AsSlice().ToInt64());
            RegisterUnsafe <byte[], ulong>((value) => value == null ? 0UL : value.AsSlice().ToUInt64());
            RegisterUnsafe <byte[], Guid>((value) => value == null || value.Length == 0 ? default(Guid) : new Uuid128(value).ToGuid());
            RegisterUnsafe <byte[], Uuid128>((value) => value == null || value.Length == 0 ? default(Uuid128) : new Uuid128(value));
            RegisterUnsafe <byte[], Uuid64>((value) => value != null ? Uuid64.Read(value) : default(Uuid64));
            RegisterUnsafe <byte[], TimeSpan>((value) => value == null ? TimeSpan.Zero : TimeSpan.FromTicks(value.AsSlice().ToInt64()));
            RegisterUnsafe <byte[], System.Net.IPAddress>((value) => value == null || value.Length == 0 ? default(System.Net.IPAddress) : new System.Net.IPAddress(value));

            RegisterUnsafe <Guid, Slice>((value) => Slice.FromGuid(value));
            RegisterUnsafe <Guid, byte[]>((value) => Slice.FromGuid(value).GetBytes());
            RegisterUnsafe <Guid, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Guid, Uuid128>((value) => new Uuid128(value));
            RegisterUnsafe <Guid, bool>((value) => value != Guid.Empty);
            RegisterUnsafe <Guid, System.Net.IPAddress>((value) => new System.Net.IPAddress(new Uuid128(value).ToByteArray()));            //REVIEW: custom converter for Guid=>IPv6?

            RegisterUnsafe <Uuid128, Slice>((value) => value.ToSlice());
            RegisterUnsafe <Uuid128, byte[]>((value) => value.ToByteArray());
            RegisterUnsafe <Uuid128, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Uuid128, Guid>((value) => value.ToGuid());
            RegisterUnsafe <Uuid128, bool>((value) => value != Uuid128.Empty);
            RegisterUnsafe <Uuid128, System.Net.IPAddress>((value) => new System.Net.IPAddress(value.ToByteArray()));            //REVIEW: custom converter for Guid=>IPv6?

            RegisterUnsafe <Uuid64, Slice>((value) => value.ToSlice());
            RegisterUnsafe <Uuid64, byte[]>((value) => value.ToByteArray());
            RegisterUnsafe <Uuid64, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Uuid64, long>((value) => value.ToInt64());
            RegisterUnsafe <Uuid64, ulong>((value) => value.ToUInt64());
            RegisterUnsafe <Uuid64, bool>((value) => value.ToInt64() != 0L);

            RegisterUnsafe <TimeSpan, Slice>((value) => Slice.FromInt64(value.Ticks));
            RegisterUnsafe <TimeSpan, byte[]>((value) => Slice.FromInt64(value.Ticks).GetBytes());
            RegisterUnsafe <TimeSpan, long>((value) => value.Ticks);
            RegisterUnsafe <TimeSpan, ulong>((value) => checked ((ulong)value.Ticks));
            RegisterUnsafe <TimeSpan, double>((value) => value.TotalSeconds);
            RegisterUnsafe <TimeSpan, bool>((value) => value == TimeSpan.Zero);

            RegisterUnsafe <System.Net.IPAddress, Slice>((value) => (value?.GetAddressBytes()).AsSlice());
            RegisterUnsafe <System.Net.IPAddress, byte[]>((value) => value?.GetAddressBytes());
            RegisterUnsafe <System.Net.IPAddress, string>((value) => value?.ToString());
#pragma warning disable 618
            RegisterUnsafe <System.Net.IPAddress, int>((value) => (int)(value?.Address ?? 0));
#pragma warning restore 618

            RegisterUnsafe <Slice, byte[]>((value) => value.GetBytes());
            RegisterUnsafe <Slice, string>((value) => value.ToUnicode());
            RegisterUnsafe <Slice, bool>((value) => value.ToBool());
            RegisterUnsafe <Slice, sbyte>((value) => value.ToSByte());
            RegisterUnsafe <Slice, byte>((value) => value.ToByte());
            RegisterUnsafe <Slice, short>((value) => value.ToInt16());
            RegisterUnsafe <Slice, ushort>((value) => value.ToUInt16());
            RegisterUnsafe <Slice, int>((value) => value.ToInt32());
            RegisterUnsafe <Slice, uint>((value) => value.ToUInt32());
            RegisterUnsafe <Slice, long>((value) => value.ToInt64());
            RegisterUnsafe <Slice, ulong>((value) => value.ToUInt64());
            RegisterUnsafe <Slice, float>((value) => value.ToSingle());
            RegisterUnsafe <Slice, double>((value) => value.ToDouble());
            RegisterUnsafe <Slice, decimal>((value) => value.ToDecimal());
            RegisterUnsafe <Slice, Guid>((value) => value.ToGuid());
            RegisterUnsafe <Slice, Uuid128>((value) => value.ToUuid128());
            RegisterUnsafe <Slice, Uuid64>((value) => value.ToUuid64());
            RegisterUnsafe <Slice, TimeSpan>((value) => TimeSpan.FromTicks(value.ToInt64()));
            RegisterUnsafe <Slice, System.Net.IPAddress>((value) => !value.IsNullOrEmpty ? new System.Net.IPAddress(value.GetBytesOrEmpty()) : null);
        }
        /// <summary>Register all the default converters</summary>
        private static void RegisterDefaultConverters()
        {
            //TODO: there is too much generic type combinations! need to refactor this ...

            RegisterUnsafe <bool, Slice>((value) => Slice.FromByte(value ? (byte)1 : default(byte)));
            RegisterUnsafe <bool, byte[]>((value) => Slice.FromByte(value ? (byte)1 : default(byte)).GetBytes());
            RegisterUnsafe <bool, string>((value) => value ? "true" : "false");
            RegisterUnsafe <bool, sbyte>((value) => value ? (sbyte)1 : default(sbyte));
            RegisterUnsafe <bool, byte>((value) => value ? (byte)1 : default(byte));
            RegisterUnsafe <bool, short>((value) => value ? (short)1 : default(short));
            RegisterUnsafe <bool, ushort>((value) => value ? (ushort)1 : default(ushort));
            RegisterUnsafe <bool, int>((value) => value ? 1 : default(int));
            RegisterUnsafe <bool, uint>((value) => value ? 1U : default(uint));
            RegisterUnsafe <bool, long>((value) => value ? 1L : default(long));
            RegisterUnsafe <bool, ulong>((value) => value ? 1UL : default(ulong));
            RegisterUnsafe <bool, double>((value) => value ? 0.0d : 1.0d);
            RegisterUnsafe <bool, float>((value) => value ? 0.0f : 1.0f);

            RegisterUnsafe <int, Slice>((value) => Slice.FromInt32(value));
            RegisterUnsafe <int, byte[]>((value) => Slice.FromInt32(value).GetBytes());
            RegisterUnsafe <int, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <int, bool>((value) => value != 0);
            RegisterUnsafe <int, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <int, byte>((value) => checked ((byte)value));
            RegisterUnsafe <int, short>((value) => checked ((short)value));
            RegisterUnsafe <int, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <int, uint>((value) => (uint)value);
            RegisterUnsafe <int, long>((value) => value);
            RegisterUnsafe <int, ulong>((value) => (ulong)value);
            RegisterUnsafe <int, double>((value) => value);
            RegisterUnsafe <int, float>((value) => checked ((float)value));
            RegisterUnsafe <int, FdbTupleAlias>((value) => (FdbTupleAlias)value);

            RegisterUnsafe <uint, Slice>((value) => Slice.FromUInt64(value));
            RegisterUnsafe <uint, byte[]>((value) => Slice.FromUInt64(value).GetBytes());
            RegisterUnsafe <uint, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <uint, bool>((value) => value != 0);
            RegisterUnsafe <uint, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <uint, byte>((value) => checked ((byte)value));
            RegisterUnsafe <uint, short>((value) => checked ((short)value));
            RegisterUnsafe <uint, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <uint, int>((value) => (int)value);
            RegisterUnsafe <uint, long>((value) => value);
            RegisterUnsafe <uint, ulong>((value) => value);
            RegisterUnsafe <uint, double>((value) => value);
            RegisterUnsafe <uint, float>((value) => checked ((float)value));

            RegisterUnsafe <long, Slice>((value) => Slice.FromInt64(value));
            RegisterUnsafe <long, byte[]>((value) => Slice.FromInt64(value).GetBytes());
            RegisterUnsafe <long, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <long, bool>((value) => value != 0);
            RegisterUnsafe <long, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <long, byte>((value) => checked ((byte)value));
            RegisterUnsafe <long, short>((value) => checked ((short)value));
            RegisterUnsafe <long, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <long, int>((value) => checked ((int)value));
            RegisterUnsafe <long, uint>((value) => (uint)value);
            RegisterUnsafe <long, ulong>((value) => (ulong)value);
            RegisterUnsafe <long, double>((value) => checked ((double)value));
            RegisterUnsafe <long, float>((value) => checked ((float)value));
            RegisterUnsafe <long, TimeSpan>((value) => TimeSpan.FromTicks(value));
            RegisterUnsafe <long, Uuid64>((value) => new Uuid64(value));
            RegisterUnsafe <long, System.Net.IPAddress>((value) => new System.Net.IPAddress(value));

            RegisterUnsafe <ulong, Slice>((value) => Slice.FromUInt64(value));
            RegisterUnsafe <ulong, byte[]>((value) => Slice.FromUInt64(value).GetBytes());
            RegisterUnsafe <ulong, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <ulong, bool>((value) => value != 0);
            RegisterUnsafe <ulong, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <ulong, byte>((value) => checked ((byte)value));
            RegisterUnsafe <ulong, short>((value) => checked ((short)value));
            RegisterUnsafe <ulong, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <ulong, int>((value) => checked ((int)value));
            RegisterUnsafe <ulong, uint>((value) => checked ((uint)value));
            RegisterUnsafe <ulong, long>((value) => checked ((long)value));
            RegisterUnsafe <ulong, double>((value) => checked ((double)value));
            RegisterUnsafe <ulong, float>((value) => checked ((float)value));
            RegisterUnsafe <ulong, Uuid64>((value) => new Uuid64(value));
            RegisterUnsafe <ulong, TimeSpan>((value) => TimeSpan.FromTicks(checked ((long)value)));

            RegisterUnsafe <short, Slice>((value) => Slice.FromInt32(value));
            RegisterUnsafe <short, byte[]>((value) => Slice.FromInt32(value).GetBytes());
            RegisterUnsafe <short, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <short, bool>((value) => value != 0);
            RegisterUnsafe <short, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <short, byte>((value) => checked ((byte)value));
            RegisterUnsafe <short, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <short, int>((value) => value);
            RegisterUnsafe <short, uint>((value) => checked ((uint)value));
            RegisterUnsafe <short, long>((value) => value);
            RegisterUnsafe <short, ulong>((value) => checked ((ulong)value));
            RegisterUnsafe <short, double>((value) => value);
            RegisterUnsafe <short, float>((value) => value);
            RegisterUnsafe <short, FdbTupleAlias>((value) => (FdbTupleAlias)value);

            RegisterUnsafe <ushort, Slice>((value) => Slice.FromUInt64(value));
            RegisterUnsafe <ushort, byte[]>((value) => Slice.FromUInt64(value).GetBytes());
            RegisterUnsafe <ushort, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <ushort, bool>((value) => value != 0);
            RegisterUnsafe <ushort, byte>((value) => checked ((byte)value));
            RegisterUnsafe <ushort, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <ushort, short>((value) => checked ((short)value));
            RegisterUnsafe <ushort, int>((value) => value);
            RegisterUnsafe <ushort, uint>((value) => value);
            RegisterUnsafe <ushort, long>((value) => value);
            RegisterUnsafe <ushort, ulong>((value) => value);
            RegisterUnsafe <ushort, double>((value) => value);
            RegisterUnsafe <ushort, float>((value) => value);

            RegisterUnsafe <byte, Slice>((value) => Slice.FromInt32(value));
            RegisterUnsafe <byte, byte[]>((value) => Slice.FromInt32(value).GetBytes());
            RegisterUnsafe <byte, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <byte, bool>((value) => value != 0);
            RegisterUnsafe <byte, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <byte, short>((value) => value);
            RegisterUnsafe <byte, ushort>((value) => value);
            RegisterUnsafe <byte, int>((value) => value);
            RegisterUnsafe <byte, uint>((value) => value);
            RegisterUnsafe <byte, long>((value) => value);
            RegisterUnsafe <byte, ulong>((value) => value);
            RegisterUnsafe <byte, double>((value) => value);
            RegisterUnsafe <byte, float>((value) => value);
            RegisterUnsafe <byte, FdbTupleAlias>((value) => (FdbTupleAlias)value);

            RegisterUnsafe <sbyte, Slice>((value) => Slice.FromInt64(value));
            RegisterUnsafe <sbyte, byte[]>((value) => Slice.FromInt64(value).GetBytes());
            RegisterUnsafe <sbyte, string>((value) => value.ToString(CultureInfo.InvariantCulture));            //TODO: string table!
            RegisterUnsafe <sbyte, bool>((value) => value != 0);
            RegisterUnsafe <sbyte, byte>((value) => checked ((byte)value));
            RegisterUnsafe <sbyte, short>((value) => value);
            RegisterUnsafe <sbyte, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <sbyte, int>((value) => value);
            RegisterUnsafe <sbyte, uint>((value) => checked ((uint)value));
            RegisterUnsafe <sbyte, long>((value) => value);
            RegisterUnsafe <sbyte, ulong>((value) => checked ((ulong)value));
            RegisterUnsafe <sbyte, double>((value) => value);
            RegisterUnsafe <sbyte, float>((value) => value);

            RegisterUnsafe <float, Slice>((value) => Slice.FromSingle(value));
            RegisterUnsafe <float, byte[]>((value) => Slice.FromSingle(value).GetBytes());
            RegisterUnsafe <float, string>((value) => value.ToString("R", CultureInfo.InvariantCulture));
            RegisterUnsafe <float, bool>((value) => !(value == 0f || float.IsNaN(value)));
            RegisterUnsafe <float, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <float, byte>((value) => checked ((byte)value));
            RegisterUnsafe <float, short>((value) => checked ((short)value));
            RegisterUnsafe <float, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <float, int>((value) => checked ((int)value));
            RegisterUnsafe <float, uint>((value) => (uint)value);
            RegisterUnsafe <float, long>((value) => checked ((long)value));
            RegisterUnsafe <float, ulong>((value) => (ulong)value);
            RegisterUnsafe <float, double>((value) => value);

            RegisterUnsafe <double, Slice>((value) => Slice.FromDouble(value));
            RegisterUnsafe <double, byte[]>((value) => Slice.FromDouble(value).GetBytes());
            RegisterUnsafe <double, string>((value) => value.ToString("R", CultureInfo.InvariantCulture));
            RegisterUnsafe <double, bool>((value) => !(value == 0d || double.IsNaN(value)));
            RegisterUnsafe <double, sbyte>((value) => checked ((sbyte)value));
            RegisterUnsafe <double, byte>((value) => checked ((byte)value));
            RegisterUnsafe <double, short>((value) => checked ((short)value));
            RegisterUnsafe <double, ushort>((value) => checked ((ushort)value));
            RegisterUnsafe <double, int>((value) => checked ((int)value));
            RegisterUnsafe <double, uint>((value) => (uint)value);
            RegisterUnsafe <double, long>((value) => checked ((long)value));
            RegisterUnsafe <double, ulong>((value) => (ulong)value);
            RegisterUnsafe <double, float>((value) => checked ((float)value));

            RegisterUnsafe <string, Slice>((value) => Slice.FromString(value));
            RegisterUnsafe <string, byte[]>((value) => Slice.FromString(value).GetBytes());
            RegisterUnsafe <string, bool>((value) => !string.IsNullOrEmpty(value));
            RegisterUnsafe <string, sbyte>((value) => string.IsNullOrEmpty(value) ? default(sbyte) : SByte.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, byte>((value) => string.IsNullOrEmpty(value) ? default(byte) : Byte.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, short>((value) => string.IsNullOrEmpty(value) ? default(short) : Int16.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, ushort>((value) => string.IsNullOrEmpty(value) ? default(ushort) : UInt16.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, int>((value) => string.IsNullOrEmpty(value) ? default(int) : Int32.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, uint>((value) => string.IsNullOrEmpty(value) ? default(uint) : UInt32.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, long>((value) => string.IsNullOrEmpty(value) ? default(long) : Int64.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, ulong>((value) => string.IsNullOrEmpty(value) ? default(ulong) : UInt64.Parse(value, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, float>((value) => string.IsNullOrEmpty(value) ? default(float) : Single.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, double>((value) => string.IsNullOrEmpty(value) ? default(double) : Double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture));
            RegisterUnsafe <string, Guid>((value) => string.IsNullOrEmpty(value) ? default(Guid) : Guid.Parse(value));
            RegisterUnsafe <string, Uuid128>((value) => string.IsNullOrEmpty(value) ? default(Uuid128) : Uuid128.Parse(value));
            RegisterUnsafe <string, Uuid64>((value) => string.IsNullOrEmpty(value) ? default(Uuid64) : Uuid64.Parse(value));
            RegisterUnsafe <string, System.Net.IPAddress>((value) => string.IsNullOrEmpty(value) ? default(System.Net.IPAddress) : System.Net.IPAddress.Parse(value));

            RegisterUnsafe <byte[], Slice>((value) => Slice.Create(value));
            RegisterUnsafe <byte[], string>((value) => value == null ? default(string) : value.Length == 0 ? String.Empty : System.Convert.ToBase64String(value));
            RegisterUnsafe <byte[], bool>((value) => value != null && value.Length > 0);
            RegisterUnsafe <byte[], sbyte>((value) => value == null ? default(sbyte) : Slice.Create(value).ToSByte());
            RegisterUnsafe <byte[], byte>((value) => value == null ? default(byte) : Slice.Create(value).ToByte());
            RegisterUnsafe <byte[], short>((value) => value == null ? default(short) : Slice.Create(value).ToInt16());
            RegisterUnsafe <byte[], ushort>((value) => value == null ? default(ushort) : Slice.Create(value).ToUInt16());
            RegisterUnsafe <byte[], int>((value) => value == null ? 0 : Slice.Create(value).ToInt32());
            RegisterUnsafe <byte[], uint>((value) => value == null ? 0U : Slice.Create(value).ToUInt32());
            RegisterUnsafe <byte[], long>((value) => value == null ? 0L : Slice.Create(value).ToInt64());
            RegisterUnsafe <byte[], ulong>((value) => value == null ? 0UL : Slice.Create(value).ToUInt64());
            RegisterUnsafe <byte[], Guid>((value) => value == null || value.Length == 0 ? default(Guid) : new Uuid128(value).ToGuid());
            RegisterUnsafe <byte[], Uuid128>((value) => value == null || value.Length == 0 ? default(Uuid128) : new Uuid128(value));
            RegisterUnsafe <byte[], Uuid64>((value) => value == null || value.Length == 0 ? default(Uuid64) : new Uuid64(value));
            RegisterUnsafe <byte[], TimeSpan>((value) => value == null ? TimeSpan.Zero : TimeSpan.FromTicks(Slice.Create(value).ToInt64()));
            RegisterUnsafe <byte[], System.Net.IPAddress>((value) => value == null || value.Length == 0 ? default(System.Net.IPAddress) : new System.Net.IPAddress(value));

            RegisterUnsafe <Guid, Slice>((value) => Slice.FromGuid(value));
            RegisterUnsafe <Guid, byte[]>((value) => Slice.FromGuid(value).GetBytes());
            RegisterUnsafe <Guid, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Guid, Uuid128>((value) => new Uuid128(value));
            RegisterUnsafe <Guid, bool>((value) => value != Guid.Empty);
            RegisterUnsafe <Guid, System.Net.IPAddress>((value) => new System.Net.IPAddress(new Uuid128(value).ToByteArray()));

            RegisterUnsafe <Uuid128, Slice>((value) => value.ToSlice());
            RegisterUnsafe <Uuid128, byte[]>((value) => value.ToByteArray());
            RegisterUnsafe <Uuid128, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Uuid128, Guid>((value) => value.ToGuid());
            RegisterUnsafe <Uuid128, bool>((value) => value != Uuid128.Empty);
            RegisterUnsafe <Guid, System.Net.IPAddress>((value) => new System.Net.IPAddress(value.ToByteArray()));

            RegisterUnsafe <Uuid64, Slice>((value) => value.ToSlice());
            RegisterUnsafe <Uuid64, byte[]>((value) => value.ToByteArray());
            RegisterUnsafe <Uuid64, string>((value) => value.ToString("D", null));
            RegisterUnsafe <Uuid64, long>((value) => value.ToInt64());
            RegisterUnsafe <Uuid64, ulong>((value) => value.ToUInt64());
            RegisterUnsafe <Uuid64, bool>((value) => value.ToInt64() != 0L);

            RegisterUnsafe <TimeSpan, Slice>((value) => Slice.FromInt64(value.Ticks));
            RegisterUnsafe <TimeSpan, byte[]>((value) => Slice.FromInt64(value.Ticks).GetBytes());
            RegisterUnsafe <TimeSpan, long>((value) => value.Ticks);
            RegisterUnsafe <TimeSpan, ulong>((value) => checked ((ulong)value.Ticks));
            RegisterUnsafe <TimeSpan, double>((value) => value.TotalSeconds);
            RegisterUnsafe <TimeSpan, bool>((value) => value == TimeSpan.Zero);

            RegisterUnsafe <System.Net.IPAddress, Slice>((value) => value != null ? Slice.Create(value.GetAddressBytes()) : Slice.Nil);
            RegisterUnsafe <System.Net.IPAddress, byte[]>((value) => value != null ? value.GetAddressBytes() : null);
            RegisterUnsafe <System.Net.IPAddress, string>((value) => value != null ? value.ToString() : null);

            RegisterUnsafe <FdbTupleAlias, byte>((value) => (byte)value);
            RegisterUnsafe <FdbTupleAlias, int>((value) => (int)value);
            RegisterUnsafe <FdbTupleAlias, Slice>((value) => Slice.FromByte((byte)value));

            //REVIEW: this should go in the Tuples layer !
            RegisterUnsafe <Slice, byte[]>((value) => value.GetBytes());
            RegisterUnsafe <Slice, string>((value) => value.ToUnicode());
            RegisterUnsafe <Slice, bool>((value) => value.ToBool());
            RegisterUnsafe <Slice, sbyte>((value) => value.ToSByte());
            RegisterUnsafe <Slice, byte>((value) => value.ToByte());
            RegisterUnsafe <Slice, short>((value) => value.ToInt16());
            RegisterUnsafe <Slice, ushort>((value) => value.ToUInt16());
            RegisterUnsafe <Slice, int>((value) => value.ToInt32());
            RegisterUnsafe <Slice, uint>((value) => value.ToUInt32());
            RegisterUnsafe <Slice, long>((value) => value.ToInt64());
            RegisterUnsafe <Slice, ulong>((value) => value.ToUInt64());
            RegisterUnsafe <Slice, Guid>((value) => value.ToGuid());
            RegisterUnsafe <Slice, Uuid128>((value) => value.ToUuid128());
            RegisterUnsafe <Slice, Uuid64>((value) => value.ToUuid64());
            RegisterUnsafe <Slice, TimeSpan>((value) => TimeSpan.FromTicks(value.ToInt64()));
            RegisterUnsafe <Slice, FdbTupleAlias>((value) => (FdbTupleAlias)value.ToByte());
            RegisterUnsafe <Slice, System.Net.IPAddress>((value) => !value.IsNullOrEmpty ? new System.Net.IPAddress(value.GetBytes()) : null);
        }
		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;
		}
		public void ReadJumpTable(CancellationToken ct)
		{
			ct.ThrowIfCancellationRequested();

			if (!m_hasHeader)
			{
				throw new InvalidOperationException("Cannot read the Jump Table without reading the Header first!");
			}

			// an empty database will have at least 2 pages: the header and the JT
			if (m_file.Length < checked(m_pageSize << 1))
			{
				throw ParseError("File size ({0}) is too small to be a valid snapshot", m_file.Length);
			}

			// the jumptable is always in the last page of the file and is expected to fit nicely
			// > file size MUST be evenly divible by page size
			// > then JT offset will be file.Length - pageSize
			if (m_file.Length % m_pageSize != 0)
			{
				throw ParseError("The file size ({0}) is not a multiple of the page size ({1}), which may be a symptom of truncation", m_file.Length, m_pageSize);
			}

			var jumpTableStart = m_file.Length - m_pageSize;
			Contract.Assert(jumpTableStart % m_pageSize == 0);
			m_dataEnd = jumpTableStart;

			var reader = m_file.CreateReader(jumpTableStart, m_pageSize);

			// "JMPT"
			var signature = reader.ReadFixed32();
			// Page Size (repeated)
			var pageSizeRepeated = (int)reader.ReadFixed32();
			// Sequence Number (repeated)
			var sequenceRepeated = reader.ReadFixed64();
			// Database ID (repeated)
			var uidRepeated = new Uuid128(reader.ReadBytes(16).GetBytes());
			// Header CRC (repeated)
			var headerChecksumRepeated = reader.ReadFixed32();

			// Sanity checks

			if (signature != SnapshotFormat.JUMP_TABLE_MAGIC_NUMBER) throw ParseError("Last page does not appear to be the Jump Table");
			if (pageSizeRepeated != m_pageSize) throw ParseError("Page size in Jump Table does not match the header value");
			if (sequenceRepeated != m_sequence) throw ParseError("Sequence in Jump Table does not match the header value");
			if (uidRepeated != m_uid) throw ParseError("Database ID in Jump Table does not match the header value");
			if (headerChecksumRepeated != m_headerChecksum) throw ParseError("Database ID in Jump Table does not match the header value");

			// read the table itself
			int levels = (int)reader.ReadFixed32();
			if (levels < 0 || levels > 32) throw ParseError("The number of levels in the snapshot does not appear to be valid");

			var table = new LevelAddress[levels];
			for (int level = 0; level < levels; level++)
			{
				ulong offset = reader.ReadFixed64();
				ulong size = reader.ReadFixed64();

				// Offset and Size cannot be negative
				// Empty levels (size == 0) must have a zero offset
				// Non empty levels (size > 0) must have a non zero offset that is greater than the headerSize
				if ((size == 0 && offset != 0) || (size > 0 && offset < m_dataStart)) throw ParseError("Level in Jump Table has invalid size ({0}) or offset ({1})", size, offset);
				if (checked(offset + size) > m_dataEnd) throw ParseError("Level in Jump Table would end after the end of the file");

				table[level].Offset = offset;
				table[level].Size = size;
				table[level].PaddedSize = RoundUp(size, m_pageSize);
			}

			// end attributes
			uint attributeCount = reader.ReadFixed32();
			if (attributeCount != 0) throw new NotImplementedException("Footer attributes not yet implemented!");

			// end marker
			if (reader.ReadFixed32() != uint.MaxValue) throw ParseError("Jump Table end marker not found");

			// checksum
			uint actualChecksum = SnapshotFormat.ComputeChecksum(reader.Base, reader.Offset);
			uint checksum = reader.ReadFixed32();
			if (actualChecksum != checksum) throw ParseError("Jump Table checksum does not match ({0} != {1}). This may be an indication of data corruption", checksum, actualChecksum);

			m_jumpTable = table;
			m_levels = levels;
			m_hasJumpTable = true;
		}
		/// <summary>Writes a RFC 4122 encoded 128-bit UUID</summary>
		public static void WriteUuid128(ref SliceWriter writer, Uuid128 value)
		{
			writer.EnsureBytes(17);
			writer.UnsafeWriteByte(FdbTupleTypes.Uuid128);
			unsafe
			{
				byte* ptr = stackalloc byte[16];
				value.WriteTo(ptr);
				writer.UnsafeWriteBytes(ptr, 16);
			}
		}
        /// <summary>Write the header to the file</summary>
        /// <param name="headerFlags"></param>
        /// <param name="uid"></param>
        /// <param name="sequence"></param>
        /// <param name="count"></param>
        /// <param name="timestamp"></param>
        /// <param name="attributes"></param>
        /// <remarks>This needs to be called before writing any level to the file</remarks>
        public Task WriteHeaderAsync(SnapshotFormat.Flags headerFlags, Uuid128 uid, ulong sequence, long count, long timestamp, IDictionary <string, IFdbTuple> attributes)
        {
            // The header will be use on ore more "pages", to simplify the job of loading / peeking at a stream content (no need for fancy buffering, just need to read 4K pages)
            // > The last page is padded with 0xAAs to detect corruption.

            m_uid       = uid;
            m_sequence  = sequence;
            m_itemCount = count;
            m_timestamp = timestamp;

            // HEADER
            // - DB_HEADER (64 bytes)
            // - DB ATTRIBUTES (variable size list of k/v)
            // - END_MARKER + HEADER_CRC
            // - PADDING (to fill last page)

            // DB Header

            // "PNDB"
            m_writer.WriteFixed32(SnapshotFormat.HEADER_MAGIC_NUMBER);
            // v1.0
            m_writer.WriteFixed16(1);             // major
            m_writer.WriteFixed16(0);             // minor
            // FLAGS
            m_writer.WriteFixed64((ulong)headerFlags);
            // Database ID
            m_writer.WriteBytes(uid.ToSlice());
            // Database Version
            m_writer.WriteFixed64(sequence);
            // Number of items in the database
            m_writer.WriteFixed64((ulong)count);
            // Database Timestamp
            m_writer.WriteFixed64((ulong)timestamp);
            // Page Size
            m_writer.WriteFixed32(SnapshotFormat.PAGE_SIZE);
            // Header Size (not known yet and will be filled in later)
            int offsetToHeaderSize = m_writer.Skip(4);

            // we should be at the 64 byte mark
            Contract.Assert(m_writer.Position == SnapshotFormat.HEADER_METADATA_BYTES);

            // DB Attributes
            m_writer.WriteFixed32((uint)attributes.Count);
            foreach (var kvp in attributes)
            {
                // Name
                m_writer.WriteVarbytes(Slice.FromString(kvp.Key));

                // Value
                m_writer.WriteVarbytes(kvp.Value.ToSlice());
            }

            // Mark the end of the header
            m_writer.WriteFixed32(uint.MaxValue);

            // we now have the size of the header, and can fill in the blank
            var headerEnd = m_writer.Position;

            m_writer.Position = offsetToHeaderSize;
            // write the header size (includes the CRC)
            m_writer.WriteFixed32((uint)checked (headerEnd + SnapshotFormat.HEADER_CRC_SIZE));
            m_writer.Position = headerEnd;

            // now we can compute the actual CRC
            uint headerChecksum = SnapshotFormat.ComputeChecksum(m_writer.ToSlice());

            m_writer.WriteFixed32(headerChecksum);
            m_headerChecksum = headerChecksum;

            // optional padding to fill the rest of the page
            PadPageIfNeeded(SnapshotFormat.PAGE_SIZE, 0xFD);

            return(TaskHelpers.CompletedTask);
        }
		/// <summary>Write the header to the file</summary>
		/// <param name="headerFlags"></param>
		/// <param name="uid"></param>
		/// <param name="sequence"></param>
		/// <param name="count"></param>
		/// <param name="timestamp"></param>
		/// <param name="attributes"></param>
		/// <remarks>This needs to be called before writing any level to the file</remarks>
		public Task WriteHeaderAsync(SnapshotFormat.Flags headerFlags, Uuid128 uid, ulong sequence, long count, long timestamp, IDictionary<string, IFdbTuple> attributes)
		{
			// The header will be use on ore more "pages", to simplify the job of loading / peeking at a stream content (no need for fancy buffering, just need to read 4K pages)
			// > The last page is padded with 0xAAs to detect corruption.

			m_uid = uid;
			m_sequence = sequence;
			m_itemCount = count;
			m_timestamp = timestamp;

			// HEADER
			// - DB_HEADER (64 bytes)
			// - DB ATTRIBUTES (variable size list of k/v)
			// - END_MARKER + HEADER_CRC
			// - PADDING (to fill last page)

			// DB Header

			// "PNDB"
			m_writer.WriteFixed32(SnapshotFormat.HEADER_MAGIC_NUMBER);
			// v1.0
			m_writer.WriteFixed16(1); // major
			m_writer.WriteFixed16(0); // minor
			// FLAGS
			m_writer.WriteFixed64((ulong)headerFlags);
			// Database ID
			m_writer.WriteBytes(uid.ToSlice());
			// Database Version
			m_writer.WriteFixed64(sequence);
			// Number of items in the database
			m_writer.WriteFixed64((ulong)count);
			// Database Timestamp
			m_writer.WriteFixed64((ulong)timestamp);
			// Page Size
			m_writer.WriteFixed32(SnapshotFormat.PAGE_SIZE);
			// Header Size (not known yet and will be filled in later)
			int offsetToHeaderSize = m_writer.Skip(4);

			// we should be at the 64 byte mark
			Contract.Assert(m_writer.Position == SnapshotFormat.HEADER_METADATA_BYTES);

			// DB Attributes
			m_writer.WriteFixed32((uint)attributes.Count);
			foreach (var kvp in attributes)
			{
				// Name
				m_writer.WriteVarbytes(Slice.FromString(kvp.Key));

				// Value
				m_writer.WriteVarbytes(kvp.Value.ToSlice());
			}

			// Mark the end of the header
			m_writer.WriteFixed32(uint.MaxValue);

			// we now have the size of the header, and can fill in the blank
			var headerEnd = m_writer.Position;
			m_writer.Position = offsetToHeaderSize;
			// write the header size (includes the CRC)
			m_writer.WriteFixed32((uint)checked(headerEnd + SnapshotFormat.HEADER_CRC_SIZE));
			m_writer.Position = headerEnd;

			// now we can compute the actual CRC
			uint headerChecksum = SnapshotFormat.ComputeChecksum(m_writer.ToSlice());
			m_writer.WriteFixed32(headerChecksum);
			m_headerChecksum = headerChecksum;

			// optional padding to fill the rest of the page
			PadPageIfNeeded(SnapshotFormat.PAGE_SIZE, 0xFD);

			return TaskHelpers.CompletedTask;
		}
Beispiel #26
0
 public static string Stringify(Uuid128 item) => item.ToString("B", CultureInfo.InstalledUICulture);             /* {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} */
        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;
        }
		public void Test_Slice_FromUuid128()
		{
			// Verify that FoundationDb.Client.Uuid are stored as 128-bit UUIDs using RFC 4122

			Slice slice;

			// empty guid should be all zeroes
			slice = Slice.FromUuid128(Uuid128.Empty);
			Assert.That(slice.ToHexaString(), Is.EqualTo("00000000000000000000000000000000"));

			// UUIDs should be stored using RFC 4122 (big endian)
			var uuid = new Uuid128("00112233-4455-6677-8899-aabbccddeeff");

			// byte order should follow the string!
			slice = Slice.FromUuid128(uuid);
			Assert.That(slice.ToHexaString(), Is.EqualTo("00112233445566778899aabbccddeeff"), "Slice.FromUuid() should preserve RFC 4122 ordering");

			// ToByteArray() should also be safe
			slice = Slice.Create(uuid.ToByteArray());
			Assert.That(slice.ToHexaString(), Is.EqualTo("00112233445566778899aabbccddeeff"));
		}
 public Slice EncodeValue(Uuid128 value) => Slice.FromUuid128(value);