/// <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)); }