public CompressedBitmap(MutableSlice data)
        {
            if (data.IsNull)
            {
                throw new ArgumentNullException(nameof(data));
            }
            if (data.Count > 0 && data.Count < 8)
            {
                throw new ArgumentException("A compressed bitmap must either be empty, or at least 8 bytes long", nameof(data));
            }
            if ((data.Count & 3) != 0)
            {
                throw new ArgumentException("A compressed bitmap size must be a multiple of 4 bytes", nameof(data));
            }

            if (data.Count == 0)
            {
                m_data   = MutableSlice.Empty;
                m_bounds = BitRange.Empty;
            }
            else
            {
                m_data   = data;
                m_bounds = ComputeBounds(data);
            }
        }
예제 #2
0
        private MutableSlice AllocateFallback(int count)
        {
            // keys that are too large are best kept in their own chunks
            if (count > (m_pageSize >> 1))
            {
                var tmp = MutableSlice.Create(count);
                Keep(tmp);
                return(tmp);
            }

            int pageSize = m_pageSize;

            // double the page size on each new allocation
            if (m_current != null)
            {
                if (m_pos > 0)
                {
                    Keep(new MutableSlice(m_current, 0, m_pos));
                }
                pageSize <<= 1;
                if (pageSize > MaxPageSize)
                {
                    pageSize = MaxPageSize;
                }
                m_pageSize = pageSize;
            }

            var buffer = new byte[pageSize];

            m_current   = buffer;
            m_pos       = count;
            m_remaining = pageSize - count;

            return(new MutableSlice(buffer, 0, count));
        }
        public void Test_Uuid64_TryWriteTo()
        {
            var original = Uuid64.Parse("01234567-89ABCDEF");

            Assume.That(original.ToUInt64(), Is.EqualTo(0x0123456789ABCDEF));

            // span with more space
            var scratch = MutableSlice.Repeat(0xAA, 16);

            Assert.That(original.TryWriteTo(scratch.Span), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with no offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 16);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(0, 8)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with offset
            scratch = MutableSlice.Repeat(0xAA, 16);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(4)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 01 23 45 67 89 AB CD EF AA AA AA AA"));

            // span with offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 16);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(4, 8)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 01 23 45 67 89 AB CD EF AA AA AA AA"));

            // errors

            Assert.That(original.TryWriteTo(Span <byte> .Empty), Is.False, "Target buffer is empty");

            scratch = MutableSlice.Repeat(0xAA, 16);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(0, 7)), Is.False, "Target buffer is too small");
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA"), "Buffer should not have been overwritten!");
        }
        public void Test_MutableSliceComparer_Equals()
        {
            var cmp = MutableSlice.Comparer.Default;

            Assert.That(cmp, Is.Not.Null);
            Assert.That(MutableSlice.Comparer.Default, Is.SameAs(cmp));

            Assert.That(cmp.Equals(MutableSlice.Nil, MutableSlice.Nil), Is.True);
            Assert.That(cmp.Equals(MutableSlice.Empty, MutableSlice.Empty), Is.True);
            Assert.That(cmp.Equals(MutableSlice.Nil, MutableSlice.Empty), Is.False);
            Assert.That(cmp.Equals(MutableSlice.Empty, MutableSlice.Nil), Is.False);

            Assert.That(cmp.Equals(MutableSlice.FromByte(42), MutableSlice.FromByte(42)), Is.True);
            Assert.That(cmp.Equals(MutableSlice.FromByte(42), new byte[] { 42 }.AsMutableSlice()), Is.True);
            Assert.That(cmp.Equals(MutableSlice.FromByte(42), MutableSlice.FromByte(77)), Is.False);

            Assert.That(cmp.Equals(new byte[] { 65, 66, 67 }.AsMutableSlice(), MutableSlice.FromString("ABC")), Is.True);
            Assert.That(cmp.Equals(new byte[] { 65, 66, 67, 68 }.AsMutableSlice(), MutableSlice.FromString("ABC")), Is.False);

            var buf1 = Encoding.ASCII.GetBytes("ABBAABA");
            var buf2 = Encoding.ASCII.GetBytes("ABBAABA");

            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 2), buf1.AsMutableSlice(0, 2)), Is.True);
            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 2), buf1.AsMutableSlice(0, 3)), Is.False);
            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 2), buf1.AsMutableSlice(4, 2)), Is.True);
            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 3), buf1.AsMutableSlice(4, 3)), Is.False);
            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 2), buf2.AsMutableSlice(4, 2)), Is.True);
            Assert.That(cmp.Equals(buf1.AsMutableSlice(0, 3), buf2.AsMutableSlice(4, 3)), Is.False);
        }
        internal CompressedBitmap(MutableSlice data, BitRange bounds)
        {
            if (data.IsNull)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (data.Count == 0)
            {
                m_data   = MutableSlice.Empty;
                m_bounds = BitRange.Empty;
            }
            else
            {
                if ((data.Count & 3) != 0)
                {
                    throw new ArgumentException("A compressed bitmap size must be a multiple of 4 bytes", nameof(data));
                }
                if (data.Count < 4)
                {
                    throw new ArgumentException("A compressed bitmap must be at least 4 bytes long", nameof(data));
                }
                m_data   = data;
                m_bounds = bounds;
            }
        }
        public void Test_MutableSliceComparer_Compare()
        {
            var cmp = MutableSlice.Comparer.Default;

            Assert.That(cmp, Is.Not.Null);

            Assert.That(cmp.Compare(MutableSlice.Nil, MutableSlice.Nil), Is.Zero);
            Assert.That(cmp.Compare(MutableSlice.Empty, MutableSlice.Empty), Is.Zero);
            Assert.That(cmp.Compare(MutableSlice.FromByte(42), MutableSlice.FromByte(42)), Is.Zero);

            //REVIEW: Inconsistency: compare(nil, empty) == 0, but Equals(nil, empty) == false
            Assert.That(cmp.Compare(MutableSlice.Nil, MutableSlice.Empty), Is.Zero, "Nil and Empty are considered similar regarding ordering");
            Assert.That(cmp.Compare(MutableSlice.Empty, MutableSlice.Nil), Is.Zero, "Nil and Empty are considered similar regarding ordering");

            Assert.That(cmp.Compare(MutableSlice.FromByte(42), MutableSlice.FromByte(77)), Is.LessThan(0));
            Assert.That(cmp.Compare(MutableSlice.FromByte(42), MutableSlice.FromByte(21)), Is.GreaterThan(0));
            Assert.That(cmp.Compare(MutableSlice.FromByte(42), MutableSlice.Empty), Is.GreaterThan(0));
            Assert.That(cmp.Compare(MutableSlice.FromByte(42), MutableSlice.Nil), Is.GreaterThan(0));

            Assert.That(cmp.Compare(MutableSlice.FromString("hello"), MutableSlice.FromString("world")), Is.LessThan(0));
            Assert.That(cmp.Compare(MutableSlice.FromString("world"), MutableSlice.FromString("hello")), Is.GreaterThan(0));

            Assert.That(cmp.Compare(MutableSlice.FromString("hell"), MutableSlice.FromString("hello")), Is.LessThan(0));
            Assert.That(cmp.Compare(MutableSlice.FromString("help"), MutableSlice.FromString("hello")), Is.GreaterThan(0));
        }
 internal CompressedBitmapWordIterator(MutableSlice buffer)
 {
     Contract.Requires((buffer.Count & 3) == 0 && (buffer.Count == 0 || buffer.Count >= 8));
     if (buffer.Count == 0)
     {
         m_reader = new SliceReader();
     }
     else
     {             // skip the header
         m_reader = new SliceReader(buffer.Substring(4));
     }
     m_current = 0;
 }
        public void Test_Uuid96_WriteTo()
        {
            var original = Uuid96.Parse("1E2D3C4B-01234567-89ABCDEF");

            (var hi, var lo) = original;
            Assume.That(hi, Is.EqualTo(0x1E2D3C4B));
            Assume.That(lo, Is.EqualTo(0x0123456789ABCDEF));

            // span with more space
            var scratch = MutableSlice.Repeat(0xAA, 20);

            original.WriteTo(scratch.Span);
            Assert.That(scratch.ToString("X"), Is.EqualTo("1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with no offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 20);
            original.WriteTo(scratch.Span.Slice(0, 12));
            Assert.That(scratch.ToString("X"), Is.EqualTo("1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with offset
            scratch = MutableSlice.Repeat(0xAA, 20);
            original.WriteTo(scratch.Span.Slice(4));
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA"));

            // span with offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 20);
            original.WriteTo(scratch.Span.Slice(4, 12));
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA"));

            unsafe
            {
                Span <byte> buf = stackalloc byte[20];
                buf.Fill(0xAA);
                original.WriteToUnsafe(buf.Slice(3));
                Assert.That(buf.ToArray().AsSlice().ToString("X"), Is.EqualTo("AA AA AA 1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA AA"));
            }

            // errors

            Assert.That(() => original.WriteTo(Span <byte> .Empty), Throws.InstanceOf <ArgumentException>(), "Target buffer is empty");

            scratch = MutableSlice.Repeat(0xAA, 16);
            Assert.That(() => original.WriteTo(scratch.Span.Slice(0, 11)), Throws.InstanceOf <ArgumentException>(), "Target buffer is too small");
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA"), "Buffer should not have been overwritten!");
        }
        public void Test_MutableSliceComparer_GetHashCode_Should_Return_Same_As_Slice()
        {
            var cmp = MutableSlice.Comparer.Default;

            Assert.That(cmp, Is.Not.Null);

            Assert.That(cmp.GetHashCode(MutableSlice.Nil), Is.EqualTo(MutableSlice.Nil.GetHashCode()));
            Assert.That(cmp.GetHashCode(MutableSlice.Empty), Is.EqualTo(MutableSlice.Empty.GetHashCode()));
            Assert.That(cmp.GetHashCode(MutableSlice.Nil), Is.Not.EqualTo(MutableSlice.Empty));

            var rnd = new Random(123456);

            for (int i = 0; i < 100; i++)
            {
                var s = MutableSlice.Random(rnd, rnd.Next(1, 16));
                Assert.That(cmp.GetHashCode(s), Is.EqualTo(s.GetHashCode()));
            }
        }
예제 #10
0
        /// <summary>Remove an entity from the index</summary>
        /// <param name="trans">Transaction to use</param>
        /// <param name="id">Id of the entity that has been deleted</param>
        /// <param name="value">Previous value of the entity in the index</param>
        public async Task <bool> RemoveAsync(IFdbTransaction trans, long id, TValue value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            var key  = this.Subspace[value];
            var data = await trans.GetAsync(key).ConfigureAwait(false);

            if (data.HasValue)
            {
                var builder = new CompressedBitmapBuilder(MutableSlice.AsUnsafeMutableSlice(data));
                builder.Clear((int)id);                 //BUGBUG: 64 bit id!
                trans.Set(key, builder.ToSlice());
                return(true);
            }
            return(false);
        }
예제 #11
0
        /// <summary>Returns a list of ids matching a specific value</summary>
        /// <param name="trans"></param>
        /// <param name="value">Value to lookup</param>
        /// <param name="reverse"></param>
        /// <returns>List of document ids matching this value for this particular index (can be empty if no document matches)</returns>
        public async Task <IEnumerable <long>?> LookupAsync(IFdbReadOnlyTransaction trans, TValue value, bool reverse = false)
        {
            var key  = this.Subspace[value];
            var data = await trans.GetAsync(key).ConfigureAwait(false);

            if (data.IsNull)
            {
                return(null);
            }
            if (data.IsEmpty)
            {
                return(Enumerable.Empty <long>());
            }
            var bitmap = new CompressedBitmap(MutableSlice.AsUnsafeMutableSlice(data));

            if (reverse)
            {
                throw new NotImplementedException();                      //TODO: GetView(reverse:true) !
            }
            return(bitmap.GetView().Select(x => (long)x /*BUGBUG 64 bits*/));
        }
예제 #12
0
        /// <summary>Update the indexed values of an entity</summary>
        /// <param name="trans">Transaction to use</param>
        /// <param name="id">Id of the entity that has changed</param>
        /// <param name="newValue">Previous value of this entity in the index</param>
        /// <param name="previousValue">New value of this entity in the index</param>
        /// <returns>True if a change was performed in the index; otherwise false (if <paramref name="previousValue"/> and <paramref name="newValue"/>)</returns>
        /// <remarks>If <paramref name="newValue"/> and <paramref name="previousValue"/> are identical, then nothing will be done. Otherwise, the old index value will be deleted and the new value will be added</remarks>
        public async Task <bool> UpdateAsync(IFdbTransaction trans, long id, TValue newValue, TValue previousValue)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            if (!this.ValueComparer.Equals(newValue, previousValue))
            {
                // remove previous value
                if (this.IndexNullValues || previousValue != null)
                {
                    var key  = this.Subspace[previousValue];
                    var data = await trans.GetAsync(key).ConfigureAwait(false);

                    if (data.HasValue)
                    {
                        var builder = new CompressedBitmapBuilder(MutableSlice.AsUnsafeMutableSlice(data));
                        builder.Clear((int)id);                         //BUGBUG: 64 bit id!
                        trans.Set(key, builder.ToSlice());
                    }
                }

                // add new value
                if (this.IndexNullValues || newValue != null)
                {
                    var key  = this.Subspace[newValue];
                    var data = await trans.GetAsync(key).ConfigureAwait(false);

                    var builder = data.HasValue ? new CompressedBitmapBuilder(MutableSlice.AsUnsafeMutableSlice(data)) : CompressedBitmapBuilder.Empty;
                    builder.Set((int)id);                     //BUGBUG: 64 bit id!
                    trans.Set(key, builder.ToSlice());
                }

                // cannot be both null, so we did at least something)
                return(true);
            }
            return(false);
        }
예제 #13
0
        /// <summary>Insert a newly created entity to the index</summary>
        /// <param name="trans">Transaction to use</param>
        /// <param name="id">Id of the new entity (that was never indexed before)</param>
        /// <param name="value">Value of this entity in the index</param>
        /// <returns>True if a value was inserted into the index; or false if <paramref name="value"/> is null and <see cref="IndexNullValues"/> is false, or if this <paramref name="id"/> was already indexed at this <paramref name="value"/>.</returns>
        public async Task <bool> AddAsync(IFdbTransaction trans, long id, TValue value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            if (this.IndexNullValues || value != null)
            {
                var key  = this.Subspace[value];
                var data = await trans.GetAsync(key).ConfigureAwait(false);

                var builder = data.HasValue ? new CompressedBitmapBuilder(MutableSlice.AsUnsafeMutableSlice(data)) : CompressedBitmapBuilder.Empty;

                //TODO: wasteful to crate a builder to only set on bit ?
                builder.Set((int)id);                 //BUGBUG: id should be 64-bit!

                //TODO: if bit was already set, skip the set ?
                trans.Set(key, builder.ToSlice());
                return(true);
            }
            return(false);
        }
        public void Test_Uuid96_TryWriteTo()
        {
            var original = Uuid96.Parse("1E2D3C4B-01234567-89ABCDEF");

            (var hi, var lo) = original;
            Assume.That(hi, Is.EqualTo(0x1E2D3C4B));
            Assume.That(lo, Is.EqualTo(0x0123456789ABCDEF));

            // span with more space
            var scratch = MutableSlice.Repeat(0xAA, 20);

            Assert.That(original.TryWriteTo(scratch.Span), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with no offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 20);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(0, 12)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA AA AA AA AA"));

            // span with offset
            scratch = MutableSlice.Repeat(0xAA, 20);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(4)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA"));

            // span with offset and exact size
            scratch = MutableSlice.Repeat(0xAA, 20);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(4, 12)), Is.True);
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA 1E 2D 3C 4B 01 23 45 67 89 AB CD EF AA AA AA AA"));

            // errors

            Assert.That(original.TryWriteTo(Span <byte> .Empty), Is.False, "Target buffer is empty");

            scratch = MutableSlice.Repeat(0xAA, 20);
            Assert.That(original.TryWriteTo(scratch.Span.Slice(0, 11)), Is.False, "Target buffer is too small");
            Assert.That(scratch.ToString("X"), Is.EqualTo("AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA"), "Buffer should not have been overwritten!");
        }
예제 #15
0
        public void Test_Incomplete_VersionStamp()
        {
            {             // 80-bits (no user version)
                var vs = VersionStamp.Incomplete();
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(ulong.MaxValue));
                Assert.That(vs.TransactionOrder, Is.EqualTo(ushort.MaxValue));
                Assert.That(vs.IsIncomplete, Is.True);
                Assert.That(vs.HasUserVersion, Is.False, "80-bits VersionStamps don't have a user version");
                Assert.That(vs.UserVersion, Is.Zero, "80-bits VersionStamps don't have a user version");

                Assert.That(vs.GetLength(), Is.EqualTo(10));
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("FF FF FF FF FF FF FF FF FF FF"));
                Assert.That(vs.ToString(), Is.EqualTo("@?"));
            }

            {             // 96-bits, default user version
                var vs = VersionStamp.Incomplete(0);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(ulong.MaxValue));
                Assert.That(vs.TransactionOrder, Is.EqualTo(ushort.MaxValue));
                Assert.That(vs.IsIncomplete, Is.True);
                Assert.That(vs.HasUserVersion, Is.True, "96-bits VersionStamps have a user version");
                Assert.That(vs.UserVersion, Is.EqualTo(0));

                Assert.That(vs.GetLength(), Is.EqualTo(12));
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("FF FF FF FF FF FF FF FF FF FF 00 00"));
                Assert.That(vs.ToString(), Is.EqualTo("@?#0"));
            }

            {             // 96 bits, custom user version
                var vs = VersionStamp.Incomplete(123);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(ulong.MaxValue));
                Assert.That(vs.TransactionOrder, Is.EqualTo(ushort.MaxValue));
                Assert.That(vs.HasUserVersion, Is.True);
                Assert.That(vs.UserVersion, Is.EqualTo(123));
                Assert.That(vs.IsIncomplete, Is.True);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("FF FF FF FF FF FF FF FF FF FF 00 7B"));
                Assert.That(vs.ToString(), Is.EqualTo("@?#123"));
            }

            {             // 96 bits, large user version
                var vs = VersionStamp.Incomplete(12345);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(ulong.MaxValue));
                Assert.That(vs.TransactionOrder, Is.EqualTo(ushort.MaxValue));
                Assert.That(vs.HasUserVersion, Is.True);
                Assert.That(vs.UserVersion, Is.EqualTo(12345));
                Assert.That(vs.IsIncomplete, Is.True);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("FF FF FF FF FF FF FF FF FF FF 30 39"));
                Assert.That(vs.ToString(), Is.EqualTo("@?#12345"));
            }

            Assert.That(() => VersionStamp.Incomplete(-1), Throws.ArgumentException, "User version cannot be negative");
            Assert.That(() => VersionStamp.Incomplete(65536), Throws.ArgumentException, "User version cannot be larger than 0xFFFF");

            {
                var writer = default(SliceWriter);
                writer.WriteFixed24BE(0xAAAAAA);
                VersionStamp.Incomplete(123).WriteTo(ref writer);
                writer.WriteFixed24BE(0xAAAAAA);
                Assert.That(writer.ToSlice().ToHexaString(' '), Is.EqualTo("AA AA AA FF FF FF FF FF FF FF FF FF FF 00 7B AA AA AA"));

                var reader = new SliceReader(writer.ToSlice());
                reader.Skip(3);
                var vs = VersionStamp.Parse(reader.ReadBytes(12));
                Assert.That(reader.Remaining, Is.EqualTo(3));

                Assert.That(vs.TransactionVersion, Is.EqualTo(ulong.MaxValue));
                Assert.That(vs.TransactionOrder, Is.EqualTo(ushort.MaxValue));
                Assert.That(vs.UserVersion, Is.EqualTo(123));
                Assert.That(vs.IsIncomplete, Is.True);
            }

            {
                var buf = MutableSlice.Repeat(0xAA, 18);
                VersionStamp.Incomplete(123).WriteTo(buf.Substring(3, 12));
                Assert.That(buf.ToHexaString(' '), Is.EqualTo("AA AA AA FF FF FF FF FF FF FF FF FF FF 00 7B AA AA AA"));
            }
        }
