Repository: alibaba/fastjson2
Pull request #3295 (2025-01-19 16:18:44)

simplify containsEscaped
### What this PR does / why we need it?



### Summary of your change



#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.



Patch:
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
index cb47e8f92..739128573 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
@@ -20,7 +20,7 @@ import java.util.UUID;
 
 import static com.alibaba.fastjson2.JSONFactory.*;
 import static com.alibaba.fastjson2.JSONWriter.Feature.*;
-import static com.alibaba.fastjson2.JSONWriterUTF8.containsEscaped;
+import static com.alibaba.fastjson2.JSONWriterUTF8.noneEscaped;
 import static com.alibaba.fastjson2.util.IOUtils.*;
 import static com.alibaba.fastjson2.util.JDKUtils.*;
 import static com.alibaba.fastjson2.util.TypeUtils.*;
@@ -75,7 +75,7 @@ class JSONWriterUTF16
             chars = new char[8192];
         }
         this.chars = chars;
-        this.byteVectorQuote = this.useSingleQuote ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L;
+        this.byteVectorQuote = this.useSingleQuote ? ~0x2727_2727_2727_2727L : ~0x2222_2222_2222_2222L;
     }
 
     public final void writeNull() {
@@ -280,12 +280,8 @@ class JSONWriterUTF16
         int i = 0;
         final long vecQuote = this.byteVectorQuote;
         final int upperBound = (value.length - i) & ~7;
-        for (; i < upperBound; i += 8) {
-            long vec64 = getLongLE(value, i);
-            if (containsEscaped(vec64, vecQuote)) {
-                escape = true;
-                break;
-            }
+        long vec64;
+        for (; i < upperBound && noneEscaped(vec64 = getLongLE(value, i), vecQuote); i += 8) {
             IOUtils.putLongLE(chars, off, expand(vec64));
             IOUtils.putLongLE(chars, off + 4, expand(vec64 >>> 32));
             off += 8;
@@ -372,7 +368,7 @@ class JSONWriterUTF16
             if (i + 8 < char_len) {
                 long v0 = getLongLE(value, i << 1);
                 long v1 = getLongLE(value, (i + 4) << 1);
-                if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && !containsEscaped((v0 << 8) | v1, vecQuote)) {
+                if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && noneEscaped((v0 << 8) | v1, vecQuote)) {
                     putLongLE(chars, off, v0);
                     putLongLE(chars, off + 4, v1);
                     i += 8;
@@ -2999,7 +2995,7 @@ class JSONWriterUTF16
             if (i + 8 < char_len) {
                 long v0 = getLongLE(value, i);
                 long v1 = getLongLE(value, i + 4);
-                if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && !containsEscaped((v0 << 8) | v1, vecQuote)) {
+                if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && noneEscaped((v0 << 8) | v1, vecQuote)) {
                     putLongLE(chars, off, v0);
                     putLongLE(chars, off + 4, v1);
                     i += 8;
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
index acf11b3e7..369b5766b 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
@@ -63,7 +63,7 @@ class JSONWriterUTF8
             bytes = new byte[8192];
         }
         this.bytes = bytes;
-        this.byteVectorQuote = this.useSingleQuote ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L;
+        this.byteVectorQuote = this.useSingleQuote ? ~0x2727_2727_2727_2727L : ~0x2222_2222_2222_2222L;
     }
 
     public final void writeNull() {
@@ -468,7 +468,7 @@ class JSONWriterUTF8
         int i = 0;
         final int upperBound = (value.length - i) & ~7;
         for (; i < upperBound; i += 8) {
-            if (containsEscaped(IOUtils.getLongLE(value, i), vecQuote)) {
+            if (!noneEscaped(getLongUnaligned(value, i), vecQuote)) {
                 break;
             }
         }
@@ -498,24 +498,20 @@ class JSONWriterUTF8
         this.off = off + 1;
     }
 
-    static boolean containsEscaped(long v, long quote) {
+    static boolean noneEscaped(long v, long quote) {
         /*
           for (int i = 0; i < 8; ++i) {
             byte c = (byte) data;
-            if (c == quote || c == '\\' || c < ' ') {
-                return true;
+            if (c == (byte) quote || c == '\\' || c < ' ') {
+                return false;
             }
             data >>>= 8;
           }
-          return false;
+          return true;
          */
-        long x22 = v ^ quote; // " -> 0x22, ' -> 0x27
-        long x5c = v ^ 0x5C5C5C5C5C5C5C5CL;
-
-        x22 = (x22 - 0x0101010101010101L) & ~x22;
-        x5c = (x5c - 0x0101010101010101L) & ~x5c;
-
-        return ((x22 | x5c | (0x7F7F7F7F7F7F7F7FL - v + 0x2020202020202020L) | v) & 0x8080808080808080L) != 0;
+        return ((v + 0x6060606060606060L) & 0x8080808080808080L) == 0x8080808080808080L // all >= 32
+                && ((v ^ quote) + 0x0101010101010101L & 0x8080808080808080L) == 0x8080808080808080L // != quote
+                && ((v ^ 0xA3A3A3A3A3A3A3A3L) + 0x0101010101010101L & 0x8080808080808080L) == 0x8080808080808080L; // != '\\'
     }
 
     protected final void writeStringLatin1BrowserSecure(byte[] values) {


Test patch:
diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java
index c31a61e9c..f85bcc6b3 100644
--- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java
+++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java
@@ -1131,14 +1131,95 @@ public class JSONWriterUTF8Test {
         assertEquals("\\u0001", new String(bytes));
     }
 
-    @Test
-    public void testSpecial1() {
-        byte[] buf = new byte[] {'a', 'a', 'a', 'a', 'a', 'a', 'a', 31};
-        assertEquals("\"aaaaaaa\\u001f\"", new String(JSON.toJSONBytes(new String(buf))));
+    static boolean containsEscaped(long v, long quote) {
+        /*
+          for (int i = 0; i < 8; ++i) {
+            byte c = (byte) data;
+            if (c == quote || c == '\\' || c < ' ') {
+                return true;
+            }
+            data >>>= 8;
+          }
+          return false;
+         */
+        long x22 = v ^ quote; // " -> 0x22, ' -> 0x27
+        long x5c = v ^ 0x5C5C5C5C5C5C5C5CL;
+
+        x22 = (x22 - 0x0101010101010101L) & ~x22;
+        x5c = (x5c - 0x0101010101010101L) & ~x5c;
 
+        return ((x22 | x5c | (0x7F7F7F7F7F7F7F7FL - v + 0x2020202020202020L) | v) & 0x8080808080808080L) != 0;
+    }
+
+    @Test
+    public void testSpecial_false() {
+        long quote = 0x2222_2222_2222_2222L;
+        byte[] escaped = new byte[32 + 2 + 128];
         for (int i = 0; i < 32; i++) {
-            buf[7] = (byte) i;
-            assertTrue(JSONWriterUTF8.containsEscaped(IOUtils.getLongUnaligned(buf, 0), 0x2222_2222_2222_2222L));
+            escaped[i] = (byte) i;
+        }
+        escaped[32] = (byte) quote;
+        escaped[33] = '\\';
+        for (int i = 0; i < 128; i++) {
+            escaped[i + 32 + 2] = (byte) (i + 128);
+        }
+
+        byte[] buf = new byte[8];
+        for (byte c : escaped) {
+            for (int i = 0; i < 8; i++) {
+                Arrays.fill(buf, (byte) 'a');
+                buf[i] = c;
+                long v = IOUtils.getLongUnaligned(buf, 0);
+                assertTrue(containsEscaped(v, quote));
+                assertFalse(JSONWriterUTF8.noneEscaped(v, ~quote));
+            }
+        }
+
+        quote = 0x2727_2727_2727_2727L;
+        escaped[32] = (byte) quote;
+
+        for (byte c : escaped) {
+            for (int i = 0; i < 8; i++) {
+                Arrays.fill(buf, (byte) 'a');
+                buf[i] = c;
+                long v = IOUtils.getLongUnaligned(buf, 0);
+                assertTrue(containsEscaped(v, quote));
+                assertFalse(JSONWriterUTF8.noneEscaped(v, ~quote));
+            }
+        }
+    }
+
+    @Test
+    public void testSpecial_true() {
+        long vectorQuote = 0x2222_2222_2222_2222L;
+        byte quote = (byte) vectorQuote;
+        byte[] buf = new byte[8];
+
+        for (int i = 33; i < 128; i++) {
+            if (i == quote || i == '\\') {
+                continue;
+            }
+            Arrays.fill(buf, (byte) i);
+            long v = IOUtils.getLongUnaligned(buf, 0);
+            assertTrue(JSONWriterUTF8.noneEscaped(v, ~vectorQuote));
+            assertFalse(containsEscaped(v, vectorQuote));
+        }
+    }
+
+    @Test
+    public void testSpecial_true_singleQuote() {
+        long vectorQuote = 0x2727_2727_2727_2727L;
+        byte quote = (byte) vectorQuote;
+        byte[] buf = new byte[8];
+
+        for (int i = 33; i < 128; i++) {
+            if (i == quote || i == '\\') {
+                continue;
+            }
+            Arrays.fill(buf, (byte) i);
+            long v = IOUtils.getLongUnaligned(buf, 0);
+            assertTrue(JSONWriterUTF8.noneEscaped(v, ~vectorQuote));
+            assertFalse(containsEscaped(v, vectorQuote));
         }
     }
 }