private static void WriteUInt64Slow(ref SliceWriter writer, ulong value)
        {
            // We are only called for values >= 256

            // determine the number of bytes needed to encode the value
            int bytes = NumberOfBytes(value);

            writer.EnsureBytes(bytes + 1);

            var buffer = writer.Buffer;
            int p      = writer.Position;

            // simple case (ulong can only be positive)
            buffer[p++] = (byte)(FdbTupleTypes.IntBase + bytes);

            if (bytes > 0)
            {
                // head
                --bytes;
                int shift = bytes << 3;

                while (bytes-- > 0)
                {
                    buffer[p++] = (byte)(value >> shift);
                    shift      -= 8;
                }
                // last
                buffer[p++] = (byte)value;
            }

            writer.Position = p;
        }
 /// <summary>Writes a char encoded in UTF-8</summary>
 public static void WriteChar(ref SliceWriter writer, char value)
 {
     if (value == 0)
     {             // NUL => "00 0F"
         // note: \0 is the only unicode character that will produce a zero byte when converted in UTF-8
         writer.WriteByte4(FdbTupleTypes.Utf8, 0x00, 0xFF, 0x00);
     }
     else if (value < 0x80)
     {             // 0x00..0x7F => 0xxxxxxx
         writer.WriteByte3(FdbTupleTypes.Utf8, (byte)value, 0x00);
     }
     else if (value < 0x800)
     {             // 0x80..0x7FF => 110xxxxx 10xxxxxx => two bytes
         writer.WriteByte4(FdbTupleTypes.Utf8, (byte)(0xC0 | (value >> 6)), (byte)(0x80 | (value & 0x3F)), 0x00);
     }
     else
     {             // 0x800..0xFFFF => 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
         // note: System.Char is 16 bits, and thus cannot represent UNICODE chars above 0xFFFF.
         // => This means that a System.Char will never take more than 3 bytes in UTF-8 !
         var tmp = Encoding.UTF8.GetBytes(new string(value, 1));
         writer.EnsureBytes(tmp.Length + 2);
         writer.UnsafeWriteByte(FdbTupleTypes.Utf8);
         writer.UnsafeWriteBytes(tmp, 0, tmp.Length);
         writer.UnsafeWriteByte(0x00);
     }
 }
 /// <summary>Writes a 64-bit UUID</summary>
 public static void WriteUuid64(ref SliceWriter writer, Uuid64 value)
 {
     writer.EnsureBytes(9);
     writer.UnsafeWriteByte(FdbTupleTypes.Uuid64);
     unsafe
     {
         byte *ptr = stackalloc byte[8];
         value.WriteTo(ptr);
         writer.UnsafeWriteBytes(ptr, 8);
     }
 }
 /// <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>Writes a RFC 4122 encoded 16-byte Microsoft GUID</summary>
        public static void WriteGuid(ref SliceWriter writer, Guid value)
        {
            writer.EnsureBytes(17);
            writer.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.UnsafeWriteBytes(ptr, 16);
            }
        }
        private static void WriteInt64Slow(ref SliceWriter writer, long value)
        {
            // we are only called for values <= -256 or >= 256

            // determine the number of bytes needed to encode the absolute value
            int bytes = NumberOfBytes(value);

            writer.EnsureBytes(bytes + 1);

            var buffer = writer.Buffer;
            int p      = writer.Position;

            ulong v;

            if (value > 0)
            {             // simple case
                buffer[p++] = (byte)(FdbTupleTypes.IntBase + bytes);
                v           = (ulong)value;
            }
            else
            {             // we will encode the one's complement of the absolute value
                // -1 => 0xFE
                // -256 => 0xFFFE
                // -65536 => 0xFFFFFE
                buffer[p++] = (byte)(FdbTupleTypes.IntBase - bytes);
                v           = (ulong)(~(-value));
            }

            if (bytes > 0)
            {
                // head
                --bytes;
                int shift = bytes << 3;

                while (bytes-- > 0)
                {
                    buffer[p++] = (byte)(v >> shift);
                    shift      -= 8;
                }
                // last
                buffer[p++] = (byte)v;
            }
            writer.Position = p;
        }
        /// <summary>Writes a buffer with all instances of 0 escaped as '00 FF'</summary>
        internal static void WriteNulEscapedBytes(ref SliceWriter writer, byte type, [NotNull] byte[] value, int offset, int count)
        {
            int n = count;

            // we need to know if there are any NUL chars (\0) that need escaping...
            // (we will also need to add 1 byte to the buffer size per NUL)
            for (int i = offset, end = offset + count; i < end; ++i)
            {
                if (value[i] == 0)
                {
                    ++n;
                }
            }

            writer.EnsureBytes(n + 2);
            var buffer = writer.Buffer;
            int p      = writer.Position;

            buffer[p++] = type;
            if (n > 0)
            {
                if (n == count)
                {                 // no NULs in the string, can copy all at once
                    SliceHelpers.CopyBytesUnsafe(buffer, p, value, offset, n);
                    p += n;
                }
                else
                {                 // we need to escape all NULs
                    for (int i = offset, end = offset + count; i < end; ++i)
                    {
                        byte b = value[i];
                        buffer[p++] = b;
                        if (b == 0)
                        {
                            buffer[p++] = 0xFF;
                        }
                    }
                }
            }
            buffer[p]       = FdbTupleTypes.Nil;
            writer.Position = p + 1;
        }
        /// <summary>Writes a buffer with all instances of 0 escaped as '00 FF'</summary>
        private static void WriteNulEscapedBytes(ref SliceWriter writer, byte type, [NotNull] byte[] value)
        {
            int n = value.Length;

            // we need to know if there are any NUL chars (\0) that need escaping...
            // (we will also need to add 1 byte to the buffer size per NUL)
            foreach (byte b in value)
            {
                if (b == 0)
                {
                    ++n;
                }
            }

            writer.EnsureBytes(n + 2);
            var buffer = writer.Buffer;
            int p      = writer.Position;

            buffer[p++] = type;
            if (n > 0)
            {
                if (n == value.Length)
                {                 // no NULs in the string, can copy all at once
                    SliceHelpers.CopyBytesUnsafe(buffer, p, value, 0, n);
                    p += n;
                }
                else
                {                 // we need to escape all NULs
                    foreach (byte b in value)
                    {
                        buffer[p++] = b;
                        if (b == 0)
                        {
                            buffer[p++] = 0xFF;
                        }
                    }
                }
            }
            buffer[p++]     = FdbTupleTypes.Nil;
            writer.Position = p;
        }
        /// <summary>Writes an Double at the end, and advance the cursor</summary>
        /// <param name="writer">Target buffer</param>
        /// <param name="value">IEEE Floating point, 64 bits, High Endian</param>
        public static void WriteDouble(ref SliceWriter writer, double value)
        {
            // The double is converted to its Big-Endian IEEE binary representation
            // - If the sign bit is set, flip all the bits
            // - If the sign bit is not set, just flip the sign bit
            // This ensures that all negative numbers have their first byte < 0x80, and all positive numbers have their first byte >= 0x80

            // Special case for NaN: All variants are normalized to float.NaN !
            if (double.IsNaN(value))
            {
                value = double.NaN;
            }

            // note: we could use BitConverter.DoubleToInt64Bits(...), but it does the same thing, and also it does not exist for floats...
            ulong bits;

            unsafe { bits = *((ulong *)&value); }

            if ((bits & 0x8000000000000000UL) != 0)
            {             // negative
                bits = ~bits;
            }
            else
            {             // postive
                bits |= 0x8000000000000000UL;
            }
            writer.EnsureBytes(9);
            var buffer = writer.Buffer;
            int p      = writer.Position;

            buffer[p]       = FdbTupleTypes.Double;
            buffer[p + 1]   = (byte)(bits >> 56);
            buffer[p + 2]   = (byte)(bits >> 48);
            buffer[p + 3]   = (byte)(bits >> 40);
            buffer[p + 4]   = (byte)(bits >> 32);
            buffer[p + 5]   = (byte)(bits >> 24);
            buffer[p + 6]   = (byte)(bits >> 16);
            buffer[p + 7]   = (byte)(bits >> 8);
            buffer[p + 8]   = (byte)(bits);
            writer.Position = p + 9;
        }
        private static unsafe void WriteUnescapedAsciiChars(ref SliceWriter writer, char *chars, int count)
        {
            Contract.Requires(chars != null && count >= 0);

            // copy and convert an ASCII string directly into the destination buffer

            writer.EnsureBytes(2 + count);
            int   pos = writer.Position;
            char *end = chars + count;

            fixed(byte *buffer = writer.Buffer)
            {
                buffer[pos++] = FdbTupleTypes.Utf8;
                while (chars < end)
                {
                    buffer[pos++] = (byte)(*chars++);
                }
                buffer[pos]     = 0x00;
                writer.Position = pos + 1;
            }
        }
        /// <summary>Writes an Single at the end, and advance the cursor</summary>
        /// <param name="writer">Target buffer</param>
        /// <param name="value">IEEE Floating point, 32 bits, High Endian</param>
        public static void WriteSingle(ref SliceWriter writer, float value)
        {
            // The double is converted to its Big-Endian IEEE binary representation
            // - If the sign bit is set, flip all the bits
            // - If the sign bit is not set, just flip the sign bit
            // This ensures that all negative numbers have their first byte < 0x80, and all positive numbers have their first byte >= 0x80

            // Special case for NaN: All variants are normalized to float.NaN !
            if (float.IsNaN(value))
            {
                value = float.NaN;
            }

            // note: there is no BitConverter.SingleToInt32Bits(...), so we have to do it ourselves...
            uint bits;

            unsafe { bits = *((uint *)&value); }

            if ((bits & 0x80000000U) != 0)
            {             // negative
                bits = ~bits;
            }
            else
            {             // postive
                bits |= 0x80000000U;
            }
            writer.EnsureBytes(5);
            var buffer = writer.Buffer;
            int p      = writer.Position;

            buffer[p + 0]   = FdbTupleTypes.Single;
            buffer[p + 1]   = (byte)(bits >> 24);
            buffer[p + 2]   = (byte)(bits >> 16);
            buffer[p + 3]   = (byte)(bits >> 8);
            buffer[p + 4]   = (byte)(bits);
            writer.Position = p + 5;
        }
        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);
        }