/// <summary>Read an embedded tuple, without parsing it</summary>
        internal static Slice ReadEmbeddedTupleBytes(ref TupleReader reader)
        {
            // The current embedded tuple starts here, and stops on a <00>, but itself can contain more embedded tuples, and could have a <00> bytes as part of regular items (like bytes, strings, that end with <00> or could contain a <00><FF> ...)
            // This means that we have to parse the tuple recursively, discard the tokens, and note where the cursor ended. The parsing of the tuple itself will be processed later.

            ++reader.Depth;
            int start = reader.Input.Position;

            reader.Input.Skip(1);

            while (reader.Input.HasMore)
            {
                var token = ParseNext(ref reader);
                // the token will be Nil for either the end of the stream, or the end of the tuple
                // => since we already tested Input.HasMore, we know we are in the later case
                if (token.IsNull)
                {
                    --reader.Depth;
                    //note: ParseNext() has already eaten the <00>
                    int end = reader.Input.Position;
                    return(reader.Input.Buffer.Substring(start, end - start));
                }
                // else: ignore this token, it will be processed later if the tuple is unpacked and accessed
            }

            throw new FormatException(String.Format("Truncated embedded tuple started at index {0}/{1}", start, reader.Input.Buffer.Count));
        }
        public override T DecodeOrderedSelfTerm(ref SliceReader input)
        {
            //HACKHACK: we lose the current depth!
            var  reader = new TupleReader(input);
            T    value;
            bool res = STuple.DecodeNext <T>(ref reader, out value);

            input = reader.Input;
            return(res ? value : m_missingValue);
        }
        /// <summary>Visit the different tokens of a packed tuple</summary>
        /// <param name="reader">Reader positionned at the start of a packed tuple</param>
        /// <param name="visitor">Lambda called for each segment of a tuple. Returns true to continue parsing, or false to stop</param>
        /// <returns>Number of tokens that have been visited until either <paramref name="visitor"/> returned false, or <paramref name="reader"/> reached the end.</returns>
        public static T VisitNext <T>(ref TupleReader reader, Func <Slice, FdbTupleSegmentType, T> visitor)
        {
            if (!reader.Input.HasMore)
            {
                throw new InvalidOperationException("The reader has already reached the end");
            }
            var token = FdbTupleParser.ParseNext(ref reader);

            return(visitor(token, FdbTupleTypes.DecodeSegmentType(ref token)));
        }
 /// <summary>Skip a number of tokens</summary>
 /// <param name="reader">Cursor in the packed tuple to decode</param>
 /// <param name="count">Number of tokens to skip</param>
 /// <returns>True if there was <paramref name="count"/> tokens, false if the reader was too small.</returns>
 /// <remarks>Even if this method return true, you need to check that the reader has not reached the end before reading more token!</remarks>
 public static bool Skip(ref TupleReader reader, int count)
 {
     while (count-- > 0)
     {
         if (!reader.Input.HasMore)
         {
             return(false);
         }
         var token = FdbTupleParser.ParseNext(ref reader);
         if (token.IsNull)
         {
             return(false);
         }
     }
     return(true);
 }
        /// <summary>Decode the next token from a packed tuple</summary>
        /// <param name="reader">Parser from wich to read the next token</param>
        /// <returns>Token decoded, or Slice.Nil if there was no more data in the buffer</returns>
        public static Slice ParseNext(ref TupleReader reader)
        {
            int type = reader.Input.PeekByte();

            switch (type)
            {
            case -1:
            {                     // End of Stream
                return(Slice.Nil);
            }

            case FdbTupleTypes.Nil:
            {                     // <00> / <00><FF> => null
                if (reader.Depth > 0)
                {                 // must be <00><FF> inside an embedded tuple
                    if (reader.Input.PeekByteAt(1) == 0xFF)
                    {             // this is a Nil entry
                        reader.Input.Skip(2);
                        return(Slice.Empty);
                    }
                    else
                    {                             // this is the end of the embedded tuple
                        reader.Input.Skip(1);
                        return(Slice.Nil);
                    }
                }
                else
                {                         // can be <00> outside an embedded tuple
                    reader.Input.Skip(1);
                    return(Slice.Empty);
                }
            }

            case FdbTupleTypes.Bytes:
            {                     // <01>(bytes)<00>
                return(reader.Input.ReadByteString());
            }

            case FdbTupleTypes.Utf8:
            {                     // <02>(utf8 bytes)<00>
                return(reader.Input.ReadByteString());
            }

            case FdbTupleTypes.TupleStart:
            {                     // <03>(packed tuple)<04>
                //PERF: currently, we will first scan to get all the bytes of this tuple, and parse it later.
                // This means that we may need to scan multiple times the bytes, which may not be efficient if there are multiple embedded tuples inside each other
                return(ReadEmbeddedTupleBytes(ref reader));
            }

            case FdbTupleTypes.Single:
            {                     // <20>(4 bytes)
                return(reader.Input.ReadBytes(5));
            }

            case FdbTupleTypes.Double:
            {                     // <21>(8 bytes)
                return(reader.Input.ReadBytes(9));
            }

            case FdbTupleTypes.Uuid128:
            {                     // <30>(16 bytes)
                return(reader.Input.ReadBytes(17));
            }

            case FdbTupleTypes.Uuid64:
            {                     // <31>(8 bytes)
                return(reader.Input.ReadBytes(9));
            }

            case FdbTupleTypes.AliasDirectory:
            case FdbTupleTypes.AliasSystem:
            {                     // <FE> or <FF>
                return(reader.Input.ReadBytes(1));
            }
            }

            if (type <= FdbTupleTypes.IntPos8 && type >= FdbTupleTypes.IntNeg8)
            {
                int bytes = type - FdbTupleTypes.IntZero;
                if (bytes < 0)
                {
                    bytes = -bytes;
                }

                return(reader.Input.ReadBytes(1 + bytes));
            }

            throw new FormatException(String.Format("Invalid tuple type byte {0} at index {1}/{2}", type, reader.Input.Position, reader.Input.Buffer.Count));
        }