/// <summary> /// Deserialize a stream of bytes into a message.<br />Use with caution. /// </summary> public static Message Deserialize(MemoryStream memoryStream) { // if `leaveOpen` is left false, this will dispose the memory stream once the BitEncodedStreamReader is disposed. // we don't want to dispose the memory stream because we continually reuse it for reading var rawBufferData = memoryStream.GetBufferWithReflection().Span; var stream = new BitEncodedSpanReader(rawBufferData); var scope = (ConnectionScope)stream.ReadByte(); var type = (MessageType)stream.Read7BitEncodedInt(); // i have chosen to initialize the list to this mathemetical number, // because i don't want it to go from 4 -> 16 -> 64 ... // and would rather have it start at like 123 if the length is 32 * 123 var argData = new List <object>(rawBufferData.Length / 32); while (stream.Position < rawBufferData.Length) { var patternType = stream.ReadByte(); // it is very common for there to be empty blocks in a world // this is a minor optimization which will try to excersize that while (patternType == _patternBooleanFalse) { argData.Add(false); if (stream.Position >= rawBufferData.Length) { goto EndOfFunction; } patternType = stream.ReadByte(); } switch (patternType) { // we can guarantee that this is not possible, because // we ruled it out with the optimization above // case _patternBooleanFalse: argData.Add(false); break; case _patternIntPos: argData.Add(stream.Read7BitEncodedInt()); break; case _patternString: argData.Add(stream.ReadString()); break; case _patternIntNeg: argData.Add(-stream.Read7BitEncodedInt() - 1); break; case _patternBooleanTrue: argData.Add(true); break; case _patternDouble: argData.Add(BitConverter.ToDouble(stream.ReadBytes(8))); break; case _patternBytes: { var length = stream.Read7BitEncodedInt(); argData.Add(stream.ReadBytes(length).ToArray()); } break; case _patternObject: { var objectArgs = new MessageObject(); while (stream.ReadByte() != _patternObjectEnd) { stream.Position--; object value; switch (stream.ReadByte()) { case _patternString: value = stream.ReadString(); break; case _patternIntPos: value = stream.Read7BitEncodedInt(); break; case _patternIntNeg: value = -stream.Read7BitEncodedInt() - 1; break; case _patternDouble: value = BitConverter.ToDouble(stream.ReadBytes(8)); break; case _patternBooleanFalse: value = false; break; case _patternBooleanTrue: value = true; break; case _patternBytes: { var length = stream.Read7BitEncodedInt(); value = stream.ReadBytes(length).ToArray(); } break; default: throw new InvalidDataException($"Invalid pattern type {patternType} in MessageObject."); } objectArgs.Add(stream.ReadString(), value); } argData.Add(objectArgs); } break; default: throw new InvalidDataException($"Invalid pattern type {patternType}."); } } EndOfFunction: return(new Message(scope, type, argData.ToArray())); }