From 028cb930b6ac824cc4d2c8da24bccec037ba136f Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Sun, 1 Dec 2024 15:49:18 +0900 Subject: [PATCH 1/3] Used ref instead --- Bencodex.Tests/EncoderTest.cs | 65 ++++++++------ Bencodex/Encoder.cs | 154 +++++++++++++++++----------------- 2 files changed, 114 insertions(+), 105 deletions(-) diff --git a/Bencodex.Tests/EncoderTest.cs b/Bencodex.Tests/EncoderTest.cs index f86d2b5..f99f16f 100644 --- a/Bencodex.Tests/EncoderTest.cs +++ b/Bencodex.Tests/EncoderTest.cs @@ -30,8 +30,9 @@ public void EstimateLength() public void EncodeNull() { var buffer = new byte[10]; - long size = Encoder.EncodeNull(buffer, 3L); - Assert.Equal(1L, size); + long offset = 3L; + Encoder.EncodeNull(buffer, ref offset); + Assert.Equal(3L + 1L, offset); AssertEqual(new byte[] { 0, 0, 0, 0x6e, 0, 0, 0, 0, 0, 0 }, buffer); } @@ -39,12 +40,14 @@ public void EncodeNull() public void EncodeBoolean() { var buffer = new byte[10]; - long size = Encoder.EncodeBoolean(new Boolean(true), buffer, 2L); - Assert.Equal(1L, size); + long offset = 2L; + Encoder.EncodeBoolean(new Boolean(true), buffer, ref offset); + Assert.Equal(2L + 1L, offset); AssertEqual(new byte[] { 0, 0, 0x74, 0, 0, 0, 0, 0, 0, 0 }, buffer); - size = Encoder.EncodeBoolean(new Boolean(false), buffer, 5L); - Assert.Equal(1L, size); + offset = 5L; + Encoder.EncodeBoolean(new Boolean(false), buffer, ref offset); + Assert.Equal(5L + 1L, offset); AssertEqual(new byte[] { 0, 0, 0x74, 0, 0, 0x66, 0, 0, 0, 0 }, buffer); } @@ -52,18 +55,21 @@ public void EncodeBoolean() public void EncodeInteger() { var buffer = new byte[10]; - long size = Encoder.EncodeInteger(0, buffer, 2L); - Assert.Equal(3L, size); + long offset = 2L; + Encoder.EncodeInteger(0, buffer, ref offset); + Assert.Equal(2L + 3L, offset); AssertEqual(new byte[] { 0, 0, 0x69, 0x30, 0x65, 0, 0, 0, 0, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeInteger(-123, buffer, 1L); - Assert.Equal(6L, size); + offset = 1L; + Encoder.EncodeInteger(-123, buffer, ref offset); + Assert.Equal(1L + 6L, offset); AssertEqual(new byte[] { 0, 0x69, 0x2d, 0x31, 0x32, 0x33, 0x65, 0, 0, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeInteger(456, buffer, 4L); - Assert.Equal(5L, size); + offset = 4L; + Encoder.EncodeInteger(456, buffer, ref offset); + Assert.Equal(4L + 5L, offset); AssertEqual(new byte[] { 0, 0, 0, 0, 0x69, 0x34, 0x35, 0x36, 0x65, 0 }, buffer); } @@ -71,8 +77,9 @@ public void EncodeInteger() public void EncodeBinary() { var buffer = new byte[20]; - long size = Encoder.EncodeBinary(new Binary("hello world", Encoding.ASCII), buffer, 2L); - Assert.Equal(14L, size); + long offset = 2L; + Encoder.EncodeBinary(new Binary("hello world", Encoding.ASCII), buffer, ref offset); + Assert.Equal(2L + 14L, offset); AssertEqual( new byte[20] { @@ -90,8 +97,9 @@ public void EncodeBinary() public void EncodeText() { var buffer = new byte[20]; - long size = Encoder.EncodeText("한글", buffer, 5L); - Assert.Equal(9L, size); + long offset = 5L; + Encoder.EncodeText("한글", buffer, ref offset); + Assert.Equal(5L + 9L, offset); AssertEqual( new byte[20] { @@ -130,28 +138,33 @@ public void CountDecimalDigits() public void EncodeDigits() { var buffer = new byte[10]; - long size = Encoder.EncodeDigits(0L, buffer, 2L); - Assert.Equal(1L, size); + long offset = 2L; + Encoder.EncodeDigits(0L, buffer, ref offset); + Assert.Equal(2L + 1L, offset); AssertEqual(new byte[] { 0, 0, 0x30, 0, 0, 0, 0, 0, 0, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeDigits(5L, buffer, 0L); - Assert.Equal(1L, size); + offset = 0L; + Encoder.EncodeDigits(5L, buffer, ref offset); + Assert.Equal(0L + 1L, offset); AssertEqual(new byte[] { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeDigits(10L, buffer, 5L); - Assert.Equal(2L, size); + offset = 5L; + Encoder.EncodeDigits(10L, buffer, ref offset); + Assert.Equal(5L + 2L, offset); AssertEqual(new byte[] { 0, 0, 0, 0, 0, 0x31, 0x30, 0, 0, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeDigits(123L, buffer, 6L); - Assert.Equal(3L, size); + offset = 6L; + Encoder.EncodeDigits(123L, buffer, ref offset); + Assert.Equal(6L + 3L, offset); AssertEqual(new byte[] { 0, 0, 0, 0, 0, 0, 0x31, 0x32, 0x33, 0 }, buffer); Clear(buffer, 0, buffer.Length); - size = Encoder.EncodeDigits(9876543210L, buffer, 0L); - Assert.Equal(10L, size); + offset = 0L; + Encoder.EncodeDigits(9876543210L, buffer, ref offset); + Assert.Equal(0L + 10L, offset); AssertEqual( new byte[] { 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30 }, buffer diff --git a/Bencodex/Encoder.cs b/Bencodex/Encoder.cs index 38bb704..c1768a7 100644 --- a/Bencodex/Encoder.cs +++ b/Bencodex/Encoder.cs @@ -14,7 +14,8 @@ public static byte[] Encode(IValue value) { long estimatedLength = EstimateLength(value); var buffer = new byte[estimatedLength]; - Encode(value, buffer, 0L); + long offset = 0; + Encode(value, buffer, ref offset); return buffer; } @@ -68,24 +69,21 @@ internal static long EstimateLength(IValue value) return value.EncodingLength; } - internal static long EncodeNull(byte[] buffer, long offset) + internal static void EncodeNull(byte[] buffer, ref long offset) { - buffer[offset] = 0x6e; // 'n' - return 1L; + buffer[offset++] = 0x6e; // 'n' } - internal static long EncodeBoolean(in Types.Boolean value, byte[] buffer, long offset) + internal static void EncodeBoolean(in Types.Boolean value, byte[] buffer, ref long offset) { - buffer[offset] = value.Value + buffer[offset++] = value.Value ? (byte)0x74 // 't' : (byte)0x66; // 'f' - return 1L; } - internal static long EncodeInteger(in Integer value, byte[] buffer, long offset) + internal static void EncodeInteger(in Integer value, byte[] buffer, ref long offset) { - buffer[offset] = 0x69; // 'i' - offset++; + buffer[offset++] = 0x69; // 'i' string digits = value.Value.ToString(CultureInfo.InvariantCulture); if (offset + digits.Length <= int.MaxValue) { @@ -98,104 +96,89 @@ internal static long EncodeInteger(in Integer value, byte[] buffer, long offset) } offset += digits.Length; - buffer[offset] = 0x65; // 'e' - return 1L + digits.Length + 1L; + buffer[offset++] = 0x65; // 'e' } - internal static long EncodeBinary(in Binary value, byte[] buffer, long offset) + internal static void EncodeBinary(in Binary value, byte[] buffer, ref long offset) { long len = value.ByteArray.Length; - long lenStrLength = EncodeDigits(len, buffer, offset); - offset += lenStrLength; - buffer[offset] = 0x3a; // ':' - offset++; + EncodeDigits(len, buffer, ref offset); + buffer[offset++] = 0x3a; // ':' if (offset + len <= int.MaxValue) { value.ByteArray.CopyTo(buffer, (int)offset); - return lenStrLength + 1L + len; + offset += len; + return; } byte[] b = value.ToByteArray(); Array.Copy(b, 0L, buffer, offset, b.LongLength); - return lenStrLength + 1L + b.LongLength; + offset += len; + return; } - internal static long EncodeText(in Text value, byte[] buffer, long offset) + internal static void EncodeText(in Text value, byte[] buffer, ref long offset) { buffer[offset++] = 0x75; // 'u' int utf8Length = value.Utf8Length; - long lenStrLength = EncodeDigits(utf8Length, buffer, offset); - offset += lenStrLength; - buffer[offset] = 0x3a; // ':' - offset++; + + EncodeDigits(utf8Length, buffer, ref offset); + buffer[offset++] = 0x3a; // ':' + string str = value.Value; if (offset + str.Length <= int.MaxValue) { Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, (int)offset); - return 1L + lenStrLength + 1L + utf8Length; + offset += utf8Length; + return; } byte[] utf8 = Encoding.UTF8.GetBytes(value.Value); Array.Copy(utf8, 0L, buffer, offset, utf8.LongLength); - return 1L + lenStrLength + 1L + utf8.LongLength; + offset += utf8.LongLength; + return; } // TODO: Needs a unit test. - internal static long EncodeList( - in List value, - byte[] buffer, - long offset - ) + internal static void EncodeList(in List value, byte[] buffer, ref long offset) { - buffer[offset] = 0x6c; // 'l' - long encLen = 1L; // This means the logical "expanded" encoding length. - long actualBytes = 1L; // This means the actual "collapsed" encoding length. + buffer[offset++] = 0x6c; // 'l' foreach (IValue v in value) { - actualBytes += Encode(v, buffer, offset + actualBytes); - encLen += v.EncodingLength; + Encode(v, buffer, ref offset); } - offset += actualBytes; - buffer[offset] = 0x65; // 'e' - actualBytes++; - encLen++; - return actualBytes; + buffer[offset++] = 0x65; // 'e' + return; } // TODO: Needs a unit test. - internal static long EncodeDictionary( - in Dictionary value, - byte[] buffer, - long offset - ) + internal static void EncodeDictionary(in Dictionary value, byte[] buffer, ref long offset) { - buffer[offset] = 0x64; // 'd' - long encLen = 1L; // This means the logical "expanded" encoding length. - long actualBytes = 1L; // This means the actual "collapsed" encoding length. + buffer[offset++] = 0x64; // 'd' + foreach (KeyValuePair pair in value) { - actualBytes += pair.Key switch + switch (pair.Key) { - Text tk => EncodeText(tk, buffer, offset + actualBytes), - Binary bk => EncodeBinary(bk, buffer, offset + actualBytes), - { } k => throw new ArgumentException( - $"Unsupported type: {k.GetType()}", nameof(k)), - }; - - actualBytes += Encode(pair.Value, buffer, offset + actualBytes); + case Binary binary: + EncodeBinary(binary, buffer, ref offset); + break; + case Text text: + EncodeText(text, buffer, ref offset); + break; + default: + throw new ArgumentException( + $"Unsupported type: {pair.Key.GetType()}", nameof(pair.Key)); + } - encLen += pair.Key.EncodingLength + pair.Value.EncodingLength; + Encode(pair.Value, buffer, ref offset); } - offset += actualBytes; - buffer[offset] = 0x65; // 'e' - encLen++; - actualBytes++; - value.EncodingLength = encLen; - return actualBytes; + buffer[offset++] = 0x65; // 'e' + return; } internal static long CountDecimalDigits(long value) @@ -223,7 +206,7 @@ internal static long CountDecimalDigits(long value) #pragma warning restore SA1503 } - internal static long EncodeDigits(long positiveInt, byte[] buffer, long offset) + internal static void EncodeDigits(long positiveInt, byte[] buffer, ref long offset) { const int asciiZero = 0x30; // '0' long length = CountDecimalDigits(positiveInt); @@ -233,26 +216,39 @@ internal static long EncodeDigits(long positiveInt, byte[] buffer, long offset) positiveInt /= 10; } - return length; + offset += length; } // TODO: Needs a unit test. - internal static long Encode(in IValue value, byte[] buffer, long offset) + internal static void Encode(in IValue value, byte[] buffer, ref long offset) { - return value switch + switch (value) { - Null _ => EncodeNull(buffer, offset), - Types.Boolean b => EncodeBoolean(b, buffer, offset), - Integer i => EncodeInteger(i, buffer, offset), - Binary bin => EncodeBinary(bin, buffer, offset), - Text t => EncodeText(t, buffer, offset), - List l => EncodeList(l, buffer, offset), - Dictionary d => EncodeDictionary(d, buffer, offset), - _ => + case Null _: + EncodeNull(buffer, ref offset); + break; + case Types.Boolean boolean: + EncodeBoolean(boolean, buffer, ref offset); + break; + case Integer integer: + EncodeInteger(integer, buffer, ref offset); + break; + case Binary binary: + EncodeBinary(binary, buffer, ref offset); + break; + case Text text: + EncodeText(text, buffer, ref offset); + break; + case List list: + EncodeList(list, buffer, ref offset); + break; + case Dictionary dictionary: + EncodeDictionary(dictionary, buffer, ref offset); + break; + default: throw new ArgumentException( - "Unsupported type: " + value.GetType().FullName, nameof(value) - ), - }; + $"Unsupported type: {value.GetType()}", nameof(value)); + } } } } From 91046b544be99964d00e3c07ab70ba0cc54c4a64 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Sun, 1 Dec 2024 16:41:39 +0900 Subject: [PATCH 2/3] Code readability; bugfix --- Bencodex/Encoder.cs | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Bencodex/Encoder.cs b/Bencodex/Encoder.cs index c1768a7..f53981a 100644 --- a/Bencodex/Encoder.cs +++ b/Bencodex/Encoder.cs @@ -9,6 +9,16 @@ namespace Bencodex { internal static class Encoder { + private const byte _n = 0x6e; + private const byte _t = 0x74; + private const byte _f = 0x66; + private const byte _i = 0x69; + private const byte _c = 0x3a; // `:` + private const byte _e = 0x65; + private const byte _u = 0x75; + private const byte _l = 0x6c; + private const byte _d = 0x64; + // TODO: Needs a unit test. public static byte[] Encode(IValue value) { @@ -36,24 +46,24 @@ public static void Encode(IValue value, Stream output) switch (value) { case List l: - output.WriteByte(0x6c); // 'l' + output.WriteByte(_l); foreach (IValue el in l) { Encode(el, output); } - output.WriteByte(0x65); // 'e' + output.WriteByte(_e); break; case Dictionary d: - output.WriteByte(0x6c); // 'l' + output.WriteByte(_d); foreach (KeyValuePair pair in d) { Encode(pair.Key, output); Encode(pair.Value, output); } - output.WriteByte(0x65); // 'e' + output.WriteByte(_e); break; } @@ -71,19 +81,17 @@ internal static long EstimateLength(IValue value) internal static void EncodeNull(byte[] buffer, ref long offset) { - buffer[offset++] = 0x6e; // 'n' + buffer[offset++] = _n; } internal static void EncodeBoolean(in Types.Boolean value, byte[] buffer, ref long offset) { - buffer[offset++] = value.Value - ? (byte)0x74 // 't' - : (byte)0x66; // 'f' + buffer[offset++] = value.Value ? _t : _f; } internal static void EncodeInteger(in Integer value, byte[] buffer, ref long offset) { - buffer[offset++] = 0x69; // 'i' + buffer[offset++] = _i; string digits = value.Value.ToString(CultureInfo.InvariantCulture); if (offset + digits.Length <= int.MaxValue) { @@ -96,14 +104,14 @@ internal static void EncodeInteger(in Integer value, byte[] buffer, ref long off } offset += digits.Length; - buffer[offset++] = 0x65; // 'e' + buffer[offset++] = _e; } internal static void EncodeBinary(in Binary value, byte[] buffer, ref long offset) { long len = value.ByteArray.Length; EncodeDigits(len, buffer, ref offset); - buffer[offset++] = 0x3a; // ':' + buffer[offset++] = _c; if (offset + len <= int.MaxValue) { @@ -120,14 +128,12 @@ internal static void EncodeBinary(in Binary value, byte[] buffer, ref long offse internal static void EncodeText(in Text value, byte[] buffer, ref long offset) { - buffer[offset++] = 0x75; // 'u' + buffer[offset++] = _u; int utf8Length = value.Utf8Length; - EncodeDigits(utf8Length, buffer, ref offset); - buffer[offset++] = 0x3a; // ':' + buffer[offset++] = _c; string str = value.Value; - if (offset + str.Length <= int.MaxValue) { Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, (int)offset); @@ -144,20 +150,20 @@ internal static void EncodeText(in Text value, byte[] buffer, ref long offset) // TODO: Needs a unit test. internal static void EncodeList(in List value, byte[] buffer, ref long offset) { - buffer[offset++] = 0x6c; // 'l' + buffer[offset++] = _l; foreach (IValue v in value) { Encode(v, buffer, ref offset); } - buffer[offset++] = 0x65; // 'e' + buffer[offset++] = _e; return; } // TODO: Needs a unit test. internal static void EncodeDictionary(in Dictionary value, byte[] buffer, ref long offset) { - buffer[offset++] = 0x64; // 'd' + buffer[offset++] = _d; foreach (KeyValuePair pair in value) { @@ -177,7 +183,7 @@ internal static void EncodeDictionary(in Dictionary value, byte[] buffer, ref lo Encode(pair.Value, buffer, ref offset); } - buffer[offset++] = 0x65; // 'e' + buffer[offset++] = _e; return; } From 2a04ca26362f742ff8147d833b08731d02be364c Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Sun, 1 Dec 2024 16:54:17 +0900 Subject: [PATCH 3/3] Changelog --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0278fe3..4f5cdcc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,8 +7,11 @@ Version 0.17.0 To be released. - Removed `Dictionary.GetValue()` methods. [[#122]] + - Optimized `Encoder.Encode()` method. [[#124]] + - Fixed a bug in `Encoder.Encode(IValue, Stream)` method. [[#124]] [#122]: https://github.com/planetarium/bencodex.net/pull/122 +[#124]: https://github.com/planetarium/bencodex.net/pull/124 Version 0.16.0