@@ -11,6 +11,41 @@ namespace Lagrange.Proto.Test;
1111[ TestFixture ]
1212public class ProtoWriterReaderTest
1313{
14+ private static readonly ulong [ ] VarInt64Values =
15+ [
16+ 0 ,
17+ 1 ,
18+ 127 ,
19+ 128 ,
20+ 1UL << 28 ,
21+ 1UL << 32 ,
22+ 1UL << 56 ,
23+ 1UL << 63 ,
24+ 0x0102030405060708 ,
25+ 0xFEDCBA9876543210 ,
26+ ulong . MaxValue
27+ ] ;
28+
29+ private static readonly uint [ ] VarInt32Values =
30+ [
31+ 0 ,
32+ 1 ,
33+ 127 ,
34+ 128 ,
35+ 16_383 ,
36+ 16_384 ,
37+ 2_097_151 ,
38+ 2_097_152 ,
39+ 268_435_455 ,
40+ 268_435_456 ,
41+ 1_742_816_827 ,
42+ uint . MaxValue
43+ ] ;
44+
45+ private static readonly byte [ ] VarInt8Values = [ 0 , 1 , 127 , 128 , byte . MaxValue ] ;
46+
47+ private static readonly ushort [ ] VarInt16Values = [ 0 , 1 , 127 , 128 , 16_383 , 16_384 , ushort . MaxValue ] ;
48+
1449 #region VarInt Tests
1550
1651 [ Test ]
@@ -87,6 +122,136 @@ public void TestVarInt_MaxValues()
87122 Assert . That ( ulongVal , Is . EqualTo ( ulong . MaxValue ) ) ;
88123 } ) ;
89124 }
125+
126+ [ Test ]
127+ public void TestEncodeVarInt_UInt8AndUInt16MatchReference ( )
128+ {
129+ foreach ( byte value in VarInt8Values )
130+ {
131+ AssertEncodedVarIntMatchesReference ( value , value ) ;
132+ }
133+
134+ foreach ( ushort value in VarInt16Values )
135+ {
136+ AssertEncodedVarIntMatchesReference ( value , value ) ;
137+ }
138+ }
139+
140+ [ Test ]
141+ public void TestDecodeVarIntUInt8AndUInt16_UnsafePathMatchesReference ( )
142+ {
143+ foreach ( byte value in VarInt8Values )
144+ {
145+ var reader = new ProtoReader ( PadForUnsafePath ( EncodeVarIntReference ( value ) ) ) ;
146+ Assert . That ( reader . DecodeVarInt < byte > ( ) , Is . EqualTo ( value ) ) ;
147+ }
148+
149+ foreach ( ushort value in VarInt16Values )
150+ {
151+ var reader = new ProtoReader ( PadForUnsafePath ( EncodeVarIntReference ( value ) ) ) ;
152+ Assert . That ( reader . DecodeVarInt < ushort > ( ) , Is . EqualTo ( value ) ) ;
153+ }
154+ }
155+
156+ [ TestCaseSource ( nameof ( VarInt32Values ) ) ]
157+ public void TestEncodeVarInt_UInt32MatchesReference ( uint value )
158+ {
159+ var buffer = new ArrayBufferWriter < byte > ( ) ;
160+ var writer = new ProtoWriter ( buffer ) ;
161+
162+ writer . WriteRawByte ( 0xAA ) ;
163+ writer . EncodeVarInt ( value ) ;
164+ writer . Flush ( ) ;
165+
166+ byte [ ] expected = [ 0xAA , .. EncodeVarIntReference ( value ) ] ;
167+ Assert . That ( buffer . WrittenMemory . ToArray ( ) , Is . EqualTo ( expected ) ) ;
168+ }
169+
170+ [ TestCaseSource ( nameof ( VarInt32Values ) ) ]
171+ public void TestDecodeVarIntUInt32_UnsafePathMatchesReference ( uint value )
172+ {
173+ byte [ ] encoded = EncodeVarIntReference ( value ) ;
174+ byte [ ] padded = PadForUnsafePath ( encoded ) ;
175+ var reader = new ProtoReader ( padded ) ;
176+
177+ uint decoded = reader . DecodeVarInt < uint > ( ) ;
178+
179+ Assert . That ( decoded , Is . EqualTo ( value ) ) ;
180+ }
181+
182+ [ TestCaseSource ( nameof ( VarInt32Values ) ) ]
183+ public void TestDecodeVarIntUnsafePair_UInt32MatchesReference ( uint value )
184+ {
185+ if ( ! Ssse3 . X64 . IsSupported && ! AdvSimd . Arm64 . IsSupported )
186+ {
187+ Assert . Ignore ( "SSSE3/NEON is not supported on this platform." ) ;
188+ }
189+
190+ byte [ ] first = EncodeVarIntReference ( value ) ;
191+ byte [ ] second = EncodeVarIntReference ( 42 ) ;
192+ byte [ ] padded = new byte [ first . Length + second . Length + 16 ] ;
193+ first . CopyTo ( padded , 0 ) ;
194+ second . CopyTo ( padded , first . Length ) ;
195+ var reader = new ProtoReader ( padded ) ;
196+
197+ var ( decodedFirst , decodedSecond ) = reader . DecodeVarIntUnsafe < uint , uint > ( padded ) ;
198+
199+ Assert . Multiple ( ( ) =>
200+ {
201+ Assert . That ( decodedFirst , Is . EqualTo ( value ) ) ;
202+ Assert . That ( decodedSecond , Is . EqualTo ( 42u ) ) ;
203+ } ) ;
204+ }
205+
206+ [ TestCaseSource ( nameof ( VarInt64Values ) ) ]
207+ public void TestEncodeVarInt_UInt64MatchesReference ( ulong value )
208+ {
209+ var buffer = new ArrayBufferWriter < byte > ( ) ;
210+ var writer = new ProtoWriter ( buffer ) ;
211+
212+ writer . WriteRawByte ( 0xAA ) ;
213+ writer . EncodeVarInt ( value ) ;
214+ writer . Flush ( ) ;
215+
216+ byte [ ] expected = [ 0xAA , .. EncodeVarIntReference ( value ) ] ;
217+ Assert . That ( buffer . WrittenMemory . ToArray ( ) , Is . EqualTo ( expected ) ) ;
218+ }
219+
220+ [ TestCaseSource ( nameof ( VarInt64Values ) ) ]
221+ public void TestDecodeVarIntUInt64_UnsafePathMatchesReference ( ulong value )
222+ {
223+ byte [ ] encoded = EncodeVarIntReference ( value ) ;
224+ byte [ ] padded = PadForUnsafePath ( encoded ) ;
225+ var reader = new ProtoReader ( padded ) ;
226+
227+ ulong decoded = reader . DecodeVarInt < ulong > ( ) ;
228+
229+ Assert . That ( decoded , Is . EqualTo ( value ) ) ;
230+ }
231+
232+ [ TestCaseSource ( nameof ( VarInt64Values ) ) ]
233+ public void TestDecodeVarIntUnsafePair_UInt64MatchesReference ( ulong value )
234+ {
235+ if ( ! Ssse3 . X64 . IsSupported && ! AdvSimd . Arm64 . IsSupported )
236+ {
237+ Assert . Ignore ( "SSSE3/NEON is not supported on this platform." ) ;
238+ }
239+
240+ byte [ ] first = EncodeVarIntReference ( value ) ;
241+ byte [ ] second = EncodeVarIntReference ( 42 ) ;
242+ byte [ ] padded = new byte [ first . Length + second . Length + 16 ] ;
243+ first . CopyTo ( padded , 0 ) ;
244+ second . CopyTo ( padded , first . Length ) ;
245+ var reader = new ProtoReader ( padded ) ;
246+
247+ var ( decodedFirst , decodedSecond ) = reader . DecodeVarIntUnsafe < ulong , uint > ( padded ) ;
248+
249+ Assert . Multiple ( ( ) =>
250+ {
251+ Assert . That ( decodedFirst , Is . EqualTo ( value ) ) ;
252+ Assert . That ( decodedSecond , Is . EqualTo ( 42u ) ) ;
253+ } ) ;
254+ }
90255
91256 [ Test ]
92257 public void TestVarInt_NegativeNumbers ( )
@@ -1002,4 +1167,40 @@ public void TestPerformance_MixedOperations()
10021167 }
10031168
10041169 #endregion
1005- }
1170+
1171+ private static byte [ ] EncodeVarIntReference ( ulong value )
1172+ {
1173+ var bytes = new List < byte > ( ) ;
1174+
1175+ do
1176+ {
1177+ byte next = ( byte ) ( value & 0x7F ) ;
1178+ value >>= 7 ;
1179+ if ( value != 0 ) next |= 0x80 ;
1180+ bytes . Add ( next ) ;
1181+ }
1182+ while ( value != 0 ) ;
1183+
1184+ return bytes . ToArray ( ) ;
1185+ }
1186+
1187+ private static byte [ ] PadForUnsafePath ( byte [ ] encoded )
1188+ {
1189+ byte [ ] padded = new byte [ encoded . Length + 16 ] ;
1190+ encoded . CopyTo ( padded , 0 ) ;
1191+ return padded ;
1192+ }
1193+
1194+ private static void AssertEncodedVarIntMatchesReference < T > ( T value , ulong expectedValue ) where T : unmanaged, System . Numerics . INumber < T >
1195+ {
1196+ var buffer = new ArrayBufferWriter < byte > ( ) ;
1197+ var writer = new ProtoWriter ( buffer ) ;
1198+
1199+ writer . WriteRawByte ( 0xAA ) ;
1200+ writer . EncodeVarInt ( value ) ;
1201+ writer . Flush ( ) ;
1202+
1203+ byte [ ] expected = [ 0xAA , .. EncodeVarIntReference ( expectedValue ) ] ;
1204+ Assert . That ( buffer . WrittenMemory . ToArray ( ) , Is . EqualTo ( expected ) ) ;
1205+ }
1206+ }
0 commit comments