modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MsgPack/MsgPackReader.cs (256 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Apache.Ignite.Internal.Proto.MsgPack; using System; using System.Buffers.Binary; using System.IO; using BinaryTuple; using Ignite.Sql; /// <summary> /// MsgPack reader. /// </summary> // ReSharper disable PatternIsRedundant internal ref struct MsgPackReader { private readonly ReadOnlySpan<byte> _span; private int _pos; /// <summary> /// Initializes a new instance of the <see cref="MsgPackReader"/> struct. /// </summary> /// <param name="span">Span to read from.</param> public MsgPackReader(ReadOnlySpan<byte> span) { _span = span; _pos = 0; } /// <summary> /// Gets the number of consumed bytes. /// </summary> public int Consumed => _pos; /// <summary> /// Gets a value indicating whether the end of the underlying buffer is reached. /// </summary> public bool End => _pos >= _span.Length; /// <summary> /// Reads nil if it is the next token. /// </summary> /// <returns><c>true</c> if the next token was nil; <c>false</c> otherwise.</returns> public bool TryReadNil() { if (_span[_pos] == MsgPackCode.Nil) { _pos++; return true; } return false; } /// <summary> /// Reads a boolean value. /// </summary> /// <returns>The value.</returns> public bool ReadBoolean() => _span[_pos++] switch { MsgPackCode.True => true, MsgPackCode.False => false, var invalid => throw GetInvalidCodeException("bool", invalid) }; /// <summary> /// Reads a short value. /// </summary> /// <returns>The value.</returns> public short ReadInt16() => _span[_pos++] switch { var code and >= MsgPackCode.MinNegativeFixInt and <= MsgPackCode.MaxNegativeFixInt => unchecked((sbyte)code), var code and >= MsgPackCode.MinFixInt and <= MsgPackCode.MaxFixInt => code, MsgPackCode.UInt8 => _span[_pos++], MsgPackCode.Int8 => unchecked((sbyte)_span[_pos++]), MsgPackCode.UInt16 => checked((short)BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2))), MsgPackCode.Int16 => BinaryPrimitives.ReadInt16BigEndian(GetSpan(2)), var invalid => throw GetInvalidCodeException("int32", invalid) }; /// <summary> /// Reads an int value. /// </summary> /// <returns>The value.</returns> public int ReadInt32() => _span[_pos++] switch { var code and >= MsgPackCode.MinNegativeFixInt and <= MsgPackCode.MaxNegativeFixInt => unchecked((sbyte)code), var code and >= MsgPackCode.MinFixInt and <= MsgPackCode.MaxFixInt => code, MsgPackCode.UInt8 => _span[_pos++], MsgPackCode.Int8 => unchecked((sbyte)_span[_pos++]), MsgPackCode.UInt16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Int16 => BinaryPrimitives.ReadInt16BigEndian(GetSpan(2)), MsgPackCode.UInt32 => checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))), MsgPackCode.Int32 => BinaryPrimitives.ReadInt32BigEndian(GetSpan(4)), var invalid => throw GetInvalidCodeException("int32", invalid) }; /// <summary> /// Reads int or null. /// </summary> /// <returns>Nullable int.</returns> public int? ReadInt32Nullable() => TryReadNil() ? null : ReadInt32(); /// <summary> /// Reads a long value. /// </summary> /// <returns>The value.</returns> public long ReadInt64() => _span[_pos++] switch { var code and >= MsgPackCode.MinNegativeFixInt and <= MsgPackCode.MaxNegativeFixInt => unchecked((sbyte)code), var code and >= MsgPackCode.MinFixInt and <= MsgPackCode.MaxFixInt => code, MsgPackCode.UInt8 => _span[_pos++], MsgPackCode.Int8 => unchecked((sbyte)_span[_pos++]), MsgPackCode.UInt16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Int16 => BinaryPrimitives.ReadInt16BigEndian(GetSpan(2)), MsgPackCode.UInt32 => BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4)), MsgPackCode.Int32 => BinaryPrimitives.ReadInt32BigEndian(GetSpan(4)), MsgPackCode.UInt64 => checked((long)BinaryPrimitives.ReadUInt64BigEndian(GetSpan(8))), MsgPackCode.Int64 => BinaryPrimitives.ReadInt64BigEndian(GetSpan(8)), var invalid => throw GetInvalidCodeException("int64", invalid) }; /// <summary> /// Reads an int value if it is the next token. /// </summary> /// <param name="res">result.</param> /// <returns><c>true</c> if could read an integer value; <c>false</c> otherwise.</returns> public bool TryReadInt(out int res) { if (MsgPackCode.IsInt32(_span[_pos])) { res = ReadInt32(); return true; } res = default; return false; } /// <summary> /// Reads array header. /// </summary> /// <returns>Array size.</returns> public int ReadArrayHeader() => _span[_pos++] switch { var code when MsgPackCode.IsFixArr(code) => code & 0x0F, MsgPackCode.Array16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Array32 => checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))), var invalid => throw GetInvalidCodeException("array", invalid) }; /// <summary> /// Reads string header. /// </summary> /// <returns>String length in bytes.</returns> public int ReadStringHeader() => _span[_pos++] switch { var code when MsgPackCode.IsFixStr(code) => code & 0x1F, MsgPackCode.Str8 => _span[_pos++], MsgPackCode.Str16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Str32 => checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))), var invalid => throw GetInvalidCodeException("string", invalid) }; /// <summary> /// Reads string value. /// </summary> /// <returns>String.</returns> public string ReadString() => ProtoCommon.StringEncoding.GetString(GetSpan(ReadStringHeader())); /// <summary> /// Reads string value. /// </summary> /// <returns>String.</returns> public string? ReadStringNullable() => TryReadNil() ? null : ReadString(); /// <summary> /// Reads binary header. /// </summary> /// <returns>Binary size.</returns> public int ReadBinaryHeader() => _span[_pos++] switch { MsgPackCode.Bin8 => _span[_pos++], MsgPackCode.Bin16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Bin32 => checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))), var invalid => throw GetInvalidCodeException("binary", invalid) }; /// <summary> /// Reads bytes. /// </summary> /// <returns>Span of byte.</returns> public ReadOnlySpan<byte> ReadBinary() => GetSpan(ReadBinaryHeader()); /// <summary> /// Reads map header. /// </summary> /// <returns>Map length.</returns> public int ReadMapHeader() => _span[_pos++] switch { var code when MsgPackCode.IsFixMap(code) => code & 0x0F, MsgPackCode.Map16 => BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)), MsgPackCode.Map32 => checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))), var invalid => throw GetInvalidCodeException("map", invalid) }; /// <summary> /// Reads GUID value. /// </summary> /// <returns>Guid.</returns> public Guid ReadGuid() { CheckCode(nameof(MsgPackCode.FixExt16), MsgPackCode.FixExt16); CheckCode(nameof(ClientMessagePackType.Uuid), (byte)ClientMessagePackType.Uuid); return UuidSerializer.Read(GetSpan(16)); } /// <summary> /// Skips a value. /// </summary> /// <param name="count">Count of elements to skip.</param> public void Skip(int count = 1) { while (count > 0) { count--; var code = _span[_pos++]; if (MsgPackCode.IsPosFixInt(code) || MsgPackCode.IsNegFixInt(code)) { continue; } if (MsgPackCode.IsFixMap(code)) { int mapLen = code & 0x0f; count += mapLen * 2; continue; } if (MsgPackCode.IsFixArr(code)) { int arrayLen = code & 0x0f; count += arrayLen; continue; } if (MsgPackCode.IsFixStr(code)) { int strLen = code & 0x1f; _pos += strLen; continue; } switch (code) { case MsgPackCode.True: case MsgPackCode.False: case MsgPackCode.Nil: break; case MsgPackCode.Int8: case MsgPackCode.UInt8: _pos++; break; case MsgPackCode.Int16: case MsgPackCode.UInt16: _pos += 2; break; case MsgPackCode.Int32: case MsgPackCode.UInt32: case MsgPackCode.Float32: _pos += 4; break; case MsgPackCode.Int64: case MsgPackCode.UInt64: case MsgPackCode.Float64: _pos += 8; break; case MsgPackCode.Bin8: case MsgPackCode.Str8: _pos += _span[_pos] + 1; break; case MsgPackCode.Bin16: case MsgPackCode.Str16: _pos = BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)) + _pos; break; case MsgPackCode.Bin32: case MsgPackCode.Str32: _pos = checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))) + _pos; break; case MsgPackCode.FixExt1: _pos += 2; break; case MsgPackCode.FixExt2: _pos += 3; break; case MsgPackCode.FixExt4: _pos += 5; break; case MsgPackCode.FixExt8: _pos += 9; break; case MsgPackCode.FixExt16: _pos += 17; break; case MsgPackCode.Ext8: _pos += _span[_pos] + 2; break; case MsgPackCode.Ext16: _pos = BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)) + 1 + _pos; break; case MsgPackCode.Ext32: _pos = checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))) + 1 + _pos; break; case MsgPackCode.Array16: count += BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)); break; case MsgPackCode.Array32: count += checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4))); break; case MsgPackCode.Map16: count += BinaryPrimitives.ReadUInt16BigEndian(GetSpan(2)) * 2; break; case MsgPackCode.Map32: count += checked((int)BinaryPrimitives.ReadUInt32BigEndian(GetSpan(4)) * 2); break; default: throw GetInvalidCodeException("valid type code", code); } } } /// <summary> /// Reads <see cref="ColumnType"/> and value. /// </summary> /// <returns>Value.</returns> public object? ReadObjectFromBinaryTuple() { if (TryReadNil()) { return null; } var tuple = new BinaryTupleReader(ReadBinary(), 3); return tuple.GetObject(0); } private static InvalidDataException GetInvalidCodeException(string expected, byte code) => new($"Invalid code, expected '{expected}', but got '{code}'"); private ReadOnlySpan<byte> GetSpan(int length) { var span = _span.Slice(_pos, length); _pos += length; return span; } private void CheckCode(string name, byte expectedCode) { var code = _span[_pos++]; if (code != expectedCode) { throw GetInvalidCodeException(name, code); } } }