예제 #16
0
        public void Test_Complete_VersionStamp()
        {
            {             // 80-bits, no user version
                var vs = VersionStamp.Complete(0x0123456789ABCDEFUL, 123);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(0x0123456789ABCDEFUL));
                Assert.That(vs.TransactionOrder, Is.EqualTo(123));
                Assert.That(vs.HasUserVersion, Is.False);
                Assert.That(vs.UserVersion, Is.Zero);
                Assert.That(vs.IsIncomplete, Is.False);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("01 23 45 67 89 AB CD EF 00 7B"));
                Assert.That(vs.ToString(), Is.EqualTo("@81985529216486895-123"));
            }

            {             // 96 bits, default user version
                var vs = VersionStamp.Complete(0x0123456789ABCDEFUL, 123, 0);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(0x0123456789ABCDEFUL));
                Assert.That(vs.TransactionOrder, Is.EqualTo(123));
                Assert.That(vs.HasUserVersion, Is.True);
                Assert.That(vs.UserVersion, Is.Zero);
                Assert.That(vs.IsIncomplete, Is.False);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("01 23 45 67 89 AB CD EF 00 7B 00 00"));
                Assert.That(vs.ToString(), Is.EqualTo("@81985529216486895-123#0"));
            }

            {             // custom user version
                var vs = VersionStamp.Complete(0x0123456789ABCDEFUL, 123, 456);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(0x0123456789ABCDEFUL));
                Assert.That(vs.TransactionOrder, Is.EqualTo(123));
                Assert.That(vs.HasUserVersion, Is.True);
                Assert.That(vs.UserVersion, Is.EqualTo(456));
                Assert.That(vs.IsIncomplete, Is.False);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("01 23 45 67 89 AB CD EF 00 7B 01 C8"));
                Assert.That(vs.ToString(), Is.EqualTo("@81985529216486895-123#456"));
            }

            {             // two bytes user version
                var vs = VersionStamp.Complete(0x0123456789ABCDEFUL, 12345, 6789);
                Dump(vs);
                Assert.That(vs.TransactionVersion, Is.EqualTo(0x0123456789ABCDEFUL));
                Assert.That(vs.TransactionOrder, Is.EqualTo(12345));
                Assert.That(vs.UserVersion, Is.EqualTo(6789));
                Assert.That(vs.IsIncomplete, Is.False);
                Assert.That(vs.ToSlice().ToHexaString(' '), Is.EqualTo("01 23 45 67 89 AB CD EF 30 39 1A 85"));
                Assert.That(vs.ToString(), Is.EqualTo("@81985529216486895-12345#6789"));
            }

            Assert.That(() => VersionStamp.Complete(0x0123456789ABCDEFUL, 0, -1), Throws.ArgumentException, "User version cannot be negative");
            Assert.That(() => VersionStamp.Complete(0x0123456789ABCDEFUL, 0, 65536), Throws.ArgumentException, "User version cannot be larger than 0xFFFF");

            {
                var writer = default(SliceWriter);
                writer.WriteFixed24BE(0xAAAAAA);
                VersionStamp.Complete(0x0123456789ABCDEFUL, 123, 456).WriteTo(ref writer);
                writer.WriteFixed24BE(0xAAAAAA);
                Assert.That(writer.ToSlice().ToHexaString(' '), Is.EqualTo("AA AA AA 01 23 45 67 89 AB CD EF 00 7B 01 C8 AA AA AA"));

                var reader = new SliceReader(writer.ToSlice());
                reader.Skip(3);
                var vs = VersionStamp.Parse(reader.ReadBytes(12));
                Assert.That(reader.Remaining, Is.EqualTo(3));

                Assert.That(vs.TransactionVersion, Is.EqualTo(0x0123456789ABCDEFUL));
                Assert.That(vs.TransactionOrder, Is.EqualTo(123));
                Assert.That(vs.UserVersion, Is.EqualTo(456));
                Assert.That(vs.IsIncomplete, Is.False);
            }

            {
                var buf = MutableSlice.Repeat(0xAA, 18);
                VersionStamp.Complete(0x0123456789ABCDEFUL, 123, 456).WriteTo(buf.Substring(3, 12));
                Assert.That(buf.ToHexaString(' '), Is.EqualTo("AA AA AA 01 23 45 67 89 AB CD EF 00 7B 01 C8 AA AA AA"));
            }
        }
        /// <summary>Returns the bounds of the uncompressed bitmap index</summary>
        internal static BitRange ComputeBounds(MutableSlice data, int words = -1)
        {
            int count = data.Count;

            if (count > 0 && count < 8)
            {
                throw new ArgumentException("Bitmap buffer size is too small", nameof(data));
            }
            if ((count & 3) != 0)
            {
                throw new ArgumentException("Bitmap buffer size must be a multiple of 4 bytes", nameof(data));
            }

            // if the bitmap is empty, return 0..0
            if (count == 0)
            {
                return(BitRange.Empty);
            }

            // the highest bit is in the header
            int highest;

            if (words < 0)
            {             // the bitmap is complete so we can read the header
                highest = (int)data.ReadUInt32(0, 4);
                if (highest < 0 && highest != -1)
                {
                    throw new InvalidOperationException("Corrupted bitmap buffer (highest bit underflow)" + highest);
                }
            }
            else
            {             // the bitmap is not finished, we need to find it ourselves
                highest = (words * 31) - 1;
                // check the last word if it is a literal
                int last = new CompressedWord(data.ReadUInt32(data.Count - 4, 4)).GetHighestBit();
                if (last >= 0)
                {
                    highest += last - 30;
                }
            }

            // to compute the lowest bit, we need to look for initial fillers with 0-bit, and the check the first literal
            int lowest = 0;

            using (var iter = new CompressedBitmapWordIterator(data))
            {
                while (iter.MoveNext() && lowest >= 0)
                {
                    var word = iter.Current;
                    if (word.IsLiteral)
                    {                     // count the lower bits that are 0 for the final tally
                        int first = word.GetLowestBit();
                        if (first < 0)
                        {                         // all zeroes (not regular)
                            lowest += 31;
                            continue;
                        }

                        lowest += first;
                        break;
                    }

                    if (word.FillBit == 1)
                    {                     // this is all 1s
                        break;
                    }

                    // this is 0-bit FILLER
                    lowest += 31 * word.FillCount;
                }
                if (lowest < 0)
                {
                    throw new InvalidOperationException("Corrupted bitmap buffer (lowest bit overflow)" + lowest);
                }
            }

            //Console.WriteLine("Computed bounds are: {0}..{1}", lowest, highest);
            return(new BitRange(lowest, highest));
        }
예제 #18
0
 public CompressedBitmapBuilder(MutableSlice data)
     : this(new CompressedBitmap(data))
 {
 }