/// <inheritdoc /> public AsyncWireReaderBytesReadableAdapter([NotNull] IBytesReadable bytesReadableSource) { if (bytesReadableSource == null) { throw new ArgumentNullException(nameof(bytesReadableSource)); } BytesReadableSource = bytesReadableSource; }
/// <summary> /// Reads <see cref="count"/> many bytes from the reader. /// </summary> /// <param name="readable"></param> /// <param name="count">How many bytes to read.</param> /// <returns>The read bytes.</returns> public static byte[] Read(this IBytesReadable readable, int count) { if (readable == null) { throw new ArgumentNullException(nameof(readable)); } return(readable.ReadAsync(count, 0).WaitAndUnwrapException()); }
/// <summary> /// Reads asyncronously <see cref="count"/> many bytes from the reader. /// </summary> /// <param name="buffer">The buffer to store the bytes into.</param> /// <param name="start">The start position in the buffer to start reading into.</param> /// <param name="count">How many bytes to read.</param> /// <param name="timeoutInMilliseconds">How many milliseconds to wait before canceling the operation.</param> /// <returns>A future for the read bytes.</returns> public static Task <int> ReadAsync(this IBytesReadable readable, byte[] buffer, int start, int count, int timeoutInMilliseconds) { if (readable == null) { throw new ArgumentNullException(nameof(readable)); } //TODO: DO we need to check the timeout? return(readable.ReadAsync(buffer, 0, count, timeoutInMilliseconds > 0 ? new CancellationTokenSource(timeoutInMilliseconds).Token : CancellationToken.None)); }
//From: https://github.com/mgravell/protobuf-net/blob/38a2d0b6095dad08c57ef5bd7dc821643a86a4a1/src/protobuf-net/ProtoReader.cs /// <summary> /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length /// reader to be created. /// </summary> public static async Task <long> ReadLongLengthPrefix(IBytesReadable bytesReadable, PrefixStyle style, CancellationToken token) { switch (style) { case PrefixStyle.Base128: // check for a length return((long) await TryReadUInt64Variant(bytesReadable, token)); case PrefixStyle.Fixed32: { //TODO: Figure out a way to reduce allocations byte[] bytes = await ReadFixed4Byte(bytesReadable, style, token) .ConfigureAwait(false); //Means the socket disconnected if (bytes == null) { return(0); } if (BitConverter.IsLittleEndian) { return(bytes.Reinterpret <int>()); } else { throw new NotSupportedException("TODO: Implement big endian prefix handling."); } } case PrefixStyle.Fixed32BigEndian: { //TODO: Figure out a way to reduce allocations byte[] bytes = await ReadFixed4Byte(bytesReadable, style, token) .ConfigureAwait(false); //Means the socket disconnected if (bytes == null) { return(0); } //TODO: Improve efficiency return((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]); } default: throw new ArgumentOutOfRangeException(nameof(style)); } }
/// <summary> /// Reads asyncronously <see cref="count"/> many bytes from the reader. /// </summary> /// <param name="readable"></param> /// <param name="count">How many bytes to read.</param> /// <param name="timeoutInMilliseconds">How many milliseconds to wait before canceling the operation.</param> /// <returns>A future for the read bytes.</returns> public static async Task <byte[]> ReadAsync(this IBytesReadable readable, int count, int timeoutInMilliseconds) { if (readable == null) { throw new ArgumentNullException(nameof(readable)); } byte[] bytes = new byte[count]; int resultCount = await readable.ReadAsync(bytes, 0, count, timeoutInMilliseconds) .ConfigureAwait(false); if (resultCount != count) { throw new InvalidOperationException($"Failed to read {count} many bytes form {nameof(IBytesReadable)}. Read {resultCount} many bytes."); } return(bytes); }
//TODO: Doc /// <summary> /// Asyncly reads a byte chunk from the <see cref="bytesReadable"/> /// or throws if unable. /// </summary> /// <param name="bytesReadable"></param> /// <param name="style"></param> /// <param name="token"></param> /// <returns></returns> private static async Task <byte[]> ReadFixed4Byte(IBytesReadable bytesReadable, PrefixStyle style, CancellationToken token) { byte[] bytes = new byte[4]; int count = await bytesReadable.ReadAsync(bytes, 0, 4, token) .ConfigureAwait(false); //0 means the socket disconnected if (count == 0) { return(null); } if (count < 4) { throw new InvalidOperationException($"Protobuf-Net could not read length prefix: {style}"); } return(bytes); }
/// <inheritdoc /> public async Task <TTypeToDeserializeTo> DeserializeAsync <TTypeToDeserializeTo>(IBytesReadable bytesReadable, CancellationToken token) { //TODO: Implement no prefixing support if (PrefixStyle == PrefixStyle.None) { throw new NotSupportedException("Protobuf-Net deserialization without length prefixing is not yet supported."); } //To do the async read operation with Protobuf-Net we need to do some manual buffering int prefixSize = checked ((int)await ReadLongLengthPrefix(bytesReadable, PrefixStyle, token).ConfigureAwait(false)); //TODO: Reduce allocations somehow byte[] bytes = new byte[prefixSize]; int count = await bytesReadable.ReadAsync(bytes, 0, prefixSize, token) .ConfigureAwait(false); //0 means that the socket disconnected if (count == 0) { return(default(TTypeToDeserializeTo)); } return(Serializer.Deserialize <TTypeToDeserializeTo>(new MemoryStream(bytes))); }
/// <returns>The number of bytes consumed; 0 if no data available</returns> private static async Task <ulong> TryReadUInt64Variant(IBytesReadable bytesReadable, CancellationToken token) { //TODO: Reuse-share buffer to reduce allocations byte[] tempBuffer = new byte[9]; ulong value = 0; int count = await bytesReadable.ReadAsync(tempBuffer, 0, 1, token) .ConfigureAwait(false); //0 means that the socket disconnected if (count == 0) { return(0); } int b = tempBuffer[0]; if (b < 0) { return(0); } value = (uint)b; if ((value & 0x80) == 0) { return(1); } value &= 0x7F; int bytesRead = 1, shift = 7; while (bytesRead < 9) { count = await bytesReadable.ReadAsync(tempBuffer, bytesRead, 1, token) .ConfigureAwait(false); //0 means the underlying socket disconnected if (count == 0) { return(0); } b = tempBuffer[bytesRead]; if (b < 0) { throw EoF(null); } value |= ((ulong)b & 0x7F) << shift; shift += 7; if ((b & 0x80) == 0) { return(value); } bytesRead++; } count = await bytesReadable.ReadAsync(tempBuffer, bytesRead, 1, token) .ConfigureAwait(false); if (count == 0) { return(0); } b = tempBuffer[bytesRead]; if (b < 0) { throw EoF(null); } if ((b & 1) == 0) // only use 1 bit from the last byte { value |= ((ulong)b & 0x7F) << shift; } return(value); }
/// <inheritdoc /> public Task <TTypeToDeserializeTo> DeserializeAsync <TTypeToDeserializeTo>(IBytesReadable bytesReadable, CancellationToken token) { //We have to manually add peek buffering return(Serializer.DeserializeAsync <TTypeToDeserializeTo>(new AsyncWireReaderBytesReadableAdapter(bytesReadable) .PeekWithBufferingAsync())); }