private static unsafe int CalculateDistance(string sourceString, int sourceLength, string targetString, int targetLength, int startIndex)
        {
            var                 arrayPool   = ArrayPool <ushort> .Shared;
            var                 pooledArray = arrayPool.Rent(targetLength);
            Span <ushort>       previousRow = pooledArray;
            ReadOnlySpan <char> source      = sourceString.AsSpan().Slice(startIndex, sourceLength);
            ReadOnlySpan <char> target      = targetString.AsSpan().Slice(startIndex, targetLength);

            //ArrayPool values are sometimes bigger than allocated, let's trim our span to exactly what we use
            previousRow = previousRow.Slice(0, targetLength);

            fixed(char *targetPtr = target)
            fixed(char *srcPtr           = source)
            fixed(ushort *previousRowPtr = previousRow)
            {
                FillRow(previousRowPtr, targetLength);

                var rowIndex = 0;

                for (; rowIndex < sourceLength - 7; rowIndex += 8)
                {
                    // todo max
                    var temp = Vector128.Create(rowIndex);
                    var diag = Sse42.PackUnsignedSaturate(temp, temp);
                    var one  = Vector128.Create((ushort)1);
                    var left = Sse42.AddSaturate(diag, one);

                    var sourceV = Sse42.LoadVector128((ushort *)(srcPtr + rowIndex));
                    var targetV = Vector128 <ushort> .Zero;

                    var shift = Vector128.CreateScalar(ushort.MaxValue);
                    // First 3  iterations fills the vector
                    for (int columnIndex = 0; columnIndex < 7; columnIndex++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2);
                        targetV = Sse42.Insert(targetV, (ushort)targetPtr[columnIndex], 0);

                        // Insert "(rowIndex + columnIndex + 1)" from the left
                        var leftValue = Vector128.Create(rowIndex + columnIndex + 1);
                        left  = Sse42.Or(Sse42.And(shift, Sse42.PackUnsignedSaturate(leftValue, leftValue)), left);
                        shift = Sse42.ShiftLeftLogical128BitLane(shift, 2);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse42.CompareEqual(sourceV, targetV);
                        var add   = Sse42.AndNot(match, one);
                        var next  = Sse42.AddSaturate(diag, add);

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 2);
                        up = Sse42.Insert(up, (ushort)previousRowPtr[columnIndex], 0);

                        var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;
                    }

                    previousRowPtr[0] = Sse42.Extract(left, 7);
                    var writePtr = previousRowPtr + 1;
                    for (int columnIndex = 8; columnIndex < targetLength; columnIndex++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2);
                        targetV = Sse42.Insert(targetV, (ushort)targetPtr[columnIndex], 0);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse42.CompareEqual(sourceV, targetV);
                        var add   = Sse42.AndNot(match, one);
                        var next  = Sse42.AddSaturate(diag, add);

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 2);
                        up = Sse42.Insert(up, (ushort)previousRowPtr[columnIndex], 0);

                        var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;

                        // Store one value
                        *writePtr = Sse42.Extract(next, 7);
                        writePtr = writePtr + 1;

                        // Store one value
                        //previousRowPtr[columnIndex - 7] = Sse42.Extract(next, 7);
                    }

                    // Finish with last 3 items, dont read any more chars just extract them
                    for (int i = targetLength - 7; i < previousRow.Length; i++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse42.CompareEqual(sourceV, targetV);
                        var add   = Sse42.AndNot(match, one);
                        var next  = Sse42.AddSaturate(diag, add);

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 2);

                        var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;
                        // Store one value
                        previousRowPtr[i] = Sse42.Extract(next, 7);
                    }

#if DEBUG
                    if (true)
                    {
                        Console.Write("prev values for row {0}:", rowIndex);
                        for (int i = 0; i < targetLength; ++i)
                        {
                            Console.Write("{0} ", previousRow[i]);
                        }
                        Console.WriteLine();
                    }
#endif
                }

                //Calculate Single Rows
                for (; rowIndex < sourceLength; rowIndex++)
                {
                    var lastSubstitutionCost = rowIndex;
                    var lastInsertionCost    = rowIndex + 1;
                    var sourcePrevChar       = source[rowIndex];
#if DEBUG
                    Console.Write("prev values for row {0}:", rowIndex);
                    for (int i = 0; i < targetLength; ++i)
                    {
                        Console.Write("{0} ", previousRow[i]);
                    }
                    Console.WriteLine();
#endif

                    CalculateRow(previousRowPtr, targetPtr, targetLength, sourcePrevChar, lastInsertionCost, lastSubstitutionCost);
                }
            }

            var result = previousRow[targetLength - 1];
            arrayPool.Return(pooledArray);
            return(result);
        }
