Skip to content

Commit

Permalink
support JSONWriter.Feature.BrowserSecure, for issue #964
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Nov 30, 2022
1 parent 808340a commit f8971fe
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public static void fastjson2() {
}
long millis = System.currentTimeMillis() - start;
System.out.println("fastjson2 millis : " + millis);
// zulu8.58.0.13 : 325
// zulu11.52.13 : 347 369 344
// zulu17.32.13 : 335 342
// zulu8.58.0.13 : 325 344
// zulu11.52.13 : 347 369 344 353
// zulu17.32.13 : 335 342 353

// reflect-zulu8.58.0.13 : 498 465
// reflect-zulu11.52.13 : 532 504
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,12 @@ public enum Feature {
/**
* @since 2.0.17
*/
WriteLongAsString(1L << 34);
WriteLongAsString(1L << 34),

/**
* @since 2.0.20
*/
BrowserSecure(1L << 35);

public final long mask;

Expand Down
56 changes: 54 additions & 2 deletions core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public void writeString(String str) {
}

boolean escapeNoneAscii = (context.features & Feature.EscapeNoneAscii.mask) != 0;
boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
boolean escape = false;

if (JDKUtils.JVM_VERSION > 8 && JDKUtils.UNSAFE_SUPPORT) {
Expand Down Expand Up @@ -233,6 +234,12 @@ public void writeString(String str) {
escape = true;
break;
}

if (browserSecure && (c == '<' || c == '>' || c == '(' || c == ')')) {
escape = true;
break;
}

chars[off++] = (char) c;
}

Expand Down Expand Up @@ -265,6 +272,17 @@ public void writeString(String str) {
break;
}

if (browserSecure) {
if (c0 == '<' || c1 == '<' || c2 == '<' || c3 == '<' || c4 == '<' || c5 == '<' || c6 == '<' || c7 == '<'
|| c0 == '>' || c1 == '>' || c2 == '>' || c3 == '>' || c4 == '>' || c5 == '>' || c6 == '>' || c7 == '>'
|| c0 == '(' || c1 == '(' || c2 == '(' || c3 == '(' || c4 == '(' || c5 == '(' || c6 == '(' || c7 == '('
|| c0 == ')' || c1 == ')' || c2 == ')' || c3 == ')' || c4 == ')' || c5 == ')' || c6 == ')' || c7 == ')'
) {
escape = true;
break;
}
}

if (escapeNoneAscii) {
if (c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F || c4 > 0x007F || c5 > 0x007F || c6 > 0x007F || c7 > 0x007F) {
escape = true;
Expand All @@ -290,6 +308,17 @@ public void writeString(String str) {
break;
}

if (browserSecure) {
if (c0 == '<' || c1 == '<' || c2 == '<' || c3 == '<'
|| c0 == '>' || c1 == '>' || c2 == '>' || c3 == '>'
|| c0 == '(' || c1 == '(' || c2 == '(' || c3 == '('
|| c0 == ')' || c1 == ')' || c2 == ')' || c3 == ')'
) {
escape = true;
break;
}
}

if (escapeNoneAscii) {
if (c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F) {
escape = true;
Expand All @@ -306,6 +335,12 @@ public void writeString(String str) {
char c1 = str.charAt(i + 1);
if (c0 == quote || c1 == quote || c0 == '\\' || c1 == '\\' || c0 < ' ' || c1 < ' ') {
escape = true;
} else if (browserSecure
&& (c0 == '<' || c1 == '<'
|| c0 == '>' || c1 == '>'
|| c0 == '(' || c1 == '(')
|| c0 == ')' || c1 == ')') {
escape = true;
} else if (escapeNoneAscii && (c0 > 0x007F || c1 > 0x007F)) {
escape = true;
} else {
Expand All @@ -314,7 +349,9 @@ public void writeString(String str) {
}
if (!escape && i + 1 == strlen) {
char c0 = str.charAt(i);
escape = c0 == '"' || c0 == '\\' || c0 < ' ' || (escapeNoneAscii && c0 > 0x007F);
escape = c0 == '"' || c0 == '\\' || c0 < ' '
|| (escapeNoneAscii && c0 > 0x007F)
|| (browserSecure && (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'));
}
}

Expand Down Expand Up @@ -342,7 +379,7 @@ public void writeString(String str) {
return;
}

if (escapeNoneAscii) {
if (escapeNoneAscii || browserSecure) {
ensureCapacity(off + strlen * 6 + 2);
} else {
ensureCapacity(off + strlen * 2 + 2);
Expand Down Expand Up @@ -438,6 +475,21 @@ public void writeString(String str) {
chars[off++] = '1';
chars[off++] = (char) ('a' + (ch - 26));
break;
case '<':
case '>':
case '(':
case ')':
if (browserSecure && (ch == '<' || ch == '>' || ch == '(' || ch == ')')) {
chars[off++] = '\\';
chars[off++] = 'u';
chars[off++] = DIGITS[(ch >>> 12) & 15];
chars[off++] = DIGITS[(ch >>> 8) & 15];
chars[off++] = DIGITS[(ch >>> 4) & 15];
chars[off++] = DIGITS[ch & 15];
} else {
chars[off++] = ch;
}
break;
default:
if (escapeNoneAscii && ch > 0x007F) {
chars[off++] = '\\';
Expand Down
55 changes: 53 additions & 2 deletions core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK8.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import java.util.Arrays;

import static com.alibaba.fastjson2.JSONWriter.Feature.BrowserSecure;
import static com.alibaba.fastjson2.JSONWriter.Feature.EscapeNoneAscii;

final class JSONWriterUTF16JDK8
extends JSONWriterUTF16 {
JSONWriterUTF16JDK8(Context ctx) {
Expand All @@ -22,7 +25,8 @@ public void writeString(String str) {
return;
}

boolean escapeNoneAscii = (context.features & Feature.EscapeNoneAscii.mask) != 0;
boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0;
char[] value = JDKUtils.getCharArray(str);
final int strlen = value.length;

Expand Down Expand Up @@ -55,6 +59,17 @@ public void writeString(String str) {
break;
}

if (browserSecure) {
if (c0 == '<' || c1 == '<' || c2 == '<' || c3 == '<' || c4 == '<' || c5 == '<' || c6 == '<' || c7 == '<'
|| c0 == '>' || c1 == '>' || c2 == '>' || c3 == '>' || c4 == '>' || c5 == '>' || c6 == '>' || c7 == '>'
|| c0 == '(' || c1 == '(' || c2 == '(' || c3 == '(' || c4 == '(' || c5 == '(' || c6 == '(' || c7 == '('
|| c0 == ')' || c1 == ')' || c2 == ')' || c3 == ')' || c4 == ')' || c5 == ')' || c6 == ')' || c7 == ')'
) {
escape = true;
break;
}
}

if (escapeNoneAscii) {
if (c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F || c4 > 0x007F || c5 > 0x007F || c6 > 0x007F || c7 > 0x007F) {
escape = true;
Expand Down Expand Up @@ -85,6 +100,17 @@ public void writeString(String str) {
break;
}

if (browserSecure) {
if (c0 == '<' || c1 == '<' || c2 == '<' || c3 == '<'
|| c0 == '>' || c1 == '>' || c2 == '>' || c3 == '>'
|| c0 == '(' || c1 == '(' || c2 == '(' || c3 == '('
|| c0 == ')' || c1 == ')' || c2 == ')' || c3 == ')'
) {
escape = true;
break;
}
}

if (escapeNoneAscii) {
if (c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F) {
escape = true;
Expand All @@ -102,14 +128,24 @@ public void writeString(String str) {
escape = true;
} else if (escapeNoneAscii && (c0 > 0x007F || c1 > 0x007F)) {
escape = true;
} else if (browserSecure
&& (c0 == '<' || c1 == '<'
|| c0 == '>' || c1 == '>'
|| c0 == '(' || c1 == '(')
|| c0 == ')' || c1 == ')') {
escape = true;
} else {
i += 2;
}
}

if (!escape && i + 1 == strlen) {
char c0 = value[i];
escape = c0 == quote || c0 == '\\' || c0 < ' ' || (escapeNoneAscii && c0 > 0x007F);
escape = c0 == quote
|| c0 == '\\'
|| c0 < ' '
|| (escapeNoneAscii && c0 > 0x007F)
|| (browserSecure && (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'));
}
}

Expand Down Expand Up @@ -232,6 +268,21 @@ public void writeString(String str) {
chars[off++] = '1';
chars[off++] = (char) ('a' + (ch - 26));
break;
case '<':
case '>':
case '(':
case ')':
if (browserSecure && (ch == '<' || ch == '>' || ch == '(' || ch == ')')) {
chars[off++] = '\\';
chars[off++] = 'u';
chars[off++] = DIGITS[(ch >>> 12) & 15];
chars[off++] = DIGITS[(ch >>> 8) & 15];
chars[off++] = DIGITS[(ch >>> 4) & 15];
chars[off++] = DIGITS[ch & 15];
} else {
chars[off++] = ch;
}
break;
default:
if (escapeNoneAscii && ch > 0x007F) {
chars[off++] = '\\';
Expand Down
51 changes: 45 additions & 6 deletions core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,15 @@ public void writeString(String str) {

char[] chars = JDKUtils.getCharArray(str);

boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
boolean escapeNoneAscii = (context.features & Feature.EscapeNoneAscii.mask) != 0;

// ensureCapacity
int minCapacity = off
+ chars.length * 3 // utf8 3 bytes
+ 2;

if (escapeNoneAscii) {
if (escapeNoneAscii || browserSecure) {
minCapacity += chars.length * 3;
}

Expand Down Expand Up @@ -339,10 +340,24 @@ public void writeString(String str) {
char c5 = chars[i + 5];
char c6 = chars[i + 6];
char c7 = chars[i + 7];
if (c0 == quote || c1 == quote || c2 == quote || c3 == quote || c4 == quote || c5 == quote || c6 == quote || c7 == quote
|| c0 == '\\' || c1 == '\\' || c2 == '\\' || c3 == '\\' || c4 == '\\' || c5 == '\\' || c6 == '\\' || c7 == '\\'
|| c0 < ' ' || c1 < ' ' || c2 < ' ' || c3 < ' ' || c4 < ' ' || c5 < ' ' || c6 < ' ' || c7 < ' '
|| c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F || c4 > 0x007F || c5 > 0x007F || c6 > 0x007F || c7 > 0x007F) {
if (c0 == quote || c1 == quote || c2 == quote || c3 == quote
|| c4 == quote || c5 == quote || c6 == quote || c7 == quote
|| c0 == '\\' || c1 == '\\' || c2 == '\\' || c3 == '\\'
|| c4 == '\\' || c5 == '\\' || c6 == '\\' || c7 == '\\'
|| c0 < ' ' || c1 < ' ' || c2 < ' ' || c3 < ' '
|| c4 < ' ' || c5 < ' ' || c6 < ' ' || c7 < ' '
|| c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F
|| c4 > 0x007F || c5 > 0x007F || c6 > 0x007F || c7 > 0x007F
|| (browserSecure
&& (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'
|| c1 == '<' || c1 == '>' || c1 == '(' || c1 == ')'
|| c2 == '<' || c2 == '>' || c2 == '(' || c2 == ')'
|| c3 == '<' || c3 == '>' || c3 == '(' || c3 == ')'
|| c4 == '<' || c4 == '>' || c4 == '(' || c4 == ')'
|| c5 == '<' || c5 == '>' || c5 == '(' || c5 == ')'
|| c6 == '<' || c6 == '>' || c6 == '(' || c6 == ')'
|| c7 == '<' || c7 == '>' || c7 == '(' || c7 == ')'))
) {
break;
}

Expand All @@ -367,7 +382,13 @@ public void writeString(String str) {
if (c0 == quote || c1 == quote || c2 == quote || c3 == quote
|| c0 == '\\' || c1 == '\\' || c2 == '\\' || c3 == '\\'
|| c0 < ' ' || c1 < ' ' || c2 < ' ' || c3 < ' '
|| c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F) {
|| c0 > 0x007F || c1 > 0x007F || c2 > 0x007F || c3 > 0x007F
|| (browserSecure
&& (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'
|| c1 == '<' || c1 == '>' || c1 == '(' || c1 == ')'
|| c2 == '<' || c2 == '>' || c2 == '(' || c2 == ')'
|| c3 == '<' || c3 == '>' || c3 == '(' || c3 == ')'))
) {
break;
}

Expand All @@ -387,6 +408,8 @@ public void writeString(String str) {
|| c0 == '\\' || c1 == '\\'
|| c0 < ' ' || c1 < ' '
|| c0 > 0x007F || c1 > 0x007F)
&& !(browserSecure && (c0 == '<' || c0 == '>' || c0 == '('
|| c0 == ')' || c1 == '<' || c1 == '>' || c1 == '(' || c1 == ')'))
) {
bytes[off] = (byte) c0;
bytes[off + 1] = (byte) c1;
Expand All @@ -400,6 +423,7 @@ public void writeString(String str) {
&& c0 != '\\'
&& c0 >= ' '
&& c0 <= 0x007F
&& !(browserSecure && (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'))
) {
bytes[off++] = (byte) c0;
bytes[off++] = (byte) quote;
Expand Down Expand Up @@ -490,6 +514,21 @@ public void writeString(String str) {
bytes[off++] = '1';
bytes[off++] = (byte) ('a' + (ch - 26));
break;
case '<':
case '>':
case '(':
case ')':
if (browserSecure) {
bytes[off++] = '\\';
bytes[off++] = 'u';
bytes[off++] = (byte) DIGITS[(ch >>> 12) & 15];
bytes[off++] = (byte) DIGITS[(ch >>> 8) & 15];
bytes[off++] = (byte) DIGITS[(ch >>> 4) & 15];
bytes[off++] = (byte) DIGITS[ch & 15];
} else {
bytes[off++] = (byte) ch;
}
break;
default:
if (ch == quote) {
bytes[off++] = (byte) '\\';
Expand Down

0 comments on commit f8971fe

Please sign in to comment.