Repository: netty/netty
Issue: AsciiString contentEqualsIgnoreCase fails when arrayOffset is non-zero
Labels: defect

### Expected behavior
AsciiString.contentEqualsIgnoreCase is expected to work.

### Actual behavior
AsciiString.contentEqualsIgnoreCase may return true for non-matching strings of equal length

### Steps to reproduce
Create AsciiString with non-zero offset.

### Minimal yet complete reproducer code (or URL to code)

Example of failure
`   @Test
    public void testContentEqualsIgnoreCase() {
        byte[] bytes = { 32, 'a' };
        AsciiString asciiString = new AsciiString(bytes, 1, 1, false);
        assertFalse(asciiString.contentEqualsIgnoreCase("b"));
        assertFalse(asciiString.contentEqualsIgnoreCase(AsciiString.of("b")));
    }
`

Diff to fix it:
`
diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java
index ad382482df..29ef6a3e14 100644
--- a/common/src/main/java/io/netty/util/AsciiString.java
+++ b/common/src/main/java/io/netty/util/AsciiString.java
@@ -532,7 +532,7 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
 
         if (string instanceof AsciiString) {
             AsciiString rhs = (AsciiString) string;
-            for (int i = arrayOffset(), j = rhs.arrayOffset(); i < length(); ++i, ++j) {
+            for (int i = arrayOffset(), j = rhs.arrayOffset(), end = i + length(); i < end; ++i, ++j) {
                 if (!equalsIgnoreCase(value[i], rhs.value[j])) {
                     return false;
                 }
@@ -540,7 +540,7 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
             return true;
         }
 
-        for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) {
+        for (int i = arrayOffset(), j = 0, end = length(); j < end; ++i, ++j) {
             if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
                 return false;
             }
`

### Netty version

4.1.39 and earlier.

### JVM version (e.g. `java -version`)
n/a
### OS version (e.g. `uname -a`)
n/a

Issue comments:

[asciistring.diff.gz](https://github.com/netty/netty/files/3511592/asciistring.diff.gz)

Thanks @atcurtis, good catch! Could you submit a PR with your fix and unit test?

Pull request review comments:

Patch that resolved the issue:
diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java
index ad382482dfc..29ef6a3e145 100644
--- a/common/src/main/java/io/netty/util/AsciiString.java
+++ b/common/src/main/java/io/netty/util/AsciiString.java
@@ -532,7 +532,7 @@ public boolean contentEqualsIgnoreCase(CharSequence string) {
 
         if (string instanceof AsciiString) {
             AsciiString rhs = (AsciiString) string;
-            for (int i = arrayOffset(), j = rhs.arrayOffset(); i < length(); ++i, ++j) {
+            for (int i = arrayOffset(), j = rhs.arrayOffset(), end = i + length(); i < end; ++i, ++j) {
                 if (!equalsIgnoreCase(value[i], rhs.value[j])) {
                     return false;
                 }
@@ -540,7 +540,7 @@ public boolean contentEqualsIgnoreCase(CharSequence string) {
             return true;
         }
 
-        for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) {
+        for (int i = arrayOffset(), j = 0, end = length(); j < end; ++i, ++j) {
             if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
                 return false;
             }


Test patch:
diff --git a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java
index deff4267d65..7534bb40de3 100644
--- a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java
+++ b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java
@@ -37,6 +37,15 @@
 public class AsciiStringCharacterTest {
     private static final Random r = new Random();
 
+    @Test
+    public void testContentEqualsIgnoreCase() {
+        byte[] bytes = { 32, 'a' };
+        AsciiString asciiString = new AsciiString(bytes, 1, 1, false);
+        // https://github.com/netty/netty/issues/9475
+        assertFalse(asciiString.contentEqualsIgnoreCase("b"));
+        assertFalse(asciiString.contentEqualsIgnoreCase(AsciiString.of("b")));
+    }
+
     @Test
     public void testGetBytesStringBuilder() {
         final StringBuilder b = new StringBuilder();