Beispiel #2
0
        private static unsafe int CalculateDistance(string sourceString, int sourceLength, string targetString, int targetLength, int startIndex)
        {
            var                 arrayPool   = ArrayPool <int> .Shared;
            var                 pooledArray = arrayPool.Rent(targetLength);
            Span <int>          previousRow = pooledArray;
            ReadOnlySpan <char> source      = sourceString.AsSpan().Slice(startIndex, sourceLength);
            ReadOnlySpan <char> target      = targetString.AsSpan().Slice(startIndex, targetLength);

            //ArrayPool values are sometimes bigger than allocated, let's trim our span to exactly what we use
            previousRow = previousRow.Slice(0, targetLength);

            fixed(char *targetPtr = target)
            fixed(char *srcPtr        = source)
            fixed(int *previousRowPtr = previousRow)
            {
                FillRow(previousRowPtr, targetLength);

                var rowIndex = 0;

                for (; rowIndex < sourceLength - 3; rowIndex += 4)
                {
                    var diag = Vector128.Create(rowIndex);
                    var left = Vector128.Create(rowIndex + 1);

                    var sourceV = Sse42.ConvertToVector128Int32((short *)(srcPtr + rowIndex));
                    var targetV = Vector128 <int> .Zero;
                    var one     = Vector128.Create(1);

                    // First 3  iterations fills the vector
                    var shift = Vector128.CreateScalar(-1);
                    for (int columnIndex = 0; columnIndex < 4; columnIndex++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4);
                        targetV = Sse42.Insert(targetV, (short)targetPtr[columnIndex], 0);

                        //left = Sse42.Insert(left, rowIndex + columnIndex + 1, (byte)columnIndex);
                        var leftValue = Vector128.Create(rowIndex + columnIndex + 1);
                        left  = Sse42.Or(Sse42.And(shift, leftValue), left);
                        shift = Sse42.ShiftLeftLogical128BitLane(shift, 4);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle());
                        var next  = Sse42.Subtract(diag, match.AsInt32());

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 4);
                        up = Sse42.Insert(up, previousRowPtr[columnIndex], 0);

                        var tmp = Sse42.Add(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;
                    }

                    previousRowPtr[0] = Sse42.Extract(left, 3);
                    for (int columnIndex = 4; columnIndex < targetLength; columnIndex++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4);
                        targetV = Sse42.Insert(targetV, (short)targetPtr[columnIndex], 0);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse42.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle());
                        var next  = Sse42.Subtract(diag, match.AsInt32());

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 4);
                        up = Sse42.Insert(up, previousRowPtr[columnIndex], 0);

                        var tmp = Sse42.Add(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;

                        // Store one value
                        previousRowPtr[columnIndex - 3] = Sse42.Extract(next, 3);
                    }

                    // Finish with last 3 items, dont read any more chars just extract them
                    for (int i = targetLength - 3; i < targetLength; i++)
                    {
                        // Shift in the next character
                        targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4);

                        // compare source to target
                        // alternativ, compare equal and OR with One
                        var match = Sse.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle());
                        var next  = Sse42.Subtract(diag, match.AsInt32());

                        // Create next diag which is current up
                        var up = Sse42.ShiftLeftLogical128BitLane(left, 4);

                        var tmp = Sse42.Add(Sse42.Min(left, up), one);
                        next = Sse42.Min(next, tmp);

                        left = next;
                        diag = up;
                        // Store one value
                        previousRowPtr[i] = Sse42.Extract(next, 3);
                    }

#if DEBUG
                    if (true)
                    {
                        Console.Write("prev values for row {0}:", rowIndex);
                        for (int i = 0; i < targetLength; ++i)
                        {
                            Console.Write("{0} ", previousRow[i]);
                        }
                        Console.WriteLine();
                    }
#endif
                }

                //Calculate Single Rows
                for (; rowIndex < sourceLength; rowIndex++)
                {
                    var lastSubstitutionCost = rowIndex;
                    var lastInsertionCost    = rowIndex + 1;
                    var sourcePrevChar       = source[rowIndex];
#if DEBUG
                    Console.Write("prev values for row {0}:", rowIndex);
                    for (int i = 0; i < targetLength; ++i)
                    {
                        Console.Write("{0} ", previousRow[i]);
                    }
                    Console.WriteLine();
#endif

                    CalculateRow(previousRowPtr, targetPtr, targetLength, sourcePrevChar, lastInsertionCost, lastSubstitutionCost);
                }
            }

            var result = previousRow[targetLength - 1];
            arrayPool.Return(pooledArray);
            return(result);
        }