diff --git a/src/Ascii.cs b/src/Ascii.cs index 108c312..4b720bc 100644 --- a/src/Ascii.cs +++ b/src/Ascii.cs @@ -6,6 +6,8 @@ using System.Runtime.InteropServices; + + // Ideally, we would want to implement something that looks like // https://learn.microsoft.com/en-us/dotnet/api/system.text.asciiencoding?view=net-7.0 // @@ -63,7 +65,7 @@ public static unsafe bool SIMDIsAscii(this ReadOnlySpan s) { // instead of a load, we could have set it to zero, like so... // total = Vector128.Zero; - // or to a custome value like this: + // or to a custom value like this: // total = DuplicateToVector128((char)0); Vector128 total = AdvSimd.LoadVector128((ushort*)pStart); i += 8; @@ -90,21 +92,26 @@ public static unsafe bool SIMDIsAscii(this ReadOnlySpan s) fixed (char* pStart = &MemoryMarshal.GetReference(s)) { int i = 0; - if (s.Length > 8) + if (s.Length > 16) // Adjusted for the unrolled loop { Vector128 total = Sse41.LoadDquVector128((ushort*)pStart); i += 8; - // unrolling could be useful here: - for (; i + 7 < s.Length; i += 8) + + // Unrolling the loop by 2x + for (; i + 15 < s.Length; i += 16) { - Vector128 raw = Sse41.LoadDquVector128((ushort*)pStart + i); - total = Sse2.Or(total, raw); + Vector128 raw1 = Sse41.LoadDquVector128((ushort*)pStart + i); + Vector128 raw2 = Sse41.LoadDquVector128((ushort*)pStart + i + 8); + + total = Sse2.Or(total, raw1); + total = Sse2.Or(total, raw2); } + Vector128 b127 = Vector128.Create((ushort)127); Vector128 b = Sse41.Max(b127, total); Vector128 b16 = Sse41.CompareEqual(b, b127); int movemask = Sse2.MoveMask(b16.AsByte()); - if (movemask != 0xfffff) + if (movemask != 0xffff) { return false; } diff --git a/test/AsciiTest.cs b/test/AsciiTest.cs index 0a0ecc4..1bc763c 100644 --- a/test/AsciiTest.cs +++ b/test/AsciiTest.cs @@ -9,6 +9,80 @@ public void Test1() Assert.True(SimdUnicode.Ascii.IsAscii("absads12323123232131231232132132132312321321312321")); Assert.False(SimdUnicode.Ascii.IsAscii("absaé12323123232131231232132132132312321321312321")); Assert.True(SimdUnicode.Ascii.SIMDIsAscii("absads12323123232131231232132132132312321321312321")); + Assert.True(SimdUnicode.Ascii.SIMDIsAscii("12345678")); + Assert.True(SimdUnicode.Ascii.SIMDIsAscii("123456789")); + Assert.True(SimdUnicode.Ascii.SIMDIsAscii("1234567890123456")); Assert.False(SimdUnicode.Ascii.SIMDIsAscii("absaé12323123232131231232132132132312321321312321")); + Assert.False(SimdUnicode.Ascii.SIMDIsAscii("absa12323123232131231232132132132312321321312321é")); + } + + [Fact] + public void HardCodedSequencesTest() + { + string[] goodsequences = { + "a", + "abcde12345", + "\x71", + "\x75\x4c", + "\x7f\x4c\x23\x3c\x3a\x6f\x5d\x44\x13\x70" + }; + + string[] badsequences = { + "\xc3\x28", + "\xa0\xa1", + "\xe2\x28\xa1", + "\xe2\x82\x28", + "\xf0\x28\x8c\xbc", + "\xf0\x90\x28\xbc", + "\xf0\x28\x8c\x28", + "\xc0\x9f", + "\xf5\xff\xff\xff", + "\xed\xa0\x81", + "\xf8\x90\x80\x80\x80", + "123456789012345\xed", + "123456789012345\xf1", + "123456789012345\xc2", + "\xC2\x7F", + "\xce", + "\xce\xba\xe1", + "\xce\xba\xe1\xbd", + "\xce\xba\xe1\xbd\xb9\xcf", + "\xce\xba\xe1\xbd\xb9\xcf\x83\xce", + "\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce", + "\xdf", + "\xef\xbf", + "\x80", + "\x91\x85\x95\x9e", + "\x6c\x02\x8e\x18", + "\x25\x5b\x6e\x2c\x32\x2c\x5b\x5b\x33\x2c\x34\x2c\x05\x29\x2c\x33\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5d\x2c\x35\x2e\x33\x2c\x39\x2e\x33\x2c\x37\x2e\x33\x2c\x39\x2e\x34\x2c\x37\x2e\x33\x2c\x39\x2e\x33\x2c\x37\x2e\x33\x2c\x39\x2e\x34\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x20\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x23\x0a\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x7e\x7e\x0a\x0a\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5d\x2c\x37\x2e\x33\x2c\x39\x2e\x33\x2c\x37\x2e\x33\x2c\x39\x2e\x34\x2c\x37\x2e\x33\x2c\x39\x2e\x33\x2c\x37\x2e\x33\x2c\x39\x2e\x34\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x5d\x01\x01\x80\x01\x01\x01\x79\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + "[[[[[[[[[[[[[[[\x80\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x010\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + "\x20\x0b\x01\x01\x01\x64\x3a\x64\x3a\x64\x3a\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x30\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x80\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + "\x80", + "\x90", + "\xa1", + "\xb2", + "\xc3", + "\xd4", + "\xe5", + "\xf6", + "\xc3\xb1", + "\xe2\x82\xa1", + "\xf0\x90\x8c\xbc", + "\xc2\x80", + "\xf0\x90\x80\x80", + "\xee\x80\x80", + "\xef\xbb\xbf"}; + + foreach (var sequence in goodsequences) + { + Assert.True(SimdUnicode.Ascii.IsAscii(sequence), "Expected valid ASCII sequence"); + Assert.True(SimdUnicode.Ascii.SIMDIsAscii(sequence), "Expected SIMDIsAscii to validate ASCII sequence"); + } + + foreach (var sequence in badsequences) + { + Assert.False(SimdUnicode.Ascii.IsAscii(sequence), "Expected non-valid ASCII sequence"); + Assert.False(SimdUnicode.Ascii.SIMDIsAscii(sequence), "Expected SIMDIsAscii to invalidate non-ASCII sequence"); + } } } \ No newline at end of file