Hololens的.NET CoreCLR应用:手动解码VarInt32大小字段问题求助
Hey there, let's work through this VarInt32 decoding challenge you're hitting with StreamSocket on HoloLens' .NET CoreCLR. It's tricky when standard tools don't play nice, but we've got two solid approaches to solve this.
Option 1: Manually Implement VarInt32 Decoding
First, let's recap how VarInt32 works—each byte uses the highest bit (0x80) as a "continuation flag": if it's set, there are more bytes coming. The remaining 7 bits are data, which we shift and accumulate until we hit a byte without the continuation flag.
Here's an async implementation tailored for StreamSocket (using DataReader, which is the recommended way to read from StreamSocket's InputStream):
public async Task<int> ReadVarInt32Async(StreamSocket socket) { int result = 0; int shift = 0; byte currentByte; do { // Initialize DataReader for partial reads (since we might get one byte at a time) using var reader = new DataReader(socket.InputStream); reader.InputStreamOptions = InputStreamOptions.Partial; // Load exactly one byte from the socket uint bytesLoaded = await reader.LoadAsync(1); if (bytesLoaded != 1) { throw new IOException("Failed to read a byte for VarInt32 decoding"); } currentByte = reader.ReadByte(); // Extract the 7 data bits and add them to our result result |= (currentByte & 0x7F) << shift; shift += 7; // Guard against overflow—VarInt32 should never exceed 4 bytes (28 bits of data) if (shift > 28) { throw new OverflowException("VarInt32 value exceeds 32-bit integer limit"); } } while ((currentByte & 0x80) != 0); // Continue until we hit a byte without the continuation flag return result; }
Key Notes for This Approach:
- Use
InputStreamOptions.Partialto ensure we don't wait for more data than needed (critical for reading one byte at a time). - Add error handling for cases where the socket connection drops mid-read, or the data is malformed (like more than 4 bytes for a VarInt32).
- If you're expecting unsigned 32-bit values, switch the return type to
uintand adjust the overflow check to 31 bits.
Option 2: Wrap StreamSocket as a Standard Stream for CodedInputStream
If you'd rather avoid manual decoding, you can wrap StreamSocket's input/output streams into a standard System.IO.Stream that CodedInputStream can work with. This lets you use Protobuf's built-in VarInt handling directly.
Here's a simple wrapper class:
public class StreamSocketWrapper : Stream { private readonly StreamSocket _socket; private readonly DataReader _reader; private readonly DataWriter _writer; public StreamSocketWrapper(StreamSocket socket) { _socket = socket; _reader = new DataReader(socket.InputStream) { InputStreamOptions = InputStreamOptions.Partial }; _writer = new DataWriter(socket.OutputStream); } // Implement async read (used by CodedInputStream under the hood) public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { uint bytesLoaded = await _reader.LoadAsync((uint)count).AsTask(cancellationToken); _reader.ReadBytes(buffer, offset, (int)bytesLoaded); return (int)bytesLoaded; } // Implement other required Stream methods public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => true; public override long Length => throw new NotSupportedException(); public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } public override void Flush() { _writer.FlushAsync().AsTask().Wait(); } public override int Read(byte[] buffer, int offset, int count) { return ReadAsync(buffer, offset, count, CancellationToken.None).Result; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { WriteAsync(buffer, offset, count, CancellationToken.None).Wait(); } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { _writer.WriteBytes(buffer.AsSpan(offset, count)); await _writer.StoreAsync().AsTask(cancellationToken); } protected override void Dispose(bool disposing) { if (disposing) { _reader.Dispose(); _writer.Dispose(); _socket.Dispose(); } base.Dispose(disposing); } }
Then you can use it with CodedInputStream like this:
// Assuming you already have a connected StreamSocket instance using var socketStream = new StreamSocketWrapper(yourStreamSocket); using var codedInput = CodedInputStream.Create(socketStream); // Now use Protobuf's built-in method to read VarInt32 int sizeField = await codedInput.ReadVarint32Async();
Key Notes for This Approach:
- The wrapper handles converting StreamSocket's async I/O to the standard Stream interface that CodedInputStream expects.
- Make sure to properly dispose of the wrapper, DataReader, DataWriter, and StreamSocket when you're done to avoid resource leaks.
- This approach is cleaner if you're already using Protobuf elsewhere in your app, since it reuses existing, well-tested code.
Either of these should get you past the variable-length decoding hurdle. Let me know if you need clarification on any part!
内容的提问来源于stack exchange,提问作者Mytheral

