From 92e4999a7c26850b4e20af05097bccb814a72114 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 3 Aug 2023 01:08:09 +0900 Subject: [PATCH 1/4] Less backing --- Bencodex/Decoder.cs | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/Bencodex/Decoder.cs b/Bencodex/Decoder.cs index d8f612d..edc030f 100644 --- a/Bencodex/Decoder.cs +++ b/Bencodex/Decoder.cs @@ -28,7 +28,8 @@ public Decoder(Stream stream) public IValue Decode() { - IValue value = DecodeValue(); + IValue value = DecodeValue() ?? + throw new DecodingException($"Failed to decode stream"); if (ReadByte() is { } b) { throw new DecodingException( @@ -39,7 +40,7 @@ public IValue Decode() return value; } - private IValue DecodeValue() + private IValue? DecodeValue() { const byte e = 0x65; // 'e' @@ -50,6 +51,9 @@ private IValue DecodeValue() $"The byte stream terminates unexpectedly at {_offset}." ); + case 0x65: // 'e' + return null; + case 0x6e: // 'n' #pragma warning disable SA1129 return new Null(); @@ -70,18 +74,8 @@ private IValue DecodeValue() case 0x6c: // 'l' var elements = new List(); - while (true) + while (DecodeValue() is IValue element) { - byte b = ReadByte() ?? throw new DecodingException( - $"The byte stream terminates unexpectedly at {_offset}." - ); - if (b == e) - { - break; - } - - Back(); - IValue element = DecodeValue(); elements.Add(element); } @@ -89,19 +83,10 @@ private IValue DecodeValue() case 0x64: // 'd' var pairs = new List>(); - while (true) + while (DecodeKey() is IKey key) { - byte b = ReadByte() ?? throw new DecodingException( - $"The byte stream terminates unexpectedly at {_offset}." - ); - if (b == e) - { - break; - } - - Back(); - IKey key = DecodeKey(); - IValue value = DecodeValue(); + IValue value = DecodeValue() + ?? throw new DecodingException("Failed to decode"); pairs.Add(new KeyValuePair(key, value)); } @@ -125,7 +110,7 @@ private IValue DecodeValue() } } - private IKey DecodeKey() + private IKey? DecodeKey() { switch (ReadByte()) { @@ -134,6 +119,9 @@ private IKey DecodeKey() $"Expected a dictionary key, but the byte stream terminates at {_offset}." ); + case 0x65: // 'e' + return null; + case 0x75: // 'u': return ReadTextAfterPrefix(); From 1036714b18fdab7e58b4d7bf830fb00a3ef2dbb2 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 3 Aug 2023 10:32:24 +0900 Subject: [PATCH 2/4] Non-nullable ReadByte() --- Bencodex/Decoder.cs | 75 +++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/Bencodex/Decoder.cs b/Bencodex/Decoder.cs index edc030f..baffefc 100644 --- a/Bencodex/Decoder.cs +++ b/Bencodex/Decoder.cs @@ -30,11 +30,11 @@ public IValue Decode() { IValue value = DecodeValue() ?? throw new DecodingException($"Failed to decode stream"); - if (ReadByte() is { } b) + + if (!EndOfStream()) { throw new DecodingException( - $"An unexpected trailing byte 0x{b:x} at {_offset - 1}." - ); + $"An unexpected trailing byte remain at {_offset}."); } return value; @@ -46,11 +46,6 @@ public IValue Decode() switch (ReadByte()) { - case null: - throw new DecodingException( - $"The byte stream terminates unexpectedly at {_offset}." - ); - case 0x65: // 'e' return null; @@ -114,11 +109,6 @@ public IValue Decode() { switch (ReadByte()) { - case null: - throw new DecodingException( - $"Expected a dictionary key, but the byte stream terminates at {_offset}." - ); - case 0x65: // 'e' return null; @@ -172,7 +162,7 @@ private byte[] Read(byte[] buffer) return buffer; } - private byte? ReadByte() + private byte ReadByte() { if (_didBack) { @@ -189,7 +179,20 @@ private byte[] Read(byte[] buffer) _offset++; _didBack = false; - return read == 0 ? (byte?)null : _tinyBuffer[0]; + return read == 0 + ? throw new DecodingException($"The byte stream terminates unexpectedly at {_offset}.") + : _tinyBuffer[0]; + } + + // Checks end of stream. Should be called only once at the very end. + private bool EndOfStream() + { + if (_didBack) + { + return false; + } + + return _stream.Read(_tinyBuffer, 0, 1) == 0; } private void Back() @@ -212,15 +215,7 @@ private int ReadLength() const int asciiZero = 0x30; // '0' int length = 0; - var b = ReadByte(); - - if (b is null) - { - throw new DecodingException( - $"Expected digits, but the byte stream terminates at {_offset}."); - } - - byte lastByte = b.Value; + byte lastByte = ReadByte(); while (lastByte != colon) { #pragma warning disable SA1131 @@ -235,10 +230,7 @@ private int ReadLength() length *= 10; length += lastByte - asciiZero; - lastByte = ReadByte() ?? throw new DecodingException( - $"Expected a delimiter byte 0x{colon:x}, but the byte stream terminates " + - $"at {_offset}." - ); + lastByte = ReadByte(); } return length; @@ -250,27 +242,11 @@ private byte[] ReadDigits(byte delimiter) byte[] buffer = new byte[defaultBufferSize]; var b = ReadByte(); - - if (b is null) - { - throw new DecodingException( - $"Expected a minus sign or a digit, " + - $"but the byte stream terminates at {_offset}." - ); - } - bool minus = false; if (b == 0x2d) // '-' { minus = true; b = ReadByte(); - - if (b is null) - { - throw new DecodingException( - $"Expected digits, but the byte stream terminates at {_offset}." - ); - } } int digitsLength; @@ -278,16 +254,16 @@ private byte[] ReadDigits(byte delimiter) if (minus) { buffer[0] = 0x2d; - buffer[1] = b.Value; + buffer[1] = b; digitsLength = 2; } else { - buffer[0] = b.Value; + buffer[0] = b; digitsLength = 1; } - byte lastByte = b.Value; + byte lastByte = b; while (lastByte != delimiter) { @@ -300,10 +276,7 @@ private byte[] ReadDigits(byte delimiter) } #pragma warning restore SA1131 - lastByte = ReadByte() ?? throw new DecodingException( - $"Expected a delimiter byte 0x{delimiter:x}, but the byte stream terminates " + - $"at {_offset}." - ); + lastByte = ReadByte(); if (digitsLength >= buffer.Length) { From 97f7a19bc627b5ae5872d6d5cb3b28c0133c2322 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 3 Aug 2023 11:03:51 +0900 Subject: [PATCH 3/4] Changelog --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 8060e4d..e5c0709 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,8 +12,10 @@ To be released. - Removed `IndirectValue` class. [[#91]] - Changed `Codec.Encode()` and `Codec.Decode()` to no longer accept `IOffloadOptions` as an argument. [[#91]] + - Optimized for faster decoding on encoded `List`s and `Dictionary`s. [[#93]] [#91]: https://github.com/planetarium/bencodex.net/pull/91 +[#93]: https://github.com/planetarium/bencodex.net/pull/93 Version 0.12.0 From f36973179f59abea3bc74c36552f2e4dc4545b59 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Thu, 3 Aug 2023 11:08:58 +0900 Subject: [PATCH 4/4] Better exception description --- Bencodex/Decoder.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Bencodex/Decoder.cs b/Bencodex/Decoder.cs index baffefc..a430c32 100644 --- a/Bencodex/Decoder.cs +++ b/Bencodex/Decoder.cs @@ -29,15 +29,13 @@ public Decoder(Stream stream) public IValue Decode() { IValue value = DecodeValue() ?? - throw new DecodingException($"Failed to decode stream"); - - if (!EndOfStream()) - { throw new DecodingException( - $"An unexpected trailing byte remain at {_offset}."); - } + $"An unexpected token byte 0x{0x65:x} at {_offset - 1}"); - return value; + return EndOfStream() + ? value + : throw new DecodingException( + $"An unexpected trailing byte remains at {_offset}."); } private IValue? DecodeValue() @@ -81,7 +79,8 @@ public IValue Decode() while (DecodeKey() is IKey key) { IValue value = DecodeValue() - ?? throw new DecodingException("Failed to decode"); + ?? throw new DecodingException( + $"An unexpected token byte 0x{0x65:x} at {_offset - 1}"); pairs.Add(new KeyValuePair(key, value)); }