public static Slice EncodeKey <T1, T2>(this ICompositeKeyEncoder <T1, T2> encoder, Slice prefix, T1 item1, T2 item2) { var writer = new SliceWriter(prefix.Count + 24); writer.WriteBytes(in prefix); encoder.WriteKeyTo(ref writer, item1, item2); return(writer.ToSlice()); }
public static Slice EncodeKey <T1>([NotNull] this IKeyEncoder <T1> encoder, Slice prefix, T1 value) { var writer = new SliceWriter(prefix.Count + 16); // ~16 bytes si T1 = Guid writer.WriteBytes(in prefix); encoder.WriteKeyTo(ref writer, value); return(writer.ToSlice()); }
/// <summary>Pack a tuple into a key, using the specified encoder</summary> public static Slice Pack <TTuple>(this IDynamicKeyEncoder encoder, TTuple tuple) where TTuple : IVarTuple { var writer = new SliceWriter(checked (tuple.Count * 8)); encoder.PackKey(ref writer, tuple); return(writer.ToSlice()); }
public static Slice EncodePartialKey <T1, T2>(this ICompositeKeyEncoder <T1, T2> encoder, Slice prefix, T1 item1) { var writer = new SliceWriter(prefix.Count + 16); writer.WriteBytes(in prefix); var tuple = (item1, default(T2)); encoder.WriteKeyPartsTo(ref writer, 1, ref tuple); return(writer.ToSlice()); }
public void Test_WriteBytes_Resize_Buffer() { // check buffer resize occurs as intended var original = new byte[32]; var writer = new SliceWriter(original); Assert.That(writer.Buffer, Is.SameAs(original)); // first write should not resize the buffer writer.WriteBytes(Slice.Repeat((byte)'a', 24)); Assert.That(writer.Buffer, Is.SameAs(original)); Assert.That(writer.ToSlice().ToStringAscii(), Is.EqualTo("aaaaaaaaaaaaaaaaaaaaaaaa")); // second write should resize the buffer writer.WriteBytes(Slice.Repeat((byte)'b', 24)); // buffer should have been replaced with larger one Assert.That(writer.Buffer, Is.Not.SameAs(original)); Assert.That(writer.Buffer.Length, Is.GreaterThanOrEqualTo(48)); //but the content should be unchanged Assert.That(writer.ToSlice().ToStringAscii(), Is.EqualTo("aaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbb")); // adding exactly what is missing should not resize the buffer writer = new SliceWriter(original); writer.WriteBytes(Slice.Repeat((byte)'c', original.Length)); Assert.That(writer.Buffer, Is.SameAs(original)); Assert.That(writer.ToSlice().ToStringAscii(), Is.EqualTo("cccccccccccccccccccccccccccccccc")); // adding nothing should not resize the buffer writer.WriteBytes(Slice.Empty); Assert.That(writer.Buffer, Is.SameAs(original)); Assert.That(writer.ToSlice().ToStringAscii(), Is.EqualTo("cccccccccccccccccccccccccccccccc")); // adding a single byte should resize the buffer writer.WriteBytes(Slice.FromChar('Z')); Assert.That(writer.Buffer, Is.Not.SameAs(original)); Assert.That(writer.Buffer.Length, Is.GreaterThanOrEqualTo(33)); Assert.That(writer.ToSlice().ToStringAscii(), Is.EqualTo("ccccccccccccccccccccccccccccccccZ")); }
public void Test_Skip() { var writer = new SliceWriter(); writer.WriteBytes(Slice.FromString("hello")); Assert.That(writer.Position, Is.EqualTo(5)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("hello")); // default pad is 255 Assert.That(writer.Skip(3), Is.EqualTo(5)); Assert.That(writer.Position, Is.EqualTo(8)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("hello<FF><FF><FF>")); writer.WriteBytes(Slice.FromString("world")); Assert.That(writer.Position, Is.EqualTo(13)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("hello<FF><FF><FF>world")); // custom pad Assert.That(writer.Skip(5, 42), Is.EqualTo(13)); Assert.That(writer.Position, Is.EqualTo(18)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("hello<FF><FF><FF>world*****")); }
public void Test_ToSlice() { var writer = new SliceWriter(64); var slice = writer.ToSlice(); //note: slice.Array is not guaranteed to be equal to writer.Buffer Assert.That(slice.Count, Is.EqualTo(0)); Assert.That(slice.Offset, Is.EqualTo(0)); writer.WriteBytes(Slice.FromString("hello world!")); slice = writer.ToSlice(); Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(0)); Assert.That(slice.Count, Is.EqualTo(12)); Assert.That(slice.ToStringAscii(), Is.EqualTo("hello world!")); writer.WriteBytes(Slice.FromString("foo")); slice = writer.ToSlice(); Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(0)); Assert.That(slice.Count, Is.EqualTo(15)); Assert.That(slice.ToStringAscii(), Is.EqualTo("hello world!foo")); }
public void Test_Flush() { var writer = new SliceWriter(); writer.WriteBytes(Slice.FromString("hello world!")); Assert.That(writer.Position, Is.EqualTo(12)); writer.Flush(5); Assert.That(writer.Position, Is.EqualTo(7)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo(" world!")); writer.Flush(1); Assert.That(writer.Position, Is.EqualTo(6)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("world!")); writer.Flush(0); Assert.That(writer.Position, Is.EqualTo(6)); Assert.That(writer.ToSlice().ToString(), Is.EqualTo("world!")); // REVIEW: should we throw if we flush more bytes than in the writer? (currently, it just clears it) writer.Flush(7); Assert.That(writer.Position, Is.EqualTo(0)); Assert.That(writer.ToSlice(), Is.EqualTo(Slice.Empty)); }
public void Test_AppendBytes() { var writer = new SliceWriter(64); var slice = writer.AppendBytes(Slice.Empty); //note: slice.Array is not guaranteed to be equal to writer.Buffer Assert.That(slice.Offset, Is.EqualTo(0)); Assert.That(slice.Count, Is.EqualTo(0)); slice = writer.AppendBytes(Slice.FromString("hello world!")); Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(0)); Assert.That(slice.Count, Is.EqualTo(12)); Assert.That(slice.ToStringUtf8(), Is.EqualTo("hello world!")); Assert.That(writer.ToSlice().ToStringUtf8(), Is.EqualTo("hello world!")); var foo = Slice.FromString("foo"); slice = writer.AppendBytes(foo); Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(12)); Assert.That(slice.Count, Is.EqualTo(3)); Assert.That(slice.ToStringUtf8(), Is.EqualTo("foo")); Assert.That(writer.ToSlice().ToStringUtf8(), Is.EqualTo("hello world!foo")); var bar = Slice.FromString("bar"); unsafe { fixed(byte *ptr = &bar.DangerousGetPinnableReference()) { slice = writer.AppendBytes(ptr, 3); } } Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(15)); Assert.That(slice.Count, Is.EqualTo(3)); Assert.That(slice.ToStringUtf8(), Is.EqualTo("bar")); Assert.That(writer.ToSlice().ToStringUtf8(), Is.EqualTo("hello world!foobar")); var baz = Slice.FromString("baz"); unsafe { fixed(byte *ptr = &baz.DangerousGetPinnableReference()) { //TODO: this test was using ReadOnlySpan<byte>, update it once we enable support for these! slice = writer.AppendBytes(ptr, 3); } } Assert.That(slice.Array, Is.SameAs(writer.Buffer)); Assert.That(slice.Offset, Is.EqualTo(18)); Assert.That(slice.Count, Is.EqualTo(3)); Assert.That(slice.ToStringUtf8(), Is.EqualTo("baz")); Assert.That(writer.ToSlice().ToStringUtf8(), Is.EqualTo("hello world!foobarbaz")); unsafe { slice = writer.AppendBytes(null, 0); } //note: slice.Array is not guaranteed to be equal to writer.Buffer Assert.That(slice.Offset, Is.EqualTo(0)); //REVIEW: should we return (Buffer, Position, 0) instead of (EmptyArray, 0, 0) ? Assert.That(slice.Count, Is.EqualTo(0)); }
/// <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); }