public void PackTo(ref SliceWriter writer)
 {
     if (m_packed.IsPresent)
     {
         writer.WriteBytes(m_packed);
     }
 }
예제 #2
0
        void IUnorderedTypeCodec <TDocument> .EncodeUnorderedSelfTerm(ref SliceWriter output, TDocument value)
        {
            var packed = EncodeInternal(value);

            Contract.Assert(packed.Count >= 0);
            output.WriteVarint32((uint)packed.Count);
            output.WriteBytes(packed);
        }
        internal static SliceWriter OpenWriter(this IKeySubspace self, int extra = 32)
        {
            var key = self.GetPrefix();
            var sw  = new SliceWriter(key.Count + extra);            //TODO: BufferPool ?

            sw.WriteBytes(key);
            return(sw);
        }
예제 #4
0
        /// <summary>Encode a value into a key, with an additional prefix, using the specified encoder</summary>
        public static Slice EncodeKey <T1>(this IKeyEncoder <T1> encoder, Slice prefix, [AllowNull] T1 value)
        {
            var writer = new SliceWriter(prefix.Count + 16);             // ~16 bytes si T1 = Guid

            writer.WriteBytes(prefix);
            encoder.WriteKeyTo(ref writer, value);
            return(writer.ToSlice());
        }
예제 #5
0
        /// <summary>Encode a pair of values into a key, with an additional prefix, using the specified encoder</summary>
        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(prefix);
            encoder.WriteKeyTo(ref writer, item1, item2);
            return(writer.ToSlice());
        }
예제 #6
0
        public void PackTo(ref SliceWriter writer)
        {
            var slices = m_slices;

            for (int n = m_count, p = m_offset; n > 0; n--)
            {
                writer.WriteBytes(slices[p++]);
            }
        }
 public void WriteKeyTo(ref SliceWriter writer, TKey value)
 {
     if (this.Pack is Func <TKey, Slice> f)
     {
         writer.WriteBytes(f(value));
         return;
     }
     throw new InvalidOperationException();
 }
예제 #8
0
        /// <summary>Pack a tuple into a key, with an additional prefix, using the specified encoder</summary>
        public static Slice Pack <TTuple>(this IDynamicKeyEncoder encoder, Slice prefix, TTuple tuple)
            where TTuple : IVarTuple
        {
            var writer = new SliceWriter(checked (prefix.Count + tuple.Count * 8));

            writer.WriteBytes(prefix);
            encoder.PackKey(ref writer, tuple);
            return(writer.ToSlice());
        }
예제 #9
0
        /// <summary>Encode only the first part of a key, with an additional prefix, using the specified encoder</summary>
        public static Slice EncodePartialKey <T1, T2>(this ICompositeKeyEncoder <T1, T2> encoder, Slice prefix, T1 item1)
        {
            var writer = new SliceWriter(prefix.Count + 16);

            writer.WriteBytes(prefix);
            var tuple = (item1, default(T2));

            encoder.WriteKeyPartsTo(ref writer, 1, ref tuple);
            return(writer.ToSlice());
        }
예제 #10
0
        public void Test_Tail()
        {
            var writer = new SliceWriter(64);
            var slice  = writer.Tail(0);

            Assert.That(slice.Count, Is.EqualTo(0));
            Assert.That(slice.Offset, Is.EqualTo(0));
            //note: slice.Array is not guaranteed to be equal to writer.Buffer
            Assert.That(() => writer.Head(1), Throws.InstanceOf <ArgumentOutOfRangeException>());

            writer.WriteBytes(Slice.FromString("hello world!"));
            slice = writer.Tail(6);
            Assert.That(slice.Array, Is.SameAs(writer.Buffer));
            Assert.That(slice.Offset, Is.EqualTo(6));
            Assert.That(slice.Count, Is.EqualTo(6));
            Assert.That(slice.ToStringAscii(), Is.EqualTo("world!"));

            slice = writer.Tail(12);
            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!"));

            Assert.That(() => writer.Tail(13), Throws.InstanceOf <ArgumentOutOfRangeException>());
            Assert.That(() => writer.Tail(-1), Throws.InstanceOf <ArgumentOutOfRangeException>());

            writer.WriteBytes(Slice.FromString("foo"));
            slice = writer.Tail(3);
            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.ToStringAscii(), Is.EqualTo("foo"));

            slice = writer.Tail(15);
            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"));

            Assert.That(() => writer.Tail(16), Throws.InstanceOf <ArgumentOutOfRangeException>());
        }
예제 #11
0
        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*****"));
        }
예제 #12
0
        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"));
        }
예제 #13
0
        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"));
        }
