@@ -22,9 +22,17 @@ public class ManagedHasherTests
2222
2323 private static readonly int [ ] BoundaryLengths =
2424 [
25- 0 , 1 , 2 , 3 , 4 , 5 , 7 , 8 , 15 , 16 , 31 , 32 , 63 , 64 , 65 , 100 ,
25+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 15 , 16 , 31 , 32 , 63 , 64 , 65 , 100 ,
2626 127 , 128 , 129 , 255 , 512 , 960 , 1000 , 1023 , 1024 , 1025 , 2048 , 2049 ,
27- 3072 , 3073 , 4096 , 4097 , 8192 , 8193 , 16384 , 31744 , 102400 ,
27+ 3072 , 3073 , 4096 , 4097 , 5120 , 5121 , 6144 , 6145 , 7168 , 7169 , 8192 ,
28+ 8193 , 16383 , 16384 , 16385 , 31743 , 31744 , 31745 , 102400 ,
29+ ] ;
30+
31+ // Mirrors the upstream BLAKE3 test_compare_update_multiple cases through four chunks.
32+ private static readonly int [ ] IncrementalUpdateLengths =
33+ [
34+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 63 , 64 , 65 , 127 , 128 , 129 ,
35+ 1023 , 1024 , 1025 , 2048 , 2049 , 3072 , 3073 , 4096 ,
2836 ] ;
2937
3038 [ Test ]
@@ -151,6 +159,30 @@ public void FragmentedUpdatesMatchNative(int length)
151159 } ) ;
152160 }
153161
162+ [ Test ]
163+ public void EveryPairOfBoundaryUpdatesMatchesNative ( )
164+ {
165+ const int OutputLength = 303 ;
166+ foreach ( var firstLength in IncrementalUpdateLengths )
167+ {
168+ foreach ( var secondLength in IncrementalUpdateLengths )
169+ {
170+ var input = CreateVectorInput ( firstLength + secondLength ) ;
171+ var expected = new byte [ OutputLength ] ;
172+ var actual = new byte [ OutputLength ] ;
173+ Hasher . Hash ( input , expected ) ;
174+
175+ using var managed = ManagedHasher . New ( ) ;
176+ managed . Update ( input . AsSpan ( 0 , firstLength ) ) ;
177+ managed . Update ( input . AsSpan ( firstLength , secondLength ) ) ;
178+ managed . Finalize ( actual ) ;
179+
180+ Assert . That ( actual , Is . EqualTo ( expected ) ,
181+ $ "firstLength={ firstLength } , secondLength={ secondLength } ") ;
182+ }
183+ }
184+ }
185+
154186 [ TestCase ( 0 ) ]
155187 [ TestCase ( 1 ) ]
156188 [ TestCase ( 64 ) ]
@@ -214,6 +246,33 @@ public void XofSeekResetJoinAndGenericUpdatesMatchNative()
214246 Assert . That ( ManagedHasher . Hash ( MemoryMarshal . AsBytes ( values . AsSpan ( ) ) ) , Is . EqualTo ( managed . Finalize ( ) ) ) ;
215247 }
216248
249+ [ Test ]
250+ public void ResetPreservesKeyedAndDeriveKeyModes ( )
251+ {
252+ var discardedInput = CreateVectorInput ( ( 3 * 1024 ) + 7 ) ;
253+ var input = CreateVectorInput ( 1027 ) ;
254+
255+ using var nativeKeyed = Hasher . NewKeyed ( VectorKey ) ;
256+ using var managedKeyed = ManagedHasher . NewKeyed ( VectorKey ) ;
257+ managedKeyed . Update ( discardedInput ) ;
258+ managedKeyed . Reset ( ) ;
259+ nativeKeyed . Update ( input ) ;
260+ managedKeyed . Update ( input ) ;
261+
262+ using var nativeDerived = Hasher . NewDeriveKey ( VectorContext ) ;
263+ using var managedDerived = ManagedHasher . NewDeriveKey ( VectorContext ) ;
264+ managedDerived . Update ( discardedInput ) ;
265+ managedDerived . Reset ( ) ;
266+ nativeDerived . Update ( input ) ;
267+ managedDerived . Update ( input ) ;
268+
269+ Assert . Multiple ( ( ) =>
270+ {
271+ AssertManagedHashEqualsNative ( managedKeyed . Finalize ( ) , nativeKeyed . Finalize ( ) ) ;
272+ AssertManagedHashEqualsNative ( managedDerived . Finalize ( ) , nativeDerived . Finalize ( ) ) ;
273+ } ) ;
274+ }
275+
217276 [ TestCase ( 262143 ) ]
218277 [ TestCase ( 262144 ) ]
219278 [ TestCase ( 300123 ) ]
@@ -324,6 +383,20 @@ public void DeriveKeyByteContextUsesNativeLossyUtf8Semantics()
324383 AssertManagedHashEqualsNative ( managed . Finalize ( ) , native . Finalize ( ) ) ;
325384 }
326385
386+ [ TestCase ( "" ) ]
387+ [ TestCase ( "BLAKE3 managed context \u2026 \u2603 \u2014 \ud83d \udd11 " ) ]
388+ [ TestCase ( "context with unpaired surrogate \ud800 " ) ]
389+ public void DeriveKeyStringContextMatchesNativeForUnicode ( string context )
390+ {
391+ using var native = Hasher . NewDeriveKey ( context ) ;
392+ using var managed = ManagedHasher . NewDeriveKey ( context ) ;
393+ var input = CreateVectorInput ( 1025 ) ;
394+ native . Update ( input ) ;
395+ managed . Update ( input ) ;
396+
397+ AssertManagedHashEqualsNative ( managed . Finalize ( ) , native . Finalize ( ) ) ;
398+ }
399+
327400 [ Test ]
328401 public void DisposedHasherRejectsOperations ( )
329402 {
@@ -333,19 +406,28 @@ public void DisposedHasherRejectsOperations()
333406 Assert . Multiple ( ( ) =>
334407 {
335408 Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Update ( [ 1 ] ) ) ;
409+ Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Update < int > ( [ 1 ] ) ) ;
336410 Assert . Throws < ObjectDisposedException > ( ( ) => hasher . UpdateWithJoin ( [ 1 ] ) ) ;
411+ Assert . Throws < ObjectDisposedException > ( ( ) => hasher . UpdateWithJoin < int > ( [ 1 ] ) ) ;
337412 Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Finalize ( ) ) ;
413+ Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Finalize ( new byte [ 1 ] ) ) ;
414+ Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Finalize ( 0L , new byte [ 1 ] ) ) ;
415+ Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Finalize ( 0UL , new byte [ 1 ] ) ) ;
338416 Assert . Throws < ObjectDisposedException > ( ( ) => hasher . Reset ( ) ) ;
417+ Assert . DoesNotThrow ( hasher . Dispose ) ;
339418 } ) ;
340419 }
341420
342421 [ Test ]
343- public void KeyMustBeExactly32Bytes ( )
422+ public void InvalidArgumentsAreRejected ( )
344423 {
424+ using var hasher = ManagedHasher . New ( ) ;
345425 Assert . Multiple ( ( ) =>
346426 {
347427 Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ManagedHasher . NewKeyed ( new byte [ 31 ] ) ) ;
348428 Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ManagedHasher . NewKeyed ( new byte [ 33 ] ) ) ;
429+ Assert . Throws < ArgumentNullException > ( ( ) => ManagedHasher . NewDeriveKey ( ( string ) null ! ) ) ;
430+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => hasher . Finalize ( - 1L , new byte [ 1 ] ) ) ;
349431 } ) ;
350432 }
351433
0 commit comments