1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics . CodeAnalysis ;
3
4
using System . Diagnostics . Contracts ;
4
5
using System . IO ;
5
6
using System . Linq ;
14
15
15
16
// ReSharper disable BuiltInTypeReferenceStyle
16
17
17
- #if BIT64
18
18
// Use nint when referring to pointer values, long when referring to 64 bit values.
19
19
using nint = System . Int64 ;
20
20
using nuint = System . UInt64 ;
21
21
// ReSharper disable SuggestVarOrType_BuiltInTypes
22
22
// ReSharper disable SuggestVarOrType_Elsewhere
23
- #else
24
- using nint = System . Int32 ;
25
- using nuint = System . UInt32 ;
26
- #endif
27
23
28
24
namespace Portent
29
25
{
@@ -48,9 +44,11 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
48
44
_compoundResultCollection . Clear ( ) ;
49
45
var wordLength = ( uint ) word . Length ;
50
46
51
- // TODO: Align these allocated chunks together.
47
+ #pragma warning disable S1135 // Track uses of "TODO" tags
48
+ // TODO: Align these allocated chunks together.
52
49
// + 1 for the (char)0 at the start.
53
50
var inputLength = MemoryAlignmentHelper . GetCacheAlignedSize < char > ( wordLength + 1 ) ;
51
+ #pragma warning restore S1135 // Track uses of "TODO" tags
54
52
var inputBytes = stackalloc byte [ inputLength ] ;
55
53
var input = MemoryAlignmentHelper . GetCacheAlignedStart < char > ( inputBytes ) ;
56
54
* input = ( char ) 0 ;
@@ -81,7 +79,7 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
81
79
var run = stackalloc Run [ 1 ] ;
82
80
run [ 0 ] = new Run ( input , toCache , wordLength , maxPlusOne ) ;
83
81
// TODO: There's got to be a simpler way of doing this. Not a high priority though.
84
- var hasRun = stackalloc bool [ ( int ) rootLast - ( int ) rootFirst ] ;
82
+ var hasRun = stackalloc bool [ ( int ) ( rootLast - rootFirst ) ] ;
85
83
for ( nint i = 0 ; i < rootLast - rootFirst ; i ++ )
86
84
{
87
85
hasRun [ i ] = false ;
@@ -131,6 +129,7 @@ public IEnumerable<SuggestItem> Lookup(in string word, uint maxEdits)
131
129
132
130
[ LocalsInit ( false ) ]
133
131
[ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
132
+ [ SuppressMessage ( "Major Code Smell" , "S907:\" goto\" statement should not be used" ) ]
134
133
private static void Search5 ( Job me )
135
134
{
136
135
Run * run = me . _run ;
@@ -159,7 +158,6 @@ private static void Search5(Job me)
159
158
160
159
var allBytes = stackalloc byte [ ( int ) bytesRequiredTotal ] ;
161
160
162
- //var builderAlloc = stackalloc byte[builderByteLength];
163
161
var builderStart = MemoryAlignmentHelper . GetCacheAlignedStart < char > ( allBytes ) ;
164
162
* builderStart = ( char ) 0 ;
165
163
builderStart ++ ;
@@ -179,7 +177,7 @@ private static void Search5(Job me)
179
177
// Row 1 is the first character and so on...
180
178
// And we sneak in an extra row before row0, to ensure that the transposition row offset has room.
181
179
closureArg . _transpositionRow = ( uint * ) 0 ;
182
- closureArg . _row0 = editMatrix ; // + twiceMaxPlusOne;
180
+ closureArg . _row0 = editMatrix ;
183
181
editMatrix -- ;
184
182
nint x = 0 ;
185
183
nint diagonal = 0 ;
@@ -197,6 +195,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
197
195
nuint builderDepth = closure . _builderDepth ;
198
196
uint * previousRow = closure . _row0 ;
199
197
uint mp1 = closure . _maxPlusOne ;
198
+
200
199
// This is the value for the column directly before our diagonal stripe.
201
200
// For the early rows, it's the 0'th column. After that, the value is > maxPlusOne anyways.
202
201
// TODO: Potentially a place to cut down on an addition or two, if it fits in the branching done above.
@@ -209,8 +208,8 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
209
208
char * firstWithOffset = closure . _first ;
210
209
uint to = closure . _wordLength ;
211
210
212
- //char previousWordCharacter;
213
211
uint previousRowPreviousColumn ;
212
+
214
213
// Very predictable branching
215
214
if ( wordArrayOffset > 0 )
216
215
{
@@ -224,7 +223,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
224
223
}
225
224
else
226
225
{
227
- //TODO: I could move this out of the if-else and make the if do ++ instead of +=. Does this affect latency ?
226
+ //TODO: Test alternative: move this outside of the conditional, and change the previous addition to +1 ?
228
227
transpositionRow ++ ;
229
228
}
230
229
@@ -240,6 +239,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
240
239
// I tried to arrange these so that the order of operations leaves them as far away as possible from ops affecting their values.
241
240
// To reduce latency.
242
241
closure . _transpositionRow = transpositionRow ;
242
+
243
243
// TODO: is this one predictable?
244
244
if ( t2 < to )
245
245
{
@@ -250,7 +250,6 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
250
250
251
251
// Save a read by combining both characters into one register. Used ulong because comparison is best done between two int32
252
252
// Shift the previous-previous character into the high bits, putting the previous character into the bottom 2.
253
- // 00ab -> //b00a | so (uint)edgeCharacter == 0a, (uint) (edgeCharacter >> 48) == 0b
254
253
// Make sure it's unsigned so that >> 48 will shift 0's
255
254
ulong edgeCharacter = MathUtils . RotateRight ( ( ulong ) * ( uint * ) ( closure . _builderStart + builderDepth - 1 ) , 16 ) ;
256
255
@@ -260,16 +259,12 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
260
259
do
261
260
{
262
261
uint previousRowCurrentColumn = previousRow[ j] ;
263
-
264
262
ulong wordCharacter = MathUtils. RotateRight( ( ulong ) * ( uint * ) ( firstWithOffset + j - 1 ) , 16 ) ;
265
- //uint wordCharacter = *(uint*) (firstWithOffset + j - 1);
266
-
267
263
if ( ( uint ) edgeCharacter != ( uint ) wordCharacter )
268
264
{
269
265
// Non-branching Min() call here because it's not predictable.
270
266
currentRowPreviousColumn = MathUtils . Min ( currentRowPreviousColumn , previousRowCurrentColumn ) ;
271
267
uint diagonalEntry ;
272
- //if ((uint) edgeCharacter != (uint) (wordCharacter >> 48) || (uint) (edgeCharacter >> 48) != (uint) wordCharacter)
273
268
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
274
269
if ( edgeCharacter ! = ( ( wordCharacter >> 48 ) | ( wordCharacter << 48 ) ) )
275
270
{
@@ -291,13 +286,14 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
291
286
}
292
287
293
288
currentRow[ j ] = currentRowPreviousColumn ;
294
- // Doing this here removes a pause between ++ and < in (++j < to)
289
+
290
+ // Doing this here to reduce latency between the add and the conditional
295
291
++ j;
296
292
297
293
// Will make `any` negative if currentRowPreviousColumn < maxErrors
298
294
any |= ( int ) currentRowPreviousColumn - ( int ) maxPlusOne ;
299
295
previousRowPreviousColumn = previousRowCurrentColumn ;
300
- } while ( ( uint ) j < to ) ;
296
+ } while ( ( uint ) j < to ) ;
301
297
302
298
if ( any >= 0 )
303
299
{
@@ -315,6 +311,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
315
311
string stringResult = new string ( bs , 0 , ( int ) newDepth ) ;
316
312
ulong count = GetWordCount( bs , newDepth , closure . _graph ) ;
317
313
closure . _results . Add ( stringResult , count ) ;
314
+
318
315
// If we followed this branch, the register was overwritten.
319
316
// Fetch the value from the correct place instead of having the compiler store it on the stack.
320
317
maxPlusOne = closure. _maxPlusOne;
@@ -376,7 +373,9 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
376
373
ulong temp = * ( ulong * ) ( closure . _graph . _firstChildEdgeIndex + ( uint ) closure . _node ) ;
377
374
// This one is used an a pointer offset, so it can be nuint. Also, taking only the lower 32 bits.
378
375
// ReSharper disable once RedundantCast - Just being explicit and clear
376
+ #pragma warning disable IDE0004 , S1905 // Remove Unnecessary Cast
379
377
nuint childEdge = ( nuint ) ( uint ) temp;
378
+ #pragma warning restore IDE0004 , S1905 // Remove Unnecessary Cast
380
379
// Upper 32 bits. Not used as an offset, so keep as uint.
381
380
uint childEdgeEnd = ( uint ) ( temp > > 32 ) ;
382
381
@@ -386,7 +385,6 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
386
385
}
387
386
388
387
// TODO: We have some extra registers to store stuff in, and they're getting push-popped anyways. Load things and prevent re-reading from memory.
389
- // TODO: make a BIT64 distinction. We don't have all those extra registers in 32 bit mode.
390
388
uint * row0 = closure. _row0;
391
389
closure. _builderDepth = newDepth;
392
390
// casting to byte* because otherwise it was doing both shl 1 and shl 2 to double _maxPlusOne and then convert it to pointer moves
@@ -416,6 +414,7 @@ static void MatchCharacter(ref Search3Closure closure, nuint skip)
416
414
MatchCharacter( ref closureArg, 0 ) ;
417
415
}
418
416
417
+ #pragma warning disable S3898 //Value types should implement "IEquatable<T>"
419
418
private readonly ref struct Run
420
419
{
421
420
public readonly char * _input;
@@ -431,6 +430,7 @@ public Run(char* input, uint* toCache, uint wordLength, uint maxPlusOne)
431
430
_maxPlusOne = maxPlusOne;
432
431
}
433
432
}
433
+ #pragma warning restore S3898 //Value types should implement "IEquatable<T>"
434
434
435
435
private class Job
436
436
{
@@ -458,6 +458,7 @@ private void SearchPrivate()
458
458
}
459
459
460
460
[ StructLayout( LayoutKind. Explicit) ]
461
+ #pragma warning disable S3898 //Value types should implement "IEquatable<T>"
461
462
private readonly struct DawgGraph
462
463
{
463
464
[ FieldOffset( 0x00 ) ] //48
@@ -488,6 +489,7 @@ public DawgGraph(ushort* reachableTerminalNodes, ulong* wordCounts, uint* firstC
488
489
_rootNodeIndex = rootNodeIndex;
489
490
}
490
491
}
492
+ #pragma warning restore S3898 //Value types should implement "IEquatable<T>"
491
493
492
494
[ Pure]
493
495
[ MethodImpl( MethodImplOptions. AggressiveOptimization) ]
@@ -507,12 +509,16 @@ private static ulong GetWordCount(char* word, nuint length, in DawgGraph dawg)
507
509
char target = * word;
508
510
word++ ;
509
511
// ReSharper disable once RedundantCast - Just being explicit and clear
512
+ #pragma warning disable IDE0004 // Remove Unnecessary Cast
510
513
nuint i = ( nuint ) edgeChildIndex[ currentNode] ;
514
+ #pragma warning restore IDE0004 // Remove Unnecessary Cast
511
515
uint lastChildIndex = edgeChildIndex[ currentNode+ 1 ] ;
512
516
do
513
517
{
514
518
// ReSharper disable once RedundantCast - Just being explicit and clear
519
+ #pragma warning disable IDE0004 , S1905 // Remove Unnecessary Cast
515
520
currentNode = ( nint ) edgeNodeIndex[ i] ;
521
+ #pragma warning restore IDE0004 , S1905 // Remove Unnecessary Cast
516
522
char currentEdgeChar = characters[ i] ;
517
523
i++ ;
518
524
if ( currentEdgeChar != target)
@@ -541,6 +547,7 @@ private static ulong GetWordCount(char* word, nuint length, in DawgGraph dawg)
541
547
}
542
548
543
549
[ StructLayout( LayoutKind. Explicit) ]
550
+ #pragma warning disable S3898 //Value types should implement "IEquatable<T>"
544
551
private ref struct Search3Closure
545
552
{
546
553
#region CacheLine1
@@ -582,6 +589,7 @@ private ref struct Search3Closure
582
589
public SuggestItemCollection _results;
583
590
#endregion CacheLine3
584
591
}
592
+ #pragma warning restore S3898 //Value types should implement "IEquatable<T>"
585
593
586
594
public uint WordCount { get ; }
587
595
@@ -695,7 +703,9 @@ public int GetIndex(in string word)
695
703
#pragma warning restore S907 // "goto" statement should not be used
696
704
}
697
705
706
+ #pragma warning disable S907 // "goto" statement should not be used
698
707
goto singleReturnPoint ;
708
+ #pragma warning restore S907 // "goto" statement should not be used
699
709
700
710
nextIteration :
701
711
#pragma warning disable S1116 // Empty statements should be removed - This statement is important. It allows the label/goto to work.
0 commit comments