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); } }
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())); } }
/// <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); }
/// <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*/)); }
/// <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); }
/// <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!"); }
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")); } }
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)); }
public CompressedBitmapBuilder(MutableSlice data) : this(new CompressedBitmap(data)) { }