예제 #14
0
        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));
        }
        /// <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);
        }
 public void WriteKeyTo(ref SliceWriter writer, T key)
 {
     //TODO: PERF: optimize this!
     writer.WriteBytes(m_codec.EncodeOrdered(key));
 }
 static void Test(ref SliceWriter writer, byte[] value) => writer.WriteBytes(value);
예제 #18
0
 public void WriteKeyTo(ref SliceWriter writer, T value)
 {
     writer.WriteBytes(m_encoder(value));
 }
예제 #19
0
 public void WriteKeyTo(ref SliceWriter writer, Slice key)
 {
     writer.WriteBytes(key);
 }
        private static unsafe bool TryWriteUnescapedUtf8String(ref SliceWriter writer, char *chars, int count)
        {
            Contract.Requires(chars != null && count >= 0);

            // Several observations:
            // * Most strings will be keywords or ASCII-only with no zeroes. These can be copied directly to the buffer
            // * We will only attempt to optimze strings that don't have any 00 to escape to 00 FF. For these, we will fallback to converting to byte[] then escaping.
            // * Since .NET's strings are UTF-16, the max possible UNICODE value to encode is 0xFFFF, which takes 3 bytes in UTF-8 (EF BF BF)
            // * Most western europe languages have only a few non-ASCII chars here and there, and most of them will only use 2 bytes (ex: 'é' => 'C3 A9')
            // * More complex scripts with dedicated symbol pages (kanjis, arabic, ....) will take 2 or 3 bytes for each charecter.

            // We will first do a pass to check for the presence of 00 and non-ASCII chars
            // => if we find at least on 00, we fallback to escaping the result of Encoding.UTF8.GetBytes()
            // => if we find only ASCII (1..127) chars, we have an optimized path that will truncate the chars to bytes
            // => if not, we will use an UTF8Encoder to convert the string to UTF-8, in chunks, using a small buffer allocated on the stack

            #region First pass: look for \0 and non-ASCII chars

            // fastest way to check for non-ASCII, is to OR all the chars together, and look at bits 7 to 15. If they are not all zero, there is at least ONE non-ASCII char.
            // also, we abort as soon as we find a \0

            char *ptr = chars;
            char *end = chars + count;
            char  mask = '\0', c;
            while (ptr < end && (c = *ptr) != '\0')
            {
                mask |= c; ++ptr;
            }

            if (ptr < end)
            {
                return(false);                       // there is at least one \0 in the string
            }
            // bit 7-15 all unset means the string is pure ASCII
            if ((mask >> 7) == 0)
            {             // => directly dump the chars to the buffer
                WriteUnescapedAsciiChars(ref writer, chars, count);
                return(true);
            }

            #endregion

            #region Second pass: encode the string to UTF-8, in chunks

            // Here we know that there is at least one unicode char, and that there are no \0
            // We will tterate through the string, filling as much of the buffer as possible

            bool done;
            int  remaining = count;
            ptr = chars;

            // We need at most 3 * CHUNK_SIZE to encode the chunk
            // > For small strings, we will allocated exactly string.Length * 3 bytes, and will be done in one chunk
            // > For larger strings, we will call encoder.Convert(...) until it says it is done.
            const int CHUNK_SIZE = 1024;
            int       bufLen     = Encoding.UTF8.GetMaxByteCount(Math.Min(count, CHUNK_SIZE));
            byte *    buf        = stackalloc byte[bufLen];

            // We can not really predict the final size of the encoded string, but:
            // * Western languages have a few chars that usually need 2 bytes. If we pre-allocate 50% more bytes, it should fit most of the time, without too much waste
            // * Eastern langauges will have all chars encoded to 3 bytes. If we also pre-allocated 50% more, we should only need one resize of the buffer (150% x 2 = 300%), which is acceptable
            writer.EnsureBytes(checked (2 + count + (count >> 1)));            // preallocate 150% of the string + 2 bytes
            writer.UnsafeWriteByte(FdbTupleTypes.Utf8);

            var encoder = Encoding.UTF8.GetEncoder();
            // note: encoder.Convert() tries to fill up the buffer as much as possible with complete chars, and will set 'done' to true when all chars have been converted.
            do
            {
                int charsUsed, bytesUsed;
                encoder.Convert(ptr, remaining, buf, bufLen, true, out charsUsed, out bytesUsed, out done);
                if (bytesUsed > 0)
                {
                    writer.WriteBytes(buf, bytesUsed);
                }
                remaining -= charsUsed;
                ptr       += charsUsed;
            }while (!done);
            Contract.Assert(remaining == 0 && ptr == end);

            // close the string
            writer.WriteByte(0x00);

            #endregion

            return(true);
        }
예제 #21
0
 public void PackTo(ref SliceWriter writer)
 {
     writer.WriteBytes(m_prefix);
     m_items.PackTo(ref writer);
 }